Mercurial > fife-parpg
comparison clients/editor/scripts/undomanager.py @ 255:51cc05d862f2
Merged editor_rewrite branch to trunk.
This contains changes that may break compatibility against existing clients.
For a list of changes that may affect your client, see: http://wiki.fifengine.de/Changes_to_pychan_and_FIFE_in_editor_rewrite_branch
author | cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Mon, 08 Jun 2009 16:00:02 +0000 |
parents | |
children | e893afb4963b |
comparison
equal
deleted
inserted
replaced
254:10b5f7f36dd4 | 255:51cc05d862f2 |
---|---|
1 from events.signal import Signal | |
2 import collections | |
3 import pdb | |
4 | |
5 actionAdded = Signal(providing_args=["action"]) | |
6 preUndo = Signal() | |
7 postUndo = Signal() | |
8 preRedo = Signal() | |
9 postRedo = Signal() | |
10 cleared = Signal() | |
11 modeChanged = Signal(providing_args=["mode"]) | |
12 changed = Signal() | |
13 | |
14 class UndoManager: | |
15 """ | |
16 The undo manager provides advanced undo functionality. | |
17 | |
18 Add actions with addAction. If you want to add a lot of actions and | |
19 group them, use startGroup and endGroup. When you undo a group, you will | |
20 undo all the actions within it. | |
21 | |
22 If branched mode is enabled, you will not overwrite the redostack when | |
23 adding an action. Instead, a new branch will be created with the new actions. | |
24 To navigate in branches, you can use nextBranch and redoBranch. | |
25 | |
26 Example | |
27 ======= | |
28 # Init undomanager | |
29 undomanager = UndoManager() | |
30 | |
31 def doSomething(): | |
32 # Adds an action to the undomanager | |
33 undocallback = lambda: doSomethingElse() | |
34 redocallback = lambda: doSomething() | |
35 action = UndoObject("Did something", "Something was done somewhere in the program.", "icon.png") | |
36 undomanager.addAction(action) | |
37 | |
38 def doLotOfActions(): | |
39 # Starts an actiongroup and adds three actions | |
40 undomanager.startGroup("Did lot of actions", "Lot of actions was done somewhere in the program") | |
41 doSomething() | |
42 doSomething() | |
43 doSomething() | |
44 undomanager.endGroup() | |
45 | |
46 # This will create an actiongroup with three actions, and undo it | |
47 doLotOfActions() | |
48 undomanager.undo() | |
49 """ | |
50 | |
51 def __init__(self, branchedMode = False): | |
52 self._groups = [] | |
53 self._branched_mode = False | |
54 | |
55 def warn(msg): | |
56 print "Warning: ",msg | |
57 self.first_item = UndoStackItem(UndoObject(None, None)) | |
58 self.first_item.object.name = "First item" | |
59 self.first_item.object.description = "First item in stack. Placeholder" | |
60 self.first_item.object.undoCallback = lambda: warn("Tried to undo first item") | |
61 self.first_item.object.redoCallback = lambda: warn("Tried to redo first item") | |
62 | |
63 self.current_item = self.first_item | |
64 | |
65 def startGroup(self, name="", description="", icon=""): | |
66 """ | |
67 Starts an undogroup. Subsequent items will be added to the group | |
68 until endGroup is called. Undogroups can be nested. | |
69 | |
70 name, description and icon are information that can be used by | |
71 scripts which analyze the undostack. | |
72 """ | |
73 undogroup = UndoGroup(name, description, icon) | |
74 self._groups.append(undogroup) | |
75 return undogroup | |
76 | |
77 def endGroup(self): | |
78 """ | |
79 Ends the undogroup. | |
80 """ | |
81 if len(self._groups) <= 0: | |
82 print "Warning: UndoManager: No groups to end!" | |
83 return | |
84 | |
85 group = self._groups.pop() | |
86 self.addAction(group) | |
87 | |
88 def addAction(self, action): | |
89 """ | |
90 Adds an action to the stack. | |
91 | |
92 If the redostack is not empty and branchmode is enabed, | |
93 a new branch will be created. If branchmod is disabled, | |
94 the redo branch will be cleared. | |
95 """ | |
96 | |
97 if len(self._groups) > 0: | |
98 self._groups[len(self._groups)-1].addObject(action) | |
99 else: | |
100 stackitem = UndoStackItem(action) | |
101 | |
102 stackitem.previous = self.current_item | |
103 if self._branched_mode: | |
104 stackitem.previous.addBranch(stackitem) | |
105 else: | |
106 stackitem.previous.next = stackitem | |
107 | |
108 self.current_item = stackitem | |
109 | |
110 actionAdded.send(sender=self, action=action) | |
111 changed.send(sender=self) | |
112 | |
113 def clear(self): | |
114 """ | |
115 Clears the undostack. | |
116 """ | |
117 self._groups = [] | |
118 self.first_item.clearBranches() | |
119 self.current_item = self.first_item | |
120 | |
121 cleared.send(sender=self) | |
122 changed.send(sender=self) | |
123 | |
124 # Linear undo | |
125 def undo(self, amount=1): | |
126 """ Undo [amount] items """ | |
127 if amount <= 0: | |
128 return | |
129 | |
130 preUndo.send(sender=self) | |
131 for i in range(amount): | |
132 if self.current_item == self.first_item: | |
133 print "Warning: UndoManager: Tried to undo non-existing action." | |
134 break | |
135 | |
136 self.current_item.object.undo() | |
137 self.current_item = self.current_item.previous | |
138 | |
139 postUndo.send(sender=self) | |
140 changed.send(sender=self) | |
141 | |
142 | |
143 def redo(self, amount=1): | |
144 """ Redo [amount] items. """ | |
145 if amount <= 0: | |
146 return | |
147 | |
148 preRedo.send(sender=self) | |
149 for i in range(amount): | |
150 if self.current_item.next is None: | |
151 print "Warning: UndoManager: Tried to redo non-existing action." | |
152 break | |
153 | |
154 self.current_item = self.current_item.next | |
155 self.current_item.object.redo() | |
156 | |
157 postRedo.send(sender=self) | |
158 changed.send(sender=self) | |
159 | |
160 def getBranchMode(self): | |
161 """ Returns true if branch mode is enabled """ | |
162 return self._branched_mode | |
163 | |
164 def setBranchMode(self, enable): | |
165 """ Enable or disable branch mode """ | |
166 self._branched_mode = enable | |
167 changed.send(sender=self) | |
168 | |
169 def getBranches(self): | |
170 """ Returns branches from current stack item. """ | |
171 return self.current_item.getBranches() | |
172 def nextBranch(self): | |
173 """ Switch to next branch in current item """ | |
174 self.current_item.nextBranch() | |
175 changed.send(sender=self) | |
176 | |
177 def previousBranch(self): | |
178 """ Switch previous branch in current item """ | |
179 self.current_item.previousBranch() | |
180 changed.send(sender=self) | |
181 | |
182 class UndoObject: | |
183 """ UndoObject contains all the information that is needed to undo or redo an action, | |
184 as well as representation of it. | |
185 | |
186 ATTRIBUTES | |
187 ========== | |
188 - name: Name used by scripts analyzing the undostack to represent this item | |
189 - description: Description of this item | |
190 - icon: Icon used to represent this item | |
191 - redoCallback: Function used to redo this item | |
192 - undoCallback: Function used to undo | |
193 | |
194 """ | |
195 def __init__(self, undoCallback, redoCallback, name="", description="", icon=""): | |
196 self.name = name | |
197 self.redoCallback = redoCallback | |
198 self.undoCallback = undoCallback | |
199 self.description = description | |
200 self.icon = icon | |
201 | |
202 self.undone = False | |
203 | |
204 def undo(self): | |
205 """ Undoes the action. Do not use directly! """ | |
206 if self.undone is True: | |
207 print "Tried to undo already undone action!" | |
208 return | |
209 | |
210 self.undone = True | |
211 self.undoCallback() | |
212 | |
213 def redo(self): | |
214 """ Redoes the action. Do not use directly! """ | |
215 if self.undone is False: | |
216 print "Tried to redo already redone action!" | |
217 return | |
218 | |
219 self.undone = False | |
220 self.redoCallback() | |
221 | |
222 class UndoGroup: | |
223 """ | |
224 Contains a list of actions. Used to group actions together. | |
225 | |
226 Use UndoManager.startGroup and UndoManager.endGroup. | |
227 | |
228 """ | |
229 def __init__(self, name="", description="", icon=""): | |
230 self.name = name | |
231 self.description = description | |
232 self.icon = icon | |
233 | |
234 self.undoobjects = [] | |
235 | |
236 def addObject(self, object): | |
237 """ Adds an action to the list """ | |
238 self.undoobjects.append(object) | |
239 | |
240 def getObjects(self): | |
241 """ Returns a list of the actions contained """ | |
242 return self.undoobjects | |
243 | |
244 def undo(self): | |
245 """ Undoes all actions. """ | |
246 for action in reversed(self.undoobjects): | |
247 action.undo() | |
248 | |
249 def redo(self): | |
250 """ Redoes all actions. """ | |
251 for action in self.undoobjects: | |
252 action.redo() | |
253 | |
254 class UndoStackItem: | |
255 """ Represents an action or actiongroup in the undostack. Do not use directly! """ | |
256 def __init__(self, object): | |
257 self._branches = [] | |
258 self._currentbranch = -1 | |
259 | |
260 self.parent = None | |
261 self.object = object | |
262 self.previous = None | |
263 self.next = None | |
264 | |
265 def getBranches(self): | |
266 """ Returns a list of the branches """ | |
267 return self._branches; | |
268 | |
269 def addBranch(self, item): | |
270 """ Adds a branch to the list and sets this item to point to that branch """ | |
271 self._branches.append(item) | |
272 | |
273 self._currentbranch += 1 | |
274 self.next = self._branches[self._currentbranch] | |
275 self.next.parent = self | |
276 | |
277 def nextBranch(self): | |
278 """ Sets this item to point to next branch """ | |
279 if len(self._branches) <= 0: | |
280 return | |
281 self._currentbranch += 1 | |
282 if self._currentbranch >= len(self._branches): | |
283 self._currentbranch = 0 | |
284 self.next = self._branches[self._currentbranch] | |
285 changed.send(sender=self) | |
286 | |
287 def previousBranch(self): | |
288 """ Sets this item to point to previous branch """ | |
289 if len(self._branches) <= 0: | |
290 return | |
291 | |
292 self._currentbranch -= 1 | |
293 if self._currentbranch < 0: | |
294 self._currentbranch = len(self._branches)-1 | |
295 self.next = self._branches[self._currentbranch] | |
296 changed.send(sender=self) | |
297 | |
298 def setBranchIndex(self, index): | |
299 """ Set this item to point to branches[index] """ | |
300 if index < 0 or index >= len(self._branches): | |
301 return | |
302 self._currentbranch = index | |
303 changed.send(sender=self) | |
304 self.next = self._branches[self._currentbranch] | |
305 | |
306 def clearBranches(self): | |
307 """ Removes all branches """ | |
308 self._branches = [] | |
309 self._currentbranch = -1 | |
310 self._next = None | |
311 changed.send(sender=self) | |
312 | |
313 def setBranch(self, branch): | |
314 """ Set this item to point to branch. Returns True on success. """ | |
315 for b in range(len(self._branches)): | |
316 if self._branches[b] == branch: | |
317 self._currentbranch = b | |
318 self.next = self._branches[self._currentbranch] | |
319 changed.send(sender=self) | |
320 return True | |
321 else: | |
322 print "Didn't find branch!" | |
323 return False | |
324 |