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