[pitivi: 2/8] ui/timeline: move to the next/prev keyframe with 'E'/'R' keys or menu buttons.



commit efa26eb0eec9104a137870a59bdb8905d2c519ff
Author: Luis de Bethencourt <luis debethencourt com>
Date:   Mon Sep 20 19:20:53 2010 +0200

    ui/timeline: move to the next/prev keyframe with 'E'/'R' keys or menu buttons.

 pitivi/timeline/timeline.py |   55 +++++++++++++++++++++++++++
 pitivi/ui/timeline.py       |   29 ++++++++++++++-
 tests/test_timeline.py      |   87 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletions(-)
---
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index e16d7b0..48f938f 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -1954,3 +1954,58 @@ class Timeline(Signallable, Loggable):
                 break
         return objects
 
+    def getPrevKeyframe(self, time):
+        tl_objs = []
+
+        # Exclude objects that start after current position.
+        for obj in self.timeline_objects:
+                tl_objs.append(obj)
+                if obj.start > time:
+                    break
+
+        keyframe_positions = self._getKeyframePositions(tl_objs)
+        for n in range(len(keyframe_positions) -1, -1, -1):
+            if keyframe_positions[n] < time:
+                return keyframe_positions[n]
+        return None
+
+    def getNextKeyframe(self, time):
+        tl_objs = []
+
+        # Include from first object whose end is after the current
+        # position onward.
+        for obj in self.timeline_objects:
+            if (obj.start + obj.duration) > time:
+                n = self.timeline_objects.index(obj)
+                tl_objs.extend(self.timeline_objects[n:])
+                break
+
+        keyframe_positions = self._getKeyframePositions(tl_objs)
+        for n in range(0, len(keyframe_positions)):
+                if keyframe_positions[n] > time:
+                    return keyframe_positions[n]
+
+        return None
+
+    def _getKeyframePositions(self, timeline_objects):
+        keyframe_positions = set([])
+        for tl_obj in timeline_objects:
+            first_track = True
+            for track_obj in tl_obj.track_objects:
+                start = track_obj.start
+                in_point = track_obj.in_point
+                if first_track:
+                    keyframe_positions.add(start)
+                    keyframe_positions.add(start + track_obj.duration)
+                    first_track = False
+
+                interpolators = track_obj.getInterpolators()
+                for value in interpolators:
+                    interpolator = track_obj.getInterpolator(value)
+                    keyframes = interpolator.getInteriorKeyframes()
+                    for kf in keyframes:
+                        position_in_obj = kf.getTime() + start - in_point
+                        keyframe_positions.add(position_in_obj)
+        keyframe_positions = list(keyframe_positions)
+        keyframe_positions.sort()
+        return keyframe_positions
diff --git a/pitivi/ui/timeline.py b/pitivi/ui/timeline.py
index 1a6a8ab..6b8ab2b 100644
--- a/pitivi/ui/timeline.py
+++ b/pitivi/ui/timeline.py
@@ -47,6 +47,8 @@ from pitivi.ui.curve import Curve
 DELETE = _("Delete Selected")
 SPLIT = _("Split clip at playhead position")
 KEYFRAME = _("Add a keyframe")
+PREVFRAME = _("Move to the previous keyframe")
+NEXTFRAME = _("Move to the next keyframe")
 ZOOM_IN =  _("Zoom In")
 ZOOM_OUT =  _("Zoom Out")
 UNLINK = _("Break links between clips")
