[pitivi] add timeline controls next to timeline canvas



commit d2aca9ad64f2a1774c908e6f7944342da9918f56
Author: Brandon Lewis <brandon_lewis berkeley edu>
Date:   Sat Feb 28 12:59:54 2009 -0800

    add timeline controls next to timeline canvas
    
    set priority based on y-axis
    
    add max-priority property to timeline.track
    
    fix function signature
    
    disable vertical movement for now, we have a long way to go
    
    start working on track controls
    
    fix some issues
---
 pitivi/timeline/track.py      |   20 +++++++++
 pitivi/ui/timeline.py         |   44 +++++++++++++++-----
 pitivi/ui/timelinecanvas.py   |    9 ++--
 pitivi/ui/timelinecontrols.py |   46 ++++++++++++++++++++
 pitivi/ui/track.py            |   19 +++++++--
 pitivi/ui/trackobject.py      |   92 +++++++++++++++++++++++++++-------------
 6 files changed, 181 insertions(+), 49 deletions(-)

diff --git a/pitivi/timeline/track.py b/pitivi/timeline/track.py
index a9fe944..c858b1f 100644
--- a/pitivi/timeline/track.py
+++ b/pitivi/timeline/track.py
@@ -365,6 +365,24 @@ class Track(object, Signallable):
 
     max_priority = property(_getMaxPriority)
 
+    __max_priority = 0
+
+    @property
+    def max_priority(self):
+        return self.__max_priority
+
+    def _trackObjectPriorityCb(self, trackobject, priority):
+        op = self.__max_priority
+        self.__max_priority = max(self.__max_priority, priority)
+        if op != self.__max_priority:
+            self.emit("max-priority-changed")
+
+    def _connectToTrackObjectSignals(self, track_object):
+        track_object.connect("priority-changed", self._trackObjectPriorityCb)
+
+    def _disconnectTrackObjectSignals(self, track_object):
+        track_object.disconnect_by_function(self._trackObjectPriorityCb)
+
     def addTrackObject(self, track_object):
         if track_object.track is not None:
             raise TrackError()
@@ -378,6 +396,7 @@ class Track(object, Signallable):
         self.track_objects.append(track_object)
 
         track_object.makeBin()
+        self._connectToTrackObjectSignals(track_object)
 
         self._updateMaxPriority()
         self._connectToTrackObject(track_object)
@@ -399,6 +418,7 @@ class Track(object, Signallable):
 
         self.track_objects.remove(track_object)
         track_object.track = None
+        self._disconnectTrackObjectSignals(track_object)
 
         self._updateMaxPriority()
 
diff --git a/pitivi/ui/timeline.py b/pitivi/ui/timeline.py
index 8cd244d..3e012d1 100644
--- a/pitivi/ui/timeline.py
+++ b/pitivi/ui/timeline.py
@@ -33,6 +33,7 @@ import gobject
 
 from gettext import gettext as _
 from timelinecanvas import TimelineCanvas
+from timelinecontrols import TimelineControls
 from pitivi.receiver import receiver, handler
 from zoominterface import Zoomable
 
