Changeset 1171
- Timestamp:
- 03/22/07 02:05:12 (2 years ago)
- Files:
-
- gaphor/trunk/gaphor/UML/elementfactory.py (modified) (7 diffs)
- gaphor/trunk/gaphor/UML/event.py (modified) (2 diffs)
- gaphor/trunk/gaphor/UML/interfaces.py (modified) (2 diffs)
- gaphor/trunk/gaphor/UML/properties.py (modified) (23 diffs)
- gaphor/trunk/gaphor/UML/tests/test_elementfactory.py (modified) (4 diffs)
- gaphor/trunk/gaphor/services/tests/test_undomanager.py (modified) (3 diffs)
- gaphor/trunk/gaphor/services/undomanager.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphor/trunk/gaphor/UML/elementfactory.py
r1170 r1171 9 9 from gaphor.UML.element import Element 10 10 from gaphor.UML.diagram import Diagram 11 from gaphor.UML.interfaces import I CreateElementEvent, IRemoveElementEvent, \11 from gaphor.UML.interfaces import IElementCreateEvent, IElementDeleteEvent, \ 12 12 IFlushFactoryEvent, IModelFactoryEvent, \ 13 13 IService 14 from gaphor.UML.event import CreateElementEvent, RemoveElementEvent, \14 from gaphor.UML.event import ElementCreateEvent, ElementDeleteEvent, \ 15 15 FlushFactoryEvent, ModelFactoryEvent 16 17 18 class _xxUndoCreateAction(object):19 20 def __init__(self, factory, element):21 self.factory = factory22 self.element = element23 24 def undo(self):25 try:26 del self.factory._elements[self.element.id]27 except KeyError:28 pass # Key was probably already removed in an unlink call29 self.factory.notify(self.element, 'remove')30 component.handle(RemoveElementEvent(self.factory, self.element))31 32 def redo(self):33 self.factory._elements[self.element.id] = self.element34 self.factory.notify(self.element, 'create')35 component.handle(CreateElementEvent(self.factory, self.element))36 37 38 class _xxUndoRemoveAction(object):39 40 def __init__(self, factory, element):41 self.factory = factory42 self.element = element43 44 def undo(self):45 self.factory._elements[self.element.id] = self.element46 self.factory.notify(self.element, 'create')47 component.handle(CreateElementEvent(self.factory, self.element))48 49 def redo(self):50 del self.factory._elements[self.element.id]51 self.factory.notify(self.element, 'remove')52 component.handle(RemoveElementEvent(self.factory, self.element))53 16 54 17 … … 86 49 obj.connect('__unlink__', self._element_signal) 87 50 self.notify(obj, 'create') 88 component.handle( CreateElementEvent(self, obj))51 component.handle(ElementCreateEvent(self, obj)) 89 52 return obj 90 53 … … 211 174 #get_undo_manager().add_undo_action(_UndoRemoveAction(self, element)) 212 175 self.notify(element, 'remove') 213 component.handle( RemoveElementEvent(self, element))176 component.handle(ElementDeleteEvent(self, element)) 214 177 # elif pspec == '__relink__' and not self._elements.has_key(element.id): 215 178 # log.debug('Relinking element: %s' % element) … … 217 180 # self.notify(element, 'create') 218 181 219 component.provideUtility(ElementFactory(), IService, " element_factory")220 221 222 @component.adapter(I CreateElementEvent)182 component.provideUtility(ElementFactory(), IService, "ElementFactory") 183 184 185 @component.adapter(IElementCreateEvent) 223 186 def undo_create_event(event): 224 187 factory = event.service … … 230 193 pass # Key was probably already removed in an unlink call 231 194 factory.notify(element, 'remove') 232 component.handle( RemoveElementEvent(factory, element))195 component.handle(ElementDeleteEvent(factory, element)) 233 196 get_undo_manager().add_undo_action(_undo_create_event) 234 197 … … 236 199 237 200 238 @component.adapter(I RemoveElementEvent)201 @component.adapter(IElementDeleteEvent) 239 202 def undo_remove_event(event): 240 203 factory = event.service … … 243 206 factory._elements[element.id] = element 244 207 factory.notify(element, 'create') 245 component.handle( CreateElementEvent(factory, element))208 component.handle(ElementCreateEvent(factory, element)) 246 209 get_undo_manager().add_undo_action(_undo_remove_event) 247 210 gaphor/trunk/gaphor/UML/event.py
r1170 r1171 7 7 8 8 9 class AttributeChange dEvent(object):10 interface.implements(IAttributeChange dEvent)9 class AttributeChangeEvent(object): 10 interface.implements(IAttributeChangeEvent) 11 11 12 def __init__(self, element, attribute ):12 def __init__(self, element, attribute, old_value, new_value): 13 13 self.element = element 14 self.attribute = attribute 14 self.property = attribute 15 self.old_value = old_value 16 self.new_value = new_value 15 17 16 18 17 class AssociationChange dEvent(object):18 interface.implements(IAssociationChange dEvent)19 class AssociationChangeEvent(object): 20 interface.implements(IAssociationChangeEvent) 19 21 20 22 def __init__(self, element, associaton): 21 23 self.element = element 22 self. associaton= associaton24 self.property = associaton 23 25 24 26 25 class CreateElementEvent(object): 26 interface.implements(ICreateElementEvent) 27 class AssociationSetEvent(AssociationChangeEvent): 28 29 def __init__(self, element, associaton, old_value, new_value): 30 AssociationChangeEvent.__init__(self, element, associaton) 31 self.old_value = old_value 32 self.new_value = new_value 33 34 35 class AssociationAddEvent(AssociationChangeEvent): 36 37 def __init__(self, element, associaton, new_value): 38 AssociationChangeEvent.__init__(self, element, associaton) 39 self.new_value = new_value 40 41 42 class AssociationDeleteEvent(AssociationChangeEvent): 43 44 def __init__(self, element, associaton, old_value): 45 AssociationChangeEvent.__init__(self, element, associaton) 46 self.old_value = old_value 47 48 49 class ElementCreateEvent(object): 50 interface.implements(IElementCreateEvent, IElementFactoryEvent) 27 51 28 52 def __init__(self, service, element): … … 31 55 32 56 33 class RemoveElementEvent(object):34 interface.implements(I RemoveElementEvent)57 class ElementDeleteEvent(object): 58 interface.implements(IElementDeleteEvent, IElementFactoryEvent) 35 59 36 60 def __init__(self, service, element): gaphor/trunk/gaphor/UML/interfaces.py
r1170 r1171 6 6 from gaphor.interfaces import IService, IServiceEvent 7 7 8 class IElement ChangedEvent(interface.Interface):8 class IElementEvent(interface.Interface): 9 9 """Generic event fired when element state changes. 10 10 """ … … 12 12 13 13 14 class IAttributeChangedEvent(IElementChangedEvent): 15 """An attribute has changed. 16 """ 17 attribute = interface.Attribute("The attribute") 18 19 20 class IAssociationChangedEvent(IElementChangedEvent): 21 """An association hs changed. 22 This event may be fired for both ends of the association. 23 """ 24 association = interface.Attribute("The association") 25 26 27 class IModelFactoryEvent(IServiceEvent): 28 """A new model is loaded into the ElementFactory. 29 """ 30 31 32 class IFlushFactoryEvent(IServiceEvent): 33 """All elements are removed from the ElementFactory. 34 """ 35 36 37 class IFactoryElementEvent(IServiceEvent): 38 """Events related to individual model elements. 39 """ 40 element = interface.Attribute("The element") 41 42 43 class ICreateElementEvent(IFactoryElementEvent): 14 class IElementCreateEvent(IElementEvent): 44 15 """A new element has been created. 45 16 """ 46 17 47 18 48 class I RemoveElementEvent(IFactoryElementEvent):19 class IElementDeleteEvent(IElementEvent): 49 20 """An element is deleted from the model. 50 21 """ 51 22 52 23 24 class IElementChangeEvent(IElementEvent): 25 """Generic event fired when element state changes. 26 """ 27 property = interface.Attribute("The property that changed") 28 old_value = interface.Attribute("The property value before the change") 29 new_value = interface.Attribute("The property value after the change") 30 31 32 class IAttributeChangeEvent(IElementChangeEvent): 33 """ 34 An attribute has changed. 35 """ 36 37 38 class IAssociationChangeEvent(IElementChangeEvent): 39 """ 40 An association hs changed. 41 This event may be fired for both ends of the association. 42 """ 43 44 class IElementFactoryEvent(IServiceEvent): 45 """ 46 Events related to individual model elements. 47 """ 48 49 class IModelFactoryEvent(IElementFactoryEvent): 50 """ 51 A new model is loaded into the ElementFactory. 52 """ 53 54 55 class IFlushFactoryEvent(IElementFactoryEvent): 56 """ 57 All elements are removed from the ElementFactory. 58 """ 59 60 53 61 # vim: sw=4:et gaphor/trunk/gaphor/UML/properties.py
r1135 r1171 1 1 #!/usr/bin/env python 2 2 # vim:sw=4:et 3 """Properties used to create the UML 2.0 data model. 3 """ 4 Properties used to create the UML 2.0 data model. 4 5 5 6 The logic for creating and destroying connections between UML objects is … … 24 25 load(value): load 'value' as the current value for this property 25 26 save(save_func): send the value of the property to save_func(name, value) 26 #unlink(): remove references to other elements.27 27 """ 28 28 29 29 __all__ = [ 'attribute', 'enumeration', 'association', 'derivedunion', 'redefine' ] 30 30 31 from zope import component 31 32 from collection import collection 33 from event import AttributeChangeEvent, AssociationSetEvent, \ 34 AssociationAddEvent, AssociationDeleteEvent 32 35 import operator 33 36 from gaphor.undomanager import get_undo_manager 34 37 35 #infinite = 10000036 38 37 39 class undoaction(object): … … 53 55 get_undo_manager().add_undo_action(self.undo) 54 56 57 55 58 class umlproperty(object): 56 """Superclass for attribute, enumeration and association. 59 """ 60 Superclass for attribute, enumeration and association. 57 61 The subclasses should define a 'name' attribute that contains the name 58 62 of the property. Derived properties (derivedunion and redefine) can be … … 87 91 pass 88 92 89 # def unlink(self, obj):90 # if hasattr(obj, self._name):91 # self.__delete__(obj)92 93 93 def notify(self, obj): 94 """Notify obj that the property's value has been changed. 94 """ 95 Notify obj that the property's value has been changed. 95 96 Deriviates are also triggered to send a notify signal. 96 97 """ … … 120 121 121 122 class undoattributeaction(undoaction): 122 """This undo action contains one undo action for an attribute 123 """ 124 This undo action contains one undo action for an attribute 123 125 property. 124 126 """ … … 175 177 return 176 178 177 undoattributeaction(self, obj, self._get(obj)) 178 179 #undoattributeaction(self, obj, self._get(obj)) 180 181 old = self._get(obj) 179 182 if value == self.default and hasattr(obj, self._name): 180 183 delattr(obj, self._name) 181 184 else: 182 185 setattr(obj, self._name, value) 186 component.handle(AttributeChangeEvent(obj, self, old, value)) 183 187 self.notify(obj) 184 188 185 189 def _del(self, obj, value=None): 186 #self.old = self._get(obj)187 try: 188 undoattributeaction(self, obj, self._get(obj))190 old = self._get(obj) 191 try: 192 #undoattributeaction(self, obj, self._get(obj)) 189 193 delattr(obj, self._name) 190 194 except AttributeError: 191 195 pass 192 196 else: 197 component.handle(AttributeChangeEvent(obj, self, old, self.default)) 193 198 self.notify(obj) 194 199 195 200 196 201 class enumeration(umlproperty): 197 """Enumeration. 198 Element.enum = enumeration('enum', ('one', 'two', 'three'), 'one')""" 202 """ 203 Enumeration. 204 Element.enum = enumeration('enum', ('one', 'two', 'three'), 'one') 205 """ 199 206 200 207 def __init__(self, name, values, default): … … 221 228 if not value in self.values: 222 229 raise AttributeError, 'Value should be one of %s' % str(self.values) 223 if value != self._get(obj): 224 undoattributeaction(self, obj, self._get(obj)) 225 if value == self.default: 226 delattr(obj, self._name) 227 else: 228 setattr(obj, self._name, value) 229 self.notify(obj) 230 if value == self._get(obj): 231 return 232 233 #undoattributeaction(self, obj, self._get(obj)) 234 old = self._get(obj) 235 if value == self.default: 236 delattr(obj, self._name) 237 else: 238 setattr(obj, self._name, value) 239 component.handle(AttributeChangeEvent(obj, self, old, value)) 240 self.notify(obj) 230 241 231 242 def _del(self, obj, value=None): 232 try: 233 undoattributeaction(self, obj, self._get(obj)) 243 old = self._get(obj) 244 try: 245 #undoattributeaction(self, obj, self._get(obj)) 234 246 delattr(obj, self._name) 235 247 except AttributeError: 236 248 pass 237 249 else: 250 component.handle(AttributeChangeEvent(obj, self, old, self.default)) 238 251 self.notify(obj) 239 252 240 253 241 254 class undosetassociationaction(undoaction): 242 """Undo a 'set' action in an association. 255 """ 256 Undo a 'set' action in an association. 243 257 """ 244 258 … … 270 284 271 285 class association(umlproperty): 272 """Association, both uni- and bi-directional. 286 """ 287 Association, both uni- and bi-directional. 288 273 289 Element.assoc = association('assoc', Element, opposite='other') 274 290 … … 276 292 will cause the association to be ended if the element on the other end 277 293 of the association is unlinked. 294 278 295 If the association is a composite relationship, the value is connected to 279 296 the elements __unlink__ signal too. This will cause the value to be … … 293 310 if not isinstance(value, self.type): 294 311 raise AttributeError, 'Value for %s should be of type %s (%s)' % (self.name, self.type.__name__, type(value).__name__) 295 # TODO: avoid sending notifications:296 312 self._set(obj, value, do_notify=False) 297 313 … … 327 343 # Create the empty collection here since it might be used to 328 344 # add 329 #c = collection(self, obj, self.type)330 #setattr(obj, self._name, c)331 345 c = collection(self, obj, self.type) 332 346 setattr(obj, self._name, c) … … 336 350 337 351 def _set(self, obj, value, from_opposite=False, do_notify=True): 338 """Set a new value for our attribute. If this is a collection, append 352 """ 353 Set a new value for our attribute. If this is a collection, append 339 354 to the existing collection. 340 355 … … 348 363 if self.upper == 1: 349 364 old = self._get(obj) 365 350 366 # do nothing if we are assigned our current value: 351 367 if value is old: 352 368 return 353 369 354 # is done i s_del(): undoassociationaction(self, obj, old)370 # is done in _del(): undoassociationaction(self, obj, old) 355 371 if old: 356 372 self._del(obj, old) 373 357 374 if value is None: 358 375 return 359 if value is self._get(obj): 360 #log.debug('association: value already in obj: %s' % value) 361 return 362 363 if not from_opposite: 364 undosetassociationaction(self, obj, value) 376 377 #if value is self._get(obj): 378 # #log.debug('association: value already in obj: %s' % value) 379 # return 380 381 #if not from_opposite: 382 # undosetassociationaction(self, obj, value) 365 383 setattr(obj, self._name, value) 384 if do_notify: 385 component.handle(AssociationSetEvent(obj, self, old, value)) 366 386 else: 367 387 # Set the actual value … … 374 394 return 375 395 376 if not from_opposite:377 undosetassociationaction(self, obj, value)396 #if not from_opposite: 397 # undosetassociationaction(self, obj, value) 378 398 c.items.append(value) 399 if do_notify: 400 component.handle(AssociationAddEvent(obj, self, value)) 379 401 380 402 # Callbacks are only connected if a new relationship has … … 391 413 392 414 def _del(self, obj, value, from_opposite=False): 393 # TODO: move code to _del394 """Delete is used for element deletion and for removal of415 """ 416 Delete is used for element deletion and for removal of 395 417 elements from a list. 396 418 """ … … 400 422 if self.upper > 1: 401 423 raise Exception, 'Can not delete collections' 402 value = self._get(obj)424 old = value = self._get(obj) 403 425 if value is None: 404 426 return 405 427 406 if not from_opposite:407 undodelassociationaction(self, obj, value)428 #if not from_opposite: 429 # undodelassociationaction(self, obj, value) 408 430 409 431 if not from_opposite and self.opposite: … … 418 440 except: 419 441 pass 442 else: 443 component.handle(AssociationDeleteEvent(obj, self, value)) 444 # Remove items collection if empty 420 445 if not items: 421 446 delattr(obj, self._name) … … 426 451 pass 427 452 #print 'association._del: delattr failed for %s' % self.name 453 else: 454 component.handle(AssociationSetEvent(obj, self, value, None)) 428 455 429 456 value.disconnect(self.__on_unlink, obj) … … 432 459 self.notify(obj) 433 460 434 # def unlink(self, obj):435 # #print 'unlink', self, obj436 # lst = getattr(obj, self._name)437 # while lst:438 # self.__delete__(obj, lst[0])439 # # re-establish unlink handler:440 # value.connect('__unlink__', self.__on_unlink, obj)441 442 461 def __on_unlink(self, value, pspec, obj): 443 """Disconnect when the element on the other end of the association 462 """ 463 Disconnect when the element on the other end of the association 444 464 (value) sends the '__unlink__' signal. This is especially important 445 465 for uni-directional associations. … … 452 472 453 473 def __on_composite_unlink(self, obj, pspec, value): 454 """Unlink value if we have a part-whole (composite) relationship 474 """ 475 Unlink value if we have a part-whole (composite) relationship 455 476 (value is a composite of obj). 456 477 The implementation of value.unlink() should ensure that no deadlocks … … 489 510 def save(self, obj, save_func): 490 511 pass 491 492 # def unlink(self, obj):493 # pass494 512 495 513 def __str__(self): … … 528 546 529 547 class redefine(umlproperty): 530 """Redefined association 548 """ 549 Redefined association 531 550 Element.x = redefine('x', Class, Element.assoc) 532 551 If the redefine eclipses the original property (it has the same name) … … 553 572 if self.original.name == self.name: 554 573 self.original.save(obj, save_func) 555 556 # def unlink(self, obj):557 # self.original.unlink(obj)558 574 559 575 def __str__(self): gaphor/trunk/gaphor/UML/tests/test_elementfactory.py
r1170 r1171 86 86 global handled 87 87 p = ef.create(Parameter) 88 self.assertTrue(I CreateElementEvent.providedBy(last_event) )88 self.assertTrue(IElementCreateEvent.providedBy(last_event) ) 89 89 self.assertTrue(handled) 90 90 … … 93 93 global handled 94 94 p = ef.create(Parameter) 95 self.assertTrue(I CreateElementEvent.providedBy(last_event) )95 self.assertTrue(IElementCreateEvent.providedBy(last_event) ) 96 96 self.assertTrue(handled) 97 97 self.clearEvents() 98 98 p.unlink() 99 self.assertTrue(I RemoveElementEvent.providedBy(last_event) )99 self.assertTrue(IElementDeleteEvent.providedBy(last_event) ) 100 100 101 101 def testModelEvent(self): … … 112 112 113 113 def testUndo(self): 114 pass115 # TODO: implement116 114 from gaphor.services.undomanager import get_undo_manager 117 115 get_undo_manager().begin_transaction() … … 120 118 121 119 assert get_undo_manager().can_undo() 120 122 121 get_undo_manager().commit_transaction() 123 122 assert get_undo_manager().can_undo() 124 123 assert ef.size() == 1 124 125 125 get_undo_manager().undo_transaction() 126 126 assert not get_undo_manager().can_undo() 127 127 assert get_undo_manager().can_redo() 128 assert ef.size() == 0 128 129 130 get_undo_manager().redo_transaction() 131 assert get_undo_manager().can_undo() 132 assert not get_undo_manager().can_redo() 133 assert ef.size() == 1 134 assert ef.lselect()[0] is p 135 136 137 # vim:sw=4:et gaphor/trunk/gaphor/services/tests/test_undomanager.py
r1170 r1171 4 4 5 5 import unittest 6 from gaphor. undomanager import UndoManager, undoableproperty6 from gaphor.services.undomanager import UndoManager 7 7 8 8 class TestUndoManager(unittest.TestCase): … … 48 48 undone = [ 0 ] 49 49 def undo_action(undone=undone): 50 assert undo_manager._in_undo50 #print 'undo_action called' 51 51 undone[0] = 1 52 return undo_action 52 undo_manager.add_undo_action(redo_action) 53 54 def redo_action(undone=undone): 55 #print 'redo_action called' 56 undone[0] = -1 57 undo_manager.add_undo_action(undo_action) 53 58 54 59 undo_manager = UndoManager() … … 63 68 64 69 undo_manager.undo_transaction() 65 assert not undo_manager.can_undo() 66 assert undone[0] == 1 70 assert not undo_manager.can_undo(), undo_manager._undo_stack 71 assert undone[0] == 1, undone 67 72 68 73 undone[0] = 0 69 74 70 assert undo_manager.can_redo() 75 assert undo_manager.can_redo(), undo_manager._redo_stack 71 76 72 77 undo_manager.redo_transaction() 73 78 assert not undo_manager.can_redo() 74 79 assert undo_manager.can_undo() 75 assert undone[0] == 180 assert undone[0] == -1, undone 76 81 77 82 78 def test_undoableproperty(self):79 class A(object):80 def _set_x(self, value):81 self._x = value82 def _del_x(self):83 del self._x84 x = undoableproperty(lambda s: s._x, _set_x, _del_x)85 86 a = A()87 assert A.x88 89 a.x = 390 assert a.x == 391 assert a._x == 392 93 a.x = 994 assert a.x == 995 assert a._x == 996 97 del a.x98 assert not hasattr(a, 'x')99 assert not hasattr(a, '_x')100 101 a.x = 3102 assert hasattr(a, 'x')103 assert hasattr(a, '_x')104 105 def test_undoableproperty_property(self):106 undo_manager = UndoManager()107 class A(object):108 def _set_x(self, value):109 self._x = value110 def _del_x(self):111 del self._x112 x = undoableproperty(property=property(lambda s: s._x, _set_x, _del_x),113 undo_manager=undo_manager)114 115 a = A()116 a.x = 3117 assert a.x == 3118 119 def test_undoableproperty_in_transaction(self):120 undo_manager = UndoManager()121 class A(object):122 def _set_x(self, value):123 self._x = value124 def _del_x(self):125 del self._x126 x = undoableproperty(lambda s: s._x, _set_x, _del_x,127 undo_manager=undo_manager)128 129 a = A()130 a.x = 3131 undo_manager.begin_transaction()132 assert undo_manager._current_transaction133 a.x = 2134 135 undo_manager.commit_transaction()136 assert undo_manager._undo_stack137 assert a.x == 2138 139 undo_manager.undo_transaction()140 assert not undo_manager._undo_stack141 assert undo_manager._redo_stack142 assert a.x == 3143 144 undo_manager.redo_transaction()145 assert undo_manager._undo_stack146 assert not undo_manager._redo_stack147 148 assert a.x == 2149 150 83 # vim:sw=4:et gaphor/trunk/gaphor/services/undomanager.py
r1170 r1171 39 39 except: 40 40 undo_manager.rollback_transaction() 41 raise 41 42 else: 42 43 undo_manager.commit_transaction() … … 116 117 117 118 def clear_redo_stack(self): 118 self._redo_stack = []119 del self._redo_stack[:] 119 120 120 121 def begin_transaction(self): … … 128 129 129 130 self._current_transaction = Transaction() 130 self.clear_redo_stack()131 131 self._transaction_depth += 1 132 132 … … 138 138 if not self._current_transaction: 139 139 return 140 141 if self._redo_stack:142 self.clear_redo_stack()143 140 144 141 self._current_transaction.add(action) … … 153 150 if self._transaction_depth == 0: 154 151 if self._current_transaction.can_execute(): 152 self.clear_redo_stack() 155 153 self._undo_stack.append(self._current_transaction) 156 154 else: … … 192 190 transaction = self._undo_stack.pop() 193 191 192 self._current_transaction = Transaction() 193 self._transaction_depth += 1 194 194 195 transaction.execute() 196 197 assert self._transaction_depth == 1 198 self._redo_stack.append(self._current_transaction) 199 self._current_transaction = None 200 self._transaction_depth = 0 195 201 component.handle(UndoManagerStateChanged(self)) 196 202 … … 201 207 transaction = self._redo_stack.pop() 202 208 209 self._current_transaction = Transaction() 210 self._transaction_depth += 1 211 203 212 transaction.execute() 213 214 assert self._transaction_depth == 1 215 self._undo_stack.append(self._current_transaction) 216 self._current_transaction = None 217 self._transaction_depth = 0 218 204 219 component.handle(UndoManagerStateChanged(self)) 205 220
