Changeset 1100

Show
Ignore:
Timestamp:
12/07/06 07:56:40 (2 years ago)
Author:
arjanmol
Message:

First fixes FlowItem?, ObjectNode?. May not work.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphor/branches/new-canvas/gaphor/actions/placementactions.py

    r1063 r1100  
    300300 
    301301 
    302 #class ObjectNodePlacementAction(NamespacePlacementAction): 
    303 #    id = 'InsertObjectNode' 
    304 #    label = 'Object Node' 
    305 #    tooltip = 'Create a new object node' 
    306 #    stock_id = 'gaphor-object-node' 
    307 #    name = 'Object' 
    308 #    type = diagram.ObjectNodeItem 
    309 #    subject_type = UML.ObjectNode 
    310 
    311 #register_action(ObjectNodePlacementAction) 
     302class ObjectNodePlacementAction(NamespacePlacementAction): 
     303    id = 'InsertObjectNode' 
     304    label = 'Object Node' 
     305    tooltip = 'Create a new object node' 
     306    stock_id = 'gaphor-object-node' 
     307    name = 'Object' 
     308    type = items.ObjectNodeItem 
     309    subject_type = UML.ObjectNode 
     310 
     311register_action(ObjectNodePlacementAction) 
    312312 
    313313 
     
    396396 
    397397 
    398 #class FlowPlacementAction(PlacementAction): 
    399 #    id = 'InsertFlow' 
    400 #    label = 'Control/Object _Flow' 
    401 #    tooltip = 'Create a new control/object flow' 
    402 #    stock_id = 'gaphor-control-flow' 
    403 #    name = 'Flow' 
    404 #    type = diagram.FlowItem 
    405 
    406 #register_action(FlowPlacementAction) 
     398class FlowPlacementAction(PlacementAction): 
     399    id = 'InsertFlow' 
     400    label = 'Control/Object _Flow' 
     401    tooltip = 'Create a new control/object flow' 
     402    stock_id = 'gaphor-control-flow' 
     403    name = 'Flow' 
     404    type = items.FlowItem 
     405 
     406register_action(FlowPlacementAction) 
    407407 
    408408 
  • gaphor/branches/new-canvas/gaphor/actions/tests/test_placementactions.py

    r1046 r1100  
    104104        self.do_test_placement(placementactions.FlowFinalNodePlacementAction()) 
    105105 
     106    def test_flow_placement(self): 
     107        self.do_test_placement(placementactions.FlowPlacementAction()) 
     108 
    106109 
    107110# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/diagram/association.py

    r1097 r1100  
    609609            if h: 
    610610                name_dx = ofs 
    611                 name_dy = -ofs - name_h # - height 
     611                name_dy = -ofs - name_h 
    612612                mult_dx = ofs 
    613613                mult_dy = ofs 
    614614            else: 
    615615                name_dx = -ofs - name_w 
    616                 name_dy = -ofs - name_h # - height 
     616                name_dy = -ofs - name_h 
    617617                mult_dx = -ofs - mult_w 
    618618                mult_dy = ofs 
     
    620620            #print 'vertical line' 
    621621            if v: 
    622                 name_dx = -ofs - name_w # - width 
     622                name_dx = -ofs - name_w 
    623623                name_dy = ofs 
    624624                mult_dx = ofs 
    625625                mult_dy = ofs 
    626626            else: 
    627                 name_dx = -ofs - name_w # - width 
    628                 name_dy = -ofs - name_h # - height 
     627                name_dx = -ofs - name_w 
     628                name_dy = -ofs - name_h 
    629629                mult_dx = ofs 
    630                 mult_dy = -ofs - mult_h # - height 
     630                mult_dy = -ofs - mult_h 
    631631        else: 
    632632            r = abs_rc < 1.0 
     
    637637                mult_dx = ofs 
    638638            else: 
    639                 name_dx = -ofs - name_w # - width 
    640                 mult_dx = -ofs - mult_w # - width 
     639                name_dx = -ofs - name_w 
     640                mult_dx = -ofs - mult_w 
    641641            if align_bottom: 
    642                 name_dy = -ofs - name_h # - height 
    643                 mult_dy = -ofs - name_h - mult_h # - height 
     642                name_dy = -ofs - name_h 
     643                mult_dy = -ofs - name_h - mult_h 
    644644            else: 
    645645                name_dy = ofs  
  • gaphor/branches/new-canvas/gaphor/diagram/connector.py

    r926 r1100  
    5050""" 
    5151 
    52 import diacanvas 
    5352from gaphor import resource, UML 
    5453from diagramline import DiagramLine, FreeLine 
  • gaphor/branches/new-canvas/gaphor/diagram/flow.py

    r935 r1100  
    44Contains also implementation to split flows using activity edge connectors. 
    55""" 
    6 # vim:sw=4:et:ai 
    7  
    8 from __future__ import generators 
    9  
    10 import diacanvas 
     6 
     7from math import atan, pi, sin, cos 
    118 
    129from gaphor import resource 
     10 
    1311from gaphor import UML 
    14 from gaphor.diagram import TextElement 
    1512from gaphor.diagram.diagramline import DiagramLine 
    16  
    17 import gaphor.diagram.util 
     13from gaphas.geometry import Rectangle 
     14from gaphas.util import text_extents, text_multiline 
    1815 
    1916import itertools 
    2017 
    2118 
    22 class FlowBase(DiagramLine): 
    23     """ 
    24     Control flow and object flow abstract class. Allows to create flows 
    25     with name and guard. 
     19node_classes = { 
     20    UML.ForkNode:     UML.JoinNode, 
     21    UML.DecisionNode: UML.MergeNode, 
     22    UML.JoinNode:     UML.ForkNode, 
     23    UML.MergeNode:    UML.DecisionNode, 
     24
     25 
     26 
     27class FlowItem(DiagramLine): 
     28    """ 
     29    Representation of control flow and object flow. Flow item has name and 
     30    guard. It can be splitted into two flows with activity edge connectors. 
    2631    """ 
    2732 
    2833    __uml__ = UML.ControlFlow 
    29     __relationship__ = 'source', 'outgoing', 'target', 'incoming' 
     34 
     35    popup_menu = DiagramLine.popup_menu + ( 
     36        'separator', 
     37        'SplitFlow', 
     38    ) 
    3039 
    3140    def __init__(self, id = None): 
    32         GroupBase.__init__(self) 
    3341        DiagramLine.__init__(self, id) 
    34  
    35         self.set(has_tail=1, tail_fill_color=0, 
    36                  tail_a=0.0, tail_b=15.0, tail_c=6.0, tail_d=6.0) 
    37  
    38  
    39     def create_name(self): 
    40         self._name  = TextElement('name') 
    41         self.add(self._name) 
    42  
    43  
    44     def create_guard(self): 
    45         self._guard = TextElement('value') 
    46         self.add(self._guard) 
     42        self._name_bounds = None 
     43        self._guard_bounds = None 
     44 
     45        #self.set(has_tail=1, tail_fill_color=0, 
     46        #         tail_a=0.0, tail_b=15.0, tail_c=6.0, tail_d=6.0) 
    4747 
    4848 
    4949    def on_subject_notify(self, pspec, notifiers = ()): 
    50         DiagramLine.on_subject_notify(self, pspec, notifiers) 
    51  
    52         if hasattr(self, '_guard'): 
    53             if self.subject: 
    54                 self._guard.subject = self.subject.guard 
    55             else: 
    56                 self._guard.subject = None 
    57  
    58         if hasattr(self, '_name'): 
    59             self._name.subject = self.subject 
     50        DiagramLine.on_subject_notify(self, pspec, ('guard', 'guard.value',) + notifiers) 
    6051 
    6152        self.request_update() 
    6253 
    63  
    64     def update_name(self, affine): 
    65         handles = self.handles 
     54    def on_subject_notify__guard(self, subject, pspec=None): 
     55        self.request_update() 
     56 
     57    def on_subject_notify__guard_value(self, subject, pspec=None): 
     58        self.request_update() 
     59 
     60    def update_name(self, context): 
     61        handles = self._handles 
    6662 
    6763        def get_pos(p1, p2, width, height): 
     
    7268            return x, y 
    7369 
    74         p1 = handles[-2].get_pos_i() 
    75         p2 = handles[-1].get_pos_i() 
    76         w, h = self._name.get_size(
     70        p1 = handles[-2].pos 
     71        p2 = handles[-1].pos 
     72        w, h = text_extents(context.cairo, self.subject and self.subject.name
    7773        x, y = get_pos(p1, p2, w, h) 
    78         self._name.update_label(x, y
    79  
    80  
    81     def update_guard(self, affine): 
    82         handles = self.handles 
     74        self._name_bounds = Rectangle(x, y, width=max(10, w), height=max(10, h)
     75 
     76 
     77    def update_guard(self, context): 
     78        handles = self._handles 
    8379        middle = len(handles)/2 
    8480 
     
    9086            return x, y 
    9187 
    92         p1 = handles[middle-1].get_pos_i() 
    93         p2 = handles[middle].get_pos_i() 
    94         w, h = self._guard.get_size(
     88        p1 = handles[middle-1].pos 
     89        p2 = handles[middle].pos 
     90        w, h = text_extents(context.cairo, self.subject and self.subject.guard and self.subject.guard.value
    9591        x, y = get_pos_centered(p1, p2, w, h) 
    96         self._guard.update_label(x, y) 
    97  
    98  
    99     def on_update(self, affine): 
    100         DiagramLine.on_update(self, affine) 
    101         GroupBase.on_update(self, affine) 
    102  
    103  
    104     def allow_connect_handle(self, handle, connecting_to): 
    105         """See DiagramLine.allow_connect_handle(). 
    106         """ 
    107         can_connect = False 
    108  
    109         subject = connecting_to.subject 
    110         if isinstance(subject, UML.ActivityNode): 
    111             source = self.handles[0]  
    112             target = self.handles[-1] 
    113  
    114             # forbid flow source to connect to final node 
    115             # forbid flow target to connect to initial nodes 
    116             can_connect = True 
    117             if source is handle and isinstance(subject, UML.FinalNode) \ 
    118                     or target is handle and isinstance(subject, UML.InitialNode): 
    119                 can_connect = False 
    120  
    121         return can_connect 
    122  
    123  
    124     def connect_items(self, c1, c2): 
    125         if c1 and c2: 
    126             s1 = c1.subject 
    127             if isinstance(s1, tuple(gaphor.diagram.util.node_classes.keys())) \ 
    128                     and c1.props.combined: 
    129                 log.debug('getting combined node for flow source') 
    130                 s1 = s1.outgoing[0].target 
    131  
    132             s2 = c2.subject 
    133             relation = self.relationship 
    134             if not relation: 
    135                 factory = resource(UML.ElementFactory) 
    136  
    137                 # if we connect to object node than flow uml class should 
    138                 # be ObjectFlow 
    139                 if isinstance(s1, UML.ObjectNode) \ 
    140                         or isinstance(s2, UML.ObjectNode): 
    141                     relcls = UML.ObjectFlow 
    142                 else: 
    143                     relcls = UML.ControlFlow 
    144                 assert relcls == UML.ObjectFlow or relcls == UML.ControlFlow 
    145  
    146                 relation = factory.create(relcls) 
    147                 relation.source = s1 
    148                 relation.target = s2 
    149                 relation.guard = factory.create(UML.LiteralSpecification) 
    150             self.subject = relation 
    151  
    152             gaphor.diagram.util.determine_node_on_connect(c1) 
    153             gaphor.diagram.util.determine_node_on_connect(c2) 
    154  
    155  
    156     def disconnect_items(self, c1, c2, was_connected_to): 
    157         if not c1: 
    158             c1 = was_connected_to 
    159         if not c2: 
    160             c2 = was_connected_to 
    161  
    162         self.set_subject(None) 
    163  
    164         if c1: 
    165             gaphor.diagram.util.determine_node_on_disconnect(c1) 
    166         if c2: 
    167             gaphor.diagram.util.determine_node_on_disconnect(c2) 
    168  
    169  
    170  
    171 class FlowItem(FlowBase): 
    172     """ 
    173     Representation of control flow and object flow. Flow item has name and 
    174     guard. It can be splitted into two flows with activity edge connectors. 
    175     """ 
    176  
    177     popup_menu = DiagramLine.popup_menu + ( 
    178         'separator', 
    179         'SplitFlow', 
    180     ) 
    181  
    182     def __init__(self, id = None): 
    183         FlowBase.__init__(self, id) 
    184  
    185         self.create_name() 
    186         self.create_guard() 
    187  
    188  
    189     def on_update(self, affine): 
    190         self.update_name(affine) 
    191         self.update_guard(affine) 
    192         FlowBase.on_update(self, affine) 
    193  
    194  
    195     # Gaphor Connection Protocol 
    196  
    197     def confirm_connect_handle (self, handle): 
    198         """See DiagramLine.confirm_connect_handle(). 
    199         """ 
    200         c1 = self.handles[0].connected_to   # source 
    201         c2 = self.handles[-1].connected_to  # target 
    202         self.connect_items(c1, c2) 
    203  
    204  
    205     def confirm_disconnect_handle (self, handle, was_connected_to): 
    206         """See DiagramLine.confirm_disconnect_handle(). 
    207         """ 
    208         c1 = self.handles[0].connected_to   # source 
    209         c2 = self.handles[-1].connected_to  # target 
    210         self.disconnect_items(c1, c2, was_connected_to) 
    211  
    212  
    213  
    214 class ACItem(TextElement): 
     92        self._guard_bounds = Rectangle(x, y, width=max(10, w), height=max(10, h)) 
     93 
     94 
     95    def pre_update(self, context): 
     96        self.update_name(context) 
     97        self.update_guard(context) 
     98 
     99 
     100    def draw_tail(self, context): 
     101        cr = context.cairo 
     102        cr.line_to(0, 0) 
     103        cr.stroke() 
     104        cr.move_to(15, -6) 
     105        cr.line_to(0, 0) 
     106        cr.line_to(15, 6) 
     107 
     108    def draw(self, context): 
     109        super(FlowItem, self).draw(context) 
     110        cr= context.cairo 
     111        if self.subject: 
     112            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) 
     114 
     115        if context.hovered or context.focused or context.draw_all: 
     116            cr.set_line_width(0.5) 
     117            b = self._name_bounds 
     118            cr.rectangle(b.x0, b.y0, b.width, b.height) 
     119            cr.stroke() 
     120            b = self._guard_bounds 
     121            cr.rectangle(b.x0, b.y0, b.width, b.height) 
     122            cr.stroke() 
     123 
     124         
     125#class ACItem(TextElement): 
     126class ACItem(object): 
    215127    """ 
    216128    Activity edge connector. It is a circle with name inside. 
     
    219131    RADIUS = 10 
    220132    def __init__(self, id): 
    221         TextElement.__init__(self, id) 
    222         self._circle = diacanvas.shape.Ellipse() 
    223         self._circle.set_line_width(2.0) 
    224         self._circle.set_fill_color(diacanvas.color(255, 255, 255)) 
    225         self._circle.set_fill(diacanvas.shape.FILL_SOLID) 
    226         self.show_border = False 
     133        #TextElement.__init__(self, id) 
     134        #self._circle = diacanvas.shape.Ellipse() 
     135        #self._circle.set_line_width(2.0) 
     136        #self._circle.set_fill_color(diacanvas.color(255, 255, 255)) 
     137        #self._circle.set_fill(diacanvas.shape.FILL_SOLID) 
     138        #self.show_border = False 
    227139 
    228140        # set new value notification function to change activity edge 
     
    284196 
    285197 
    286 class CFlowItem(FlowBase): 
     198class CFlowItem(FlowItem): 
    287199    """ 
    288200    Abstract class for flows with activity edge connector. Flow with 
     
    300212 
    301213    def __init__(self, id = None): 
    302         FlowBase.__init__(self, id) 
     214        FlowItem.__init__(self, id) 
    303215 
    304216        self._connector = ACItem('value') 
     
    324236        Save connector name and opposite flow with activity edge connector. 
    325237        """ 
    326         FlowBase.save(self, save_func) 
     238        FlowItem.save(self, save_func) 
    327239        save_func('opposite', self._opposite, True) 
    328240        save_func('connector-name', self._connector.subject.value) 
     
    338250            self._opposite = value 
    339251        else: 
    340             FlowBase.load(self, name, value) 
     252            FlowItem.load(self, name, value) 
    341253 
    342254 
     
    354266        x = p1[0] < p2[0] and -r or r 
    355267        y = 0 
    356         x, y = gaphor.diagram.util.rotate(p1, p2, x, y, p1[0], p1[1]) 
     268        x, y = rotate(p1, p2, x, y, p1[0], p1[1]) 
    357269 
    358270        self._connector.move_center(x, y) 
    359271 
    360         FlowBase.on_update(self, affine) 
     272        FlowItem.on_update(self, affine) 
    361273 
    362274 
     
    380292        if handle == self.get_inactive_handle(): 
    381293            return False 
    382         return FlowBase.allow_connect_handle(self, handle, connecting_to) 
     294        return FlowItem.allow_connect_handle(self, handle, connecting_to) 
    383295 
    384296 
     
    395307class CFlowItemA(CFlowItem): 
    396308    """ 
     309    * Is used for split flows, as is CFlowItemB * 
     310 
    397311    Flow with activity edge connector, which starts from node and points to 
    398312    activity edge connector. 
     
    462376        """ 
    463377        return self.handles[0] 
     378 
     379 
     380 
     381 
     382def move_collection(src, target, name): 
     383    """ 
     384    Copy collection from one object to another. 
     385 
     386    src    - source object 
     387    target - target object 
     388    name   - name of attribute, which is collection to copy 
     389    """ 
     390    # first make of copy of collection, because assigning 
     391    # element to target collection moves this element 
     392    for flow in list(getattr(src, name)): 
     393        getattr(target, name).append(flow) 
     394 
     395 
     396def is_fd(node): 
     397    """ 
     398    Check if node is fork or decision node. 
     399    """ 
     400    return isinstance(node, (UML.ForkNode, UML.DecisionNode)) 
     401 
     402 
     403def change_node_class(node): 
     404    """ 
     405    If UML constraints for fork, join, decision and merge nodes are not 
     406    met, then create new node depending on input node class, i.e. create 
     407    fork node from join node or merge node from decision node. 
     408 
     409    If constraints are met, then return node itself. 
     410    """ 
     411    if is_fd(node) and len(node.incoming) > 1 \ 
     412            or not is_fd(node) and len(node.incoming) < 2: 
     413 
     414        factory = resource(UML.ElementFactory) 
     415        cls = node_classes[node.__class__] 
     416        log.debug('creating %s' % cls) 
     417        nn = factory.create(cls) 
     418        move_collection(node, nn, 'incoming') 
     419        move_collection(node, nn, 'outgoing') 
     420    else: 
     421        nn = node 
     422 
     423    assert nn is not None 
     424 
     425    # we have to accept zero of outgoing edges in case of fork/descision 
     426    # nodes 
     427    assert is_fd(nn) and len(nn.incoming) <= 1 \ 
     428        or not is_fd(nn) and len(nn.incoming) >= 1, '%s' % nn 
     429    assert is_fd(nn) and len(nn.outgoing) >= 0 \ 
     430        or not is_fd(nn) and len(nn.outgoing) <= 1, '%s' % nn 
     431    return nn 
     432 
     433 
     434def combine_nodes(node): 
     435    """ 
     436    Create fork/join (decision/merge) nodes combination as described in UML 
     437    specification. 
     438    """ 
     439    log.debug('combining nodes') 
     440 
     441    cls = node_classes[node.__class__] 
     442    log.debug('creating %s' % cls) 
     443    factory = resource(UML.ElementFactory) 
     444    target = factory.create(cls) 
     445 
     446    source = node 
     447    if is_fd(node): 
     448        source = target 
     449        move_collection(node, source, 'incoming') 
     450 
     451        # create new fork node 
     452        cls = node_classes[source.__class__] 
     453        log.debug('creating %s' % cls) 
     454        target = factory.create(cls) 
     455        move_collection(node, target, 'outgoing') 
     456    else: 
     457        # fork node is created, referenced by target 
     458        move_collection(source, target, 'outgoing') 
     459 
     460    assert not is_fd(source) 
     461    assert is_fd(target) 
     462 
     463    # create flow 
     464    c1 = count_object_flows(source, 'incoming') 
     465    c2 = count_object_flows(target, 'outgoing') 
     466 
     467    if c1 > 0 or c2 > 0: 
     468        flow = factory.create(UML.ControlFlow) 
     469    else: 
     470        flow = factory.create(UML.ObjectFlow) 
     471    flow.source = source 
     472    flow.target = target 
     473 
     474    assert len(source.incoming) > 1 
     475    assert len(source.outgoing) == 1 
     476 
     477    assert len(target.incoming) == 1 
     478    assert len(target.outgoing) > 1 
     479 
     480    return source 
     481 
     482 
     483def decombine_nodes(source): 
     484    """ 
     485    Create node depending on source argument which denotes combination of 
     486    fork/join (decision/merge) nodes as described in UML specification. 
     487 
     488    Combination of nodes is destroyed. 
     489    """ 
     490    log.debug('decombining nodes') 
     491    flow = source.outgoing[0] 
     492    target = flow.target 
     493 
     494    if len(source.incoming) < 2: 
     495        # create fork or decision 
     496        cls = target.__class__ 
     497    else: 
     498        # create join or merge 
     499        cls = source.__class__ 
     500 
     501    factory = resource(UML.ElementFactory) 
     502    node = factory.create(cls) 
     503 
     504    move_collection(source, node, 'incoming') 
     505    move_collection(target, node, 'outgoing') 
     506 
     507    assert source != node 
     508 
     509    # delete target and combining flow 
     510    # source should be deleted by caller 
     511    target.unlink() 
     512    flow.unlink() 
     513 
     514    # return new node 
     515    return node 
     516 
     517 
     518def determine_node_on_connect(el): 
     519    """ 
     520    Determine classes of nodes depending on amount of incoming 
     521    and outgoing edges. This method is called when flow is attached 
     522    to node. 
     523 
     524    If there is more than one incoming edge and more than one 
     525    outgoing edge, then create two nodes and combine them with 
     526    flow as described in UML specification. 
     527    """ 
     528    subject = el.subject 
     529    if not isinstance(subject, tuple(node_classes.keys())): 
     530        return 
     531 
     532    new_subject = subject 
     533 
     534    if len(subject.incoming) > 1 and len(subject.outgoing) > 1: 
     535        new_subject = combine_nodes(subject) 
     536        el.props.combined = True 
     537 
     538    else: 
     539        new_subject = change_node_class(subject) 
     540 
     541    change_node_subject(el, new_subject) 
     542 
     543    if el.props.combined: 
     544        check_combining_flow(el) 
     545 
     546 
     547def determine_node_on_disconnect(el): 
     548    """ 
     549    Determine classes of nodes depending on amount of incoming 
     550    and outgoing edges. This method is called when flow is dettached 
     551    from node. 
     552 
     553    If there are combined nodes and there is no need for them, then replace 
     554    combination with appropriate node (i.e. replace with fork node when 
     555    there are less than two incoming edges). This way data model is kept as 
     556    simple as possible. 
     557    """ 
     558    subject = el.subject 
     559    if not isinstance(subject, tuple(node_classes.keys())): 
     560        return 
     561 
     562    new_subject = subject 
     563 
     564    if el.props.combined: 
     565        cs = subject.outgoing[0].target 
     566        # decombine node when there is no more than one incoming 
     567        # and no more than one outgoing flow 
     568        if len(subject.incoming) < 2 or len(cs.outgoing) < 2: 
     569            new_subject = decombine_nodes(subject) 
     570            el.props.combined = False 
     571        else: 
     572            check_combining_flow(el) 
     573 
     574    else:  
     575        new_subject = change_node_class(subject) 
     576 
     577    change_node_subject(el, new_subject) 
     578 
     579 
     580def change_node_subject(el, new_subject): 
     581    """ 
     582    Change element's subject if new subject is different than element's 
     583    subject. If subject is changed, then old subject is destroyed. 
     584    """ 
     585    subject = el.subject 
     586    if new_subject != subject: 
     587        log.debug('changing subject of ui node %s' % el) 
     588        el.set_subject(new_subject) 
     589 
     590        log.debug('deleting node %s' % subject) 
     591        subject.unlink() 
     592 
     593 
     594def create_flow(cls, flow): 
     595    """ 
     596    Create new flow of class cls. Flow data from flow argument are copied 
     597    to new created flow. Old flow is destroyed. 
     598    """ 
     599    factory = resource(UML.ElementFactory) 
     600    f = factory.create(cls) 
     601    f.source = flow.source 
     602    f.target = flow.target 
     603    flow.unlink() 
     604    return f 
     605 
     606 
     607def count_object_flows(node, attr): 
     608    """ 
     609    Count incoming or outgoing object flows. 
     610    """ 
     611    return len(getattr(node, attr) 
     612        .select(lambda flow: isinstance(flow, UML.ObjectFlow))) 
     613 
     614 
     615def check_combining_flow(el): 
     616    """ 
     617    Set object flow as combining flow when incoming or outgoing flow count 
     618    is greater than zero. Otherwise change combining flow to control flow. 
     619    """ 
     620    subject = el.subject 
     621    flow = subject.outgoing[0] # combining flow 
     622    combined = flow.target     # combined node 
     623 
     624    c1 = count_object_flows(subject, 'incoming') 
     625    c2 = count_object_flows(combined, 'outgoing') 
     626 
     627    log.debug('combined incoming and outgoing object flow count: (%d, %d)' % (c1, c2)) 
     628 
     629    if (c1 > 0 or c2 > 0) and isinstance(flow, UML.ControlFlow): 
     630        log.debug('changing combing flow to object flow') 
     631        create_flow(UML.ObjectFlow, flow) 
     632    elif c1 == 0 and c2 == 0 and isinstance(flow, UML.ObjectFlow): 
     633        log.debug('changing combing flow to control flow') 
     634        create_flow(UML.ControlFlow, flow) 
     635 
     636 
     637def create_connector_end(connector, role): 
     638    """ 
     639    Create Connector End, set role and attach created end to 
     640    connector. 
     641    """ 
     642    end = resource(UML.ElementFactory).create(UML.ConnectorEnd) 
     643    end.role = role 
     644    connector.end = end 
     645    assert end in role.end 
     646    return end 
     647 
     648 
     649def rotate(p1, p2, a, b, x, y): 
     650    """ 
     651    Rotate point (a, b) by angle, which is determined by line (p1, p2). 
     652 
     653    Rotated point is moved by vector (x, y). 
     654    """ 
     655    try: 
     656        angle = atan((p1[1] - p2[1]) / (p1[0] - p2[0])) 
     657    except ZeroDivisionError: 
     658        da = p1[1] < p2[1] and 1.5 or -1.5 
     659        angle = pi * da 
     660 
     661    sin_angle = sin(angle) 
     662    cos_angle = cos(angle) 
     663    return (cos_angle * a - sin_angle * b + x, 
     664            sin_angle * a + cos_angle * b + y) 
     665 
     666# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/diagram/interaction.py

    r772 r1100  
    22InteractionItem diagram item 
    33''' 
    4 # vim:sw=4:et 
    54 
    6 from __future__ import generators 
    7  
    8 import gobject 
    9 import pango 
    10 import diacanvas 
    115from gaphor import UML 
    126from nameditem import NamedItem 
     
    134128    def on_groupable_iter(self): 
    135129        return iter(self._children) 
     130 
     131# vim:sw=4:et 
  • gaphor/branches/new-canvas/gaphor/diagram/items.py

    r1081 r1100  
    3636from gaphor.diagram.activitynodes import DecisionNodeItem 
    3737from gaphor.diagram.activitynodes import ForkNodeItem 
     38from gaphor.diagram.flow import FlowItem 
    3839 
    3940# Use Cases: 
  • gaphor/branches/new-canvas/gaphor/diagram/lifeline.py

    r926 r1100  
    33""" 
    44 
    5 import gobject 
    6 import pango 
    7 import diacanvas 
    85from gaphor import UML 
    96from gaphor.diagram.align import V_ALIGN_MIDDLE 
    107from gaphor.diagram.diagramline import FreeLine 
    11 from gaphor.diagram.groupable import GroupBase 
    128from gaphor.diagram.nameditem import NamedItem 
    139 
  • gaphor/branches/new-canvas/gaphor/diagram/message.py

    r773 r1100  
    55import itertools 
    66 
    7 import gobject 
    8 import pango 
    9 import diacanvas 
    10  
    117from gaphor import resource, UML 
    128 
    139from gaphor.diagram.diagramline import DiagramLine 
    1410from gaphor.diagram.lifeline import LifelineItem, LifetimeItem 
    15 from gaphor.diagram import TextElement 
    16 from gaphor.diagram.groupable import GroupBase 
     11#from gaphor.diagram import TextElement 
    1712 
    1813# 
  • gaphor/branches/new-canvas/gaphor/diagram/objectnode.py

    r1097 r1100  
    4040        NamedItem.__init__(self, id) 
    4141 
    42         self._upper_bound = TextElement('value', '{ upperBound = %s }', '*') 
    43         self.add(self._upper_bound) 
     42        self._tag = '' #TextElement('value', '{ upperBound = %s }', '*') 
     43        self._tag_bounds = None 
     44        #self.add(self._upper_bound) 
    4445 
    45         self._ordering = diacanvas.shape.Text() 
    46         self._ordering.set_font_description(pango.FontDescription(font.FONT)) 
    47         self._ordering.set_alignment(pango.ALIGN_CENTER) 
    48         self._ordering.set_markup(False) 
     46        #self._ordering = diacanvas.shape.Text() 
     47        #self._ordering.set_font_description(pango.FontDescription(font.FONT)) 
     48        #self._ordering.set_alignment(pango.ALIGN_CENTER) 
     49        #self._ordering.set_markup(False) 
    4950 
    5051        self._show_ordering = False 
     52 
     53    def _set_ordering(self, ordering): 
     54        """ 
     55        Set ordering of object node. 
     56        """ 
     57        self.subject.ordering = ordering 
     58        self.request_update() 
     59 
     60    ordering = property(lambda s: s.subject.ordering, _set_ordering) 
     61 
     62    def _set_show_ordering(self, value): 
     63        #self.preserve_property(pspec.name) 
     64        self._show_ordering = value 
     65        self.request_update() 
     66 
     67    show_ordering = property(lambda s: s._show_ordering, _set_show_ordering) 
    5168 
    5269    def save(self, save_func): 
     
    6784        NamedItem.on_subject_notify(self, pspec, notifiers) 
    6885        if self.subject: 
    69             factory = UML 
    7086            if not self.subject.upperBound: 
    71                 self.subject.upperBound = factory.create(UML.LiteralSpecification) 
     87                self.subject.upperBound = UML.create(UML.LiteralSpecification) 
    7288                self.subject.upperBound.value = '*' 
    73             self._upper_bound.subject = self.subject.upperBound 
    74         else: 
    75             self._upper_bound.subject = None 
     89            #self._upper_bound.subject = self.subject.upperBound 
     90        #else: 
     91        #    self._upper_bound.subject = None 
    7692        self.request_update() 
    7793 
    78  
    79     def do_set_property(self, pspec, value): 
    80         """ 
    81         Request update of item in case of ordering visibility. 
    82         """ 
    83         if pspec.name == 'show-ordering': 
    84             self.preserve_property(pspec.name) 
    85             self._show_ordering = value 
    86             self.request_update() 
    87         else: 
    88             NamedItem.do_set_property(self, pspec, value) 
    89  
    90  
    91     def do_get_property(self, pspec): 
    92         if pspec.name == 'show-ordering': 
    93             return self._show_ordering 
    94         else: 
    95             return NamedItem.do_get_property(self, pspec) 
    96  
    97  
    98     def _set_ordering(self, ordering): 
    99         """ 
    100         Set ordering of object node. 
    101         """ 
    102         self.subject.ordering = ordering 
    103         self.request_update() 
    104  
    105  
    106     ordering = property(lambda s: s.subject.ordering, _set_ordering) 
    107  
    108     def update(self, context): 
     94    def pre_update(self, context): 
    10995        """ 
    11096        Update object node, its ordering and upper bound specification. 
     
    11298        NamedItem.update(self, context) 
    11399 
    114         if self.subject: 
    115             self._ordering.set_text('{ ordering = %s }' % self.subject.ordering) 
    116         else: 
    117             self._ordering.set_text('') 
     100        # TODO: format tag properly: 
     101        if self.subject.upperBound: 
     102            self._tag = '{ upperBound = %s }' % self.subject.upperBound.value 
    118103 
    119         #  
    120         # object ordering 
    121         # 
    122         if self.props.show_ordering: 
    123             # center ordering below border 
    124             ord_width, ord_height = self._ordering.to_pango_layout(True).get_pixel_size() 
    125             x = (self.width - ord_width) / 2 
    126             self._ordering.set_pos((x, self.height + self.style.margin[0])) 
     104        self._tag += '{ ordering = %s }' % self.subject.ordering 
    127105 
    128             self._ordering.set_max_width(ord_width) 
    129             self._ordering.set_max_height(ord_height) 
    130  
    131             self.set_bounds((min(0, x), 0, 
    132                 max(self.width, ord_width), self.height + self.style.margin[0] + ord_height)) 
    133         else: 
    134             ord_width, ord_height = 0, 0 
    135  
    136         # 
    137         # upper bound 
    138         # 
    139         ub_width, ub_height = self._upper_bound.get_size() 
    140         x = (self.width - ub_width) / 2 
    141         y = self.height + ord_height + self.style.margin[0] 
    142         self._upper_bound.update_label(x, y) 
    143  
    144  
     106        w, h = text_extents(self._tag) 
     107        x = (self.width - w) / 2 
     108        y = self.height + self.style.margin[0] 
     109        self._tag_bounds = Rectangle(x, y, width=w, height=h) 
    145110 
    146111    def draw(self, context): 
     112        cr = context.cairo 
     113        cr.rectangle(0, 0, self.width, self.height) 
     114        cr.stroke() 
     115 
    147116        super(ObjectNodeItem, self).draw(context) 
    148117 
    149     def on_shape_iter(self): 
    150         it = NamedItem.on_shape_iter(self) 
    151         if self.props.show_ordering: 
    152             return itertools.chain(it, iter([self._ordering])) 
    153         else: 
    154             return it 
     118        if self._tag: 
     119            text_multiline(cr, self._tag_bounds[0], self._tag_bounds[1], self._tag) 
     120        if context.hovered or context.focused or context.draw_all: 
     121            cr.set_line_width(0.5) 
     122            b = self._tag_bounds 
     123            cr.rectangle(b.x0, b.y0, b.width, b.height) 
     124            cr.stroke() 
    155125 
    156126 
  • gaphor/branches/new-canvas/utils/genUML2.py

    r1023 r1100  
    284284        tag = tag.value 
    285285 
    286     print 'scanning tags: %s' % tag 
     286    #print 'scanning tags: %s' % tag 
    287287 
    288288    if tag and tag.find('subsets') != -1: