~ubuntu-branches/ubuntu/lucid/pitivi/lucid

« back to all changes in this revision

Viewing changes to pitivi/ui/timeline.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Dröge
  • Date: 2009-05-27 14:22:49 UTC
  • mfrom: (1.2.1 upstream) (3.1.13 experimental)
  • Revision ID: james.westby@ubuntu.com-20090527142249-tj0qnkc37320ylml
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
1
# PiTiVi , Non-linear video editor
3
2
#
4
3
#       pitivi/ui/timeline.py
21
20
# Boston, MA 02111-1307, USA.
22
21
 
23
22
"""
24
 
Main timeline widget
 
23
Timeline widgets for the complex view
25
24
"""
26
25
 
27
26
import gtk
 
27
 
 
28
from pitivi.log.loggable import Loggable
 
29
import ruler
 
30
import dnd
 
31
import gst
28
32
import gobject
29
 
import gst
30
33
 
31
34
from gettext import gettext as _
32
 
 
33
 
import pitivi.instance as instance
34
 
 
35
 
from timelineobjects import SimpleTimeline
36
 
from complextimeline import ComplexTimelineWidget
37
 
 
38
 
class TimelineWidget(gtk.VBox):
39
 
    """ Widget for reprensenting Pitivi's Timeline """
40
 
 
41
 
    def __init__(self):
42
 
        gst.log("New Timeline Widget")
43
 
        gtk.VBox.__init__(self)
44
 
        self._createUi()
45
 
 
46
 
    def _createUi(self):
47
 
        """ draw the GUI """
48
 
        self.hadjustment = gtk.Adjustment()
49
 
        self.vadjustment = gtk.Adjustment()
50
 
 
51
 
        self.simpleview = SimpleTimelineContentWidget(self)
52
 
        self.complexview = ComplexTimelineWidget(self)
53
 
 
54
 
        self.simpleview.connect("scroll-event", self._simpleScrollCb)
55
 
        self.complexview.connect("scroll-event", self._simpleScrollCb)
56
 
 
57
 
        self.hscroll = gtk.HScrollbar(self.hadjustment)
58
 
        self.pack_end(self.hscroll, expand=False)
59
 
 
60
 
    def showSimpleView(self):
61
 
        """ Show the simple timeline """
62
 
        if self.complexview in self.get_children():
63
 
            self.remove(self.complexview)
64
 
            self.complexview.hide()
65
 
        self.pack_start(self.simpleview, expand=True)
66
 
        self.simpleview.show_all()
67
 
 
68
 
    def showComplexView(self):
69
 
        """ Show the advanced timeline """
70
 
        if self.simpleview in self.get_children():
71
 
            self.remove(self.simpleview)
72
 
            self.simpleview.hide()
73
 
        self.pack_start(self.complexview, expand=True)
74
 
        self.complexview.show_all()
75
 
 
76
 
    def _simpleScrollCb(self, unused_simplet, event):
77
 
        gst.debug("state:%s" % event.state)
78
 
        self.hscroll.emit("scroll-event", event)
79
 
 
80
 
class SimpleTimelineContentWidget(gtk.HBox):
81
 
    """ Widget for Simple Timeline content display """
82
 
    def __init__(self, twidget):
83
 
        """ init """
84
 
        self.twidget = twidget
85
 
        gtk.HBox.__init__(self)
86
 
        self._createUi()
87
 
        self.show_all()
88
 
 
89
 
    def _createUi(self):
90
 
        """ draw the GUI """
91
 
        self.timeline = SimpleTimeline(hadjustment = self.twidget.hadjustment)
92
 
 
93
 
        # real simple timeline
94
 
        self.layoutframe = gtk.Frame()
95
 
        self.layoutframe.add(self.timeline)
96
 
 
97
 
        # Explanatory message label
98
 
        txtbuffer = gtk.TextBuffer()
99
 
        txtbuffer.set_text(_("Add clips to the timeline by dragging them here."))
100
 
        txttag = gtk.TextTag()
