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 i
in range(1,len(self
._lineControlPoints
)):
334 point
= self
._lineControlPoints
[i
]
336 if first_point
[0] < last_point
[0]:
342 if first_point
[1] < last_point
[1]:
348 self
._lineControlPoints
[i
] = wx
.RealPoint((x2
- x1
) / 2.0 + x1
, (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."""
493 self
._lineControlPoints
[0] = wx
.RealPoint(x1
, y1
)
494 self
._lineControlPoints
[-1] = wx
.RealPoint(x2
, y2
)
497 self
._xpos
= (x1
+ x2
) / 2.0
498 self
._ypos
= (y1
+ y2
) / 2.0
500 # Get absolute positions of ends
502 """Get the visible endpoints of the lines for drawing between two objects."""
503 first_point
= self
._lineControlPoints
[0]
504 last_point
= self
._lineControlPoints
[-1]
506 return (first_point
[0], first_point
[1]), (last_point
[0], last_point
[1])
508 def SetAttachments(self
, from_attach
, to_attach
):
509 """Specify which object attachment points should be used at each end
512 self
._attachmentFrom
= from_attach
513 self
._attachmentTo
= to_attach
515 def HitTest(self
, x
, y
):
516 if not self
._lineControlPoints
:
519 # Look at label regions in case mouse is over a label
520 inLabelRegion
= False
523 region
= self
._regions
[i
]
524 if len(region
._formattedText
):
525 xp
, yp
= self
.GetLabelPosition(i
)
526 # Offset region from default label position
527 cx
, cy
= region
.GetPosition()
528 cw
, ch
= region
.GetSize()
532 rLeft
= cx
- cw
/ 2.0
534 rRight
= cx
+ cw
/ 2.0
535 rBottom
= cy
+ ch
/ 2.0
536 if x
> rLeft
and x
< rRight
and y
> rTop
and y
< rBottom
:
540 for i
in range(len(self
._lineControlPoints
) - 1):
541 point1
= self
._lineControlPoints
[i
]
542 point2
= self
._lineControlPoints
[i
+ 1]
544 # For inaccurate mousing allow 8 pixel corridor
547 dx
= point2
[0] - point1
[0]
548 dy
= point2
[1] - point1
[1]
550 seg_len
= math
.sqrt(dx
* dx
+ dy
* dy
)
551 if dy
== 0 or dx
== 0:
553 distance_from_seg
= seg_len
* float((x
- point1
[0]) * dy
- (y
- point1
[1]) * dx
) / (dy
* dy
+ dx
* dx
)
554 distance_from_prev
= seg_len
* float((y
- point1
[1]) * dy
+ (x
- point1
[0]) * dx
) / (dy
* dy
+ dx
* dx
)
556 if abs(distance_from_seg
) < extra
and distance_from_prev
>= 0 and distance_from_prev
<= seg_len
or inLabelRegion
:
557 return 0, distance_from_seg
561 def DrawArrows(self
, dc
):
562 """Draw all arrows."""
563 # Distance along line of each arrow: space them out evenly
568 for arrow
in self
._arcArrows
:
569 ah
= arrow
.GetArrowEnd()
570 if ah
== ARROW_POSITION_START
:
571 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
572 # If specified, x offset is proportional to line length
573 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
575 self
.DrawArrow(dc
, arrow
, startArrowPos
, False)
576 startArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
577 elif ah
== ARROW_POSITION_END
:
578 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
579 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
581 self
.DrawArrow(dc
, arrow
, endArrowPos
, False)
582 endArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
583 elif ah
== ARROW_POSITION_MIDDLE
:
584 arrow
.SetXOffset(middleArrowPos
)
585 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
586 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
588 self
.DrawArrow(dc
, arrow
, middleArrowPos
, False)
589 middleArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
591 def DrawArrow(self
, dc
, arrow
, XOffset
, proportionalOffset
):
592 """Draw the given arrowhead (or annotation)."""
593 first_line_point
= self
._lineControlPoints
[0]
594 second_line_point
= self
._lineControlPoints
[1]
596 last_line_point
= self
._lineControlPoints
[-1]
597 second_last_line_point
= self
._lineControlPoints
[-2]
599 # Position of start point of line, at the end of which we draw the arrow
600 startPositionX
, startPositionY
= 0.0, 0.0
602 ap
= arrow
.GetPosition()
603 if ap
== ARROW_POSITION_START
:
604 # If we're using a proportional offset, calculate just where this
605 # will be on the line.
607 if proportionalOffset
:
608 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]))
609 realOffset
= XOffset
* totalLength
611 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_line_point
[0], second_line_point
[1], first_line_point
[0], first_line_point
[1], realOffset
)
613 startPositionX
= second_line_point
[0]
614 startPositionY
= second_line_point
[1]
615 elif ap
== ARROW_POSITION_END
:
616 # If we're using a proportional offset, calculate just where this
617 # will be on the line.
619 if proportionalOffset
:
620 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]));
621 realOffset
= XOffset
* totalLength
623 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], last_line_point
[0], last_line_point
[1], realOffset
)
625 startPositionX
= second_last_line_point
[0]
626 startPositionY
= second_last_line_point
[1]
627 elif ap
== ARROW_POSITION_MIDDLE
:
628 # Choose a point half way between the last and penultimate points
629 x
= (last_line_point
[0] + second_last_line_point
[0]) / 2.0
630 y
= (last_line_point
[1] + second_last_line_point
[1]) / 2.0
632 # If we're using a proportional offset, calculate just where this
633 # will be on the line.
635 if proportionalOffset
:
636 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
));
637 realOffset
= XOffset
* totalLength
639 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], x
, y
, realOffset
)
640 startPositionX
= second_last_line_point
[0]
641 startPositionY
= second_last_line_point
[1]
643 # Add yOffset to arrow, if any
645 # The translation that the y offset may give
648 if arrow
.GetYOffset
and not self
._ignoreArrowOffsets
:
652 # (x1, y1)--------------(x3, y3)------------------(x2, y2)
653 # x4 = x3 - d * math.sin(theta)
654 # y4 = y3 + d * math.cos(theta)
656 # Where theta = math.tan(-1) of (y3-y1) / (x3-x1)
659 x3
= float(positionOnLineX
)
660 y3
= float(positionOnLineY
)
661 d
= -arrow
.GetYOffset() # Negate so +offset is above line
664 theta
= math
.pi
/ 2.0
666 theta
= math
.atan((y3
- y1
) / (x3
- x1
))
668 x4
= x3
- d
* math
.sin(theta
)
669 y4
= y3
+ d
* math
.cos(theta
)
671 deltaX
= x4
- positionOnLineX
672 deltaY
= y4
- positionOnLineY
674 at
= arrow
._GetType
()
675 if at
== ARROW_ARROW
:
676 arrowLength
= arrow
.GetSize()
677 arrowWidth
= arrowLength
/ 3.0
679 tip_x
, tip_y
, side1_x
, side1_y
, side2_x
, side2_y
= GetArrowPoints(startPositionX
+ deltaX
, startPositionY
+ deltaY
, positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
, arrowLength
, arrowWidth
)
681 points
= [[tip_x
, tip_y
],
687 dc
.SetBrush(self
._brush
)
688 dc
.DrawPolygon(points
)
689 elif at
in [ARROW_HOLLOW_CIRCLE
, ARROW_FILLED_CIRCLE
]:
690 # Find point on line of centre of circle, which is a radius away
691 # from the end position
692 diameter
= arrow
.GetSize()
693 x
, y
= GetPointOnLine(startPositionX
+ deltaX
, startPositionY
+ deltaY
,
694 positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
,
696 x1
= x
- diameter
/ 2.0
697 y1
= y
- diameter
/ 2.0
699 if arrow
._GetType
() == ARROW_HOLLOW_CIRCLE
:
700 dc
.SetBrush(self
.GetBackgroundBrush())
702 dc
.SetBrush(self
._brush
)
704 dc
.DrawEllipse(x1
, y1
, diameter
, diameter
)
705 elif at
== ARROW_SINGLE_OBLIQUE
:
707 elif at
== ARROW_METAFILE
:
708 if arrow
.GetMetaFile():
709 # Find point on line of centre of object, which is a half-width away
710 # from the end position
713 # <-- start pos <-----><-- positionOnLineX
715 # --------------| x | <-- e.g. rectangular arrowhead
718 x
, y
= GetPointOnLine(startPositionX
, startPositionY
,
719 positionOnLineX
, positionOnLineY
,
720 arrow
.GetMetaFile()._width
/ 2.0)
721 # Calculate theta for rotating the metafile.
724 # | o(x2, y2) 'o' represents the arrowhead.
729 # |______________________
734 x2
= float(positionOnLineX
)
735 y2
= float(positionOnLineY
)
737 if x1
== x2
and y1
== y2
:
739 elif x1
== x2
and y1
> y2
:
740 theta
= 3.0 * math
.pi
/ 2.0
741 elif x1
== x2
and y2
> y1
:
742 theta
= math
.pi
/ 2.0
743 elif x2
> x1
and y2
>= y1
:
744 theta
= math
.atan((y2
- y1
) / (x2
- x1
))
746 theta
= math
.pi
+ math
.atan((y2
- y1
) / (x2
- x1
))
747 elif x2
> x1
and y2
< y1
:
748 theta
= 2 * math
.pi
+ math
.atan((y2
- y1
) / (x2
- x1
))
750 raise "Unknown arrowhead rotation case"
752 # Rotate about the centre of the object, then place
753 # the object on the line.
754 if arrow
.GetMetaFile().GetRotateable():
755 arrow
.GetMetaFile().Rotate(0.0, 0.0, theta
)
758 # If erasing, just draw a rectangle
759 minX
, minY
, maxX
, maxY
= arrow
.GetMetaFile().GetBounds()
760 # Make erasing rectangle slightly bigger or you get droppings
762 dc
.DrawRectangle(deltaX
+ x
+ minX
- extraPixels
/ 2.0, deltaY
+ y
+ minY
- extraPixels
/ 2.0, maxX
- minX
+ extraPixels
, maxY
- minY
+ extraPixels
)
764 arrow
.GetMetaFile().Draw(dc
, x
+ deltaX
, y
+ deltaY
)
766 def OnErase(self
, dc
):
768 old_brush
= self
._brush
770 bg_pen
= self
.GetBackgroundPen()
771 bg_brush
= self
.GetBackgroundBrush()
773 self
.SetBrush(bg_brush
)
775 bound_x
, bound_y
= self
.GetBoundingBoxMax()
777 dc
.SetFont(self
._font
)
779 # Undraw text regions
782 x
, y
= self
.GetLabelPosition(i
)
783 self
.EraseRegion(dc
, self
._regions
[i
], x
, y
)
786 dc
.SetPen(self
.GetBackgroundPen())
787 dc
.SetBrush(self
.GetBackgroundBrush())
789 # Drawing over the line only seems to work if the line has a thickness
791 if old_pen
and old_pen
.GetWidth() > 1:
792 dc
.DrawRectangle(self
._xpos
- bound_x
/ 2.0 - 2, self
._ypos
- bound_y
/ 2.0 - 2,
793 bound_x
+ 4, bound_y
+ 4)
796 self
.GetEventHandler().OnDraw(dc
)
797 self
.GetEventHandler().OnEraseControlPoints(dc
)
798 self
._erasing
= False
803 self
.SetBrush(old_brush
)
805 def GetBoundingBoxMin(self
):
806 x1
, y1
= 10000, 10000
807 x2
, y2
= -10000, -10000
809 for point
in self
._lineControlPoints
:
819 return x2
- x1
, y2
- y1
821 # For a node image of interest, finds the position of this arc
822 # amongst all the arcs which are attached to THIS SIDE of the node image,
823 # and the number of same.
824 def FindNth(self
, image
, incoming
):
825 """Find the position of the line on the given object.
827 Specify whether incoming or outgoing lines are being considered
833 if image
== self
._to
:
834 this_attachment
= self
._attachmentTo
836 this_attachment
= self
._attachmentFrom
838 # Find number of lines going into / out of this particular attachment point
839 for line
in image
.GetLines():
840 if line
._from
== image
:
841 # This is the nth line attached to 'image'
842 if line
== self
and not incoming
:
845 # Increment num count if this is the same side (attachment number)
846 if line
._attachmentFrom
== this_attachment
:
849 if line
._to
== image
:
850 # This is the nth line attached to 'image'
851 if line
== self
and incoming
:
854 # Increment num count if this is the same side (attachment number)
855 if line
._attachmentTo
== this_attachment
:
860 def OnDrawOutline(self
, dc
, x
, y
, w
, h
):
862 old_brush
= self
._brush
864 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
865 self
.SetPen(dottedPen
)
866 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
868 self
.GetEventHandler().OnDraw(dc
)
875 self
.SetBrush(old_brush
)
879 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
= True):
883 if self
._lineControlPoints
and not (x_offset
== 0 and y_offset
== 0):
884 for point
in self
._lineControlPoints
:
888 # Move temporary label rectangles if necessary
890 if self
._labelObjects
[i
]:
891 self
._labelObjects
[i
].Erase(dc
)
892 xp
, yp
= self
.GetLabelPosition(i
)
893 if i
< len(self
._regions
):
894 xr
, yr
= self
._regions
[i
].GetPosition()
897 self
._labelObjects
[i
].Move(dc
, xp
+ xr
, yp
+ yr
)
900 def OnMoveLink(self
, dc
, moveControlPoints
= True):
901 """Called when a connected object has moved, to move the link to
904 if not self
._from
or not self
._to
:
907 if len(self
._lineControlPoints
) > 2:
910 # Do each end - nothing in the middle. User has to move other points
911 # manually if necessary
912 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
914 oldX
, oldY
= self
._xpos
, self
._ypos
916 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
918 # Do a second time, because one may depend on the other
919 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
920 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
922 # Try to move control points with the arc
923 x_offset
= self
._xpos
- oldX
924 y_offset
= self
._ypos
- oldY
926 # Only move control points if it's a self link. And only works
927 # if attachment mode is ON
928 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):
929 for point
in self
._lineControlPoints
[1:-1]:
933 self
.Move(dc
, self
._xpos
, self
._ypos
)
935 def FindLineEndPoints(self
):
936 """Finds the x, y points at the two ends of the line.
938 This function can be used by e.g. line-routing routines to
939 get the actual points on the two node images where the lines will be
942 if not self
._from
or not self
._to
:
945 # Do each end - nothing in the middle. User has to move other points
946 # manually if necessary.
947 second_point
= self
._lineControlPoints
[1]
948 second_last_point
= self
._lineControlPoints
[-2]
950 if len(self
._lineControlPoints
) > 2:
951 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
952 nth
, no_arcs
= self
.FindNth(self
._from
, False) # Not incoming
953 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
955 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), second_point
[0], second_point
[1])
957 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
958 nth
, no_arch
= self
.FindNth(self
._to
, True) # Incoming
959 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arch
, self
)
961 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), second_last_point
[0], second_last_point
[1])
963 fromX
= self
._from
.GetX()
964 fromY
= self
._from
.GetY()
965 toX
= self
._to
.GetX()
966 toY
= self
._to
.GetY()
968 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
969 nth
, no_arcs
= self
.FindNth(self
._from
, False)
970 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
974 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
975 nth
, no_arcs
= self
.FindNth(self
._to
, True)
976 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arcs
, self
)
980 if self
._from
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
981 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), toX
, toY
)
983 if self
._to
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
984 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), fromX
, fromY
)
986 return end_x
, end_y
, other_end_x
, other_end_y
989 def OnDraw(self
, dc
):
990 if not self
._lineControlPoints
:
996 dc
.SetBrush(self
._brush
)
999 for point
in self
._lineControlPoints
:
1000 points
.append(wx
.Point(point
[0], point
[1]))
1004 dc
.DrawSpline(points
)
1006 dc
.DrawLines(points
)
1008 if sys
.platform
[:3] == "win":
1009 # For some reason, last point isn't drawn under Windows
1011 dc
.DrawPoint(pt
[0], pt
[1])
1013 # Problem with pen - if not a solid pen, does strange things
1014 # to the arrowhead. So make (get) a new pen that's solid.
1015 if self
._pen
and self
._pen
.GetStyle() != wx
.SOLID
:
1016 solid_pen
= wx
.ThePenList
.FindOrCreatePen(self
._pen
.GetColour(), 1, wx
.SOLID
)
1018 dc
.SetPen(solid_pen
)
1022 def OnDrawControlPoints(self
, dc
):
1023 if not self
._drawHandles
:
1026 # Draw temporary label rectangles if necessary
1028 if self
._labelObjects
[i
]:
1029 self
._labelObjects
[i
].Draw(dc
)
1031 Shape
.OnDrawControlPoints(self
, dc
)
1033 def OnEraseControlPoints(self
, dc
):
1034 # Erase temporary label rectangles if necessary
1037 if self
._labelObjects
[i
]:
1038 self
._labelObjects
[i
].Erase(dc
)
1040 Shape
.OnEraseControlPoints(self
, dc
)
1042 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1045 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1048 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1051 def OnDrawContents(self
, dc
):
1052 if self
.GetDisableLabel():
1056 if self
._regions
[i
]:
1057 x
, y
= self
.GetLabelPosition(i
)
1058 self
.DrawRegion(dc
, self
._regions
[i
], x
, y
)
1060 def SetTo(self
, object):
1061 """Set the 'to' object for the line."""
1064 def SetFrom(self
, object):
1065 """Set the 'from' object for the line."""
1068 def MakeControlPoints(self
):
1069 """Make handle control points."""
1070 if self
._canvas
and self
._lineControlPoints
:
1071 first
= self
._lineControlPoints
[0]
1072 last
= self
._lineControlPoints
[-1]
1074 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, first
[0], first
[1], CONTROL_POINT_ENDPOINT_FROM
)
1075 control
._point
= first
1076 self
._canvas
.AddShape(control
)
1077 self
._controlPoints
.append(control
)
1079 for point
in self
._lineControlPoints
[1:-1]:
1080 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, point
[0], point
[1], CONTROL_POINT_LINE
)
1081 control
._point
= point
1082 self
._canvas
.AddShape(control
)
1083 self
._controlPoints
.append(control
)
1085 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, last
[0], last
[1], CONTROL_POINT_ENDPOINT_TO
)
1086 control
._point
= last
1087 self
._canvas
.AddShape(control
)
1088 self
._controlPoints
.append(control
)
1090 def ResetControlPoints(self
):
1091 if self
._canvas
and self
._lineControlPoints
:
1092 for i
in range(min(len(self
._controlPoints
), len(self
._lineControlPoints
))):
1093 point
= self
._lineControlPoints
[i
]
1094 control
= self
._controlPoints
[i
]
1095 control
.SetX(point
[0])
1096 control
.SetY(point
[1])
1098 # Override select, to create / delete temporary label-moving objects
1099 def Select(self
, select
, dc
= None):
1100 Shape
.Select(self
, select
, dc
)
1103 if self
._regions
[i
]:
1104 region
= self
._regions
[i
]
1105 if region
._formattedText
:
1106 w
, h
= region
.GetSize()
1107 x
, y
= region
.GetPosition()
1108 xx
, yy
= self
.GetLabelPosition(i
)
1110 if self
._labelObjects
[i
]:
1111 self
._labelObjects
[i
].Select(False)
1112 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1114 self
._labelObjects
[i
] = self
.OnCreateLabelShape(self
, region
, w
, h
)
1115 self
._labelObjects
[i
].AddToCanvas(self
._canvas
)
1116 self
._labelObjects
[i
].Show(True)
1118 self
._labelObjects
[i
].Move(dc
, x
+ xx
, y
+ yy
)
1119 self
._labelObjects
[i
].Select(True, dc
)
1122 if self
._labelObjects
[i
]:
1123 self
._labelObjects
[i
].Select(False, dc
)
1124 self
._labelObjects
[i
].Erase(dc
)
1125 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1126 self
._labelObjects
[i
] = None
1128 # Control points ('handles') redirect control to the actual shape, to
1129 # make it easier to override sizing behaviour.
1130 def OnSizingDragLeft(self
, pt
, draw
, x
, y
, keys
= 0, attachment
= 0):
1131 dc
= wx
.ClientDC(self
.GetCanvas())
1132 self
.GetCanvas().PrepareDC(dc
)
1134 dc
.SetLogicalFunction(OGLRBLF
)
1136 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1137 dc
.SetPen(dottedPen
)
1138 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1140 if pt
._type
== CONTROL_POINT_LINE
:
1141 x
, y
= self
._canvas
.Snap(x
, y
)
1147 old_pen
= self
.GetPen()
1148 old_brush
= self
.GetBrush()
1150 self
.SetPen(dottedPen
)
1151 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1153 self
.GetEventHandler().OnMoveLink(dc
, False)
1155 self
.SetPen(old_pen
)
1156 self
.SetBrush(old_brush
)
1158 def OnSizingBeginDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1159 dc
= wx
.ClientDC(self
.GetCanvas())
1160 self
.GetCanvas().PrepareDC(dc
)
1162 if pt
._type
== CONTROL_POINT_LINE
:
1163 pt
._originalPos
= pt
._point
1164 x
, y
= self
._canvas
.Snap(x
, y
)
1168 # Redraw start and end objects because we've left holes
1169 # when erasing the line
1170 self
.GetFrom().OnDraw(dc
)
1171 self
.GetFrom().OnDrawContents(dc
)
1172 self
.GetTo().OnDraw(dc
)
1173 self
.GetTo().OnDrawContents(dc
)
1175 self
.SetDisableLabel(True)
1176 dc
.SetLogicalFunction(OGLRBLF
)
1182 old_pen
= self
.GetPen()
1183 old_brush
= self
.GetBrush()
1185 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1186 self
.SetPen(dottedPen
)
1187 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1189 self
.GetEventHandler().OnMoveLink(dc
, False)
1191 self
.SetPen(old_pen
)
1192 self
.SetBrush(old_brush
)
1194 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
or pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1195 self
._canvas
.SetCursor(wx
.StockCursor(wx
.CURSOR_BULLSEYE
))
1196 pt
._oldCursor
= wx
.STANDARD_CURSOR
1198 def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1199 dc
= wx
.ClientDC(self
.GetCanvas())
1200 self
.GetCanvas().PrepareDC(dc
)
1202 self
.SetDisableLabel(False)
1204 if pt
._type
== CONTROL_POINT_LINE
:
1205 x
, y
= self
._canvas
.Snap(x
, y
)
1207 rpt
= wx
.RealPoint(x
, y
)
1209 # Move the control point back to where it was;
1210 # MoveControlPoint will move it to the new position
1211 # if it decides it wants. We only moved the position
1212 # during user feedback so we could redraw the line
1213 # as it changed shape.
1214 pt
._xpos
= pt
._originalPos
[0]
1215 pt
._ypos
= pt
._originalPos
[1]
1216 pt
._point
= pt
._originalPos
[0], pt
._originalPos
[1]
1218 self
.OnMoveMiddleControlPoint(dc
, pt
, rpt
)
1220 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
:
1222 self
._canvas
.SetCursor(pt
._oldCursor
)
1225 self
.GetFrom().MoveLineToNewAttachment(dc
, self
, x
, y
)
1227 if pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1229 self
._canvas
.SetCursor(pt
._oldCursor
)
1232 self
.GetTo().MoveLineToNewAttachment(dc
, self
, x
, y
)
1234 # This is called only when a non-end control point is moved
1235 def OnMoveMiddleControlPoint(self
, dc
, lpt
, pt
):
1239 lpt
._point
= pt
[0], pt
[1]
1241 self
.GetEventHandler().OnMoveLink(dc
)
1245 def AddArrow(self
, type, end
= ARROW_POSITION_END
, size
= 10.0, xOffset
= 0.0, name
= "", mf
= None, arrowId
= -1):
1246 """Add an arrow (or annotation) to the line.
1248 type may currently be one of:
1255 Conventional arrowhead.
1256 ARROW_SINGLE_OBLIQUE
1257 Single oblique stroke.
1258 ARROW_DOUBLE_OBLIQUE
1259 Double oblique stroke.
1260 ARROW_DOUBLE_METAFILE
1263 end may currently be one of:
1266 Arrow appears at the end.
1267 ARROW_POSITION_START
1268 Arrow appears at the start.
1270 arrowSize specifies the length of the arrow.
1272 xOffset specifies the offset from the end of the line.
1274 name specifies a name for the arrow.
1276 mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
1279 arrowId is the id for the arrow.
1281 arrow
= ArrowHead(type, end
, size
, xOffset
, name
, mf
, arrowId
)
1282 self
._arcArrows
.append(arrow
)
1285 # Add arrowhead at a particular position in the arrowhead list
1286 def AddArrowOrdered(self
, arrow
, referenceList
, end
):
1287 """Add an arrowhead in the position indicated by the reference list
1288 of arrowheads, which contains all legal arrowheads for this line, in
1289 the correct order. E.g.
1291 Reference list: a b c d e
1292 Current line list: a d
1294 Add c, then line list is: a c d.
1296 If no legal arrowhead position, return FALSE. Assume reference list
1297 is for one end only, since it potentially defines the ordering for
1298 any one of the 3 positions. So we don't check the reference list for
1301 if not referenceList
:
1304 targetName
= arrow
.GetName()
1306 # First check whether we need to insert in front of list,
1307 # because this arrowhead is the first in the reference
1308 # list and should therefore be first in the current list.
1309 refArrow
= referenceList
[0]
1310 if refArrow
.GetName() == targetName
:
1311 self
._arcArrows
.insert(0, arrow
)
1315 while i1
< len(referenceList
) and i2
< len(self
._arcArrows
):
1316 refArrow
= referenceList
[i1
]
1317 currArrow
= self
._arcArrows
[i2
]
1319 # Matching: advance current arrow pointer
1320 if currArrow
.GetArrowEnd() == end
and currArrow
.GetName() == refArrow
.GetName():
1323 # Check if we're at the correct position in the
1325 if targetName
== refArrow
.GetName():
1326 if i2
< len(self
._arcArrows
):
1327 self
._arcArrows
.insert(i2
, arrow
)
1329 self
._arcArrows
.append(arrow
)
1333 self
._arcArrows
.append(arrow
)
1336 def ClearArrowsAtPosition(self
, end
):
1337 """Delete the arrows at the specified position, or at any position
1341 self
._arcArrows
= []
1344 for arrow
in self
._arcArrows
:
1345 if arrow
.GetArrowEnd() == end
:
1346 self
._arcArrows
.remove(arrow
)
1348 def ClearArrow(self
, name
):
1349 """Delete the arrow with the given name."""
1350 for arrow
in self
._arcArrows
:
1351 if arrow
.GetName() == name
:
1352 self
._arcArrows
.remove(arrow
)
1356 def FindArrowHead(self
, position
, name
):
1357 """Find arrowhead by position and name.
1359 if position is -1, matches any position.
1361 for arrow
in self
._arcArrows
:
1362 if (position
== -1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1367 def FindArrowHeadId(self
, arrowId
):
1368 """Find arrowhead by id."""
1369 for arrow
in self
._arcArrows
:
1370 if arrowId
== arrow
.GetId():
1375 def DeleteArrowHead(self
, position
, name
):
1376 """Delete arrowhead by position and name.
1378 if position is -1, matches any position.
1380 for arrow
in self
._arcArrows
:
1381 if (position
== -1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1382 self
._arcArrows
.remove(arrow
)
1386 def DeleteArrowHeadId(self
, id):
1387 """Delete arrowhead by id."""
1388 for arrow
in self
._arcArrows
:
1389 if arrowId
== arrow
.GetId():
1390 self
._arcArrows
.remove(arrow
)
1394 # Calculate the minimum width a line
1395 # occupies, for the purposes of drawing lines in tools.
1396 def FindMinimumWidth(self
):
1397 """Find the horizontal width for drawing a line with arrows in
1398 minimum space. Assume arrows at end only.
1401 for arrowHead
in self
._arcArrows
:
1402 minWidth
+= arrowHead
.GetSize()
1403 if arrowHead
!= self
._arcArrows
[-1]:
1404 minWidth
+= arrowHead
+ GetSpacing
1406 # We have ABSOLUTE minimum now. So
1407 # scale it to give it reasonable aesthetics
1408 # when drawing with line.
1410 minWidth
= minWidth
* 1.4
1414 self
.SetEnds(0.0, 0.0, minWidth
, 0.0)
1419 def FindLinePosition(self
, x
, y
):
1420 """Find which position we're talking about at this x, y.
1422 Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
1424 startX
, startY
, endX
, endY
= self
.GetEnds()
1426 # Find distances from centre, start and end. The smallest wins
1427 centreDistance
= math
.sqrt((x
- self
._xpos
) * (x
- self
._xpos
) + (y
- self
._ypos
) * (y
- self
._ypos
))
1428 startDistance
= math
.sqrt((x
- startX
) * (x
- startX
) + (y
- startY
) * (y
- startY
))
1429 endDistance
= math
.sqrt((x
- endX
) * (x
- endX
) + (y
- endY
) * (y
- endY
))
1431 if centreDistance
< startDistance
and centreDistance
< endDistance
:
1432 return ARROW_POSITION_MIDDLE
1433 elif startDistance
< endDistance
:
1434 return ARROW_POSITION_START
1436 return ARROW_POSITION_END
1438 def SetAlignmentOrientation(self
, isEnd
, isHoriz
):
1440 if isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1441 self
._alignmentEnd
!= LINE_ALIGNMENT_HORIZ
1442 elif not isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1443 self
._alignmentEnd
-= LINE_ALIGNMENT_HORIZ
1445 if isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1446 self
._alignmentStart
!= LINE_ALIGNMENT_HORIZ
1447 elif not isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1448 self
._alignmentStart
-= LINE_ALIGNMENT_HORIZ
1450 def SetAlignmentType(self
, isEnd
, alignType
):
1452 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1453 if self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1454 self
._alignmentEnd |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1455 elif self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1456 self
._alignmentEnd
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1458 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1459 if self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1460 self
._alignmentStart |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1461 elif self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1462 self
._alignmentStart
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1464 def GetAlignmentOrientation(self
, isEnd
):
1466 return self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1468 return self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1470 def GetAlignmentType(self
, isEnd
):
1472 return self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1474 return self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1476 def GetNextControlPoint(self
, shape
):
1477 """Find the next control point in the line after the start / end point,
1478 depending on whether the shape is at the start or end.
1480 n
= len(self
._lineControlPoints
)
1481 if self
._to
== shape
:
1482 # Must be END of line, so we want (n - 1)th control point.
1483 # But indexing ends at n-1, so subtract 2.
1487 if nn
< len(self
._lineControlPoints
):
1488 return self
._lineControlPoints
[nn
]
1491 def OnCreateLabelShape(self
, parent
, region
, w
, h
):
1492 return LabelShape(parent
, region
, w
, h
)
1495 def OnLabelMovePre(self
, dc
, labelShape
, x
, y
, old_x
, old_y
, display
):
1496 labelShape
._shapeRegion
.SetSize(labelShape
.GetWidth(), labelShape
.GetHeight())
1498 # Find position in line's region list
1500 for region
in self
.GetRegions():
1501 if labelShape
._shapeRegion
== region
:
1502 self
.GetRegions().remove(region
)
1506 xx
, yy
= self
.GetLabelPosition(i
)
1507 # Set the region's offset, relative to the default position for
1509 labelShape
._shapeRegion
.SetPosition(x
- xx
, y
- yy
)
1513 # Need to reformat to fit region
1514 if labelShape
._shapeRegion
.GetText():
1515 s
= labelShape
._shapeRegion
.GetText()
1516 labelShape
.FormatText(dc
, s
, i
)
1517 self
.DrawRegion(dc
, labelShape
._shapeRegion
, xx
, yy
)