1
# PiTiVi , Non-linear video editor
3
# pitivi/timeline/timeline_undo.py
5
# Copyright (c) 2009, Alessandro Decina <alessandro.d@gmail.com>
7
# This program is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License as published by the Free Software Foundation; either
10
# version 2.1 of the License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# Lesser General Public License for more details.
17
# You should have received a copy of the GNU Lesser General Public
18
# License along with this program; if not, write to the
19
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
# Boston, MA 02111-1307, USA.
22
from pitivi.signalinterface import Signallable
23
from pitivi.utils import PropertyChangeTracker
24
from pitivi.undo import UndoableAction
26
class TimelineObjectPropertyChangeTracker(PropertyChangeTracker):
28
property_names = ["start", "duration", "in-point",
29
"media-duration", "priority", "selected"]
33
def connectToObject(self, obj):
34
PropertyChangeTracker.connectToObject(self, obj)
35
self.timeline = obj.timeline
36
self.timeline.connect("disable-updates", self._timelineDisableUpdatesCb)
38
def disconnectFromObject(self, obj):
39
self.timeline.disconnect_by_func(self._timelineDisableUpdatesCb)
40
PropertyChangeTracker.disconnectFromObject(self, obj)
42
def _timelineDisableUpdatesCb(self, timeline, disabled):
43
if self._disabled and not disabled:
44
self._disabled = disabled
45
properties = self._takeCurrentSnapshot(self.obj)
46
for property_name, property_value in properties.iteritems():
47
old_value = self.properties[property_name]
48
if old_value != property_value:
49
self._propertyChangedCb(self.obj, property_value, property_name)
51
self._disabled = disabled
53
def _propertyChangedCb(self, timeline_object, value, property_name):
54
if not self._disabled:
55
PropertyChangeTracker._propertyChangedCb(self,
56
timeline_object, value, property_name)
58
class KeyframeChangeTracker(Signallable):
60
"keyframe-moved": ["keyframe"]
67
def connectToObject(self, obj):
69
self.keyframes = self._takeCurrentSnapshot(obj)
70
obj.connect("keyframe-added", self._keyframeAddedCb)
71
obj.connect("keyframe-removed", self._keyframeRemovedCb)
72
obj.connect("keyframe-moved", self._keyframeMovedCb)
74
def _takeCurrentSnapshot(self, obj):
76
for keyframe in self.obj.getKeyframes():
77
keyframes[keyframe] = self._getKeyframeSnapshot(keyframe)
81
def disconnectFromObject(self, obj):
83
obj.disconnect_by_func(self._keyframeMovedCb)
85
def _keyframeAddedCb(self, interpolator, keyframe):
86
self.keyframes[keyframe] = self._getKeyframeSnapshot(keyframe)
88
def _keyframeRemovedCb(self, interpolator, keyframe):
91
def _keyframeMovedCb(self, interpolator, keyframe):
92
old_snapshot = self.keyframes[keyframe]
93
new_snapshot = self._getKeyframeSnapshot(keyframe)
94
self.keyframes[keyframe] = new_snapshot
96
self.emit("keyframe-moved", interpolator,
97
keyframe, old_snapshot, new_snapshot)
99
def _getKeyframeSnapshot(self, keyframe):
100
return (keyframe.mode, keyframe.time, keyframe.value)
102
class TimelineObjectPropertyChanged(UndoableAction):
103
def __init__(self, timeline_object, property_name, old_value, new_value):
104
self.timeline_object = timeline_object
105
self.property_name = property_name
106
self.old_value = old_value
107
self.new_value = new_value
110
setattr(self.timeline_object,
111
self.property_name.replace("-", "_"), self.new_value)
115
setattr(self.timeline_object,
116
self.property_name.replace("-", "_"), self.old_value)
119
class TimelineObjectAdded(UndoableAction):
120
def __init__(self, timeline, timeline_object):
121
self.timeline = timeline
122
self.timeline_object = timeline_object
123
self.tracks = dict((track_object, track_object.track)
124
for track_object in timeline_object.track_objects)
127
for track_object, track in self.tracks.iteritems():
128
track.addTrackObject(track_object)
130
self.timeline.addTimelineObject(self.timeline_object)
134
self.timeline.removeTimelineObject(self.timeline_object, deep=True)
137
class TimelineObjectRemoved(UndoableAction):
138
def __init__(self, timeline, timeline_object):
139
self.timeline = timeline
140
self.timeline_object = timeline_object
141
self.tracks = dict((track_object, track_object.track)
142
for track_object in timeline_object.track_objects)
145
self.timeline.removeTimelineObject(self.timeline_object, deep=True)
149
for track_object, track in self.tracks.iteritems():
150
track.addTrackObject(track_object)
152
self.timeline.addTimelineObject(self.timeline_object)
155
class InterpolatorKeyframeAdded(UndoableAction):
156
def __init__(self, track_object, keyframe):
157
self.track_object = track_object
158
self.keyframe = keyframe
161
self.track_object.newKeyframe(self.keyframe)
165
self.track_object.removeKeyframe(self.keyframe)
168
class InterpolatorKeyframeRemoved(UndoableAction):
169
def __init__(self, track_object, keyframe):
170
self.track_object = track_object
171
self.keyframe = keyframe
174
self.track_object.removeKeyframe(self.keyframe)
178
self.track_object.newKeyframe(self.keyframe.time,
179
self.keyframe.value, self.keyframe.mode)
182
class InterpolatorKeyframeChanged(UndoableAction):
183
def __init__(self, track_object, keyframe, old_snapshot, new_snapshot):
184
self.track_object = track_object
185
self.keyframe = keyframe
186
self.old_snapshot = old_snapshot
187
self.new_snapshot = new_snapshot
190
self._setSnapshot(self.new_snapshot)
194
self._setSnapshot(self.old_snapshot)
197
def _setSnapshot(self, snapshot):
198
mode, time, value = snapshot
199
self.keyframe.setMode(mode)
200
self.keyframe.setTime(time)
201
self.keyframe.setValue(value)
203
class TimelineLogObserver(object):
204
timelinePropertyChangedAction = TimelineObjectPropertyChanged
205
timelineObjectAddedAction = TimelineObjectAdded
206
timelineObjectRemovedAction = TimelineObjectRemoved
207
interpolatorKeyframeAddedAction = InterpolatorKeyframeAdded
208
interpolatorKeyframeRemovedAction = InterpolatorKeyframeRemoved
209
interpolatorKeyframeChangedAction = InterpolatorKeyframeChanged
211
def __init__(self, log):
213
self.timeline_object_property_trackers = {}
214
self.interpolator_keyframe_trackers = {}
216
def startObserving(self, timeline):
217
self._connectToTimeline(timeline)
218
for timeline_object in timeline.timeline_objects:
219
self._connectToTimelineObject(timeline_object)
220
for track_object in timeline_object.track_objects:
221
self._connectToTrackObject(track_object)
223
def stopObserving(self, timeline):
224
self._disconnectFromTimeline(timeline)
225
for timeline_object in timeline.timeline_objects:
226
self._disconnectFromTimelineObject(timeline_object)
227
for track_object in timeline_object.track_objects:
228
self._disconnectFromTrackObject(track_object)
230
def _connectToTimeline(self, timeline):
231
timeline.connect("timeline-object-added", self._timelineObjectAddedCb)
232
timeline.connect("timeline-object-removed", self._timelineObjectRemovedCb)
234
def _disconnectFromTimeline(self, timeline):
235
timeline.disconnect_by_func(self._timelineObjectAddedCb)
236
timeline.disconnect_by_func(self._timelineObjectRemovedCb)
238
def _connectToTimelineObject(self, timeline_object):
239
tracker = TimelineObjectPropertyChangeTracker()
240
tracker.connectToObject(timeline_object)
241
for property_name in tracker.property_names:
242
tracker.connect(property_name + "-changed",
243
self._timelineObjectPropertyChangedCb, property_name)
244
self.timeline_object_property_trackers[timeline_object] = tracker
246
timeline_object.connect("track-object-added", self._timelineObjectTrackObjectAddedCb)
247
timeline_object.connect("track-object-removed", self._timelineObjectTrackObjectRemovedCb)
248
for obj in timeline_object.track_objects:
249
self._connectToTrackObject(obj)
251
def _disconnectFromTimelineObject(self, timeline_object):
252
tracker = self.timeline_object_property_trackers.pop(timeline_object)
253
tracker.disconnectFromObject(timeline_object)
254
tracker.disconnect_by_func(self._timelineObjectPropertyChangedCb)
256
def _connectToTrackObject(self, track_object):
257
for prop, interpolator in track_object.getInterpolators().itervalues():
258
self._connectToInterpolator(interpolator)
260
def _disconnectFromTrackObject(self, track_object):
261
for prop, interpolator in track_object.getInterpolators().itervalues():
262
self._disconnectFromInterpolator(interpolator)
264
def _connectToInterpolator(self, interpolator):
265
interpolator.connect("keyframe-added", self._interpolatorKeyframeAddedCb)
266
interpolator.connect("keyframe-removed",
267
self._interpolatorKeyframeRemovedCb)
269
tracker = KeyframeChangeTracker()
270
tracker.connectToObject(interpolator)
271
tracker.connect("keyframe-moved", self._interpolatorKeyframeMovedCb)
272
self.interpolator_keyframe_trackers[interpolator] = tracker
274
def _disconnectFromInterpolator(self, interpolator):
275
tracker = self.interpolator_keyframe_trackers.pop(interpolator)
276
tracker.disconnectFromObject(interpolator)
277
tracker.disconnect_by_func(self._interpolatorKeyframeMovedCb)
279
def _timelineObjectAddedCb(self, timeline, timeline_object):
280
self._connectToTimelineObject(timeline_object)
281
action = self.timelineObjectAddedAction(timeline, timeline_object)
282
self.log.push(action)
284
def _timelineObjectRemovedCb(self, timeline, timeline_object):
285
self._disconnectFromTimelineObject(timeline_object)
286
action = self.timelineObjectRemovedAction(timeline, timeline_object)
287
self.log.push(action)
289
def _timelineObjectPropertyChangedCb(self, tracker, timeline_object,
290
old_value, new_value, property_name):
291
action = self.timelinePropertyChangedAction(timeline_object,
292
property_name, old_value, new_value)
293
self.log.push(action)
295
def _timelineObjectTrackObjectAddedCb(self, timeline_object, track_object):
296
self._connectToTrackObject(track_object)
298
def _timelineObjectTrackObjectRemovedCb(self, timeline_object,
300
self._disconnectFromTrackObject(track_object)
302
def _interpolatorKeyframeAddedCb(self, track_object, keyframe):
303
action = self.interpolatorKeyframeAddedAction(track_object, keyframe)
304
self.log.push(action)
306
def _interpolatorKeyframeRemovedCb(self, track_object, keyframe):
307
action = self.interpolatorKeyframeRemovedAction(track_object, keyframe)
308
self.log.push(action)
310
def _interpolatorKeyframeMovedCb(self, tracker, track_object,
311
keyframe, old_snapshot, new_snapshot):
312
action = self.interpolatorKeyframeChangedAction(track_object,
313
keyframe, old_snapshot, new_snapshot)
314
self.log.push(action)