101
 
        txttag.props.size = self.style.font_desc.get_size() * 1.5
102
 
        txtbuffer.tag_table.add(txttag)
103
 
        txtbuffer.apply_tag(txttag, txtbuffer.get_start_iter(),
104
 
                            txtbuffer.get_end_iter())
105
 
        self.messagewindow = gtk.TextView(txtbuffer)
106
 
        self.messagewindow.set_justification(gtk.JUSTIFY_CENTER)
107
 
        self.messagewindow.set_wrap_mode(gtk.WRAP_WORD)
108
 
        self.messagewindow.set_pixels_above_lines(30)
109
 
        self.messagewindow.set_cursor_visible(False)
110
 
        self.messagewindow.set_editable(False)
111
 
        self.messagewindow.set_left_margin(10)
112
 
        self.messagewindow.set_right_margin(10)
113
 
        self.messagewindow.set_size_request(-1, 120)
114
 
 
115
 
        self.messagewindow.add_events(gtk.gdk.ENTER_NOTIFY_MASK)
116
 
 
117
 
        # we start with showing the hint message
118
 
        self.pack_start(self.messagewindow)
119
 
        self.motionSigId = self.messagewindow.connect("drag-motion", self._dragMotionCb)
120
 
        self.showingTimeline = False
121
 
        self._displayTimeline()
122
 
 
123
 
    def _dragMotionCb(self, unused_layout, unused_context, x, unused_y,
124
 
                      unused_timestamp):
125
 
        gst.log("motion...")
126
 
        self.showingTimeline = False
127
 
        gobject.idle_add(self._displayTimeline)
128
 
 
129
 
    def _dragLeaveCb(self, unused_layout, unused_context, unused_timestamp):
130
 
        gst.log("leave...")
131
 
        if len(instance.PiTiVi.current.timeline.videocomp):
132
 
            return
133
 
        self.showingTimeline = True
134
 
        gobject.idle_add(self._displayTimeline, False)
135
 
 
136
 
    def _displayTimeline(self, displayed=True):
137
 
        if displayed:
138
 
            if self.showingTimeline:
139
 
                return
140
 
            gst.debug("displaying timeline")
141
 
            self.messagewindow.disconnect(self.motionSigId)
142
 
            self.motionSigId = None
143
 
            self.remove(self.messagewindow)
144
 
            self.messagewindow.hide()
145
 
            self.pack_start(self.layoutframe)
146
 
            self.reorder_child(self.layoutframe, 0)
147
 
            self.layoutframe.show_all()
148
 
            self.dragLeaveSigId = self.timeline.connect("drag-leave", self._dragLeaveCb)
149
 
            self.showingTimeline = True
150
 
        else:
151
 
            if not self.showingTimeline:
152
 
                return
153
 
            # only hide if there's nothing left in the timeline
154
 
            if not len(instance.PiTiVi.current.timeline.videocomp):
155
 
                gst.debug("hiding timeline")
156
 
                self.timeline.disconnect(self.dragLeaveSigId)
157
 
                self.dragLeaveSigId = None
158
 
                self.remove(self.layoutframe)
159
 
                self.layoutframe.hide()
160
 
                self.pack_start(self.messagewindow)
161
 
                self.reorder_child(self.messagewindow, 0)
162
 
                self.messagewindow.show()
163
 
                self.motionSigId = self.messagewindow.connect("drag-motion", self._dragMotionCb)
164
 
                self.showingTimeline = False
 
35
from timelinecanvas import TimelineCanvas
 
36
from timelinecontrols import TimelineControls
 
37
from pitivi.receiver import receiver, handler
 
38
from zoominterface import Zoomable
 
39
 
 
40
# tooltip text for toolbar
 
41
DELETE = _("Delete Selected")
 
42
RAZOR = _("Cut clip at mouse position")
 
43
ZOOM_IN =  _("Zoom In")
 
44
ZOOM_OUT =  _("Zoom Out")
 
45
UNLINK = _("Break links between clips")
 
46
LINK = _("Link together arbitrary clips")
 
47
UNGROUP = _("Ungroup clips")
 
48
GROUP = _("Group clips")
 
49
SELECT_BEFORE = ("Select all sources before selected")
 
50
SELECT_AFTER = ("Select all after selected")
 
51
 
 
52
ui = '''
 
53
<ui>
 
54
    <menubar name="MainMenuBar">
 
55
        <menu action="View">
 
56
            <placeholder name="Timeline">
 
57
                <menuitem action="ZoomIn" />
 
58
                <menuitem action="ZoomOut" />
 
59
            </placeholder>
 
60
        </menu>
 
61
        <menu action="Timeline">
 
62
            <placeholder name="Timeline">
 
63
                <menuitem action="Razor" />
 
64
                <separator />
 
65
                <menuitem action="DeleteObj" />
 
66
                <menuitem action="LinkObj" />
 
67
                <menuitem action="UnlinkObj" />
 
68
                <menuitem action="GroupObj" />
 
69
                <menuitem action="UngroupObj" />
 
70
            </placeholder>
 
71
        </menu>
 
72
    </menubar>
 
73
    <toolbar name="TimelineToolBar">
 
74
        <placeholder name="Timeline">
 
75
            <toolitem action="ZoomOut" />
 
76
            <toolitem action="ZoomIn" />
 
77
            <separator />
 
78
            <toolitem action="Razor" />
 
79
            <separator />
 
80
            <toolitem action="DeleteObj" />
 
81
            <toolitem action="UnlinkObj" />
 
82
            <toolitem action="LinkObj" />
 
83
            <toolitem action="GroupObj" />
 
84
            <toolitem action="UngroupObj" />
 
85
        </placeholder>
 
86
    </toolbar>
 
87
    <accelerator action="DeleteObj" />
 
88
</ui>
 
89
'''
 
90
 
 
91
# Complex Timeline Design v2 (08 Feb 2006)
 
92
#
 
93
#
 
94
# Tree of contents (ClassName(ParentClass))
 
95
# -----------------------------------------
 
96
#
 
97
# Timeline(gtk.VBox)
 
98
# |  Top container
 
99
# |
 
100
# +--ScaleRuler(gtk.Layout)
 
101
# |
 
102
# +--gtk.ScrolledWindow
 
103
#    |
 
104
#    +--TimelineCanvas(goocanas.Canvas)
 
105
#    |  |
 
106
#    |  +--Track(SmartGroup)
 
107
#    |
 
108
#    +--Status Bar ??
 
109
 
 
110
class Timeline(gtk.Table, Loggable, Zoomable):
 
111
 
 
112
    # the screen width of the current unit
 
113
    unit_width = 10
 
114
    # specific levels of zoom, in (multiplier, unit) pairs which
 
115
    # from zoomed out to zoomed in
 
116
 
 
117
 
 
118
    def __init__(self, instance, ui_manager):
 
119
        gtk.Table.__init__(self, rows=2, columns=1, homogeneous=False)
 
120
        Loggable.__init__(self)
 
121
        Zoomable.__init__(self)
 
122
        self.log("Creating Timeline")
 
123
 
 
124
        self.project = None
 
125
        self.ui_manager = ui_manager
 
126
        self.app = instance
 
127
        self._temp_objects = None
 
128
        self._factories = None
 
129
        self._finish_drag = False
 
130
        self._position = 0
 
131
        self._createUI()
 
132
        self._prev_duration = 0
 
133
        self.shrink = True
 
134
 
 
135
    def _createUI(self):
 
136
        self.leftSizeGroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
 
137
        self.hadj = gtk.Adjustment()
 
138
        self.vadj = gtk.Adjustment()
 
139
 
 
140
        # controls for tracks and layers
 
141
        self._controls = TimelineControls()
 
142
        self._controls.connect('track-expanded',
 
143
                self._timelineControlsTrackExpandedCb)
 
144
        controlwindow = gtk.ScrolledWindow(None, self.vadj)
 
145
        controlwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
 
146
        controlwindow.add_with_viewport(self._controls)
 