@@ -85,9 +86,8 @@ ui = '''
 #    |  +--Track(SmartGroup)
 #    |
 #    +--Status Bar ??
-#
 
-class Timeline(gtk.VBox, Loggable, Zoomable):
+class Timeline(gtk.Table, Loggable, Zoomable):
 
     # the screen width of the current unit
     unit_width = 10
@@ -96,7 +96,7 @@ class Timeline(gtk.VBox, Loggable, Zoomable):
 
 
     def __init__(self, ui_manager):
-        gtk.VBox.__init__(self)
+        gtk.Table.__init__(self, rows=2, columns=1, homogeneous=False)
         Loggable.__init__(self)
         Zoomable.__init__(self)
         self.log("Creating Timeline")
@@ -114,19 +114,28 @@ class Timeline(gtk.VBox, Loggable, Zoomable):
     def _createUI(self):
         self.leftSizeGroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
         self.hadj = gtk.Adjustment()
+        self.vadj = gtk.Adjustment()
+
+        # controls for tracks and layers
+        self._controls = TimelineControls(self.timeline)
+        controlwindow = gtk.ScrolledWindow(None, self.vadj)
+        controlwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
+        controlwindow.add_with_viewport(self._controls)
+        self.attach(controlwindow, 0, 1, 1, 2, xoptions=0)
+
+        # timeline ruler
         self.ruler = ruler.ScaleRuler(self.hadj)
         self.ruler.set_size_request(0, 35)
         self.ruler.set_border_width(2)
-        self.pack_start(self.ruler, expand=False, fill=True)
+        self.attach(self.ruler, 1, 2, 0, 1, yoptions=0)
 
-        # List of TimelineCanvas
+        # proportional timeline
         self._canvas = TimelineCanvas(self.timeline)
-
-        self.scrolledWindow = gtk.ScrolledWindow(self.hadj)
-        self.scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self.scrolledWindow.add(self._canvas)
+        timelinewindow = gtk.ScrolledWindow(self.hadj)
+        timelinewindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        timelinewindow.add(self._canvas)
         #FIXME: remove padding between scrollbar and scrolled window
-        self.pack_start(self.scrolledWindow, expand=True)
+        self.attach(timelinewindow, 1, 2, 1, 2)
 
         # drag and drop
         self.drag_dest_set(gtk.DEST_DEFAULT_MOTION,
@@ -164,6 +173,17 @@ class Timeline(gtk.VBox, Loggable, Zoomable):
         self.ui_manager.insert_action_group(self.actiongroup, 0)
         self.ui_manager.add_ui_from_string(ui)
 
+        # drag and drop
+        self.drag_dest_set(gtk.DEST_DEFAULT_MOTION, 
+            [dnd.FILESOURCE_TUPLE],
+            gtk.gdk.ACTION_COPY)
+
+        self.connect("drag-data-received", self._dragDataReceivedCb)
+        self.connect("drag-leave", self._dragLeaveCb)
+        self.connect("drag-drop", self._dragDropCb)
+        self.connect("drag-motion", self._dragMotionCb)
+
+
 ## Drag and Drop callbacks
 
     def _dragMotionCb(self, unused, context, x, y, timestamp):
@@ -218,7 +238,9 @@ class Timeline(gtk.VBox, Loggable, Zoomable):
             for factory in self._factories]
 
     def _move_temp_source(self, x, y):
-        x, y = self._canvas.convert_from_pixels(x - 10, y)
+        x1, y1, x2, y2 = self._controls.get_allocation()
+        offset = 10 + (x2 - x1)
+        x, y = self._canvas.convert_from_pixels(x - offset, y)
         delta = Zoomable.pixelToNs(x)
         for obj in self._temp_objects:
             obj.setStart(max(0, delta), snap=True)
diff --git a/pitivi/ui/timelinecanvas.py b/pitivi/ui/timelinecanvas.py
index b42894d..e2ce9ec 100644
--- a/pitivi/ui/timelinecanvas.py
+++ b/pitivi/ui/timelinecanvas.py
@@ -27,6 +27,8 @@ from pitivi.ui.track import Track
 from pitivi.ui.trackobject import TrackObject
 from pitivi.ui.point import Point
 from pitivi.ui.zoominterface import Zoomable
+from common import TRACK_SPACING
+import gtk
 
 # cursors to be used for resizing objects
 ARROW = gtk.gdk.Cursor(gtk.gdk.ARROW)
@@ -227,9 +229,8 @@ class TimelineCanvas(goocanvas.Canvas, Zoomable):
         self._regroup_tracks()
 
     def _regroup_tracks(self):
+        height = 0
         for i, track in enumerate(self._tracks):
-            # FIXME: hard-coding track height, because this won't be updated
-            # later
-            height = 50
-            track.set_simple_transform(0, i * (height + 10), 1, 0)
+            track.set_simple_transform(0, height, 1, 0)
+            height += track.height + TRACK_SPACING
         self._request_size()
diff --git a/pitivi/ui/timelinecontrols.py b/pitivi/ui/timelinecontrols.py
new file mode 100644
index 0000000..81f0522
--- /dev/null
+++ b/pitivi/ui/timelinecontrols.py
@@ -0,0 +1,46 @@
+import gtk
+from pitivi.receiver import receiver, handler
+import pitivi.stream as stream
+from gettext import gettext as _
+from common import LAYER_HEIGHT_EXPANDED, LAYER_SPACING
+
+TRACK_CONTROL_WIDTH = 75
+
+def track_name(track):
+    stream_type = type(track.stream)
+    if stream_type == stream.AudioStream:
+        return _("<b>Audio:</b>")
+    elif stream_type == stream.VideoStream:
+        return _("<b>Video:</b>")
+    elif stream_type == stream.TextStream:
+        return _("<b>Text:</b>")
+
+class TrackControls(gtk.Expander):
+
+    def __init__(self, track):
+        gtk.Expander.__init__(self, track_name(track))
+        self.track = track
+        self.set_size_request(TRACK_CONTROL_WIDTH, LAYER_HEIGHT_EXPANDED)
+        self.tracks = {}
+
+class TimelineControls(gtk.HBox):
+
+    def __init__(self, timeline):
+        gtk.HBox.__init__(self)
+        self.timeline = timeline
+        self.set_size_request(TRACK_CONTROL_WIDTH, 50)
+        self.set_spacing(LAYER_SPACING)
+
+    timeline = receiver()
+
+    @handler(timeline, "track-added")
+    def _trackAdded(self, timeline, track):
+        tc = TrackControls(track)
+        self.pack_start(tc)
+        tc.show()
+        self.tracks[track] = tc
+
+    @handler(timeline, "track-removed")
+    def _trackRemoved(self, timeline, track):
+        self.remove(self.tracks[track])
+
diff --git a/pitivi/ui/track.py b/pitivi/ui/track.py
index 0833e41..75ed2d6 100644
--- a/pitivi/ui/track.py
+++ b/pitivi/ui/track.py
@@ -1,21 +1,32 @@
 from pitivi.ui.zoominterface import Zoomable
 from pitivi.ui.trackobject import TrackObject
 from pitivi.receiver import receiver, handler
+from common import LAYER_HEIGHT_EXPANDED, LAYER_SPACING
 import goocanvas
 
-# TODO: layer managment controls
-
 class Track(goocanvas.Group, Zoomable):
     __gtype_name__ = 'Track'
 
-    track = receiver()
-
     def __init__(self, track, timeline=None):
         goocanvas.Group.__init__(self)
         Zoomable.__init__(self)
         self.widgets = {}
         self.track = track
         self.timeline = timeline
+        self.max_priority = 0
+
+## Properties
+
+    def getHeight(self):
+        return (1 + self.max_priority) * (LAYER_HEIGHT_EXPANDED + LAYER_SPACING)
+
+    height = property(getHeight)
+
+## Public API
+
+## track signals
+
+    track = receiver()
 
     @handler(track, "track-object-added")
     def _objectAdded(self, unused_timeline, track_object):
diff --git a/pitivi/ui/trackobject.py b/pitivi/ui/trackobject.py
index 43c2315..15a0a3e 100644
--- a/pitivi/ui/trackobject.py
+++ b/pitivi/ui/trackobject.py
@@ -12,6 +12,9 @@ import controller
 from zoominterface import Zoomable
 from pitivi.timeline.track import TrackError
 from preview import Preview
+import gst
+from common import LAYER_HEIGHT_EXPANDED, LAYER_HEIGHT_COLLAPSED
+from common import LAYER_SPACING
 
 LEFT_SIDE = gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE)
 RIGHT_SIDE = gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE)
@@ -44,10 +47,6 @@ class TimelineController(controller.Controller):
     def drag_end(self):
         self._view.timeline.rebuildEdges()
 
-    def set_pos(self, item, pos):
-        self._view.element.setStart(max(self._view.pixelToNs(pos[0]), 0),
-                snap=True)
-
 class TrimHandle(View, goocanvas.Image, Zoomable):
 
     """A component of a TrackObject which manage's the source's edit
@@ -100,9 +99,6 @@ class EndHandle(TrimHandle):
 
 class TrackObject(View, goocanvas.Group, Zoomable):
 
-    element = receiver()
-
-    __HEIGHT__ = 50
     __BACKGROUND__ = 0x3182bdC0
     __BORDER__ = 0xffea00FF
 
@@ -121,6 +117,13 @@ class TrackObject(View, goocanvas.Group, Zoomable):
             self._view.timeline.setSelectionToObj(
                 self._view.element, mode)
 
+        def set_pos(self, item, pos):
+            x, y = pos
+            self._view.element.setStart(max(self._view.pixelToNs(x), 0),
+                    snap=True)
+            #priority = int(max(0, y // (LAYER_HEIGHT_EXPANDED + LAYER_SPACING)))
+            #self._view.element.priority = priority
+
     def __init__(self, element, track, timeline):
         goocanvas.Group.__init__(self)
         View.__init__(self)
@@ -131,7 +134,7 @@ class TrackObject(View, goocanvas.Group, Zoomable):
         self.timeline = timeline
 
         self.bg = goocanvas.Rect(
-            height=self.__HEIGHT__,
+            height=self.height, 
             fill_color_rgba=self.__BACKGROUND__,
             stroke_color_rgba=self.__BORDER__,
             line_width=0)
@@ -159,9 +162,9 @@ class TrackObject(View, goocanvas.Group, Zoomable):
         self.namewidth = twidth
 
         self.start_handle = StartHandle(element, timeline,
-            height=self.__HEIGHT__)
+            height=self.height)
         self.end_handle = EndHandle(element, timeline,
-            height=self.__HEIGHT__)
+            height=self.height)
 
         for thing in (self.bg, self.content, self.start_handle,
             self.end_handle, self.namebg, self.name):
@@ -171,6 +174,23 @@ class TrackObject(View, goocanvas.Group, Zoomable):
             self.zoomChanged()
         self.normal()
 
+## Properties
+
+    __height = LAYER_HEIGHT_EXPANDED
+
+    def setHeight(self, height):
+        self.__height
+        self.start_handle.props.height = height
+        self.end_handle.props.height = height
+        self.__update()
+
+    def getHeight(self):
+        return self.__height
+
+    height = property(getHeight, setHeight)
+
+## Public API
+
     def focus(self):
         self.start_handle.focus()
         self.end_handle.focus()
@@ -180,37 +200,49 @@ class TrackObject(View, goocanvas.Group, Zoomable):
         self.end_handle.unfocus()
 
     def zoomChanged(self):
-        self._startDurationChangedCb(self.element, self.element.start,
-            self.element.duration)
+        self.__update()
 
-    @handler(element, "start-changed")
-    def _startChangedCb(self, track_object, start):
-        self._startDurationChangedCb(track_object,
-                track_object.start, track_object.duration)
+    def expand(self):
+        self.content.props.visibility = goocanvas.ITEM_VISIBLE
+        self.height = LAYER_HEIGHT
+
+    def collapse(self):
+        self.content.props.visibility = goocanvas.ITEM_INVISIBLE
+
+## element signals
+
+    element = receiver()
 
+    @handler(element, "start-changed")
     @handler(element, "duration-changed")
-    def _startChangedCb(self, track_object, start):
-        self._startDurationChangedCb(track_object,
-                track_object.start, track_object.duration)
+    def startChangedCb(self, track_object, start):
+        self.__update()
+
+    @handler(element, "selected-changed")
+    def selected_changed(self, element, state):
+        if element.selected:
+            self.bg.props.line_width = 2.0
+        else:
+            self.bg.props.line_width = 0
 
-    def _startDurationChangedCb(self, obj, start, duration):
-        self.set_simple_transform(self.nsToPixel(start), 0, 1, 0)
-        width = self.nsToPixel(duration)
+    @handler(element, "priority-changed")
+    def priority_changed(self, element, priority):
+        self.__update()
+
+    def __update(self):
+        x = self.nsToPixel(self.element.start)
+        y = (LAYER_HEIGHT_EXPANDED + LAYER_SPACING) * self.element.priority
+        self.set_simple_transform(x, y, 1, 0)
+
+        width = self.nsToPixel(self.element.duration)
         w = width - self.end_handle.props.width
         self.name.props.clip_path = "M%g,%g h%g v%g h-%g z" % (
-            0, 0, w, self.__HEIGHT__, w)
+            0, 0, w, self.height, w)
         if w - 10 > 0:
             self.namebg.props.width = min(w - 8, self.namewidth)
             self.namebg.props.visibility = goocanvas.ITEM_VISIBLE
         else:
             self.namebg.props.visibility = goocanvas.ITEM_INVISIBLE
         self.bg.props.width = width
-        # place end handle at appropriate distance
         self.end_handle.props.x = w
 
-    @handler(element, "selected-changed")
-    def _selected_changed(self, element, state):
-        if element.selected:
-            self.bg.props.line_width = 2.0
-        else:
-            self.bg.props.line_width = 0



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]