~timo-jyrinki/ubuntu/trusty/pitivi/backport_utopic_fixes

« back to all changes in this revision

Viewing changes to pitivi/ui/timeline.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2011-05-26 15:29:58 UTC
  • mfrom: (3.1.20 experimental)
  • Revision ID: james.westby@ubuntu.com-20110526152958-90je1myzzjly26vw
Tags: 0.13.9.90-1ubuntu1
* Resynchronize on Debian
* debian/control:
  - Depend on python-launchpad-integration
  - Drop hal from Recommends to Suggests. This version has the patch applied
    to not crash without hal.
* debian/patches/01_lpi.patch:
  - launchpad integration  
* debian/rules:
  - Use gnome.mk so a translation template is built

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
from timelinecontrols import TimelineControls
38
38
from pitivi.receiver import receiver, handler
39
39
from zoominterface import Zoomable
40
 
from pitivi.ui.common import LAYER_HEIGHT_EXPANDED, LAYER_SPACING
41
 
from pitivi.timeline.timeline import MoveContext
 
40
from pitivi.ui.common import LAYER_HEIGHT_EXPANDED, LAYER_SPACING, TRACK_SPACING
 
41
from pitivi.timeline.timeline import MoveContext, SELECT
42
42
from pitivi.utils import Seeker
43
43
from pitivi.ui.filelisterrordialog import FileListErrorDialog
44
44
from pitivi.ui.curve import Curve
 
45
from pitivi.ui.common import SPACING
 
46
 
 
47
from pitivi.factories.operation import EffectFactory
 
48
 
 
49
DND_EFFECT_LIST = [[dnd.VIDEO_EFFECT_TUPLE[0], dnd.EFFECT_TUPLE[0]],\
 
50
                  [dnd.AUDIO_EFFECT_TUPLE[0], dnd.EFFECT_TUPLE[0]]]
 
51
VIDEO_EFFECT_LIST = [dnd.VIDEO_EFFECT_TUPLE[0], dnd.EFFECT_TUPLE[0]],
 
52
AUDIO_EFFECT_LIST = [dnd.AUDIO_EFFECT_TUPLE[0], dnd.EFFECT_TUPLE[0]],
45
53
 
46
54
# tooltip text for toolbar
47
55
DELETE = _("Delete Selected")
48
56
SPLIT = _("Split clip at playhead position")
49
 
KEYFRAME = _("Create a keyframe")
 
57
KEYFRAME = _("Add a keyframe")
 
58
PREVFRAME = _("Move to the previous keyframe")
 
59
NEXTFRAME = _("Move to the next keyframe")
50
60
ZOOM_IN =  _("Zoom In")
51
61
ZOOM_OUT =  _("Zoom Out")
 
62
ZOOM_FIT = _("Zoom Fit")
52
63
UNLINK = _("Break links between clips")
53
64
LINK = _("Link together arbitrary clips")
54
65
UNGROUP = _("Ungroup clips")
63
74
            <placeholder name="Timeline">
64
75
                <menuitem action="ZoomIn" />
65
76
                <menuitem action="ZoomOut" />
 
77
                <menuitem action="ZoomFit" />
66
78
            </placeholder>
67
79
        </menu>
68
80
        <menu action="Timeline">
75
87
                <menuitem action="UnlinkObj" />
76
88
                <menuitem action="GroupObj" />
77
89
                <menuitem action="UngroupObj" />
 
90
                <separator />
 
91
                <menuitem action="Prevframe" />
 
92
                <menuitem action="Nextframe" />
78
93
            </placeholder>
79
94
        </menu>
80
95
    </menubar>
127
142
        Loggable.__init__(self)
128
143
        self.errors = []
129
144
        self.showing = False
 
145
        self._scroll_pos_ns = 0
130
146
        self._errorsmessage = _("One or more GStreamer errors has occured!")
131
147
        self._makeUI()
132
148
 
133
149
    def _makeUI(self):
134
 
        self.set_spacing(6)
 
150
        self.set_spacing(SPACING)
135
151
        self.erroricon = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING,
