root/gaphor/tags/gaphor-0.7.0/gaphor/undomanager.py

Revision 436, 5.3 kB (checked in by arjanmol, 4 years ago)

*** empty log message ***

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 # vim:sw=4:et:
2
3 import gobject
4 import diacanvas
5 from gaphor.misc.aspects import Aspect, weave_method
6
7
8 def get_undo_manager():
9     """Return the default undo manager.
10     """
11     return _default_undo_manager
12
13
14 class UndoTransactionAspect(Aspect):
15
16     def __init__(self, method):
17         self.method = method
18
19     def before(self):
20         get_undo_manager().begin_transaction()
21
22     def after(self, retval, exc):
23         if exc:
24             get_undo_manager().discard_transaction()
25         else:
26             get_undo_manager().commit_transaction()
27
28
29 class TransactionError(Exception):
30
31     def __init__(self, msg):
32         self.args = msg
33
34
35 class Transaction(object):
36     """A transaction. Every action that is added between a begin_transaction()
37     and a commit_transaction() call is recorded in a transaction, do it can
38     be played back when a transaction is undone.
39     """
40
41     def __init__(self):
42         self._actions = []
43
44     def add(self, action):
45         self._actions.append(action)
46
47     def can_undo(self):
48         return self._actions and True or False
49
50     def undo(self):
51         self._actions.reverse()
52         for action in self._actions:
53             try:
54                 #log.debug('Undoing action %s' % action)
55                 action.undo()
56             except Exception, e:
57                 log.error('Error while undoing action %s' % action, e)
58
59     def redo(self):
60         self._actions.reverse()
61         for action in self._actions:
62             try:
63                 #log.debug('Redoing action %s' % action)
64                 action.redo()
65             except Exception, e:
66                 log.error('Error while redoing action %s' % action, e)
67
68
69 class UndoManager(gobject.GObject, diacanvas.UndoManager):
70     """Simple transaction manager for Gaphor.
71     This transaction manager supports nested transactions.
72     """
73
74     def __init__(self):
75         self.__gobject_init__()
76         self._in_undo = False
77         self._undo_stack = []
78         self._redo_stack = []
79         self._stack_depth = 20
80         self._current_transaction = None
81         self._transaction_depth = 0
82         self._short_circuit = False
83
84     def clear_undo_stack(self):
85         self._undo_stack = []
86         self._current_transaction = None
87
88     def clear_redo_stack(self):
89         self._redo_stack = []
90
91 #    def add_undo_action(self, action):
92 #        try:
93 #            self._short_circuit = True
94 #            diacanvas.UndoManager.add_undo_action(self, action)
95 #            self.on_add_undo_action(action)
96 #        finally:
97 #            self._short_circuit = False
98
99     # UndoManager interface:
100
101     def on_begin_transaction(self):
102         if self._in_undo:
103             return
104
105         #log.debug('begin_transaction')
106         if self._current_transaction:
107             self._transaction_depth += 1
108             #raise TransactionError, 'Already in a transaction'
109             return
110
111         self._current_transaction = Transaction()
112         self.clear_redo_stack()
113         self._transaction_depth += 1
114
115     def on_add_undo_action(self, action):
116         """Add an action to undo. An action
117         """
118         if self._short_circuit:
119             return
120
121         #log.debug('add_undo_action: %s %s' % (self._current_transaction, action))
122         if not self._current_transaction:
123             return
124
125         if self._redo_stack:
126             self.clear_redo_stack()
127
128         self._current_transaction.add(action)
129
130     def on_commit_transaction(self):
131         if self._in_undo:
132             return
133
134         #log.debug('commit_transaction')
135         if not self._current_transaction:
136             return #raise TransactionError, 'No transaction to commit'
137
138         self._transaction_depth -= 1
139         if self._transaction_depth == 0:
140             if self._current_transaction.can_undo():
141                 self._undo_stack.append(self._current_transaction)
142             else:
143                 log.debug('nothing to commit')
144
145             self._current_transaction = None
146
147     def on_discard_transaction(self):
148         if self._in_undo:
149             return
150
151         if not self._current_transaction:
152             raise TransactionError, 'No transaction to discard'
153
154         self._transaction_depth -= 1
155         if self._transaction_depth == 0:
156             self._current_transaction = None
157
158     def on_undo_transaction(self):
159         if not self._undo_stack:
160             return
161
162         if self._current_transaction:
163             log.warning('Trying to undo a transaction, while in a transaction')
164             self.commit_transaction()
165         transaction = self._undo_stack.pop()
166         try:
167             self._in_undo = True
168             transaction.undo()
169         finally:
170             self._in_undo = False
171         self._redo_stack.append(transaction)
172
173     def on_redo_transaction(self):
174         if not self._redo_stack:
175             return
176
177         transaction = self._redo_stack.pop()
178         try:
179             self._in_undo = True
180             transaction.redo()
181         finally:
182             self._in_undo = False
183         self._undo_stack.append(transaction)
184
185     def on_in_transaction(self):
186         return self._current_transaction is not None
187
188     def on_can_undo(self):
189         return bool(self._current_transaction or self._undo_stack)
190
191     def on_can_redo(self):
192         return bool(self._redo_stack)
193
194 gobject.type_register(UndoManager)
195 diacanvas.set_undo_manager(UndoManager)
196
197 # Register as resource:
198 import gaphor
199 _default_undo_manager = gaphor.resource(UndoManager)
200 del gaphor
201
Note: See TracBrowser for help on using the browser.