Changeset 1081

Show
Ignore:
Timestamp:
11/21/06 06:01:18 (2 years ago)
Author:
arjanmol
Message:

Added connector for CommentLine?-DiagramLines?.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphor/branches/new-canvas/gaphor/adapters/connectors.py

    r1080 r1081  
    1313 
    1414 
    15 class SimpleConnect(object): 
     15class AbstractConnect(object): 
     16    """ 
     17    Connection code, used by both ElementConnect and LineConnect. 
     18    """ 
    1619    interface.implements(IConnect) 
    1720 
     
    2023        self.line = line 
    2124 
     25    def glue(self, handle, x, y): 
     26        """ 
     27        Return the point the handle could connect to. None if no connection 
     28        is allowed. 
     29        """ 
     30        raise NotImplemented, 'Implement glue() in the subclass' 
     31 
     32    def connect(self, handle, x, y): 
     33        """ 
     34        Connect to an element. Note that at this point the line may 
     35        be connected to some other, or the same element by means of the 
     36        handle.connected_to property. Also the connection at UML level 
     37        still exists. 
     38         
     39        Returns True if a connection is established. 
     40        """ 
     41        element = self.element 
     42        canvas = element.canvas 
     43        solver = canvas.solver 
     44 
     45        pos = self.glue(handle, x, y) 
     46 
     47        # Disconnect old model connection 
     48        if handle.connected_to and handle.connected_to is not self.element: 
     49            handle.disconnect() 
     50  
     51        # Stop here if no new connection should be established 
     52        if not pos: 
     53            return False 
     54         
     55        self.connect_constraint(handle, pos[0], pos[1]) 
     56 
     57        # Set disconnect handler in the adapter, so it will also wotk if 
     58        # connections are created programmatically. 
     59        def _disconnect(): 
     60            self.disconnect(handle) 
     61            handle.disconnect = lambda: 0 
     62        handle.disconnect = _disconnect 
     63 
     64        return True 
     65 
     66    def disconnect(self, handle): 
     67        """ 
     68        Do a full disconnect, also disconnect at UML model level. 
     69        Subclasses should disconnect model-level connections. 
     70        """ 
     71        self.disconnect_constraints(handle) 
     72        handle.connected_to = None 
     73 
     74    def connect_constraint(self, handle, x, y): 
     75        """ 
     76        Create the actual constraint. The handle should be positioned before 
     77        this method is called. 
     78        x, y is the position the handle should have on the line. 
     79        """ 
     80        raise NotImplemented, 'Implement connect_constraint() in a subclass' 
     81 
     82    def disconnect_constraints(self, handle): 
     83        """ 
     84        Disconnect() takes care of disconnecting the handle from the 
     85        element it's attached to, by removing the constraints. 
     86        """ 
     87        solver = self.element.canvas.solver 
     88        try: 
     89            solver.remove_constraint(handle._connect_constraint) 
     90        except AttributeError: 
     91            pass # No _connect_constraint property yet 
     92        handle._connect_constraint = None 
     93 
     94 
     95class ElementConnect(AbstractConnect): 
     96    """ 
     97    Base class for connecting a line to an ElementItem class. 
     98    """ 
     99     
    22100    def side(self, handle_pos, glued): 
    23101        """ 
     
    47125        return geometry.point_on_rectangle(bounds, (x, y), border=True) 
    48126 
    49     def connect(self, handle, x, y): 
    50         """ 
    51         Connect to an element. Note that at this point the line may 
    52         be connected to some other, or the same element by means of the 
    53         handle.connected_to property. Also the connection at UML level 
    54         still exists. 
    55          
    56         Returns True if a connection is established. 
     127    def connect_constraint(self, handle, x, y): 
     128        """ 
     129        Create the actual constraint. The handle should be positioned before 
     130        this method is called. 
    57131        """ 
    58132        element = self.element 
    59133        canvas = element.canvas 
    60134        solver = canvas.solver 
    61         pos = self.glue(handle, x, y) 
    62  
    63         # Disconnect old model connection 
    64         if handle.connected_to and handle.connected_to is not self.element: 
    65             #adapter = component.queryMultiAdapter((handle.connected_to, self.line), IConnect) 
    66             #adapter.disconnect(handle) 
    67             #print 'disconnect is', handle.disconnect 
    68             handle.disconnect() 
    69   
    70         # Stop here if no new connection should be established 
    71         if not pos: 
    72             return False 
    73  
    74         s = self.side(pos, element) 
     135 
     136        s = self.side((x, y), element) 
    75137        handle._connect_constraint = \ 
    76138            constraint.LineConstraint(canvas, element, element.handles()[s], 
     
    78140        solver.add_constraint(handle._connect_constraint) 
    79141        handle.connected_to = element 
    80          
    81         # Set disconnect handler in the adapter, so it will also wotk if 
    82         # connections are created programmatically. 
    83         def _disconnect(): 
    84             self.disconnect(handle) 
    85             handle.disconnect = lambda: 0 
    86         handle.disconnect = _disconnect 
    87  
    88         return True 
    89  
    90     def disconnect_constraints(self, handle): 
    91         """ 
    92         Disconnect() takes care of disconnecting the handle from the 
    93         element it's attached to, by removing the constraints. 
    94         """ 
    95         solver = self.element.canvas.solver 
    96         try: 
    97             solver.remove_constraint(handle._connect_constraint) 
    98         except AttributeError: 
    99             pass # No _connect_constraint property yet 
    100         handle._connect_constraint = None 
    101  
    102     def disconnect(self, handle): 
    103         """ 
    104         Do a full disconnect, also disconnect at UML model level. 
    105         Subclasses should disconnect model-level connections. 
    106         """ 
    107         self.disconnect_constraints(handle) 
    108         handle.connected_to = None 
    109  
    110  
    111 class CommentLineConnect(SimpleConnect): 
     142 
     143 
     144class LineConnect(AbstractConnect): 
     145    """ 
     146    Base class for connecting two lines to each other. 
     147    The line that is conencted to is called 'element', as in ElementConnect. 
     148    """ 
     149 
     150    def _glue(self, handle, x, y): 
     151        """ 
     152        Return the point on the element (DiagramLine) closest to (x, y) 
     153        """ 
     154        h = self.line.handles() 
     155        pos = (x, y) 
     156        h0 = h[0] 
     157        min_d = None 
     158        segment = -1 
     159        min_p = None 
     160        dlp = geometry.distance_line_point 
     161        for s, h1 in enumerate(h[1:]): 
     162            d, p = dlp(h0.pos, h1.pos, pos) 
     163            if not s or d < min_d: 
     164                min_d = d 
     165                segment = s 
     166                min_p = p 
     167        return s, min_p 
     168 
     169    def glue(self, handle, x, y): 
     170        """ 
     171        Return the point on the element (DiagramLine) closest to (x, y) 
     172        """ 
     173        return self._glue(handle, x, y)[1] 
     174 
     175    def connect_constraint(self, handle, x, y): 
     176        """ 
     177        Create the actual constraint. The handle should be positioned before 
     178        this method is called. 
     179        """ 
     180        element = self.element 
     181        canvas = element.canvas 
     182        solver = canvas.solver 
     183 
     184        s, pos = self._glue(handle, x, y) 
     185        handle._connect_constraint = \ 
     186            constraint.LineConstraint(canvas, element, element.handles()[s], 
     187                                element.handles()[s+1], self.line, handle) 
     188        solver.add_constraint(handle._connect_constraint) 
     189        handle.connected_to = element 
     190 
     191 
     192class CommentLineElementConnect(ElementConnect): 
    112193    """ 
    113194    Connect a comment line to a comment item. 
     
    145226            return None 
    146227 
    147         return super(CommentLineConnect, self).glue(handle, x, y) 
    148  
    149     def connect(self, handle, x, y): 
    150         if super(CommentLineConnect, self).connect(handle, x, y): 
     228        return super(CommentLineElementConnect, self).glue(handle, x, y) 
     229 
     230    def connect(self, handle, x, y): 
     231        if super(CommentLineElementConnect, self).connect(handle, x, y): 
    151232            opposite = self.line.opposite(handle) 
    152233            if opposite.connected_to: 
     
    163244            else: 
    164245                del handle.connected_to.subject.annotatedElement[opposite.connected_to.subject] 
    165         super(CommentLineConnect, self).disconnect(handle) 
    166  
    167 component.provideAdapter(CommentLineConnect) 
    168  
    169  
    170 class RelationshipConnect(SimpleConnect): 
     246        super(CommentLineElementConnect, self).disconnect(handle) 
     247 
     248component.provideAdapter(CommentLineElementConnect) 
     249 
     250class CommentLineLineConnect(LineConnect): 
     251    """ 
     252    Connect a comment line to a comment item. 
     253    Connect Comment.annotatedElement to any element 
     254     
     255    TODO: adapt both ElementItem and DiagramLine 
     256    use component.provideAdapter? 
     257    """ 
     258    component.adapts(items.DiagramLine, items.CommentLineItem) 
     259 
     260    def glue(self, handle, x, y): 
     261        """ 
     262        In addition to the normal check, both line ends may not be connected 
     263        to the same element. Same goes for subjects. 
     264        One of the ends should be connected to a UML.Comment element. 
     265        """ 
     266        opposite = self.line.opposite(handle) 
     267        element = self.element 
     268        connected_to = opposite.connected_to 
     269        if connected_to is element: 
     270            #print 'item identical', connected_to, element 
     271            return None 
     272 
     273        # Same goes for subjects: 
     274        if connected_to and \ 
     275                (not (connected_to.subject or element.subject)) \ 
     276                 and connected_to.subject is element.subject: 
     277            #print 'Subjects none or match:', connected_to.subject, element.subject 
     278            return None 
     279 
     280        # One end should be connected to a CommentItem: 
     281        if connected_to and \ 
     282                ((isinstance(connected_to, items.CommentItem) and isinstance(self.element, items.CommentItem)) or \ 
     283                 (not isinstance(connected_to, items.CommentItem) and not isinstance(self.element, items.CommentItem))): 
     284            return None 
     285 
     286        return super(CommentLineLineConnect, self).glue(handle, x, y) 
     287 
     288    def connect(self, handle, x, y): 
     289        if super(CommentLineLineConnect, self).connect(handle, x, y): 
     290            opposite = self.line.opposite(handle) 
     291            if opposite.connected_to: 
     292                if isinstance(opposite.connected_to.subject, UML.Comment): 
     293                    opposite.connected_to.subject.annotatedElement = self.element.subject 
     294                else: 
     295                    self.element.subject.annotatedElement = opposite.connected_to.subject 
     296 
     297    def disconnect(self, handle): 
     298        opposite = self.line.opposite(handle) 
     299        if handle.connected_to and opposite.connected_to: 
     300            if isinstance(opposite.connected_to.subject, UML.Comment): 
     301                del opposite.connected_to.subject.annotatedElement[handle.connected_to.subject] 
     302            else: 
     303                del handle.connected_to.subject.annotatedElement[opposite.connected_to.subject] 
     304        super(CommentLineLineConnect, self).disconnect(handle) 
     305 
     306component.provideAdapter(CommentLineLineConnect) 
     307 
     308 
     309class RelationshipConnect(ElementConnect): 
    171310    """ 
    172311    Base class for relationship connections, such as Association, 
  • gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py

    r1028 r1081  
    1919        UML.flush() 
    2020 
    21     def test_commentline(self): 
    22         """Test CommentLineItem connecting to comment and Actor items. 
     21    def test_commentline_element(self): 
     22        """ 
     23        Test CommentLineItem connecting to comment and Actor items. 
    2324        """ 
    2425        diagram = UML.create(UML.Diagram) 
     
    8081        assert len(comment.subject.annotatedElement) == 0, comment.subject.annotatedElement 
    8182        assert not actor2.subject in comment.subject.annotatedElement, comment.subject.annotatedElement 
     83 
     84    def test_commentline_association(self): 
     85        """ 
     86        Test CommentLineItem with AssociationItem. 
     87        """ 
     88        diagram = UML.create(UML.Diagram) 
     89        comment = diagram.create(items.CommentItem, subject=UML.create(UML.Comment)) 
     90        line = diagram.create(items.CommentLineItem) 
     91        c1 = diagram.create(items.ClassItem, subject=UML.create(UML.Class)) 
     92        c2 = diagram.create(items.ClassItem, subject=UML.create(UML.Class)) 
     93        assoc = diagram.create(items.AssociationItem) 
     94 
     95        adapter = component.queryMultiAdapter((c1, assoc), IConnect) 
     96        handle = assoc.head 
     97        adapter.connect(handle, handle.x, handle.y) 
     98 
     99        adapter = component.queryMultiAdapter((c2, assoc), IConnect) 
     100        handle = assoc.tail 
     101        adapter.connect(handle, handle.x, handle.y) 
     102 
     103        assert assoc.head.connected_to is c1 
     104        assert assoc.tail.connected_to is c2 
     105        assert assoc.subject 
     106 
     107        # Connect the association item to the head of the line: 
     108 
     109        adapter = component.queryMultiAdapter((assoc, line), IConnect) 
     110        assert adapter 
     111        assert type(adapter) is gaphor.adapters.connectors.CommentLineLineConnect 
     112        handle = line.head 
     113        adapter.connect(handle, handle.x, handle.y) 
     114 
     115        assert handle.connected_to is assoc 
     116        assert handle._connect_constraint is not None 
     117        assert not comment.subject.annotatedElement 
     118 
     119        # Connecting two ends of the line to the same item is not allowed: 
     120 
     121        handle = line.tail 
     122        adapter.connect(handle, handle.x, handle.y) 
     123 
     124        assert handle.connected_to is None 
     125        assert not hasattr(handle,'_connect_constraint') 
     126        assert not comment.subject.annotatedElement, comment.subject.annotatedElement 
     127 
     128        # now connect the comment 
     129 
     130        adapter = component.queryMultiAdapter((comment, line), IConnect) 
     131 
     132        handle = line.tail 
     133        adapter.connect(handle, handle.x, handle.y) 
     134 
     135        assert handle.connected_to is comment 
     136        assert handle._connect_constraint is not None 
     137        assert len(comment.subject.annotatedElement) == 1, comment.subject.annotatedElement 
     138        assert assoc.subject in comment.subject.annotatedElement, comment.subject.annotatedElement 
     139 
     140        # Disconnect actor: 
     141 
     142        adapter.disconnect(handle) 
     143 
     144        assert handle.connected_to is None, handle.connected_to 
     145        assert handle._connect_constraint is None 
     146        assert len(comment.subject.annotatedElement) == 0, comment.subject.annotatedElement 
     147        assert not assoc.subject in comment.subject.annotatedElement, comment.subject.annotatedElement 
    82148 
    83149 
     
    298364 
    299365 
    300 #vi:sw=4:et:ai 
     366# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/diagram/commentline.py

    r972 r1081  
    1616        #diacanvas.CanvasLine.__init__(self) 
    1717        DiagramLine.__init__(self, id) 
    18         self.__notify_id = None 
    1918 
    2019    #id = property(lambda self: self._id, doc='Id') 
  • gaphor/branches/new-canvas/gaphor/diagram/items.py

    r1063 r1081  
    55 
    66# Base classes: 
     7from gaphor.diagram.diagramitem import DiagramItem 
     8from gaphor.diagram.diagramline import DiagramLine 
    79from gaphor.diagram.elementitem import ElementItem 
    810from gaphor.diagram.nameditem import NamedItem 
  • gaphor/branches/new-canvas/gaphor/ui/toolbox.py

    r1007 r1081  
    1 # vim:sw=4:et 
    2  
    3 """Toolbox. 
     1""" 
     2Toolbox. 
    43""" 
    54 
     
    109 
    1110class Toolbox(gtk.VBox): 
    12     """A toolbox is a widget that contains a set of buttons (a Wrapbox widget) 
     11    """ 
     12    A toolbox is a widget that contains a set of buttons (a Wrapbox widget) 
    1313    with a name above it. When the user clicks on the name the box's content 
    1414    shows/hides. 
     
    2626 
    2727    def __init__(self, menu_factory, toolboxdef): 
    28         """Create a new Toolbox instance. Wrapbox objects are generated 
     28        """ 
     29        Create a new Toolbox instance. Wrapbox objects are generated 
    2930        using the menu_factory and based on the toolboxdef definition. 
    3031        """ 
     
    3536 
    3637    def on_wrapbox_decorator_toggled(self, button, content): 
    37         """This function is called when the Wrapbox decorator is clicked. It 
     38        """ 
     39        This function is called when the Wrapbox decorator is clicked. It 
    3840        changes the visibility of the content and the arrow in front of the 
    3941        button label. 
     
    5456 
    5557    def old_make_wrapbox_decorator(self, title, content): 
    56         """Create a gtk.VBox with in the top compartment a label that can be 
     58        """ 
     59        Create a gtk.VBox with in the top compartment a label that can be 
    5760        clicked to show/hide the lower compartment. 
    5861        """ 
     
    9497 
    9598    def make_wrapbox_decorator(self, title, content): 
    96         """Create a gtk.VBox with in the top compartment a label that can be 
     99        """ 
     100        Create a gtk.VBox with in the top compartment a label that can be 
    97101        clicked to show/hide the lower compartment. 
    98102        """ 
     
    134138                self.boxes.append(wrapbox) 
    135139 
    136 gobject.type_register(Toolbox) 
    137  
     140# vim:sw=4:et:ai