147
        self.attach(controlwindow, 0, 1, 1, 2, xoptions=0)
 
148
 
 
149
        # timeline ruler
 
150
        self.ruler = ruler.ScaleRuler(self.hadj)
 
151
        self.ruler.set_size_request(0, 35)
 
152
        self.ruler.set_border_width(2)
 
153
        self.ruler.connect("key-press-event", self._keyPressEventCb)
 
154
        self.attach(self.ruler, 1, 2, 0, 1, yoptions=0)
 
155
 
 
156
        # proportional timeline
 
157
        self._canvas = TimelineCanvas(self.app)
 
158
        timelinewindow = gtk.ScrolledWindow(self.hadj, self.vadj)
 
159
        timelinewindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
160
        timelinewindow.add(self._canvas)
 
161
        #FIXME: remove padding between scrollbar and scrolled window
 
162
        self.attach(timelinewindow, 1, 2, 1, 2)
 
163
 
 
164
        # drag and drop
 
165
        self.drag_dest_set(gtk.DEST_DEFAULT_MOTION,
 
166
            [dnd.FILESOURCE_TUPLE],
 
167
            gtk.gdk.ACTION_COPY)
 
168
 
 
169
        self.connect("drag-data-received", self._dragDataReceivedCb)
 
170
        self.connect("drag-leave", self._dragLeaveCb)
 
171
        self.connect("drag-drop", self._dragDropCb)
 
172
        self.connect("drag-motion", self._dragMotionCb)
 
173
 
 
174
        # toolbar actions
 
175
        actions = (
 
176
            ("ZoomIn", gtk.STOCK_ZOOM_IN, None, None, ZOOM_IN,
 
177
                self._zoomInCb),
 
178
            ("ZoomOut", gtk.STOCK_ZOOM_OUT, None, None, ZOOM_OUT,
 
179
                self._zoomOutCb),
 
180
        )
 
181
 
 
182
        selection_actions = (
 
183
            ("DeleteObj", gtk.STOCK_DELETE, None, "Delete", DELETE,
 
184
                self.deleteSelected),
 
185
            ("UnlinkObj", "pitivi-unlink", None, "<Shift><Control>L", UNLINK,
 
186
                self.unlinkSelected),
 
187
            ("LinkObj", "pitivi-link", None, "<Control>L", LINK,
 
188
                self.linkSelected),
 
189
            ("UngroupObj", "pitivi-ungroup", None, "<Shift><Control>G", UNGROUP,
 
190
                self.ungroupSelected),
 
191
            ("GroupObj", "pitivi-group", None, "<Control>G", GROUP,
 
192
                self.groupSelected),
 
193
        )
 
194
 
 
195
        toggle_actions = (
 
196
            ("Razor", "pitivi-split", _("Razor"), "<Ctrl>R", RAZOR,
 
197
                self.toggleRazor),
 
198
        )
 
199
 
 
200
        actiongroup = gtk.ActionGroup("timelinepermanent")
 
201
        actiongroup.add_actions(actions)
 
202
        actiongroup.add_toggle_actions(toggle_actions)
 
203
        self.ui_manager.insert_action_group(actiongroup, 0)
 
204
 
 
205
        actiongroup = gtk.ActionGroup("timelineselection")
 
206
        actiongroup.add_actions(selection_actions)
 
207
        self.link_action = actiongroup.get_action("LinkObj")
 
208
        self.unlink_action = actiongroup.get_action("UnlinkObj")
 
209
        self.group_action = actiongroup.get_action("GroupObj")
 
210
        self.ungroup_action = actiongroup.get_action("UngroupObj")
 
211
        self.delete_action = actiongroup.get_action("DeleteObj")
 
212
 
 
213
        self.ui_manager.insert_action_group(actiongroup, -1)
 
214
 
 
215
        self.ui_manager.add_ui_from_string(ui)
 
216
 
 
217
        # drag and drop
 
