[pitivi] undo: Reimplement undo/redo for Keyframes.
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] undo: Reimplement undo/redo for Keyframes.
- Date: Sat, 15 Nov 2014 10:33:40 +0000 (UTC)
commit d43b1a28ca012362022e58d8bd1ee820956a94ea
Author: Thibault Saunier <tsaunier gnome org>
Date: Tue Sep 30 16:24:47 2014 +0200
undo: Reimplement undo/redo for Keyframes.
https://bugzilla.gnome.org/show_bug.cgi?id=739251
pitivi/timeline/elements.py | 43 ++++++++++++
pitivi/undo/timeline.py | 149 ++++++++++++++++++++++++-------------------
2 files changed, 127 insertions(+), 65 deletions(-)
---
diff --git a/pitivi/timeline/elements.py b/pitivi/timeline/elements.py
index 4d72fa3..4e3cb11 100644
--- a/pitivi/timeline/elements.py
+++ b/pitivi/timeline/elements.py
@@ -286,6 +286,7 @@ class TimelineElement(Clutter.Actor, Zoomable):
self.keyframedElement = None
self.rightHandle = None
self.isSelected = False
+ self.updating_keyframes = False
size = self.bElement.get_duration()
self.background = self._createBackground()
@@ -317,6 +318,24 @@ class TimelineElement(Clutter.Actor, Zoomable):
self._connectToEvents()
+ def _valueChanged(self, source, value):
+ if self.updating_keyframes is True:
+ return
+
+ self.updateKeyframes()
+
+ def _valueAddedCb(self, source, value):
+ if self.updating_keyframes is True:
+ return
+
+ self.updateKeyframes()
+
+ def _valueRemovedCb(self, source, value):
+ if self.updating_keyframes is True:
+ return
+
+ self.updateKeyframes()
+
# Public API
def set_size(self, width, height, ease):
@@ -354,14 +373,22 @@ class TimelineElement(Clutter.Actor, Zoomable):
self.restore_easing_state()
def addKeyframe(self, value, timestamp):
+ self.timeline._container.app.action_log.begin("Add KeyFrame")
+ self.updating_keyframes = True
self.source.set(timestamp, value)
self.updateKeyframes()
+ self.timeline._container.app.action_log.commit()
+ self.updating_keyframes = False
def removeKeyframe(self, kf):
+ self.timeline._container.app.action_log.begin("Remove KeyFrame")
+ self.updating_keyframes = True
self.source.unset(kf.value.timestamp)
self.keyframes = sorted(
self.keyframes, key=lambda keyframe: keyframe.value.timestamp)
self.updateKeyframes()
+ self.timeline._container.app.action_log.commit()
+ self.updating_keyframes = False
def showKeyframes(self, element, propname, isDefault=False):
binding = element.get_control_binding(propname.name)
@@ -377,6 +404,9 @@ class TimelineElement(Clutter.Actor, Zoomable):
self.prop = propname
self.keyframedElement = element
self.source = self.binding.props.control_source
+ self.source.connect("value-added", self._valueAddedCb)
+ self.source.connect("value-removed", self._valueRemovedCb)
+ self.source.connect("value-changed", self._valueChanged)
if isDefault:
self.default_prop = propname
@@ -426,6 +456,8 @@ class TimelineElement(Clutter.Actor, Zoomable):
if not self.source:
return
+ updating = self.updating_keyframes
+ self.updating_keyframes = True
values = self.source.get_all()
if len(values) < 2 and self.bElement.props.duration > 0:
self.source.unset_all()
@@ -447,6 +479,7 @@ class TimelineElement(Clutter.Actor, Zoomable):
self.keyframes.append(keyframe)
self.drawLines()
+ self.updating_keyframes = updating
def cleanup(self):
Zoomable.__del__(self)
@@ -761,6 +794,8 @@ class Line(Clutter.Actor):
pass
def _dragBeginCb(self, unused_action, unused_actor, event_x, event_y, unused_modifiers):
+ self.timelineElement.timeline._container.app.action_log.begin(
+ "Dragging keyframe line")
self.dragBeginStartX = event_x
self.dragBeginStartY = event_y
self.origY = self.props.y
@@ -783,6 +818,7 @@ class Line(Clutter.Actor):
self.nextKeyframe.endDrag()
if self.timelineElement.timeline.getActorUnderPointer() != self:
self._ungrab()
+ self.timelineElement.timeline._container.app.action_log.commit()
class KeyframeMenu(GtkClutter.Actor):
@@ -923,6 +959,8 @@ class Keyframe(Clutter.Actor):
if not self.has_changeable_time:
newTs = self.lastTs
+ updating = self.timelineElement.updating_keyframes
+ self.timelineElement.updating_keyframes = True
self.timelineElement.source.unset(self.lastTs)
if (self.timelineElement.source.set(newTs, newValue)):
self.value = Gst.TimedValue()
@@ -942,7 +980,11 @@ class Keyframe(Clutter.Actor):
self.timelineElement.timeline._container.seekInPosition(
newTs + self.start)
+ self.timelineElement.updating_keyframes = updating
+
def _dragBeginCb(self, unused_action, unused_actor, event_x, event_y, unused_modifiers):
+ self.timelineElement.timeline._container.app.action_log.begin(
+ "Dragging keyframe")
self.dragProgressed = False
self.startDrag(event_x, event_y)
@@ -958,6 +1000,7 @@ class Keyframe(Clutter.Actor):
self.endDrag()
if self.timelineElement.timeline.getActorUnderPointer() != self:
self._unselect()
+ self.timelineElement.timeline._container.app.action_log.commit()
class URISourceElement(TimelineElement):
diff --git a/pitivi/undo/timeline.py b/pitivi/undo/timeline.py
index 319716c..4749f08 100644
--- a/pitivi/undo/timeline.py
+++ b/pitivi/undo/timeline.py
@@ -225,42 +225,44 @@ class KeyframeChangeTracker(GObject.Object):
}
def __init__(self):
+ GObject.Object.__init__(self)
self.keyframes = None
- self.obj = None
+ self.control_source = None
- def connectToObject(self, obj):
- self.obj = obj
- self.keyframes = self._takeCurrentSnapshot(obj)
- obj.connect("keyframe-added", self._keyframeAddedCb)
- obj.connect("keyframe-removed", self._keyframeRemovedCb)
- obj.connect("keyframe-moved", self._keyframeMovedCb)
+ def connectToObject(self, control_source):
+ self.control_source = control_source
+ self.keyframes = self._takeCurrentSnapshot(control_source)
+ control_source.connect("value-added", self._keyframeAddedCb)
+ control_source.connect("value-removed", self._keyframeRemovedCb)
+ control_source.connect("value-changed", self._keyframeMovedCb)
- def _takeCurrentSnapshot(self, obj):
+ def _takeCurrentSnapshot(self, control_source):
keyframes = {}
- for keyframe in self.obj.getKeyframes():
- keyframes[keyframe] = self._getKeyframeSnapshot(keyframe)
+ for keyframe in self.control_source.get_all():
+ keyframes[keyframe.timestamp] = self._getKeyframeSnapshot(keyframe)
return keyframes
- def disconnectFromObject(self, obj):
- self.obj = None
- obj.disconnect_by_func(self._keyframeMovedCb)
+ def disconnectFromObject(self, control_source):
+ self.control_source = None
+ control_source.disconnect_by_func(self._keyframeMovedCb)
- def _keyframeAddedCb(self, interpolator, keyframe):
- self.keyframes[keyframe] = self._getKeyframeSnapshot(keyframe)
+ def _keyframeAddedCb(self, control_source, keyframe):
+ self.keyframes[keyframe.timestamp] = self._getKeyframeSnapshot(keyframe)
- def _keyframeRemovedCb(self, interpolator, keyframe, old_value=None):
+ def _keyframeRemovedCb(self, control_source, keyframe, old_value=None):
pass # FIXME: this has not been implemented
- def _keyframeMovedCb(self, interpolator, keyframe, old_value=None):
- old_snapshot = self.keyframes[keyframe]
+ def _keyframeMovedCb(self, control_source, keyframe, old_value=None):
+ old_snapshot = self.keyframes[keyframe.timestamp]
new_snapshot = self._getKeyframeSnapshot(keyframe)
- self.keyframes[keyframe] = new_snapshot
- self.emit("keyframe-moved", interpolator,
+ self.keyframes[keyframe.timestamp] = new_snapshot
+
+ self.emit("keyframe-moved", control_source,
keyframe, old_snapshot, new_snapshot)
def _getKeyframeSnapshot(self, keyframe):
- return (keyframe.mode, keyframe.time, keyframe.value)
+ return (keyframe.timestamp, keyframe.value)
class ClipPropertyChanged(UndoableAction):
@@ -373,40 +375,44 @@ class LayerRemoved(UndoableAction):
return st
-class InterpolatorKeyframeAdded(UndoableAction):
+class ControlSourceValueAdded(UndoableAction):
- def __init__(self, track_element, keyframe):
+ def __init__(self, control_source, keyframe,
+ property_name):
UndoableAction.__init__(self)
- self.track_element = track_element
+ self.control_source = control_source
self.keyframe = keyframe
+ self.property_name = property_name
def do(self):
- self.track_element.newKeyframe(self.keyframe)
+ self.control_source.set(self.keyframe.timestamp,
+ self.keyframe.value)
self._done()
def undo(self):
- self.track_element.removeKeyframe(self.keyframe)
+ self.control_source.unset(self.keyframe.timestamp)
self._undone()
-class InterpolatorKeyframeRemoved(UndoableAction):
+class ControlSourceValueRemoved(UndoableAction):
- def __init__(self, track_element, keyframe):
+ def __init__(self, control_source, keyframe, property_name):
UndoableAction.__init__(self)
- self.track_element = track_element
+ self.control_source = control_source
self.keyframe = keyframe
+ self.property_name = property_name
def do(self):
- self.track_element.removeKeyframe(self.keyframe)
+ self.control_source.unset(self.keyframe.timestamp)
self._undone()
def undo(self):
- self.track_element.newKeyframe(self.keyframe.time,
- self.keyframe.value, self.keyframe.mode)
+ self.control_source.set(self.keyframe.timestamp,
+ self.keyframe.value)
self._done()
-class InterpolatorKeyframeChanged(UndoableAction):
+class ControlSourceKeyframeChanged(UndoableAction):
def __init__(self, track_element, keyframe, old_snapshot, new_snapshot):
UndoableAction.__init__(self)
@@ -424,8 +430,7 @@ class InterpolatorKeyframeChanged(UndoableAction):
self._undone()
def _setSnapshot(self, snapshot):
- mode, time, value = snapshot
- self.keyframe.setMode(mode)
+ time, value = snapshot
self.keyframe.setTime(time)
self.keyframe.setValue(value)
@@ -450,15 +455,12 @@ class ActivePropertyChanged(UndoableAction):
class TimelineLogObserver(object):
timelinePropertyChangedAction = ClipPropertyChanged
- interpolatorKeyframeAddedAction = InterpolatorKeyframeAdded
- interpolatorKeyframeRemovedAction = InterpolatorKeyframeRemoved
- interpolatorKeyframeChangedAction = InterpolatorKeyframeChanged
activePropertyChangedAction = ActivePropertyChanged
def __init__(self, log):
self.log = log
self.clip_property_trackers = {}
- self.interpolator_keyframe_trackers = {}
+ self.control_source_keyframe_trackers = {}
self.children_props_tracker = TrackElementChildPropertyTracker(log)
self._pipeline = None
@@ -525,34 +527,49 @@ class TimelineLogObserver(object):
tracker.disconnectFromObject(clip)
tracker.disconnect_by_func(self._clipPropertyChangedCb)
+ def _controlBindingAddedCb(self, track_element, binding):
+ self._connectToControlSource(track_element, binding)
+
def _connectToTrackElement(self, track_element):
- # FIXME: keyframes are disabled:
- # for prop, interpolator in track_element.getInterpolators().itervalues():
- # self._connectToInterpolator(interpolator)
+ for prop, binding in track_element.get_all_control_bindings().items():
+ self._connectToControlSource(track_element, binding)
+ track_element.connect("control-binding-added",
+ self._controlBindingAddedCb)
if isinstance(track_element, GES.BaseEffect):
self.children_props_tracker.addTrackElement(track_element)
elif isinstance(track_element, GES.TitleSource):
self.children_props_tracker.addTrackElement(track_element)
def _disconnectFromTrackElement(self, track_element):
- pass
- # for prop, interpolator in track_element.getInterpolators().values():
- # self._disconnectFromInterpolator(interpolator)
-
- def _connectToInterpolator(self, interpolator):
- interpolator.connect(
- "keyframe-added", self._interpolatorKeyframeAddedCb)
- interpolator.connect(
- "keyframe-removed", self._interpolatorKeyframeRemovedCb)
+ for prop, binding in track_element.get_all_control_bindings().items():
+ self._disconnectFromControlSource(binding)
+
+ def _connectToControlSource(self, track_element, binding):
+ control_source = binding.props.control_source
+
+ control_source.connect("value-added",
+ self._controlSourceKeyFrameAddedCb,
+ track_element,
+ binding.props.name)
+
+ control_source.connect("value-removed",
+ self._controlSourceKeyFrameRemovedCb,
+ track_element,
+ binding.props.name)
+
tracker = KeyframeChangeTracker()
- tracker.connectToObject(interpolator)
- tracker.connect("keyframe-moved", self._interpolatorKeyframeMovedCb)
- self.interpolator_keyframe_trackers[interpolator] = tracker
+ tracker.connectToObject(control_source)
+ tracker.connect("keyframe-moved", self._controlSourceKeyFrameMovedCb)
+ self.control_source_keyframe_trackers[control_source] = tracker
+
+ def _disconnectFromControlSource(self, binding):
+ control_source = binding.props.control_source
+ control_source.disconnect_by_func(self._controlSourceKeyFrameAddedCb)
+ control_source.disconnect_by_func(self._controlSourceKeyFrameRemovedCb)
- def _disconnectFromInterpolator(self, interpolator):
- tracker = self.interpolator_keyframe_trackers.pop(interpolator)
- tracker.disconnectFromObject(interpolator)
- tracker.disconnect_by_func(self._interpolatorKeyframeMovedCb)
+ tracker = self.control_source_keyframe_trackers.pop(control_source)
+ tracker.disconnectFromObject(control_source)
+ tracker.disconnect_by_func(self._controlSourceKeyFrameMovedCb)
def _clipAddedCb(self, layer, clip):
if isinstance(clip, GES.TransitionClip):
@@ -592,12 +609,14 @@ class TimelineLogObserver(object):
self.children_props_tracker)
self.log.push(action)
- def _interpolatorKeyframeAddedCb(self, track_element, keyframe):
- action = self.interpolatorKeyframeAddedAction(track_element, keyframe)
+ def _controlSourceKeyFrameAddedCb(self, source, keyframe, track_element,
+ property_name):
+ action = ControlSourceValueAdded(source, keyframe, property_name)
self.log.push(action)
- def _interpolatorKeyframeRemovedCb(self, track_element, keyframe, old_value=None):
- action = self.interpolatorKeyframeRemovedAction(track_element, keyframe)
+ def _controlSourceKeyFrameRemovedCb(self, source, keyframe, track_element,
+ property_name):
+ action = ControlSourceValueRemoved(source, keyframe, property_name)
self.log.push(action)
def _trackElementActiveChangedCb(self, track_element, active, add_effect_action):
@@ -607,10 +626,10 @@ class TimelineLogObserver(object):
action = self.activePropertyChangedAction(add_effect_action, active)
self.log.push(action)
- def _interpolatorKeyframeMovedCb(self, tracker, track_element,
+ def _controlSourceKeyFrameMovedCb(self, tracker, track_element,
keyframe, old_snapshot, new_snapshot):
- action = self.interpolatorKeyframeChangedAction(track_element,
- keyframe, old_snapshot, new_snapshot)
+ action = ControlSourceKeyframeChanged(track_element, keyframe,
+ old_snapshot, new_snapshot)
self.log.push(action)
def _layerAddedCb(self, timeline, layer):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]