| 110 | | # self._dir = diacanvas.shape.Path() |
|---|
| 111 | | # self._dir.set_line_width(2.0) |
|---|
| 112 | | # self._dir.line(((10, 0), (10, 10), (0, 5))) |
|---|
| 113 | | # self._dir.set_fill_color(diacanvas.color(0,0,0)) |
|---|
| 114 | | # self._dir.set_fill(diacanvas.shape.FILL_SOLID) |
|---|
| 115 | | # self._dir.set_cyclic(True) |
|---|
| 116 | | |
|---|
| 117 | | # self._head_xa = diacanvas.shape.Path() |
|---|
| 118 | | # self._head_xa.set_line_width(2.0) |
|---|
| 119 | | |
|---|
| 120 | | # self._head_xb = diacanvas.shape.Path() |
|---|
| 121 | | # self._head_xb.set_line_width(2.0) |
|---|
| 122 | | |
|---|
| 123 | | # self._tail_xa = diacanvas.shape.Path() |
|---|
| 124 | | # self._tail_xa.set_line_width(2.0) |
|---|
| 125 | | |
|---|
| 126 | | # self._tail_xb = diacanvas.shape.Path() |
|---|
| 127 | | # self._tail_xb.set_line_width(2.0) |
|---|
| | 100 | self._dir_angle = 0 |
|---|
| | 101 | self._dir_pos = 0, 0 |
|---|
| | 102 | |
|---|
| | 103 | def set_show_direction(self, dir): |
|---|
| | 104 | self._show_direction = dir |
|---|
| | 105 | self.request_update() |
|---|
| | 106 | |
|---|
| | 107 | show_direction = property(lambda s: s._show_direction, set_show_direction) |
|---|
| 249 | | |
|---|
| 250 | | def update_dir(self, p1, p2): |
|---|
| 251 | | """Create a small arrow near the middle of the association line and |
|---|
| 252 | | let it point in the direction of self.subject.memberEnd[0]. |
|---|
| 253 | | Keep in mind that self.subject.memberEnd[0].class_ points to the class |
|---|
| 254 | | *not* pointed to by the arrow. |
|---|
| 255 | | """ |
|---|
| 256 | | x = p1[0] < p2[0] and -8 or 8 |
|---|
| 257 | | y = p1[1] >= p2[1] and -8 or 8 |
|---|
| 258 | | x = (p1[0] + p2[0]) / 2.0 + x |
|---|
| 259 | | y = (p1[1] + p2[1]) / 2.0 + y |
|---|
| 260 | | |
|---|
| 261 | | try: |
|---|
| 262 | | angle = atan((p1[1] - p2[1]) / (p1[0] - p2[0])) #/ pi * 180.0 |
|---|
| 263 | | except ZeroDivisionError: |
|---|
| 264 | | angle = pi * 1.5 |
|---|
| 265 | | |
|---|
| 266 | | # Invert angle if member ends are inverted |
|---|
| 267 | | if self.subject.memberEnd[0] is self._tail_end.subject: |
|---|
| 268 | | angle += pi |
|---|
| 269 | | |
|---|
| 270 | | if p1[0] < p2[0]: |
|---|
| 271 | | angle += pi |
|---|
| 272 | | elif p1[0] == p2[0] and p1[1] > p2[1]: |
|---|
| 273 | | angle += pi |
|---|
| 274 | | #log.debug('rotation angle is %s' % (angle/pi * 180.0)) |
|---|
| 275 | | |
|---|
| 276 | | sin_angle = sin(angle) |
|---|
| 277 | | cos_angle = cos(angle) |
|---|
| 278 | | |
|---|
| 279 | | def r(a, b): |
|---|
| 280 | | return (cos_angle * a - sin_angle * b + x, \ |
|---|
| 281 | | sin_angle * a + cos_angle * b + y) |
|---|
| 282 | | |
|---|
| 283 | | # Create an arrow around (0, 0), so it can be easely rotated: |
|---|
| 284 | | self._dir.line((r(-6, 0), r(6, -5), r(6, 5))) |
|---|
| 285 | | self._dir.set_cyclic(True) |
|---|
| 286 | | |
|---|
| 287 | | return x, y, x + 12, y + 10 |
|---|
| 342 | | # if self._show_direction and self.subject and self.subject.memberEnd: |
|---|
| 343 | | # b0 = self.update_dir(handles[middle-1].pos, |
|---|
| 344 | | # handles[middle].pos) |
|---|
| 345 | | # else: |
|---|
| 346 | | # b0 = self.bounds |
|---|
| 347 | | |
|---|
| 348 | | # bounds calculation |
|---|
| 349 | | # b1 = self.bounds |
|---|
| 350 | | # b2 = self._head_end.get_bounds(self._head_end.affine) |
|---|
| 351 | | # b3 = self._tail_end.get_bounds(self._tail_end.affine) |
|---|
| 352 | | # bv = zip(self._label_bounds, b0, b1, b2, b3) |
|---|
| 353 | | # self.set_bounds((min(bv[0]), min(bv[1]), max(bv[2]), max(bv[3]))) |
|---|
| 354 | | |
|---|
| 355 | | # def on_shape_iter(self): |
|---|
| 356 | | # for s in DiagramLine.on_shape_iter(self): |
|---|
| 357 | | # yield s |
|---|
| 358 | | # yield self._label |
|---|
| 359 | | # if self._show_direction: |
|---|
| 360 | | # yield self._dir |
|---|
| 361 | | # |
|---|
| 362 | | # if self._head_end.subject and self._tail_end.subject: |
|---|
| 363 | | # if self._tail_end.subject.aggregation == intern('none') \ |
|---|
| 364 | | # and self._head_end.get_navigability() == False: |
|---|
| 365 | | # yield self._head_xa |
|---|
| 366 | | # yield self._head_xb |
|---|
| 367 | | # |
|---|
| 368 | | # if self._head_end.subject.aggregation == intern('none') \ |
|---|
| 369 | | # and self._tail_end.get_navigability() == False: |
|---|
| 370 | | # yield self._tail_xa |
|---|
| 371 | | # yield self._tail_xb |
|---|
| 470 | | # TODO: draw direction and association name |
|---|
| 471 | | |
|---|
| 472 | | |
|---|
| 473 | | # |
|---|
| 474 | | # Gaphor Connection Protocol |
|---|
| 475 | | # |
|---|
| 476 | | |
|---|
| 477 | | def allow_connect_handle(self, handle, connecting_to): |
|---|
| 478 | | """This method is called by a canvas item if the user tries to connect |
|---|
| 479 | | this object's handle. allow_connect_handle() checks if the line is |
|---|
| 480 | | allowed to be connected. In this case that means that one end of the |
|---|
| 481 | | line should be connected to a Classifier. |
|---|
| 482 | | Returns: TRUE if connection is allowed, FALSE otherwise. |
|---|
| 483 | | """ |
|---|
| 484 | | #log.debug('AssociationItem.allow_connect_handle') |
|---|
| 485 | | if isinstance(connecting_to.subject, UML.Classifier): |
|---|
| 486 | | return True |
|---|
| 487 | | return False |
|---|
| 488 | | |
|---|
| 489 | | def confirm_connect_handle(self, handle): |
|---|
| 490 | | """This method is called after a connection is established. This method |
|---|
| 491 | | sets the internal state of the line and updates the data model. |
|---|
| 492 | | """ |
|---|
| 493 | | #log.debug('AssociationItem.confirm_connect_handle') |
|---|
| 494 | | |
|---|
| 495 | | c1 = self.handles[0].connected_to |
|---|
| 496 | | c2 = self.handles[-1].connected_to |
|---|
| 497 | | if c1 and c2: |
|---|
| 498 | | head_type = c1.subject |
|---|
| 499 | | tail_type = c2.subject |
|---|
| 500 | | |
|---|
| 501 | | # First check if we do not already contain the right subject: |
|---|
| 502 | | if self.subject: |
|---|
| 503 | | end1 = self.subject.memberEnd[0] |
|---|
| 504 | | end2 = self.subject.memberEnd[1] |
|---|
| 505 | | if (end1.type is head_type and end2.type is tail_type) \ |
|---|
| 506 | | or (end2.type is head_type and end1.type is tail_type): |
|---|
| 507 | | return |
|---|
| 508 | | |
|---|
| 509 | | # Find all associations and determine if the properties on the |
|---|
| 510 | | # association ends have a type that points to the class. |
|---|
| 511 | | Association = UML.Association |
|---|
| 512 | | for assoc in resource(UML.ElementFactory).itervalues(): |
|---|
| 513 | | if isinstance(assoc, Association): |
|---|
| 514 | | #print 'assoc.memberEnd', assoc.memberEnd |
|---|
| 515 | | end1 = assoc.memberEnd[0] |
|---|
| 516 | | end2 = assoc.memberEnd[1] |
|---|
| 517 | | if (end1.type is head_type and end2.type is tail_type) \ |
|---|
| 518 | | or (end2.type is head_type and end1.type is tail_type): |
|---|
| 519 | | # check if this entry is not yet in the diagram |
|---|
| 520 | | # Return if the association is not (yet) on the canvas |
|---|
| 521 | | for item in assoc.presentation: |
|---|
| 522 | | if item.canvas is self.canvas: |
|---|
| 523 | | break |
|---|
| 524 | | else: |
|---|
| 525 | | #return end1, end2, assoc |
|---|
| 526 | | self.subject = assoc |
|---|
| 527 | | if (end1.type is head_type and end2.type is tail_type): |
|---|
| 528 | | self._head_end.subject = end1 |
|---|
| 529 | | self._tail_end.subject = end2 |
|---|
| 530 | | else: |
|---|
| 531 | | self._head_end.subject = end2 |
|---|
| 532 | | self._tail_end.subject = end1 |
|---|
| 533 | | return |
|---|
| 534 | | else: |
|---|
| 535 | | # TODO: How should we handle other types than Class??? |
|---|
| 536 | | |
|---|
| 537 | | element_factory = resource(UML.ElementFactory) |
|---|
| 538 | | relation = element_factory.create(UML.Association) |
|---|
| 539 | | head_end = element_factory.create(UML.Property) |
|---|
| 540 | | head_end.lowerValue = element_factory.create(UML.LiteralSpecification) |
|---|
| 541 | | tail_end = element_factory.create(UML.Property) |
|---|
| 542 | | tail_end.lowerValue = element_factory.create(UML.LiteralSpecification) |
|---|
| 543 | | relation.package = self.canvas.diagram.namespace |
|---|
| 544 | | relation.memberEnd = head_end |
|---|
| 545 | | relation.memberEnd = tail_end |
|---|
| 546 | | #head_end.type = tail_end.class_ = head_type |
|---|
| 547 | | #tail_end.type = head_end.class_ = tail_type |
|---|
| 548 | | head_end.type = head_type |
|---|
| 549 | | tail_end.type = tail_type |
|---|
| 550 | | head_type.ownedAttribute = tail_end |
|---|
| 551 | | tail_type.ownedAttribute = head_end |
|---|
| 552 | | # copy text from ends to AssociationEnds: |
|---|
| 553 | | #head_end.name = self._head_end._name.get_property('text') |
|---|
| 554 | | #head_end.multiplicity = self._head__end._mult.get_property('text') |
|---|
| 555 | | #tail_end.name = self._tail_end._name.get_property('text') |
|---|
| 556 | | #tail_end.multiplicity = self._tail_end._mult.get_property('text') |
|---|
| 557 | | |
|---|
| 558 | | self.subject = relation |
|---|
| 559 | | self._head_end.subject = head_end |
|---|
| 560 | | self._tail_end.subject = tail_end |
|---|
| 561 | | |
|---|
| 562 | | def confirm_disconnect_handle(self, handle, was_connected_to): |
|---|
| 563 | | #log.debug('AssociationItem.confirm_disconnect_handle') |
|---|
| 564 | | if self.subject: |
|---|
| 565 | | # First delete the Property's at the ends, otherwise they will |
|---|
| 566 | | # be interpreted as attributes. |
|---|
| 567 | | self._head_end.set_subject(None) |
|---|
| 568 | | self._tail_end.set_subject(None) |
|---|
| 569 | | self.set_subject(None) |
|---|
| | 382 | if self._show_direction: |
|---|
| | 383 | cr.save() |
|---|
| | 384 | try: |
|---|
| | 385 | cr.translate(*self._dir_pos) |
|---|
| | 386 | cr.rotate(self._dir_angle) |
|---|
| | 387 | cr.move_to(0, 0) |
|---|
| | 388 | cr.line_to(6, 5) |
|---|
| | 389 | cr.line_to(0, 10) |
|---|
| | 390 | cr.fill() |
|---|
| | 391 | finally: |
|---|
| | 392 | cr.restore() |
|---|
| | 393 | |
|---|
| | 394 | if self.subject and self.subject.name: |
|---|
| | 395 | cr.move_to(self._label_bounds[0], self._label_bounds[1]) |
|---|
| | 396 | cr.show_text(self.subject.name) |
|---|