218
        self.drag_dest_set(gtk.DEST_DEFAULT_MOTION, 
 
219
            [dnd.FILESOURCE_TUPLE],
 
220
            gtk.gdk.ACTION_COPY)
 
221
 
 
222
        self.connect("drag-data-received", self._dragDataReceivedCb)
 
223
        self.connect("drag-leave", self._dragLeaveCb)
 
224
        self.connect("drag-drop", self._dragDropCb)
 
225
        self.connect("drag-motion", self._dragMotionCb)
 
226
        self._canvas.connect("button-press-event", self._buttonPress)
 
227
        self._canvas.connect("button-release-event", self._buttonRelease)
 
228
        self._canvas.connect("key-press-event", self._keyPressEventCb)
 
229
 
 
230
    def _timelineControlsTrackExpandedCb(self, timeline_controls,
 
231
            track, expanded):
 
232
        self._canvas.setExpanded(track, expanded)
 
233
 
 
234
 
 
235
## Event callbacks
 
236
 
 
237
    def _keyPressEventCb(self, unused_widget, event):
 
238
        kv = event.keyval
 
239
        self.debug("kv:%r", kv)
 
240
        if kv not in [gtk.keysyms.Left, gtk.keysyms.Right]:
 
241
            return False
 
242
        mod = event.get_state()
 
243
        try:
 
244
            if mod & gtk.gdk.CONTROL_MASK:
 
245
                now = self.project.pipeline.getPosition()
 
246
                ltime, rtime = self.project.timeline.edges.closest(now)
 
247
 
 
248
            if kv == gtk.keysyms.Left:
 
249
                if mod & gtk.gdk.SHIFT_MASK:
 
250
                    self.project.pipeline.seekRelative(-gst.SECOND)
 
251
                elif mod & gtk.gdk.CONTROL_MASK:
 
252
                    self.project.pipeline.seek(ltime+1)
 
253
                else:
 
254
                    self.project.pipeline.seekRelative(-long(self.rate * gst.SECOND))
 
255
            elif kv == gtk.keysyms.Right:
 
256
                if mod & gtk.gdk.SHIFT_MASK:
 
257
                    self.project.pipeline.seekRelative(gst.SECOND)
 
258
                elif mod & gtk.gdk.CONTROL_MASK:
 
259
                    self.project.pipeline.seek(rtime+1)
 
260
                else:
 
261
                    self.project.pipeline.seekRelative(long(self.rate * gst.SECOND))
 
262
        finally:
 
263
            return True
 
264
 
 
265
    def _buttonPress(self, window, event):
 
266
        self.shrink = False
 
267
 
 
268
    def _buttonRelease(self, window, event):
 
269
        self.shrink = True
 
270
        self._timelineStartDurationChanged(self.timeline,
 
271
            self.timeline.duration)
 
272
 
 
273
## Drag and Drop callbacks
 
274
 
 
275
    def _dragMotionCb(self, unused, context, x, y, timestamp):
 
276
        self.warning("self._factories:%r, self._temp_objects:%r",
 
277
                     not not self._factories,
 
278
                     not not self._temp_objects)
 
279
        if not self._factories:
 
280
            atom = gtk.gdk.atom_intern(dnd.FILESOURCE_TUPLE[0])
 
281
            self.drag_get_data(context, atom, timestamp)
 
282
            self.drag_highlight()
 
283
        else:
 
284
            # actual drag-and-drop
 
285
            if not self._temp_objects:
 
286
                self.timeline.disableUpdates()
 
287
                self._add_temp_source()
 
288
            self._move_temp_source(x, y)
 
289
        return True
 
290
 
 
291
    def _dragLeaveCb(self, unused_layout, unused_context, unused_tstamp):
 
292
        if self._temp_objects:
 
293
            try:
 
294
                for obj in self._temp_objects:
 
295
                    self.timeline.removeTimelineObject(obj, deep=True)
 
296
            finally:
 
297
                self._temp_objects = None
 
298
        self.drag_unhighlight()
 
299
        self.timeline.enableUpdates()
 
300
 
 
301
    def _dragDropCb(self, widget, context, x, y, timestamp):
 
