Changeset 1132

Show
Ignore:
Timestamp:
02/18/07 20:40:28 (2 years ago)
Author:
arjanmol
Message:

renamed undomanager.undoable to transactional. added misc.partial

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphor/trunk/data/plugins/python/plugin.xml

    r543 r1132  
    1515      --> 
    1616    <module name="logilab.common"/> 
     17    <module name="logilab.common.astng"/> 
    1718  </require> 
    1819 
  • gaphor/trunk/doc/gaphor.txt

    r173 r1132  
    22~~~~~~ 
    33 
    4 Gaphor is a UML CASE tool aiming for symplicity. It is written in Python and 
    5 C. The goal is to write as much as possible in Python (easy scriptable for 
    6 code generators) and only some elementary parts are written in C (this involves 
    7 mainly the graphical items). 
     4Gaphor is a UML CASE tool aiming for symplicity. It is written in Python. 
     5The goal is to write as much as possible in Python (easy scriptable for 
     6code generators). 
    87 
    98So here's what has to be created: 
     
    4039~~~~~~~~~~~~~~~ 
    4140Of course we need something to draw. The items that are to be placed on the 
    42 diagrams need to be created. Since graphical stuff can take up a lot of time 
    43 I think it is wise to create those things in C, instead of Python. 
    44 We will however create hooks for the graphical items, so we can at least move 
    45 them around and place them on a diagram from within Python. 
     41diagrams need to be created. 
    4642 
    4743 
  • gaphor/trunk/gaphor/UML/properties.py

    r443 r1132  
    3636 
    3737class undoaction(object): 
    38     """This undo action contains one undo action for an attribute 
    39     property. 
     38    """ 
     39    Base class for undo actions. 
     40 
     41    This is an abstract class. The subclasses should implement an undo() 
     42    and a redo() method. 
     43    The undo() method should return a pointer to the redo() method and 
     44    visa-versa. 
     45 
    4046    """ 
    4147 
     
    4551        self.value = value 
    4652        #print 'adding new property', prop.name, obj, value 
    47         get_undo_manager().add_undo_action(self) 
    48  
     53        get_undo_manager().add_undo_action(self.undo) 
    4954 
    5055class umlproperty(object): 
     
    123128        setattr(self.obj, self.prop._name, self.value) 
    124129        self.prop.notify(self.obj) 
     130        return self.redo 
    125131 
    126132    def redo(self): 
    127133        setattr(self.obj, self.prop._name, self.redo_value) 
    128134        self.prop.notify(self.obj) 
     135        return self.undo 
    129136 
    130137 
     
    239246        #log.debug('undosetassociationaction del: %s %s %s' % (self.obj, self.prop.name, self.value)) 
    240247        self.prop._del(self.obj, self.value) 
     248        return self.redo 
    241249 
    242250    def redo(self): 
    243251        #log.debug('undosetassociationaction set: %s %s %s' % (self.obj, self.prop.name, self.value)) 
    244252        self.prop._set(self.obj, self.value) 
     253        return self.undo 
    245254 
    246255 
     
    252261        #log.debug('undodelassociationaction set: %s %s %s' % (self.obj, self.prop.name, self.value)) 
    253262        self.prop._set(self.obj, self.value) 
     263        return self.redo 
    254264 
    255265    def redo(self): 
    256266        #log.debug('undodelassociationaction del: %s %s %s' % (self.obj, self.prop.name, self.value)) 
    257267        self.prop._del(self.obj, self.value) 
     268        return self.undo 
    258269 
    259270 
  • gaphor/trunk/gaphor/actions/diagramactions.py

    r1121 r1132  
    99from gaphor import resource 
    1010from gaphor import UML 
    11 from gaphor.undomanager import get_undo_manager, undoable 
     11from gaphor.undomanager import get_undo_manager, transactional 
    1212from gaphor.misc.action import Action, CheckAction, RadioAction 
    1313from gaphor.misc.action import register_action as _register_action 
     
    342342            self._item.load(name, str(value)) 
    343343 
    344     @undoable 
     344    @transactional 
    345345    def execute(self): 
    346346        view = self._window.get_current_diagram_view() 
  • gaphor/trunk/gaphor/actions/itemactions.py

    r1121 r1132  
    77from gaphor import UML 
    88from gaphor.diagram import items 
    9 from gaphor.undomanager import undoable 
     9from gaphor.undomanager import transactional 
    1010from gaphor.misc.action import Action, CheckAction, RadioAction, ObjectAction 
    1111from gaphor.misc.action import register_action 
     
    132132                self.active = item.subject and item.subject.isAbstract 
    133133 
    134     @undoable 
     134    @transactional 
    135135    def execute(self): 
    136136        item = get_parent_focus_item(self._window) 
     
    158158#                self.active = item.subject and item.subject.isAbstract 
    159159# 
    160 #    @undoable 
     160#    @transactional 
    161161#    def execute(self): 
    162162#        item = self._window.get_current_diagram_view().focused_item 
     
    188188                self.sensitive = item.show_attributes 
    189189 
    190     @undoable 
     190    @transactional 
    191191    def execute(self): 
    192192        view = self._window.get_current_diagram_view() 
     
    231231                self.sensitive = item.show_operations 
    232232 
    233     @undoable 
     233    @transactional 
    234234    def execute(self): 
    235235        view = self._window.get_current_diagram_view() 
     
    261261#        self._window = window 
    262262# 
    263 #    @undoable 
     263#    @transactional 
    264264#    def execute(self): 
    265265#        #subject = get_parent_focus_item(self._window).subject 
     
    302302                self.active = item.show_attributes 
    303303 
    304     @undoable 
     304    @transactional 
    305305    def execute(self): 
    306306        item = get_parent_focus_item(self._window) 
     
    327327                self.active = item.show_operations 
    328328 
    329     @undoable 
     329    @transactional 
    330330    def execute(self): 
    331331        item = get_parent_focus_item(self._window) 
     
    361361    tooltip='Add a segment to the line' 
    362362 
    363     @undoable 
     363    @transactional 
    364364    def execute(self): 
    365365        item, segment = self.get_item_and_segment() 
     
    383383            pass 
    384384 
    385     @undoable 
     385    @transactional 
    386386    def execute(self): 
    387387        item, segment = self.get_item_and_segment() 
     
    408408            pass 
    409409 
    410     @undoable 
     410    @transactional 
    411411    def execute(self): 
    412412        fi = get_parent_focus_item(self._window) 
     
    436436#            pass 
    437437# 
    438 #    @undoable 
     438#    @transactional 
    439439#    def execute(self): 
    440440#        fi = get_parent_focus_item(self._window) 
     
    465465            pass 
    466466 
    467     @undoable 
     467    @transactional 
    468468    def execute(self): 
    469469        fi = get_parent_focus_item(self._window) 
     
    482482        self._window = window 
    483483 
    484     @undoable 
     484    @transactional 
    485485    def execute(self): 
    486486        fi = get_parent_focus_item(self._window) 
     
    509509            pass 
    510510 
    511     @undoable 
     511    @transactional 
    512512    def execute(self): 
    513513        item = self.get_association_end() 
     
    592592            pass 
    593593 
    594     @undoable 
     594    @transactional 
    595595    def execute(self): 
    596596        if self.active: 
     
    729729            pass 
    730730 
    731     @undoable 
     731    @transactional 
    732732    def execute(self): 
    733733        if self.active: 
     
    790790                self.active = item.auto_dependency 
    791791 
    792     @undoable 
     792    @transactional 
    793793    def execute(self): 
    794794        item = get_parent_focus_item(self._window) 
     
    815815                self.active = item.subject and item.subject.isIndirectlyInstantiated 
    816816 
    817     @undoable 
     817    @transactional 
    818818    def execute(self): 
    819819        item = get_parent_focus_item(self._window) 
     
    856856                self.sensitive = self._isSensitive(cls_item.subject, item) 
    857857 
    858     @undoable 
     858    @transactional 
    859859    def execute(self): 
    860860        cls = self._getParent().subject 
     
    912912            self.sensitive = isinstance(item, items.InterfaceItem) 
    913913 
    914     @undoable 
     914    @transactional 
    915915    def execute(self): 
    916916        item = get_parent_focus_item(self._window) 
     
    927927    tooltip = 'View details' 
    928928 
    929     @undoable 
     929    @transactional 
    930930    def execute(self): 
    931931        item = get_parent_focus_item(self._window) 
     
    962962                self.active = False 
    963963 
    964     @undoable 
     964    @transactional 
    965965    def execute(self): 
    966966        item = get_parent_focus_item(self._window) 
     
    10421042        new_rel.unlink() 
    10431043 
    1044     @undoable 
     1044    @transactional 
    10451045    def execute(self): 
    10461046        # TODO: AJM: disabled action 
     
    10821082            pass 
    10831083 
    1084     @undoable 
     1084    @transactional 
    10851085    def execute(self): 
    10861086        if self.active: 
     
    11421142                self.active = item.props.show_ordering 
    11431143 
    1144     @undoable 
     1144    @transactional 
    11451145    def execute(self): 
    11461146        item = get_parent_focus_item(self._window) 
     
    11681168            pass 
    11691169 
    1170     @undoable 
     1170    @transactional 
    11711171    def execute(self): 
    11721172        item = get_parent_focus_item(self._window) 
     
    11921192            pass 
    11931193 
    1194     @undoable 
     1194    @transactional 
    11951195    def execute(self): 
    11961196        """ 
     
    12881288            pass 
    12891289 
    1290     @undoable 
     1290    @transactional 
    12911291    def execute(self): 
    12921292        """ 
     
    13431343            self.active = isinstance(item, items.ConnectorEndItem) 
    13441344 
    1345     @undoable 
     1345    @transactional 
    13461346    def execute(self): 
    13471347        item = self.focused_item 
     
    13731373                self.active = (self.interface == item.subject) 
    13741374 
    1375     @undoable 
     1375    @transactional 
    13761376    def execute(self): 
    13771377        item = self.focused_item 
     
    14021402                self.active = item.props.has_lifetime 
    14031403 
    1404     @undoable 
     1404    @transactional 
    14051405    def execute(self): 
    14061406        item = get_parent_focus_item(self._window) 
  • gaphor/trunk/gaphor/diagram/association.py

    r1121 r1132  
    2222 
    2323from gaphor import resource, UML 
    24 from gaphor.undomanager import undoable 
    2524#from gaphor.diagram import Relationship 
    2625from gaphor.diagram.diagramitem import SubjectSupport 
  • gaphor/trunk/gaphor/diagram/feature.py

    r1121 r1132  
    77from gaphas.item import Item 
    88from diagramitem import DiagramItem 
    9 from gaphor.undomanager import undoable 
    109from gaphor.diagram import DiagramItemMeta 
    1110from gaphas.util import text_extents, text_set_font 
  • gaphor/trunk/gaphor/tests/test_undomanager.py

    r1125 r1132  
    88class TestUndoManager(unittest.TestCase): 
    99 
    10     def test_1(self): 
     10    def test_transactions(self): 
     11 
    1112        undo_manager = UndoManager() 
    1213        assert undo_manager._transaction_depth == 0 
     
    3031        assert undo_manager._current_transaction is None 
    3132 
    32     def test_2(self): 
     33    def test_not_in_transaction(self): 
    3334        undo_manager = UndoManager() 
    3435 
     
    4041        undo_manager.add_undo_action(action) 
    4142        assert undo_manager._current_transaction 
     43        assert undo_manager.can_undo() 
    4244        assert len(undo_manager._current_transaction._actions) == 1 
    4345 
     46         
     47    def test_actions(self): 
     48        undone = [ 0 ] 
     49        def undo_action(undone=undone): 
     50            assert undo_manager._in_undo 
     51            undone[0] = 1 
     52            return undo_action 
     53 
     54        undo_manager = UndoManager() 
     55 
     56        undo_manager.begin_transaction() 
     57        undo_manager.add_undo_action(undo_action) 
     58        assert undo_manager._current_transaction 
     59        assert undo_manager.can_undo() 
     60        assert len(undo_manager._current_transaction._actions) == 1 
     61 
     62        undo_manager.commit_transaction() 
     63 
     64        undo_manager.undo_transaction() 
     65        assert not undo_manager.can_undo() 
     66        assert undone[0] == 1 
     67         
     68        undone[0] = 0 
     69 
     70        assert undo_manager.can_redo() 
     71 
     72        undo_manager.redo_transaction() 
     73        assert not undo_manager.can_redo() 
     74        assert undo_manager.can_undo() 
     75        assert undone[0] == 1 
    4476# vim:sw=4:et 
  • gaphor/trunk/gaphor/undomanager.py

    r1125 r1132  
    11# vim:sw=4:et: 
    2  
    3 import gobject 
     2""" 
     3Undo management for Gaphor. 
     4 
     5Undoing and redoing actions is managed through the UndoManager. 
     6 
     7An undo action should be a callable object (called with no arguments). 
     8 
     9An undo action should return a callable object that acts as redo function. 
     10If None is returned the undo action is considered to be the redo action as well. 
     11 
     12NOTE: it would be nice to use actions in conjunction with functools.partial, 
     13      but that's Python2.5 stuff.. 
     14""" 
    415 
    516 
     
    1021 
    1122 
    12 def undoable(func): 
    13     """Descriptor. Enables an undo transaction around the method/function. 
     23def transactional(func): 
     24    """ 
     25    Descriptor. Begins a transaction around the method/function. 
     26 
     27    TODO: In casse of an exception, roll back the transaction if the transaction 
     28    level == 1. 
    1429    """ 
    1530    def wrapper(*args, **kwargs): 
     
    2237    return wrapper 
    2338 
     39 
    2440class TransactionError(Exception): 
    2541 
     
    3147    """ 
    3248    A transaction. Every action that is added between a begin_transaction() 
    33     and a commit_transaction() call is recorded in a transaction, do it can 
    34     be played back when a transaction is undone. 
     49    and a commit_transaction() call is recorded in a transaction, so it can 
     50    be played back when a transaction is executed. This executing a 
     51    transaction has the effect of performing the actions recorded, which will 
     52    typically undo actions performed by the user. 
    3553    """ 
    3654 
     
    4159        self._actions.append(action) 
    4260 
    43     def can_undo(self): 
     61    def can_execute(self): 
    4462        return self._actions and True or False 
    4563 
    46     def undo(self): 
     64    def execute(self): 
    4765        self._actions.reverse() 
     66        contra_transaction = Transaction() 
    4867        for action in self._actions: 
    4968            try: 
    50                 #log.debug('Undoing action %s' % action
    51                 action.undo(
     69                contra_action = action(
     70                contra_transaction.add(contra_action or action
    5271            except Exception, e: 
    5372                log.error('Error while undoing action %s' % action, e) 
    54  
    55     def redo(self): 
    56         self._actions.reverse() 
    57         for action in self._actions: 
    58             try: 
    59                 #log.debug('Redoing action %s' % action) 
    60                 action.redo() 
    61             except Exception, e: 
    62                 log.error('Error while redoing action %s' % action, e) 
    63  
     73        return contra_transaction 
    6474 
    6575class UndoManager(object): 
     
    6777    Simple transaction manager for Gaphor. 
    6878    This transaction manager supports nested transactions. 
     79     
     80    The Undo manager sports an undo and a redo stack. Each stack contains 
     81    a set of actions that can be executed, just by calling them (e.i action()) 
     82    If something is returned by an action, that is considered the callable 
     83    to be used to undo or redo the last performed action. 
    6984    """ 
    7085 
     
    126141        self._transaction_depth -= 1 
    127142        if self._transaction_depth == 0: 
    128             if self._current_transaction.can_undo(): 
     143            if self._current_transaction.can_execute(): 
    129144                self._undo_stack.append(self._current_transaction) 
    130145            else: 
     
    154169        try: 
    155170            self._in_undo = True 
    156             transaction.undo() 
     171            redo_transaction = transaction.execute() 
    157172        finally: 
    158173            self._in_undo = False 
    159         self._redo_stack.append(transaction) 
     174        self._redo_stack.append(redo_transaction) 
    160175 
    161176    def redo_transaction(self): 
     
    166181        try: 
    167182            self._in_undo = True 
    168             transaction.redo() 
     183            undo_transaction = transaction.execute() 
    169184        finally: 
    170185            self._in_undo = False 
    171         self._undo_stack.append(transaction) 
     186        self._undo_stack.append(undo_transaction) 
    172187 
    173188    def in_transaction(self):