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., 51 Franklin St, Fifth Floor,
20
# Boston, MA 02110-1301, USA.
22
from pitivi.signalinterface import Signallable
23
from pitivi.log.loggable import Loggable
26
class UndoError(Exception):
30
class UndoWrongStateError(UndoError):
34
class UndoableAction(Signallable):
42
raise NotImplementedError()
45
raise NotImplementedError()
57
class UndoableActionStack(UndoableAction):
64
def __init__(self, action_group_name):
65
self.action_group_name = action_group_name
66
self.done_actions = []
67
self.undone_actions = []
70
def push(self, action):
71
self.done_actions.append(action)
73
def _runAction(self, action_list, method_name):
74
for action in action_list[::-1]:
75
method = getattr(action, method_name)
79
self._runAction(self.undone_actions, "do")
80
self.done_actions = self.undone_actions[::-1]
84
self._runAction(self.done_actions, "undo")
85
self.undone_actions = self.done_actions[::-1]
89
actions = self.done_actions + self.undone_actions
90
self.undone_actions = []
91
self.done_actions = []
92
self._runAction(actions, "clean")
96
class UndoableActionLog(Signallable):
98
"begin": ["stack", "nested"],
99
"push": ["stack", "action"],
100
"rollback": ["stack", "nested"],
101
"commit": ["stack", "nested"],
108
self.undo_stacks = []
109
self.redo_stacks = []
112
self._checkpoint = self._takeSnapshot()
114
def begin(self, action_group_name):
118
stack = UndoableActionStack(action_group_name)
119
nested = self._stackIsNested(stack)
120
self.stacks.append(stack)
121
self.emit("begin", stack, nested)
123
def push(self, action):
128
stack = self._getTopmostStack()
129
except UndoWrongStateError:
133
self.emit("push", stack, action)
139
stack = self._getTopmostStack(pop=True)
142
nested = self._stackIsNested(stack)
143
self.emit("rollback", stack, nested)
150
stack = self._getTopmostStack(pop=True)
153
nested = self._stackIsNested(stack)
155
self.undo_stacks.append(stack)
157
self.stacks[-1].push(stack)
160
self.redo_stacks = []
162
self.emit("commit", stack, nested)
165
if self.stacks or not self.undo_stacks:
166
raise UndoWrongStateError()
168
stack = self.undo_stacks.pop(-1)
170
self._runStack(stack, stack.undo)
172
self.redo_stacks.append(stack)
173
self.emit("undo", stack)
176
if self.stacks or not self.redo_stacks:
177
raise UndoWrongStateError()
179
stack = self.redo_stacks.pop(-1)
181
self._runStack(stack, stack.do)
182
self.undo_stacks.append(stack)
183
self.emit("redo", stack)
186
stacks = self.redo_stacks + self.undo_stacks
187
self.redo_stacks = []
188
self.undo_stacks = []
191
self._runStack(stack, stack.clean)
194
def _takeSnapshot(self):
195
return list(self.undo_stacks)
197
def checkpoint(self):
199
raise UndoWrongStateError()
201
self._checkpoint = self._takeSnapshot()
204
current_snapshot = self._takeSnapshot()
205
return current_snapshot != self._checkpoint
207
def _runStack(self, stack, run):
214
def _getTopmostStack(self, pop=False):
218
stack = self.stacks.pop(-1)
220
stack = self.stacks[-1]
222
raise UndoWrongStateError()
226
def _stackIsNested(self, stack):
227
return bool(len(self.stacks))
230
class DebugActionLogObserver(Loggable):
231
def startObserving(self, log):
232
self._connectToActionLog(log)
234
def stopObserving(self, log):
235
self._disconnectFromActionLog(log)
237
def _connectToActionLog(self, log):
238
log.connect("begin", self._actionLogBeginCb)
239
log.connect("commit", self._actionLogCommitCb)
240
log.connect("rollback", self._actionLogRollbackCb)
241
log.connect("push", self._actionLogPushCb)
243
def _disconnectFromActionLog(self, log):
244
for method in (self._actionLogBeginCb, self._actionLogCommitCb,
245
self._actionLogrollbackCb, self._actionLogPushCb):
246
log.disconnect_by_func(method)
248
def _actionLogBeginCb(self, log, stack, nested):
249
self.debug("begin action %s nested %s",
250
stack.action_group_name, nested)
252
def _actionLogCommitCb(self, log, stack, nested):
253
self.debug("commit action %s nested %s",
254
stack.action_group_name, nested)
256
def _actionLogRollbackCb(self, log, stack, nested):
257
self.debug("rollback action %s nested %s",
258
stack.action_group_name, nested)
260
def _actionLogPushCb(self, log, stack, action):
261
self.debug("push %s in %s", action, stack.action_group_name)