302
        self._add_temp_source()
 
303
        self._move_temp_source(x, y)
 
304
        context.drop_finish(True, timestamp)
 
305
        self._factories = None
 
306
        self._temp_objects = None
 
307
        return True
 
308
 
 
309
    def _dragDataReceivedCb(self, unused_layout, context, x, y,
 
310
        selection, targetType, timestamp):
 
311
        self.log("SimpleTimeline, targetType:%d, selection.data:%s" %
 
312
            (targetType, selection.data))
 
313
        # FIXME: let's have just one target type, call it
 
314
        # TYPE_PITIVI_OBJECTFACTORY.
 
315
        # TODO: handle uri targets by doign an import-add. This would look
 
316
        # something like this:
 
317
        # tell current project to import the uri
 
318
        # wait for source-added signal, meanwhile ignore dragMotion signals
 
319
        # when ready, add factories to the timeline.
 
320
        if targetType == dnd.TYPE_PITIVI_FILESOURCE:
 
321
            uris = selection.data.split("\n")
 
322
        else:
 
323
            context.finish(False, False, timestamp)
 
324
        self._factories = [self.project.sources[uri] for uri in uris]
 
325
        context.drag_status(gtk.gdk.ACTION_COPY, timestamp)
 
326
        return True
 
327
 
 
328
    def _add_temp_source(self):
 
329
        self._temp_objects = [self.timeline.addSourceFactory(factory)
 
330
            for factory in self._factories]
 
331
 
 
332
    def _move_temp_source(self, x, y):
 
333
        x1, y1, x2, y2 = self._controls.get_allocation()
 
334
        offset = 10 + (x2 - x1)
 
335
        x, y = self._canvas.convert_from_pixels(x - offset, y)
 
336
        delta = Zoomable.pixelToNs(x)
 
337
        for obj in self._temp_objects:
 
338
            obj.setStart(max(0, delta), snap=True)
 
339
            delta += obj.duration
 
340
 
 
341
 
 
342
## Zooming and Scrolling
 
343
 
 
344
    def zoomChanged(self):
 
345
        # this has to be in a timeout, because the resize hasn't actually
 
346
        # completed yet, and so the canvas can't actually complete the scroll
 
347
        gobject.idle_add(self.scrollToPlayhead)
 
348
 
 
349
    def timelinePositionChanged(self, position):
 
350
        self._position = position
 
351
        self.ruler.timelinePositionChanged(position)
 
352
        self._canvas._position = position
 
353
        self.scrollToPlayhead()
 
354
 
 
355
    def scrollToPlayhead(self):
 
356
        width = self.get_allocation().width
 
357
        new_pos = Zoomable.nsToPixel(self._position)
 
358
        scroll_pos = self.hadj.get_value()
 
359
        if (new_pos < scroll_pos) or (new_pos > scroll_pos + width):
 
360
            self.scrollToPosition(new_pos - width / 2)
 
361
        return False
 
362
 
 
363
    def scrollToPosition(self, position):
 
364
        if position > self.hadj.upper:
 
365
            # we can't perform the scroll because the canvas needs to be
 
366
            # updated
 
367
            gobject.idle_add(self._scrollToPosition, position)
 
368
        else:
 
369
            self._scrollToPosition(position)
 
370
 
 
371
    def _scrollToPosition(self, position):
 
372
        self.hadj.set_value(position)
 
373
        return False
 
374
 
 
375
## Project callbacks
 
376
 
 
377
    def _setProject(self):
 
378
        if self.project:
 
379
            self.timeline = self.project.timeline
 
380
            self._controls.timeline = self.timeline
 
381
            self._canvas.timeline = self.timeline
 
382
            self._canvas.zoomChanged()
 
383
            self.ruler.zoomChanged()
 
384
            self._settingsChangedCb(self.project)
 
385
 
 
386
    project = receiver(_setProject)
 
387
 
 
388
    @handler(project, "settings-changed")
 
389
    def _settingsChangedCb(self, project):
 
