Changeset 1104
- Timestamp:
- 12/14/06 02:45:13 (2 years ago)
- Files:
-
- gaphor/branches/new-canvas/TODO (modified) (1 diff)
- gaphor/branches/new-canvas/gaphor/UML/elementfactory.py (modified) (1 diff)
- gaphor/branches/new-canvas/gaphor/adapters/connectors.py (modified) (1 diff)
- gaphor/branches/new-canvas/gaphor/adapters/editors.py (modified) (2 diffs)
- gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py (modified) (1 diff)
- gaphor/branches/new-canvas/gaphor/adapters/tests/test_editor.py (modified) (1 diff)
- gaphor/branches/new-canvas/gaphor/diagram/activitynodes.py (modified) (5 diffs)
- gaphor/branches/new-canvas/gaphor/diagram/connector.py (modified) (2 diffs)
- gaphor/branches/new-canvas/gaphor/diagram/flow.py (modified) (5 diffs)
- gaphor/branches/new-canvas/gaphor/diagram/items.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphor/branches/new-canvas/TODO
r1099 r1104 7 7 - text placement for action nodes. 8 8 - implement ObjectNode and flow items 9 - create a generic method to swap class types 9 10 10 11 For 0.10.0 and later: gaphor/branches/new-canvas/gaphor/UML/elementfactory.py
r1074 r1104 10 10 from gaphor.UML.event import CreateElementEvent, RemoveElementEvent, \ 11 11 FlushFactoryEvent, ModelFactoryEvent 12 13 # TODO: create an ElementFactory method that allows to swap instances of 14 # one type to another (e.g. ForkNode <-> DescisionNode) 12 15 13 16 gaphor/branches/new-canvas/gaphor/adapters/connectors.py
r1093 r1104 891 891 892 892 893 class 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 949 component.provideAdapter(factory=FlowConnect, 950 adapts=(items.ActionItem, items.FlowItem)) 951 component.provideAdapter(factory=FlowConnect, 952 adapts=(items.ActivityNodeItem, items.FlowItem)) 953 component.provideAdapter(factory=FlowConnect, 954 adapts=(items.ObjectNodeItem, items.FlowItem)) 955 956 957 class 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 989 component.provideAdapter(FlowForkNodeConnect) 990 991 893 992 # vim:sw=4:et:ai gaphor/branches/new-canvas/gaphor/adapters/editors.py
r1101 r1104 71 71 component.adapts(items.ObjectNodeItem) 72 72 73 def __init__(self, item): 74 super(ObjectNodeItemEditor, self).__init__(item) 75 self.edit_tag = False 76 73 77 def is_editable(self, x, y): 74 78 self.edit_tag = (x, y) in self._item.tag_bounds … … 89 93 90 94 component.provideAdapter(ObjectNodeItemEditor) 95 96 97 class 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 135 component.provideAdapter(FlowItemEditor) 91 136 92 137 gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py
r1088 r1104 450 450 assert len(list(UML.select())) == 3, list(UML.select()) 451 451 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. 453 626 454 627 # vim:sw=4:et:ai gaphor/branches/new-canvas/gaphor/adapters/tests/test_editor.py
r1035 r1104 44 44 assert adapter._edit is assoc.tail_end 45 45 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 120 120 121 121 class 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 123 124 combined property, which determines if the they represent combination 124 125 of fork/join or decision/merge nodes as described in UML … … 176 177 cr.stroke() 177 178 178 super( FDNode, self).draw(context)179 super(DecisionNodeItem, self).draw(context) 179 180 180 181 … … 198 199 self._join_spec_y = 0 199 200 200 # for h in self.handles:201 # h.props.movable = False202 # h.props.visible = False203 # self.handles[diacanvas.HANDLE_N].props.visible = True204 # self.handles[diacanvas.HANDLE_S].props.visible = True205 # self.handles[diacanvas.HANDLE_N].props.movable = True206 # self.handles[diacanvas.HANDLE_S].props.movable = True207 208 201 def update(self, context): 209 202 """ … … 211 204 """ 212 205 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), 214 207 self.width, self.height, 215 208 (ALIGN_CENTER, ALIGN_TOP), 216 209 (10, 0, 0, 0), 217 210 True) 218 211 super(ForkNodeItem, self).update(context) 219 212 220 213 def draw(self, context): … … 225 218 cr = context.cairo 226 219 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) 229 223 cr.move_to(self.name_x, self.name_y) 230 224 231 225 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) 233 227 234 228 cr.stroke() 235 229 super(ForkNodeItem, self).draw(context) 236 230 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() 270 247 271 248 # vim:sw=4:et gaphor/branches/new-canvas/gaphor/diagram/connector.py
r1100 r1104 54 54 from elementitem import ElementItem 55 55 from component import ComponentItem 56 from util import create_connector_end57 56 58 57 from gaphor.diagram.interfaceicon import AssembledInterfaceIcon … … 586 585 # return DiagramLine.on_shape_iter(self) 587 586 587 def 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 588 598 # vim:sw=4:et gaphor/branches/new-canvas/gaphor/diagram/flow.py
r1100 r1104 13 13 from gaphas.geometry import Rectangle 14 14 from gaphas.util import text_extents, text_multiline 15 16 import itertools 15 from gaphas.geometry import distance_rectangle_point 17 16 18 17 … … 46 45 # tail_a=0.0, tail_b=15.0, tail_c=6.0, tail_d=6.0) 47 46 47 name_bounds = property(lambda s: s._name_bounds) 48 guard_bounds = property(lambda s: s._guard_bounds) 48 49 49 50 def on_subject_notify(self, pspec, notifiers = ()): … … 97 98 self.update_guard(context) 98 99 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) 99 106 100 107 def draw_tail(self, context): … … 111 118 if self.subject: 112 119 text_multiline(cr, self._name_bounds[0], self._name_bounds[3], self.subject.name) 113 text_multiline(cr, self._guar td_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) 114 121 115 122 if context.hovered or context.focused or context.draw_all: … … 185 192 """ 186 193 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 196 194 197 195 gaphor/branches/new-canvas/gaphor/diagram/items.py
r1101 r1104 32 32 # Actions: 33 33 from gaphor.diagram.action import ActionItem 34 from gaphor.diagram.activitynodes import ActivityNodeItem 34 35 from gaphor.diagram.activitynodes import InitialNodeItem, ActivityFinalNodeItem 35 36 from gaphor.diagram.activitynodes import FlowFinalNodeItem
