*** empty log message ***

git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@424 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
Arjan Molenaar 2004-10-22 13:47:47 +00:00
parent bd1ef40ab6
commit 1a87f7f550
28 changed files with 663 additions and 234 deletions

View File

@ -1,7 +1,18 @@
2004-10-22 Arjan Molenaar <arjanmolenaar@hetnet.nl>
* gaphor/diagram/diagramitem.py: do no longer automatically unlink
in diagramassociation. unlink in DiagramItem.unlink() instead.
2004-10-15 Arjan Molenaar <arjanmolenaar@hetnet.nl>
* gaphor/diagram/association.py: arrow heads were drawn in the wrong
direction. corrected
* gaphor/*: got complete rid of the __relink__ signal.
* gaphor/diagram/itemactions.py: added undo transactions to actions.
* gaphor/UML/properties/py: implemented undo awareness for attributes,
enumerations and associations.
* gaphor/diagram/association.py, nameditem.py, feature.py: added
undo awareness to on_editing_done() methods.
2004-10-11 Arjan Molenaar <arjanmolenaar@hetnet.nl>
@ -12,6 +23,8 @@
* gaphor/diagram/actions.py: classes, packages, etc. are assigned a
unique name on creation.
* gaphor/ui/mainwindow.py: added toolbox items to the menu.
* gaphor/ui/diagramtab.py: 'Delete' has the same meaning as Ctrl-d
from now on.
2004-10-07 Arjan Molenaar <arjanmolenaar@hetnet.nl>

16
TODO
View File

@ -5,18 +5,24 @@ As always, there is much to do...
Problem: the existing <Placeholder> stuff works only on constrcution time.
For this we need a menu that can change during the life of the application.
- write really good test cases for undo functionality.
- confirmation window when creating a new model.
- An option that shows the selected item (in the namespace view) in a diagram.
- Copy/Paste for diagramitems
- How to figure out if a module exists without loading it?
- fix accelarators (HOW?), partially done, accelerators on items in popup menus
does not work.
Accelerators should be activated by the window, should not depend on menu
items
Accelerators should be activated by the (Abstract)Window, should not depend
on menu items
- Undo/redo functionality doesn;t work as espected
- Undo/redo functionality doesn't work as espected
==> Create our own UndoManager, this should also register connect/disconnect
actions on gaphor.UML classes.
- Exporting diagrams to UML XMI, code, images (SVG/png), etc. - make a plugin!
@ -27,7 +33,9 @@ As always, there is much to do...
Package.
#- Set up a plugin architecture. Since the internals of gaphor are pretty
modular, plugins should not be that hard.
modular, plugins should not be that hard. I'm very happy with the new one
it is easy to program, yet powerful (due to the XML description file, which
allows gaphor to check several dependencies upfront.
UI
--

View File

@ -25,6 +25,9 @@ def select(expression=None):
"""
return _default_element_factory.select(expression)
def flush():
_default_element_factory.flush()
if 0 and __debug__:
# Keep track of all model elements that are created
from gaphor.misc.aspects import ReferenceAspect, LoggerAspect, weave_method

View File

@ -10,6 +10,7 @@ __date__ = '$date$'
import gobject
import diacanvas
import gaphor.misc.uniqueid as uniqueid
from gaphor.undomanager import get_undo_manager
from uml2 import Namespace, PackageableElement
class DiagramCanvas(diacanvas.Canvas):
@ -24,6 +25,7 @@ class DiagramCanvas(diacanvas.Canvas):
def __init__(self, diagram):
self.__gobject_init__()
self._diagram = diagram
self.set_undo_manager(get_undo_manager())
diagram = property(lambda d: d._diagram)

View File

@ -14,8 +14,8 @@ unlink()
Remove all references to the element. This is done by emiting the
'__unlink__' signal to all attached signals. unlink() can not be called
recursively.
relink()
Inverse operation of unlink(). Used by diagram items during undo operations.
#relink()
# Inverse operation of unlink(). Used by diagram items during undo operations.
connect ('name', callback, *data) or
connect (('name', 'other_property'), callback, *data)
@ -103,10 +103,10 @@ class Element(object):
#log.debug('Element.unlink(%s)' % self)
self.__unlink('__unlink__')
def relink(self):
"""Undo the unlink operation."""
log.debug('Element.relink(%s)' % self)
self.__unlink('__relink__')
# def relink(self):
# """Undo the unlink operation."""
# log.debug('Element.relink(%s)' % self)
# self.__unlink('__relink__')
def connect(self, names, callback, *data):
"""Attach 'callback' to a list of names. Names may also be a string.

View File

@ -4,10 +4,44 @@
import gaphor
import gaphor.misc.uniqueid as uniqueid
from gaphor.undomanager import get_undo_manager
from element import Element
from diagram import Diagram
import gaphor.misc.odict
class _UndoCreateAction(object):
def __init__(self, factory, element):
self.factory = factory
self.element = element
def undo(self):
try:
del self.factory._elements[self.element.id]
except KeyError:
pass # Key was probably already removed in an unlink call
self.factory.notify(self.element, 'remove')
def redo(self):
self.factory._elements[self.element.id] = self.element
self.factory.notify(self.element, 'create')
class _UndoRemoveAction(object):
def __init__(self, factory, element):
self.factory = factory
self.element = element
def undo(self):
self.factory._elements[self.element.id] = self.element
self.factory.notify(obj, 'create')
def redo(self):
del self.factory._elements[self.element.id]
self.factory.notify(obj, 'remove')
class ElementFactory(object):
"""The ElementFactory is used to create elements and do lookups to
elements.
@ -36,6 +70,7 @@ class ElementFactory(object):
assert issubclass(type, Element)
obj = type(id, self)
self._elements[id] = obj
get_undo_manager().add_undo_action(_UndoCreateAction(self, obj))
obj.connect('__unlink__', self.__element_signal)
self.notify(obj, 'create')
return obj
@ -138,10 +173,12 @@ class ElementFactory(object):
#log.debug('element %s send signal %s' % (element, name))
if pspec == '__unlink__' and self._elements.has_key(element.id):
#log.debug('Unlinking element: %s' % element)
# TODO: make undo action
del self._elements[element.id]
get_undo_manager().add_undo_action(_UndoRemoveAction(self, element))
self.notify(element, 'remove')
elif pspec == '__relink__' and not self._elements.has_key(element.id):
log.debug('Relinking element: %s' % element)
self._elements[element.id] = element
self.notify(element, 'create')
# elif pspec == '__relink__' and not self._elements.has_key(element.id):
# log.debug('Relinking element: %s' % element)
# self._elements[element.id] = element
# self.notify(element, 'create')

View File

@ -31,9 +31,123 @@ __all__ = [ 'attribute', 'enumeration', 'association', 'derivedunion', 'redefine
from collection import collection
import operator
from gaphor.undomanager import get_undo_manager
#infinite = 100000
class attributeundoaction(object):
"""This undo action contains one undo action for an attribute
property.
"""
def __init__(self, prop, obj, value):
self.prop = prop
self.obj = obj
self.value = value
get_undo_manager().add_undo_action(self)
def undo(self):
self.redo_value = self.prop._get(self.obj)
setattr(self.obj, self.prop._name, self.value)
self.prop.notify(self.obj)
def redo(self):
setattr(self.obj, self.prop._name, self.redo_value)
self.prop.notify(self.obj)
class associationundoaction(object):
"""The state of an association can be reverted with this action.
"""
def __init__(self, prop, obj, value):
self.prop = prop
self.obj = obj
self.value = value
#print 'adding undo action', prop, obj, value
get_undo_manager().add_undo_action(self)
def undo(self):
"""Undo an event. This is very hard to read.
TODO: cleanup and structure association code!!!!!
TODO: self.value returns to [] on undo
"""
prop = self.prop
obj = self.obj
log.debug('undo association %s' % prop.name)
if prop.upper > 1:
l = prop._get(obj)
self.redo_value = list(l)
#print 'redo_value =', self.redo_value, self.value
for item in l:
if item not in self.value:
if prop.opposite:
getattr(type(item), prop.opposite)._del(item, obj)
prop._del(obj, item)
l = prop._get(obj)
for item in self.value:
if item not in l:
if prop._set2(obj, item):
# Set opposite side.
# Use type(value) since the property may be overridden:
if prop.opposite:
getattr(type(item), prop.opposite)._set(item, obj)
#print 'redo_value =', self.redo_value, self.value
prop.notify(obj)
else:
value = self.redo_value = prop._get(obj)
if self.value is None:
# _del does a notify().
if value is None:
return
if prop.opposite:
getattr(type(value), prop.opposite)._del(value, obj)
prop._del(obj, prop._get(obj))
else:
if prop._set2(obj, self.value):
if prop.opposite:
getattr(type(value), prop.opposite)._set(value, obj)
prop.notify(obj)
def redo(self):
prop = self.prop
obj = self.obj
log.debug('redo association %s' % prop.name)
if prop.upper > 1:
#print 'setting... on', prop, obj, self.redo_value
l = prop._get(obj)
for item in l:
if item not in self.redo_value:
if prop.opposite:
getattr(type(item), prop.opposite)._del(item, obj)
prop._del(obj, item)
l = prop._get(obj)
for item in self.redo_value:
if item not in l:
if prop._set2(obj, item):
# Set opposite side.
# Use type(value) since the property may be overridden:
if prop.opposite:
getattr(type(item), prop.opposite)._set(item, obj)
#print prop._get(obj), self.redo_value
prop.notify(obj)
else:
#self.redo_value = prop._get(obj)
#print self.redo_value
if self.redo_value is None:
# _del does a notify().
value = prop._get(obj)
if value is None:
return
if prop.opposite:
getattr(type(value), prop.opposite)._del(value, obj)
prop._del(obj, prop._get(obj))
else:
if prop._set2(obj, self.redo_value):
if prop.opposite:
getattr(type(self.redo_value), prop.opposite)._set(self.redo_value, obj)
prop.notify(obj)
class umlproperty(object):
"""Superclass for attribute, enumeration and association.
The subclasses should define a 'name' attribute that contains the name
@ -118,10 +232,6 @@ class attribute(umlproperty):
#print 'attribute.load:', self.name, self.type, value,
if self.type is not object:
value = self.type(value)
#if not isinstance(value, self.type):
# if type(self.type) is not type(()):
# else:
# raise AttributeError, 'Value should be of type %s (is %s)' % (self.type.__name__, type(value))
setattr(obj, self._name, value)
def __str__(self):
@ -139,7 +249,9 @@ class attribute(umlproperty):
def _set(self, obj, value):
if value is not None and not isinstance(value, self.type):
raise AttributeError, 'Value should be of type %s' % hasattr(self.type, '__name__') and self.type.__name__ or self.type
#self.old = self._get(obj)
attributeundoaction(self, obj, self._get(obj))
if value == self.default and hasattr(obj, self._name):
delattr(obj, self._name)
else:
@ -149,6 +261,7 @@ class attribute(umlproperty):
def _del(self, obj, value=None):
#self.old = self._get(obj)
try:
attributeundoaction(self, obj, self._get(obj))
delattr(obj, self._name)
except AttributeError:
pass
@ -184,6 +297,7 @@ class enumeration(umlproperty):
if not value in self.values:
raise AttributeError, 'Value should be one of %s' % str(self.values)
if value != self._get(obj):
attributeundoaction(self, obj, self._get(obj))
if value == self.default:
delattr(obj, self._name)
else:
@ -192,6 +306,7 @@ class enumeration(umlproperty):
def _del(self, obj, value=None):
try:
attributeundoaction(self, obj, self._get(obj))
delattr(obj, self._name)
except AttributeError:
pass
@ -277,11 +392,14 @@ class association(umlproperty):
# do nothing if we are assigned our current value:
if value is old:
return
associationundoaction(self, obj, old)
if old:
self.__delete__(obj, old)
if value is None:
#self.notify(obj)
return
else:
associationundoaction(self, obj, list(self._get(obj)))
# if we needed to set our own side, set the opposite
# Call _set2() so we can make sure the opposite side is set before
@ -304,6 +422,12 @@ class association(umlproperty):
value = self._get(obj)
if value is None:
return
# Save undo info
if self.upper > 1:
associationundoaction(self, obj, list(self._get(obj)))
else:
associationundoaction(self, obj, self._get(obj))
if self.opposite:
getattr(type(value), self.opposite)._del(value, obj)
self._del(obj, value)
@ -397,9 +521,9 @@ class association(umlproperty):
self.__delete__(obj, value)
# re-establish unlink handler:
value.connect('__unlink__', self.__on_unlink, obj)
else:
print 'RELINK'
self.__set__(obj, value)
#else:
# print 'RELINK'
# self.__set__(obj, value)
def __on_composite_unlink(self, obj, pspec, value):
"""Unlink value if we have a part-whole (composite) relationship
@ -411,9 +535,9 @@ class association(umlproperty):
if pspec == '__unlink__':
value.unlink()
obj.connect('__unlink__', self.__on_composite_unlink, value)
else:
print 'RELINK'
value.relink()
#else:
#print 'RELINK'
#value.relink()
class derivedunion(umlproperty):

View File

@ -563,7 +563,9 @@ class AssociationEnd(diacanvas.CanvasItem, diacanvas.CanvasEditable, DiagramItem
def on_editable_editing_done(self, shape, new_text):
if shape in (self._name, self._mult):
if self.subject and (shape == self._name or new_text != ''):
self.canvas.get_undo_manager().begin_transaction()
self.subject.parse(new_text)
self.canvas.get_undo_manager().commit_transaction()
#self.set_text()
#log.info('editing done')

View File

@ -34,7 +34,7 @@ class AttributeItem(FeatureItem):
+ notifiers)
def on_subject_notify__name(self, subject, pspec):
self._expression.set_text(self.subject.render())
self._expression.set_text(self.subject.render() or '')
self.request_update()
on_subject_notify__isDerived = on_subject_notify__name

View File

@ -43,10 +43,10 @@ class diagramassociation(association):
# TODO: Add some extra notification here to tell the diagram
# item that the reference is about to be removed.
association._del(self, obj, value)
if len(value.presentation) == 0 or \
len(value.presentation) == 1 and obj in value.presentation:
#log.debug('diagramassociation._del: No more presentations: unlinking')
value.unlink()
# if len(value.presentation) == 0 or \
# len(value.presentation) == 1 and obj in value.presentation:
# #log.debug('diagramassociation._del: No more presentations: unlinking')
# value.unlink()
class DiagramItem(Presentation):
@ -143,16 +143,23 @@ class DiagramItem(Presentation):
#log.debug('DiagramItem.unlink(%s)' % self)
# emit the __unlink__ signal the way UML.Element would have done:
self.emit('__unlink__', '__unlink__')
subject = self.subject
# remove the subject if we have one
if self.subject:
del self.subject
if subject and len(subject.presentation) == 0:
#log.debug('diagramitem.unlink: No more presentations: unlinking')
value.unlink()
self.set_property('parent', None)
def relink(self):
"""Relinking is done by popping the undo stack...
"""
log.info('RELINK DiagramItem')
#self.emit('__unlink__', '__relink__')
# def relink(self):
# """Relinking is done by popping the undo stack...
# """
# log.info('RELINK DiagramItem')
# #self.emit('__unlink__', '__relink__')
# gaphor.UML.Element like signal interface:

View File

@ -58,8 +58,11 @@ class FeatureItem(CanvasItem, CanvasEditable, DiagramItem):
def do_set_property(self, pspec, value):
if pspec.name == 'expression':
if self.subject:
self.preserve_property('expression')
#self.preserve_property('expression')
self.canvas.get_undo_manager().begin_transaction()
self.subject.parse(value)
self.canvas.get_undo_manager().commit_transaction()
self._expression.set_text(self.subject.render())
self.request_update()
else:

View File

@ -7,6 +7,7 @@ import diacanvas
import gaphor
import gaphor.diagram
import gaphor.UML as UML
from gaphor.undomanager import get_undo_manager
from gaphor.misc.action import Action, CheckAction, RadioAction, ObjectAction
from gaphor.misc.action import register_action
@ -56,14 +57,9 @@ class EditItemAction(Action):
def execute(self):
# Stay backwards compatible:
if diacanvas.diacanvas_version < (0, 14, 0):
item = self._window.get_current_diagram_view().focus_item.item
#assert isinstance(subject, (UML.Property, UML.Operation))
item.edit()
else:
view = self._window.get_current_diagram_view()
wx, wy = view.window_to_world(*view.get_pointer())
view.start_editing(view.focus_item, wx, wy)
view = self._window.get_current_diagram_view()
wx, wy = view.window_to_world(*view.get_pointer())
view.start_editing(view.focus_item, wx, wy)
register_action(EditItemAction, 'ItemFocus')
@ -104,7 +100,9 @@ class AbstractClassAction(CheckAction):
def execute(self):
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
item.subject.isAbstract = self.active
get_undo_manager().commit_transaction()
register_action(AbstractClassAction, 'ItemFocus')
@ -131,20 +129,28 @@ class CreateAttributeAction(Action):
self.sensitive = item.get_property('show-attributes')
def execute(self):
view = self._window.get_current_diagram_view()
focus_item = get_parent_focus_item(self._window)
subject = focus_item.subject
assert isinstance(subject, (UML.Class, UML.Interface))
elemfact = gaphor.resource(UML.ElementFactory)
get_undo_manager().begin_transaction()
attribute = elemfact.create(UML.Property)
attribute.name = 'new'
subject.ownedAttribute = attribute
# Select this item for editing
presentation = attribute.presentation
focus_item.update_now()
wx, wy = view.window_to_world(*view.get_pointer())
for f in focus_item.groupable_iter():
if f in presentation:
f.edit()
vf = view.find_view_item(f)
view.start_editing(vf, wx, wy)
break
get_undo_manager().commit_transaction()
register_action(CreateAttributeAction, 'ShowAttributes', 'ItemFocus')
@ -167,20 +173,27 @@ class CreateOperationAction(Action):
self.sensitive = item.get_property('show-operations')
def execute(self):
view = self._window.get_current_diagram_view()
focus_item = get_parent_focus_item(self._window)
subject = focus_item.subject
assert isinstance(subject, UML.Classifier)
elemfact = gaphor.resource(UML.ElementFactory)
get_undo_manager().begin_transaction()
operation = elemfact.create(UML.Operation)
operation.name = 'new'
subject.ownedOperation = operation
# Select this item for editing
presentation = operation.presentation
focus_item.update_now()
wx, wy = view.window_to_world(*view.get_pointer())
for f in focus_item.groupable_iter():
if f in presentation:
f.edit()
vf = view.find_view_item(f)
view.start_editing(vf, wx, wy)
break
get_undo_manager().commit_transaction()
register_action(CreateOperationAction, 'ShowOperations', 'ItemFocus')
@ -194,7 +207,9 @@ class DeleteFeatureAction(Action):
#subject = get_parent_focus_item(self._window).subject
item = self._window.get_current_diagram_view().focus_item.item
#assert isinstance(subject, (UML.Property, UML.Operation))
get_undo_manager().begin_transaction()
item.subject.unlink()
get_undo_manager().commit_transaction()
class DeleteAttributeAction(DeleteFeatureAction):
id = 'DeleteAttribute'
@ -231,7 +246,9 @@ class ShowAttributesAction(CheckAction):
def execute(self):
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
item.set_property('show-attributes', self.active)
get_undo_manager().commit_transaction()
register_action(ShowAttributesAction, 'ItemFocus')
@ -256,7 +273,9 @@ class ShowOperationsAction(CheckAction):
def execute(self):
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
item.set_property('show-operations', self.active)
get_undo_manager().commit_transaction()
register_action(ShowOperationsAction, 'ItemFocus')
@ -290,7 +309,9 @@ class AddSegmentAction(SegmentAction):
def execute(self):
item, segment = self.get_item_and_segment()
if item:
get_undo_manager().begin_transaction()
item.set_property('add_segment', segment)
get_undo_manager().commit_transaction()
register_action(AddSegmentAction, 'ItemFocus')
@ -311,7 +332,9 @@ class DeleteSegmentAction(SegmentAction):
def execute(self):
item, segment = self.get_item_and_segment()
if item:
get_undo_manager().begin_transaction()
item.set_property('del_segment', segment)
get_undo_manager().commit_transaction()
register_action(DeleteSegmentAction, 'ItemFocus', 'AddSegment')
@ -335,9 +358,11 @@ class OrthogonalAction(CheckAction):
def execute(self):
fi = get_parent_focus_item(self._window)
assert isinstance(fi, diacanvas.CanvasLine)
get_undo_manager().begin_transaction()
if self.active and len(fi.handles) < 3:
fi.set_property('add_segment', 0)
fi.set_property('orthogonal', self.active)
get_undo_manager().commit_transaction()
register_action(OrthogonalAction, 'ItemFocus', 'AddSegment', 'DeleteSegment')
@ -362,7 +387,9 @@ class OrthogonalAlignmentAction(CheckAction):
def execute(self):
fi = get_parent_focus_item(self._window)
assert isinstance(fi, diacanvas.CanvasLine)
get_undo_manager().begin_transaction()
fi.set_property('horizontal', self.active)
get_undo_manager().commit_transaction()
register_action(OrthogonalAlignmentAction, 'ItemFocus', 'Orthogonal')
@ -394,7 +421,9 @@ class NavigableAction(CheckAction):
item = self.get_association_end()
assert item.subject
assert isinstance(item.subject, UML.Property)
get_undo_manager().begin_transaction()
item.set_navigable(self.active)
get_undo_manager().commit_transaction()
class HeadNavigableAction(NavigableAction):
@ -433,7 +462,9 @@ class AggregationAction(RadioAction):
if self.active:
subject = get_parent_focus_item(self._window).get_property(self.end_name).subject
assert isinstance(subject, UML.Property)
get_undo_manager().begin_transaction()
subject.aggregation = self.aggregation
get_undo_manager().commit_transaction()
class HeadNoneAction(AggregationAction):
@ -568,9 +599,11 @@ class DependencyTypeAction(RadioAction):
def execute(self):
if self.active:
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
item.set_dependency_type(self.dependency_type)
#item.auto_dependency = False
self._window.get_action_pool().execute('AutoDependency', active=False)
get_undo_manager().commit_transaction()
class DependencyTypeDependencyAction(DependencyTypeAction):
@ -627,7 +660,9 @@ class AutoDependencyAction(CheckAction):
def execute(self):
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
item.auto_dependency = self.active
get_undo_manager().commit_transaction()
register_action(AutoDependencyAction, 'ItemFocus')
@ -651,7 +686,9 @@ class IndirectlyInstantiatedComponentAction(CheckAction):
def execute(self):
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
item.subject.isIndirectlyInstantiated = self.active
get_undo_manager().commit_transaction()
register_action(IndirectlyInstantiatedComponentAction, 'ItemFocus')
@ -664,11 +701,9 @@ class MoveAction(Action):
return self._window.get_current_diagram_view() \
.focus_item.item
def _getParent(self):
return get_parent_focus_item(self._window)
def _getElements(self, cls, item):
if isinstance(item, AttributeItem):
collection = cls.ownedAttribute
@ -676,11 +711,9 @@ class MoveAction(Action):
collection = cls.ownedOperation
return collection
def init(self, window):
self._window = window
def update(self):
try:
cls_item = self._getParent()
@ -692,7 +725,6 @@ class MoveAction(Action):
self.active = item.subject
self.sensitive = self._isSensitive(cls_item.subject, item)
def execute(self):
cls = self._getParent().subject
item = self._getItem()
@ -702,7 +734,9 @@ class MoveAction(Action):
# get method to move the element: moveUp or moveDown
move = getattr(self._getElements(cls, item), self.move_action)
get_undo_manager().begin_transaction()
move(item.subject)
get_undo_manager().commit_transaction()
self._window.execute_action('ItemFocus')
@ -749,11 +783,12 @@ class Fold(Action):
else:
self.sensitive = self.isSensitive(item)
def execute(self):
item = get_parent_focus_item(self._window)
log.debug('Action %s: %s' % (self.id, item.subject.name))
get_undo_manager().begin_transaction()
# create interface diagram element and assign model element to
# diagram element
diag = self._window.get_current_diagram()
@ -811,6 +846,7 @@ class Fold(Action):
# remove old diagram element
item.unlink()
get_undo_manager().commit_transaction()
class UnfoldAction(Fold):
@ -865,8 +901,10 @@ class ApplyStereotypeAction(CheckAction, ObjectAction):
def execute(self):
item = get_parent_focus_item(self._window)
get_undo_manager().begin_transaction()
if self.active:
item.subject.appliedStereotype = self.stereotype
else:
del item.subject.appliedStereotype[self.stereotype]
get_undo_manager().commit_transaction()

View File

@ -41,6 +41,13 @@ class Compartment(list):
for item in self:
save_func(None, item)
def has_item(self, item):
"""Check if the compartment already contains an item with the
same subject as item.
"""
s = item.subject
local_elements = [f.subject for f in self]
return s and s in local_elements
def pre_update(self, width, height, affine):
"""Calculate the size of the feates in this compartment.
@ -226,11 +233,14 @@ class ClassItem(NamedItem, diacanvas.CanvasGroupable):
to_add = [el for el in elements if el not in local_elements]
print 'sync_elems:', local_elements, to_add
# sync local elements with elements
del compartment[:]
for el in elements:
if el in to_add:
print 'sync_elems: creating', el
creator(el)
else:
compartment.append(mapping[el])
@ -304,7 +314,7 @@ class ClassItem(NamedItem, diacanvas.CanvasGroupable):
def on_subject_notify__ownedAttribute(self, subject, pspec=None):
"""Called when the ownedAttribute property of our subject changes.
"""
#log.debug('on_subject_notify__ownedAttribute')
log.debug('on_subject_notify__ownedAttribute')
# Filter attributes that are connected to an association:
self.sync_attributes()
@ -350,6 +360,9 @@ class ClassItem(NamedItem, diacanvas.CanvasGroupable):
width = 0
height = ClassItem.HEAD_MARGIN_Y
#self.sync_attributes()
#self.sync_operations()
compartments = (self._attributes, self._operations)
if has_stereotype:
@ -416,14 +429,17 @@ class ClassItem(NamedItem, diacanvas.CanvasGroupable):
"""
#if isinstance(item.subject, UML.Property):
if isinstance(item, AttributeItem):
#log.debug('Adding attribute %s' % item)
self._attributes.append(item)
item.set_child_of(self)
# TODO: check if property not already in attribute list
if not self._attributes.has_item(item):
log.debug('Adding attribute %s' % item)
self._attributes.append(item)
item.set_child_of(self)
#elif isinstance(item.subject, UML.Operation):
elif isinstance(item, OperationItem):
#log.debug('Adding operation %s' % item)
self._operations.append(item)
item.set_child_of(self)
if not self._operations.has_item(item):
self._operations.append(item)
item.set_child_of(self)
else:
log.warning('feature %s is not a Feature' % item)
return 0
@ -434,6 +450,7 @@ class ClassItem(NamedItem, diacanvas.CanvasGroupable):
"""Remove a feature subitem.
"""
if item in self._attributes:
print 'remove attr:', item
self._attributes.remove(item)
item.set_child_of(None)
elif item in self._operations:

View File

@ -74,7 +74,7 @@ class NamedItem(ElementItem, diacanvas.CanvasEditable):
def on_subject_notify__name(self, subject, pspec):
assert self.subject is subject
#print 'on_subject_notify__name: %s' % self.subject.name
self._name.set_text(self.subject.name)
self._name.set_text(self.subject.name or '')
self.request_update()
# CanvasItem callbacks:
@ -104,7 +104,10 @@ class NamedItem(ElementItem, diacanvas.CanvasEditable):
def on_editable_editing_done(self, shape, new_text):
self.preserve_property('name')
if new_text != self.subject.name:
self.canvas.get_undo_manager().begin_transaction()
self.subject.name = new_text
self.canvas.get_undo_manager().commit_transaction()
self.request_update()
initialize_item(NamedItem)

View File

@ -29,7 +29,7 @@ class OperationItem(FeatureItem):
# TODO: Handle subject.returnResult[*] and subject.formalParameter[*]
def on_subject_notify__name(self, subject, pspec):
self._expression.set_text(self.subject.render())
self._expression.set_text(self.subject.render() or '')
self.request_update()
on_subject_notify__visibility = on_subject_notify__name

View File

@ -54,15 +54,18 @@ class PlacementTool(diacanvas.PlacementTool):
diacanvas.PlacementTool._grab_handle(self, view, event, item)
def do_button_press_event(self, view, event):
self.is_released = False
view.unselect_all()
#print 'Gaphor: on_button_press_event: %s' % self.__dict__
view.canvas.get_undo_manager().begin_transaction()
return diacanvas.PlacementTool.do_button_press_event(self, view, event)
def do_button_release_event(self, view, event):
self.is_released = True
if resource('reset-tool-after-create', False):
view.set_tool(None)
#print 'Gaphor: do_button_release_event: %s' % self.__dict__
view.canvas.get_undo_manager().commit_transaction()
print 'Gaphor: do_button_release_event: %s' % self.__dict__
return diacanvas.PlacementTool.do_button_release_event(self, view, event)
gobject.type_register(PlacementTool)

View File

@ -233,7 +233,9 @@ class GaphorLoader(handler.ContentHandler):
elif state == ROOT and name == 'gaphor':
assert attrs['version'] in ('3.0',)
self.version = attrs['version']
self.gaphor_version = attrs.get('gaphor_version')
self.gaphor_version = attrs.get('gaphor-version')
if not self.gaphor_version:
self.gaphor_version = attrs.get('gaphor_version')
self.push(None, GAPHOR)
else:

View File

@ -16,6 +16,7 @@ from __future__ import generators
from cStringIO import StringIO
from xml.sax.saxutils import escape
import types
import sys
import os.path
import gc
import UML
@ -112,8 +113,8 @@ def save_generator(filename=None, factory=None):
factory = gaphor.resource(UML.ElementFactory)
buffer = StringIO()
buffer.write('<?xml version="1.0"?>\n')
buffer.write('<gaphor version="%s" gaphor_version="%s">' % (FILE_FORMAT_VERSION, gaphor.resource('Version')))
buffer.write('<?xml version="1.0" encoding="%s"?>\n' % sys.getdefaultencoding())
buffer.write('<gaphor version="%s" gaphor-version="%s">' % (FILE_FORMAT_VERSION, gaphor.resource('Version')))
size = factory.size()
n = 0

View File

@ -199,6 +199,8 @@ register_action(DeselectAllAction, 'ItemSelect', 'EditDelete')
class DeleteAction(Action):
"""Delete each selected canvas item.
"""
id = 'EditDelete'
label = '_Delete'
accel = 'C-d'
@ -213,13 +215,14 @@ class DeleteAction(Action):
def execute(self):
view = self._window.get_current_diagram_view()
view.canvas.undo_manager.begin_transaction()
try:
items = view.selected_items
for i in items:
i.item.unlink()
finally:
view.canvas.undo_manager.commit_transaction()
if view.is_focus():
view.canvas.undo_manager.begin_transaction()
try:
items = view.selected_items
for i in items:
i.item.unlink()
finally:
view.canvas.undo_manager.commit_transaction()
register_action(DeleteAction, 'ItemSelect')

View File

@ -27,16 +27,16 @@ class DiagramTab(object):
def set_diagram(self, diagram):
if self.diagram:
self.diagram.disconnect(self.__on_diagram_event)
self.diagram.canvas.disconnect(self.__undo_id)
#self.diagram.canvas.disconnect(self.__undo_id)
#self.diagram.canvas.disconnect(self.__snap_to_grid_id)
self.diagram = diagram
if diagram:
log.info('set diagram')
diagram.canvas.set_property ('allow_undo', 1)
diagram.connect(('name', '__unlink__'), self.__on_diagram_event)
self.__undo_id = diagram.canvas.undo_manager.connect('begin_transaction', self.__on_diagram_undo)
#self.__undo_id = diagram.canvas.undo_manager.connect('begin_transaction', self.__on_diagram_undo)
# Set capabilities:
self.__on_diagram_undo(diagram.canvas.undo_manager)
#self.__on_diagram_undo(diagram.canvas.undo_manager)
def construct(self):
title = self.diagram and self.diagram.name or '<None>'
@ -66,9 +66,10 @@ class DiagramTab(object):
view.connect('notify::tool', self.__on_view_notify_tool)
view.connect_after('event-after', self.__on_view_event_after)
view.connect('focus_item', self.__on_view_focus_item)
view.connect('select_item', self.__on_view_select_item)
view.connect('unselect_item', self.__on_view_select_item)
view.connect('focus-item', self.__on_view_focus_item)
view.connect('select-item', self.__on_view_select_item)
view.connect('unselect-item', self.__on_view_select_item)
view.connect_after('key-press-event', self.__on_key_press_event)
self.view = view
item_tool = ItemTool(self.owning_window.get_action_pool())
@ -90,6 +91,16 @@ class DiagramTab(object):
del self.view
del self.diagram
def __on_key_press_event(self, view, event):
"""Handle the 'Delete' key. This can not be handled directly (through
GTK's accelerators) since otherwise this key will confuse the text
edit stuff.
"""
if view.is_focus():
if event.keyval == 0xFFFF and event.state == 0: # Delete
self.owning_window.execute_action('EditDelete')
def __on_view_event_after(self, view, event):
# handle mouse button 3 (popup menu):
if event.type == gtk.gdk.BUTTON_PRESS:
@ -116,9 +127,9 @@ class DiagramTab(object):
def __on_view_notify_tool(self, view, pspec):
self.owning_window.execute_action('ToolChange')
def __on_diagram_undo(self, undo_manager):
log.info('set undo stack %s' % (undo_manager))
self.owning_window.execute_action('EditUndoStack')
# def __on_diagram_undo(self, undo_manager):
# #log.info('set undo stack %s' % (undo_manager))
# self.owning_window.execute_action('editundostack')
def __on_diagram_event(self, element, pspec):
if pspec == '__unlink__':

View File

@ -8,6 +8,7 @@ import gobject
import gtk
import gaphor.UML as UML
import gaphor.diagram as diagram
import gaphor.undomanager as undomanager
import gc
import traceback
from threading import Thread
@ -504,3 +505,59 @@ class TabChangeAction(Action):
pass
register_action(TabChangeAction)
class UndoStackAction(Action):
"""Dummy action that triggers the undo and redo actions to update
themselves.
"""
id = 'UndoStack'
def init(self, window):
pass
register_action(UndoStackAction)
class UndoAction(Action):
id = 'Undo'
stock_id = 'gtk-undo'
label = '_Undo'
tooltip = 'Undo the most recent changes'
accel = 'C-z'
# TODO: check if the diagram can undo.
def init(self, window):
self._window = window
def update(self):
self.sensitive = undomanager.get_undo_manager().can_undo()
def execute(self):
undomanager.get_undo_manager().undo_transaction()
#self.update()
self._window.execute_action('UndoStack')
register_action(UndoAction, 'UndoStack')
class RedoAction(Action):
id = 'Redo'
stock_id = 'gtk-redo'
tooltip = 'Redo the undone changes'
accel = 'C-r'
def init(self, window):
self._window = window
def update(self):
self.sensitive = undomanager.get_undo_manager().can_redo()
def execute(self):
print 'RedoAction'
undomanager.get_undo_manager().redo_transaction()
#self.update()
self._window.execute_action('UndoStack')
register_action(RedoAction, 'UndoStack')

View File

@ -5,6 +5,7 @@ import gtk
import namespace
import gaphor
import gaphor.UML as UML
import gaphor.undomanager as undomanager
from gaphor.i18n import _
from abstractwindow import AbstractWindow
from diagramtab import DiagramTab
@ -75,8 +76,8 @@ class MainWindow(AbstractWindow):
'separator',
'FileQuit'),
_('_Edit'), (
'EditUndo',
'EditRedo',
'Undo',
'Redo',
'separator',
'EditDelete',
'separator',
@ -115,8 +116,8 @@ class MainWindow(AbstractWindow):
'FileSave',
'FileSaveAs',
'separator',
'EditUndo',
'EditRedo',
'Undo',
'Redo',
'separator',
'ViewZoomIn',
'ViewZoomOut',
@ -248,6 +249,10 @@ class MainWindow(AbstractWindow):
self._toolbox = toolbox
# Set some handles for the undo manager
undomanager.get_undo_manager().connect('begin_transaction', self.on_undo)
undomanager.get_undo_manager().connect('add_undo_action', self.on_undo)
def add_transient_window(self, window):
"""Add a window as a sub-window of the main application.
"""
@ -369,6 +374,9 @@ class MainWindow(AbstractWindow):
"""
self.execute_action('TabChange')
def on_undo(self, *args):
self.execute_action('UndoStack')
# def on_transient_window_closed(self, window):
# assert window in self.__transient_windows
# log.debug('%s closed.' % window)
@ -383,4 +391,3 @@ class MainWindow(AbstractWindow):
#self.set_capability('model', not factory.is_empty())
gtk.accel_map_add_filter('gaphor')
print toolbox_to_menu(MainWindow.toolbox)

View File

@ -22,10 +22,11 @@ def _mod_and_keyval_from_accel(accel):
keyval = 0
modifier = 0
if accel:
gtk_accel = accel.upper().replace('C-', '<Control>').replace('S-', '<Shift>').replace('M-', '<Alt>')
gtk_accel = accel.replace('C-', '<Control>').replace('S-', '<Shift>').replace('M-', '<Alt>')
if gtk_accel[-1] in keyval_map.keys():
gtk_accel = gtk_accel[:-1] + keyval_map[gtk_accel[-1]]
keyval, modifier = gtk.accelerator_parse(gtk_accel)
#log.debug('Translated %s to %d' % (gtk_accel, keyval))
# accel = accel.upper()
# if accel.find('S-') != -1:

View File

@ -10,6 +10,7 @@ import stock
import gaphor.UML as UML
from gaphor import resource
from gaphor.undomanager import get_undo_manager
import operator
# The following items will not be shown in the treeview, although they
@ -498,7 +499,9 @@ class NamespaceView(gtk.TreeView):
# Set package. This only works for classifiers, packages and
# diagrams. Properties and operations should not be moved.
#get_undo_manager().begin_transaction()
element.package = dest_element
#get_undo_manager().commit_transaction()
except AttributeError:
#print dir(context)

View File

@ -1,95 +0,0 @@
# vim:sw=4:et
import gobject
import gtk
import gaphor
import gaphor.UML as UML
from abstractwindow import AbstractWindow
from gaphor.i18n import _
def create_option_menu(options):
menu = gtk.Menu()
for str in options:
menu_item = gtk.MenuItem(str)
menu_item.show()
gtk.MenuShell.append(menu, menu_item)
option_menu = gtk.OptionMenu()
option_menu.set_menu(menu)
return option_menu
class StereotypeWindow(object):
"""The stereotype window is used to define and describe stereotypes.
"""
#menu = ('_File', (
#'FileClose',)
#)
def __init__(self):
#AbstractWindow.__init__(self)
pass
def __text_box(self):
source = gtk.TextView()
scrolled_window = gtk.ScrolledWindow()
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolled_window.set_shadow_type(gtk.SHADOW_ETCHED_IN)
scrolled_window.add(source)
return scrolled_window, source
def run(self, window=None):
hbox = gtk.HBox()
stereotypes = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
st_view = gtk.TreeView(stereotypes)
st_frame = gtk.Frame(_('Stereotypes'))
st_frame.set_shadow_type(gtk.SHADOW_IN)
st_frame.add(st_view)
hbox.add(st_frame)
vbox = gtk.VBox()
hbox.pack_start(vbox)
label = gtk.Label(_('Base class:'))
vbox.pack_start(label, expand=False, fill=False)
sc_win = gtk.Entry()
vbox.pack_start(sc_win, expand=True, fill=True)
label = gtk.Label(_('Description:'))
vbox.pack_start(label, expand=False, fill=False)
sc_win, text_view = self.__text_box()
vbox.pack_start(sc_win, expand=True, fill=True)
label = gtk.Label(_('Constraints:'))
vbox.pack_start(label, expand=False, fill=False)
sc_win, text_view = self.__text_box()
vbox.pack_start(sc_win, expand=True, fill=True)
bbox = gtk.HButtonBox()
bbox.set_border_width(5)
bbox.set_layout(gtk.BUTTONBOX_SPREAD)
bbox.add(gtk.Button(stock='gtk-save'))
bbox.add(gtk.Button(stock='gtk-add'))
bbox.add(gtk.Button(stock='gtk-delete'))
vbox.pack_start(bbox, expand=False, fill=False)
hbox.show_all()
dialog = gtk.Dialog("Stereotypes", window, 0,
(gtk.STOCK_OK, gtk.RESPONSE_OK,))
#gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dialog.vbox.pack_start(hbox, gtk.TRUE, gtk.TRUE, 0)
response = dialog.run()
#if response == gtk.RESPONSE_OK:
dialog.destroy()

113
po/nl.po
View File

@ -1,91 +1,100 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Dutch translations for Gaphor
#
# Copyright (C) 2004 Arjan Molenaar
# Arjan Molenaar <arjanmol_at_users.sourceforge.net>, 2004.
#
msgid ""
msgstr ""
"Project-Id-Version: 0.5.1\n"
"POT-Creation-Date: Tue Aug 31 17:32:43 2004\n"
"Project-Id-Version: 0.6.0\n"
"POT-Creation-Date: Tue Oct 12 07:49:17 2004\n"
"PO-Revision-Date: 2004-06-10 08:00+001\n"
"Last-Translator: Arjan Molenaar <arjanmol_at_users.sourceforge.org>\n"
"Last-Translator: Arjan Molenaar <arjanmol_at_users.sourceforge.net>\n"
"Language-Team: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.4\n"
#: gaphor/diagram/klass.py:320
#: gaphor/diagram/klass.py:324
msgid "(from %s)"
msgstr "(van %s)"
#: gaphor/storage.py:143
#: gaphor/storage.py:156
msgid "Loading %d elements..."
msgstr "Laden van %s elementen..."
#: gaphor/ui/mainactions.py:48
msgid "Error occured while in worker thread"
msgstr "Er is een fout opgetreden in de worker thread"
#: gaphor/ui/mainactions.py:115
#: gaphor/ui/mainactions.py:87
msgid "Created a new model"
msgstr "Maak een nieuw model"
#: gaphor/ui/mainactions.py:154
#: gaphor/ui/mainactions.py:121
msgid "Loading model from %s"
msgstr "Laden van model %s"
#: gaphor/ui/mainactions.py:154
#: gaphor/ui/mainactions.py:121
msgid "Loading..."
msgstr "Bezig met laden..."
#: gaphor/ui/mainwindow.py:21
msgid "_File"
msgstr "_Bestand"
#: gaphor/ui/mainwindow.py:24
msgid "Recent files"
msgstr "Recente bestanden"
#: gaphor/ui/mainwindow.py:25
msgid "To be implemented"
msgstr "Not niet gemaakt"
#: gaphor/ui/mainwindow.py:32
msgid "_Export"
msgstr "_Exporteer"
#: gaphor/ui/mainwindow.py:40
msgid "_Edit"
msgstr "_Wijzig"
#: gaphor/ui/mainwindow.py:49
msgid "_Diagram"
msgstr "_Diagram"
#: gaphor/ui/mainwindow.py:60
msgid "_Window"
msgstr "_Venster"
#: gaphor/ui/mainwindow.py:64
msgid "_Help"
msgstr "_Help"
#: gaphor/ui/mainwindow.py:86
#: gaphor/ui/mainwindow.py:27
msgid "Classes"
msgstr "Klassen"
#: gaphor/ui/mainwindow.py:94
#: gaphor/ui/mainwindow.py:35
msgid "Actions"
msgstr "Akties"
#: gaphor/ui/mainwindow.py:100
#: gaphor/ui/mainwindow.py:41
msgid "Components"
msgstr "Componenten"
#: gaphor/ui/mainwindow.py:102
#: gaphor/ui/mainwindow.py:43
msgid "Use Cases"
msgstr "Use cases"
#: gaphor/ui/mainwindow.py:108
#: gaphor/ui/mainwindow.py:49
msgid "Profiles"
msgstr "Profilen"
#: gaphor/ui/mainwindow.py:56
msgid "_File"
msgstr "_Bestand"
#: gaphor/ui/mainwindow.py:59
msgid "Recent files"
msgstr "Recente bestanden"
#: gaphor/ui/mainwindow.py:60
msgid "To be implemented"
msgstr "Not niet gemaakt"
#: gaphor/ui/mainwindow.py:67
#, fuzzy
msgid "_Import"
msgstr "_Exporteer"
#: gaphor/ui/mainwindow.py:69
msgid "_Export"
msgstr "_Exporteer"
#: gaphor/ui/mainwindow.py:77
msgid "_Edit"
msgstr "_Wijzig"
#: gaphor/ui/mainwindow.py:88
msgid "_Diagram"
msgstr "_Diagram"
#: gaphor/ui/mainwindow.py:100
msgid "Tools"
msgstr ""
#: gaphor/ui/mainwindow.py:104
msgid "_Window"
msgstr "_Venster"
#: gaphor/ui/mainwindow.py:108
msgid "_Help"
msgstr "_Help"
#~ msgid "Error occured while in worker thread"
#~ msgstr "Er is een fout opgetreden in de worker thread"

169
tests/test-undo.py Executable file
View File

@ -0,0 +1,169 @@
#!/usr/bin/env python
#
# vim: sw=4
import gaphor.UML as UML
import gaphor.diagram as diagram
import gaphor.undomanager
import weakref, gobject, gc
import unittest
undo_manager = gaphor.undomanager.get_undo_manager()
class DiagramItemTestCase(unittest.TestCase):
def setUp(self):
undo_manager.clear_undo_stack()
undo_manager.clear_redo_stack()
UML.flush()
def testUndoAttribute(self):
c1 = UML.create(UML.Class)
c1.name = 'one'
undo_manager.begin_transaction()
c1.name = 'two'
undo_manager.commit_transaction()
self.failUnless(undo_manager.can_undo())
undo_manager.undo_transaction()
self.failIf(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(c1.name, 'one')
undo_manager.redo_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(c1.name, 'two')
def testUndoEnumeration(self):
p = UML.create(UML.Property)
p.aggregation = 'none'
undo_manager.begin_transaction()
p.aggregation = 'composite'
undo_manager.commit_transaction()
self.failUnless(undo_manager.can_undo())
undo_manager.undo_transaction()
self.failIf(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(p.aggregation, 'none')
undo_manager.redo_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(p.aggregation, 'composite')
def testUndoAssociation_1(self):
"""Test undo of 1:? associations."""
p1 = UML.create(UML.Package)
p2 = UML.create(UML.Package)
undo_manager.begin_transaction()
p2.package = p1
undo_manager.commit_transaction()
self.failUnless(undo_manager.can_undo())
undo_manager.undo_transaction()
self.failIf(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(p2.package, None)
self.failUnlessEqual(len(p1.nestedPackage), 0)
undo_manager.redo_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(p2.package, p1)
self.failUnlessEqual(len(p1.nestedPackage), 1)
self.failUnlessEqual(p1.nestedPackage[0], p2)
def testUndoAssociation_N(self):
"""Test undo of n:? associations."""
p1 = UML.create(UML.Package)
p2 = UML.create(UML.Package)
undo_manager.begin_transaction()
p1.nestedPackage = p2
undo_manager.commit_transaction()
self.failUnless(undo_manager.can_undo())
undo_manager.undo_transaction()
self.failIf(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(p2.package, None)
self.failUnlessEqual(len(p1.nestedPackage), 0)
undo_manager.redo_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(p2.package, p1)
self.failUnlessEqual(len(p1.nestedPackage), 1)
self.failUnlessEqual(p1.nestedPackage[0], p2)
def testUndoElementFactory(self):
"""Test gaphor.UML.ElementFactory."""
self.failUnlessEqual(len(UML.select()), 0)
undo_manager.begin_transaction()
c = UML.create(UML.Class)
undo_manager.commit_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(len(UML.select()), 1)
undo_manager.undo_transaction()
self.failIf(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(len(UML.select()), 0)
undo_manager.redo_transaction()
self.failIf(undo_manager.can_redo())
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(len(UML.select()), 1)
def testUndoElementFactory_2(self):
"""Test gaphor.UML.ElementFactory."""
self.failUnlessEqual(len(UML.select()), 0)
undo_manager.begin_transaction()
c = UML.create(UML.Class)
undo_manager.commit_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(len(UML.select()), 1)
undo_manager.begin_transaction()
o = UML.create(UML.Operation)
c.ownedOperation = o
undo_manager.commit_transaction()
self.failUnlessEqual(len(UML.select()), 2)
undo_manager.undo_transaction()
self.failUnless(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(len(UML.select()), 1)
undo_manager.undo_transaction()
self.failIf(undo_manager.can_undo())
self.failUnless(undo_manager.can_redo())
self.failUnlessEqual(len(UML.select()), 0)
undo_manager.redo_transaction()
self.failUnless(undo_manager.can_redo())
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(len(UML.select()), 1)
undo_manager.redo_transaction()
self.failIf(undo_manager.can_redo())
self.failUnless(undo_manager.can_undo())
self.failUnlessEqual(len(UML.select()), 2)
# TODO: check signals.
if __name__ == '__main__':
unittest.main()

View File

@ -84,10 +84,11 @@ class Compare(object):
elements = gaphor.parser.parse(filename)
factory = gaphor.UML.ElementFactory()
try:
gaphor.storage._load(elements, factory)
gaphor.storage.load_elements(elements, factory)
except Exception, e:
self.out('! File %s could not be loaded completely.' % filename)
self.out('! Trying to diff on parsed elements only.')
self.out(e)
return elements, factory
def elements_in_both_files(self):