1
# PiTiVi , Non-linear video editor
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.log.loggable import Loggable
25
class UndoError(Exception):
28
class UndoWrongStateError(UndoError):
31
class UndoableAction(Signallable):
39
raise NotImplementedError()
42
raise NotImplementedError()
53
class UndoableActionStack(UndoableAction):
60
def __init__(self, action_group_name):
61
self.action_group_name = action_group_name
62
self.done_actions = []
63
self.undone_actions = []
66
def push(self, action):
67
self.done_actions.append(action)
69
def _runAction(self, action_list, method_name):
70
for action in action_list[::-1]:
71
method = getattr(action, method_name)
75
self._runAction(self.undone_actions, "do")
76
self.done_actions = self.undone_actions[::-1]
80
self._runAction(self.done_actions, "undo")
81
self.undone_actions = self.done_actions[::-1]
85
actions = self.done_actions + self.undone_actions
86
self.undone_actions = []
87
self.done_actions = []
88
self._runAction(actions, "clean")
92
class UndoableActionLog(Signallable):
94
"begin": ["stack", "nested"],
95
"push": ["stack", "action"],
96
"rollback": ["stack", "nested"],
97
"commit": ["stack", "nested"],
103
self.undo_stacks = []
104
self.redo_stacks = []
107
self._checkpoint = self._takeSnapshot()
109
def begin(self, action_group_name):
113
stack = UndoableActionStack(action_group_name)
114
nested = self._stackIsNested(stack)
115
self.stacks.append(stack)
116
self.emit("begin", stack, nested)
118
def push(self, action):
123
stack = self._getTopmostStack()
124
except UndoWrongStateError:
128
self.emit("push", stack, action)
134
stack = self._getTopmostStack(pop=True)
137
nested = self._stackIsNested(stack)
138
self.emit("rollback", stack, nested)
145
stack = self._getTopmostStack(pop=True)
148
nested = self._stackIsNested(stack)
150
self.undo_stacks.append(stack)
152
self.stacks[-1].push(stack)
155
self.redo_stacks = []
157
self.emit("commit", stack, nested)
160
if self.stacks or not self.undo_stacks:
161
raise UndoWrongStateError()
163
stack = self.undo_stacks.pop(-1)
165
self._runStack(stack, stack.undo)
167
self.redo_stacks.append(stack)
168
self.emit("undo", stack)
171
if self.stacks or not self.redo_stacks:
172
raise UndoWrongStateError()
174
stack = self.redo_stacks.pop(-1)
176
self._runStack(stack, stack.do)
177
self.undo_stacks.append(stack)
178
self.emit("redo", stack)
181
stacks = self.redo_stacks + self.undo_stacks
182
self.redo_stacks = []
183
self.undo_stacks = []
186
self._runStack(stack, stack.clean)
189
def _takeSnapshot(self):
190
return list(self.undo_stacks)
192
def checkpoint(self):
194
raise UndoWrongStateError()
196
self._checkpoint = self._takeSnapshot()
199
current_snapshot = self._takeSnapshot()
200
return current_snapshot != self._checkpoint
202
def _runStack(self, stack, run):
209
def _getTopmostStack(self, pop=False):
213
stack = self.stacks.pop(-1)
215
stack = self.stacks[-1]
217
raise UndoWrongStateError()
221
def _stackIsNested(self, stack):
222
return bool(len(self.stacks))
224
class DebugActionLogObserver(Loggable):
225
def startObserving(self, log):
226
self._connectToActionLog(log)
228
def stopObserving(self, log):
229
self._disconnectFromActionLog(log)
231
def _connectToActionLog(self, log):
232
log.connect("begin", self._actionLogBeginCb)
233
log.connect("commit", self._actionLogCommitCb)
234
log.connect("rollback", self._actionLogRollbackCb)
235
log.connect("push", self._actionLogPushCb)
237
def _disconnectFromActionLog(self, log):
238
for method in (self._actionLogBeginCb, self._actionLogCommitCb,
239
self._actionLogrollbackCb, self._actionLogPushCb):
240
log.disconnect_by_func(method)
242
def _actionLogBeginCb(self, log, stack, nested):
243
self.debug("begin action %s nested %s",
244
stack.action_group_name, nested)
246
def _actionLogCommitCb(self, log, stack, nested):
247
self.debug("commit action %s nested %s",
248
stack.action_group_name, nested)
250
def _actionLogRollbackCb(self, log, stack, nested):
251
self.debug("rollback action %s nested %s",
252
stack.action_group_name, nested)
254
def _actionLogPushCb(self, log, stack, action):
255
self.debug("push %s in %s", action, stack.action_group_name)