136
152
                                                  gtk.ICON_SIZE_SMALL_TOOLBAR)
137
153
 
191
207
    # specific levels of zoom, in (multiplier, unit) pairs which
192
208
    # from zoomed out to zoomed in
193
209
 
194
 
 
195
210
    def __init__(self, instance, ui_manager):
196
211
        gtk.Table.__init__(self, rows=2, columns=1, homogeneous=False)
197
212
        Loggable.__init__(self)
209
224
        self._state = gst.STATE_NULL
210
225
        self._createUI()
211
226
        self._prev_duration = 0
212
 
        self.shrink = True
213
227
        self.rate = gst.Fraction(1,1)
214
228
 
215
229
    def _createUI(self):
219
233
        self.hadj = gtk.Adjustment()
220
234
        self.vadj = gtk.Adjustment()
221
235
 
 
236
        # zooming slider's "zoom fit" button
 
237
        zoom_controls_hbox = gtk.HBox()
 
238
        zoom_best_fit_button = gtk.Button(_("Zoom"))
 
239
        zoom_best_fit_button.set_relief(gtk.RELIEF_NONE)
 
240
        zoom_best_fit_button.set_tooltip_text(ZOOM_FIT)
 
241
        zoom_best_fit_button.set_image(gtk.image_new_from_stock(gtk.STOCK_ZOOM_FIT, gtk.ICON_SIZE_BUTTON))
 
242
        zoom_best_fit_button.connect("clicked", self._zoomFitCb)
 
243
        zoom_controls_hbox.pack_start(zoom_best_fit_button)
222
244
        # zooming slider
223
245
        self._zoomAdjustment = gtk.Adjustment()
224
246
        self._zoomAdjustment.set_value(Zoomable.getCurrentZoomLevel())
229
251
        zoomslider = gtk.HScale(self._zoomAdjustment)
230
252
        zoomslider.props.draw_value = False
231
253
        zoomslider.set_tooltip_text(_("Zoom Timeline"))
232
 
        self.attach(zoomslider, 0, 1, 0, 1, yoptions=0, xoptions=gtk.FILL)
 
254
        zoomslider.connect("scroll-event", self._zoomSliderScrollCb)
 
255
        zoomslider.set_size_request(100, 0) # At least 100px wide for precision
 
256
        zoom_controls_hbox.pack_start(zoomslider)
 
257
        self.attach(zoom_controls_hbox, 0, 1, 0, 1, yoptions=0, xoptions=gtk.FILL)
233
258
 
234
259
        # controls for tracks and layers
235
260
        self._controls = TimelineControls()
237
262
        controlwindow.add(self._controls)
238
263
        controlwindow.set_size_request(-1, 1)
239
264
        controlwindow.set_shadow_type(gtk.SHADOW_OUT)
240
 
        self.attach(controlwindow, 0, 1, 1, 2, xoptions=0)
 
265
        self.attach(controlwindow, 0, 1, 1, 2, xoptions=gtk.FILL)
241
266
 
242
267
        # timeline ruler
243
268
        self.ruler = ruler.ScaleRuler(self.app, self.hadj)
244
269
        self.ruler.set_size_request(0, 25)
245
 
        self.ruler.set_border_width(2)
 
270
        #self.ruler.set_border_width(2)
246
271
        self.ruler.connect("key-press-event", self._keyPressEventCb)
247
272
        self.ruler.connect("size-allocate", self._rulerSizeAllocateCb)
248
273
        rulerframe = gtk.Frame()
254
279
        self._canvas = TimelineCanvas(self.app)
255
280
        self._root_item = self._canvas.get_root_item()
256
281
        self.attach(self._canvas, 1, 2, 1, 2)
 
282
        self.vadj.connect("changed", self._unsureVadjHeightCb)
257
283
 
258
284
        # scrollbar
259
285
        self._hscrollbar = gtk.HScrollbar(self.hadj)
276
302
                self._zoomInCb),
277
303
            ("ZoomOut", gtk.STOCK_ZOOM_OUT, None, "<Control>minus", ZOOM_OUT,
278
304
                self._zoomOutCb),
 
