Changeset 1115

Show
Ignore:
Timestamp:
01/10/07 05:13:54 (2 years ago)
Author:
arjanmol
Message:
  • updated stereotype drawing in ClassItem? (stereotype was substituted by 'metaclass')
  • formalized label drawing on lines
  • fixed name display for FlowItem?
  • separated Stereotype code in StereotypeSupport? class
Files:

Legend:

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

    r1104 r1115  
    66 - join/fork node text editing 
    77 - text placement for action nodes. 
    8  - implement ObjectNode and flow items 
    9  - create a generic method to swap class types 
    108 
    119For 0.10.0 and later: 
  • gaphor/branches/new-canvas/gaphor/adapters/connectors.py

    r1113 r1115  
    731731                   or (end2.type is head_type and end1.type is tail_type): 
    732732                    return 
    733                      
     733              
     734            # TODO: make element at head end update! 
     735 
    734736            # Find all associations and determine if the properties on 
    735737            # the association ends have a type that points to the class. 
  • gaphor/branches/new-canvas/gaphor/diagram/association.py

    r1114 r1115  
    187187        self.request_update() 
    188188 
    189     def update_label(self, context, p1, p2): 
    190         """Update the name label near the middle of the association. 
    191         """ 
    192         cr = context.cairo 
    193         w, h = text_extents(cr, self.subject and self.subject.name) 
    194  
    195         x = p1[0] > p2[0] and w + 2 or -2 
    196         x = (p1[0] + p2[0]) / 2.0 - x 
    197         y = p1[1] <= p2[1] and h or 0 
    198         y = (p1[1] + p2[1]) / 2.0 - y 
    199  
    200         #log.debug('label pos = (%d, %d)' % (x, y)) 
    201         #return x, y, max(x + 10, x + w), max(y + 10, y + h) 
    202         return x, y, x + w, y + h 
    203  
    204  
    205189    def update(self, context): 
    206190        """ 
     
    261245         
    262246        # update name label: 
    263         middle = len(handles)/2 
    264         self._label_bounds = self.update_label(context, handles[middle-1].pos, 
    265                                                handles[middle].pos) 
     247        self._label_bounds = self.update_label(context, self.subject and self.subject.name) 
    266248 
    267249 
  • gaphor/branches/new-canvas/gaphor/diagram/diagramitem.py

    r1111 r1115  
    147147 
    148148 
    149 class DiagramItem(SubjectSupport): 
    150     """ 
    151     Basic functionality for all model elements (lines and elements!). 
    152  
    153     This class contains common functionallity for model elements and 
    154     relationships. 
    155     It provides an interface similar to UML.Element for connecting and 
    156     disconnecting signals. 
    157  
    158     This class is not very useful on its own. It contains some glue-code for 
    159     diacanvas.DiaCanvasItem and gaphor.UML.Element. 
    160  
    161     Example: 
    162         class ElementItem(diacanvas.CanvasElement, DiagramItem): 
    163             connect = DiagramItem.connect 
    164             disconnect = DiagramItem.disconnect 
    165             ... 
    166  
    167     @cvar style: styles information (derived from DiagramItemMeta) 
    168     """ 
    169  
    170     __metaclass__ = DiagramItemMeta 
    171  
    172     stereotype_list = [] 
    173     popup_menu = ('Stereotype', stereotype_list) 
    174  
    175     def __init__(self, id=None): 
    176         SubjectSupport.__init__(self) 
    177         self._id = id # or uniqueid.generate_id() 
    178  
    179         # properties, which should be saved in file 
    180         self._persistent_props = set() 
    181  
    182         # stereotype 
     149class StereotypeSupport(object): 
     150    """ 
     151    Support methods for stereotypes. 
     152    """ 
     153 
     154    def __init__(self): 
    183155        self._stereotype = None 
    184  
    185     id = property(lambda self: self._id, doc='Id') 
    186  
    187     def set_prop_persistent(self, name): 
    188         """Specify property of diagram item, which should be saved in file. 
    189         """ 
    190         self._persistent_props.add(name) 
    191  
    192  
    193     # UML.Element interface used by properties: 
    194  
    195     # TODO: Use adapters for load/save functionality 
    196     def save(self, save_func): 
    197         if self.subject: 
    198             save_func('subject', self.subject) 
    199  
    200         # save persistent properties 
    201         for p in self._persistent_props: 
    202             save_func(p, getattr(self, p.replace('-', '_')), reference=True) 
    203  
    204  
    205     def load(self, name, value): 
    206         if name == 'subject': 
    207             type(self).subject.load(self, value) 
    208         else: 
    209             #log.debug('Setting unknown property "%s" -> "%s"' % (name, value)) 
    210             try: 
    211                 setattr(self, name.replace('-', '_'), eval(value)) 
    212             except: 
    213                 log.warning('%s has no property named %s (value %s)' % (self, name, value)) 
    214  
    215     def postload(self): 
    216         if self.subject: 
    217             self.on_subject_notify(type(self).subject) 
    218  
    219     def save_property(self, save_func, name): 
    220         """Save a property, this is a shorthand method. 
    221         """ 
    222         save_func(name, getattr(self, name.replace('-', '_'))) 
    223  
    224     def save_properties(self, save_func, *names): 
    225         """Save a property, this is a shorthand method. 
    226         """ 
    227         for name in names: 
    228             self.save_property(save_func, name) 
    229  
    230     def unlink(self): 
    231         """ 
    232         Remove the item from the canvas and set subject to None. 
    233         """ 
    234         if self.canvas: 
    235             self.canvas.remove(self) 
    236         self.subject = None 
    237         super(DiagramItem, self).unlink() 
    238  
    239     def get_popup_menu(self): 
    240         """In the popup menu a submenu is created with Stereotypes than can be 
    241         applied to this classifier (Class, Interface). 
    242         If the class itself is a metaclass, an option is added to check if the class 
    243         exists. 
    244         """ 
    245         subject = self.subject 
    246         stereotype_list = self.stereotype_list 
    247         stereotype_list[:] = [] 
    248  
    249         # UML specs does not allow to extend stereotypes with stereotypes 
    250         if subject and not isinstance(subject, UML.Stereotype): 
    251             # look for stereotypes to put them into context menu of an item 
    252             # this can be only done when subject exists 
    253  
    254             from gaphor.actions.itemactions import ApplyStereotypeAction, register_action 
    255  
    256             cls = type(subject) 
    257  
    258             # find out names of classes, which are superclasses of our 
    259             # subject 
    260             names = set(c.__name__ for c in cls.__mro__ if issubclass(c, Element)) 
    261  
    262             # find stereotypes that extend out metaclass 
    263             classes = subject._factory.select(lambda e: e.isKindOf(UML.Class) and e.name in names) 
    264  
    265             for class_ in classes: 
    266                 for extension in class_.extension: 
    267                     stereotype = extension.ownedEnd.type 
    268                     stereotype_action = ApplyStereotypeAction(stereotype) 
    269                     register_action(stereotype_action, 'ItemFocus') 
    270                     stereotype_list.append(stereotype_action.id) 
    271         return self.popup_menu 
    272  
    273  
    274     def on_subject_notify__appliedStereotype(self, subject, pspec=None): 
    275         if self.subject: 
    276             self.update_stereotype() 
    277  
    278     def request_update(self): 
    279         pass 
    280  
    281     def on_subject_notify(self, pspec, notifiers=()): 
    282         SubjectSupport.on_subject_notify(self, pspec, notifiers + ('appliedStereotype',)) 
    283         if self.subject: 
    284             self.update_stereotype() 
    285  
    286     # 
    287     # Stereotypes 
    288     # 
    289     def get_stereotype(self): 
    290         if self._stereotype: 
    291             return self._stereotype 
    292156 
    293157    def set_stereotype(self, text=None): 
     
    305169        self.request_update() 
    306170 
    307     stereotype = property(get_stereotype, set_stereotype) 
     171    stereotype = property(lambda s: s._stereotype, set_stereotype) 
    308172 
    309173    def update_stereotype(self): 
     
    346210        # Phew! :] 
    347211        self.set_stereotype(stereotype) 
    348  
    349     # 
    350     # utility methods 
    351     # 
    352212 
    353213    def parse_stereotype(self, data): 
     
    371231            ok = True 
    372232            if cls: 
    373                 ok = isinstance(subject, cls) 
     233                ok = type(subject) is cls #isinstance(subject, cls) 
    374234            if predicate: 
    375235                ok = predicate(self) 
     
    378238                return stereotype 
    379239        return None 
     240 
     241 
     242class DiagramItem(SubjectSupport, StereotypeSupport): 
     243    """ 
     244    Basic functionality for all model elements (lines and elements!). 
     245 
     246    This class contains common functionallity for model elements and 
     247    relationships. 
     248    It provides an interface similar to UML.Element for connecting and 
     249    disconnecting signals. 
     250 
     251    This class is not very useful on its own. It contains some glue-code for 
     252    diacanvas.DiaCanvasItem and gaphor.UML.Element. 
     253 
     254    Example: 
     255        class ElementItem(diacanvas.CanvasElement, DiagramItem): 
     256            connect = DiagramItem.connect 
     257            disconnect = DiagramItem.disconnect 
     258            ... 
     259 
     260    @cvar style: styles information (derived from DiagramItemMeta) 
     261    """ 
     262 
     263    __metaclass__ = DiagramItemMeta 
     264 
     265    stereotype_list = [] 
     266    popup_menu = ('Stereotype', stereotype_list) 
     267 
     268    def __init__(self, id=None): 
     269        SubjectSupport.__init__(self) 
     270        StereotypeSupport.__init__(self) 
     271 
     272        self._id = id 
     273 
     274        # properties, which should be saved in file 
     275        self._persistent_props = set() 
     276 
     277    id = property(lambda self: self._id, doc='Id') 
     278 
     279    def set_prop_persistent(self, name): 
     280        """Specify property of diagram item, which should be saved in file. 
     281        """ 
     282        self._persistent_props.add(name) 
     283 
     284 
     285    # UML.Element interface used by properties: 
     286 
     287    # TODO: Use adapters for load/save functionality 
     288    def save(self, save_func): 
     289        if self.subject: 
     290            save_func('subject', self.subject) 
     291 
     292        # save persistent properties 
     293        for p in self._persistent_props: 
     294            save_func(p, getattr(self, p.replace('-', '_')), reference=True) 
     295 
     296 
     297    def load(self, name, value): 
     298        if name == 'subject': 
     299            type(self).subject.load(self, value) 
     300        else: 
     301            #log.debug('Setting unknown property "%s" -> "%s"' % (name, value)) 
     302            try: 
     303                setattr(self, name.replace('-', '_'), eval(value)) 
     304            except: 
     305                log.warning('%s has no property named %s (value %s)' % (self, name, value)) 
     306 
     307    def postload(self): 
     308        if self.subject: 
     309            self.on_subject_notify(type(self).subject) 
     310 
     311    def save_property(self, save_func, name): 
     312        """Save a property, this is a shorthand method. 
     313        """ 
     314        save_func(name, getattr(self, name.replace('-', '_'))) 
     315 
     316    def save_properties(self, save_func, *names): 
     317        """Save a property, this is a shorthand method. 
     318        """ 
     319        for name in names: 
     320            self.save_property(save_func, name) 
     321 
     322    def unlink(self): 
     323        """ 
     324        Remove the item from the canvas and set subject to None. 
     325        """ 
     326        if self.canvas: 
     327            self.canvas.remove(self) 
     328        self.subject = None 
     329        super(DiagramItem, self).unlink() 
     330 
     331    def get_popup_menu(self): 
     332        """In the popup menu a submenu is created with Stereotypes than can be 
     333        applied to this classifier (Class, Interface). 
     334        If the class itself is a metaclass, an option is added to check if the class 
     335        exists. 
     336        """ 
     337        subject = self.subject 
     338        stereotype_list = self.stereotype_list 
     339        stereotype_list[:] = [] 
     340 
     341        # UML specs does not allow to extend stereotypes with stereotypes 
     342        if subject and not isinstance(subject, UML.Stereotype): 
     343            # look for stereotypes to put them into context menu of an item 
     344            # this can be only done when subject exists 
     345 
     346            from gaphor.actions.itemactions import ApplyStereotypeAction, register_action 
     347 
     348            cls = type(subject) 
     349 
     350            # find out names of classes, which are superclasses of our 
     351            # subject 
     352            names = set(c.__name__ for c in cls.__mro__ if issubclass(c, Element)) 
     353 
     354            # find stereotypes that extend out metaclass 
     355            classes = subject._factory.select(lambda e: e.isKindOf(UML.Class) and e.name in names) 
     356 
     357            for class_ in classes: 
     358                for extension in class_.extension: 
     359                    stereotype = extension.ownedEnd.type 
     360                    stereotype_action = ApplyStereotypeAction(stereotype) 
     361                    register_action(stereotype_action, 'ItemFocus') 
     362                    stereotype_list.append(stereotype_action.id) 
     363        return self.popup_menu 
     364 
     365 
     366    def on_subject_notify__appliedStereotype(self, subject, pspec=None): 
     367        if self.subject: 
     368            self.request_update() 
     369 
     370    def request_update(self): 
     371        """Placeholder for gaphor.Item's request_update() method. 
     372        """ 
     373        pass 
     374 
     375    def on_subject_notify(self, pspec, notifiers=()): 
     376        SubjectSupport.on_subject_notify(self, pspec, notifiers + ('appliedStereotype',)) 
     377 
     378# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/diagram/diagramline.py

    r1093 r1115  
    77import gaphas 
    88from gaphas.util import text_extents 
     9from gaphas.geometry import Rectangle 
    910from diagramitem import DiagramItem 
    1011from interfaces import IConnect 
     
    2122        DiagramItem.__init__(self, id) 
    2223        self.fuzzyness = 2 
    23         self._stereotype_pos = (0, 0) 
    24         self._stereotype_width = 0 
    25         self._stereotype_height = 0 
     24        self._stereotype_bounds = None 
    2625 
    2726    head = property(lambda self: self._handles[0]) 
    2827    tail = property(lambda self: self._handles[-1]) 
    2928 
    30     def update(self, context): 
    31         super(LineItem, self).update(context) 
     29    def update_label(self, context, text): 
     30        """ 
     31        Update the name label near the middle of the association. 
     32 
     33        Returns a geometry.Rectangle object with the boundig box for the label text. 
     34        """ 
    3235        cr = context.cairo 
    33  
    34         # update stereotype 
    35         self.update_stereotype() 
    36  
    37         sw, sh = text_extents(cr, self._stereotype) 
     36        handles = self._handles 
     37        middle = len(handles)/2 
     38        p1 = handles[middle].pos 
     39        p2 = handles[middle-1].pos 
     40 
     41        w, h = text_extents(cr, text) 
     42 
     43        x = p1[0] > p2[0] and w + 5 or -2 
     44        x = (p1[0] + p2[0]) / 2.0 - x 
     45        y = p1[1] <= p2[1] and h + 2 or 0 
     46        y = (p1[1] + p2[1]) / 2.0 - y 
     47 
     48        #log.debug('label pos = (%d, %d)' % (x, y)) 
     49        #return x, y, max(x + 10, x + w), max(y + 10, y + h) 
     50        return Rectangle(x, y, x + max(10, w), y + max(10, h)) 
     51 
     52    def update_stereotype(self, context): 
     53        super(LineItem, self).update_stereotype() 
     54 
     55        sw, sh = text_extents(context.cairo, self._stereotype) 
    3856 
    3957        handles = self._handles 
     
    4765        y = (p1[1] + p2[1]) / 2.0 - y 
    4866 
    49         self._stereotype_pos = (x, y) 
    50         self._stereotype_width = sw 
    51         self._stereotype_height = sh 
     67        self._stereotype_bounds = Rectangle(x, y, width=sw, height=sh) 
     68 
     69    def update(self, context): 
     70        super(LineItem, self).update(context) 
     71        cr = context.cairo 
     72 
     73        # update stereotype 
     74        self.update_stereotype(context) 
    5275 
    5376    def draw(self, context): 
     
    5578        cr = context.cairo 
    5679        if self._stereotype: 
    57             cr.move_to(*self._stereotype_pos
     80            cr.move_to(self._stereotype_bounds[0], self._stereotype_bounds[1]
    5881            cr.show_text(self._stereotype) 
    5982 
  • gaphor/branches/new-canvas/gaphor/diagram/elementitem.py

    r1071 r1115  
    3636            DiagramItem.load(self, name, value) 
    3737 
     38    def pre_update(self, context): 
     39        super(ElementItem, self).pre_update(context) 
     40        self.update_stereotype() 
    3841 
    3942# vim:sw=4 
  • gaphor/branches/new-canvas/gaphor/diagram/flow.py

    r1114 r1115  
    6060 
    6161    def update_name(self, context): 
     62        cr = context.cairo 
     63        ofs = 5 
     64 
    6265        handles = self._handles 
    63  
    64         def get_pos(p1, p2, width, height): 
    65             x = p1[0] > p2[0] and -10 or width + 10 
    66             x = p2[0] - x 
    67             y = p1[1] <= p2[1] and height + 5 or -15 
    68             y = p2[1] - y 
    69             return x, y 
    70  
    71         p1 = handles[-2].pos 
    72         p2 = handles[-1].pos 
    73         w, h = text_extents(context.cairo, self.subject and self.subject.name) 
    74         x, y = get_pos(p1, p2, w, h) 
    75         self._name_bounds = Rectangle(x, y, width=max(10, w), height=max(10, h)) 
    76  
    77  
    78     def update_guard(self, context): 
    79         handles = self._handles 
    80         middle = len(handles)/2 
    81  
    82         def get_pos_centered(p1, p2, width, height): 
    83             x = p1[0] > p2[0] and width + 2 or -2 
    84             x = (p1[0] + p2[0]) / 2.0 - x 
    85             y = p1[1] <= p2[1] and height or 0 
    86             y = (p1[1] + p2[1]) / 2.0 - y 
    87             return x, y 
    88  
    89         p1 = handles[middle-1].pos 
    90         p2 = handles[middle].pos 
    91         w, h = text_extents(context.cairo, self.subject and self.subject.guard and self.subject.guard.value) 
    92         x, y = get_pos_centered(p1, p2, w, h) 
    93         self._guard_bounds = Rectangle(x, y, width=max(10, w), height=max(10, h)) 
    94  
     66        p1 = handles[-1].pos 
     67        p2 = handles[-2].pos 
     68 
     69        name_w, name_h = map(max, text_extents(cr, self.subject and self.subject.name, multiline=True), (10, 10)) 
     70 
     71        name_dx = 0.0 
     72        name_dy = 0.0 
     73 
     74        dx = float(p2[0]) - float(p1[0]) 
     75        dy = float(p2[1]) - float(p1[1]) 
     76         
     77        if dy == 0: 
     78            rc = 1000.0 # quite a lot... 
     79        else: 
     80            rc = dx / dy 
     81        abs_rc = abs(rc) 
     82        h = dx > 0 # right side of the box 
     83        v = dy > 0 # bottom side 
     84 
     85        if abs_rc > 6: 
     86            # horizontal line 
     87            if h: 
     88                name_dx = ofs 
     89                name_dy = -ofs - name_h 
     90            else: 
     91                name_dx = -ofs - name_w 
     92                name_dy = -ofs - name_h 
     93        elif 0 <= abs_rc <= 0.2: 
     94            # vertical line 
     95            if v: 
     96                name_dx = -ofs - name_w 
     97                name_dy = ofs 
     98            else: 
     99                name_dx = -ofs - name_w 
     100                name_dy = -ofs - name_h 
     101        else: 
     102            # Should both items be placed on the same side of the line? 
     103            r = abs_rc < 1.0 
     104 
     105            # Find out alignment of text (depends on the direction of the line) 
     106            align_left = (h and not r) or (r and not h) 
     107            align_bottom = (v and not r) or (r and not v) 
     108            if align_left: 
     109                name_dx = ofs 
     110            else: 
     111                name_dx = -ofs - name_w 
     112            if align_bottom: 
     113                name_dy = -ofs - name_h 
     114            else: 
     115                name_dy = ofs  
     116 
     117        self._name_bounds = Rectangle(p1[0] + name_dx, 
     118                                      p1[1] + name_dy, 
     119                                      width=name_w, 
     120                                      height=name_h) 
    95121 
    96122    def update(self, context): 
    97123        super(FlowItem, self).update(context) 
    98124        self.update_name(context) 
    99         self.update_guard(context) 
     125        # update guard label: 
     126        self._guard_bounds = self.update_label(context, self.subject and self.subject.guard and self.subject.guard.value) 
    100127 
    101128    def point(self, x, y): 
  • gaphor/branches/new-canvas/gaphor/diagram/klass.py

    r1023 r1115  
    2626    __stereotype__ = { 
    2727        'stereotype': UML.Stereotype, 
    28          'metaclass': (UML.Class, lambda self: hasattr(self.subject, 'extension') and self.subject.extension)
     28         'metaclass': lambda self: (not isinstance(self.subject, UML.Stereotype)) and hasattr(self.subject, 'extension') and self.subject.extension
    2929    } 
    3030