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 self
._initialised
= False
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.0
311 line_y
= (last_point
[1] + second_last_point
[1]) / 2.0
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 i
in range(1,len(self
._lineControlPoints
)):
336 point
= self
._lineControlPoints
[i
]
338 if first_point
[0] < last_point
[0]:
344 if first_point
[1] < last_point
[1]:
350 self
._lineControlPoints
[i
] = wx
.RealPoint((x2
- x1
) / 2.0 + x1
, (y2
- y1
) / 2.0 + y1
)
351 self
._initialised
= True
353 def FormatText(self
, dc
, s
, i
):
354 """Format a text string according to the region size, adding
355 strings with positions to region text list.
359 if len(self
._regions
) == 0 or i
>= len(self
._regions
):
362 region
= self
._regions
[i
]
364 dc
.SetFont(region
.GetFont())
366 w
, h
= region
.GetSize()
367 # Initialize the size if zero
368 if (w
== 0 or h
== 0) and s
:
372 string_list
= FormatText(dc
, s
, w
- 5, h
- 5, region
.GetFormatMode())
373 for s
in string_list
:
374 line
= ShapeTextLine(0.0, 0.0, s
)
375 region
.GetFormattedText().append(line
)
379 if region
.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS
:
380 actualW
, actualH
= GetCentredTextExtent(dc
, region
.GetFormattedText(), self
._xpos
, self
._ypos
, w
, h
)
381 if actualW
!= w
or actualH
!= h
:
382 xx
, yy
= self
.GetLabelPosition(i
)
383 self
.EraseRegion(dc
, region
, xx
, yy
)
384 if len(self
._labelObjects
) < i
:
385 self
._labelObjects
[i
].Select(False, dc
)
386 self
._labelObjects
[i
].Erase(dc
)
387 self
._labelObjects
[i
].SetSize(actualW
, actualH
)
389 region
.SetSize(actualW
, actualH
)
391 if len(self
._labelObjects
) < i
:
392 self
._labelObjects
[i
].Select(True, dc
)
393 self
._labelObjects
[i
].Draw(dc
)
395 CentreText(dc
, region
.GetFormattedText(), self
._xpos
, self
._ypos
, actualW
, actualH
, region
.GetFormatMode())
396 self
._formatted
= True
398 def DrawRegion(self
, dc
, region
, x
, y
):
399 """Format one region at this position."""
400 if self
.GetDisableLabel():
403 w
, h
= region
.GetSize()
405 # Get offset from x, y
406 xx
, yy
= region
.GetPosition()
411 # First, clear a rectangle for the text IF there is any
412 if len(region
.GetFormattedText()):
413 dc
.SetPen(self
.GetBackgroundPen())
414 dc
.SetBrush(self
.GetBackgroundBrush())
418 dc
.SetFont(region
.GetFont())
419 dc
.DrawRectangle(xp
- w
/ 2.0, yp
- h
/ 2.0, w
, h
)
423 dc
.SetTextForeground(region
.GetActualColourObject())
425 DrawFormattedText(dc
, region
.GetFormattedText(), xp
, yp
, w
, h
, region
.GetFormatMode())
427 def EraseRegion(self
, dc
, region
, x
, y
):
428 """Erase one region at this position."""
429 if self
.GetDisableLabel():
432 w
, h
= region
.GetSize()
434 # Get offset from x, y
435 xx
, yy
= region
.GetPosition()
440 if region
.GetFormattedText():
441 dc
.SetPen(self
.GetBackgroundPen())
442 dc
.SetBrush(self
.GetBackgroundBrush())
444 dc
.DrawRectangle(xp
- w
/ 2.0, yp
- h
/ 2.0, w
, h
)
446 def GetLabelPosition(self
, position
):
447 """Get the reference point for a label.
449 Region x and y are offsets from this.
450 position is 0 (middle), 1 (start), 2 (end).
453 # Want to take the middle section for the label
454 half_way
= int(len(self
._lineControlPoints
) / 2.0)
456 # Find middle of this line
457 point
= self
._lineControlPoints
[half_way
- 1]
458 next_point
= self
._lineControlPoints
[half_way
]
460 dx
= next_point
[0] - point
[0]
461 dy
= next_point
[1] - point
[1]
463 return point
[0] + dx
/ 2.0, point
[1] + dy
/ 2.0
465 return self
._lineControlPoints
[0][0], self
._lineControlPoints
[0][1]
467 return self
._lineControlPoints
[-1][0], self
._lineControlPoints
[-1][1]
469 def Straighten(self
, dc
= None):
470 """Straighten verticals and horizontals."""
471 if len(self
._lineControlPoints
) < 3:
477 GraphicsStraightenLine(self
._lineControlPoints
[-1], self
._lineControlPoints
[-2])
479 for i
in range(len(self
._lineControlPoints
) - 2):
480 GraphicsStraightenLine(self
._lineControlPoints
[i
], self
._lineControlPoints
[i
+ 1])
486 """Unlink the line from the nodes at either end."""
488 self
._to
.GetLines().remove(self
)
490 self
._from
.GetLines().remove(self
)
494 def SetEnds(self
, x1
, y1
, x2
, y2
):
495 """Set the end positions of the line."""
496 self
._lineControlPoints
[0] = wx
.RealPoint(x1
, y1
)
497 self
._lineControlPoints
[-1] = wx
.RealPoint(x2
, y2
)
500 self
._xpos
= (x1
+ x2
) / 2.0
501 self
._ypos
= (y1
+ y2
) / 2.0
503 # Get absolute positions of ends
505 """Get the visible endpoints of the lines for drawing between two objects."""
506 first_point
= self
._lineControlPoints
[0]
507 last_point
= self
._lineControlPoints
[-1]
509 return (first_point
[0], first_point
[1]), (last_point
[0], last_point
[1])
511 def SetAttachments(self
, from_attach
, to_attach
):
512 """Specify which object attachment points should be used at each end
515 self
._attachmentFrom
= from_attach
516 self
._attachmentTo
= to_attach
518 def HitTest(self
, x
, y
):
519 if not self
._lineControlPoints
:
522 # Look at label regions in case mouse is over a label
523 inLabelRegion
= False
526 region
= self
._regions
[i
]
527 if len(region
._formattedText
):
528 xp
, yp
= self
.GetLabelPosition(i
)
529 # Offset region from default label position
530 cx
, cy
= region
.GetPosition()
531 cw
, ch
= region
.GetSize()
535 rLeft
= cx
- cw
/ 2.0
537 rRight
= cx
+ cw
/ 2.0
538 rBottom
= cy
+ ch
/ 2.0
539 if x
> rLeft
and x
< rRight
and y
> rTop
and y
< rBottom
:
543 for i
in range(len(self
._lineControlPoints
) - 1):
544 point1
= self
._lineControlPoints
[i
]
545 point2
= self
._lineControlPoints
[i
+ 1]
547 # For inaccurate mousing allow 8 pixel corridor
550 dx
= point2
[0] - point1
[0]
551 dy
= point2
[1] - point1
[1]
553 seg_len
= math
.sqrt(dx
* dx
+ dy
* dy
)
554 if dy
== 0 and dx
== 0:
556 distance_from_seg
= seg_len
* float((x
- point1
[0]) * dy
- (y
- point1
[1]) * dx
) / (dy
* dy
+ dx
* dx
)
557 distance_from_prev
= seg_len
* float((y
- point1
[1]) * dy
+ (x
- point1
[0]) * dx
) / (dy
* dy
+ dx
* dx
)
559 if abs(distance_from_seg
) < extra
and distance_from_prev
>= 0 and distance_from_prev
<= seg_len
or inLabelRegion
:
560 return 0, distance_from_seg
564 def DrawArrows(self
, dc
):
565 """Draw all arrows."""
566 # Distance along line of each arrow: space them out evenly
571 for arrow
in self
._arcArrows
:
572 ah
= arrow
.GetArrowEnd()
573 if ah
== ARROW_POSITION_START
:
574 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
575 # If specified, x offset is proportional to line length
576 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
578 self
.DrawArrow(dc
, arrow
, startArrowPos
, False)
579 startArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
580 elif ah
== ARROW_POSITION_END
:
581 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
582 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
584 self
.DrawArrow(dc
, arrow
, endArrowPos
, False)
585 endArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
586 elif ah
== ARROW_POSITION_MIDDLE
:
587 arrow
.SetXOffset(middleArrowPos
)
588 if arrow
.GetXOffset() and not self
._ignoreArrowOffsets
:
589 self
.DrawArrow(dc
, arrow
, arrow
.GetXOffset(), True)
591 self
.DrawArrow(dc
, arrow
, middleArrowPos
, False)
592 middleArrowPos
+= arrow
.GetSize() + arrow
.GetSpacing()
594 def DrawArrow(self
, dc
, arrow
, XOffset
, proportionalOffset
):
595 """Draw the given arrowhead (or annotation)."""
596 first_line_point
= self
._lineControlPoints
[0]
597 second_line_point
= self
._lineControlPoints
[1]
599 last_line_point
= self
._lineControlPoints
[-1]
600 second_last_line_point
= self
._lineControlPoints
[-2]
602 # Position of start point of line, at the end of which we draw the arrow
603 startPositionX
, startPositionY
= 0.0, 0.0
605 ap
= arrow
.GetPosition()
606 if ap
== ARROW_POSITION_START
:
607 # If we're using a proportional offset, calculate just where this
608 # will be on the line.
610 if proportionalOffset
:
611 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]))
612 realOffset
= XOffset
* totalLength
614 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_line_point
[0], second_line_point
[1], first_line_point
[0], first_line_point
[1], realOffset
)
616 startPositionX
= second_line_point
[0]
617 startPositionY
= second_line_point
[1]
618 elif ap
== ARROW_POSITION_END
:
619 # If we're using a proportional offset, calculate just where this
620 # will be on the line.
622 if proportionalOffset
:
623 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]));
624 realOffset
= XOffset
* totalLength
626 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], last_line_point
[0], last_line_point
[1], realOffset
)
628 startPositionX
= second_last_line_point
[0]
629 startPositionY
= second_last_line_point
[1]
630 elif ap
== ARROW_POSITION_MIDDLE
:
631 # Choose a point half way between the last and penultimate points
632 x
= (last_line_point
[0] + second_last_line_point
[0]) / 2.0
633 y
= (last_line_point
[1] + second_last_line_point
[1]) / 2.0
635 # If we're using a proportional offset, calculate just where this
636 # will be on the line.
638 if proportionalOffset
:
639 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
));
640 realOffset
= XOffset
* totalLength
642 positionOnLineX
, positionOnLineY
= GetPointOnLine(second_last_line_point
[0], second_last_line_point
[1], x
, y
, realOffset
)
643 startPositionX
= second_last_line_point
[0]
644 startPositionY
= second_last_line_point
[1]
646 # Add yOffset to arrow, if any
648 # The translation that the y offset may give
651 if arrow
.GetYOffset
and not self
._ignoreArrowOffsets
:
655 # (x1, y1)--------------(x3, y3)------------------(x2, y2)
656 # x4 = x3 - d * math.sin(theta)
657 # y4 = y3 + d * math.cos(theta)
659 # Where theta = math.tan(-1) of (y3-y1) / (x3-x1)
662 x3
= float(positionOnLineX
)
663 y3
= float(positionOnLineY
)
664 d
= -arrow
.GetYOffset() # Negate so +offset is above line
667 theta
= math
.pi
/ 2.0
669 theta
= math
.atan((y3
- y1
) / (x3
- x1
))
671 x4
= x3
- d
* math
.sin(theta
)
672 y4
= y3
+ d
* math
.cos(theta
)
674 deltaX
= x4
- positionOnLineX
675 deltaY
= y4
- positionOnLineY
677 at
= arrow
._GetType
()
678 if at
== ARROW_ARROW
:
679 arrowLength
= arrow
.GetSize()
680 arrowWidth
= arrowLength
/ 3.0
682 tip_x
, tip_y
, side1_x
, side1_y
, side2_x
, side2_y
= GetArrowPoints(startPositionX
+ deltaX
, startPositionY
+ deltaY
, positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
, arrowLength
, arrowWidth
)
684 points
= [[tip_x
, tip_y
],
690 dc
.SetBrush(self
._brush
)
691 dc
.DrawPolygon(points
)
692 elif at
in [ARROW_HOLLOW_CIRCLE
, ARROW_FILLED_CIRCLE
]:
693 # Find point on line of centre of circle, which is a radius away
694 # from the end position
695 diameter
= arrow
.GetSize()
696 x
, y
= GetPointOnLine(startPositionX
+ deltaX
, startPositionY
+ deltaY
,
697 positionOnLineX
+ deltaX
, positionOnLineY
+ deltaY
,
699 x1
= x
- diameter
/ 2.0
700 y1
= y
- diameter
/ 2.0
702 if arrow
._GetType
() == ARROW_HOLLOW_CIRCLE
:
703 dc
.SetBrush(self
.GetBackgroundBrush())
705 dc
.SetBrush(self
._brush
)
707 dc
.DrawEllipse(x1
, y1
, diameter
, diameter
)
708 elif at
== ARROW_SINGLE_OBLIQUE
:
710 elif at
== ARROW_METAFILE
:
711 if arrow
.GetMetaFile():
712 # Find point on line of centre of object, which is a half-width away
713 # from the end position
716 # <-- start pos <-----><-- positionOnLineX
718 # --------------| x | <-- e.g. rectangular arrowhead
721 x
, y
= GetPointOnLine(startPositionX
, startPositionY
,
722 positionOnLineX
, positionOnLineY
,
723 arrow
.GetMetaFile()._width
/ 2.0)
724 # Calculate theta for rotating the metafile.
727 # | o(x2, y2) 'o' represents the arrowhead.
732 # |______________________
737 x2
= float(positionOnLineX
)
738 y2
= float(positionOnLineY
)
740 if x1
== x2
and y1
== y2
:
742 elif x1
== x2
and y1
> y2
:
743 theta
= 3.0 * math
.pi
/ 2.0
744 elif x1
== x2
and y2
> y1
:
745 theta
= math
.pi
/ 2.0
746 elif x2
> x1
and y2
>= y1
:
747 theta
= math
.atan((y2
- y1
) / (x2
- x1
))
749 theta
= math
.pi
+ math
.atan((y2
- y1
) / (x2
- x1
))
750 elif x2
> x1
and y2
< y1
:
751 theta
= 2 * math
.pi
+ math
.atan((y2
- y1
) / (x2
- x1
))
753 raise "Unknown arrowhead rotation case"
755 # Rotate about the centre of the object, then place
756 # the object on the line.
757 if arrow
.GetMetaFile().GetRotateable():
758 arrow
.GetMetaFile().Rotate(0.0, 0.0, theta
)
761 # If erasing, just draw a rectangle
762 minX
, minY
, maxX
, maxY
= arrow
.GetMetaFile().GetBounds()
763 # Make erasing rectangle slightly bigger or you get droppings
765 dc
.DrawRectangle(deltaX
+ x
+ minX
- extraPixels
/ 2.0, deltaY
+ y
+ minY
- extraPixels
/ 2.0, maxX
- minX
+ extraPixels
, maxY
- minY
+ extraPixels
)
767 arrow
.GetMetaFile().Draw(dc
, x
+ deltaX
, y
+ deltaY
)
769 def OnErase(self
, dc
):
771 old_brush
= self
._brush
773 bg_pen
= self
.GetBackgroundPen()
774 bg_brush
= self
.GetBackgroundBrush()
776 self
.SetBrush(bg_brush
)
778 bound_x
, bound_y
= self
.GetBoundingBoxMax()
780 dc
.SetFont(self
._font
)
782 # Undraw text regions
785 x
, y
= self
.GetLabelPosition(i
)
786 self
.EraseRegion(dc
, self
._regions
[i
], x
, y
)
789 dc
.SetPen(self
.GetBackgroundPen())
790 dc
.SetBrush(self
.GetBackgroundBrush())
792 # Drawing over the line only seems to work if the line has a thickness
794 if old_pen
and old_pen
.GetWidth() > 1:
795 dc
.DrawRectangle(self
._xpos
- bound_x
/ 2.0 - 2, self
._ypos
- bound_y
/ 2.0 - 2,
796 bound_x
+ 4, bound_y
+ 4)
799 self
.GetEventHandler().OnDraw(dc
)
800 self
.GetEventHandler().OnEraseControlPoints(dc
)
801 self
._erasing
= False
806 self
.SetBrush(old_brush
)
808 def GetBoundingBoxMin(self
):
809 x1
, y1
= 10000, 10000
810 x2
, y2
= -10000, -10000
812 for point
in self
._lineControlPoints
:
822 return x2
- x1
, y2
- y1
824 # For a node image of interest, finds the position of this arc
825 # amongst all the arcs which are attached to THIS SIDE of the node image,
826 # and the number of same.
827 def FindNth(self
, image
, incoming
):
828 """Find the position of the line on the given object.
830 Specify whether incoming or outgoing lines are being considered
836 if image
== self
._to
:
837 this_attachment
= self
._attachmentTo
839 this_attachment
= self
._attachmentFrom
841 # Find number of lines going into / out of this particular attachment point
842 for line
in image
.GetLines():
843 if line
._from
== image
:
844 # This is the nth line attached to 'image'
845 if line
== self
and not incoming
:
848 # Increment num count if this is the same side (attachment number)
849 if line
._attachmentFrom
== this_attachment
:
852 if line
._to
== image
:
853 # This is the nth line attached to 'image'
854 if line
== self
and incoming
:
857 # Increment num count if this is the same side (attachment number)
858 if line
._attachmentTo
== this_attachment
:
863 def OnDrawOutline(self
, dc
, x
, y
, w
, h
):
865 old_brush
= self
._brush
867 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
868 self
.SetPen(dottedPen
)
869 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
871 self
.GetEventHandler().OnDraw(dc
)
878 self
.SetBrush(old_brush
)
882 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
= True):
886 if self
._lineControlPoints
and not (x_offset
== 0 and y_offset
== 0):
887 for point
in self
._lineControlPoints
:
891 # Move temporary label rectangles if necessary
893 if self
._labelObjects
[i
]:
894 self
._labelObjects
[i
].Erase(dc
)
895 xp
, yp
= self
.GetLabelPosition(i
)
896 if i
< len(self
._regions
):
897 xr
, yr
= self
._regions
[i
].GetPosition()
900 self
._labelObjects
[i
].Move(dc
, xp
+ xr
, yp
+ yr
)
903 def OnMoveLink(self
, dc
, moveControlPoints
= True):
904 """Called when a connected object has moved, to move the link to
907 if not self
._from
or not self
._to
:
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 if len(self
._lineControlPoints
) > 2:
921 # Do a second time, because one may depend on the other
922 end_x
, end_y
, other_end_x
, other_end_y
= self
.FindLineEndPoints()
923 self
.SetEnds(end_x
, end_y
, other_end_x
, other_end_y
)
925 # Try to move control points with the arc
926 x_offset
= self
._xpos
- oldX
927 y_offset
= self
._ypos
- oldY
929 # Only move control points if it's a self link. And only works
930 # if attachment mode is ON
931 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):
932 for point
in self
._lineControlPoints
[1:-1]:
936 self
.Move(dc
, self
._xpos
, self
._ypos
)
938 def FindLineEndPoints(self
):
939 """Finds the x, y points at the two ends of the line.
941 This function can be used by e.g. line-routing routines to
942 get the actual points on the two node images where the lines will be
945 if not self
._from
or not self
._to
:
948 # Do each end - nothing in the middle. User has to move other points
949 # manually if necessary.
950 second_point
= self
._lineControlPoints
[1]
951 second_last_point
= self
._lineControlPoints
[-2]
953 if len(self
._lineControlPoints
) > 2 and self
._initialised
:
954 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
955 nth
, no_arcs
= self
.FindNth(self
._from
, False) # Not incoming
956 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
958 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), second_point
[0], second_point
[1])
960 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
961 nth
, no_arch
= self
.FindNth(self
._to
, True) # Incoming
962 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arch
, self
)
964 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), second_last_point
[0], second_last_point
[1])
966 fromX
= self
._from
.GetX()
967 fromY
= self
._from
.GetY()
968 toX
= self
._to
.GetX()
969 toY
= self
._to
.GetY()
971 if self
._from
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
972 nth
, no_arcs
= self
.FindNth(self
._from
, False)
973 end_x
, end_y
= self
._from
.GetAttachmentPosition(self
._attachmentFrom
, nth
, no_arcs
, self
)
977 if self
._to
.GetAttachmentMode() != ATTACHMENT_MODE_NONE
:
978 nth
, no_arcs
= self
.FindNth(self
._to
, True)
979 other_end_x
, other_end_y
= self
._to
.GetAttachmentPosition(self
._attachmentTo
, nth
, no_arcs
, self
)
983 if self
._from
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
984 end_x
, end_y
= self
._from
.GetPerimeterPoint(self
._from
.GetX(), self
._from
.GetY(), toX
, toY
)
986 if self
._to
.GetAttachmentMode() == ATTACHMENT_MODE_NONE
:
987 other_end_x
, other_end_y
= self
._to
.GetPerimeterPoint(self
._to
.GetX(), self
._to
.GetY(), fromX
, fromY
)
989 return end_x
, end_y
, other_end_x
, other_end_y
992 def OnDraw(self
, dc
):
993 if not self
._lineControlPoints
:
999 dc
.SetBrush(self
._brush
)
1002 for point
in self
._lineControlPoints
:
1003 points
.append(wx
.Point(point
[0], point
[1]))
1006 dc
.DrawSpline(points
)
1008 dc
.DrawLines(points
)
1010 if sys
.platform
[:3] == "win":
1011 # For some reason, last point isn't drawn under Windows
1013 dc
.DrawPoint(pt
[0], pt
[1])
1015 # Problem with pen - if not a solid pen, does strange things
1016 # to the arrowhead. So make (get) a new pen that's solid.
1017 if self
._pen
and self
._pen
.GetStyle() != wx
.SOLID
:
1018 solid_pen
= wx
.ThePenList
.FindOrCreatePen(self
._pen
.GetColour(), 1, wx
.SOLID
)
1020 dc
.SetPen(solid_pen
)
1024 def OnDrawControlPoints(self
, dc
):
1025 if not self
._drawHandles
:
1028 # Draw temporary label rectangles if necessary
1030 if self
._labelObjects
[i
]:
1031 self
._labelObjects
[i
].Draw(dc
)
1033 Shape
.OnDrawControlPoints(self
, dc
)
1035 def OnEraseControlPoints(self
, dc
):
1036 # Erase temporary label rectangles if necessary
1039 if self
._labelObjects
[i
]:
1040 self
._labelObjects
[i
].Erase(dc
)
1042 Shape
.OnEraseControlPoints(self
, dc
)
1044 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1047 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1050 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1053 def OnDrawContents(self
, dc
):
1054 if self
.GetDisableLabel():
1058 if self
._regions
[i
]:
1059 x
, y
= self
.GetLabelPosition(i
)
1060 self
.DrawRegion(dc
, self
._regions
[i
], x
, y
)
1062 def SetTo(self
, object):
1063 """Set the 'to' object for the line."""
1066 def SetFrom(self
, object):
1067 """Set the 'from' object for the line."""
1070 def MakeControlPoints(self
):
1071 """Make handle control points."""
1072 if self
._canvas
and self
._lineControlPoints
:
1073 first
= self
._lineControlPoints
[0]
1074 last
= self
._lineControlPoints
[-1]
1076 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, first
[0], first
[1], CONTROL_POINT_ENDPOINT_FROM
)
1077 control
._point
= first
1078 self
._canvas
.AddShape(control
)
1079 self
._controlPoints
.append(control
)
1081 for point
in self
._lineControlPoints
[1:-1]:
1082 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, point
[0], point
[1], CONTROL_POINT_LINE
)
1083 control
._point
= point
1084 self
._canvas
.AddShape(control
)
1085 self
._controlPoints
.append(control
)
1087 control
= LineControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, last
[0], last
[1], CONTROL_POINT_ENDPOINT_TO
)
1088 control
._point
= last
1089 self
._canvas
.AddShape(control
)
1090 self
._controlPoints
.append(control
)
1092 def ResetControlPoints(self
):
1093 if self
._canvas
and self
._lineControlPoints
and self
._controlPoints
:
1094 for i
in range(min(len(self
._controlPoints
), len(self
._lineControlPoints
))):
1095 point
= self
._lineControlPoints
[i
]
1096 control
= self
._controlPoints
[i
]
1097 control
.SetX(point
[0])
1098 control
.SetY(point
[1])
1100 # Override select, to create / delete temporary label-moving objects
1101 def Select(self
, select
, dc
= None):
1102 Shape
.Select(self
, select
, dc
)
1105 if self
._regions
[i
]:
1106 region
= self
._regions
[i
]
1107 if region
._formattedText
:
1108 w
, h
= region
.GetSize()
1109 x
, y
= region
.GetPosition()
1110 xx
, yy
= self
.GetLabelPosition(i
)
1112 if self
._labelObjects
[i
]:
1113 self
._labelObjects
[i
].Select(False)
1114 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1116 self
._labelObjects
[i
] = self
.OnCreateLabelShape(self
, region
, w
, h
)
1117 self
._labelObjects
[i
].AddToCanvas(self
._canvas
)
1118 self
._labelObjects
[i
].Show(True)
1120 self
._labelObjects
[i
].Move(dc
, x
+ xx
, y
+ yy
)
1121 self
._labelObjects
[i
].Select(True, dc
)
1124 if self
._labelObjects
[i
]:
1125 self
._labelObjects
[i
].Select(False, dc
)
1126 self
._labelObjects
[i
].Erase(dc
)
1127 self
._labelObjects
[i
].RemoveFromCanvas(self
._canvas
)
1128 self
._labelObjects
[i
] = None
1130 # Control points ('handles') redirect control to the actual shape, to
1131 # make it easier to override sizing behaviour.
1132 def OnSizingDragLeft(self
, pt
, draw
, x
, y
, keys
= 0, attachment
= 0):
1133 dc
= wx
.ClientDC(self
.GetCanvas())
1134 self
.GetCanvas().PrepareDC(dc
)
1136 dc
.SetLogicalFunction(OGLRBLF
)
1138 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1139 dc
.SetPen(dottedPen
)
1140 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1142 if pt
._type
== CONTROL_POINT_LINE
:
1143 x
, y
= self
._canvas
.Snap(x
, y
)
1150 old_pen
= self
.GetPen()
1151 old_brush
= self
.GetBrush()
1153 self
.SetPen(dottedPen
)
1154 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1156 self
.GetEventHandler().OnMoveLink(dc
, False)
1158 self
.SetPen(old_pen
)
1159 self
.SetBrush(old_brush
)
1161 def OnSizingBeginDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1162 dc
= wx
.ClientDC(self
.GetCanvas())
1163 self
.GetCanvas().PrepareDC(dc
)
1165 if pt
._type
== CONTROL_POINT_LINE
:
1166 pt
._originalPos
= pt
._point
1167 x
, y
= self
._canvas
.Snap(x
, y
)
1171 # Redraw start and end objects because we've left holes
1172 # when erasing the line
1173 self
.GetFrom().OnDraw(dc
)
1174 self
.GetFrom().OnDrawContents(dc
)
1175 self
.GetTo().OnDraw(dc
)
1176 self
.GetTo().OnDrawContents(dc
)
1178 self
.SetDisableLabel(True)
1179 dc
.SetLogicalFunction(OGLRBLF
)
1186 old_pen
= self
.GetPen()
1187 old_brush
= self
.GetBrush()
1189 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
1190 self
.SetPen(dottedPen
)
1191 self
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1193 self
.GetEventHandler().OnMoveLink(dc
, False)
1195 self
.SetPen(old_pen
)
1196 self
.SetBrush(old_brush
)
1198 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
or pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1199 self
._canvas
.SetCursor(wx
.StockCursor(wx
.CURSOR_BULLSEYE
))
1200 pt
._oldCursor
= wx
.STANDARD_CURSOR
1202 def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
= 0, attachment
= 0):
1203 dc
= wx
.ClientDC(self
.GetCanvas())
1204 self
.GetCanvas().PrepareDC(dc
)
1206 self
.SetDisableLabel(False)
1208 if pt
._type
== CONTROL_POINT_LINE
:
1209 x
, y
= self
._canvas
.Snap(x
, y
)
1211 rpt
= wx
.RealPoint(x
, y
)
1213 # Move the control point back to where it was;
1214 # MoveControlPoint will move it to the new position
1215 # if it decides it wants. We only moved the position
1216 # during user feedback so we could redraw the line
1217 # as it changed shape.
1218 pt
._xpos
= pt
._originalPos
[0]
1219 pt
._ypos
= pt
._originalPos
[1]
1220 pt
._point
[0] = pt
._originalPos
[0]
1221 pt
._point
[1] = pt
._originalPos
[1]
1223 self
.OnMoveMiddleControlPoint(dc
, pt
, rpt
)
1225 if pt
._type
== CONTROL_POINT_ENDPOINT_FROM
:
1227 self
._canvas
.SetCursor(pt
._oldCursor
)
1230 self
.GetFrom().MoveLineToNewAttachment(dc
, self
, x
, y
)
1232 if pt
._type
== CONTROL_POINT_ENDPOINT_TO
:
1234 self
._canvas
.SetCursor(pt
._oldCursor
)
1237 self
.GetTo().MoveLineToNewAttachment(dc
, self
, x
, y
)
1239 # This is called only when a non-end control point is moved
1240 def OnMoveMiddleControlPoint(self
, dc
, lpt
, pt
):
1244 lpt
._point
[0] = pt
[0]
1245 lpt
._point
[1] = pt
[1]
1247 self
.GetEventHandler().OnMoveLink(dc
)
1251 def AddArrow(self
, type, end
= ARROW_POSITION_END
, size
= 10.0, xOffset
= 0.0, name
= "", mf
= None, arrowId
= -1):
1252 """Add an arrow (or annotation) to the line.
1254 type may currently be one of:
1261 Conventional arrowhead.
1262 ARROW_SINGLE_OBLIQUE
1263 Single oblique stroke.
1264 ARROW_DOUBLE_OBLIQUE
1265 Double oblique stroke.
1266 ARROW_DOUBLE_METAFILE
1269 end may currently be one of:
1272 Arrow appears at the end.
1273 ARROW_POSITION_START
1274 Arrow appears at the start.
1276 arrowSize specifies the length of the arrow.
1278 xOffset specifies the offset from the end of the line.
1280 name specifies a name for the arrow.
1282 mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
1285 arrowId is the id for the arrow.
1287 arrow
= ArrowHead(type, end
, size
, xOffset
, name
, mf
, arrowId
)
1288 self
._arcArrows
.append(arrow
)
1291 # Add arrowhead at a particular position in the arrowhead list
1292 def AddArrowOrdered(self
, arrow
, referenceList
, end
):
1293 """Add an arrowhead in the position indicated by the reference list
1294 of arrowheads, which contains all legal arrowheads for this line, in
1295 the correct order. E.g.
1297 Reference list: a b c d e
1298 Current line list: a d
1300 Add c, then line list is: a c d.
1302 If no legal arrowhead position, return FALSE. Assume reference list
1303 is for one end only, since it potentially defines the ordering for
1304 any one of the 3 positions. So we don't check the reference list for
1307 if not referenceList
:
1310 targetName
= arrow
.GetName()
1312 # First check whether we need to insert in front of list,
1313 # because this arrowhead is the first in the reference
1314 # list and should therefore be first in the current list.
1315 refArrow
= referenceList
[0]
1316 if refArrow
.GetName() == targetName
:
1317 self
._arcArrows
.insert(0, arrow
)
1321 while i1
< len(referenceList
) and i2
< len(self
._arcArrows
):
1322 refArrow
= referenceList
[i1
]
1323 currArrow
= self
._arcArrows
[i2
]
1325 # Matching: advance current arrow pointer
1326 if currArrow
.GetArrowEnd() == end
and currArrow
.GetName() == refArrow
.GetName():
1329 # Check if we're at the correct position in the
1331 if targetName
== refArrow
.GetName():
1332 if i2
< len(self
._arcArrows
):
1333 self
._arcArrows
.insert(i2
, arrow
)
1335 self
._arcArrows
.append(arrow
)
1339 self
._arcArrows
.append(arrow
)
1342 def ClearArrowsAtPosition(self
, end
):
1343 """Delete the arrows at the specified position, or at any position
1347 self
._arcArrows
= []
1350 for arrow
in self
._arcArrows
:
1351 if arrow
.GetArrowEnd() == end
:
1352 self
._arcArrows
.remove(arrow
)
1354 def ClearArrow(self
, name
):
1355 """Delete the arrow with the given name."""
1356 for arrow
in self
._arcArrows
:
1357 if arrow
.GetName() == name
:
1358 self
._arcArrows
.remove(arrow
)
1362 def FindArrowHead(self
, position
, name
):
1363 """Find arrowhead by position and name.
1365 if position is -1, matches any position.
1367 for arrow
in self
._arcArrows
:
1368 if (position
== -1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1373 def FindArrowHeadId(self
, arrowId
):
1374 """Find arrowhead by id."""
1375 for arrow
in self
._arcArrows
:
1376 if arrowId
== arrow
.GetId():
1381 def DeleteArrowHead(self
, position
, name
):
1382 """Delete arrowhead by position and name.
1384 if position is -1, matches any position.
1386 for arrow
in self
._arcArrows
:
1387 if (position
== -1 or position
== arrow
.GetArrowEnd()) and arrow
.GetName() == name
:
1388 self
._arcArrows
.remove(arrow
)
1392 def DeleteArrowHeadId(self
, id):
1393 """Delete arrowhead by id."""
1394 for arrow
in self
._arcArrows
:
1395 if arrowId
== arrow
.GetId():
1396 self
._arcArrows
.remove(arrow
)
1400 # Calculate the minimum width a line
1401 # occupies, for the purposes of drawing lines in tools.
1402 def FindMinimumWidth(self
):
1403 """Find the horizontal width for drawing a line with arrows in
1404 minimum space. Assume arrows at end only.
1407 for arrowHead
in self
._arcArrows
:
1408 minWidth
+= arrowHead
.GetSize()
1409 if arrowHead
!= self
._arcArrows
[-1]:
1410 minWidth
+= arrowHead
+ GetSpacing
1412 # We have ABSOLUTE minimum now. So
1413 # scale it to give it reasonable aesthetics
1414 # when drawing with line.
1416 minWidth
= minWidth
* 1.4
1420 self
.SetEnds(0.0, 0.0, minWidth
, 0.0)
1425 def FindLinePosition(self
, x
, y
):
1426 """Find which position we're talking about at this x, y.
1428 Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
1430 startX
, startY
, endX
, endY
= self
.GetEnds()
1432 # Find distances from centre, start and end. The smallest wins
1433 centreDistance
= math
.sqrt((x
- self
._xpos
) * (x
- self
._xpos
) + (y
- self
._ypos
) * (y
- self
._ypos
))
1434 startDistance
= math
.sqrt((x
- startX
) * (x
- startX
) + (y
- startY
) * (y
- startY
))
1435 endDistance
= math
.sqrt((x
- endX
) * (x
- endX
) + (y
- endY
) * (y
- endY
))
1437 if centreDistance
< startDistance
and centreDistance
< endDistance
:
1438 return ARROW_POSITION_MIDDLE
1439 elif startDistance
< endDistance
:
1440 return ARROW_POSITION_START
1442 return ARROW_POSITION_END
1444 def SetAlignmentOrientation(self
, isEnd
, isHoriz
):
1446 if isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1447 self
._alignmentEnd
!= LINE_ALIGNMENT_HORIZ
1448 elif not isHoriz
and self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1449 self
._alignmentEnd
-= LINE_ALIGNMENT_HORIZ
1451 if isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
!= LINE_ALIGNMENT_HORIZ
:
1452 self
._alignmentStart
!= LINE_ALIGNMENT_HORIZ
1453 elif not isHoriz
and self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
:
1454 self
._alignmentStart
-= LINE_ALIGNMENT_HORIZ
1456 def SetAlignmentType(self
, isEnd
, alignType
):
1458 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1459 if self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1460 self
._alignmentEnd |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1461 elif self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1462 self
._alignmentEnd
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1464 if alignType
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1465 if self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
!= LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1466 self
._alignmentStart |
= LINE_ALIGNMENT_TO_NEXT_HANDLE
1467 elif self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
== LINE_ALIGNMENT_TO_NEXT_HANDLE
:
1468 self
._alignmentStart
-= LINE_ALIGNMENT_TO_NEXT_HANDLE
1470 def GetAlignmentOrientation(self
, isEnd
):
1472 return self
._alignmentEnd
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1474 return self
._alignmentStart
& LINE_ALIGNMENT_HORIZ
== LINE_ALIGNMENT_HORIZ
1476 def GetAlignmentType(self
, isEnd
):
1478 return self
._alignmentEnd
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1480 return self
._alignmentStart
& LINE_ALIGNMENT_TO_NEXT_HANDLE
1482 def GetNextControlPoint(self
, shape
):
1483 """Find the next control point in the line after the start / end point,
1484 depending on whether the shape is at the start or end.
1486 n
= len(self
._lineControlPoints
)
1487 if self
._to
== shape
:
1488 # Must be END of line, so we want (n - 1)th control point.
1489 # But indexing ends at n-1, so subtract 2.
1493 if nn
< len(self
._lineControlPoints
):
1494 return self
._lineControlPoints
[nn
]
1497 def OnCreateLabelShape(self
, parent
, region
, w
, h
):
1498 return LabelShape(parent
, region
, w
, h
)
1501 def OnLabelMovePre(self
, dc
, labelShape
, x
, y
, old_x
, old_y
, display
):
1502 labelShape
._shapeRegion
.SetSize(labelShape
.GetWidth(), labelShape
.GetHeight())
1504 # Find position in line's region list
1506 for region
in self
.GetRegions():
1507 if labelShape
._shapeRegion
== region
:
1508 self
.GetRegions().remove(region
)
1512 xx
, yy
= self
.GetLabelPosition(i
)
1513 # Set the region's offset, relative to the default position for
1515 labelShape
._shapeRegion
.SetPosition(x
- xx
, y
- yy
)
1519 # Need to reformat to fit region
1520 if labelShape
._shapeRegion
.GetText():
1521 s
= labelShape
._shapeRegion
.GetText()
1522 labelShape
.FormatText(dc
, s
, i
)
1523 self
.DrawRegion(dc
, labelShape
._shapeRegion
, xx
, yy
)