Permalink
Browse files

Tagged version 0.10.3 (now easy_install should really, really work)

git-svn-id: file:///Users/arjan/backup/gaphor/gaphor/tags/release-0.10.3@1206 a8418922-720d-0410-834f-a69b97ada669
  • Loading branch information...
1 parent 0a03d2b commit b46ed16844ca89422ce1c2229be0d818d7325bee @amolenaar committed Apr 10, 2007
View
@@ -52,13 +52,13 @@ To update:
remove_constraint()
Variable state
- In Gaphor, connecting two diagram items is considered an atomic task,
- performed by a IConnect adapter. This operation results in a set of primitive
- tasks (properties set and a constraint created).
+In Gaphor, connecting two diagram items is considered an atomic task,
+performed by a IConnect adapter. This operation results in a set of primitive
+tasks (properties set and a constraint created).
- For methods, it should be possible to create a decorator (@reversable) that
- schedules a method with the same signature as the calling operation, but with
- the inverse effect (e.g. the gaphas.tree module):
+For methods, it should be possible to create a decorator (@reversible) that
+schedules a method with the same signature as the calling operation, but with
+the inverse effect (e.g. the gaphas.tree module):
Tree(object):
@@ -70,30 +70,75 @@ To update:
def remove(self, node):
... remove
- Okay, so the second case is toucher...
+Okay, so the second case is tougher...
-We have three options:
+So what we did:
+Add a StateManager to gaphas. All changed are send to the statemanager.
+Gaphor should implement it's own state manager.
+ + all state changes can easily be recorded
+ + fix it in one place
+ + reusable throughout Gaphas and subtypes.
- 1. Add a StateManager to gaphas. All changed are send to the statemanager.
- Gaphor should implement it's own state manager.
- + all state changes can easely be recorded
- + fix it in one place
- + reusable thoughout Gaphas and subtypes.
- ? does this solve undo-problems for other applications too?
- 2. Override all attrs and properties, decorate the method from a special
- update script at runtime.
- + keeps Gaphas' code clean, less complicated
- - it's not clear what happens
- - users require some sort of state recording/undo mechanism anyway
+Transactions
+============
- 3. Why not use Tools?
- - Lot's of interaction can be achieved though menu items or other
- programmatic actions.
+Gaphor's Undo manager works transactional. Typically, methods can be
+decorated with @transactional and undo data is stored in the current
+transaction. A new tx is created when none exists.
+Although undo functionality is at the core of Gaphor (diagram items and
+model elements have been adapted to provide proper undo information), the
+UndoManager itself is just a service.
-References:
+Transaction support though is a real core functionality. By letting elements
+and items emit event notifications on important changed other (yet to be
+defined) services can take benefit of those events. The UML module already
+works this way. Gaphas (the Gaphor canvas) also emits state changes.
+
+When state changes happen in model elements and diagram items an event is
+emitted. Those events are handled by special handlers that produce
+"reverse-events". Reverse events are functions that perform exactly the
+opposite operation. Those functions are stored in a list (which technically is
+the Transaction). When an undo request is done the list is executed in LIFO
+order.
+
+To start a transaction:
+
+ 1. A Transaction object has been created.
+ 2. This results in the emission of a TransactionBegin event.
+ 3. The TransactionBegin event is a trigger for the UndoManager to start
+ listening for IUndoEvent actions.
+
+Now, that should be done when a model element or diagram item sends a state
+change:
+
+ 1. The event is handled by the "reverse-handler"
+ 2. Reverse handler generates a IUndoEvent signal
+ 3. The signal is received and stored as part of the undo-transaction.
+
+(Maybe step 2 and 3 can be merged, since only one function is not of any
+interest to the rest of the application - creates nasty dependencies)
+
+If nested transaction are created a simple counter is incremented.
+
+When the topmost Transaction is committed:
+
+ 1. A TransactionCommit event is emitted
+ 2. This triggers the UndoManager to close and store the transaction.
+
+When a transaction is rolled back:
+
+ 1. The main transaction is marked for rollback
+ 2. When the toplevel tx is rolled back or commited a
+ TransactionRollback event is emitted
+ 2. This triggers the UndoManager to play back all recorded actions and
+ stop listening.
+
+
+References
+==========
A Framework for Undoing Actions in Collaborative Systems
http://www.eecs.umich.edu/~aprakash/papers/undo-tochi94.pdf
View
@@ -9,8 +9,6 @@
import misc.logger
-import pkg_resources
-
from misc.resource import Resource
if os.name == 'nt':
@@ -29,8 +27,6 @@
# resource is returned.
resource = Resource(initial_resources={
'Name': 'gaphor',
- 'Version': pkg_resources.get_distribution('gaphor').version,
- 'DataDir': os.path.join(pkg_resources.get_distribution('gaphor').location, 'gaphor', 'data'),
'UserDataDir': user_data_dir,
'ui.toolbox.classes': True,
})
@@ -70,6 +66,11 @@ def main(gaphor_file=None):
This involves importing plugins and creating the main window.
"""
+ import pkg_resources
+
+ resource('Version', pkg_resources.get_distribution('gaphor').version)
+ resource('DataDir', os.path.join(pkg_resources.get_distribution('gaphor').location, 'gaphor', 'data'))
+
# Import GUI stuff here, since the user might not need all the GUI stuff
import gtk
import ui
@@ -1077,15 +1077,15 @@ def update(self):
try:
item = get_parent_focus_item(self._window)
if isinstance(item, items.ObjectNodeItem):
- self.active = (item.get_ordering() == self.ordering)
+ self.active = (item.ordering == self.ordering)
except NoFocusItemError:
pass
@transactional
def execute(self):
if self.active:
item = get_parent_focus_item(self._window)
- item.set_ordering(self.ordering)
+ item.ordering = self.ordering
class ObjectNodeOrderingUnorderedAction(ObjectNodeOrderingAction):
@@ -1139,12 +1139,12 @@ def update(self):
pass
else:
if isinstance(item, items.ObjectNodeItem):
- self.active = item.props.show_ordering
+ self.active = item.show_ordering
@transactional
def execute(self):
item = get_parent_focus_item(self._window)
- item.props.show_ordering = self.active
+ item.show_ordering = self.active
register_action(ObjectNodeOrderingVisibiltyAction, 'ItemFocus')
View
@@ -16,6 +16,11 @@ class ActionItem(NamedItem):
'name-align': (ALIGN_CENTER, ALIGN_MIDDLE),
}
+ def pre_update(self, context):
+ self.update_name_size(context)
+ self.min_width, self.min_height = self.get_name_size()
+ super(ActionItem, self).pre_update(context)
+
def draw(self, context):
"""
Draw action symbol.
@@ -39,5 +44,4 @@ def draw(self, context):
super(ActionItem, self).draw(context)
-
# vim:sw=4:et
@@ -130,7 +130,18 @@ class ForkDecisionNodeItem(ActivityNodeItem):
def __init__(self, id=None):
ActivityNodeItem.__init__(self, id)
self._combined = None
- self.set_prop_persistent('combined')
+ #self.set_prop_persistent('combined')
+
+ def save(self, save_func):
+ if self._combined:
+ save_func('combined', self._combined, reference=True)
+ super(ForkDecisionNodeItem, self).save(save_func)
+
+ def load(self, name, value):
+ if name == 'combined':
+ self._combined = value
+ else:
+ super(ForkDecisionNodeItem, self).load(name, value)
@observed
def _set_combined(self, value):
@@ -338,7 +338,6 @@ def get_icon_pos(self):
def draw_compartment(self, context):
- #if not self.subject: return
cr = context.cairo
cr.rectangle(0, 0, self.width, self.height)
cr.stroke()
@@ -78,7 +78,7 @@ def draw(self, context):
c.stroke()
if self.subject.body:
# Do not print empty string, since cairo-win32 can't handle it.
- text_multiline(c, 5, 15, self.subject.body, padding=2)
+ text_multiline(c, 5, 5, self.subject.body, padding=2)
#c.move_to(10, 15)
#c.show_text(self.subject.body)
@@ -40,4 +40,5 @@ def pre_update(self, context):
super(ElementItem, self).pre_update(context)
self.update_stereotype()
+
# vim:sw=4
@@ -14,7 +14,8 @@
from gaphor.diagram.diagramline import DiagramLine
class ExtensionItem(DiagramLine):
- """ExtensionItem represents associations.
+ """
+ ExtensionItem represents associations.
An ExtensionItem has two ExtensionEnd items. Each ExtensionEnd item
represents a Property (with Property.association == my association).
"""
@@ -24,29 +25,6 @@ class ExtensionItem(DiagramLine):
def __init__(self, id=None):
DiagramLine.__init__(self, id)
- def save (self, save_func):
- DiagramLine.save(self, save_func)
- if self._head_end.subject:
- save_func('head_subject', self._head_end.subject)
- if self._tail_end.subject:
- save_func('tail_subject', self._tail_end.subject)
-
- def load (self, name, value):
- # end_head and end_tail were used in an older Gaphor version
- if name in ( 'head_end', 'head_subject' ):
- #type(self._head_end).subject.load(self._head_end, value)
- self._head_end.load('subject', value)
- elif name in ( 'tail_end', 'tail_subject' ):
- #type(self._tail_end).subject.load(self._tail_end, value)
- self._tail_end.load('subject', value)
- else:
- DiagramLine.load(self, name, value)
-
- def postload(self):
- DiagramLine.postload(self)
- self._head_end.postload()
- self._tail_end.postload()
-
def on_subject_notify(self, pspec, notifiers=()):
DiagramLine.on_subject_notify(self, pspec,
notifiers + ('ownedEnd',))
@@ -55,16 +55,6 @@ def update_name_size(self, context):
width, height = text_extents(cr, text)
padding = self.style.name_padding
self._name_size = width + padding[0] + padding[2], height + padding[1] + padding[3]
-# if self.min_width < self._name_size[0]:
-# self.min_width = self._name_size[0]
-# if self.min_height < self._name_size[1]:
-# self.min_height = self._name_size[1]
-# self.min_width, self.min_height = get_min_size(width, height,
-# self.style.min_size,
-# self.style.name_padding)
-#
-# super(NamedItem, self).pre_update(context)
-
def update(self, context):
"""
@@ -92,5 +82,5 @@ def draw(self, context):
if text:
#cr.move_to(self.name_x, self.name_y)
#cr.show_text(text)
- text_align(cr, self.name_x, self.name_y, text, *self.style.name_align)
+ text_align(cr, self.name_x, self.name_y, text, 1, -1)
super(NamedItem, self).draw(context)
@@ -80,22 +80,17 @@ def on_subject_notify(self, pspec, notifiers = ()):
element subject.
"""
NamedItem.on_subject_notify(self, pspec, notifiers)
- if self.subject:
- if not self.subject.upperBound:
- self.subject.upperBound = UML.create(UML.LiteralSpecification)
- self.subject.upperBound.value = '*'
- #self._upper_bound.subject = self.subject.upperBound
- #else:
- # self._upper_bound.subject = None
+ if self.subject and not self.subject.upperBound:
+ self.subject.upperBound = UML.create(UML.LiteralSpecification)
+ self.subject.upperBound.value = '*'
self.request_update()
def pre_update(self, context):
"""
Update object node, its ordering and upper bound specification.
"""
- NamedItem.update(self, context)
+ NamedItem.pre_update(self, context)
- # TODO: format tag properly:
if self.subject.upperBound:
self._tag = '{ upperBound = %s }\n' % self.subject.upperBound.value
@@ -31,7 +31,6 @@ def pre_update(self, context):
super(PackageItem, self).pre_update(context)
-
def draw(self, context):
cr = context.cairo
o = 0.0
@@ -48,9 +47,12 @@ def draw(self, context):
cr.line_to(o, y)
cr.stroke()
if self.stereotype:
- text_align(cr, w / 2, y + 10, self.stereotype)
+ y += 10
+ text_align(cr, w / 2, y, self.stereotype)
+ y += 10
- super(PackageItem, self).draw(context)
+ if self.subject and self.subject.name:
+ text_align(cr, w / 2, y + 10, self.subject.name)
# vim:sw=4:et
View
@@ -1,3 +1,33 @@
+"""
+Application wide events are managed here.
+"""
+
from zope import interface
from gaphor.interfaces import *
+class TransactionBegin(object):
+ """
+ This event denotes the beginning of an transaction.
+ Nested (sub-) transactions should not emit this signal.
+ """
+ interface.implements(ITransactionEvent)
+
+
+class TransactionCommit(object):
+ """
+ This event is emitted when a transaction (toplevel) is successfully
+ commited.
+ """
+ interface.implements(ITransactionEvent)
+
+class TransactionRollback(object):
+ """
+ If a set of operations fail (e.i. due to an exception) the transaction
+ should be marked for rollback. This event is emitted to tell the operation
+ has failed.
+ """
+ interface.implements(ITransactionEvent)
+
+
+# vim:sw=4:et:ai
+
Oops, something went wrong.

0 comments on commit b46ed16

Please sign in to comment.