Changeset 1104

Show
Ignore:
Timestamp:
12/14/06 02:45:13 (2 years ago)
Author:
arjanmol
Message:

updated flow/actions stuff

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphor/branches/new-canvas/TODO

    r1099 r1104  
    77 - text placement for action nodes. 
    88 - implement ObjectNode and flow items 
     9 - create a generic method to swap class types 
    910 
    1011For 0.10.0 and later: 
  • gaphor/branches/new-canvas/gaphor/UML/elementfactory.py

    r1074 r1104  
    1010from gaphor.UML.event import CreateElementEvent, RemoveElementEvent, \ 
    1111                             FlushFactoryEvent, ModelFactoryEvent 
     12 
     13# TODO: create an ElementFactory method that allows to swap instances of 
     14#       one type to another (e.g. ForkNode <-> DescisionNode) 
    1215 
    1316 
  • gaphor/branches/new-canvas/gaphor/adapters/connectors.py

    r1093 r1104  
    891891 
    892892 
     893class FlowConnect(RelationshipConnect): 
     894    """ 
     895    Connect FlowItem and Action/ObjectNode, initial/final nodes. 
     896    """ 
     897 
     898    def glue(self, handle, x, y): 
     899        """ 
     900        In addition to the normal check, both line ends may not be connected 
     901        to the same element. Same goes for subjects. 
     902        """ 
     903        opposite = self.line.opposite(handle) 
     904        line = self.line 
     905        element = self.element 
     906        subject = element.subject 
     907        connected_to = opposite.connected_to 
     908 
     909        if handle is line.head and isinstance(subject, UML.FinalNode) \ 
     910           or handle is line.tail and isinstance(subject, UML.InitialNode): 
     911            return None 
     912 
     913        # Another flow may not be connected: 
     914        #connected = line.canvas.get_connected_items(element) 
     915        #if handle is line.head: 
     916        #    for i, h in connected: 
     917        #        if isinstance(i, items.FlowItem) and h is i.head: 
     918        #            return None 
     919 
     920        #if handle is line.tail: 
     921        #    for i, h in connected: 
     922        #        if isinstance(i, items.FlowItem) and h is i.tail: 
     923        #            return None 
     924 
     925        #if handle is line.head and subject.outgoing and subject.outgoing is not line.subject: 
     926        #    return None 
     927 
     928        #if handle is line.tail and subject.incoming and subject.incoming is not line.subject: 
     929        #    return None 
     930 
     931        return super(FlowConnect, self).glue(handle, x, y) 
     932 
     933    def connect_subject(self): 
     934        line = self.line 
     935        element = self.element 
     936        if isinstance(line.head.connected_to, items.ObjectNodeItem) \ 
     937           or isinstance(line.tail.connected_to, items.ObjectNodeItem): 
     938            relation = self.relationship_or_new(UML.ObjectFlow, 
     939                        ('source', 'outgoing'), 
     940                        ('target', 'incoming')) 
     941        else: 
     942            relation = self.relationship_or_new(UML.ControlFlow, 
     943                        ('source', 'outgoing'), 
     944                        ('target', 'incoming')) 
     945        if not relation.guard: 
     946            relation.guard = UML.create(UML.LiteralSpecification) 
     947        self.line.subject = relation 
     948 
     949component.provideAdapter(factory=FlowConnect, 
     950                         adapts=(items.ActionItem, items.FlowItem)) 
     951component.provideAdapter(factory=FlowConnect, 
     952                         adapts=(items.ActivityNodeItem, items.FlowItem)) 
     953component.provideAdapter(factory=FlowConnect, 
     954                         adapts=(items.ObjectNodeItem, items.FlowItem)) 
     955 
     956 
     957class FlowForkNodeConnect(FlowConnect): 
     958    """ 
     959    Connect Flow to a ForkNode 
     960    """ 
     961    component.adapts(items.ForkNodeItem, items.FlowItem) 
     962 
     963    def glue(self, handle, x, y): 
     964        """ 
     965        In addition to the normal check, one end should have at most one 
     966        edge (incoming or outgoing). 
     967        """ 
     968        line = self.line 
     969        element = self.element 
     970        subject = element.subject 
     971 
     972        # If one side of self.element has more than one edge, the 
     973        # type of node is determined (either join or fork). 
     974        if handle is line.head and len(subject.incoming) > 1 and len(subject.outgoing) > 0: 
     975            print 'head: type determined' 
     976            return None 
     977 
     978        if handle is line.tail and len(subject.incoming) > 0 and len(subject.outgoing) > 1: 
     979            print 'tail: type determined' 
     980            return None 
     981 
     982        return super(FlowForkNodeConnect, self).glue(handle, x, y) 
     983 
     984    def connect_subject(self): 
     985        super(FlowForkNodeConnect, self).connect_subject() 
     986        # Switch class for self.element Join/Fork depending on the number 
     987        # of incoming/outgoing edges. 
     988 
     989component.provideAdapter(FlowForkNodeConnect) 
     990 
     991 
    893992# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/adapters/editors.py

    r1101 r1104  
    7171    component.adapts(items.ObjectNodeItem) 
    7272 
     73    def __init__(self, item): 
     74        super(ObjectNodeItemEditor, self).__init__(item) 
     75        self.edit_tag = False 
     76 
    7377    def is_editable(self, x, y): 
    7478        self.edit_tag = (x, y) in self._item.tag_bounds 
     
    8993 
    9094component.provideAdapter(ObjectNodeItemEditor) 
     95 
     96 
     97class FlowItemEditor(object): 
     98    """Text edit support for Named items. 
     99    """ 
     100    interface.implements(IEditor) 
     101    component.adapts(items.FlowItem) 
     102 
     103    def __init__(self, item): 
     104        self._item = item 
     105        self.edit_name = False 
     106        self.edit_guard = False 
     107 
     108    def is_editable(self, x, y): 
     109        if not self._item.subject: 
     110            return False 
     111        if (x, y) in self._item.name_bounds: 
     112            self.edit_name = True 
     113        elif (x, y) in self._item.guard_bounds: 
     114            self.edit_guard = True 
     115        return self.edit_name or self.edit_guard 
     116 
     117    def get_text(self): 
     118        if self.edit_name: 
     119            return self._item.subject.name 
     120        elif self.edit_guard: 
     121            return self._item.subject.guard.value 
     122 
     123    def get_bounds(self): 
     124        return None 
     125 
     126    def update_text(self, text): 
     127        if self.edit_name: 
     128            self._item.subject.name = text 
     129        elif self.edit_guard: 
     130            self._item.subject.guard.value = text 
     131 
     132    def key_pressed(self, pos, key): 
     133        pass 
     134 
     135component.provideAdapter(FlowItemEditor) 
    91136 
    92137 
  • gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py

    r1088 r1104  
    450450        assert len(list(UML.select())) == 3, list(UML.select()) 
    451451 
    452  
     452    def test_flow_activitynodes(self): 
     453        assert len(list(UML.select())) == 0 
     454 
     455        diagram = UML.create(UML.Diagram) 
     456        flow = diagram.create(items.FlowItem) 
     457        i1 = diagram.create(items.InitialNodeItem, subject=UML.create(UML.InitialNode)) 
     458        f1 = diagram.create(items.ActivityFinalNodeItem, subject=UML.create(UML.ActivityFinalNode)) 
     459        f2 = diagram.create(items.FlowFinalNodeItem, subject=UML.create(UML.FlowFinalNode)) 
     460 
     461        assert len(UML.lselect()) == 4 
     462 
     463        # head may not connect to FinalNode 
     464 
     465        adapter = component.queryMultiAdapter((f1, flow), IConnect) 
     466        assert adapter 
     467        adapter.connect(flow.head, flow.head.x, flow.head.y) 
     468        assert flow.head.connected_to is None 
     469 
     470        adapter.connect(flow.tail, flow.tail.x, flow.tail.y) 
     471        assert flow.head.connected_to is None 
     472        assert flow.tail.connected_to is f1 
     473 
     474        adapter.disconnect(flow.tail) 
     475        assert flow.head.connected_to is None 
     476        assert flow.tail.connected_to is None 
     477 
     478        adapter = component.queryMultiAdapter((f2, flow), IConnect) 
     479        assert adapter 
     480        adapter.connect(flow.head, flow.head.x, flow.head.y) 
     481        assert flow.head.connected_to is None 
     482 
     483        adapter.connect(flow.tail, flow.tail.x, flow.tail.y) 
     484        assert flow.head.connected_to is None 
     485        assert flow.tail.connected_to is f2 
     486 
     487        adapter.disconnect(flow.tail) 
     488        assert flow.head.connected_to is None 
     489        assert flow.tail.connected_to is None 
     490 
     491        # tail may not connect to InitialNode 
     492 
     493        adapter = component.queryMultiAdapter((i1, flow), IConnect) 
     494        assert adapter 
     495        adapter.connect(flow.tail, flow.tail.x, flow.tail.y) 
     496        assert flow.head.connected_to is None 
     497        assert flow.tail.connected_to is None 
     498 
     499        adapter.connect(flow.head, flow.head.x, flow.head.y) 
     500        assert flow.tail.connected_to is None 
     501        assert flow.head.connected_to is i1 
     502 
     503     
     504    def test_flow_action(self): 
     505        assert len(list(UML.select())) == 0 
     506 
     507        diagram = UML.create(UML.Diagram) 
     508        flow = diagram.create(items.FlowItem) 
     509        a1 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     510        a2 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     511        o1 = diagram.create(items.ObjectNodeItem, subject=UML.create(UML.ObjectNode)) 
     512 
     513        assert len(UML.lselect()) == 5, UML.lselect() 
     514 
     515        # Connect between two actions (ControlFlow) 
     516        adapter = component.queryMultiAdapter((a1, flow), IConnect) 
     517        assert adapter 
     518        adapter.connect(flow.tail, flow.tail.x, flow.tail.y) 
     519 
     520        assert flow.tail.connected_to is a1 
     521        assert flow.subject is None 
     522 
     523        adapter = component.queryMultiAdapter((a2, flow), IConnect) 
     524        adapter.connect(flow.head, flow.head.x, flow.head.y) 
     525 
     526        assert flow.head.connected_to is a2 
     527        assert flow.tail.connected_to is a1 
     528        assert not flow.subject is None 
     529        assert isinstance(flow.subject, UML.ControlFlow) 
     530 
     531        adapter.connect(flow.tail, flow.tail.x, flow.tail.y) 
     532 
     533        assert flow.head.connected_to is a2 
     534        assert flow.tail.connected_to is a2 
     535        assert not flow.subject is None 
     536        assert isinstance(flow.subject, UML.ControlFlow) 
     537 
     538        # Connection between action and objectNode (ObjectFlow) 
     539 
     540        adapter = component.queryMultiAdapter((o1, flow), IConnect) 
     541        adapter.connect(flow.head, flow.head.x, flow.head.y) 
     542 
     543        assert flow.head.connected_to is o1 
     544        assert flow.tail.connected_to is a2 
     545        assert not flow.subject is None 
     546        assert isinstance(flow.subject, UML.ObjectFlow) 
     547 
     548        adapter.disconnect(flow.head) 
     549 
     550        assert flow.head.connected_to is None 
     551        assert flow.tail.connected_to is a2 
     552        assert flow.subject is None 
     553 
     554    def test_flow_connect(self): 
     555        assert len(list(UML.select())) == 0 
     556 
     557        diagram = UML.create(UML.Diagram) 
     558        flow1 = diagram.create(items.FlowItem) 
     559        flow2 = diagram.create(items.FlowItem) 
     560        a1 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     561        a2 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     562        o1 = diagram.create(items.ObjectNodeItem, subject=UML.create(UML.ObjectNode)) 
     563 
     564        assert len(UML.lselect()) == 5, UML.lselect() 
     565 
     566        adapter = component.queryMultiAdapter((a1, flow1), IConnect) 
     567        assert adapter 
     568        adapter.connect(flow1.tail, flow1.tail.x, flow1.tail.y) 
     569        assert flow1.tail.connected_to is a1 
     570        assert not a1.subject.incoming, a1.subject.incoming 
     571 
     572        # More than one edge may be connected to an action: 
     573 
     574        adapter = component.queryMultiAdapter((a1, flow2), IConnect) 
     575        assert adapter 
     576        adapter.connect(flow2.tail, flow2.tail.x, flow2.tail.y) 
     577        assert flow1.tail.connected_to is a1 
     578        assert flow2.tail.connected_to is a1 
     579 
     580        adapter = component.queryMultiAdapter((a2, flow1), IConnect) 
     581        assert adapter 
     582        adapter.connect(flow1.head, flow1.head.x, flow1.head.y) 
     583        assert flow1.head.connected_to is a2 
     584        assert flow1.tail.connected_to is a1 
     585        assert flow1.subject in a1.subject.incoming 
     586        assert flow1.subject.target is a1.subject 
     587        assert flow1.subject in a2.subject.outgoing 
     588        assert flow1.subject.source is a2.subject 
     589 
     590    def test_flow_fork_decision(self): 
     591        """ 
     592        Test fork/decision behaviour. 
     593        """ 
     594        assert len(list(UML.select())) == 0 
     595 
     596        diagram = UML.create(UML.Diagram) 
     597        flow1 = diagram.create(items.FlowItem) 
     598        flow2 = diagram.create(items.FlowItem) 
     599        a1 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     600        a2 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     601        f1 = diagram.create(items.ForkNodeItem, subject=UML.create(UML.JoinNode)) 
     602 
     603        assert len(UML.lselect()) == 5, UML.lselect() 
     604 
     605        # Connect between two actions (ControlFlow) 
     606        adapter = component.queryMultiAdapter((a1, flow1), IConnect) 
     607        assert adapter 
     608        adapter.connect(flow1.head, flow1.head.x, flow1.head.y) 
     609 
     610        adapter = component.queryMultiAdapter((f1, flow1), IConnect) 
     611        assert adapter 
     612        adapter.connect(flow1.tail, flow1.tail.x, flow1.tail.y) 
     613        assert flow1.subject 
     614        assert flow1.subject.target is f1.subject 
     615        assert type(f1.subject) is UML.JoinNode 
     616 
     617        # [1] A join node has one outgoing edge. 
     618        #     self.outgoing->size() = 1 
     619        # [2] If a join node has an incoming object flow, it must have an 
     620        #     outgoing object flow, otherwise, it must have an outgoing control 
     621        #     flow. 
     622 
     623        # [1] A fork node has one incoming edge. 
     624        # [2] The edges coming into and out of a fork node must be either all 
     625        #     object flows or all control flows. 
    453626 
    454627# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/adapters/tests/test_editor.py

    r1035 r1104  
    4444        assert adapter._edit is assoc.tail_end 
    4545         
     46    def test_objectnode_editor(self): 
     47        diagram = UML.create(UML.Diagram) 
     48        node = diagram.create(items.ObjectNodeItem, subject=UML.create(UML.ObjectNode)) 
     49        diagram.canvas.update_now() 
     50 
     51        adapter = IEditor(node) 
     52        assert adapter.is_editable(10, 10) 
     53        assert not adapter.edit_tag 
     54 
     55        assert adapter.is_editable(*node.tag_bounds[:2]) 
     56        assert adapter.edit_tag 
     57 
     58 
  • gaphor/branches/new-canvas/gaphor/diagram/activitynodes.py

    r1072 r1104  
    120120 
    121121class FDNode(ActivityNodeItem): 
    122     """Abstract class for fork and decision UI nodes. These nodes contain 
     122    """ 
     123    Abstract class for fork and decision UI nodes. These nodes contain 
    123124    combined property, which determines if the they represent combination 
    124125    of fork/join or decision/merge nodes as described in UML 
     
    176177        cr.stroke() 
    177178 
    178         super(FDNode, self).draw(context) 
     179        super(DecisionNodeItem, self).draw(context) 
    179180 
    180181 
     
    198199        self._join_spec_y = 0 
    199200 
    200 #        for h in self.handles: 
    201 #            h.props.movable = False 
    202 #            h.props.visible = False 
    203 #        self.handles[diacanvas.HANDLE_N].props.visible = True 
    204 #        self.handles[diacanvas.HANDLE_S].props.visible = True 
    205 #        self.handles[diacanvas.HANDLE_N].props.movable = True 
    206 #        self.handles[diacanvas.HANDLE_S].props.movable = True 
    207  
    208201    def update(self, context): 
    209202        """ 
     
    211204        """ 
    212205        self._join_spec_x, self._join_spec_y = get_text_point( 
    213                 text_extents(context.cairo, self._join_spec), 
     206                text_extents(context.cairo, self.subject.joinSpec.value), 
    214207                self.width, self.height, 
    215208                (ALIGN_CENTER, ALIGN_TOP), 
    216209                (10, 0, 0, 0), 
    217210                True) 
    218  
     211        super(ForkNodeItem, self).update(context) 
    219212 
    220213    def draw(self, context): 
     
    225218        cr = context.cairo 
    226219        cr.set_line_width(self.width) 
    227         cr.move_to(0, 0) 
    228         cr.line_to(0, self.height) 
     220        x = self.width / 2. 
     221        cr.move_to(x, 0) 
     222        cr.line_to(x, self.height) 
    229223        cr.move_to(self.name_x, self.name_y) 
    230224 
    231225        cr.move_to(self._join_spec_x, self._join_spec_y) 
    232         cr.show_text(self._join_spec
     226        cr.show_text(self.subject.joinSpec.value
    233227 
    234228        cr.stroke() 
    235229        super(ForkNodeItem, self).draw(context) 
    236230 
    237  
    238 ###    def on_update(self, affine): 
    239 ###        """ 
    240 ###        Update fork/join node. 
    241 ### 
    242 ###        If node is join node then update also join specification. 
    243 ###        """ 
    244 ###        FDNode.on_update(self, affine) 
    245 ### 
    246 ###        w, h = self._join_spec.get_size() 
    247 ###        self._join_spec.update_label((self.width - w) / 2,  
    248 ###            -h - self.MARGIN) 
    249 ### 
    250 ###        GroupBase.on_update(self, affine) 
    251 ### 
    252 ### 
    253 ###    def on_subject_notify(self, pspec, notifiers = ()): 
    254 ###        """ 
    255 ###        Detect changes of subject. 
    256 ### 
    257 ###        If subject is join node, then set subject of join specification 
    258 ###        text element. 
    259 ###        """ 
    260 ###        FDNode.on_subject_notify(self, pspec, notifiers) 
    261 ###        if self.subject and isinstance(self.subject, UML.JoinNode): 
    262 ###            factory = resource(UML.ElementFactory) 
    263 ###            if not self.subject.joinSpec: 
    264 ###                self.subject.joinSpec = factory.create(UML.LiteralSpecification) 
    265 ###                self.subject.joinSpec.value = 'and' 
    266 ###            self._join_spec.subject = self.subject.joinSpec 
    267 ###        else: 
    268 ###            self._join_spec.subject = None 
    269 ###        self.request_update() 
     231    def on_subject_notify(self, pspec, notifiers = ()): 
     232        """ 
     233        Detect changes of subject. 
     234 
     235        If subject is join node, then set subject of join specification 
     236        text element. 
     237        """ 
     238        FDNode.on_subject_notify(self, pspec, notifiers) 
     239        if self.subject and isinstance(self.subject, UML.JoinNode): 
     240            if not self.subject.joinSpec: 
     241                self.subject.joinSpec = UML.create(UML.LiteralSpecification) 
     242                self.subject.joinSpec.value = 'and' 
     243            #self._join_spec.subject = self.subject.joinSpec 
     244        #else: 
     245        #    self._join_spec.subject = None 
     246        self.request_update() 
    270247 
    271248# vim:sw=4:et 
  • gaphor/branches/new-canvas/gaphor/diagram/connector.py

    r1100 r1104  
    5454from elementitem import ElementItem 
    5555from component import ComponentItem 
    56 from util import create_connector_end 
    5756 
    5857from gaphor.diagram.interfaceicon import AssembledInterfaceIcon 
     
    586585#            return DiagramLine.on_shape_iter(self) 
    587586 
     587def create_connector_end(connector, role): 
     588    """ 
     589    Create Connector End, set role and attach created end to 
     590    connector. 
     591    """ 
     592    end = resource(UML.ElementFactory).create(UML.ConnectorEnd) 
     593    end.role = role 
     594    connector.end = end 
     595    assert end in role.end 
     596    return end  
     597 
    588598# vim:sw=4:et 
  • gaphor/branches/new-canvas/gaphor/diagram/flow.py

    r1100 r1104  
    1313from gaphas.geometry import Rectangle 
    1414from gaphas.util import text_extents, text_multiline 
    15  
    16 import itertools 
     15from gaphas.geometry import distance_rectangle_point 
    1716 
    1817 
     
    4645        #         tail_a=0.0, tail_b=15.0, tail_c=6.0, tail_d=6.0) 
    4746 
     47    name_bounds = property(lambda s: s._name_bounds) 
     48    guard_bounds = property(lambda s: s._guard_bounds) 
    4849 
    4950    def on_subject_notify(self, pspec, notifiers = ()): 
     
    9798        self.update_guard(context) 
    9899 
     100    def point(self, x, y): 
     101        d1 = super(FlowItem, self).point(x, y) 
     102        drp = distance_rectangle_point 
     103        d2 = drp(self._name_bounds, (x, y)) 
     104        d3 = drp(self._guard_bounds, (x, y)) 
     105        return min(d1, d2, d3) 
    99106 
    100107    def draw_tail(self, context): 
     
    111118        if self.subject: 
    112119            text_multiline(cr, self._name_bounds[0], self._name_bounds[3], self.subject.name) 
    113             text_multiline(cr, self._guartd_bounds[0], self._guard_bounds[3], self.subject.guard and self.subject.guard.value) 
     120            text_multiline(cr, self._guard_bounds[0], self._guard_bounds[3], self.subject.guard and self.subject.guard.value) 
    114121 
    115122        if context.hovered or context.focused or context.draw_all: 
     
    185192        """ 
    186193        pass 
    187  
    188  
    189     def on_shape_iter(self): 
    190         """ 
    191         Return activity edge name and circle. 
    192         """ 
    193         it = TextElement.on_shape_iter(self) 
    194         return itertools.chain([self._circle], it) 
    195  
    196194 
    197195 
  • gaphor/branches/new-canvas/gaphor/diagram/items.py

    r1101 r1104  
    3232# Actions: 
    3333from gaphor.diagram.action import ActionItem 
     34from gaphor.diagram.activitynodes import ActivityNodeItem 
    3435from gaphor.diagram.activitynodes import InitialNodeItem, ActivityFinalNodeItem 
    3536from gaphor.diagram.activitynodes import FlowFinalNodeItem