Package arcmap :: Module undo
[hide private]
[frames] | no frames]

Source Code for Module arcmap.undo

  1  ################################################################################ 
  2  # Authors: Brian Schott (Sir Alaran) 
  3  # Copyright: Brian Schott (Sir Alaran) 
  4  # Date: Sep 26 2009 
  5  # License: 
  6  # 
  7  # This program is free software: you can redistribute it and/or modify 
  8  # it under the terms of the GNU General Public License as published by 
  9  # the Free Software Foundation, either version 3 of the License, or 
 10  # (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 
 15  # GNU General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU General Public License 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 19  ################################################################################ 
 20   
 21  """ 
 22  Module for undoing and redoing actions in the map editor 
 23  """ 
 24   
 25  import copy 
 26   
 27  # DO NOT ACCESS THESE DIRECTLY 
 28  __undoList = [] 
 29  __redoList = [] 
 30   
 31   
32 -def addUndoAction(action):
33 """ 34 Don't use this directly. Call the addUndoAction function of MapController. 35 Doing so will update the status of the undo and redo buttons on the window. 36 @type action: UndoAction 37 @param action: the action to add to the undo stack 38 """ 39 __undoList.append(action) 40 __redoList = []
41 42
43 -def undo():
44 """ 45 @rtype: bool 46 @return: True if anything was undone, False otherwise 47 """ 48 if len(__undoList) > 0: 49 __undoList[-1].undo() 50 __redoList.append(__undoList.pop()) 51 return True 52 else: 53 return False
54 55
56 -def redo():
57 """ 58 @rtype: bool 59 @return: True if anything was redone, False otherwise 60 """ 61 if len(__redoList) > 0: 62 __redoList[-1].redo() 63 __undoList.append(__redoList.pop()) 64 return True 65 else: 66 return False
67 68
69 -def canUndo():
70 """ 71 @rtype: bool 72 @return: True if there are any actions that can be undone 73 """ 74 return len(__undoList) > 0
75 76
77 -def canRedo():
78 """ 79 @rtype: bool 80 @return: True if there are any actions that can be redone 81 """ 82 return len(__redoList) > 0
83 84
85 -def getRedoDescription():
86 """ 87 @rtype: str 88 @return: a textual description of the next action that can be redone, or 89 None. It's a good idea to call canRedo before this. 90 """ 91 if canRedo(): 92 return __redoList[-1].getRedoString() 93 else: 94 return None
95 96
97 -def getUndoDescription():
98 """ 99 @rtype: str 100 @return: a textual description of the next action taht can be redone, or 101 None. It's a good idea to call canUndo before this. 102 """ 103 if canUndo(): 104 return __undoList[-1].getUndoString() 105 else: 106 return None
107 108
109 -class UndoAction(object):
110 """ 111 Base class for undo actions 112 """
113 - def __init__(self, controller):
114 self.__controller = controller 115 self.__description = "REPLACE THIS TEXT"
116
117 - def getController(self):
118 return self.__controller
119
120 - def setDescription(self, desc):
121 """ 122 Only derived classes should call this 123 @type desc: str 124 @param desc: A description of the action to be taken 125 """ 126 self.__description = desc
127
128 - def undo(self):
129 """ 130 Undo the action 131 """ 132 raise NameError("UndoAction.undo is abstract.")
133
134 - def redo(self):
135 """ 136 Redo the action 137 """ 138 raise NameError("UndoAction.redo is abstract.")
139
140 - def getUndoString(self):
141 """ 142 @rtype: str 143 @return: A string describing the action to be done 144 """ 145 return "Undo " + self.__description
146
147 - def getRedoString(self):
148 """ 149 @rtype: str 150 @return: A string describing the action to be done 151 """ 152 return "Redo " + self.__description
153 154
155 -class ShapeMoveAction(UndoAction):
156 """ 157 UndoAction for moving physics shapes around the map 158 """
159 - def __init__(self, controller, shape, dx, dy):
160 UndoAction.__init__(self, controller) 161 self.__shape = shape 162 self.__dx = dx 163 self.__dy = dy 164 self.setDescription("move shape")
165
166 - def undo(self):
167 self.__shape.shift(-self.__dx, -self.__dy)
168
169 - def redo(self):
170 self.__shape.shift(self.__dx, self.__dy)
171 172
173 -class ShapeDeleteAction(UndoAction):
174 """ 175 UndoAction for deleting a shape from the map 176 """
177 - def __init__(self, controller, shape):
178 UndoAction.__init__(self, controller) 179 self.__shape = shape 180 self.setDescription("delete shape")
181
182 - def undo(self):
183 self.getController().addShape(self.__shape)
184
185 - def redo(self):
186 self.getController().removeShape(self.__shape)
187 188
189 -class ShapeAddAction(UndoAction):
190 """ 191 UndoAction for adding a shape to the map 192 """
193 - def __init__(self, controller, shape):
194 UndoAction.__init__(self, controller) 195 self.__shape = shape 196 self.setDescription("add shape")
197
198 - def undo(self):
199 self.getController().removeShape(self.__shape)
200
201 - def redo(self):
202 self.getController().addShape(self.__shape)
203 204
205 -class ShapeAdjustAction(UndoAction):
206 """ 207 UndoAction for adjusting the geometry of a shape 208 """
209 - def __init__(self, controller, shape, index, oldX, oldY, newX, newY):
210 """ 211 @type controller: mapcontroller.MapController 212 @param controller: the map controller 213 @type index: int 214 @param index: the index of the shape's handle being adjusted 215 @type oldX: int 216 @param oldX: old x-coordinate of the handle 217 @type oldY: int 218 @param oldY: old y-coordinate of the handle 219 @type newX: int 220 @param newX: new x-coordinate of the handle 221 @type newY: int 222 @param newY: new y-coordinate of the handle 223 """ 224 UndoAction.__init__(self, controller) 225 self.__oldX = oldX 226 self.__oldY = oldY 227 self.__newX = newX 228 self.__newY = newY 229 self.__shape = shape 230 self.__index = index 231 self.setDescription("adjust shape")
232
233 - def undo(self):
234 self.__shape.adjust(self.__oldX, self.__oldY, self.__index)
235
236 - def redo(self):
237 self.__shape.adjust(self.__newX, self.__newY, self.__index)
238 239
240 -class TileAddAction(UndoAction):
241 """ 242 Action for adding tiles to the map. Because tiles are often added in chunks, 243 the appendTileAdd method must be used. This way the entire chunk can be 244 undone at once 245 """
246 - def __init__(self, controller):
247 UndoAction.__init__(self, controller) 248 self.setDescription("add tiles") 249 self.__addedTiles = {} 250 self.__oldTiles = {}
251
252 - def appendTileAdd(self, coords, newImageCoords, oldImageCoords):
253 """ 254 Adds a tile add operation to this undo action 255 @type coords: (int, int, int) 256 @param coords: x-coordinate, y-coordinate, and layer 257 @type newImageCoords: (int, int, int) 258 @param newImageCoords: x-coordinate of new tile's image, 259 x-coordinate of new tile's image, and image index of new tile's 260 image 261 @type oldImageCoords: (int, int, int) 262 @param oldImageCoords: x-coordinate of old tile's image, 263 x-coordinate of old tile's image, and image index of old tile's 264 image 265 """ 266 self.__addedTiles[coords] = newImageCoords 267 self.__oldTiles[coords] = oldImageCoords
268
269 - def undo(self):
270 for coord, imageCoord in self.__oldTiles.iteritems(): 271 x = coord[0] 272 y = coord[1] 273 z = coord[2] 274 controller = self.getController() 275 if self.__oldTiles[coord] == None: 276 controller.removeTile(x, y, z) 277 else: 278 ix = imageCoord[0] 279 iy = imageCoord[1] 280 ii = imageCoord[2] 281 controller.addTile(x, y, z, ix, iy, ii)
282
283 - def redo(self):
284 for coord, imageCoord in self.__addedTiles.iteritems(): 285 x = coord[0] 286 y = coord[1] 287 z = coord[2] 288 controller = self.getController() 289 ix = imageCoord[0] 290 iy = imageCoord[1] 291 ii = imageCoord[2] 292 controller.addTile(x, y, z, ix, iy, ii)
293 294
295 -class TileRemoveAction(UndoAction):
296 """ 297 Action for removing tiles from the map 298 """
299 - def __init__(self, controller):
300 UndoAction.__init__(self, controller) 301 self.setDescription("remove tiles") 302 self.__oldTiles = {}
303
304 - def appendTileRemove(self, coords, oldImageCoords):
305 """ 306 Adds a tile remove operation to this undo action 307 @type coords: (int, int, int) 308 @param coords: x-coordinate, y-coordinate, and layer 309 @type oldImageCoords: (int, int, int) 310 @param oldImageCoords: x-coordinate of old tile's image, 311 x-coordinate of old tile's image, and image index of old tile's 312 image 313 """ 314 if coords in self.__oldTiles: 315 return 316 self.__oldTiles[coords] = oldImageCoords
317
318 - def undo(self):
319 for coord, imageCoord in self.__oldTiles.iteritems(): 320 x = coord[0] 321 y = coord[1] 322 z = coord[2] 323 controller = self.getController() 324 if self.__oldTiles[coord] != None: 325 ix = imageCoord[0] 326 iy = imageCoord[1] 327 ii = imageCoord[2] 328 controller.addTile(x, y, z, ix, iy, ii)
329
330 - def redo(self):
331 controller = self.getController() 332 for coord in self.__oldTiles: 333 x = coord[0] 334 y = coord[1] 335 z = coord[2] 336 controller.removeTile(x, y, z)
337 338
339 -class ResizeAction(UndoAction):
340 - def __init__(self, controller, newWidth, newHeight, xOffset, yOffset, 341 oldWidth, oldHeight):
342 UndoAction.__init__(self, controller) 343 self.setDescription("resize map") 344 self.__newWidth = newWidth 345 self.__newHeight = newHeight 346 self.__xOffset = xOffset 347 self.__yOffset = yOffset 348 self.__oldWidth = oldWidth 349 self.__oldHeight = oldHeight 350 351 self.__tileRemove = TileRemoveAction(controller) 352 353 def addTileRange(x1, x2, y1, y2): 354 for x in range(x1, x2): 355 for y in range(y1, y2): 356 for z in range(controller.mapLayers()): 357 r = controller.removeTile(x, y, z) 358 self.__tileRemove.appendTileRemove((x, y, z), r)
359 360 # Left edge 361 if xOffset < 0: 362 addTileRange(0, -xOffset, 0, oldHeight) 363 364 # Top edge 365 if yOffset < 0: 366 addTileRange(0, oldWidth, 0, -yOffset) 367 368 # Right edge 369 rightStart = oldWidth - (oldWidth - newWidth + xOffset) 370 addTileRange(rightStart, oldWidth, 0, oldHeight) 371 372 # Bottom edge 373 bottomStart = oldHeight - (oldHeight - newHeight + yOffset) 374 addTileRange(0, oldWidth, bottomStart, oldHeight)
375
376 - def undo(self):
377 self.getController().resize(self.__oldWidth, self.__oldHeight, 378 -self.__xOffset, -self.__yOffset) 379 self.__tileRemove.undo()
380
381 - def redo(self):
382 self.__tileRemove.redo() 383 self.getController().resize(self.__newWidth, self.__newHeight, 384 self.__xOffset, self.__yOffset)
385