~ubuntu-branches/ubuntu/trusty/pitivi/trusty-proposed

« back to all changes in this revision

Viewing changes to pitivi/undo.py

  • Committer: Package Import Robot
  • Author(s): Sebastian Dröge
  • Date: 2014-04-05 15:28:16 UTC
  • mfrom: (6.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20140405152816-6lijoax4cngiz5j5
Tags: 0.93-3
* debian/control:
  + Depend on python-gi (>= 3.10), older versions do not work
    with pitivi (Closes: #732813).
  + Add missing dependency on gir1.2-clutter-gst-2.0 (Closes: #743692).
  + Add suggests on gir1.2-notify-0.7 and gir1.2-gnomedesktop-3.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# PiTiVi , Non-linear video editor
2
 
#
3
 
#       pitivi/undo.py
4
 
#
5
 
# Copyright (c) 2009, Alessandro Decina <alessandro.d@gmail.com>
6
 
#
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.
11
 
#
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.
16
 
#
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.
21
 
 
22
 
from pitivi.signalinterface import Signallable
23
 
from pitivi.log.loggable import Loggable
24
 
 
25
 
 
26
 
class UndoError(Exception):
27
 
    pass
28
 
 
29
 
 
30
 
class UndoWrongStateError(UndoError):
31
 
    pass
32
 
 
33
 
 
34
 
class UndoableAction(Signallable):
35
 
    __signals__ = {
36
 
        "done": [],
37
 
        "undone": [],
38
 
        "undone": [],
39
 
    }
40
 
 
41
 
    def do(self):
42
 
        raise NotImplementedError()
43
 
 
44
 
    def undo(self):
45
 
        raise NotImplementedError()
46
 
 
47
 
    def clean(self):
48
 
        pass
49
 
 
50
 
    def _done(self):
51
 
        self.emit("done")
52
 
 
53
 
    def _undone(self):
54
 
        self.emit("undone")
55
 
 
56
 
 
57
 
class UndoableActionStack(UndoableAction):
58
 
    __signals__ = {
59
 
        "done": [],
60
 
        "undone": [],
61
 
        "cleaned": [],
62
 
    }
63
 
 
64
 
    def __init__(self, action_group_name):
65
 
        self.action_group_name = action_group_name
66
 
        self.done_actions = []
67
 
        self.undone_actions = []
68
 
        self.actions = []
69
 
 
70
 
    def push(self, action):
71
 
        self.done_actions.append(action)
72
 
 
73
 
    def _runAction(self, action_list, method_name):
74
 
        for action in action_list[::-1]:
75
 
            method = getattr(action, method_name)
76
 
            method()
77
 
 
78
 
    def do(self):
79
 
        self._runAction(self.undone_actions, "do")
80
 
        self.done_actions = self.undone_actions[::-1]
81
 
        self.emit("done")
82
 
 
83
 
    def undo(self):
84
 
        self._runAction(self.done_actions, "undo")
85
 
        self.undone_actions = self.done_actions[::-1]
86
 
        self.emit("undone")
87
 
 
88
 
    def clean(self):
89
 
        actions = self.done_actions + self.undone_actions
90
 
        self.undone_actions = []
91
 
        self.done_actions = []
92
 
        self._runAction(actions, "clean")
93
 
        self.emit("cleaned")
94
 
 
95
 
 
96
 
class UndoableActionLog(Signallable):
97
 
    __signals__ = {
98
 
        "begin": ["stack", "nested"],
99
 
        "push": ["stack", "action"],
100
 
        "rollback": ["stack", "nested"],
101
 
        "commit": ["stack", "nested"],
102
 
        "undo": ["stack"],
103
 
        "redo": ["stack"],
104
 
        "cleaned": [],
105
 
    }
106
 
 
107
 
    def __init__(self):
108
 
        self.undo_stacks = []
109
 
        self.redo_stacks = []
110
 
        self.stacks = []
111
 
        self.running = False
112
 
        self._checkpoint = self._takeSnapshot()
113
 
 
114
 
    def begin(self, action_group_name):
115
 
        if self.running:
116
 
            return
117
 
 
118
 
        stack = UndoableActionStack(action_group_name)
119
 
        nested = self._stackIsNested(stack)
120
 
        self.stacks.append(stack)
121
 
        self.emit("begin", stack, nested)
122
 
 
123
 
    def push(self, action):
124
 
        if self.running:
125
 
            return
126
 
 
127
 
        try:
128
 
            stack = self._getTopmostStack()
129
 
        except UndoWrongStateError:
130
 
            return
131
 
 
132
 
        stack.push(action)
133
 
        self.emit("push", stack, action)
134
 
 
135
 
    def rollback(self):
136
 
        if self.running:
137
 
            return
138
 
 
139
 
        stack = self._getTopmostStack(pop=True)
140
 
        if stack is None:
141
 
            return
142
 
        nested = self._stackIsNested(stack)
143
 
        self.emit("rollback", stack, nested)
144
 
        stack.undo()
145
 
 
146
 
    def commit(self):
147
 
        if self.running:
148
 
            return
149
 
 
150
 
        stack = self._getTopmostStack(pop=True)
151
 
        if stack is None:
152
 
            return
153
 
        nested = self._stackIsNested(stack)
154
 
        if not self.stacks:
155
 
            self.undo_stacks.append(stack)
156
 
        else:
157
 
            self.stacks[-1].push(stack)
158
 
 
159
 
        if self.redo_stacks:
160
 
            self.redo_stacks = []
161
 
 
162
 
        self.emit("commit", stack, nested)
163
 
 
164
 
    def undo(self):
165
 
        if self.stacks or not self.undo_stacks:
166
 
            raise UndoWrongStateError()
167
 
 
168
 
        stack = self.undo_stacks.pop(-1)
169
 
 
170
 
        self._runStack(stack, stack.undo)
171
 
 
172
 
        self.redo_stacks.append(stack)
173
 
        self.emit("undo", stack)
174
 
 
175
 
    def redo(self):
176
 
        if self.stacks or not self.redo_stacks:
177
 
            raise UndoWrongStateError()
178
 
 
179
 
        stack = self.redo_stacks.pop(-1)
180
 
 
181
 
        self._runStack(stack, stack.do)
182
 
        self.undo_stacks.append(stack)
183
 
        self.emit("redo", stack)
184
 
 
185
 
    def clean(self):
186
 
        stacks = self.redo_stacks + self.undo_stacks
187
 
        self.redo_stacks = []
188
 
        self.undo_stacks = []
189
 
 
190
 
        for stack in stacks:
191
 
            self._runStack(stack, stack.clean)
192
 
        self.emit("cleaned")
193
 
 
194
 
    def _takeSnapshot(self):
195
 
        return list(self.undo_stacks)
196
 
 
197
 
    def checkpoint(self):
198
 
        if self.stacks:
199
 
            raise UndoWrongStateError()
200
 
 
201
 
        self._checkpoint = self._takeSnapshot()
202
 
 
203
 
    def dirty(self):
204
 
        current_snapshot = self._takeSnapshot()
205
 
        return current_snapshot != self._checkpoint
206
 
 
207
 
    def _runStack(self, stack, run):
208
 
        self.running = True
209
 
        try:
210
 
            run()
211
 
        finally:
212
 
            self.running = False
213
 
 
214
 
    def _getTopmostStack(self, pop=False):
215
 
        stack = None
216
 
        try:
217
 
            if pop:
218
 
                stack = self.stacks.pop(-1)
219
 
            else:
220
 
                stack = self.stacks[-1]
221
 
        except IndexError:
222
 
            raise UndoWrongStateError()
223
 
 
224
 
        return stack
225
 
 
226
 
    def _stackIsNested(self, stack):
227
 
        return bool(len(self.stacks))
228
 
 
229
 
 
230
 
class DebugActionLogObserver(Loggable):
231
 
    def startObserving(self, log):
232
 
        self._connectToActionLog(log)
233
 
 
234
 
    def stopObserving(self, log):
235
 
        self._disconnectFromActionLog(log)
236
 
 
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)
242
 
 
243
 
    def _disconnectFromActionLog(self, log):
244
 
        for method in (self._actionLogBeginCb, self._actionLogCommitCb,
245
 
                self._actionLogrollbackCb, self._actionLogPushCb):
246
 
            log.disconnect_by_func(method)
247
 
 
248
 
    def _actionLogBeginCb(self, log, stack, nested):
249
 
        self.debug("begin action %s nested %s",
250
 
                stack.action_group_name, nested)
251
 
 
252
 
    def _actionLogCommitCb(self, log, stack, nested):
253
 
        self.debug("commit action %s nested %s",
254
 
                stack.action_group_name, nested)
255
 
 
256
 
    def _actionLogRollbackCb(self, log, stack, nested):
257
 
        self.debug("rollback action %s nested %s",
258
 
                stack.action_group_name, nested)
259
 
 
260
 
    def _actionLogPushCb(self, log, stack, action):
261
 
        self.debug("push %s in %s", action, stack.action_group_name)