390
        self.rate = float(1 / self.project.getSettings().videorate)
 
391
 
 
392
## Timeline callbacks
 
393
 
 
394
    def _setTimeline(self):
 
395
        if self.timeline:
 
396
            self._timelineSelectionChanged(self.timeline)
 
397
            self._timelineStartDurationChanged(self.timeline,
 
398
                self.timeline.duration)
 
399
 
 
400
        self._controls.timeline = self.timeline
 
401
 
 
402
    timeline = receiver(_setTimeline)
 
403
 
 
404
    @handler(timeline, "duration-changed")
 
405
    def _timelineStartDurationChanged(self, unused_timeline, duration):
 
406
        if self.shrink:
 
407
            self._prev_duration = duration
 
408
            self.ruler.setMaxDuration(duration)
 
409
            self._canvas.setMaxDuration(duration)
 
410
            self.ruler.setShadedDuration(duration)
 
411
        else:
 
412
            # only resize if new size is larger
 
413
            if duration > self._prev_duration:
 
414
                self._prev_duration = duration
 
415
                self.ruler.setMaxDuration(duration)
 
416
                self._canvas.setMaxDuration(duration)
 
417
                #self.ruler.setShadedDuration(duration)
 
418
 
 
419
    @handler(timeline, "selection-changed")
 
420
    def _timelineSelectionChanged(self, timeline):
 
421
        delete = False
 
422
        link = False
 
423
        unlink = False
 
424
        group = False
 
425
        ungroup = False
 
426
        timeline_objects = {}
 
427
        if timeline.selection:
 
428
            delete = True
 
429
            if len(timeline.selection) > 1:
 
430
                link = True
 
431
                group = True
 
432
 
 
433
            start = None
 
434
            duration = None
 
435
            for obj in self.timeline.selection:
 
436
                if obj.link:
 
437
                    unlink = True
 
438
 
 
439
                if len(obj.track_objects) > 1:
 
440
                    ungroup = True
 
441
 
 
442
                if start is not None and duration is not None:
 
443
                    if obj.start != start or obj.duration != duration:
 
444
                        group = False
 
445
                else:
 
446
                    start = obj.start
 
447
                    duration = obj.duration
 
448
 
 
449
        self.delete_action.set_sensitive(delete)
 
450
        self.link_action.set_sensitive(link)
 
451
        self.unlink_action.set_sensitive(unlink)
 
452
        self.group_action.set_sensitive(group)
 
453
        self.ungroup_action.set_sensitive(ungroup)
 
454
 
 
455
## ToolBar callbacks
 
456
 
 
457
    def hide(self):
 
458
        self.actiongroup.set_visible(False)
 
459
        gtk.Vbox.hide(self)
 
460
 
 
461
    def _computeZoomRatio(self, index):
 
462
        return self.zoom_levels[index]
 
463
 
 
464
    def _zoomInCb(self, unused_action):
 
465
        Zoomable.zoomIn()
 
466
 
 
467
    def _zoomOutCb(self, unused_action):
 
468
        Zoomable.zoomOut()
 
469
 
 
470
    def deleteSelected(self, unused_action):
 
471
        if self.timeline:
 
472
            self.timeline.deleteSelection()
 
473
 
 
474
    def unlinkSelected(self, unused_action):
 
475
        if self.timeline:
 
476
            self.timeline.unlinkSelection()
 
477
 
 
478
    def linkSelected(self, unused_action):
 
479
        if self.timeline:
 
480
            self.timeline.linkSelection()
 
481
 
 
482
    def ungroupSelected(self, unused_action):
 
483
        if self.timeline:
 
484
            self.timeline.ungroupSelection()
 
485
 
 
486
    def groupSelected(self, unused_action):
 
487
        if self.timeline:
 
488
            self.timeline.groupSelection()
 
489
 
 
490
    def toggleRazor(self, action):
 
491
        if action.props.active:
 
492
            self._canvas.activateRazor(action)
 
493
        else:
 
494
            self._canvas.deactivateRazor()