Commit efa26eb0 authored by Luis de Bethencourt's avatar Luis de Bethencourt Committed by Alessandro Decina

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

parent 39896266
......@@ -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
......@@ -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)
......@@ -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):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment