1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
4 # Purpose: LineShape class
6 # Author: Pierre Hjälm (from C++ original by Julian Smart)
10 # Copyright: (c) 2004 Pierre Hjälm - 1998 Julian Smart
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------------
17 from _basic
import Shape
, ShapeRegion
, ControlPoint
, RectangleShape
18 from _oglmisc
import *
20 # Line alignment flags
22 LINE_ALIGNMENT_HORIZ
= 1
23 LINE_ALIGNMENT_VERT
= 0
24 LINE_ALIGNMENT_TO_NEXT_HANDLE
= 2
25 LINE_ALIGNMENT_NONE
= 0
29 class LineControlPoint(ControlPoint
):
30 def __init__(self
, theCanvas
= None, object = None, size
= 0.0, x
= 0.0, y
= 0.0, the_type
= 0):
31 ControlPoint
.__init
__(self
, theCanvas
, object, size
, x
, y
, the_type
)
36 self
._originalPos
= None
39 RectangleShape
.OnDraw(self
, dc
)
41 # Implement movement of Line point
42 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
43 self
._shape
.GetEventHandler().OnSizingDragLeft(self
, draw
, x
, y
, keys
, attachment
)
45 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
46 self
._shape
.GetEventHandler().OnSizingBeginDragLeft(self
, x
, y
, keys
, attachment
)
48 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
49 self
._shape
.GetEventHandler().OnSizingEndDragLeft(self
, x
, y
, keys
, attachment
)
53 class ArrowHead(object):
54 def __init__(self
, type = 0, end
= 0, size
= 0.0, dist
= 0.0, name
= "", mf
= None, arrowId
= -1):
55 if isinstance(type, ArrowHead
):
58 self
._arrowType
= type
60 self
._arrowSize
= size
65 self
._arrowName
= name
72 return self
._arrowType
74 def GetPosition(self
):
77 def SetPosition(self
, pos
):
90 return self
._arrowSize
92 def SetSize(self
, size
):
93 self
._arrowSize
= size
94 if self
._arrowType
== ARROW_METAFILE
and self
._metaFile
:
95 oldWidth
= self
._metaFile
._width
99 scale
= float(size
) / oldWidth
101 self
._metaFile
.Scale(scale
, scale
)
104 return self
._arrowName
106 def SetXOffset(self
, x
):
109 def SetYOffset(self
, y
):
112 def GetMetaFile(self
):
113 return self
._metaFile
118 def GetArrowEnd(self
):
119 return self
._arrowEnd
121 def GetArrowSize(self
):
122 return self
._arrowSize
124 def SetSpacing(self
, sp
):
129 class LabelShape(RectangleShape
):
130 def __init__(self
, parent
, region
, w
, h
):
131 RectangleShape
.__init
__(self
, w
, h
)
132 self
._lineShape
= parent
133 self
._shapeRegion
= region
134 self
.SetPen(wx
.ThePenList
.FindOrCreatePen(wx
.Colour(0, 0, 0), 1, wx
.DOT
))
136 def OnDraw(self
, dc
):
137 if self
._lineShape
and not self
._lineShape
.GetDrawHandles():
140 x1
= self
._xpos
- self
._width
/ 2.0
141 y1
= self
._ypos
- self
._height
/ 2.0
144 if self
._pen
.GetWidth() == 0:
145 dc
.SetPen(wx
.Pen(wx
.WHITE
, 1, wx
.TRANSPARENT
))
148 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
150 if self
._cornerRadius
> 0:
151 dc
.DrawRoundedRectangle(x1
, y1
, self
._width
, self
._height
, self
._cornerRadius
)
153 dc
.DrawRectangle(x1
, y1
, self
._width
, self
._height
)
155 def OnDrawContents(self
, dc
):
158 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
159 RectangleShape
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
161 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
162 RectangleShape
.OnBeginDragLeft(self
, x
, y
, keys
, attachment
)
164 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
165 RectangleShape
.OnEndDragLeft(self
, x
, y
, keys
, attachment
)
167 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
):
168 return self
._lineShape
.OnLabelMovePre(dc
, self
, x
, y
, old_x
, old_y
, display
)
170 # Divert left and right clicks to line object
171 def OnLeftClick(self
, x
, y
, keys
= 0, attachment
= 0):
172 self
._lineShape
.GetEventHandler().OnLeftClick(x
, y
, keys
, attachment
)
174 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
175 self
._lineShape
.GetEventHandler().OnRightClick(x
, y
, keys
, attachment
)
179 class LineShape(Shape
):
180 """LineShape may be attached to two nodes;
181 it may be segmented, in which case a control point is drawn for each joint.
183 A wxLineShape may have arrows at the beginning, end and centre.
191 self
._sensitivity
= OP_CLICK_LEFT | OP_CLICK_RIGHT
192 self
._draggable
= False
193 self
._attachmentTo
= 0
194 self
._attachmentFrom
= 0
197 self
._erasing
= False
198 self
._arrowSpacing
= 5.0
199 self
._ignoreArrowOffsets
= False
200 self
._isSpline
= False
201 self
._maintainStraightLines
= False
202 self
._alignmentStart
= 0
203 self
._alignmentEnd
= 0
205 self
._lineControlPoints
= None
207 # Clear any existing regions (created in an earlier constructor)
208 # and make the three line regions.
210 for name
in ["Middle","Start","End"]:
211 newRegion
= ShapeRegion()
212 newRegion
.SetName(name
)
213 newRegion
.SetSize(150, 50)
214 self
._regions
.append(newRegion
)
216 self
._labelObjects
= [None, None, None]
217 self
._lineOrientations
= []
218 self
._lineControlPoints
= []
222 if self
._lineControlPoints
:
223 self
.ClearPointList(self
._lineControlPoints
)
224 self
._lineControlPoints
= []
226 if self
._labelObjects
[i
]:
227 self
._labelObjects
[i
].Select(False)
228 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
229 self
._labelObjects
= []
230 self
.ClearArrowsAtPosition(-1)
233 """Return the 'from' object."""
237 """Return the 'to' object."""
240 def GetAttachmentFrom(self
):
241 """Return the attachment point on the 'from' node."""
242 return self
._attachmentFrom
244 def GetAttachmentTo(self
):
245 """Return the attachment point on the 'to' node."""
246 return self
._attachmentTo
248 def GetLineControlPoints(self
):
249 return self
._lineControlPoints
251 def SetSpline(self
, spline
):
252 """Specifies whether a spline is to be drawn through the control points."""
253 self
._isSpline
= spline
256 """TRUE if a spline is drawn through the control points."""
257 return self
._isSpline
259 def SetAttachmentFrom(self
, attach
):
260 """Set the 'from' shape attachment."""
261 self
._attachmentFrom
= attach
263 def SetAttachmentTo(self
, attach
):
264 """Set the 'to' shape attachment."""
265 self
._attachmentTo
= attach
267 # This is really to distinguish between lines and other images.
268 # For lines, want to pass drag to canvas, since lines tend to prevent
269 # dragging on a canvas (they get in the way.)
273 def SetIgnoreOffsets(self
, ignore
):
274 """Set whether to ignore offsets from the end of the line when drawing."""
275 self
._ignoreArrowOffsets
= ignore
278 return self
._arcArrows
280 def GetAlignmentStart(self
):
281 return self
._alignmentStart
283 def GetAlignmentEnd(self
):
284 return self
._alignmentEnd
286 def IsEnd(self
, nodeObject
):
287 """TRUE if shape is at the end of the line."""
288 return self
._to
== nodeObject
290 def MakeLineControlPoints(self
, n
):
291 """Make a given number of control points (minimum of two)."""
292 if self
._lineControlPoints
:
293 self
.ClearPointList(self
._lineControlPoints
)
294 self
._lineControlPoints
= []
297 point
= wx
.RealPoint(-999, -999)
298 self
._lineControlPoints
.append(point
)
300 def InsertLineControlPoint(self
, dc
= None):
301 """Insert a control point at an arbitrary position."""
305 last_point
= self
._lineControlPoints
[-1]
306 second_last_point
= self
._lineControlPoints
[-2]
308 line_x
= (last_point
[0] + second_last_point
[0]) / 2.0
309 line_y
= (last_point
[1] + second_last_point
[1]) / 2.0
311 point
= wx
.RealPoint(line_x
, line_y
)
312 self
._lineControlPoints
.insert(len(self
._lineControlPoints
), point
)
314 def DeleteLineControlPoint(self
):
315 """Delete an arbitary point on the line."""
316 if len(self
._lineControlPoints
) < 3:
319 del self
._lineControlPoints
[-2]
322 def Initialise(self
):
323 """Initialise the line object."""
324 if self
._lineControlPoints
:
325 # Just move the first and last control points
326 first_point
= self
._lineControlPoints
[0]
327 last_point
= self
._lineControlPoints
[-1]
329 # If any of the line points are at -999, we must
330 # initialize them by placing them half way between the first
333 for point
in self
._lineControlPoints
[1:]:
335 if first_point
[0] < last_point
[0]:
341 if first_point
[1] < last_point
[1]:
347 point
[0] = (x2
- x1
) / 2.0 + x1
348 point
[1] = (y2
- y1
) / 2.0 + y1
350 def FormatText(self
, dc
, s
, i
):
351 """Format a text string according to the region size, adding
352 strings with positions to region text list.
356 if len(self
._regions
) == 0 or i
>= len(self
._regions
):
359 region
= self
._regions
[i
]
361 dc
.SetFont(region
.GetFont())
363 w
, h
= region
.GetSize()
364 # Initialize the size if zero
365 if (w
== 0 or h
== 0) and s
:
369 string_list
= FormatText(dc
, s
, w
- 5, h
- 5, region
.GetFormatMode())
370 for s
in string_list
:
371 line
= ShapeTextLine(0.0, 0.0, s
)
372 region
.GetFormattedText().append(line
)
376 if region
.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS
:
377 actualW
, actualH
= GetCentredTextExtent(dc
, region
.GetFormattedText(), self
._xpos
, self
._ypos
, w
, h
)
378 if actualW
!= w
or actualH
!= h
:
379 xx
, yy
= self
.GetLabelPosition(i
)
380 self
.EraseRegion(dc
, region
, xx
, yy
)
381 if len(self
._labelObjects
) < i
:
382 self
._labelObjects
[i
].Select(False, dc
)
383 self
._labelObjects
[i
].Erase(dc
)
384 self
._labelObjects
[i
].SetSize(actualW
, actualH
)
386 region
.SetSize(actualW
, actualH
)
388 if len(self
._labelObjects
) < i
:
389 self
._labelObjects
[i
].Select(True, dc
)
390 self
._labelObjects
[i
].Draw(dc
)
392 CentreText(dc
, region
.GetFormattedText(), self
._xpos
, self
._ypos
, actualW
, actualH
, region
.GetFormatMode())
393 self
._formatted
= True
395 def DrawRegion(self
, dc
, region
, x
, y
):
396 """Format one region at this position."""
397 if self
.GetDisableLabel():
400 w
, h
= region
.GetSize()
402 # Get offset from x, y
403 xx
, yy
= region
.GetPosition()
408 # First, clear a rectangle for the text IF there is any
409 if len(region
.GetFormattedText()):
410 dc
.SetPen(self
.GetBackgroundPen())
411 dc
.SetBrush(self
.GetBackgroundBrush())
415 dc
.SetFont(region
.GetFont())
416 dc
.DrawRectangle(xp
- w
/ 2.0, yp
- h
/ 2.0, w
, h
)
420 dc
.SetTextForeground(region
.GetActualColourObject())
422 DrawFormattedText(dc
, region
.GetFormattedText(), xp
, yp
, w
, h
, region
.GetFormatMode())
424 def EraseRegion(self
, dc
, region
, x
, y
):
425 """Erase one region at this position."""
426 if self
.GetDisableLabel():
429 w
, h
= region
.GetSize()
431 # Get offset from x, y
432 xx
, yy
= region
.GetPosition()
437 if region
.GetFormattedText():
438 dc
.SetPen(self
.GetBackgroundPen())
439 dc
.SetBrush(self
.GetBackgroundBrush())
441 dc
.DrawRectangle(xp
- w
/ 2.0, yp
- h
/ 2.0, w
, h
)
443 def GetLabelPosition(self
, position
):
444 """Get the reference point for a label.
446 Region x and y are offsets from this.
447 position is 0 (middle), 1 (start), 2 (end).
450 # Want to take the middle section for the label
451 half_way
= int(len(self
._lineControlPoints
) / 2.0)
453 # Find middle of this line
454 point
= self
._lineControlPoints
[half_way
- 1]
455 next_point
= self
._lineControlPoints
[half_way
]
457 dx
= next_point
[0] - point
[0]
458 dy
= next_point
[1] - point
[1]
460 return point
[0] + dx
/ 2.0, point
[1] + dy
/ 2.0
462 return self
._lineControlPoints
[0][0], self
._lineControlPoints
[0][1]
464 return self
._lineControlPoints
[-1][0], self
._lineControlPoints
[-1][1]
466 def Straighten(self
, dc
= None):
467 """Straighten verticals and horizontals."""
468 if len(self
._lineControlPoints
) < 3:
474 GraphicsStraightenLine(self
._lineControlPoints
[-1], self
._lineControlPoints
[-2])
476 for i
in range(len(self
._lineControlPoints
) - 2):
477 GraphicsStraightenLine(self
._lineControlPoints
[i
], self
._lineControlPoints
[i
+ 1])
483 """Unlink the line from the nodes at either end."""
485 self
._to
.GetLines().remove(self
)
487 self
._from
.GetLines().remove(self
)
491 def SetEnds(self
, x1
, y1
, x2
, y2
):
492 """Set the end positions of the line."""
494 first_point
= self
._lineControlPoints
[0]
495 last_point
= self
._lineControlPoints
[-1]
502 self
._xpos
= (x1
+ x2
) / 2.0
503 self
._ypos
= (y1
+ y2
) / 2.0
505 # Get absolute positions of ends
507 """Get the visible endpoints of the lines for drawing between two objects."""
508 first_point
= self
._lineControlPoints
[0]
509 last_point
= self
._lineControlPoints
[-1]
511 return (first_point
[0], first_point
[1]), (last_point
[0], last_point
[1])
513 def SetAttachments(self
, from_attach
, to_attach
):
514 """Specify which object attachment points should be used at each end
517 self
._attachmentFrom
= from_attach
518 self
._attachmentTo
= to_attach
520 def HitTest(self
, x
, y
):
521 if not self
._lineControlPoints
:
524 # Look at label regions in case mouse is over a label
525 inLabelRegion
= False
528 region
= self
._regions
[i
]
529 if len(region
._formattedText
):
530 xp
, yp
= self
.GetLabelPosition(i
)
531 # Offset region from default label position
532 cx
, cy
= region
.GetPosition()
533 cw
, ch
= region
.GetSize()
537 rLeft
= cx
- cw
/ 2.0
539 rRight
= cx
+ cw
/ 2.0
540 rBottom
= cy
+ ch
/ 2.0
541 if x
> rLeft
and x
< rRight
and y
> rTop
and y
< rBottom
:
545 for i
in range(len(self
._lineControlPoints
) - 1):
546 point1
= self
._lineControlPoints
[i
]
547 point2
= self
._lineControlPoints
[i
+ 1]
549 # For inaccurate mousing allow 8 pixel corridor
552 dx
= point2
[0] - point1
[0]
553 dy
= point2
[1] - point1
[1]
555 seg_len
= math
.sqrt(dx
* dx
+ dy
* dy
)
556 if dy
== 0 or dx
== 0:
558 distance_from_seg
= seg_len
* float((x
- point1
[0]) * dy
- (y
- point1
[1]) * dx
) / (dy
* dy
+ dx
* dx
)
559 distance_from_prev
= seg_len
* float((y
- point1
[1]) * dy
+ (x
- point1
[0]) * dx
) / (dy
* dy
+ dx
* dx
)
561 if abs(distance_from_seg
) < extra
and distance_from_prev
>= 0 and distance_from_prev
<= seg_len
or inLabelRegion
:
562 return 0, distance_from_seg
566 def DrawArrows(self
, dc
):
567 """Draw all arrows."""
568 # Distance along line of each arrow: space them out evenly
573 for arrow
in self
._arcArrows
:
574 ah
= arrow
.GetArrowEnd()
575 if ah
== ARROW_POSITION_START
:
576 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
577 # If specified, x offset is proportional to line length
578 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
580 self
.DrawArrow(dc
, arrow
, startArrowPos
, False)
581 startArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
582 elif ah
== ARROW_POSITION_END
:
583 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
584 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
586 self
.DrawArrow(dc
, arrow
, endArrowPos
, False)
587 endArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
588 elif ah
== ARROW_POSITION_MIDDLE
:
589 arrow
.SetXOffset(middleArrowPos
)
590 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
591 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
593 self
.DrawArrow(dc
, arrow
, middleArrowPos
, False)
594 middleArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
596 def DrawArrow(self
, dc
, arrow
, XOffset
, proportionalOffset
):
597 """Draw the given arrowhead (or annotation)."""
598 first_line_point
= self
._lineControlPoints
[0]
599 second_line_point
= self
._lineControlPoints
[1]
601 last_line_point
= self
._lineControlPoints
[-1]
602 second_last_line_point
= self
._lineControlPoints
[-2]
604 # Position of start point of line, at the end of which we draw the arrow
605 startPositionX
, startPositionY
= 0.0, 0.0
607 ap
= arrow
.GetPosition()
608 if ap
== ARROW_POSITION_START
:
609 # If we're using a proportional offset, calculate just where this
610 # will be on the line.
612 if proportionalOffset
:
613 totalLength
= math
.sqrt((second_line_point
[0] - first_line_point
[0]) * (second_line_point
[0] - first_line_point
[0]) + (second_line_point
[1] - first_line_point
[1]) * (second_line_point
[1] - first_line_point
[1]))
614 realOffset
= XOffset
* totalLength
616 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_line_point
[0], second_line_point
[1], first_line_point
[0], first_line_point
[1], realOffset
)
618 startPositionX
= second_line_point
[0]
619 startPositionY
= second_line_point
[1]
620 elif ap
== ARROW_POSITION_END
:
621 # If we're using a proportional offset, calculate just where this
622 # will be on the line.
624 if proportionalOffset
:
625 totalLength
= math
.sqrt((second_last_line_point
[0] - last_line_point
[0]) * (second_last_line_point
[0] - last_line_point
[0]) + (second_last_line_point
[1] - last_line_point
[1]) * (second_last_line_point
[1] - last_line_point
[1]));
626 realOffset
= XOffset
* totalLength
628 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], last_line_point
[0], last_line_point
[1], realOffset
)
630 startPositionX
= second_last_line_point
[0]
631 startPositionY
= second_last_line_point
[1]
632 elif ap
== ARROW_POSITION_MIDDLE
:
633 # Choose a point half way between the last and penultimate points
634 x
= (last_line_point
[0] + second_last_line_point
[0]) / 2.0
635 y
= (last_line_point
[1] + second_last_line_point
[1]) / 2.0
637 # If we're using a proportional offset, calculate just where this
638 # will be on the line.
640 if proportionalOffset
:
641 totalLength
= math
.sqrt((second_last_line_point
[0] - x
) * (second_last_line_point
[0] - x
) + (second_last_line_point
[1] - y
) * (second_last_line_point
[1] - y
));
642 realOffset
= XOffset
* totalLength
644 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], x
, y
, realOffset
)
645 startPositionX
= second_last_line_point
[0]
646 startPositionY
= second_last_line_point
[1]
648 # Add yOffset to arrow, if any
650 # The translation that the y offset may give
653 if arrow
.GetYOffset
and not self
._ignoreArrowOffsets
:
657 # (x1, y1)--------------(x3, y3)------------------(x2, y2)
658 # x4 = x3 - d * math.sin(theta)
659 # y4 = y3 + d * math.cos(theta)
661 # Where theta = math.tan(-1) of (y3-y1) / (x3-x1)
664 x3
= float(positionOnLineX
)
665 y3
= float(positionOnLineY
)
666 d
= -arrow
.GetYOffset() # Negate so +offset is above line
669 theta
= math
.pi
/ 2.0
671 theta
= math
.atan((y3
- y1
) / (x3
- x1
))
673 x4
= x3
- d
* math
.sin(theta
)
674 y4
= y3
+ d
* math
.cos(theta
)
676 deltaX
= x4
- positionOnLineX
677 deltaY
= y4
- positionOnLineY
679 at
= arrow
._GetType
()
680 if at
== ARROW_ARROW
:
681 arrowLength
= arrow
.GetSize()
682 arrowWidth
= arrowLength
/ 3.0
684 tip_x
, tip_y
, side1_x
, side1_y
, side2_x
, side2_y
= GetArrowPoints(startPositionX
+ deltaX
, startPositionY
+ deltaY
, positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
, arrowLength
, arrowWidth
)
686 points
= [[tip_x
, tip_y
],
692 dc
.SetBrush(self
._brush
)
693 dc
.DrawPolygon(points
)
694 elif at
in [ARROW_HOLLOW_CIRCLE
, ARROW_FILLED_CIRCLE
]:
695 # Find point on line of centre of circle, which is a radius away
696 # from the end position
697 diameter
= arrow
.GetSize()
698 x
, y
= GetPointOnLine(startPositionX
+ deltaX
, startPositionY
+ deltaY
,
699 positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
,
701 x1
= x
- diameter
/ 2.0
702 y1
= y
- diameter
/ 2.0
704 if arrow
._GetType
() == ARROW_HOLLOW_CIRCLE
:
705 dc
.SetBrush(self
.GetBackgroundBrush())
707 dc
.SetBrush(self
._brush
)
709 dc
.DrawEllipse(x1
, y1
, diameter
, diameter
)
710 elif at
== ARROW_SINGLE_OBLIQUE
:
712 elif at
== ARROW_METAFILE
:
713 if arrow
.GetMetaFile():
714 # Find point on line of centre of object, which is a half-width away
715 # from the end position
718 # <-- start pos <-----><-- positionOnLineX
720 # --------------| x | <-- e.g. rectangular arrowhead
723 x
, y
= GetPointOnLine(startPositionX
, startPositionY
,
724 positionOnLineX
, positionOnLineY
,
725 arrow
.GetMetaFile()._width
/ 2.0)
726 # Calculate theta for rotating the metafile.
729 # | o(x2, y2) 'o' represents the arrowhead.
734 # |______________________
739 x2
= float(positionOnLineX
)
740 y2
= float(positionOnLineY
)
742 if x1
== x2
and y1
== y2
:
744 elif x1
== x2
and y1
> y2
:
745 theta
= 3.0 * math
.pi
/ 2.0
746 elif x1
== x2
and y2
> y1
:
747 theta
= math
.pi
/ 2.0
748 elif x2
> x1
and y2
>= y1
:
749 theta
= math
.atan((y2
- y1
) / (x2
- x1
))
751 theta
= math
.pi
+ math
.atan((y2
- y1
) / (x2
- x1
))
752 elif x2
> x1
and y2
< y1
:
753 theta
= 2 * math
.pi
+ math
.atan((y2
- y1
) / (x2
- x1
))
755 raise "Unknown arrowhead rotation case"
757 # Rotate about the centre of the object, then place
758 # the object on the line.
759 if arrow
.GetMetaFile().GetRotateable():
760 arrow
.GetMetaFile().Rotate(0.0, 0.0, theta
)
763 # If erasing, just draw a rectangle
764 minX
, minY
, maxX
, maxY
= arrow
.GetMetaFile().GetBounds()
765 # Make erasing rectangle slightly bigger or you get droppings
767 dc
.DrawRectangle(deltaX
+ x
+ minX
- extraPixels
/ 2.0, deltaY
+ y
+ minY
- extraPixels
/ 2.0, maxX
- minX
+ extraPixels
, maxY
- minY
+ extraPixels
)
769 arrow
.GetMetaFile().Draw(dc
, x
+ deltaX
, y
+ deltaY
)
771 def OnErase(self
, dc
):
773 old_brush
= self
._brush
775 bg_pen
= self
.GetBackgroundPen()
776 bg_brush
= self
.GetBackgroundBrush()
778 self
.SetBrush(bg_brush
)
780 bound_x
, bound_y
= self
.GetBoundingBoxMax()
782 dc
.SetFont(self
._font
)
784 # Undraw text regions
787 x
, y
= self
.GetLabelPosition(i
)
788 self
.EraseRegion(dc
, self
._regions
[i
], x
, y
)
791 dc
.SetPen(self
.GetBackgroundPen())
792 dc
.SetBrush(self
.GetBackgroundBrush())
794 # Drawing over the line only seems to work if the line has a thickness
796 if old_pen
and old_pen
.GetWidth() > 1:
797 dc
.DrawRectangle(self
._xpos
- bound_x
/ 2.0 - 2, self
._ypos
- bound_y
/ 2.0 - 2,
798 bound_x
+ 4, bound_y
+ 4)
801 self
.GetEventHandler().OnDraw(dc
)
802 self
.GetEventHandler().OnEraseControlPoints(dc
)
803 self
._erasing
= False
808 self
.SetBrush(old_brush
)
810 def GetBoundingBoxMin(self
):
811 x1
, y1
= 10000, 10000
812 x2
, y2
= -10000, -10000
814 for point
in self
._lineControlPoints
:
824 return x2
- x1
, y2
- y1
826 # For a node image of interest, finds the position of this arc
827 # amongst all the arcs which are attached to THIS SIDE of the node image,
828 # and the number of same.
829 def FindNth(self
, image
, incoming
):
830 """Find the position of the line on the given object.
832 Specify whether incoming or outgoing lines are being considered
838 if image
== self
._to
:
839 this_attachment
= self
._attachmentTo
841 this_attachment
= self
._attachmentFrom
843 # Find number of lines going into / out of this particular attachment point
844 for line
in image
.GetLines():
845 if line
._from
== image
:
846 # This is the nth line attached to 'image'
847 if line
== self
and not incoming
:
850 # Increment num count if this is the same side (attachment number)
851 if line
._attachmentFrom
== this_attachment
:
854 if line
._to
== image
:
855 # This is the nth line attached to 'image'
856 if line
== self
and incoming
:
859 # Increment num count if this is the same side (attachment number)
860 if line
._attachmentTo
== this_attachment
:
865 def OnDrawOutline(self
, dc
, x
, y
, w
, h
):
867 old_brush
= self
._brush
869 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
870 self
.SetPen(dottedPen
)
871 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
873 self
.GetEventHandler().OnDraw(dc
)
880 self
.SetBrush(old_brush
)
884 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
= True):
888 if self
._lineControlPoints
and not (x_offset
== 0 and y_offset
== 0):
889 for point
in self
._lineControlPoints
:
893 # Move temporary label rectangles if necessary
895 if self
._labelObjects
[i
]:
896 self
._labelObjects
[i
].Erase(dc
)
897 xp
, yp
= self
.GetLabelPosition(i
)
898 if i
< len(self
._regions
):
899 xr
, yr
= self
._regions
[i
].GetPosition()
902 self
._labelObjects
[i
].Move(dc
, xp
+ xr
, yp
+ yr
)
905 def OnMoveLink(self
, dc
, moveControlPoints
= True):
906 """Called when a connected object has moved, to move the link to
909 if not self
._from
or not self
._to
:
912 if len(self
._lineControlPoints
) > 2:
915 # Do each end - nothing in the middle. User has to move other points
916 # manually if necessary
917 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
919 first
= self
._lineControlPoints
[0]
920 last
= self
._lineControlPoints
[-1]
922 oldX
, oldY
= self
._xpos
, self
._ypos
924 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
926 # Do a second time, because one may depend on the other
927 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
928 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
930 # Try to move control points with the arc
931 x_offset
= self
._xpos
- oldX
932 y_offset
= self
._ypos
- oldY
934 # Only move control points if it's a self link. And only works
935 # if attachment mode is ON
936 if self
._from
== self
._to
and self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
and moveControlPoints
and self
._lineControlPoints
and not (x_offset
== 0 and y_offset
== 0):
937 for point
in self
._lineControlPoints
[1:-1]:
941 self
.Move(dc
, self
._xpos
, self
._ypos
)
943 def FindLineEndPoints(self
):
944 """Finds the x, y points at the two ends of the line.
946 This function can be used by e.g. line-routing routines to
947 get the actual points on the two node images where the lines will be
950 if not self
._from
or not self
._to
:
953 # Do each end - nothing in the middle. User has to move other points
954 # manually if necessary.
955 second_point
= self
._lineControlPoints
[1]
956 second_last_point
= self
._lineControlPoints
[-2]
958 if len(self
._lineControlPoints
) > 2:
959 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
960 nth
, no_arcs
= self
.FindNth(self
._from
, False) # Not incoming
961 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
963 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), second_point
[0], second_point
[1])
965 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
966 nth
, no_arch
= self
.FindNth(self
._to
, True) # Incoming
967 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arch
, self
)
969 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), second_last_point
[0], second_last_point
[1])
971 fromX
= self
._from
.GetX()
972 fromY
= self
._from
.GetY()
973 toX
= self
._to
.GetX()
974 toY
= self
._to
.GetY()
976 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
977 nth
, no_arcs
= self
.FindNth(self
._from
, False)
978 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
982 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
983 nth
, no_arcs
= self
.FindNth(self
._to
, True)
984 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arcs
, self
)
988 if self
._from
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
989 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), toX
, toY
)
991 if self
._to
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
992 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), fromX
, fromY
)
994 #print type(self._from), type(self._to), end_x, end_y, other_end_x, other_end_y
995 return end_x
, end_y
, other_end_x
, other_end_y
997 def OnDraw(self
, dc
):
998 if not self
._lineControlPoints
:
1002 dc
.SetPen(self
._pen
)
1004 dc
.SetBrush(self
._brush
)
1007 for point
in self
._lineControlPoints
:
1008 points
.append(wx
.Point(point
.x
, point
.y
))
1012 dc
.DrawSpline(points
)
1014 dc
.DrawLines(points
)
1016 if sys
.platform
[:3] == "win":
1017 # For some reason, last point isn't drawn under Windows
1019 dc
.DrawPoint(pt
.x
, pt
.y
)
1021 # Problem with pen - if not a solid pen, does strange things
1022 # to the arrowhead. So make (get) a new pen that's solid.
1023 if self
._pen
and self
._pen
.GetStyle() != wx
.SOLID
:
1024 solid_pen
= wx
.ThePenList
.FindOrCreatePen(self
._pen
.GetColour(), 1, wx
.SOLID
)
1026 dc
.SetPen(solid_pen
)
1030 def OnDrawControlPoints(self
, dc
):
1031 if not self
._drawHandles
:
1034 # Draw temporary label rectangles if necessary
1036 if self
._labelObjects
[i
]:
1037 self
._labelObjects
[i
].Draw(dc
)
1039 Shape
.OnDrawControlPoints(self
, dc
)
1041 def OnEraseControlPoints(self
, dc
):
1042 # Erase temporary label rectangles if necessary
1045 if self
._labelObjects
[i
]:
1046 self
._labelObjects
[i
].Erase(dc
)
1048 Shape
.OnEraseControlPoints(self
, dc
)
1050 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1053 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1056 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1059 def OnDrawContents(self
, dc
):
1060 if self
.GetDisableLabel():
1064 if self
._regions
[i
]:
1065 x
, y
= self
.GetLabelPosition(i
)
1066 self
.DrawRegion(dc
, self
._regions
[i
], x
, y
)
1068 def SetTo(self
, object):
1069 """Set the 'to' object for the line."""
1072 def SetFrom(self
, object):
1073 """Set the 'from' object for the line."""
1076 def MakeControlPoints(self
):
1077 """Make handle control points."""
1078 if self
._canvas
and self
._lineControlPoints
:
1079 first
= self
._lineControlPoints
[0]
1080 last
= self
._lineControlPoints
[-1]
1082 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, first
[0], first
[1], CONTROL_POINT_ENDPOINT_FROM
)
1083 control
._point
= first
1084 self
._canvas
.AddShape(control
)
1085 self
._controlPoints
.Append(control
)
1087 for point
in self
._lineControlPoints
[1:-1]:
1088 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, point
[0], point
[1], CONTROL_POINT_LINE
)
1089 control
._point
= point
1090 self
._canvas
.AddShape(control
)
1091 self
._controlPoints
.Append(control
)
1093 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, last
[0], last
[1], CONTROL_POINT_ENDPOINT_TO
)
1094 control
._point
= last
1095 self
._canvas
.AddShape(control
)
1096 self
._controlPoints
.Append(control
)
1098 def ResetControlPoints(self
):
1099 if self
._canvas
and self
._lineControlPoints
:
1100 for i
in range(min(len(self
._controlPoints
), len(self
._lineControlPoints
))):
1101 point
= self
._lineControlPoints
[i
]
1102 control
= self
._controlPoints
[i
]
1103 control
.SetX(point
[0])
1104 control
.SetY(point
[1])
1106 # Override select, to create / delete temporary label-moving objects
1107 def Select(self
, select
, dc
= None):
1108 Shape
.Select(self
, select
, dc
)
1111 if self
._regions
[i
]:
1112 region
= self
._regions
[i
]
1113 if region
._formattedText
:
1114 w
, h
= region
.GetSize()
1115 x
, y
= region
.GetPosition()
1116 xx
, yy
= self
.GetLabelPosition(i
)
1118 if self
._labelObjects
[i
]:
1119 self
._labelObjects
[i
].Select(False)
1120 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1122 self
._labelObjects
[i
] = self
.OnCreateLabelShape(self
, region
, w
, h
)
1123 self
._labelObjects
[i
].AddToCanvas(self
._canvas
)
1124 self
._labelObjects
[i
].Show(True)
1126 self
._labelObjects
[i
].Move(dc
, x
+ xx
, y
+ yy
)
1127 self
._labelObjects
[i
].Select(True, dc
)
1130 if self
._labelObjects
[i
]:
1131 self
._labelObjects
[i
].Select(False, dc
)
1132 self
._labelObjects
[i
].Erase(dc
)
1133 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1134 self
._labelObjects
[i
] = None
1136 # Control points ('handles') redirect control to the actual shape, to
1137 # make it easier to override sizing behaviour.
1138 def OnSizingDragLeft(self
, pt
, draw
, x
, y
, keys
= 0, attachment
= 0):
1139 dc
= wx
.ClientDC(self
.GetCanvas())
1140 self
.GetCanvas().PrepareDC(dc
)
1142 dc
.SetLogicalFunction(OGLRBLF
)
1144 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1145 dc
.SetPen(dottedPen
)
1146 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1148 if pt
._type
== CONTROL_POINT_LINE
:
1149 x
, y
= self
._canvas
.Snap()
1156 old_pen
= self
.GetPen()
1157 old_brush
= self
.GetBrush()
1159 self
.SetPen(dottedPen
)
1160 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1162 self
.GetEventHandler().OnMoveLink(dc
, False)
1164 self
.SetPen(old_pen
)
1165 self
.SetBrush(old_brush
)
1167 def OnSizingBeginDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1168 dc
= wx
.ClientDC(self
.GetCanvas())
1169 self
.GetCanvas().PrepareDC(dc
)
1171 if pt
._type
== CONTROL_POINT_LINE
:
1172 pt
._originalPos
= pt
._point
1173 x
, y
= self
._canvas
.Snap()
1177 # Redraw start and end objects because we've left holes
1178 # when erasing the line
1179 self
.GetFrom().OnDraw(dc
)
1180 self
.GetFrom().OnDrawContents(dc
)
1181 self
.GetTo().OnDraw(dc
)
1182 self
.GetTo().OnDrawContents(dc
)
1184 self
.SetDisableLabel(True)
1185 dc
.SetLogicalFunction(OGLRBLF
)
1192 old_pen
= self
.GetPen()
1193 old_brush
= self
.GetBrush()
1195 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1196 self
.SetPen(dottedPen
)
1197 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1199 self
.GetEventHandler().OnMoveLink(dc
, False)
1201 self
.SetPen(old_pen
)
1202 self
.SetBrush(old_brush
)
1204 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
or pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1205 self
._canvas
.SetCursor(wx
.Cursor(wx
.CURSOR_BULLSEYE
))
1206 pt
._oldCursor
= wx
.STANDARD_CURSOR
1208 def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1209 dc
= wx
.ClientDC(self
.GetCanvas())
1210 self
.GetCanvas().PrepareDC(dc
)
1212 self
.SetDisableLabel(False)
1214 if pt
._type
== CONTROL_POINT_LINE
:
1215 x
, y
= self
._canvas
.Snap()
1217 rpt
= wx
.RealPoint(x
, y
)
1219 # Move the control point back to where it was;
1220 # MoveControlPoint will move it to the new position
1221 # if it decides it wants. We only moved the position
1222 # during user feedback so we could redraw the line
1223 # as it changed shape.
1224 pt
._xpos
= pt
._originalPos
[0]
1225 pt
._ypos
= pt
._originalPos
[1]
1226 pt
._point
[0] = pt
._originalPos
[0]
1227 pt
._point
[1] = pt
._originalPos
[1]
1229 self
.OnMoveMiddleControlPoint(dc
, pt
, rpt
)
1231 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
:
1233 self
._canvas
.SetCursor(pt
._oldCursor
)
1236 self
.GetFrom().MoveLineToNewAttachment(dc
, self
, x
, y
)
1238 if pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1240 self
._canvas
.SetCursor(pt
._oldCursor
)
1243 self
.GetTo().MoveLineToNewAttachment(dc
, self
, x
, y
)
1245 # This is called only when a non-end control point is moved
1246 def OnMoveMiddleControlPoint(self
, dc
, lpt
, pt
):
1250 lpt
._point
[0] = pt
[0]
1251 lpt
._point
[1] = pt
[1]
1253 self
.GetEventHandler().OnMoveLink(dc
)
1257 def AddArrow(self
, type, end
= ARROW_POSITION_END
, size
= 10.0, xOffset
= 0.0, name
= "", mf
= None, arrowId
= -1):
1258 """Add an arrow (or annotation) to the line.
1260 type may currently be one of:
1267 Conventional arrowhead.
1268 ARROW_SINGLE_OBLIQUE
1269 Single oblique stroke.
1270 ARROW_DOUBLE_OBLIQUE
1271 Double oblique stroke.
1272 ARROW_DOUBLE_METAFILE
1275 end may currently be one of:
1278 Arrow appears at the end.
1279 ARROW_POSITION_START
1280 Arrow appears at the start.
1282 arrowSize specifies the length of the arrow.
1284 xOffset specifies the offset from the end of the line.
1286 name specifies a name for the arrow.
1288 mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
1291 arrowId is the id for the arrow.
1293 arrow
= ArrowHead(type, end
, size
, xOffset
, name
, mf
, arrowId
)
1294 self
._arcArrows
.append(arrow
)
1297 # Add arrowhead at a particular position in the arrowhead list
1298 def AddArrowOrdered(self
, arrow
, referenceList
, end
):
1299 """Add an arrowhead in the position indicated by the reference list
1300 of arrowheads, which contains all legal arrowheads for this line, in
1301 the correct order. E.g.
1303 Reference list: a b c d e
1304 Current line list: a d
1306 Add c, then line list is: a c d.
1308 If no legal arrowhead position, return FALSE. Assume reference list
1309 is for one end only, since it potentially defines the ordering for
1310 any one of the 3 positions. So we don't check the reference list for
1313 if not referenceList
:
1316 targetName
= arrow
.GetName()
1318 # First check whether we need to insert in front of list,
1319 # because this arrowhead is the first in the reference
1320 # list and should therefore be first in the current list.
1321 refArrow
= referenceList
[0]
1322 if refArrow
.GetName() == targetName
:
1323 self
._arcArrows
.insert(0, arrow
)
1327 while i1
< len(referenceList
) and i2
< len(self
._arcArrows
):
1328 refArrow
= referenceList
[i1
]
1329 currArrow
= self
._arcArrows
[i2
]
1331 # Matching: advance current arrow pointer
1332 if currArrow
.GetArrowEnd() == end
and currArrow
.GetName() == refArrow
.GetName():
1335 # Check if we're at the correct position in the
1337 if targetName
== refArrow
.GetName():
1338 if i2
< len(self
._arcArrows
):
1339 self
._arcArrows
.insert(i2
, arrow
)
1341 self
._arcArrows
.append(arrow
)
1345 self
._arcArrows
.append(arrow
)
1348 def ClearArrowsAtPosition(self
, end
):
1349 """Delete the arrows at the specified position, or at any position
1353 self
._arcArrows
= []
1356 for arrow
in self
._arcArrows
:
1357 if arrow
.GetArrowEnd() == end
:
1358 self
._arcArrows
.remove(arrow
)
1360 def ClearArrow(self
, name
):
1361 """Delete the arrow with the given name."""
1362 for arrow
in self
._arcArrows
:
1363 if arrow
.GetName() == name
:
1364 self
._arcArrows
.remove(arrow
)
1368 def FindArrowHead(self
, position
, name
):
1369 """Find arrowhead by position and name.
1371 if position is -1, matches any position.
1373 for arrow
in self
._arcArrows
:
1374 if (position
== -1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1379 def FindArrowHeadId(self
, arrowId
):
1380 """Find arrowhead by id."""
1381 for arrow
in self
._arcArrows
:
1382 if arrowId
== arrow
.GetId():
1387 def DeleteArrowHead(self
, position
, name
):
1388 """Delete arrowhead by position and name.
1390 if position is -1, matches any position.
1392 for arrow
in self
._arcArrows
:
1393 if (position
== -1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1394 self
._arcArrows
.remove(arrow
)
1398 def DeleteArrowHeadId(self
, id):
1399 """Delete arrowhead by id."""
1400 for arrow
in self
._arcArrows
:
1401 if arrowId
== arrow
.GetId():
1402 self
._arcArrows
.remove(arrow
)
1406 # Calculate the minimum width a line
1407 # occupies, for the purposes of drawing lines in tools.
1408 def FindMinimumWidth(self
):
1409 """Find the horizontal width for drawing a line with arrows in
1410 minimum space. Assume arrows at end only.
1413 for arrowHead
in self
._arcArrows
:
1414 minWidth
+= arrowHead
.GetSize()
1415 if arrowHead
!= self
._arcArrows
[-1]:
1416 minWidth
+= arrowHead
+ GetSpacing
1418 # We have ABSOLUTE minimum now. So
1419 # scale it to give it reasonable aesthetics
1420 # when drawing with line.
1422 minWidth
= minWidth
* 1.4
1426 self
.SetEnds(0.0, 0.0, minWidth
, 0.0)
1431 def FindLinePosition(self
, x
, y
):
1432 """Find which position we're talking about at this x, y.
1434 Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
1436 startX
, startY
, endX
, endY
= self
.GetEnds()
1438 # Find distances from centre, start and end. The smallest wins
1439 centreDistance
= math
.sqrt((x
- self
._xpos
) * (x
- self
._xpos
) + (y
- self
._ypos
) * (y
- self
._ypos
))
1440 startDistance
= math
.sqrt((x
- startX
) * (x
- startX
) + (y
- startY
) * (y
- startY
))
1441 endDistance
= math
.sqrt((x
- endX
) * (x
- endX
) + (y
- endY
) * (y
- endY
))
1443 if centreDistance
< startDistance
and centreDistance
< endDistance
:
1444 return ARROW_POSITION_MIDDLE
1445 elif startDistance
< endDistance
:
1446 return ARROW_POSITION_START
1448 return ARROW_POSITION_END
1450 def SetAlignmentOrientation(self
, isEnd
, isHoriz
):
1452 if isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1453 self
._alignmentEnd
!= LINE_ALIGNMENT_HORIZ
1454 elif not isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1455 self
._alignmentEnd
-= LINE_ALIGNMENT_HORIZ
1457 if isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1458 self
._alignmentStart
!= LINE_ALIGNMENT_HORIZ
1459 elif not isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1460 self
._alignmentStart
-= LINE_ALIGNMENT_HORIZ
1462 def SetAlignmentType(self
, isEnd
, alignType
):
1464 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1465 if self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1466 self
._alignmentEnd |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1467 elif self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1468 self
._alignmentEnd
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1470 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1471 if self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1472 self
._alignmentStart |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1473 elif self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1474 self
._alignmentStart
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1476 def GetAlignmentOrientation(self
, isEnd
):
1478 return self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1480 return self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1482 def GetAlignmentType(self
, isEnd
):
1484 return self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1486 return self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1488 def GetNextControlPoint(self
, shape
):
1489 """Find the next control point in the line after the start / end point,
1490 depending on whether the shape is at the start or end.
1492 n
= len(self
._lineControlPoints
)
1493 if self
._to
== shape
:
1494 # Must be END of line, so we want (n - 1)th control point.
1495 # But indexing ends at n-1, so subtract 2.
1499 if nn
< len(self
._lineControlPoints
):
1500 return self
._lineControlPoints
[nn
]
1503 def OnCreateLabelShape(self
, parent
, region
, w
, h
):
1504 return LabelShape(parent
, region
, w
, h
)
1507 def OnLabelMovePre(self
, dc
, labelShape
, x
, y
, old_x
, old_y
, display
):
1508 labelShape
._shapeRegion
.SetSize(labelShape
.GetWidth(), labelShape
.GetHeight())
1510 # Find position in line's region list
1512 for region
in self
.GetRegions():
1513 if labelShape
._shapeRegion
== region
:
1514 self
.GetRegions().remove(region
)
1518 xx
, yy
= self
.GetLabelPosition(i
)
1519 # Set the region's offset, relative to the default position for
1521 labelShape
._shapeRegion
.SetPosition(x
- xx
, y
- yy
)
1525 # Need to reformat to fit region
1526 if labelShape
._shapeRegion
.GetText():
1527 s
= labelShape
._shapeRegion
.GetText()
1528 labelShape
.FormatText(dc
, s
, i
)
1529 self
.DrawRegion(dc
, labelShape
._shapeRegion
, xx
, yy
)