Changeset 1132
- Timestamp:
- 02/18/07 20:40:28 (2 years ago)
- Files:
-
- gaphor/trunk/data/plugins/python/plugin.xml (modified) (1 diff)
- gaphor/trunk/doc/capabilities.txt (deleted)
- gaphor/trunk/doc/diagram-in-python.txt (deleted)
- gaphor/trunk/doc/gaphor.txt (modified) (2 diffs)
- gaphor/trunk/doc/plan.txt (deleted)
- gaphor/trunk/doc/ui-design.txt (deleted)
- gaphor/trunk/doc/usecases.txt (deleted)
- gaphor/trunk/gaphor/UML/properties.py (modified) (5 diffs)
- gaphor/trunk/gaphor/actions/diagramactions.py (modified) (2 diffs)
- gaphor/trunk/gaphor/actions/itemactions.py (modified) (32 diffs)
- gaphor/trunk/gaphor/diagram/association.py (modified) (1 diff)
- gaphor/trunk/gaphor/diagram/feature.py (modified) (1 diff)
- gaphor/trunk/gaphor/misc/partial.py (added)
- gaphor/trunk/gaphor/tests/test_undomanager.py (modified) (3 diffs)
- gaphor/trunk/gaphor/undomanager.py (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphor/trunk/data/plugins/python/plugin.xml
r543 r1132 15 15 --> 16 16 <module name="logilab.common"/> 17 <module name="logilab.common.astng"/> 17 18 </require> 18 19 gaphor/trunk/doc/gaphor.txt
r173 r1132 2 2 ~~~~~~ 3 3 4 Gaphor is a UML CASE tool aiming for symplicity. It is written in Python and 5 C. The goal is to write as much as possible in Python (easy scriptable for 6 code generators) and only some elementary parts are written in C (this involves 7 mainly the graphical items). 4 Gaphor is a UML CASE tool aiming for symplicity. It is written in Python. 5 The goal is to write as much as possible in Python (easy scriptable for 6 code generators). 8 7 9 8 So here's what has to be created: … … 40 39 ~~~~~~~~~~~~~~~ 41 40 Of course we need something to draw. The items that are to be placed on the 42 diagrams need to be created. Since graphical stuff can take up a lot of time 43 I think it is wise to create those things in C, instead of Python. 44 We will however create hooks for the graphical items, so we can at least move 45 them around and place them on a diagram from within Python. 41 diagrams need to be created. 46 42 47 43 gaphor/trunk/gaphor/UML/properties.py
r443 r1132 36 36 37 37 class undoaction(object): 38 """This undo action contains one undo action for an attribute 39 property. 38 """ 39 Base class for undo actions. 40 41 This is an abstract class. The subclasses should implement an undo() 42 and a redo() method. 43 The undo() method should return a pointer to the redo() method and 44 visa-versa. 45 40 46 """ 41 47 … … 45 51 self.value = value 46 52 #print 'adding new property', prop.name, obj, value 47 get_undo_manager().add_undo_action(self) 48 53 get_undo_manager().add_undo_action(self.undo) 49 54 50 55 class umlproperty(object): … … 123 128 setattr(self.obj, self.prop._name, self.value) 124 129 self.prop.notify(self.obj) 130 return self.redo 125 131 126 132 def redo(self): 127 133 setattr(self.obj, self.prop._name, self.redo_value) 128 134 self.prop.notify(self.obj) 135 return self.undo 129 136 130 137 … … 239 246 #log.debug('undosetassociationaction del: %s %s %s' % (self.obj, self.prop.name, self.value)) 240 247 self.prop._del(self.obj, self.value) 248 return self.redo 241 249 242 250 def redo(self): 243 251 #log.debug('undosetassociationaction set: %s %s %s' % (self.obj, self.prop.name, self.value)) 244 252 self.prop._set(self.obj, self.value) 253 return self.undo 245 254 246 255 … … 252 261 #log.debug('undodelassociationaction set: %s %s %s' % (self.obj, self.prop.name, self.value)) 253 262 self.prop._set(self.obj, self.value) 263 return self.redo 254 264 255 265 def redo(self): 256 266 #log.debug('undodelassociationaction del: %s %s %s' % (self.obj, self.prop.name, self.value)) 257 267 self.prop._del(self.obj, self.value) 268 return self.undo 258 269 259 270 gaphor/trunk/gaphor/actions/diagramactions.py
r1121 r1132 9 9 from gaphor import resource 10 10 from gaphor import UML 11 from gaphor.undomanager import get_undo_manager, undoable11 from gaphor.undomanager import get_undo_manager, transactional 12 12 from gaphor.misc.action import Action, CheckAction, RadioAction 13 13 from gaphor.misc.action import register_action as _register_action … … 342 342 self._item.load(name, str(value)) 343 343 344 @ undoable344 @transactional 345 345 def execute(self): 346 346 view = self._window.get_current_diagram_view() gaphor/trunk/gaphor/actions/itemactions.py
r1121 r1132 7 7 from gaphor import UML 8 8 from gaphor.diagram import items 9 from gaphor.undomanager import undoable9 from gaphor.undomanager import transactional 10 10 from gaphor.misc.action import Action, CheckAction, RadioAction, ObjectAction 11 11 from gaphor.misc.action import register_action … … 132 132 self.active = item.subject and item.subject.isAbstract 133 133 134 @ undoable134 @transactional 135 135 def execute(self): 136 136 item = get_parent_focus_item(self._window) … … 158 158 # self.active = item.subject and item.subject.isAbstract 159 159 # 160 # @ undoable160 # @transactional 161 161 # def execute(self): 162 162 # item = self._window.get_current_diagram_view().focused_item … … 188 188 self.sensitive = item.show_attributes 189 189 190 @ undoable190 @transactional 191 191 def execute(self): 192 192 view = self._window.get_current_diagram_view() … … 231 231 self.sensitive = item.show_operations 232 232 233 @ undoable233 @transactional 234 234 def execute(self): 235 235 view = self._window.get_current_diagram_view() … … 261 261 # self._window = window 262 262 # 263 # @ undoable263 # @transactional 264 264 # def execute(self): 265 265 # #subject = get_parent_focus_item(self._window).subject … … 302 302 self.active = item.show_attributes 303 303 304 @ undoable304 @transactional 305 305 def execute(self): 306 306 item = get_parent_focus_item(self._window) … … 327 327 self.active = item.show_operations 328 328 329 @ undoable329 @transactional 330 330 def execute(self): 331 331 item = get_parent_focus_item(self._window) … … 361 361 tooltip='Add a segment to the line' 362 362 363 @ undoable363 @transactional 364 364 def execute(self): 365 365 item, segment = self.get_item_and_segment() … … 383 383 pass 384 384 385 @ undoable385 @transactional 386 386 def execute(self): 387 387 item, segment = self.get_item_and_segment() … … 408 408 pass 409 409 410 @ undoable410 @transactional 411 411 def execute(self): 412 412 fi = get_parent_focus_item(self._window) … … 436 436 # pass 437 437 # 438 # @ undoable438 # @transactional 439 439 # def execute(self): 440 440 # fi = get_parent_focus_item(self._window) … … 465 465 pass 466 466 467 @ undoable467 @transactional 468 468 def execute(self): 469 469 fi = get_parent_focus_item(self._window) … … 482 482 self._window = window 483 483 484 @ undoable484 @transactional 485 485 def execute(self): 486 486 fi = get_parent_focus_item(self._window) … … 509 509 pass 510 510 511 @ undoable511 @transactional 512 512 def execute(self): 513 513 item = self.get_association_end() … … 592 592 pass 593 593 594 @ undoable594 @transactional 595 595 def execute(self): 596 596 if self.active: … … 729 729 pass 730 730 731 @ undoable731 @transactional 732 732 def execute(self): 733 733 if self.active: … … 790 790 self.active = item.auto_dependency 791 791 792 @ undoable792 @transactional 793 793 def execute(self): 794 794 item = get_parent_focus_item(self._window) … … 815 815 self.active = item.subject and item.subject.isIndirectlyInstantiated 816 816 817 @ undoable817 @transactional 818 818 def execute(self): 819 819 item = get_parent_focus_item(self._window) … … 856 856 self.sensitive = self._isSensitive(cls_item.subject, item) 857 857 858 @ undoable858 @transactional 859 859 def execute(self): 860 860 cls = self._getParent().subject … … 912 912 self.sensitive = isinstance(item, items.InterfaceItem) 913 913 914 @ undoable914 @transactional 915 915 def execute(self): 916 916 item = get_parent_focus_item(self._window) … … 927 927 tooltip = 'View details' 928 928 929 @ undoable929 @transactional 930 930 def execute(self): 931 931 item = get_parent_focus_item(self._window) … … 962 962 self.active = False 963 963 964 @ undoable964 @transactional 965 965 def execute(self): 966 966 item = get_parent_focus_item(self._window) … … 1042 1042 new_rel.unlink() 1043 1043 1044 @ undoable1044 @transactional 1045 1045 def execute(self): 1046 1046 # TODO: AJM: disabled action … … 1082 1082 pass 1083 1083 1084 @ undoable1084 @transactional 1085 1085 def execute(self): 1086 1086 if self.active: … … 1142 1142 self.active = item.props.show_ordering 1143 1143 1144 @ undoable1144 @transactional 1145 1145 def execute(self): 1146 1146 item = get_parent_focus_item(self._window) … … 1168 1168 pass 1169 1169 1170 @ undoable1170 @transactional 1171 1171 def execute(self): 1172 1172 item = get_parent_focus_item(self._window) … … 1192 1192 pass 1193 1193 1194 @ undoable1194 @transactional 1195 1195 def execute(self): 1196 1196 """ … … 1288 1288 pass 1289 1289 1290 @ undoable1290 @transactional 1291 1291 def execute(self): 1292 1292 """ … … 1343 1343 self.active = isinstance(item, items.ConnectorEndItem) 1344 1344 1345 @ undoable1345 @transactional 1346 1346 def execute(self): 1347 1347 item = self.focused_item … … 1373 1373 self.active = (self.interface == item.subject) 1374 1374 1375 @ undoable1375 @transactional 1376 1376 def execute(self): 1377 1377 item = self.focused_item … … 1402 1402 self.active = item.props.has_lifetime 1403 1403 1404 @ undoable1404 @transactional 1405 1405 def execute(self): 1406 1406 item = get_parent_focus_item(self._window) gaphor/trunk/gaphor/diagram/association.py
r1121 r1132 22 22 23 23 from gaphor import resource, UML 24 from gaphor.undomanager import undoable25 24 #from gaphor.diagram import Relationship 26 25 from gaphor.diagram.diagramitem import SubjectSupport gaphor/trunk/gaphor/diagram/feature.py
r1121 r1132 7 7 from gaphas.item import Item 8 8 from diagramitem import DiagramItem 9 from gaphor.undomanager import undoable10 9 from gaphor.diagram import DiagramItemMeta 11 10 from gaphas.util import text_extents, text_set_font gaphor/trunk/gaphor/tests/test_undomanager.py
r1125 r1132 8 8 class TestUndoManager(unittest.TestCase): 9 9 10 def test_1(self): 10 def test_transactions(self): 11 11 12 undo_manager = UndoManager() 12 13 assert undo_manager._transaction_depth == 0 … … 30 31 assert undo_manager._current_transaction is None 31 32 32 def test_ 2(self):33 def test_not_in_transaction(self): 33 34 undo_manager = UndoManager() 34 35 … … 40 41 undo_manager.add_undo_action(action) 41 42 assert undo_manager._current_transaction 43 assert undo_manager.can_undo() 42 44 assert len(undo_manager._current_transaction._actions) == 1 43 45 46 47 def test_actions(self): 48 undone = [ 0 ] 49 def undo_action(undone=undone): 50 assert undo_manager._in_undo 51 undone[0] = 1 52 return undo_action 53 54 undo_manager = UndoManager() 55 56 undo_manager.begin_transaction() 57 undo_manager.add_undo_action(undo_action) 58 assert undo_manager._current_transaction 59 assert undo_manager.can_undo() 60 assert len(undo_manager._current_transaction._actions) == 1 61 62 undo_manager.commit_transaction() 63 64 undo_manager.undo_transaction() 65 assert not undo_manager.can_undo() 66 assert undone[0] == 1 67 68 undone[0] = 0 69 70 assert undo_manager.can_redo() 71 72 undo_manager.redo_transaction() 73 assert not undo_manager.can_redo() 74 assert undo_manager.can_undo() 75 assert undone[0] == 1 44 76 # vim:sw=4:et gaphor/trunk/gaphor/undomanager.py
r1125 r1132 1 1 # vim:sw=4:et: 2 3 import gobject 2 """ 3 Undo management for Gaphor. 4 5 Undoing and redoing actions is managed through the UndoManager. 6 7 An undo action should be a callable object (called with no arguments). 8 9 An undo action should return a callable object that acts as redo function. 10 If None is returned the undo action is considered to be the redo action as well. 11 12 NOTE: it would be nice to use actions in conjunction with functools.partial, 13 but that's Python2.5 stuff.. 14 """ 4 15 5 16 … … 10 21 11 22 12 def undoable(func): 13 """Descriptor. Enables an undo transaction around the method/function. 23 def transactional(func): 24 """ 25 Descriptor. Begins a transaction around the method/function. 26 27 TODO: In casse of an exception, roll back the transaction if the transaction 28 level == 1. 14 29 """ 15 30 def wrapper(*args, **kwargs): … … 22 37 return wrapper 23 38 39 24 40 class TransactionError(Exception): 25 41 … … 31 47 """ 32 48 A transaction. Every action that is added between a begin_transaction() 33 and a commit_transaction() call is recorded in a transaction, do it can 34 be played back when a transaction is undone. 49 and a commit_transaction() call is recorded in a transaction, so it can 50 be played back when a transaction is executed. This executing a 51 transaction has the effect of performing the actions recorded, which will 52 typically undo actions performed by the user. 35 53 """ 36 54 … … 41 59 self._actions.append(action) 42 60 43 def can_ undo(self):61 def can_execute(self): 44 62 return self._actions and True or False 45 63 46 def undo(self):64 def execute(self): 47 65 self._actions.reverse() 66 contra_transaction = Transaction() 48 67 for action in self._actions: 49 68 try: 50 #log.debug('Undoing action %s' % action)51 action.undo()69 contra_action = action() 70 contra_transaction.add(contra_action or action) 52 71 except Exception, e: 53 72 log.error('Error while undoing action %s' % action, e) 54 55 def redo(self): 56 self._actions.reverse() 57 for action in self._actions: 58 try: 59 #log.debug('Redoing action %s' % action) 60 action.redo() 61 except Exception, e: 62 log.error('Error while redoing action %s' % action, e) 63 73 return contra_transaction 64 74 65 75 class UndoManager(object): … … 67 77 Simple transaction manager for Gaphor. 68 78 This transaction manager supports nested transactions. 79 80 The Undo manager sports an undo and a redo stack. Each stack contains 81 a set of actions that can be executed, just by calling them (e.i action()) 82 If something is returned by an action, that is considered the callable 83 to be used to undo or redo the last performed action. 69 84 """ 70 85 … … 126 141 self._transaction_depth -= 1 127 142 if self._transaction_depth == 0: 128 if self._current_transaction.can_ undo():143 if self._current_transaction.can_execute(): 129 144 self._undo_stack.append(self._current_transaction) 130 145 else: … … 154 169 try: 155 170 self._in_undo = True 156 transaction.undo()171 redo_transaction = transaction.execute() 157 172 finally: 158 173 self._in_undo = False 159 self._redo_stack.append( transaction)174 self._redo_stack.append(redo_transaction) 160 175 161 176 def redo_transaction(self): … … 166 181 try: 167 182 self._in_undo = True 168 transaction.redo()183 undo_transaction = transaction.execute() 169 184 finally: 170 185 self._in_undo = False 171 self._undo_stack.append( transaction)186 self._undo_stack.append(undo_transaction) 172 187 173 188 def in_transaction(self):