305
            ("ZoomFit", gtk.STOCK_ZOOM_FIT, None, None, ZOOM_FIT,
 
306
                self._zoomFitCb),
279
307
 
280
308
            # actions for adding additional accelerators
281
309
            ("ControlEqualAccel", gtk.STOCK_ZOOM_IN, None, "<Control>equal", ZOOM_IN,
299
327
                self.groupSelected),
300
328
        )
301
329
 
302
 
        playhead_actions = (
 
330
        self.playhead_actions = (
303
331
            ("Split", "pitivi-split", _("Split"), "S", SPLIT,
304
332
                self.split),
305
 
            ("Keyframe", "pitivi-keyframe", _("Keyframe"), "K", KEYFRAME,
 
333
            ("Keyframe", "pitivi-keyframe", _("Add a keyframe"), "K", KEYFRAME,
306
334
                self.keyframe),
 
335
            ("Prevframe", "pitivi-prevframe", _("_Previous keyframe"), "E", PREVFRAME,
 
336
                self.prevframe),
 
337
            ("Nextframe", "pitivi-nextframe", _("_Next keyframe"), "R", NEXTFRAME,
 
338
                self.nextframe),
307
339
        )
308
340
 
309
341
        actiongroup = gtk.ActionGroup("timelinepermanent")
312
344
 
313
345
        actiongroup = gtk.ActionGroup("timelineselection")
314
346
        actiongroup.add_actions(selection_actions)
315
 
        actiongroup.add_actions(playhead_actions)
 
347
        actiongroup.add_actions(self.playhead_actions)
316
348
        self.link_action = actiongroup.get_action("LinkObj")
317
349
        self.unlink_action = actiongroup.get_action("UnlinkObj")
318
350
        self.group_action = actiongroup.get_action("GroupObj")
320
352
        self.delete_action = actiongroup.get_action("DeleteObj")
321
353
        self.split_action = actiongroup.get_action("Split")
322
354
        self.keyframe_action = actiongroup.get_action("Keyframe")
 
355
        self.prevframe_action = actiongroup.get_action("Prevframe")
 
356
        self.nextframe_action = actiongroup.get_action("Nextframe")
323
357
 
324
358
        self.ui_manager.insert_action_group(actiongroup, -1)
325
359
 
326
360
        self.ui_manager.add_ui_from_string(ui)
327
361
 
328
362
        # drag and drop
329
 
        self.drag_dest_set(gtk.DEST_DEFAULT_MOTION, 
330
 
            [dnd.FILESOURCE_TUPLE],
 
363
        self.drag_dest_set(gtk.DEST_DEFAULT_MOTION,
 
364
            [dnd.FILESOURCE_TUPLE, dnd.EFFECT_TUPLE],
331
365
            gtk.gdk.ACTION_COPY)
332
366
 
333
367
        self.connect("drag-data-received", self._dragDataReceivedCb)
334
368
        self.connect("drag-leave", self._dragLeaveCb)
335
369
        self.connect("drag-drop", self._dragDropCb)
336
370
        self.connect("drag-motion", self._dragMotionCb)
337
 
        self._canvas.connect("button-press-event", self._buttonPress)
338
 
        self._canvas.connect("button-release-event", self._buttonRelease)
339
371
        self._canvas.connect("key-press-event", self._keyPressEventCb)
 
372
        self._canvas.connect("scroll-event", self._scrollEventCb)
340
373
 
341
374
 
342
375
## Event callbacks
375
408
            pipeline.getDuration()))
376
409
        self._seeker.seek(seekvalue)
377
410
 
378
 
    def _buttonPress(self, window, event):
379
 
        self.shrink = False
380
 
 
381
 
    def _buttonRelease(self, window, event):
382
 
        self.shrink = True
383
 
        self._timelineStartDurationChanged(self.timeline,
384
 
            self.timeline.duration)
385
 
 
386
411
## Drag and Drop callbacks
387
412
 
388
413
    def _dragMotionCb(self, unused, context, x, y, timestamp):
389
414
        self.warning("self._factories:%r, self._temp_objects:%r",
390
415
                     not not self._factories,
391
416
                     not not self._temp_objects)
 
417
 
392
418
        if self._factories is None:
393
 
            atom = gtk.gdk.atom_intern(dnd.FILESOURCE_TUPLE[0])
 
419
            if  context.targets in DND_EFFECT_LIST:
 
420
                atom = gtk.gdk.atom_intern(dnd.EFFECT_TUPLE[0])
 
421
            else:
 
422
                atom = gtk.gdk.atom_intern(dnd.FILESOURCE_TUPLE[0])
 
423
 
394
424
            self.drag_get_data(context, atom, timestamp)
395
425
            self.drag_highlight()
396
426
        else:
397
 
            # actual drag-and-drop
398
 
            if not self._temp_objects:
399
 
                self.timeline.disableUpdates()
400
 
                self._add_temp_source()
401
 
                focus = self._temp_objects[0]
402
 
                self._move_context = MoveContext(self.timeline,
403
 
                        focus, set(self._temp_objects[1:]))
404
 
            self._move_temp_source(self.hadj.props.value + x, y)
 
427
            if  context.targets not in DND_EFFECT_LIST:
 
428
                if not self._temp_objects:
 
429
                    self.timeline.disableUpdates()
 
430
                    self._add_temp_source()
 
431
                    focus = self._temp_objects[0]
 
432
                    self._move_context = MoveContext(self.timeline,
 
433
                            focus, set(self._temp_objects[1:]))
 
434
                self._move_temp_source(self.hadj.props.value + x, y)
405
435
        return True
406
436
 
407
 
    def _dragLeaveCb(self, unused_layout, unused_context, unused_tstamp):
 
437
    def _dragLeaveCb(self, unused_layout, context, unused_tstamp):
408
438
        if self._temp_objects:
409
439
            try:
410
440
                for obj in self._temp_objects:
411
441
                    self.timeline.removeTimelineObject(obj, deep=True)
412
442
            finally:
413
443
                self._temp_objects = None
 
444
 
414
445
        self.drag_unhighlight()
415
446
        self.timeline.enableUpdates()
416
447
 
417
448
    def _dragDropCb(self, widget, context, x, y, timestamp):
418
 
        self.app.action_log.begin("add clip")
419
 
        self.timeline.disableUpdates()
420
 
        self._add_temp_source()
421
 
        focus = self._temp_objects[0]
422
 
        self._move_context = MoveContext(self.timeline,
423
 
                focus, set(self._temp_objects[1:]))
424
 
        self._move_temp_source(self.hadj.props.value + x, y)
425
 
        self._move_context.finish()
426
 
        self.timeline.enableUpdates()
427
 
        self.app.action_log.commit()
428
 
        context.drop_finish(True, timestamp)
429
 
        self._factories = None
430
 
        self._temp_objects = None
431
 
        self.app.current.seeker.seek(self._position)
432
 
        return True
 
449
        if  context.targets not in DND_EFFECT_LIST:
 
450
            self.app.action_log.begin("add clip")
 
451
            self.timeline.disableUpdates()
 
452
 
 
453
            self._add_temp_source()
 
454
            self.timeline.selection.setSelection(self._temp_objects, SELECT)
 
455
            focus = self._temp_objects[0]
 
456
            self._move_context = MoveContext(self.timeline,
 
457
                                             focus, set(self._temp_objects[1:]))
 
458
            self._move_temp_source(self.hadj.props.value + x, y)
 
459
            self._move_context.finish()
 
460
            self.app.action_log.commit()
 
461
            context.drop_finish(True, timestamp)
 
462
            self._factories = None
 
463
            self._temp_objects = None
 
464
            self.app.current.seeker.seek(self._position)
 
465
 
 
466
            return True
 
467
        elif context.targets in DND_EFFECT_LIST:
 
468
            if not self.timeline.timeline_objects:
 
469
                return False
 
470
            factory = self._factories[0]
 
471
            timeline_objs = self._getTimelineObjectUnderMouse(x, y, factory.getInputStreams()[0])
 
472
            if timeline_objs:
 
473
                self.app.action_log.begin("add effect")
 
474
                self.timeline.addEffectFactoryOnObject(factory,
 
475
                                               timeline_objects = timeline_objs)
 
476
                self.app.action_log.commit()
 
477
                self._factories = None
 
478
                self.app.current.seeker.seek(self._position)
 
479
                context.drop_finish(True, timestamp)
 
480
 
 
481
                self.timeline.selection.setSelection(timeline_objs, SELECT)
 
482
 
 
483
            return True
 
484
 
 
485
        return False
433
486
 
434
487
    def _dragDataReceivedCb(self, unused_layout, context, x, y,
435
488
        selection, targetType, timestamp):
442
495
        # tell current project to import the uri
443
496
        # wait for source-added signal, meanwhile ignore dragMotion signals
444
497
        # when ready, add factories to the timeline.
445
 
        if targetType != dnd.TYPE_PITIVI_FILESOURCE:
 
498
 
 
499
        if targetType not in [dnd.TYPE_PITIVI_FILESOURCE, dnd.TYPE_PITIVI_EFFECT]:
446
500
            context.finish(False, False, timestamp)
447
501
            return
448
502
 
449
 
        uris = selection.data.split("\n")
450
 
        self._factories = [self.project.sources.getUri(uri) for uri in uris]
 
503
        if targetType == dnd.TYPE_PITIVI_FILESOURCE:
 
504
            uris = selection.data.split("\n")
 
505
            self._factories = [self.project.sources.getUri(uri) for uri in uris]
 
506
        else:
 
507
            if not self.timeline.timeline_objects:
 
508
                return False
 
509
            self._factories = [self.app.effects.getFactoryFromName(selection.data)]
 
510
 
451
511
        context.drag_status(gtk.gdk.ACTION_COPY, timestamp)
452
512
        return True
453
513
 
 
514
    def _getTimelineObjectUnderMouse(self, x, y, stream):
 
515
        timeline_objs = []
 
516
        items_in_area = self._canvas.getItemsInArea(x, y-15, x+1, y-30)
 
517
        tracks = [obj for obj in items_in_area[0]]
 
518
        track_objects = [obj for obj in items_in_area[1]]
 
519
        for track_object in track_objects:
 
520
            if (type(stream) == type(track_object.stream)):
 
521
                timeline_objs.append(track_object.timeline_object)
 
522
 
 
523
        return timeline_objs
 
524
 
454
525
    def _add_temp_source(self):
455
526
        self._temp_objects = [self.timeline.addSourceFactory(factory)
456
527
            for factory in self._factories]
465
536
 
466
537
## Zooming and Scrolling
467
538
 
 
539
    def _scrollEventCb(self, canvas, event):
 
540
        if event.state & gtk.gdk.SHIFT_MASK:
 
541
            # shift + scroll => vertical (up/down) scroll
 
542
            if event.direction == gtk.gdk.SCROLL_UP:
 
543
                self.scroll_up()
 
544
            elif event.direction == gtk.gdk.SCROLL_DOWN:
 
545
                self.scroll_down()
 
546
            event.state &= ~gtk.gdk.SHIFT_MASK
 
547
        elif event.state & gtk.gdk.CONTROL_MASK:
 
548
            # zoom + scroll => zooming (up: zoom in)
 
549
            if event.direction == gtk.gdk.SCROLL_UP:
 
550
                Zoomable.zoomIn()
 
551
                return True
 
552
            elif event.direction == gtk.gdk.SCROLL_DOWN:
 
553
                Zoomable.zoomOut()
 
554
                return True
 
555
            return False
 
556
        else:
 
557
            if event.direction == gtk.gdk.SCROLL_UP:
 
558
                self.scroll_left()
 
559
            elif event.direction == gtk.gdk.SCROLL_DOWN:
 
560
                self.scroll_right()
 
561
        return True
 
562
 
 
563
    def scroll_left(self):
 
564
        self._hscrollbar.set_value (self._hscrollbar.get_value() -
 
565
            self.hadj.props.page_size ** (2.0 / 3.0))
 
566
 
 
567
    def scroll_right(self):
 
568
        self._hscrollbar.set_value (self._hscrollbar.get_value() +
 
569
            self.hadj.props.page_size ** (2.0 / 3.0))
 
570
 
 
571
    def scroll_up(self):
 
572
        self._vscrollbar.set_value (self._vscrollbar.get_value() -
 
573
            self.vadj.props.page_size ** (2.0 / 3.0))
 
574
 
 
575
    def scroll_down(self):
 
576
        self._vscrollbar.set_value (self._vscrollbar.get_value() +
 
577
            self.vadj.props.page_size ** (2.0 / 3.0))
 
578
 
468
579
    def _updateScrollPosition(self, adjustment):
469
 
        self._root_item.set_simple_transform( -self.hadj.get_value(), 
 
580
        self._scroll_pos_ns = Zoomable.pixelToNs(self.hadj.get_value())
 
581
        self._root_item.set_simple_transform( -self.hadj.get_value(),
470
582
            -self.vadj.get_value(), 1.0, 0)
471
583
 
472
584
    def _zoomAdjustmentChangedCb(self, adjustment):
475
587
        Zoomable.setZoomLevel(int(adjustment.get_value()))
476
588
        self._updateZoom = True
477
589
 
 
590
    def _unsureVadjHeightCb(self, adj):
 
591
        # GTK crack, without that, at loading a project, the vadj upper
 
592
        # property is reset to be equal as the lower, right after the
 
593
        # trackobjects are added to the timeline (bug: #648714)
 
594
        if self.vadj.props.upper < self._canvas.height:
 
595
            self.vadj.props.upper = self._canvas.height
 
596
 
 
597
    _scroll_pos_ns = 0
 
598
 
 
599
    def _zoomSliderScrollCb(self, unused_widget, event):
 
600
        value = self._zoomAdjustment.get_value()
 
601
        if event.direction in [gtk.gdk.SCROLL_UP, gtk.gdk.SCROLL_RIGHT]:
 
602
            self._zoomAdjustment.set_value(value + 1)
 
603
        elif event.direction in [gtk.gdk.SCROLL_DOWN, gtk.gdk.SCROLL_LEFT]:
 
604
            self._zoomAdjustment.set_value(value - 1)
 
605
 
478
606
    def zoomChanged(self):
479
 
        self._canvas.props.redraw_when_scrolled = True
480
607
        if self._updateZoom:
481
608
            self._zoomAdjustment.set_value(self.getCurrentZoomLevel())
 
609
 
 
610
        # the new scroll position should preserve the current horizontal
 
611
        # position of the playhead in the window
 
612
        cur_playhead_offset = self._canvas._playhead.props.x -\
 
613
            self.hadj.props.value
 
614
        new_pos = Zoomable.nsToPixel(self._position) - cur_playhead_offset
 
615
 
 
616
 
 
617
        self._updateScrollAdjustments()
 
618
        self._scrollToPosition(new_pos)
482
619
        self.ruler.queue_resize()
483
620
        self.ruler.queue_draw()
484
621
 
494
631
 
495
632
    def scrollToPlayhead(self):
496
633
        """
497
 
        Scroll the current position as close to the center of the view
498
 
        as possible (as close as the timeline canvas allows).
 
634
        If the current position is out of the view bouds, then scroll
 
635
        as close to the center of the view as possible or as close as the
 
636
        timeline canvas allows.
499
637
        """
500
638
        page_size = self.hadj.get_page_size()
501
639
 
514
652
            self._scrollToPosition(position)
515
653
 
516
654
    def _scrollToPosition(self, position):
517
 
        self.hadj.set_value(position)
 
655
        self._hscrollbar.set_value(position)
518
656
        return False
519
657
 
520
658
    def _rulerSizeAllocateCb(self, ruler, allocation):
555
693
 
556
694
    @handler(timeline, "duration-changed")
557
695
    def _timelineStartDurationChanged(self, unused_timeline, duration):
558
 
        if self.shrink:
559
 
            self._prev_duration = duration
560
 
            self.ruler.setMaxDuration(duration + 60 * gst.SECOND)
561
 
            self._canvas.setMaxDuration(duration + 60 * gst.SECOND)
562
 
            self.ruler.setShadedDuration(duration)
563
 
        else:
564
 
            # only resize if new size is larger
565
 
            if duration > self._prev_duration:
566
 
                self._prev_duration = duration
567
 
                self.ruler.setMaxDuration(duration)
568
 
                self._canvas.setMaxDuration(duration)
569
 
                #self.ruler.setShadedDuration(duration)
 
696
        self._prev_duration = duration
 
697
        self.ruler.setMaxDuration(duration + 60 * gst.SECOND)
 
698
        self._canvas.setMaxDuration(duration + 60 * gst.SECOND)
 
699
        self.ruler.setShadedDuration(duration)
 
700
        self._updateScrollAdjustments()
 
701
 
 
702
    def _updateScrollAdjustments(self):
 
703
        a = self.get_allocation()
 
704
        size = Zoomable.nsToPixel(self.timeline.duration)
 
705
        self.hadj.props.lower = 0
 
706
        self.hadj.props.upper = size + 200 # why is this necessary???
 
707
        self.hadj.props.page_size = a.width
 
708
        self.hadj.props.page_increment = size * 0.9
 
709
        self.hadj.props.step_increment = size * 0.1
 
710
 
570
711
 
571
712
    @handler(timeline, "selection-changed")
572
713
    def _timelineSelectionChanged(self, timeline):
577
718
        ungroup = False
578
719
        split = False
579
720
        keyframe = False
580
 
        timeline_objects = {}
581
721
        if timeline.selection:
582
722
            delete = True
583
723
            if len(timeline.selection) > 1:
601
741
                    start = obj.start
602
742
                    duration = obj.duration
603
743
 
 
744
            keyframe = True
 
745
 
 
746
        if (len (timeline.timeline_objects) > 0):
604
747
            split = True
605
 
            keyframe = True
606
748
 
607
749
        self.delete_action.set_sensitive(delete)
608
750
        self.link_action.set_sensitive(link)
619
761
        self.actiongroup.set_visible(False)
620
762
        gtk.Vbox.hide(self)
621
763
 
 
764
    def _zoomFitCb(self, unused_action):
 
765
        self.app.gui.setBestZoomRatio()
 
766
 
622
767
    def _zoomInCb(self, unused_action):
623
768
        Zoomable.zoomIn()
624
769
 
661
806
    def keyframe(self, action):
662
807
        timeline_position = self._position
663
808
        selected = self.timeline.selection.getSelectedTrackObjs()
664
 
        
 
809
 
665
810
        for obj in selected:
666
811
            keyframe_exists = False
667
812
 
668
 
            position_in_obj = timeline_position - obj.start
 
813
            position_in_obj = (timeline_position - obj.start) + obj.in_point
669
814
            interpolators = obj.getInterpolators()
670
815
            for value in interpolators:
671
816
                interpolator = obj.getInterpolator(value)
680
825
                    self.app.action_log.begin("add volume point")
681
826
                    interpolator.newKeyframe(position_in_obj)
682
827
                    self.app.action_log.commit()
 
828
 
 
829
    def prevframe(self, action):
 
830
        timeline_position = self._position
 
831
 
 
832
        prev_kf = self.timeline.getPrevKeyframe(timeline_position)
 
833
        if prev_kf != None:
 
834
            self._seeker.seek(prev_kf)
 
835
            self.scrollToPlayhead()
 
836
 
 
837
    def nextframe(self, action):
 
838
        timeline_position = self._position
 
839
 
 
840
        next_kf = self.timeline.getNextKeyframe(timeline_position)
 
841
        if next_kf:
 
842
            self._seeker.seek(next_kf)
 
843
            self.scrollToPlayhead()