*** empty log message ***
git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@424 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
parent
bd1ef40ab6
commit
1a87f7f550
13
ChangeLog
13
ChangeLog
@ -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
16
TODO
@ -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
|
||||
--
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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__':
|
||||
|
@ -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')
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
113
po/nl.po
@ -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
169
tests/test-undo.py
Executable 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()
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user