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 #----------------------------------------------------------------------------
14 from __future__
import division
19 from _basic
import Shape
, ShapeRegion
, ControlPoint
, RectangleShape
20 from _oglmisc
import *
22 # Line alignment flags
24 LINE_ALIGNMENT_HORIZ
= 1
25 LINE_ALIGNMENT_VERT
= 0
26 LINE_ALIGNMENT_TO_NEXT_HANDLE
= 2
27 LINE_ALIGNMENT_NONE
= 0
31 class LineControlPoint(ControlPoint
):
32 def __init__(self
, theCanvas
= None, object = None, size
= 0.0, x
= 0.0, y
= 0.0, the_type
= 0):
33 ControlPoint
.__init
__(self
, theCanvas
, object, size
, x
, y
, the_type
)
38 self
._originalPos
= None
41 RectangleShape
.OnDraw(self
, dc
)
43 # Implement movement of Line point
44 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
45 self
._shape
.GetEventHandler().OnSizingDragLeft(self
, draw
, x
, y
, keys
, attachment
)
47 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
48 self
._shape
.GetEventHandler().OnSizingBeginDragLeft(self
, x
, y
, keys
, attachment
)
50 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
51 self
._shape
.GetEventHandler().OnSizingEndDragLeft(self
, x
, y
, keys
, attachment
)
55 class ArrowHead(object):
56 def __init__(self
, type = 0, end
= 0, size
= 0.0, dist
= 0.0, name
="",mf
= None, arrowId
=-1):
57 if isinstance(type, ArrowHead
):
60 self
._arrowType
= type
62 self
._arrowSize
= size
67 self
._arrowName
= name
74 return self
._arrowType
76 def GetPosition(self
):
79 def SetPosition(self
, pos
):
92 return self
._arrowSize
94 def SetSize(self
, size
):
95 self
._arrowSize
= size
96 if self
._arrowType
== ARROW_METAFILE
and self
._metaFile
:
97 oldWidth
= self
._metaFile
._width
101 scale
= size
/ oldWidth
103 self
._metaFile
.Scale(scale
, scale
)
106 return self
._arrowName
108 def SetXOffset(self
, x
):
111 def SetYOffset(self
, y
):
114 def GetMetaFile(self
):
115 return self
._metaFile
120 def GetArrowEnd(self
):
121 return self
._arrowEnd
123 def GetArrowSize(self
):
124 return self
._arrowSize
126 def SetSpacing(self
, sp
):
131 class LabelShape(RectangleShape
):
132 def __init__(self
, parent
, region
, w
, h
):
133 RectangleShape
.__init
__(self
, w
, h
)
134 self
._lineShape
= parent
135 self
._shapeRegion
= region
136 self
.SetPen(wx
.ThePenList
.FindOrCreatePen(wx
.Colour(0, 0, 0), 1, wx
.DOT
))
138 def OnDraw(self
, dc
):
139 if self
._lineShape
and not self
._lineShape
.GetDrawHandles():
142 x1
= self
._xpos
-self
._width
/ 2
143 y1
= self
._ypos
-self
._height
/ 2
146 if self
._pen
.GetWidth() == 0:
147 dc
.SetPen(wx
.Pen(wx
.WHITE
, 1, wx
.TRANSPARENT
))
150 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
152 if self
._cornerRadius
>0:
153 dc
.DrawRoundedRectangle(x1
, y1
, self
._width
, self
._height
, self
._cornerRadius
)
155 dc
.DrawRectangle(x1
, y1
, self
._width
, self
._height
)
157 def OnDrawContents(self
, dc
):
160 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
161 RectangleShape
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
163 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
164 RectangleShape
.OnBeginDragLeft(self
, x
, y
, keys
, attachment
)
166 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
167 RectangleShape
.OnEndDragLeft(self
, x
, y
, keys
, attachment
)
169 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
):
170 return self
._lineShape
.OnLabelMovePre(dc
, self
, x
, y
, old_x
, old_y
, display
)
172 # Divert left and right clicks to line object
173 def OnLeftClick(self
, x
, y
, keys
= 0, attachment
= 0):
174 self
._lineShape
.GetEventHandler().OnLeftClick(x
, y
, keys
, attachment
)
176 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
177 self
._lineShape
.GetEventHandler().OnRightClick(x
, y
, keys
, attachment
)
181 class LineShape(Shape
):
182 """LineShape may be attached to two nodes;
183 it may be segmented, in which case a control point is drawn for each joint.
185 A wxLineShape may have arrows at the beginning, end and centre.
193 self
._sensitivity
= OP_CLICK_LEFT | OP_CLICK_RIGHT
194 self
._draggable
= False
195 self
._attachmentTo
= 0
196 self
._attachmentFrom
= 0
199 self
._erasing
= False
200 self
._arrowSpacing
= 5.0
201 self
._ignoreArrowOffsets
= False
202 self
._isSpline
= False
203 self
._maintainStraightLines
= False
204 self
._alignmentStart
= 0
205 self
._alignmentEnd
= 0
207 self
._lineControlPoints
= None
209 # Clear any existing regions (created in an earlier constructor)
210 # and make the three line regions.
212 for name
in ["Middle","Start","End"]:
213 newRegion
= ShapeRegion()
214 newRegion
.SetName(name
)
215 newRegion
.SetSize(150, 50)
216 self
._regions
.append(newRegion
)
218 self
._labelObjects
= [None, None, None]
219 self
._lineOrientations
= []
220 self
._lineControlPoints
= []
224 if self
._lineControlPoints
:
225 self
.ClearPointList(self
._lineControlPoints
)
226 self
._lineControlPoints
= []
228 if self
._labelObjects
[i
]:
229 self
._labelObjects
[i
].Select(False)
230 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
231 self
._labelObjects
= []
232 self
.ClearArrowsAtPosition(-1)
235 """Return the 'from' object."""
239 """Return the 'to' object."""
242 def GetAttachmentFrom(self
):
243 """Return the attachment point on the 'from' node."""
244 return self
._attachmentFrom
246 def GetAttachmentTo(self
):
247 """Return the attachment point on the 'to' node."""
248 return self
._attachmentTo
250 def GetLineControlPoints(self
):
251 return self
._lineControlPoints
253 def SetSpline(self
, spline
):
254 """Specifies whether a spline is to be drawn through the control points."""
255 self
._isSpline
= spline
258 """TRUE if a spline is drawn through the control points."""
259 return self
._isSpline
261 def SetAttachmentFrom(self
, attach
):
262 """Set the 'from' shape attachment."""
263 self
._attachmentFrom
= attach
265 def SetAttachmentTo(self
, attach
):
266 """Set the 'to' shape attachment."""
267 self
._attachmentTo
= attach
269 # This is really to distinguish between lines and other images.
270 # For lines, want to pass drag to canvas, since lines tend to prevent
271 # dragging on a canvas (they get in the way.)
275 def SetIgnoreOffsets(self
, ignore
):
276 """Set whether to ignore offsets from the end of the line when drawing."""
277 self
._ignoreArrowOffsets
= ignore
280 return self
._arcArrows
282 def GetAlignmentStart(self
):
283 return self
._alignmentStart
285 def GetAlignmentEnd(self
):
286 return self
._alignmentEnd
288 def IsEnd(self
, nodeObject
):
289 """TRUE if shape is at the end of the line."""
290 return self
._to
== nodeObject
292 def MakeLineControlPoints(self
, n
):
293 """Make a given number of control points (minimum of two)."""
294 if self
._lineControlPoints
:
295 self
.ClearPointList(self
._lineControlPoints
)
296 self
._lineControlPoints
= []
299 point
= wx
.RealPoint(-999,-999)
300 self
._lineControlPoints
.append(point
)
302 def InsertLineControlPoint(self
, dc
= None):
303 """Insert a control point at an arbitrary position."""
307 last_point
= self
._lineControlPoints
[-1]
308 second_last_point
= self
._lineControlPoints
[-2]
310 line_x
= (last_point
[0] + second_last_point
[0]) / 2
311 line_y
= (last_point
[1] + second_last_point
[1]) / 2
313 point
= wx
.RealPoint(line_x
, line_y
)
314 self
._lineControlPoints
.insert(len(self
._lineControlPoints
), point
)
316 def DeleteLineControlPoint(self
):
317 """Delete an arbitary point on the line."""
318 if len(self
._lineControlPoints
)<3:
321 del self
._lineControlPoints
[-2]
324 def Initialise(self
):
325 """Initialise the line object."""
326 if self
._lineControlPoints
:
327 # Just move the first and last control points
328 first_point
= self
._lineControlPoints
[0]
329 last_point
= self
._lineControlPoints
[-1]
331 # If any of the line points are at -999, we must
332 # initialize them by placing them half way between the first
335 for point
in self
._lineControlPoints
[1:]:
337 if first_point
[0]<last_point
[0]:
343 if first_point
[1]<last_point
[1]:
349 point
[0] = (x2
-x1
) / 2 + x1
350 point
[1] = (y2
-y1
) / 2 + y1
352 def FormatText(self
, dc
, s
, i
):
353 """Format a text string according to the region size, adding
354 strings with positions to region text list.
358 if len(self
._regions
) == 0 or i
>= len(self
._regions
):
361 region
= self
._regions
[i
]
363 dc
.SetFont(region
.GetFont())
365 w
, h
= region
.GetSize()
366 # Initialize the size if zero
367 if (w
== 0 or h
== 0) and s
:
371 string_list
= FormatText(dc
, s
, w
-5, h
-5, region
.GetFormatMode())
372 for s
in string_list
:
373 line
= ShapeTextLine(0.0, 0.0, s
)
374 region
.GetFormattedText().append(line
)
378 if region
.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS
:
379 actualW
, actualH
= GetCentredTextExtent(dc
, region
.GetFormattedText(), self
._xpos
, self
._ypos
, w
, h
)
380 if actualW
!= w
or actualH
!= h
:
381 xx
, yy
= self
.GetLabelPosition(i
)
382 self
.EraseRegion(dc
, region
, xx
, yy
)
383 if len(self
._labelObjects
)<i
:
384 self
._labelObjects
[i
].Select(False, dc
)
385 self
._labelObjects
[i
].Erase(dc
)
386 self
._labelObjects
[i
].SetSize(actualW
, actualH
)
388 region
.SetSize(actualW
, actualH
)
390 if len(self
._labelObjects
)<i
:
391 self
._labelObjects
[i
].Select(True, dc
)
392 self
._labelObjects
[i
].Draw(dc
)
394 CentreText(dc
, region
.GetFormattedText(), self
._xpos
, self
._ypos
, actualW
, actualH
, region
.GetFormatMode())
395 self
._formatted
= True
397 def DrawRegion(self
, dc
, region
, x
, y
):
398 """Format one region at this position."""
399 if self
.GetDisableLabel():
402 w
, h
= region
.GetSize()
404 # Get offset from x, y
405 xx
, yy
= region
.GetPosition()
410 # First, clear a rectangle for the text IF there is any
411 if len(region
.GetFormattedText()):
412 dc
.SetPen(self
.GetBackgroundPen())
413 dc
.SetBrush(self
.GetBackgroundBrush())
417 dc
.SetFont(region
.GetFont())
418 dc
.DrawRectangle(xp
-w
/ 2, yp
-h
/ 2, w
, h
)
422 dc
.SetTextForeground(region
.GetActualColourObject())
424 DrawFormattedText(dc
, region
.GetFormattedText(), xp
, yp
, w
, h
, region
.GetFormatMode())
426 def EraseRegion(self
, dc
, region
, x
, y
):
427 """Erase one region at this position."""
428 if self
.GetDisableLabel():
431 w
, h
= region
.GetSize()
433 # Get offset from x, y
434 xx
, yy
= region
.GetPosition()
439 if region
.GetFormattedText():
440 dc
.SetPen(self
.GetBackgroundPen())
441 dc
.SetBrush(self
.GetBackgroundBrush())
443 dc
.DrawRectangle(xp
-w
/ 2, yp
-h
/ 2, w
, h
)
445 def GetLabelPosition(self
, position
):
446 """Get the reference point for a label.
448 Region x and y are offsets from this.
449 position is 0 (middle), 1 (start), 2 (end).
452 # Want to take the middle section for the label
453 half_way
= int(len(self
._lineControlPoints
) / 2)
455 # Find middle of this line
456 point
= self
._lineControlPoints
[half_way
-1]
457 next_point
= self
._lineControlPoints
[half_way
]
459 dx
= next_point
[0]-point
[0]
460 dy
= next_point
[1]-point
[1]
462 return point
[0] + dx
/ 2, point
[1] + dy
/ 2
464 return self
._lineControlPoints
[0][0], self
._lineControlPoints
[0][1]
466 return self
._lineControlPoints
[-1][0], self
._lineControlPoints
[-1][1]
468 def Straighten(self
, dc
= None):
469 """Straighten verticals and horizontals."""
470 if len(self
._lineControlPoints
)<3:
476 GraphicsStraightenLine(self
._lineControlPoints
[-1], self
._lineControlPoints
[-2])
478 for i
in range(len(self
._lineControlPoints
)-2):
479 GraphicsStraightenLine(self
._lineControlPoints
[i
], self
._lineControlPoints
[i
+ 1])
485 """Unlink the line from the nodes at either end."""
487 self
._to
.GetLines().remove(self
)
489 self
._from
.GetLines().remove(self
)
493 def SetEnds(self
, x1
, y1
, x2
, y2
):
494 """Set the end positions of the line."""
496 first_point
= self
._lineControlPoints
[0]
497 last_point
= self
._lineControlPoints
[-1]
504 self
._xpos
= (x1
+ x2
) / 2
505 self
._ypos
= (y1
+ y2
) / 2
507 # Get absolute positions of ends
509 """Get the visible endpoints of the lines for drawing between two objects."""
510 first_point
= self
._lineControlPoints
[0]
511 last_point
= self
._lineControlPoints
[-1]
513 return (first_point
[0], first_point
[1]), (last_point
[0], last_point
[1])
515 def SetAttachments(self
, from_attach
, to_attach
):
516 """Specify which object attachment points should be used at each end
519 self
._attachmentFrom
= from_attach
520 self
._attachmentTo
= to_attach
522 def HitTest(self
, x
, y
):
523 if not self
._lineControlPoints
:
526 # Look at label regions in case mouse is over a label
527 inLabelRegion
= False
530 region
= self
._regions
[i
]
531 if len(region
._formattedText
):
532 xp
, yp
= self
.GetLabelPosition(i
)
533 # Offset region from default label position
534 cx
, cy
= region
.GetPosition()
535 cw
, ch
= region
.GetSize()
542 rBottom
= cy
+ ch
/ 2
543 if x
>rLeft
and x
<rRight
and y
>rTop
and y
<rBottom
:
547 for i
in range(len(self
._lineControlPoints
)-1):
548 point1
= self
._lineControlPoints
[i
]
549 point2
= self
._lineControlPoints
[i
+ 1]
551 # For inaccurate mousing allow 8 pixel corridor
554 dx
= point2
[0]-point1
[0]
555 dy
= point2
[1]-point1
[1]
557 seg_len
= math
.sqrt(dx
* dx
+ dy
* dy
)
558 if dy
== 0 or dx
== 0:
560 distance_from_seg
= seg_len
* ((x
-point1
[0]) * dy
-(y
-point1
[1]) * dx
) / (dy
* dy
+ dx
* dx
)
561 distance_from_prev
= seg_len
* ((y
-point1
[1]) * dy
+ (x
-point1
[0]) * dx
) / (dy
* dy
+ dx
* dx
)
563 if abs(distance_from_seg
)<extra
and distance_from_prev
>= 0 and distance_from_prev
<= seg_len
or inLabelRegion
:
564 return 0, distance_from_seg
568 def DrawArrows(self
, dc
):
569 """Draw all arrows."""
570 # Distance along line of each arrow: space them out evenly
575 for arrow
in self
._arcArrows
:
576 ah
= arrow
.GetArrowEnd()
577 if ah
== ARROW_POSITION_START
:
578 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
579 # If specified, x offset is proportional to line length
580 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
582 self
.DrawArrow(dc
, arrow
, startArrowPos
, False)
583 startArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
584 elif ah
== ARROW_POSITION_END
:
585 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
586 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
588 self
.DrawArrow(dc
, arrow
, endArrowPos
, False)
589 endArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
590 elif ah
== ARROW_POSITION_MIDDLE
:
591 arrow
.SetXOffset(middleArrowPos
)
592 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
593 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
595 self
.DrawArrow(dc
, arrow
, middleArrowPos
, False)
596 middleArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
598 def DrawArrow(self
, dc
, arrow
, XOffset
, proportionalOffset
):
599 """Draw the given arrowhead (or annotation)."""
600 first_line_point
= self
._lineControlPoints
[0]
601 second_line_point
= self
._lineControlPoints
[1]
603 last_line_point
= self
._lineControlPoints
[-1]
604 second_last_line_point
= self
._lineControlPoints
[-2]
606 # Position of start point of line, at the end of which we draw the arrow
607 startPositionX
, startPositionY
= 0.0, 0.0
609 ap
= arrow
.GetPosition()
610 if ap
== ARROW_POSITION_START
:
611 # If we're using a proportional offset, calculate just where this
612 # will be on the line.
614 if proportionalOffset
:
615 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]))
616 realOffset
= XOffset
* totalLength
618 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_line_point
[0], second_line_point
[1], first_line_point
[0], first_line_point
[1], realOffset
)
620 startPositionX
= second_line_point
[0]
621 startPositionY
= second_line_point
[1]
622 elif ap
== ARROW_POSITION_END
:
623 # If we're using a proportional offset, calculate just where this
624 # will be on the line.
626 if proportionalOffset
:
627 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]));
628 realOffset
= XOffset
* totalLength
630 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], last_line_point
[0], last_line_point
[1], realOffset
)
632 startPositionX
= second_last_line_point
[0]
633 startPositionY
= second_last_line_point
[1]
634 elif ap
== ARROW_POSITION_MIDDLE
:
635 # Choose a point half way between the last and penultimate points
636 x
= (last_line_point
[0] + second_last_line_point
[0]) / 2
637 y
= (last_line_point
[1] + second_last_line_point
[1]) / 2
639 # If we're using a proportional offset, calculate just where this
640 # will be on the line.
642 if proportionalOffset
:
643 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
));
644 realOffset
= XOffset
* totalLength
646 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], x
, y
, realOffset
)
647 startPositionX
= second_last_line_point
[0]
648 startPositionY
= second_last_line_point
[1]
650 # Add yOffset to arrow, if any
652 # The translation that the y offset may give
655 if arrow
.GetYOffset
and not self
._ignoreArrowOffsets
:
659 # (x1, y1)--------------(x3, y3)------------------(x2, y2)
660 # x4 = x3 - d * math.sin(theta)
661 # y4 = y3 + d * math.cos(theta)
663 # Where theta = math.tan(-1) of (y3-y1) / (x3-x1)
668 d
=-arrow
.GetYOffset() # Negate so +offset is above line
673 theta
= math
.atan((y3
-y1
) / (x3
-x1
))
675 x4
= x3
-d
* math
.sin(theta
)
676 y4
= y3
+ d
* math
.cos(theta
)
678 deltaX
= x4
-positionOnLineX
679 deltaY
= y4
-positionOnLineY
681 at
= arrow
._GetType
()
682 if at
== ARROW_ARROW
:
683 arrowLength
= arrow
.GetSize()
684 arrowWidth
= arrowLength
/ 3
686 tip_x
, tip_y
, side1_x
, side1_y
, side2_x
, side2_y
= GetArrowPoints(startPositionX
+ deltaX
, startPositionY
+ deltaY
, positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
, arrowLength
, arrowWidth
)
688 points
= [[tip_x
, tip_y
],
694 dc
.SetBrush(self
._brush
)
695 dc
.DrawPolygon(points
)
696 elif at
in [ARROW_HOLLOW_CIRCLE
, ARROW_FILLED_CIRCLE
]:
697 # Find point on line of centre of circle, which is a radius away
698 # from the end position
699 diameter
= arrow
.GetSize()
700 x
, y
= GetPointOnLine(startPositionX
+ deltaX
, startPositionY
+ deltaY
,
701 positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
,
706 if arrow
._GetType
() == ARROW_HOLLOW_CIRCLE
:
707 dc
.SetBrush(self
.GetBackgroundBrush())
709 dc
.SetBrush(self
._brush
)
711 dc
.DrawEllipse(x1
, y1
, diameter
, diameter
)
712 elif at
== ARROW_SINGLE_OBLIQUE
:
714 elif at
== ARROW_METAFILE
:
715 if arrow
.GetMetaFile():
716 # Find point on line of centre of object, which is a half-width away
717 # from the end position
720 # <-- start pos <-----><-- positionOnLineX
722 # --------------| x | <-- e.g. rectangular arrowhead
725 x
, y
= GetPointOnLine(startPositionX
, startPositionY
,
726 positionOnLineX
, positionOnLineY
,
727 arrow
.GetMetaFile()._width
/ 2)
728 # Calculate theta for rotating the metafile.
731 # | o(x2, y2) 'o' represents the arrowhead.
736 # |______________________
744 if x1
== x2
and y1
== y2
:
746 elif x1
== x2
and y1
>y2
:
747 theta
= 3.0 * math
.pi
/ 2
748 elif x1
== x2
and y2
>y1
:
750 elif x2
>x1
and y2
>= y1
:
751 theta
= math
.atan((y2
-y1
) / (x2
-x1
))
753 theta
= math
.pi
+ math
.atan((y2
-y1
) / (x2
-x1
))
754 elif x2
>x1
and y2
<y1
:
755 theta
= 2 * math
.pi
+ math
.atan((y2
-y1
) / (x2
-x1
))
757 raise "Unknown arrowhead rotation case"
759 # Rotate about the centre of the object, then place
760 # the object on the line.
761 if arrow
.GetMetaFile().GetRotateable():
762 arrow
.GetMetaFile().Rotate(0.0, 0.0, theta
)
765 # If erasing, just draw a rectangle
766 minX
, minY
, maxX
, maxY
= arrow
.GetMetaFile().GetBounds()
767 # Make erasing rectangle slightly bigger or you get droppings
769 dc
.DrawRectangle(deltaX
+ x
+ minX
-extraPixels
/ 2, deltaY
+ y
+ minY
-extraPixels
/ 2, maxX
-minX
+ extraPixels
, maxY
-minY
+ extraPixels
)
771 arrow
.GetMetaFile().Draw(dc
, x
+ deltaX
, y
+ deltaY
)
773 def OnErase(self
, dc
):
775 old_brush
= self
._brush
777 bg_pen
= self
.GetBackgroundPen()
778 bg_brush
= self
.GetBackgroundBrush()
780 self
.SetBrush(bg_brush
)
782 bound_x
, bound_y
= self
.GetBoundingBoxMax()
784 dc
.SetFont(self
._font
)
786 # Undraw text regions
789 x
, y
= self
.GetLabelPosition(i
)
790 self
.EraseRegion(dc
, self
._regions
[i
], x
, y
)
793 dc
.SetPen(self
.GetBackgroundPen())
794 dc
.SetBrush(self
.GetBackgroundBrush())
796 # Drawing over the line only seems to work if the line has a thickness
798 if old_pen
and old_pen
.GetWidth()>1:
799 dc
.DrawRectangle(self
._xpos
-bound_x
/ 2-2, self
._ypos
-bound_y
/ 2-2,
800 bound_x
+ 4, bound_y
+ 4)
803 self
.GetEventHandler().OnDraw(dc
)
804 self
.GetEventHandler().OnEraseControlPoints(dc
)
805 self
._erasing
= False
810 self
.SetBrush(old_brush
)
812 def GetBoundingBoxMin(self
):
813 x1
, y1
= 10000, 10000
816 for point
in self
._lineControlPoints
:
828 # For a node image of interest, finds the position of this arc
829 # amongst all the arcs which are attached to THIS SIDE of the node image,
830 # and the number of same.
831 def FindNth(self
, image
, incoming
):
832 """Find the position of the line on the given object.
834 Specify whether incoming or outgoing lines are being considered
840 if image
== self
._to
:
841 this_attachment
= self
._attachmentTo
843 this_attachment
= self
._attachmentFrom
845 # Find number of lines going into / out of this particular attachment point
846 for line
in image
.GetLines():
847 if line
._from
== image
:
848 # This is the nth line attached to 'image'
849 if line
== self
and not incoming
:
852 # Increment num count if this is the same side (attachment number)
853 if line
._attachmentFrom
== this_attachment
:
856 if line
._to
== image
:
857 # This is the nth line attached to 'image'
858 if line
== self
and incoming
:
861 # Increment num count if this is the same side (attachment number)
862 if line
._attachmentTo
== this_attachment
:
867 def OnDrawOutline(self
, dc
, x
, y
, w
, h
):
869 old_brush
= self
._brush
871 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
872 self
.SetPen(dottedPen
)
873 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
875 self
.GetEventHandler().OnDraw(dc
)
882 self
.SetBrush(old_brush
)
886 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
= True):
890 if self
._lineControlPoints
and not (x_offset
== 0 and y_offset
== 0):
891 for point
in self
._lineControlPoints
:
895 # Move temporary label rectangles if necessary
897 if self
._labelObjects
[i
]:
898 self
._labelObjects
[i
].Erase(dc
)
899 xp
, yp
= self
.GetLabelPosition(i
)
900 if i
<len(self
._regions
):
901 xr
, yr
= self
._regions
[i
].GetPosition()
904 self
._labelObjects
[i
].Move(dc
, xp
+ xr
, yp
+ yr
)
907 def OnMoveLink(self
, dc
, moveControlPoints
= True):
908 """Called when a connected object has moved, to move the link to
911 if not self
._from
or not self
._to
:
914 if len(self
._lineControlPoints
)>2:
917 # Do each end - nothing in the middle. User has to move other points
918 # manually if necessary
919 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
921 first
= self
._lineControlPoints
[0]
922 last
= self
._lineControlPoints
[-1]
924 oldX
, oldY
= self
._xpos
, self
._ypos
926 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
928 # Do a second time, because one may depend on the other
929 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
930 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
932 # Try to move control points with the arc
933 x_offset
= self
._xpos
-oldX
934 y_offset
= self
._ypos
-oldY
936 # Only move control points if it's a self link. And only works
937 # if attachment mode is ON
938 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):
939 for point
in self
._lineControlPoints
[1:-1]:
943 self
.Move(dc
, self
._xpos
, self
._ypos
)
945 def FindLineEndPoints(self
):
946 """Finds the x, y points at the two ends of the line.
948 This function can be used by e.g. line-routing routines to
949 get the actual points on the two node images where the lines will be
952 if not self
._from
or not self
._to
:
955 # Do each end - nothing in the middle. User has to move other points
956 # manually if necessary.
957 second_point
= self
._lineControlPoints
[1]
958 second_last_point
= self
._lineControlPoints
[-2]
960 if len(self
._lineControlPoints
)>2:
961 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
962 nth
, no_arcs
= self
.FindNth(self
._from
, False) # Not incoming
963 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
965 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), second_point
[0], second_point
[1])
967 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
968 nth
, no_arch
= self
.FindNth(self
._to
, True) # Incoming
969 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arch
, self
)
971 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), second_last_point
[0], second_last_point
[1])
973 fromX
= self
._from
.GetX()
974 fromY
= self
._from
.GetY()
975 toX
= self
._to
.GetX()
976 toY
= self
._to
.GetY()
978 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
979 nth
, no_arcs
= self
.FindNth(self
._from
, False)
980 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
984 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
985 nth
, no_arcs
= self
.FindNth(self
._to
, True)
986 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arcs
, self
)
990 if self
._from
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
991 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), toX
, toY
)
993 if self
._to
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
994 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), fromX
, fromY
)
996 #print type(self._from), type(self._to), end_x, end_y, other_end_x, other_end_y
997 return end_x
, end_y
, other_end_x
, other_end_y
999 def OnDraw(self
, dc
):
1000 if not self
._lineControlPoints
:
1004 dc
.SetPen(self
._pen
)
1006 dc
.SetBrush(self
._brush
)
1009 for point
in self
._lineControlPoints
:
1010 points
.append(wx
.Point(point
.x
, point
.y
))
1014 dc
.DrawSpline(points
)
1016 dc
.DrawLines(points
)
1018 if sys
.platform
[:3]=="win":
1019 # For some reason, last point isn't drawn under Windows
1021 dc
.DrawPoint(pt
.x
, pt
.y
)
1023 # Problem with pen - if not a solid pen, does strange things
1024 # to the arrowhead. So make (get) a new pen that's solid.
1025 if self
._pen
and self
._pen
.GetStyle() != wx
.SOLID
:
1026 solid_pen
= wx
.ThePenList().FindOrCreatePen(self
._pen
.GetColour(), 1, wx
.SOLID
)
1028 dc
.SetPen(solid_pen
)
1032 def OnDrawControlPoints(self
, dc
):
1033 if not self
._drawHandles
:
1036 # Draw temporary label rectangles if necessary
1038 if self
._labelObjects
[i
]:
1039 self
._labelObjects
[i
].Draw(dc
)
1041 Shape
.OnDrawControlPoints(self
, dc
)
1043 def OnEraseControlPoints(self
, dc
):
1044 # Erase temporary label rectangles if necessary
1047 if self
._labelObjects
[i
]:
1048 self
._labelObjects
[i
].Erase(dc
)
1050 Shape
.OnEraseControlPoints(self
, dc
)
1052 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1055 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1058 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1061 def OnDrawContents(self
, dc
):
1062 if self
.GetDisableLabel():
1066 if self
._regions
[i
]:
1067 x
, y
= self
.GetLabelPosition(i
)
1068 self
.DrawRegion(dc
, self
._regions
[i
], x
, y
)
1070 def SetTo(self
, object):
1071 """Set the 'to' object for the line."""
1074 def SetFrom(self
, object):
1075 """Set the 'from' object for the line."""
1078 def MakeControlPoints(self
):
1079 """Make handle control points."""
1080 if self
._canvas
and self
._lineControlPoints
:
1081 first
= self
._lineControlPoints
[0]
1082 last
= self
._lineControlPoints
[-1]
1084 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, first
[0], first
[1], CONTROL_POINT_ENDPOINT_FROM
)
1085 control
._point
= first
1086 self
._canvas
.AddShape(control
)
1087 self
._controlPoints
.Append(control
)
1089 for point
in self
._lineControlPoints
[1:-1]:
1090 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, point
[0], point
[1], CONTROL_POINT_LINE
)
1091 control
._point
= point
1092 self
._canvas
.AddShape(control
)
1093 self
._controlPoints
.Append(control
)
1095 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, last
[0], last
[1], CONTROL_POINT_ENDPOINT_TO
)
1096 control
._point
= last
1097 self
._canvas
.AddShape(control
)
1098 self
._controlPoints
.Append(control
)
1100 def ResetControlPoints(self
):
1101 if self
._canvas
and self
._lineControlPoints
:
1102 for i
in range(min(len(self
._controlPoints
), len(self
._lineControlPoints
))):
1103 point
= self
._lineControlPoints
[i
]
1104 control
= self
._controlPoints
[i
]
1105 control
.SetX(point
[0])
1106 control
.SetY(point
[1])
1108 # Override select, to create / delete temporary label-moving objects
1109 def Select(self
, select
, dc
= None):
1110 Shape
.Select(self
, select
, dc
)
1113 if self
._regions
[i
]:
1114 region
= self
._regions
[i
]
1115 if region
._formattedText
:
1116 w
, h
= region
.GetSize()
1117 x
, y
= region
.GetPosition()
1118 xx
, yy
= self
.GetLabelPosition(i
)
1120 if self
._labelObjects
[i
]:
1121 self
._labelObjects
[i
].Select(False)
1122 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1124 self
._labelObjects
[i
] = self
.OnCreateLabelShape(self
, region
, w
, h
)
1125 self
._labelObjects
[i
].AddToCanvas(self
._canvas
)
1126 self
._labelObjects
[i
].Show(True)
1128 self
._labelObjects
[i
].Move(dc
, x
+ xx
, y
+ yy
)
1129 self
._labelObjects
[i
].Select(True, dc
)
1132 if self
._labelObjects
[i
]:
1133 self
._labelObjects
[i
].Select(False, dc
)
1134 self
._labelObjects
[i
].Erase(dc
)
1135 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1136 self
._labelObjects
[i
] = None
1138 # Control points ('handles') redirect control to the actual shape, to
1139 # make it easier to override sizing behaviour.
1140 def OnSizingDragLeft(self
, pt
, draw
, x
, y
, keys
= 0, attachment
= 0):
1141 dc
= wx
.ClientDC(self
.GetCanvas())
1142 self
.GetCanvas().PrepareDC(dc
)
1144 dc
.SetLogicalFunction(OGLRBLF
)
1146 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1147 dc
.SetPen(dottedPen
)
1148 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1150 if pt
._type
== CONTROL_POINT_LINE
:
1151 x
, y
= self
._canvas
.Snap()
1158 old_pen
= self
.GetPen()
1159 old_brush
= self
.GetBrush()
1161 self
.SetPen(dottedPen
)
1162 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1164 self
.GetEventHandler().OnMoveLink(dc
, False)
1166 self
.SetPen(old_pen
)
1167 self
.SetBrush(old_brush
)
1169 def OnSizingBeginDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1170 dc
= wx
.ClientDC(self
.GetCanvas())
1171 self
.GetCanvas().PrepareDC(dc
)
1173 if pt
._type
== CONTROL_POINT_LINE
:
1174 pt
._originalPos
= pt
._point
1175 x
, y
= self
._canvas
.Snap()
1179 # Redraw start and end objects because we've left holes
1180 # when erasing the line
1181 self
.GetFrom().OnDraw(dc
)
1182 self
.GetFrom().OnDrawContents(dc
)
1183 self
.GetTo().OnDraw(dc
)
1184 self
.GetTo().OnDrawContents(dc
)
1186 self
.SetDisableLabel(True)
1187 dc
.SetLogicalFunction(OGLRBLF
)
1194 old_pen
= self
.GetPen()
1195 old_brush
= self
.GetBrush()
1197 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1198 self
.SetPen(dottedPen
)
1199 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1201 self
.GetEventHandler().OnMoveLink(dc
, False)
1203 self
.SetPen(old_pen
)
1204 self
.SetBrush(old_brush
)
1206 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
or pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1207 self
._canvas
.SetCursor(wx
.Cursor(wx
.CURSOR_BULLSEYE
))
1208 pt
._oldCursor
= wx
.STANDARD_CURSOR
1210 def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1211 dc
= wx
.ClientDC(self
.GetCanvas())
1212 self
.GetCanvas().PrepareDC(dc
)
1214 self
.SetDisableLabel(False)
1216 if pt
._type
== CONTROL_POINT_LINE
:
1217 x
, y
= self
._canvas
.Snap()
1219 rpt
= wx
.RealPoint(x
, y
)
1221 # Move the control point back to where it was;
1222 # MoveControlPoint will move it to the new position
1223 # if it decides it wants. We only moved the position
1224 # during user feedback so we could redraw the line
1225 # as it changed shape.
1226 pt
._xpos
= pt
._originalPos
[0]
1227 pt
._ypos
= pt
._originalPos
[1]
1228 pt
._point
[0] = pt
._originalPos
[0]
1229 pt
._point
[1] = pt
._originalPos
[1]
1231 self
.OnMoveMiddleControlPoint(dc
, pt
, rpt
)
1233 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
:
1235 self
._canvas
.SetCursor(pt
._oldCursor
)
1238 self
.GetFrom().MoveLineToNewAttachment(dc
, self
, x
, y
)
1240 if pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1242 self
._canvas
.SetCursor(pt
._oldCursor
)
1245 self
.GetTo().MoveLineToNewAttachment(dc
, self
, x
, y
)
1247 # This is called only when a non-end control point is moved
1248 def OnMoveMiddleControlPoint(self
, dc
, lpt
, pt
):
1252 lpt
._point
[0] = pt
[0]
1253 lpt
._point
[1] = pt
[1]
1255 self
.GetEventHandler().OnMoveLink(dc
)
1259 def AddArrow(self
, type, end
= ARROW_POSITION_END
, size
= 10.0, xOffset
= 0.0, name
="",mf
= None, arrowId
=-1):
1260 """Add an arrow (or annotation) to the line.
1262 type may currently be one of:
1269 Conventional arrowhead.
1270 ARROW_SINGLE_OBLIQUE
1271 Single oblique stroke.
1272 ARROW_DOUBLE_OBLIQUE
1273 Double oblique stroke.
1274 ARROW_DOUBLE_METAFILE
1277 end may currently be one of:
1280 Arrow appears at the end.
1281 ARROW_POSITION_START
1282 Arrow appears at the start.
1284 arrowSize specifies the length of the arrow.
1286 xOffset specifies the offset from the end of the line.
1288 name specifies a name for the arrow.
1290 mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
1293 arrowId is the id for the arrow.
1295 arrow
= ArrowHead(type, end
, size
, xOffset
, name
, mf
, arrowId
)
1296 self
._arcArrows
.append(arrow
)
1299 # Add arrowhead at a particular position in the arrowhead list
1300 def AddArrowOrdered(self
, arrow
, referenceList
, end
):
1301 """Add an arrowhead in the position indicated by the reference list
1302 of arrowheads, which contains all legal arrowheads for this line, in
1303 the correct order. E.g.
1305 Reference list: a b c d e
1306 Current line list: a d
1308 Add c, then line list is: a c d.
1310 If no legal arrowhead position, return FALSE. Assume reference list
1311 is for one end only, since it potentially defines the ordering for
1312 any one of the 3 positions. So we don't check the reference list for
1315 if not referenceList
:
1318 targetName
= arrow
.GetName()
1320 # First check whether we need to insert in front of list,
1321 # because this arrowhead is the first in the reference
1322 # list and should therefore be first in the current list.
1323 refArrow
= referenceList
[0]
1324 if refArrow
.GetName() == targetName
:
1325 self
._arcArrows
.insert(0, arrow
)
1329 while i1
<len(referenceList
) and i2
<len(self
._arcArrows
):
1330 refArrow
= referenceList
[i1
]
1331 currArrow
= self
._arcArrows
[i2
]
1333 # Matching: advance current arrow pointer
1334 if currArrow
.GetArrowEnd() == end
and currArrow
.GetName() == refArrow
.GetName():
1337 # Check if we're at the correct position in the
1339 if targetName
== refArrow
.GetName():
1340 if i2
<len(self
._arcArrows
):
1341 self
._arcArrows
.insert(i2
, arrow
)
1343 self
._arcArrows
.append(arrow
)
1347 self
._arcArrows
.append(arrow
)
1350 def ClearArrowsAtPosition(self
, end
):
1351 """Delete the arrows at the specified position, or at any position
1355 self
._arcArrows
= []
1358 for arrow
in self
._arcArrows
:
1359 if arrow
.GetArrowEnd() == end
:
1360 self
._arcArrows
.remove(arrow
)
1362 def ClearArrow(self
, name
):
1363 """Delete the arrow with the given name."""
1364 for arrow
in self
._arcArrows
:
1365 if arrow
.GetName() == name
:
1366 self
._arcArrows
.remove(arrow
)
1370 def FindArrowHead(self
, position
, name
):
1371 """Find arrowhead by position and name.
1373 if position is -1, matches any position.
1375 for arrow
in self
._arcArrows
:
1376 if (position
==-1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1381 def FindArrowHeadId(self
, arrowId
):
1382 """Find arrowhead by id."""
1383 for arrow
in self
._arcArrows
:
1384 if arrowId
== arrow
.GetId():
1389 def DeleteArrowHead(self
, position
, name
):
1390 """Delete arrowhead by position and name.
1392 if position is -1, matches any position.
1394 for arrow
in self
._arcArrows
:
1395 if (position
==-1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1396 self
._arcArrows
.remove(arrow
)
1400 def DeleteArrowHeadId(self
, id):
1401 """Delete arrowhead by id."""
1402 for arrow
in self
._arcArrows
:
1403 if arrowId
== arrow
.GetId():
1404 self
._arcArrows
.remove(arrow
)
1408 # Calculate the minimum width a line
1409 # occupies, for the purposes of drawing lines in tools.
1410 def FindMinimumWidth(self
):
1411 """Find the horizontal width for drawing a line with arrows in
1412 minimum space. Assume arrows at end only.
1415 for arrowHead
in self
._arcArrows
:
1416 minWidth
+= arrowHead
.GetSize()
1417 if arrowHead
!= self
._arcArrows
[-1]:
1418 minWidth
+= arrowHead
+ GetSpacing
1420 # We have ABSOLUTE minimum now. So
1421 # scale it to give it reasonable aesthetics
1422 # when drawing with line.
1424 minWidth
= minWidth
* 1.4
1428 self
.SetEnds(0.0, 0.0, minWidth
, 0.0)
1433 def FindLinePosition(self
, x
, y
):
1434 """Find which position we're talking about at this x, y.
1436 Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
1438 startX
, startY
, endX
, endY
= self
.GetEnds()
1440 # Find distances from centre, start and end. The smallest wins
1441 centreDistance
= math
.sqrt((x
-self
._xpos
) * (x
-self
._xpos
) + (y
-self
._ypos
) * (y
-self
._ypos
))
1442 startDistance
= math
.sqrt((x
-startX
) * (x
-startX
) + (y
-startY
) * (y
-startY
))
1443 endDistance
= math
.sqrt((x
-endX
) * (x
-endX
) + (y
-endY
) * (y
-endY
))
1445 if centreDistance
<startDistance
and centreDistance
<endDistance
:
1446 return ARROW_POSITION_MIDDLE
1447 elif startDistance
<endDistance
:
1448 return ARROW_POSITION_START
1450 return ARROW_POSITION_END
1452 def SetAlignmentOrientation(self
, isEnd
, isHoriz
):
1454 if isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1455 self
._alignmentEnd
!= LINE_ALIGNMENT_HORIZ
1456 elif not isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1457 self
._alignmentEnd
-= LINE_ALIGNMENT_HORIZ
1459 if isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1460 self
._alignmentStart
!= LINE_ALIGNMENT_HORIZ
1461 elif not isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1462 self
._alignmentStart
-= LINE_ALIGNMENT_HORIZ
1464 def SetAlignmentType(self
, isEnd
, alignType
):
1466 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1467 if self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1468 self
._alignmentEnd |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1469 elif self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1470 self
._alignmentEnd
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1472 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1473 if self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1474 self
._alignmentStart |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1475 elif self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1476 self
._alignmentStart
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1478 def GetAlignmentOrientation(self
, isEnd
):
1480 return self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1482 return self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1484 def GetAlignmentType(self
, isEnd
):
1486 return self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1488 return self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1490 def GetNextControlPoint(self
, shape
):
1491 """Find the next control point in the line after the start / end point,
1492 depending on whether the shape is at the start or end.
1494 n
= len(self
._lineControlPoints
)
1495 if self
._to
== shape
:
1496 # Must be END of line, so we want (n - 1)th control point.
1497 # But indexing ends at n-1, so subtract 2.
1501 if nn
<len(self
._lineControlPoints
):
1502 return self
._lineControlPoints
[nn
]
1505 def OnCreateLabelShape(self
, parent
, region
, w
, h
):
1506 return LabelShape(parent
, region
, w
, h
)
1509 def OnLabelMovePre(self
, dc
, labelShape
, x
, y
, old_x
, old_y
, display
):
1510 labelShape
._shapeRegion
.SetSize(labelShape
.GetWidth(), labelShape
.GetHeight())
1512 # Find position in line's region list
1514 for region
in self
.GetRegions():
1515 if labelShape
._shapeRegion
== region
:
1516 self
.GetRegions().remove(region
)
1520 xx
, yy
= self
.GetLabelPosition(i
)
1521 # Set the region's offset, relative to the default position for
1523 labelShape
._shapeRegion
.SetPosition(x
-xx
, y
-yy
)
1527 # Need to reformat to fit region
1528 if labelShape
._shapeRegion
.GetText():
1529 s
= labelShape
._shapeRegion
.GetText()
1530 labelShape
.FormatText(dc
, s
, i
)
1531 self
.DrawRegion(dc
, labelShape
._shapeRegion
, xx
, yy
)