| 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 |
|---|
| | 149 | class StereotypeSupport(object): |
|---|
| | 150 | """ |
|---|
| | 151 | Support methods for stereotypes. |
|---|
| | 152 | """ |
|---|
| | 153 | |
|---|
| | 154 | def __init__(self): |
|---|
| 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 |
|---|
| | 240 | |
|---|
| | 241 | |
|---|
| | 242 | class 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 |
|---|