@@ -75,6 +77,9 @@ ui = '''
                 <menuitem action="UnlinkObj" />
                 <menuitem action="GroupObj" />
                 <menuitem action="UngroupObj" />
+                <separator />
+                <menuitem action="Prevframe" />
+                <menuitem action="Nextframe" />
             </placeholder>
         </menu>
     </menubar>
@@ -311,6 +316,10 @@ class Timeline(gtk.Table, Loggable, Zoomable):
                 self.split),
             ("Keyframe", "pitivi-keyframe", _("Add a keyframe"), "K", KEYFRAME,
                 self.keyframe),
+            ("Prevframe", "pitivi-prevframe", _("_Prevframe"), "E", PREVFRAME,
+                self.prevframe),
+            ("Nextframe", "pitivi-nextframe", _("_Nextframe"), "R", NEXTFRAME,
+                self.nextframe),
         )
 
         actiongroup = gtk.ActionGroup("timelinepermanent")
@@ -327,6 +336,8 @@ class Timeline(gtk.Table, Loggable, Zoomable):
         self.delete_action = actiongroup.get_action("DeleteObj")
         self.split_action = actiongroup.get_action("Split")
         self.keyframe_action = actiongroup.get_action("Keyframe")
+        self.prevframe_action = actiongroup.get_action("Prevframe")
+        self.nextframe_action = actiongroup.get_action("Nextframe")
 
         self.ui_manager.insert_action_group(actiongroup, -1)
 
@@ -668,7 +679,7 @@ class Timeline(gtk.Table, Loggable, Zoomable):
         for obj in selected:
             keyframe_exists = False
 
-            position_in_obj = timeline_position - obj.start
+            position_in_obj = (timeline_position - obj.start) + obj.in_point
             interpolators = obj.getInterpolators()
             for value in interpolators:
                 interpolator = obj.getInterpolator(value)
@@ -683,3 +694,19 @@ class Timeline(gtk.Table, Loggable, Zoomable):
                     self.app.action_log.begin("add volume point")
                     interpolator.newKeyframe(position_in_obj)
                     self.app.action_log.commit()
+
+    def prevframe(self, action):
+        timeline_position = self._position
+
+        prev_kf = self.timeline.getPrevKeyframe(timeline_position)
+        if prev_kf != None:
+            self._seeker.seek(prev_kf)
+            self.timelinePositionChanged(prev_kf)
+
+    def nextframe(self, action):
+        timeline_position = self._position
+
+        next_kf = self.timeline.getNextKeyframe(timeline_position)
+        if next_kf:
+                self._seeker.seek(next_kf)
+                self.timelinePositionChanged(next_kf)
diff --git a/tests/test_timeline.py b/tests/test_timeline.py
index 1abeabf..8cf956a 100644
--- a/tests/test_timeline.py
+++ b/tests/test_timeline.py
@@ -629,6 +629,93 @@ class TestTimeline(TestCase):
             min_priority=3, max_priority=4)
         self.failUnlessEqual(result, tmp_obj_list)
 
+    def testGetKeyframe(self):
+        timeline_object0 = self.makeTimelineObject()
+        timeline_object1 = self.makeTimelineObject()
+        timeline_object3 = self.makeTimelineObject()
+
+        timeline_object0.start = 0 * gst.SECOND
+        timeline_object0.duration = 1 * gst.SECOND
+        timeline_object0.priority = 0
+
+        timeline_object1.start = 1 * gst.SECOND
+        timeline_object1.duration = 5 * gst.SECOND
+        timeline_object1.priority = 1
+
+        timeline_object3.start = 15 * gst.SECOND
+        timeline_object3.duration = 5 * gst.SECOND
+        timeline_object3.priority = 2
+
+        factory = AudioTestSourceFactory()
+        stream = AudioStream(gst.Caps("audio/x-raw-int"))
+        track_object = SourceTrackObject(factory, stream)
+        self.track1.addTrackObject(track_object)
+        timeline_object2 = TimelineObject(factory)
+        timeline_object2.addTrackObject(track_object)
+        self.timeline.addTimelineObject(timeline_object2)
+        timeline_object2.start = 3 * gst.SECOND
+        timeline_object2.duration = 10 * gst.SECOND
+        timeline_object2.priority = 1
+
+        interpolator = track_object.getInterpolator("volume")
+        keyframe_position = 7 * gst.SECOND - timeline_object2.start
+        interpolator.newKeyframe(keyframe_position, 0.0, "mode")
+
+        timeline = self.timeline
+
+        time1 = 0
+        time2 = 0.5 * gst.SECOND
+        time3 = 2 * gst.SECOND
+        time4 = 6.5 * gst.SECOND
+        time5 = 10 * gst.SECOND
+        time6 = 14 * gst.SECOND
+        time7 = 25 * gst.SECOND
+
+        result = timeline.getPrevKeyframe(time1)
+        self.failUnlessEqual(result, None)
+        result = timeline.getPrevKeyframe(time2)
+        self.failUnlessEqual(result, 0 * gst.SECOND)
+        result = timeline.getPrevKeyframe(time3)
+        self.failUnlessEqual(result, 1 * gst.SECOND)
+        result = timeline.getPrevKeyframe(time4)
+        self.failUnlessEqual(result, 6 * gst.SECOND)
+        result = timeline.getPrevKeyframe(time5)
+        self.failUnlessEqual(result, 7 * gst.SECOND)
+        result = timeline.getPrevKeyframe(time6)
+        self.failUnlessEqual(result, 13 * gst.SECOND)
+        result = timeline.getPrevKeyframe(time7)
+        self.failUnlessEqual(result, 20 * gst.SECOND)
+
+        result = timeline.getNextKeyframe(time1)
+        self.failUnlessEqual(result, 1 * gst.SECOND)
+        result = timeline.getNextKeyframe(time2)
+        self.failUnlessEqual(result, 1 * gst.SECOND)
+        result = timeline.getNextKeyframe(time3)
+        self.failUnlessEqual(result, 3 * gst.SECOND)
+        result = timeline.getNextKeyframe(time4)
+        self.failUnlessEqual(result, 7 * gst.SECOND)
+        result = timeline.getNextKeyframe(time5)
+        self.failUnlessEqual(result, 13 * gst.SECOND)
+        result = timeline.getNextKeyframe(time6)
+        self.failUnlessEqual(result, 15 * gst.SECOND)
+        result = timeline.getNextKeyframe(time7)
+        self.failUnlessEqual(result, None)
+
+        other_object2 = timeline_object2.split(8 * gst.SECOND)
+        timeline_object2.start = 5 * gst.SECOND
+        other_object2.start = 7 * gst.SECOND
+        time1 = 7 * gst.SECOND
+        time2 = 10 * gst.SECOND
+        result = timeline.getNextKeyframe(time1)
+        self.failUnlessEqual(result, 9 * gst.SECOND)
+        result = timeline.getNextKeyframe(time2)
+        self.failUnlessEqual(result, 12 * gst.SECOND)
+
+        position = 8.5 * gst.SECOND
+        interpolator = other_object2.track_objects[0].getInterpolator("volume")
+        interpolator.newKeyframe(position)
+        result = timeline.getNextKeyframe(time2)
+        self.failUnlessEqual(result, 10.5 * gst.SECOND)
 
 class TestLink(TestCase):
 



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