Changeset 1233

Show
Ignore:
Timestamp:
04/17/07 23:09:32 (1 year ago)
Author:
arj..@yirdis.nl
Message:

reduced dependency on UML.ElementFactory? and resource()

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphor/trunk/TODO

    r1190 r1233  
    1313   make unit testing easier too). 
    1414   This should get rid of the pseudo singletons (_default* instances per module) 
    15     
     15    - replace DataDir code with pkg_resources (ui.stock) 
     16    - copy-buffer (separate copy service?) 
     17    - make a service of ElementFactory 
     18     
    1619 - using stereotypes 
     20 - reimplement the mechanism that adds already existing relationships to the 
     21   diagram if an item is copied to that diagram. 
     22 
    1723 - components and stuff 
    1824 
  • gaphor/trunk/gaphor/actions/mainactions.py

    r1229 r1233  
    7676    stock_id = 'gtk-new' 
    7777 
    78     def init(self, window): 
    79         self._window = window 
    80  
    81     def execute(self): 
    82         factory = resource(UML.ElementFactory) 
     78    element_factory = inject('element_factory') 
     79 
     80    def init(self, window): 
     81        self._window = window 
     82 
     83    def execute(self): 
     84        factory = self.element_factory 
    8385        if factory.size(): 
    8486            dialog = gtk.MessageDialog(self._window.get_window(), 
     
    117119    tooltip = 'Reload the loaded Gaphor project from file' 
    118120 
     121    element_factory = inject('element_factory') 
     122 
    119123    def init(self, window): 
    120124        # The filename of the last file loaded 
     
    138142            self.filename = filename 
    139143            gc.collect() 
    140             worker = GIdleThread(storage.load_generator(filename), queue) 
     144            worker = GIdleThread(storage.load_generator(filename, self.element_factory), queue) 
    141145            self._window.action_pool.insensivate_actions() 
    142146            get_undo_manager().clear_undo_stack() 
     
    212216    tooltip = _('Save the model to a new file') 
    213217 
    214     def init(self, window): 
    215         self._window = window 
    216         self.factory = resource(UML.ElementFactory) 
     218    factory = inject('element_factory') 
     219 
     220    def init(self, window): 
     221        self._window = window 
     222        #self.factory = self.element_factory 
    217223        #self.factory.connect(self.on_element_factory) 
    218224        #self.on_element_factory(self) 
     
    243249                out = open(filename, 'w') 
    244250 
    245                 worker = GIdleThread(storage.save_generator(XMLWriter(out)), queue) 
     251                worker = GIdleThread(storage.save_generator(XMLWriter(out), self.element_factory), queue) 
    246252                action_states = self._window.action_pool.get_action_states() 
    247253                self._window.action_pool.insensivate_actions() 
  • gaphor/trunk/gaphor/actions/placementactions.py

    r1121 r1233  
    66 
    77import gaphas 
    8 from gaphor import resource 
     8from gaphor.core import inject 
    99from gaphor import UML 
    1010from gaphor import diagram 
     
    4040    tooltip = 'Reset the tool to the pointer tool after creation of an item' 
    4141 
     42    properties = inject('properties') 
    4243    def init(self, window): 
    4344        self._window = window 
    44         self.active = resource('reset-tool-after-create', True) 
     45        self.active = self.properties('reset-tool-after-create', True) 
    4546 
    4647    def execute(self): 
    47         resource.set('reset-tool-after-create', self.active, persistent=True) 
     48        self.properties.set('reset-tool-after-create', self.active) 
    4849 
    4950register_action(ResetToolAfterCreateAction) 
     
    8990        subject = None 
    9091        if self.subject_type: 
    91             subject = resource(UML.ElementFactory).create(self.subject_type) 
     92            subject = UML.create(self.subject_type) 
    9293        diagram = self._window.get_current_diagram() 
    9394        return diagram.create(self.type, subject=subject) 
  • gaphor/trunk/gaphor/adapters/tests/test_connector.py

    r1121 r1233  
    55import unittest 
    66from zope import component 
    7 from gaphor import resource 
    87from gaphor import UML 
    9 from gaphor.ui.mainwindow import MainWindow 
    108from gaphor.diagram import items 
    119from gaphor.diagram.interfaces import IConnect 
  • gaphor/trunk/gaphor/diagram/association.py

    r1193 r1233  
    2323 
    2424from gaphor import resource, UML 
    25 #from gaphor.diagram import Relationship 
    2625from gaphor.diagram.diagramitem import SubjectSupport 
    2726from gaphor.diagram.diagramline import DiagramLine 
    2827 
    29 #class AssociationRelationship(Relationship): 
    30 #    """Relationship for associations. 
    31 #    """ 
    32 def xxx(): 
    33     def relationship(self, line, head_subject = None, tail_subject = None): 
    34         # First check if we do not already contain the right subject: 
    35         if line.subject: 
    36             end1 = line.subject.memberEnd[0] 
    37             end2 = line.subject.memberEnd[1] 
    38             if (end1.type is head_type and end2.type is tail_type) \ 
    39                or (end2.type is head_type and end1.type is tail_type): 
    40                 return 
    41                  
    42         # Find all associations and determine if the properties on the 
    43         # association ends have a type that points to the class. 
    44         Association = UML.Association 
    45         for assoc in resource(UML.ElementFactory).itervalues(): 
    46             if isinstance(assoc, Association): 
    47                 #print 'assoc.memberEnd', assoc.memberEnd 
    48                 end1 = assoc.memberEnd[0] 
    49                 end2 = assoc.memberEnd[1] 
    50                 if (end1.type is head_type and end2.type is tail_type) \ 
    51                    or (end2.type is head_type and end1.type is tail_type): 
    52                     # check if this entry is not yet in the diagram 
    53                     # Return if the association is not (yet) on the canvas 
    54                     for item in assoc.presentation: 
    55                         if item.canvas is line.canvas: 
    56                             break 
    57                     else: 
    58                         return assoc 
    59         return None 
    60  
    61  
    6228 
    6329class AssociationItem(DiagramLine): 
    64     """AssociationItem represents associations.  
     30    """ 
     31    AssociationItem represents associations.  
    6532    An AssociationItem has two AssociationEnd items. Each AssociationEnd item 
    6633    represents a Property (with Property.association == my association). 
  • gaphor/trunk/gaphor/diagram/flow.py

    r1121 r1233  
    222222 
    223223 
    224 class CFlowItem(FlowItem): 
    225     """ 
    226     Abstract class for flows with activity edge connector. Flow with 
    227     activity edge connector references other one, which has activity edge 
    228     connector with same name (it is called opposite one). 
    229  
    230     Such flows have active and inactive ends. Active end is connected to 
    231     any node and inactive end is connected only to activity edge connector. 
    232     """ 
    233  
    234     popup_menu = DiagramLine.popup_menu + ( 
    235         'separator', 
    236         'MergeFlow', 
    237     ) 
    238  
    239     def __init__(self, id = None): 
    240         FlowItem.__init__(self, id) 
    241  
    242         self._connector = ACItem('value') 
    243  
    244         factory = resource(UML.ElementFactory) 
    245         self._connector.subject = factory.create(UML.LiteralSpecification) 
    246         self.add(self._connector) 
    247  
    248         self._opposite = None 
    249  
    250         # when flow item with connector is deleted, then kill opposite, too 
    251         self.unlink_handler_id = self.connect('__unlink__', self.kill_opposite) 
    252  
    253  
    254     def kill_opposite(self, source, name): 
    255         # do not allow to be killed by opposite 
    256         self._opposite.disconnect(self._opposite.unlink_handler_id) 
    257         self._opposite.unlink() 
    258  
    259  
    260     def save(self, save_func): 
    261         """ 
    262         Save connector name and opposite flow with activity edge connector. 
    263         """ 
    264         FlowItem.save(self, save_func) 
    265         save_func('opposite', self._opposite, True) 
    266         save_func('connector-name', self._connector.subject.value) 
    267  
    268  
    269     def load(self, name, value): 
    270         """ 
    271         Load connector name and opposite flow with activity edge connector. 
    272         """ 
    273         if name == 'connector-name': 
    274             self._connector.subject.value = value 
    275         elif name == 'opposite': 
    276             self._opposite = value 
    277         else: 
    278             FlowItem.load(self, name, value) 
    279  
    280  
    281     def on_update(self, affine): 
    282         """ 
    283         Draw flow line and activity edge connector. 
    284         """ 
    285         # get parent line points to determine angle 
    286         # used to rotate position of activity edge connector 
    287         p1, p2 = self.get_line() 
    288  
    289         # calculate position of connector center 
    290         r = self._connector.RADIUS 
    291         #x = p1[0] < p2[0] and r or -r 
    292         x = p1[0] < p2[0] and -r or r 
    293         y = 0 
    294         x, y = rotate(p1, p2, x, y, p1[0], p1[1]) 
    295  
    296         self._connector.move_center(x, y) 
    297  
    298         FlowItem.on_update(self, affine) 
    299  
    300  
    301     def confirm_connect_handle(self, handle): 
    302         """See DiagramLine.confirm_connect_handle(). 
    303         """ 
    304         c1 = self.get_active_handle().connected_to            # source 
    305         c2 = self._opposite.get_active_handle().connected_to  # target 
    306  
    307         # set correct relationship between connected items; 
    308         # it should be (source, target) not (target, source); 
    309         # otherwise we are looking for non-existing or wrong relationship 
    310         if isinstance(self, CFlowItemB): 
    311             c1, c2 = c2, c1 
    312  
    313         self.connect_items(c1, c2) 
    314         self._opposite.set_subject(self.subject) 
    315  
    316  
    317     def allow_connect_handle(self, handle, connecting_to): 
    318         if handle == self.get_inactive_handle(): 
    319             return False 
    320         return FlowItem.allow_connect_handle(self, handle, connecting_to) 
    321  
    322  
    323     def confirm_disconnect_handle (self, handle, was_connected_to): 
    324         """See DiagramLine.confirm_disconnect_handle(). 
    325         """ 
    326         c1 = self.get_active_handle().connected_to             # source 
    327         c2 = self._opposite.get_active_handle().connected_to   # target 
    328         self.disconnect_items(c1, c2, was_connected_to) 
    329         self._opposite.set_subject(None) 
    330  
    331  
    332  
    333 class CFlowItemA(CFlowItem): 
    334     """ 
    335     * Is used for split flows, as is CFlowItemB * 
    336  
    337     Flow with activity edge connector, which starts from node and points to 
    338     activity edge connector. 
    339     """ 
    340     def __init__(self, id): 
    341         CFlowItem.__init__(self, id) 
    342         self.create_guard() 
    343  
    344  
    345     def on_update(self, affine): 
    346         self.update_guard(affine) 
    347         CFlowItem.on_update(self, affine) 
    348  
    349  
    350     def get_line(self): 
    351         p1 = self.handles[-1].get_pos_i() 
    352         p2 = self.handles[-2].get_pos_i() 
    353         return p1, p2 
    354  
    355  
    356     def get_active_handle(self): 
    357         """ 
    358         Return source handle as active one. 
    359         """ 
    360         return self.handles[0] 
    361  
    362  
    363     def get_inactive_handle(self): 
    364         """ 
    365         Return target handle as inactive one. 
    366         """ 
    367         return self.handles[-1] 
    368  
    369  
    370  
    371 class CFlowItemB(CFlowItem): 
    372     """ 
    373     Flow with activity edge connector, which starts from activity edge 
    374     connector and points to a node. 
    375     """ 
    376     def __init__(self, id): 
    377         CFlowItem.__init__(self, id) 
    378         self.create_name() 
    379  
    380  
    381     def on_update(self, affine): 
    382         self.update_name(affine) 
    383         CFlowItem.on_update(self, affine) 
    384  
    385  
    386     def get_line(self): 
    387         p1 = self.handles[0].get_pos_i() 
    388         p2 = self.handles[1].get_pos_i() 
    389         return p1, p2 
    390  
    391  
    392     def get_active_handle(self): 
    393         """ 
    394         Return target handle as active one. 
    395         """ 
    396         return self.handles[-1] 
    397  
    398  
    399     def get_inactive_handle(self): 
    400         """ 
    401         Return source handle as inactive one. 
    402         """ 
    403         return self.handles[0] 
    404  
    405  
    406  
    407  
    408 def move_collection(src, target, name): 
    409     """ 
    410     Copy collection from one object to another. 
    411  
    412     src    - source object 
    413     target - target object 
    414     name   - name of attribute, which is collection to copy 
    415     """ 
    416     # first make of copy of collection, because assigning 
    417     # element to target collection moves this element 
    418     for flow in list(getattr(src, name)): 
    419         getattr(target, name).append(flow) 
    420  
    421  
    422 def is_fd(node): 
    423     """ 
    424     Check if node is fork or decision node. 
    425     """ 
    426     return isinstance(node, (UML.ForkNode, UML.DecisionNode)) 
    427  
    428  
    429 def change_node_class(node): 
    430     """ 
    431     If UML constraints for fork, join, decision and merge nodes are not 
    432     met, then create new node depending on input node class, i.e. create 
    433     fork node from join node or merge node from decision node. 
    434  
    435     If constraints are met, then return node itself. 
    436     """ 
    437     if is_fd(node) and len(node.incoming) > 1 \ 
    438             or not is_fd(node) and len(node.incoming) < 2: 
    439  
    440         factory = resource(UML.ElementFactory) 
    441         cls = node_classes[node.__class__] 
    442         log.debug('creating %s' % cls) 
    443         nn = factory.create(cls) 
    444         move_collection(node, nn, 'incoming') 
    445         move_collection(node, nn, 'outgoing') 
    446     else: 
    447         nn = node 
    448  
    449     assert nn is not None 
    450  
    451     # we have to accept zero of outgoing edges in case of fork/descision 
    452     # nodes 
    453     assert is_fd(nn) and len(nn.incoming) <= 1 \ 
    454         or not is_fd(nn) and len(nn.incoming) >= 1, '%s' % nn 
    455     assert is_fd(nn) and len(nn.outgoing) >= 0 \ 
    456         or not is_fd(nn) and len(nn.outgoing) <= 1, '%s' % nn 
    457     return nn 
    458  
    459  
    460 def combine_nodes(node): 
    461     """ 
    462     Create fork/join (decision/merge) nodes combination as described in UML 
    463     specification. 
    464     """ 
    465     log.debug('combining nodes') 
    466  
    467     cls = node_classes[node.__class__] 
    468     log.debug('creating %s' % cls) 
    469     factory = resource(UML.ElementFactory) 
    470     target = factory.create(cls) 
    471  
    472     source = node 
    473     if is_fd(node): 
    474         source = target 
    475         move_collection(node, target, 'incoming') 
    476  
    477         # create new fork node 
    478         cls = node_classes[target.__class__] 
    479         log.debug('creating %s' % cls) 
    480         target = factory.create(cls) 
    481         move_collection(node, target, 'outgoing') 
    482     else: 
    483         # fork node is created, referenced by target 
    484         move_collection(node, target, 'outgoing') 
    485  
    486     assert not is_fd(source) 
    487     assert is_fd(target) 
    488  
    489     # create flow 
    490     c1 = count_object_flows(source, 'incoming') 
    491     c2 = count_object_flows(target, 'outgoing') 
    492  
    493     if c1 > 0 or c2 > 0: 
    494         flow = factory.create(UML.ControlFlow) 
    495     else: 
    496         flow = factory.create(UML.ObjectFlow) 
    497     flow.source = source 
    498     flow.target = target 
    499  
    500     assert len(source.incoming) > 1 
    501     assert len(source.outgoing) == 1 
    502  
    503     assert len(target.incoming) == 1 
    504     assert len(target.outgoing) > 1 
    505  
    506     return source 
    507  
    508  
    509 def decombine_nodes(source): 
    510     """ 
    511     Create node depending on source argument which denotes combination of 
    512     fork/join (decision/merge) nodes as described in UML specification. 
    513  
    514     Combination of nodes is destroyed. 
    515     """ 
    516     log.debug('decombining nodes') 
    517     flow = source.outgoing[0] 
    518     target = flow.target 
    519  
    520     if len(source.incoming) < 2: 
    521         # create fork or decision 
    522         cls = target.__class__ 
    523     else: 
    524         # create join or merge 
    525         cls = source.__class__ 
    526  
    527     factory = resource(UML.ElementFactory) 
    528     node = factory.create(cls) 
    529  
    530     move_collection(source, node, 'incoming') 
    531     move_collection(target, node, 'outgoing') 
    532  
    533     assert source != node 
    534  
    535     # delete target and combining flow 
    536     # source should be deleted by caller 
    537     target.unlink() 
    538     flow.unlink() 
    539  
    540     # return new node 
    541     return node 
    542  
    543  
    544 def determine_node_on_connect(el): 
    545     """ 
    546     Determine classes of nodes depending on amount of incoming 
    547     and outgoing edges. This method is called when flow is attached 
    548     to node. 
    549  
    550     If there is more than one incoming edge and more than one 
    551     outgoing edge, then create two nodes and combine them with 
    552     flow as described in UML specification. 
    553     """ 
    554     subject = el.subject 
    555     if not isinstance(subject, tuple(node_classes.keys())): 
    556         return 
    557  
    558     new_subject = subject 
    559  
    560     if len(subject.incoming) > 1 and len(subject.outgoing) > 1: 
    561         new_subject = combine_nodes(subject) 
    562         el.props.combined = True 
    563  
    564     else: 
    565         new_subject = change_node_class(subject) 
    566  
    567     change_node_subject(el, new_subject) 
    568  
    569     if el.props.combined: 
    570         check_combining_flow(el) 
    571  
    572  
    573 def determine_node_on_disconnect(el): 
    574     """ 
    575     Determine classes of nodes depending on amount of incoming 
    576     and outgoing edges. This method is called when flow is dettached 
    577     from node. 
    578  
    579     If there are combined nodes and there is no need for them, then replace 
    580     combination with appropriate node (i.e. replace with fork node when 
    581     there are less than two incoming edges). This way data model is kept as 
    582     simple as possible. 
    583     """ 
    584     subject = el.subject 
    585     if not isinstance(subject, tuple(node_classes.keys())): 
    586         return 
    587  
    588     new_subject = subject 
    589  
    590     if el.props.combined: 
    591         cs = subject.outgoing[0].target 
    592         # decombine node when there is no more than one incoming 
    593         # and no more than one outgoing flow 
    594         if len(subject.incoming) < 2 or len(cs.outgoing) < 2: 
    595             new_subject = decombine_nodes(subject) 
    596             el.props.combined = False 
    597         else: 
    598             check_combining_flow(el) 
    599  
    600     else:  
    601         new_subject = change_node_class(subject) 
    602  
    603     change_node_subject(el, new_subject) 
    604  
    605  
    606 def change_node_subject(el, new_subject): 
    607     """ 
    608     Change element's subject if new subject is different than element's 
    609     subject. If subject is changed, then old subject is destroyed. 
    610     """ 
    611     subject = el.subject 
    612     if new_subject != subject: 
    613         log.debug('changing subject of ui node %s' % el) 
    614         el.set_subject(new_subject) 
    615  
    616         log.debug('deleting node %s' % subject) 
    617         subject.unlink() 
    618  
    619  
    620 def create_flow(cls, flow): 
    621     """ 
    622     Create new flow of class cls. Flow data from flow argument are copied 
    623     to new created flow. Old flow is destroyed. 
    624     """ 
    625     factory = resource(UML.ElementFactory) 
    626     f = factory.create(cls) 
    627     f.source = flow.source 
    628     f.target = flow.target 
    629     flow.unlink() 
    630     return f 
    631  
    632  
    633 def count_object_flows(node, attr): 
    634     """ 
    635     Count incoming or outgoing object flows. 
    636     """ 
    637     return len(getattr(node, attr) 
    638         .select(lambda flow: isinstance(flow, UML.ObjectFlow))) 
    639  
    640  
    641 def check_combining_flow(el): 
    642     """ 
    643     Set object flow as combining flow when incoming or outgoing flow count 
    644     is greater than zero. Otherwise change combining flow to control flow. 
    645     """ 
    646     subject = el.subject 
    647     flow = subject.outgoing[0] # combining flow 
    648     combined = flow.target     # combined node 
    649  
    650     c1 = count_object_flows(subject, 'incoming') 
    651     c2 = count_object_flows(combined, 'outgoing') 
    652  
    653     log.debug('combined incoming and outgoing object flow count: (%d, %d)' % (c1, c2)) 
    654  
    655     if (c1 > 0 or c2 > 0) and isinstance(flow, UML.ControlFlow): 
    656         log.debug('changing combing flow to object flow') 
    657         create_flow(UML.ObjectFlow, flow) 
    658     elif c1 == 0 and c2 == 0 and isinstance(flow, UML.ObjectFlow): 
    659         log.debug('changing combing flow to control flow') 
    660         create_flow(UML.ControlFlow, flow) 
    661  
    662  
    663 def create_connector_end(connector, role): 
    664     """ 
    665     Create Connector End, set role and attach created end to 
    666     connector. 
    667     """ 
    668     end = resource(UML.ElementFactory).create(UML.ConnectorEnd) 
    669     end.role = role 
    670     connector.end = end 
    671     assert end in role.end 
    672     return end 
    673  
    674  
    675 def rotate(p1, p2, a, b, x, y): 
    676     """ 
    677     Rotate point (a, b) by angle, which is determined by line (p1, p2). 
    678  
    679     Rotated point is moved by vector (x, y). 
    680     """ 
    681     try: 
    682         angle = atan((p1[1] - p2[1]) / (p1[0] - p2[0])) 
    683     except ZeroDivisionError: 
    684         da = p1[1] < p2[1] and 1.5 or -1.5 
    685         angle = pi * da 
    686  
    687     sin_angle = sin(angle) 
    688     cos_angle = cos(angle) 
    689     return (cos_angle * a - sin_angle * b + x, 
    690             sin_angle * a + cos_angle * b + y) 
     224#class CFlowItem(FlowItem): 
     225#    """ 
     226#    Abstract class for flows with activity edge connector. Flow with 
     227#    activity edge connector references other one, which has activity edge 
     228#    connector with same name (it is called opposite one). 
     229
     230#    Such flows have active and inactive ends. Active end is connected to 
     231#    any node and inactive end is connected only to activity edge connector. 
     232#    """ 
     233
     234#    popup_menu = DiagramLine.popup_menu + ( 
     235#        'separator', 
     236#        'MergeFlow', 
     237#    ) 
     238
     239#    def __init__(self, id = None): 
     240#        FlowItem.__init__(self, id) 
     241
     242#        self._connector = ACItem('value') 
     243
     244#        factory = resource(UML.ElementFactory) 
     245#        self._connector.subject = factory.create(UML.LiteralSpecification) 
     246#        self.add(self._connector) 
     247
     248#        self._opposite = None 
     249
     250#        # when flow item with connector is deleted, then kill opposite, too 
     251#        self.unlink_handler_id = self.connect('__unlink__', self.kill_opposite) 
     252
     253
     254#    def kill_opposite(self, source, name): 
     255#        # do not allow to be killed by opposite 
     256#        self._opposite.disconnect(self._opposite.unlink_handler_id) 
     257#        self._opposite.unlink() 
     258
     259
     260#    def save(self, save_func): 
     261#        """ 
     262#        Save connector name and opposite flow with activity edge connector. 
     263#        """ 
     264#        FlowItem.save(self, save_func) 
     265#        save_func('opposite', self._opposite, True) 
     266#        save_func('connector-name', self._connector.subject.value) 
     267
     268
     269#    def load(self, name, value): 
     270#        """ 
     271#        Load connector name and opposite flow with activity edge connector. 
     272#        """ 
     273#        if name == 'connector-name': 
     274#            self._connector.subject.value = value 
     275#        elif name == 'opposite': 
     276#            self._opposite = value 
     277#        else: 
     278#            FlowItem.load(self, name, value) 
     279
     280
     281#    def on_update(self, affine): 
     282#        """ 
     283#        Draw flow line and activity edge connector. 
     284#        """ 
     285#        # get parent line points to determine angle 
     286#        # used to rotate position of activity edge connector 
     287#        p1, p2 = self.get_line() 
     288
     289#        # calculate position of connector center 
     290#        r = self._connector.RADIUS 
     291#        #x = p1[0] < p2[0] and r or -r 
     292#        x = p1[0] < p2[0] and -r or r 
     293#        y = 0 
     294#        x, y = rotate(p1, p2, x, y, p1[0], p1[1]) 
     295
     296#        self._connector.move_center(x, y) 
     297
     298#        FlowItem.on_update(self, affine) 
     299
     300
     301#    def confirm_connect_handle(self, handle): 
     302#        """See DiagramLine.confirm_connect_handle(). 
     303#        """ 
     304#        c1 = self.get_active_handle().connected_to            # source 
     305#        c2 = self._opposite.get_active_handle().connected_to  # target 
     306
     307#        # set correct relationship between connected items; 
     308#        # it should be (source, target) not (target, source); 
     309#        # otherwise we are looking for non-existing or wrong relationship 
     310#        if isinstance(self, CFlowItemB): 
     311#            c1, c2 = c2, c1 
     312
     313#        self.connect_items(c1, c2) 
     314#        self._opposite.set_subject(self.subject) 
     315
     316
     317#    def allow_connect_handle(self, handle, connecting_to): 
     318#        if handle == self.get_inactive_handle(): 
     319#            return False 
     320#        return FlowItem.allow_connect_handle(self, handle, connecting_to) 
     321
     322
     323#    def confirm_disconnect_handle (self, handle, was_connected_to): 
     324#        """See DiagramLine.confirm_disconnect_handle(). 
     325#        """ 
     326#        c1 = self.get_active_handle().connected_to             # source 
     327#        c2 = self._opposite.get_active_handle().connected_to   # target 
     328#        self.disconnect_items(c1, c2, was_connected_to) 
     329#        self._opposite.set_subject(None) 
     330
     331
     332
     333#class CFlowItemA(CFlowItem): 
     334#    """ 
     335#    * Is used for split flows, as is CFlowItemB * 
     336
     337#    Flow with activity edge connector, which starts from node and points to 
     338#    activity edge connector. 
     339#    """ 
     340#    def __init__(self, id): 
     341#        CFlowItem.__init__(self, id) 
     342#        self.create_guard() 
     343
     344
     345#    def on_update(self, affine): 
     346#        self.update_guard(affine) 
     347#        CFlowItem.on_update(self, affine) 
     348
     349
     350#    def get_line(self): 
     351#        p1 = self.handles[-1].get_pos_i() 
     352#        p2 = self.handles[-2].get_pos_i() 
     353#        return p1, p2 
     354
     355
     356#    def get_active_handle(self): 
     357#        """ 
     358#        Return source handle as active one. 
     359#        """ 
     360#        return self.handles[0] 
     361
     362
     363#    def get_inactive_handle(self): 
     364#        """ 
     365#        Return target handle as inactive one. 
     366#        """ 
     367#        return self.handles[-1] 
     368
     369
     370
     371#class CFlowItemB(CFlowItem): 
     372#    """ 
     373#    Flow with activity edge connector, which starts from activity edge 
     374#    connector and points to a node. 
     375#    """ 
     376#    def __init__(self, id): 
     377#        CFlowItem.__init__(self, id) 
     378#        self.create_name() 
     379
     380
     381#    def on_update(self, affine): 
     382#        self.update_name(affine) 
     383#        CFlowItem.on_update(self, affine) 
     384
     385
     386#    def get_line(self): 
     387#        p1 = self.handles[0].get_pos_i() 
     388#        p2 = self.handles[1].get_pos_i() 
     389#        return p1, p2 
     390
     391
     392#    def get_active_handle(self): 
     393#        """ 
     394#        Return target handle as active one. 
     395#        """ 
     396#        return self.handles[-1] 
     397
     398
     399#    def get_inactive_handle(self): 
     400#        """ 
     401#        Return source handle as inactive one. 
     402#        """ 
     403#        return self.handles[0] 
     404
     405
     406#def move_collection(src, target, name): 
     407#    """ 
     408#    Copy collection from one object to another. 
     409
     410#    src    - source object 
     411#    target - target object 
     412#    name   - name of attribute, which is collection to copy 
     413#    """ 
     414#    # first make of copy of collection, because assigning 
     415#    # element to target collection moves this element 
     416#    for flow in list(getattr(src, name)): 
     417#        getattr(target, name).append(flow) 
     418
     419 
     420#def is_fd(node): 
     421#    """ 
     422#    Check if node is fork or decision node. 
     423#    """ 
     424#    return isinstance(node, (UML.ForkNode, UML.DecisionNode)) 
     425
     426
     427#def change_node_class(node): 
     428#    """ 
     429#    If UML constraints for fork, join, decision and merge nodes are not 
     430#    met, then create new node depending on input node class, i.e. create 
     431#    fork node from join node or merge node from decision node. 
     432
     433#    If constraints are met, then return node itself. 
     434#    """ 
     435#    if is_fd(node) and len(node.incoming) > 1 \ 
     436#            or not is_fd(node) and len(node.incoming) < 2: 
     437
     438#        factory = resource(UML.ElementFactory) 
     439#        cls = node_classes[node.__class__] 
     440#        log.debug('creating %s' % cls) 
     441#        nn = factory.create(cls) 
     442#        move_collection(node, nn, 'incoming') 
     443#        move_collection(node, nn, 'outgoing') 
     444#    else: 
     445#        nn = node 
     446
     447#    assert nn is not None 
     448
     449#    # we have to accept zero of outgoing edges in case of fork/descision 
     450#    # nodes 
     451#    assert is_fd(nn) and len(nn.incoming) <= 1 \ 
     452#        or not is_fd(nn) and len(nn.incoming) >= 1, '%s' % nn 
     453#    assert is_fd(nn) and len(nn.outgoing) >= 0 \ 
     454#        or not is_fd(nn) and len(nn.outgoing) <= 1, '%s' % nn 
     455#    return nn 
     456
     457
     458#def combine_nodes(node): 
     459#    """ 
     460#    Create fork/join (decision/merge) nodes combination as described in UML 
     461#    specification. 
     462#    """ 
     463#    log.debug('combining nodes') 
     464
     465#    cls = node_classes[node.__class__] 
     466#    log.debug('creating %s' % cls) 
     467#    factory = resource(UML.ElementFactory) 
     468#    target = factory.create(cls) 
     469
     470#    source = node 
     471#    if is_fd(node): 
     472#        source = target 
     473#        move_collection(node, target, 'incoming') 
     474
     475#        # create new fork node 
     476#        cls = node_classes[target.__class__] 
     477#        log.debug('creating %s' % cls) 
     478#        target = factory.create(cls) 
     479#        move_collection(node, target, 'outgoing') 
     480#    else: 
     481#        # fork node is created, referenced by target 
     482#        move_collection(node, target, 'outgoing') 
     483
     484#    assert not is_fd(source) 
     485#    assert is_fd(target) 
     486
     487#    # create flow 
     488#    c1 = count_object_flows(source, 'incoming') 
     489#    c2 = count_object_flows(target, 'outgoing') 
     490
     491#    if c1 > 0 or c2 > 0: 
     492#        flow = factory.create(UML.ControlFlow) 
     493#    else: 
     494#        flow = factory.create(UML.ObjectFlow) 
     495#    flow.source = source 
     496#    flow.target = target 
     497
     498#    assert len(source.incoming) > 1 
     499#    assert len(source.outgoing) == 1 
     500
     501#    assert len(target.incoming) == 1 
     502#    assert len(target.outgoing) > 1 
     503
     504#    return source 
     505
     506
     507#def decombine_nodes(source): 
     508#    """ 
     509#    Create node depending on source argument which denotes combination of 
     510#    fork/join (decision/merge) nodes as described in UML specification. 
     511
     512#    Combination of nodes is destroyed. 
     513#    """ 
     514#    log.debug('decombining nodes') 
     515#    flow = source.outgoing[0] 
     516#    target = flow.target 
     517
     518#    if len(source.incoming) < 2: 
     519#        # create fork or decision 
     520#        cls = target.__class__ 
     521#    else: 
     522#        # create join or merge 
     523#        cls = source.__class__ 
     524
     525#    factory = resource(UML.ElementFactory) 
     526#    node = factory.create(cls) 
     527
     528#    move_collection(source, node, 'incoming') 
     529#    move_collection(target, node, 'outgoing') 
     530
     531#    assert source != node 
     532
     533#    # delete target and combining flow 
     534#    # source should be deleted by caller 
     535#    target.unlink() 
     536#    flow.unlink() 
     537
     538#    # return new node 
     539#    return node 
     540 
     541 
     542#def determine_node_on_connect(el): 
     543#    """ 
     544#    Determine classes of nodes depending on amount of incoming 
     545#    and outgoing edges. This method is called when flow is attached 
     546#    to node. 
     547
     548#    If there is more than one incoming edge and more than one 
     549#    outgoing edge, then create two nodes and combine them with 
     550#    flow as described in UML specification. 
     551#    """ 
     552#    subject = el.subject 
     553#    if not isinstance(subject, tuple(node_classes.keys())): 
     554#        return 
     555
     556#    new_subject = subject 
     557
     558#    if len(subject.incoming) > 1 and len(subject.outgoing) > 1: 
     559#        new_subject = combine_nodes(subject) 
     560#        el.props.combined = True 
     561
     562#    else: 
     563#        new_subject = change_node_class(subject) 
     564
     565#    change_node_subject(el, new_subject) 
     566
     567#    if el.props.combined: 
     568#        check_combining_flow(el) 
     569 
     570 
     571#def determine_node_on_disconnect(el): 
     572#    """ 
     573#    Determine classes of nodes depending on amount of incoming 
     574#    and outgoing edges. This method is called when flow is dettached 
     575#    from node. 
     576
     577#    If there are combined nodes and there is no need for them, then replace 
     578#    combination with appropriate node (i.e. replace with fork node when 
     579#    there are less than two incoming edges). This way data model is kept as 
     580#    simple as possible. 
     581#    """ 
     582#    subject = el.subject 
     583#    if not isinstance(subject, tuple(node_classes.keys())): 
     584#        return 
     585
     586#    new_subject = subject 
     587
     588#    if el.props.combined: 
     589#        cs = subject.outgoing[0].target 
     590#        # decombine node when there is no more than one incoming 
     591#        # and no more than one outgoing flow 
     592#        if len(subject.incoming) < 2 or len(cs.outgoing) < 2: 
     593#            new_subject = decombine_nodes(subject) 
     594#            el.props.combined = False 
     595#        else: 
     596#            check_combining_flow(el) 
     597
     598#    else:  
     599#        new_subject = change_node_class(subject) 
     600
     601#    change_node_subject(el, new_subject) 
     602 
     603 
     604#def change_node_subject(el, new_subject): 
     605#    """ 
     606#    Change element's subject if new subject is different than element's 
     607#    subject. If subject is changed, then old subject is destroyed. 
     608#    """ 
     609#    subject = el.subject 
     610#    if new_subject != subject: 
     611#        log.debug('changing subject of ui node %s' % el) 
     612#        el.set_subject(new_subject) 
     613
     614#        log.debug('deleting node %s' % subject) 
     615#        subject.unlink() 
     616 
     617 
     618#def create_flow(cls, flow): 
     619#    """ 
     620#    Create new flow of class cls. Flow data from flow argument are copied 
     621#    to new created flow. Old flow is destroyed. 
     622#    """ 
     623#    factory = resource(UML.ElementFactory) 
     624#    f = factory.create(cls) 
     625#    f.source = flow.source 
     626#    f.target = flow.target 
     627#    flow.unlink() 
     628#    return f 
     629 
     630 
     631#def count_object_flows(node, attr): 
     632#   """ 
     633#    Count incoming or outgoing object flows. 
     634#    """ 
     635#    return len(getattr(node, attr) 
     636#        .select(lambda flow: isinstance(flow, UML.ObjectFlow))) 
     637
     638
     639#def check_combining_flow(el): 
     640#    """ 
     641#    Set object flow as combining flow when incoming or outgoing flow count 
     642#    is greater than zero. Otherwise change combining flow to control flow. 
     643#    """ 
     644#    subject = el.subject 
     645#    flow = subject.outgoing[0] # combining flow 
     646#    combined = flow.target     # combined node 
     647
     648#    c1 = count_object_flows(subject, 'incoming') 
     649#    c2 = count_object_flows(combined, 'outgoing') 
     650
     651#    log.debug('combined incoming and outgoing object flow count: (%d, %d)' % (c1, c2)) 
     652
     653#    if (c1 > 0 or c2 > 0) and isinstance(flow, UML.ControlFlow): 
     654#        log.debug('changing combing flow to object flow') 
     655#        create_flow(UML.ObjectFlow, flow) 
     656#    elif c1 == 0 and c2 == 0 and isinstance(flow, UML.ObjectFlow): 
     657#        log.debug('changing combing flow to control flow') 
     658#        create_flow(UML.ControlFlow, flow) 
     659
     660 
     661#def create_connector_end(connector, role): 
     662#    """ 
     663#    Create Connector End, set role and attach created end to 
     664#    connector. 
     665#    """ 
     666#    end = resource(UML.ElementFactory).create(UML.ConnectorEnd) 
     667#    end.role = role 
     668#    connector.end = end 
     669#    assert end in role.end 
     670#    return end 
     671
     672 
     673#def rotate(p1, p2, a, b, x, y): 
     674#    """ 
     675#    Rotate point (a, b) by angle, which is determined by line (p1, p2). 
     676
     677#    Rotated point is moved by vector (x, y). 
     678#    """ 
     679#    try: 
     680#        angle = atan((p1[1] - p2[1]) / (p1[0] - p2[0])) 
     681#    except ZeroDivisionError: 
     682#        da = p1[1] < p2[1] and 1.5 or -1.5 
     683#        angle = pi * da 
     684
     685#    sin_angle = sin(angle) 
     686#    cos_angle = cos(angle) 
     687#    return (cos_angle * a - sin_angle * b + x, 
     688#            sin_angle * a + cos_angle * b + y) 
    691689 
    692690# vim:sw=4:et:ai 
  • gaphor/trunk/gaphor/diagram/implementation.py

    r1121 r1233  
    4242        super(ImplementationItem, self).draw(context) 
    4343 
    44     def allow_connect_handle(self, handle, connecting_to): 
    45         """Implementation can connect head to Interface and 
    46         tail to BehavioredClassifier. 
    47         """ 
    48         can_connect = False 
    49  
    50         head = self.handles[0]  
    51         tail = self.handles[-1] 
    52  
    53         if head is handle and isinstance(connecting_to.subject, UML.Interface): 
    54             can_connect = True 
    55         elif tail is handle and isinstance(connecting_to.subject, UML.BehavioredClassifier): 
    56             can_connect = True 
    57  
    58         assert not head.connected_to \ 
    59             or head.connected_to and isinstance(head.connected_to.subject, UML.Interface) 
    60         assert not tail.connected_to or \ 
    61             tail.connected_to and isinstance(tail.connected_to.subject, UML.BehavioredClassifier) 
    62         assert not head.connected_to or not tail.connected_to \ 
    63             or head.connected_to and tail.connected_to \ 
    64                 and head.connected_to.subject != tail.connected_to.subject 
    65  
    66         #print 'Implementation.allow_connect_handle:', can_connect 
    67         return can_connect 
    68  
    69  
    70     def confirm_connect_handle (self, handle): 
    71         c1 = self.handles[0].connected_to 
    72         c2 = self.handles[-1].connected_to 
    73         if c1 and c2: 
    74             s1 = c1.subject 
    75