1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
4 # Purpose: Composite 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 RectangleShape
, Shape
, ControlPoint
22 KEY_SHIFT
, KEY_CTRL
= 1, 2
27 CONSTRAINT_CENTRED_VERTICALLY
= 1
28 CONSTRAINT_CENTRED_HORIZONTALLY
= 2
29 CONSTRAINT_CENTRED_BOTH
= 3
30 CONSTRAINT_LEFT_OF
= 4
31 CONSTRAINT_RIGHT_OF
= 5
34 CONSTRAINT_ALIGNED_TOP
= 8
35 CONSTRAINT_ALIGNED_BOTTOM
= 9
36 CONSTRAINT_ALIGNED_LEFT
= 10
37 CONSTRAINT_ALIGNED_RIGHT
= 11
39 # Like aligned, but with the objects centred on the respective edge
40 # of the reference object.
41 CONSTRAINT_MIDALIGNED_TOP
= 12
42 CONSTRAINT_MIDALIGNED_BOTTOM
= 13
43 CONSTRAINT_MIDALIGNED_LEFT
= 14
44 CONSTRAINT_MIDALIGNED_RIGHT
= 15
47 # Backwards compatibility names. These should be removed eventually.
48 gyCONSTRAINT_CENTRED_VERTICALLY
= CONSTRAINT_CENTRED_VERTICALLY
49 gyCONSTRAINT_CENTRED_HORIZONTALLY
= CONSTRAINT_CENTRED_HORIZONTALLY
50 gyCONSTRAINT_CENTRED_BOTH
= CONSTRAINT_CENTRED_BOTH
51 gyCONSTRAINT_LEFT_OF
= CONSTRAINT_LEFT_OF
52 gyCONSTRAINT_RIGHT_OF
= CONSTRAINT_RIGHT_OF
53 gyCONSTRAINT_ABOVE
= CONSTRAINT_ABOVE
54 gyCONSTRAINT_BELOW
= CONSTRAINT_BELOW
55 gyCONSTRAINT_ALIGNED_TOP
= CONSTRAINT_ALIGNED_TOP
56 gyCONSTRAINT_ALIGNED_BOTTOM
= CONSTRAINT_ALIGNED_BOTTOM
57 gyCONSTRAINT_ALIGNED_LEFT
= CONSTRAINT_ALIGNED_LEFT
58 gyCONSTRAINT_ALIGNED_RIGHT
= CONSTRAINT_ALIGNED_RIGHT
59 gyCONSTRAINT_MIDALIGNED_TOP
= CONSTRAINT_MIDALIGNED_TOP
60 gyCONSTRAINT_MIDALIGNED_BOTTOM
= CONSTRAINT_MIDALIGNED_BOTTOM
61 gyCONSTRAINT_MIDALIGNED_LEFT
= CONSTRAINT_MIDALIGNED_LEFT
62 gyCONSTRAINT_MIDALIGNED_RIGHT
= CONSTRAINT_MIDALIGNED_RIGHT
66 class ConstraintType(object):
67 def __init__(self
, theType
, theName
, thePhrase
):
70 self
._phrase
= thePhrase
75 [CONSTRAINT_CENTRED_VERTICALLY
,
76 ConstraintType(CONSTRAINT_CENTRED_VERTICALLY
, "Centre vertically", "centred vertically w.r.t.")],
78 [CONSTRAINT_CENTRED_HORIZONTALLY
,
79 ConstraintType(CONSTRAINT_CENTRED_HORIZONTALLY
, "Centre horizontally", "centred horizontally w.r.t.")],
81 [CONSTRAINT_CENTRED_BOTH
,
82 ConstraintType(CONSTRAINT_CENTRED_BOTH
, "Centre", "centred w.r.t.")],
85 ConstraintType(CONSTRAINT_LEFT_OF
, "Left of", "left of")],
88 ConstraintType(CONSTRAINT_RIGHT_OF
, "Right of", "right of")],
91 ConstraintType(CONSTRAINT_ABOVE
, "Above", "above")],
94 ConstraintType(CONSTRAINT_BELOW
, "Below", "below")],
97 [CONSTRAINT_ALIGNED_TOP
,
98 ConstraintType(CONSTRAINT_ALIGNED_TOP
, "Top-aligned", "aligned to the top of")],
100 [CONSTRAINT_ALIGNED_BOTTOM
,
101 ConstraintType(CONSTRAINT_ALIGNED_BOTTOM
, "Bottom-aligned", "aligned to the bottom of")],
103 [CONSTRAINT_ALIGNED_LEFT
,
104 ConstraintType(CONSTRAINT_ALIGNED_LEFT
, "Left-aligned", "aligned to the left of")],
106 [CONSTRAINT_ALIGNED_RIGHT
,
107 ConstraintType(CONSTRAINT_ALIGNED_RIGHT
, "Right-aligned", "aligned to the right of")],
110 [CONSTRAINT_MIDALIGNED_TOP
,
111 ConstraintType(CONSTRAINT_MIDALIGNED_TOP
, "Top-midaligned", "centred on the top of")],
113 [CONSTRAINT_MIDALIGNED_BOTTOM
,
114 ConstraintType(CONSTRAINT_MIDALIGNED_BOTTOM
, "Bottom-midaligned", "centred on the bottom of")],
116 [CONSTRAINT_MIDALIGNED_LEFT
,
117 ConstraintType(CONSTRAINT_MIDALIGNED_LEFT
, "Left-midaligned", "centred on the left of")],
119 [CONSTRAINT_MIDALIGNED_RIGHT
,
120 ConstraintType(CONSTRAINT_MIDALIGNED_RIGHT
, "Right-midaligned", "centred on the right of")]
126 class Constraint(object):
127 """A Constraint object helps specify how child shapes are laid out with
128 respect to siblings and parents.
133 def __init__(self
, type, constraining
, constrained
):
137 self
._constraintType
= type
138 self
._constraintingObject
= constraining
140 self
._constraintId
= 0
141 self
._constraintName
="noname"
143 self
._constrainedObjects
= constrained
[:]
146 return "<%s.%s>" % (self
.__class
__.__module
__, self
.__class
__.__name
__)
148 def SetSpacing(self
, x
, y
):
149 """Sets the horizontal and vertical spacing for the constraint."""
153 def Equals(self
, a
, b
):
154 """Return TRUE if x and y are approximately equal (for the purposes
155 of evaluating the constraint).
159 return b
<= a
+ marg
and b
>= a
-marg
162 """Evaluate this constraint and return TRUE if anything changed."""
163 maxWidth
, maxHeight
= self
._constraintingObject
.GetBoundingBoxMax()
164 minWidth
, minHeight
= self
._constraintingObject
.GetBoundingBoxMin()
165 x
= self
._constraintingObject
.GetX()
166 y
= self
._constraintingObject
.GetY()
168 dc
= wx
.ClientDC(self
._constraintingObject
.GetCanvas())
169 self
._constraintingObject
.GetCanvas().PrepareDC(dc
)
171 if self
._constraintType
== CONSTRAINT_CENTRED_VERTICALLY
:
172 n
= len(self
._constrainedObjects
)
173 totalObjectHeight
= 0.0
174 for constrainedObject
in self
._constrainedObjects
:
175 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
176 totalObjectHeight
+= height2
178 # Check if within the constraining object...
179 if totalObjectHeight
+ (n
+ 1) * self
._ySpacing
<= minHeight
:
180 spacingY
= (minHeight
-totalObjectHeight
) / (n
+ 1)
181 startY
= y
-minHeight
/ 2
182 else: # Otherwise, use default spacing
183 spacingY
= self
._ySpacing
184 startY
= y
-(totalObjectHeight
+ (n
+ 1) * spacingY
) / 2
186 # Now position the objects
188 for constrainedObject
in self
._constrainedObjects
:
189 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
190 startY
+= spacingY
+ height2
/ 2
191 if not self
.Equals(startY
, constrainedObject
.GetY()):
192 constrainedObject
.Move(dc
, constrainedObject
.GetX(), startY
, False)
194 startY
+= height2
/ 2
196 elif self
._constraintType
== CONSTRAINT_CENTRED_HORIZONTALLY
:
197 n
= len(self
._constrainedObjects
)
198 totalObjectWidth
= 0.0
199 for constrainedObject
in self
._constrainedObjects
:
200 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
201 totalObjectWidth
+= width2
203 # Check if within the constraining object...
204 if totalObjectWidth
+ (n
+ 1) * self
._xSpacing
<minWidth
:
205 spacingX
= (minWidth
-totalObjectWidth
) / (n
+ 1)
206 startX
= x
-minWidth
/ 2
207 else: # Otherwise, use default spacing
208 spacingX
= self
._xSpacing
209 startX
= x
-(totalObjectWidth
+ (n
+ 1) * spacingX
) / 2
211 # Now position the objects
213 for constrainedObject
in self
._constrainedObjects
:
214 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
215 startX
+= spacingX
+ width2
/ 2
216 if not self
.Equals(startX
, constrainedObject
.GetX()):
217 constrainedObject
.Move(dc
, startX
, constrainedObject
.GetY(), False)
221 elif self
._constraintType
== CONSTRAINT_CENTRED_BOTH
:
222 n
= len(self
._constrainedObjects
)
223 totalObjectWidth
= 0.0
224 totalObjectHeight
= 0.0
226 for constrainedObject
in self
._constrainedObjects
:
227 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
228 totalObjectWidth
+= width2
229 totalObjectHeight
+= height2
231 # Check if within the constraining object...
232 if totalObjectHeight
+ (n
+ 1) * self
._xSpacing
<= minWidth
:
233 spacingX
= (minWidth
-totalObjectWidth
) / (n
+ 1)
234 startX
= x
-minWidth
/ 2
235 else: # Otherwise, use default spacing
236 spacingX
= self
._xSpacing
237 startX
= x
-(totalObjectWidth
+ (n
+ 1) * spacingX
) / 2
239 # Check if within the constraining object...
240 if totalObjectHeight
+ (n
+ 1) * self
._ySpacing
<= minHeight
:
241 spacingY
= (minHeight
-totalObjectHeight
) / (n
+ 1)
242 startY
= y
-minHeight
/ 2
243 else: # Otherwise, use default spacing
244 spacingY
= self
._ySpacing
245 startY
= y
-(totalObjectHeight
+ (n
+ 1) * spacingY
) / 2
247 # Now position the objects
249 for constrainedObject
in self
._constrainedObjects
:
250 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
251 startX
+= spacingX
+ width2
/ 2
252 startY
+= spacingY
+ height2
/ 2
254 if not self
.Equals(startX
, constrainedObject
.GetX()) or not self
.Equals(startY
, constrainedObject
.GetY()):
255 constrainedObject
.Move(dc
, startX
, startY
, False)
259 startY
+= height2
/ 2
261 elif self
._constraintType
== CONSTRAINT_LEFT_OF
:
263 for constrainedObject
in self
._constrainedObjects
:
264 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
266 x3
= x
-minWidth
/ 2-width2
/ 2-self
._xSpacing
267 if not self
.Equals(x3
, constrainedObject
.GetX()):
269 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
271 elif self
._constraintType
== CONSTRAINT_RIGHT_OF
:
274 for constrainedObject
in self
._constrainedObjects
:
275 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
276 x3
= x
+ minWidth
/ 2 + width2
/ 2 + self
._xSpacing
277 if not self
.Equals(x3
, constrainedObject
.GetX()):
278 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
281 elif self
._constraintType
== CONSTRAINT_ABOVE
:
284 for constrainedObject
in self
._constrainedObjects
:
285 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
287 y3
= y
-minHeight
/ 2-height2
/ 2-self
._ySpacing
288 if not self
.Equals(y3
, constrainedObject
.GetY()):
290 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
292 elif self
._constraintType
== CONSTRAINT_BELOW
:
295 for constrainedObject
in self
._constrainedObjects
:
296 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
298 y3
= y
+ minHeight
/ 2 + height2
/ 2 + self
._ySpacing
299 if not self
.Equals(y3
, constrainedObject
.GetY()):
301 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
303 elif self
._constraintType
== CONSTRAINT_ALIGNED_LEFT
:
305 for constrainedObject
in self
._constrainedObjects
:
306 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
307 x3
= x
-minWidth
/ 2 + width2
/ 2 + self
._xSpacing
308 if not self
.Equals(x3
, constrainedObject
.GetX()):
310 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
312 elif self
._constraintType
== CONSTRAINT_ALIGNED_RIGHT
:
314 for constrainedObject
in self
._constrainedObjects
:
315 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
316 x3
= x
+ minWidth
/ 2-width2
/ 2-self
._xSpacing
317 if not self
.Equals(x3
, constrainedObject
.GetX()):
319 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
321 elif self
._constraintType
== CONSTRAINT_ALIGNED_TOP
:
323 for constrainedObject
in self
._constrainedObjects
:
324 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
325 y3
= y
-minHeight
/ 2 + height2
/ 2 + self
._ySpacing
326 if not self
.Equals(y3
, constrainedObject
.GetY()):
328 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
330 elif self
._constraintType
== CONSTRAINT_ALIGNED_BOTTOM
:
332 for constrainedObject
in self
._constrainedObjects
:
333 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
334 y3
= y
+ minHeight
/ 2-height2
/ 2-self
._ySpacing
335 if not self
.Equals(y3
, constrainedObject
.GetY()):
337 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
339 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_LEFT
:
341 for constrainedObject
in self
._constrainedObjects
:
343 if not self
.Equals(x3
, constrainedObject
.GetX()):
345 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
347 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_RIGHT
:
349 for constrainedObject
in self
._constrainedObjects
:
350 x3
= x
+ minWidth
/ 2
351 if not self
.Equals(x3
, constrainedObject
.GetX()):
353 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
355 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_TOP
:
357 for constrainedObject
in self
._constrainedObjects
:
359 if not self
.Equals(y3
, constrainedObject
.GetY()):
361 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
363 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_BOTTOM
:
365 for constrainedObject
in self
._constrainedObjects
:
366 y3
= y
+ minHeight
/ 2
367 if not self
.Equals(y3
, constrainedObject
.GetY()):
369 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
374 OGLConstraint
= wx
._core
._deprecated
(Constraint
,
375 "The OGLConstraint name is deprecated, use `ogl.Constraint` instead.")
378 class CompositeShape(RectangleShape
):
379 """This is an object with a list of child objects, and a list of size
380 and positioning constraints between the children.
386 RectangleShape
.__init
__(self
, 100.0, 100.0)
388 self
._oldX
= self
._xpos
389 self
._oldY
= self
._ypos
391 self
._constraints
= []
392 self
._divisions
= [] # In case it's a container
394 def OnDraw(self
, dc
):
395 x1
= self
._xpos
-self
._width
/ 2
396 y1
= self
._ypos
-self
._height
/ 2
398 if self
._shadowMode
!= SHADOW_NONE
:
399 if self
._shadowBrush
:
400 dc
.SetBrush(self
._shadowBrush
)
401 dc
.SetPen(wx
.Pen(wx
.WHITE
, 1, wx
.TRANSPARENT
))
403 if self
._cornerRadius
:
404 dc
.DrawRoundedRectangle(x1
+ self
._shadowOffsetX
, y1
+ self
._shadowOffsetY
, self
._width
, self
._height
, self
._cornerRadius
)
406 dc
.DrawRectangle(x1
+ self
._shadowOffsetX
, y1
+ self
._shadowOffsetY
, self
._width
, self
._height
)
408 # For debug purposes /pi
409 #dc.DrawRectangle(x1, y1, self._width, self._height)
411 def OnDrawContents(self
, dc
):
412 for object in self
._children
:
416 Shape
.OnDrawContents(self
, dc
)
418 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
= True):
422 for object in self
._children
:
424 object.Move(dc
, object.GetX() + diffX
, object.GetY() + diffY
, display
)
428 def OnErase(self
, dc
):
429 RectangleShape
.OnErase(self
, dc
)
430 for object in self
._children
:
433 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
434 xx
, yy
= self
._canvas
.Snap(x
, y
)
435 offsetX
= xx
- _objectStartX
436 offsetY
= yy
- _objectStartY
438 dc
= wx
.ClientDC(self
.GetCanvas())
439 self
.GetCanvas().PrepareDC(dc
)
441 dc
.SetLogicalFunction(OGLRBLF
)
442 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
444 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
446 self
.GetEventHandler().OnDrawOutline(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
, self
.GetWidth(), self
.GetHeight())
448 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
449 global _objectStartX
, _objectStartY
454 dc
= wx
.ClientDC(self
.GetCanvas())
455 self
.GetCanvas().PrepareDC(dc
)
459 dc
.SetLogicalFunction(OGLRBLF
)
460 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
462 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
463 self
._canvas
.CaptureMouse()
465 xx
, yy
= self
._canvas
.Snap(x
, y
)
466 offsetX
= xx
- _objectStartX
467 offsetY
= yy
- _objectStartY
469 self
.GetEventHandler().OnDrawOutline(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
, self
.GetWidth(), self
.GetHeight())
471 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
472 dc
= wx
.ClientDC(self
.GetCanvas())
473 self
.GetCanvas().PrepareDC(dc
)
475 if self
._canvas
.HasCapture():
476 self
._canvas
.ReleaseMouse()
478 if not self
._draggable
:
480 self
._parent
.GetEventHandler().OnEndDragLeft(x
, y
, keys
, 0)
485 dc
.SetLogicalFunction(wx
.COPY
)
487 xx
, yy
= self
._canvas
.Snap(x
, y
)
488 offsetX
= xx
- _objectStartX
489 offsetY
= yy
- _objectStartY
491 self
.Move(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
)
493 if self
._canvas
and not self
._canvas
.GetQuickEditMode():
494 self
._canvas
.Redraw(dc
)
496 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
497 # If we get a ctrl-right click, this means send the message to
498 # the division, so we can invoke a user interface for dealing
501 for division
in self
._divisions
:
502 hit
= division
.HitTest(x
, y
)
504 division
.GetEventHandler().OnRightClick(x
, y
, keys
, hit
[0])
507 def SetSize(self
, w
, h
, recursive
= True):
508 self
.SetAttachmentSize(w
, h
)
510 xScale
= w
/ max(1, self
.GetWidth())
511 yScale
= h
/ max(1, self
.GetHeight())
519 dc
= wx
.ClientDC(self
.GetCanvas())
520 self
.GetCanvas().PrepareDC(dc
)
522 for object in self
._children
:
523 # Scale the position first
524 newX
= (object.GetX()-self
.GetX()) * xScale
+ self
.GetX()
525 newY
= (object.GetY()-self
.GetY()) * yScale
+ self
.GetY()
527 object.Move(dc
, newX
, newY
)
530 # Now set the scaled size
531 xbound
, ybound
= object.GetBoundingBoxMax()
532 if not object.GetFixedWidth():
534 if not object.GetFixedHeight():
536 object.SetSize(xbound
, ybound
)
538 self
.SetDefaultRegionSize()
540 def AddChild(self
, child
, addAfter
= None):
541 """Adds a child shape to the composite.
543 If addAfter is not None, the shape will be added after this shape.
545 self
._children
.append(child
)
546 child
.SetParent(self
)
548 # Ensure we add at the right position
550 child
.RemoveFromCanvas(self
._canvas
)
551 child
.AddToCanvas(self
._canvas
, addAfter
)
553 def RemoveChild(self
, child
):
554 """Removes the child from the composite and any constraint
555 relationships, but does not delete the child.
557 self
._children
.remove(child
)
558 self
._divisions
.remove(child
)
559 self
.RemoveChildFromConstraints(child
)
560 child
.SetParent(None)
562 def DeleteConstraintsInvolvingChild(self
, child
):
563 """This function deletes constraints which mention the given child.
565 Used when deleting a child from the composite.
567 for constraint
in self
._constraints
:
568 if constraint
._constrainingObject
== child
or child
in constraint
._constrainedObjects
:
569 self
._constraints
.remove(constraint
)
571 def RemoveChildFromConstraints(self
, child
):
572 for constraint
in self
._constraints
:
573 if child
in constraint
._constrainedObjects
:
574 constraint
._constrainedObjects
.remove(child
)
575 if constraint
._constrainingObject
== child
:
576 constraint
._constrainingObject
= None
578 # Delete the constraint if no participants left
579 if not constraint
._constrainingObject
:
580 self
._constraints
.remove(constraint
)
582 def AddConstraint(self
, constraint
):
583 """Adds a constraint to the composite."""
584 self
._constraints
.append(constraint
)
585 if constraint
._constraintId
== 0:
586 constraint
._constraintId
= wx
.NewId()
589 def AddSimpleConstraint(self
, type, constraining
, constrained
):
590 """Add a constraint of the given type to the composite.
592 constraining is the shape doing the constraining
593 constrained is a list of shapes being constrained
595 constraint
= Constraint(type, constraining
, constrained
)
596 if constraint
._constraintId
== 0:
597 constraint
._constraintId
= wx
.NewId()
598 self
._constraints
.append(constraint
)
601 def FindConstraint(self
, cId
):
602 """Finds the constraint with the given id.
604 Returns a tuple of the constraint and the actual composite the
605 constraint was in, in case that composite was a descendant of
608 Returns None if not found.
610 for constraint
in self
._constraints
:
611 if constraint
._constraintId
== cId
:
612 return constraint
, self
614 # If not found, try children
615 for child
in self
._children
:
616 if isinstance(child
, CompositeShape
):
617 constraint
= child
.FindConstraint(cId
)
619 return constraint
[0], child
623 def DeleteConstraint(self
, constraint
):
624 """Deletes constraint from composite."""
625 self
._constraints
.remove(constraint
)
627 def CalculateSize(self
):
628 """Calculates the size and position of the composite based on
629 child sizes and positions.
636 for child
in self
._children
:
637 # Recalculate size of composite objects because may not conform
638 # to size it was set to - depends on the children.
639 if isinstance(child
, CompositeShape
):
640 child
.CalculateSize()
642 w
, h
= child
.GetBoundingBoxMax()
643 if child
.GetX() + w
/ 2>maxX
:
644 maxX
= child
.GetX() + w
/ 2
645 if child
.GetX()-w
/ 2<minX
:
646 minX
= child
.GetX()-w
/ 2
647 if child
.GetY() + h
/ 2>maxY
:
648 maxY
= child
.GetY() + h
/ 2
649 if child
.GetY()-h
/ 2<minY
:
650 minY
= child
.GetY()-h
/ 2
652 self
._width
= maxX
-minX
653 self
._height
= maxY
-minY
654 self
._xpos
= self
._width
/ 2 + minX
655 self
._ypos
= self
._height
/ 2 + minY
658 """Recomputes any constraints associated with the object. If FALSE is
659 returned, the constraints could not be satisfied (there was an
664 while changed
and noIterations
<500:
665 changed
= self
.Constrain()
674 for child
in self
._children
:
675 if isinstance(child
, CompositeShape
) and child
.Constrain():
678 for constraint
in self
._constraints
:
679 if constraint
.Evaluate():
684 def MakeContainer(self
):
685 """Makes this composite into a container by creating one child
688 division
= self
.OnCreateDivision()
689 self
._divisions
.append(division
)
690 self
.AddChild(division
)
692 division
.SetSize(self
._width
, self
._height
)
694 dc
= wx
.ClientDC(self
.GetCanvas())
695 self
.GetCanvas().PrepareDC(dc
)
697 division
.Move(dc
, self
.GetX(), self
.GetY())
701 def OnCreateDivision(self
):
702 return DivisionShape()
704 def FindContainerImage(self
):
705 """Finds the image used to visualize a container. This is any child of
706 the composite that is not in the divisions list.
708 for child
in self
._children
:
709 if child
in self
._divisions
:
714 def ContainsDivision(self
, division
):
715 """Returns TRUE if division is a descendant of this container."""
716 if division
in self
._divisions
:
719 for child
in self
._children
:
720 if isinstance(child
, CompositeShape
):
721 return child
.ContainsDivision(division
)
725 def GetDivisions(self
):
726 """Return the list of divisions."""
727 return self
._divisions
729 def GetConstraints(self
):
730 """Return the list of constraints."""
731 return self
._constraints
734 # A division object is a composite with special properties,
735 # to be used for containment. It's a subdivision of a container.
736 # A containing node image consists of a composite with a main child shape
737 # such as rounded rectangle, plus a list of division objects.
738 # It needs to be a composite because a division contains pieces
740 # NOTE a container has at least one wxDivisionShape for consistency.
741 # This can be subdivided, so it turns into two objects, then each of
742 # these can be subdivided, etc.
744 DIVISION_SIDE_NONE
=0
745 DIVISION_SIDE_LEFT
=1
747 DIVISION_SIDE_RIGHT
=3
748 DIVISION_SIDE_BOTTOM
=4
757 class DivisionControlPoint(ControlPoint
):
758 def __init__(self
, the_canvas
, object, size
, the_xoffset
, the_yoffset
, the_type
):
759 ControlPoint
.__init
__(self
, the_canvas
, object, size
, the_xoffset
, the_yoffset
, the_type
)
760 self
.SetEraseObject(False)
762 # Implement resizing of canvas object
763 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
764 ControlPoint
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
766 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
767 global originalX
, originalY
, originalW
, originalH
769 originalX
= self
._shape
.GetX()
770 originalY
= self
._shape
.GetY()
771 originalW
= self
._shape
.GetWidth()
772 originalH
= self
._shape
.GetHeight()
774 ControlPoint
.OnBeginDragLeft(self
, x
, y
, keys
, attachment
)
776 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
777 ControlPoint
.OnEndDragLeft(self
, x
, y
, keys
, attachment
)
779 dc
= wx
.ClientDC(self
.GetCanvas())
780 self
.GetCanvas().PrepareDC(dc
)
782 division
= self
._shape
783 divisionParent
= division
.GetParent()
785 # Need to check it's within the bounds of the parent composite
786 x1
= divisionParent
.GetX()-divisionParent
.GetWidth() / 2
787 y1
= divisionParent
.GetY()-divisionParent
.GetHeight() / 2
788 x2
= divisionParent
.GetX() + divisionParent
.GetWidth() / 2
789 y2
= divisionParent
.GetY() + divisionParent
.GetHeight() / 2
791 # Need to check it has not made the division zero or negative
793 dx1
= division
.GetX()-division
.GetWidth() / 2
794 dy1
= division
.GetY()-division
.GetHeight() / 2
795 dx2
= division
.GetX() + division
.GetWidth() / 2
796 dy2
= division
.GetY() + division
.GetHeight() / 2
799 if division
.GetHandleSide() == DIVISION_SIDE_LEFT
:
800 if x
<= x1
or x
>= x2
or x
>= dx2
:
802 # Try it out first...
803 elif not division
.ResizeAdjoining(DIVISION_SIDE_LEFT
, x
, True):
806 division
.ResizeAdjoining(DIVISION_SIDE_LEFT
, x
, False)
807 elif division
.GetHandleSide() == DIVISION_SIDE_TOP
:
808 if y
<= y1
or y
>= y2
or y
>= dy2
:
810 elif not division
.ResizeAdjoining(DIVISION_SIDE_TOP
, y
, True):
813 division
.ResizingAdjoining(DIVISION_SIDE_TOP
, y
, False)
814 elif division
.GetHandleSide() == DIVISION_SIDE_RIGHT
:
815 if x
<= x1
or x
>= x2
or x
<= dx1
:
817 elif not division
.ResizeAdjoining(DIVISION_SIDE_RIGHT
, x
, True):
820 division
.ResizeAdjoining(DIVISION_SIDE_RIGHT
, x
, False)
821 elif division
.GetHandleSide() == DIVISION_SIDE_BOTTOM
:
822 if y
<= y1
or y
>= y2
or y
<= dy1
:
824 elif not division
.ResizeAdjoining(DIVISION_SIDE_BOTTOM
, y
, True):
827 division
.ResizeAdjoining(DIVISION_SIDE_BOTTOM
, y
, False)
830 division
.SetSize(originalW
, originalH
)
831 division
.Move(dc
, originalX
, originalY
)
833 divisionParent
.Draw(dc
)
834 division
.GetEventHandler().OnDrawControlPoints(dc
)
838 DIVISION_MENU_SPLIT_HORIZONTALLY
=1
839 DIVISION_MENU_SPLIT_VERTICALLY
=2
840 DIVISION_MENU_EDIT_LEFT_EDGE
=3
841 DIVISION_MENU_EDIT_TOP_EDGE
=4
842 DIVISION_MENU_EDIT_RIGHT_EDGE
=5
843 DIVISION_MENU_EDIT_BOTTOM_EDGE
=6
844 DIVISION_MENU_DELETE_ALL
=7
848 class PopupDivisionMenu(wx
.Menu
):
850 wx
.Menu
.__init
__(self
)
851 self
.Append(DIVISION_MENU_SPLIT_HORIZONTALLY
,"Split horizontally")
852 self
.Append(DIVISION_MENU_SPLIT_VERTICALLY
,"Split vertically")
853 self
.AppendSeparator()
854 self
.Append(DIVISION_MENU_EDIT_LEFT_EDGE
,"Edit left edge")
855 self
.Append(DIVISION_MENU_EDIT_TOP_EDGE
,"Edit top edge")
857 wx
.EVT_MENU_RANGE(self
, DIVISION_MENU_SPLIT_HORIZONTALLY
, DIVISION_MENU_EDIT_BOTTOM_EDGE
, self
.OnMenu
)
859 def SetClientData(self
, data
):
860 self
._clientData
= data
862 def GetClientData(self
):
863 return self
._clientData
865 def OnMenu(self
, event
):
866 division
= self
.GetClientData()
867 if event
.GetId() == DIVISION_MENU_SPLIT_HORIZONTALLY
:
868 division
.Divide(wx
.HORIZONTAL
)
869 elif event
.GetId() == DIVISION_MENU_SPLIT_VERTICALLY
:
870 division
.Divide(wx
.VERTICAL
)
871 elif event
.GetId() == DIVISION_MENU_EDIT_LEFT_EDGE
:
872 division
.EditEdge(DIVISION_SIDE_LEFT
)
873 elif event
.GetId() == DIVISION_MENU_EDIT_TOP_EDGE
:
874 division
.EditEdge(DIVISION_SIDE_TOP
)
878 class DivisionShape(CompositeShape
):
879 """A division shape is like a composite in that it can contain further
880 objects, but is used exclusively to divide another shape into regions,
881 or divisions. A wxDivisionShape is never free-standing.
887 CompositeShape
.__init
__(self
)
888 self
.SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT
)
889 self
.SetCentreResize(False)
890 self
.SetAttachmentMode(True)
891 self
._leftSide
= None
892 self
._rightSide
= None
894 self
._bottomSide
= None
895 self
._handleSide
= DIVISION_SIDE_NONE
896 self
._leftSidePen
= wx
.BLACK_PEN
897 self
._topSidePen
= wx
.BLACK_PEN
898 self
._leftSideColour
="BLACK"
899 self
._topSideColour
="BLACK"
900 self
._leftSideStyle
="Solid"
901 self
._topSideStyle
="Solid"
904 def SetLeftSide(self
, shape
):
905 """Set the the division on the left side of this division."""
906 self
._leftSide
= shape
908 def SetTopSide(self
, shape
):
909 """Set the the division on the top side of this division."""
910 self
._topSide
= shape
912 def SetRightSide(self
, shape
):
913 """Set the the division on the right side of this division."""
914 self
._rightSide
= shape
916 def SetBottomSide(self
, shape
):
917 """Set the the division on the bottom side of this division."""
918 self
._bottomSide
= shape
920 def GetLeftSide(self
):
921 """Return the division on the left side of this division."""
922 return self
._leftSide
924 def GetTopSide(self
):
925 """Return the division on the top side of this division."""
928 def GetRightSide(self
):
929 """Return the division on the right side of this division."""
930 return self
._rightSide
932 def GetBottomSide(self
):
933 """Return the division on the bottom side of this division."""
934 return self
._bottomSide
936 def SetHandleSide(self
, side
):
937 """Sets the side which the handle appears on.
939 Either DIVISION_SIDE_LEFT or DIVISION_SIDE_TOP.
941 self
._handleSide
= side
943 def GetHandleSide(self
):
944 """Return the side which the handle appears on."""
945 return self
._handleSide
947 def SetLeftSidePen(self
, pen
):
948 """Set the colour for drawing the left side of the division."""
949 self
._leftSidePen
= pen
951 def SetTopSidePen(self
, pen
):
952 """Set the colour for drawing the top side of the division."""
953 self
._topSidePen
= pen
955 def GetLeftSidePen(self
):
956 """Return the pen used for drawing the left side of the division."""
957 return self
._leftSidePen
959 def GetTopSidePen(self
):
960 """Return the pen used for drawing the top side of the division."""
961 return self
._topSidePen
963 def GetLeftSideColour(self
):
964 """Return the colour used for drawing the left side of the division."""
965 return self
._leftSideColour
967 def GetTopSideColour(self
):
968 """Return the colour used for drawing the top side of the division."""
969 return self
._topSideColour
971 def SetLeftSideColour(self
, colour
):
972 """Set the colour for drawing the left side of the division."""
973 self
._leftSideColour
= colour
975 def SetTopSideColour(self
, colour
):
976 """Set the colour for drawing the top side of the division."""
977 self
._topSideColour
= colour
979 def GetLeftSideStyle(self
):
980 """Return the style used for the left side of the division."""
981 return self
._leftSideStyle
983 def GetTopSideStyle(self
):
984 """Return the style used for the top side of the division."""
985 return self
._topSideStyle
987 def SetLeftSideStyle(self
, style
):
988 self
._leftSideStyle
= style
990 def SetTopSideStyle(self
, style
):
991 self
._lefttopStyle
= style
993 def OnDraw(self
, dc
):
994 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
995 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
997 x1
= self
.GetX()-self
.GetWidth() / 2
998 y1
= self
.GetY()-self
.GetHeight() / 2
999 x2
= self
.GetX() + self
.GetWidth() / 2
1000 y2
= self
.GetY() + self
.GetHeight() / 2
1002 # Should subtract 1 pixel if drawing under Windows
1003 if sys
.platform
[:3]=="win":
1007 dc
.SetPen(self
._leftSidePen
)
1008 dc
.DrawLine(x1
, y2
, x1
, y1
)
1011 dc
.SetPen(self
._topSidePen
)
1012 dc
.DrawLine(x1
, y1
, x2
, y1
)
1014 # For testing purposes, draw a rectangle so we know
1015 # how big the division is.
1016 #dc.SetBrush(wx.RED_BRUSH)
1017 #dc.DrawRectangle(x1, y1, self.GetWidth(), self.GetHeight())
1019 def OnDrawContents(self
, dc
):
1020 CompositeShape
.OnDrawContents(self
, dc
)
1022 def OnMovePre(self
, dc
, x
, y
, oldx
, oldy
, display
= True):
1025 for object in self
._children
:
1027 object.Move(dc
, object.GetX() + diffX
, object.GetY() + diffY
, display
)
1030 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1031 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1033 hit
= self
._parent
.HitTest(x
, y
)
1035 attachment
, dist
= hit
1036 self
._parent
.GetEventHandler().OnDragLeft(draw
, x
, y
, keys
, attachment
)
1038 Shape
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
1040 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1041 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1043 hit
= self
._parent
.HitTest(x
, y
)
1045 attachment
, dist
= hit
1046 self
._parent
.GetEventHandler().OnBeginDragLeft(x
, y
, keys
, attachment
)
1048 Shape
.OnBeginDragLeft(x
, y
, keys
, attachment
)
1050 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1051 if self
._canvas
.HasCapture():
1052 self
._canvas
.ReleaseMouse()
1053 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1055 hit
= self
._parent
.HitTest(x
, y
)
1057 attachment
, dist
= hit
1058 self
._parent
.GetEventHandler().OnEndDragLeft(x
, y
, keys
, attachment
)
1061 dc
= wx
.ClientDC(self
.GetCanvas())
1062 self
.GetCanvas().PrepareDC(dc
)
1064 dc
.SetLogicalFunction(wx
.COPY
)
1066 self
._xpos
, self
._ypos
= self
._canvas
.Snap(self
._xpos
, self
._ypos
)
1067 self
.GetEventHandler().OnMovePre(dc
, x
, y
, self
._oldX
, self
._oldY
)
1069 self
.ResetControlPoints()
1072 self
.GetEventHandler().OnDrawControlPoints(dc
)
1074 if self
._canvas
and not self
._canvas
.GetQuickEditMode():
1075 self
._canvas
.Redraw(dc
)
1077 def SetSize(self
, w
, h
, recursive
= True):
1080 RectangleShape
.SetSize(self
, w
, h
, recursive
)
1082 def CalculateSize(self
):
1086 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
1088 self
.PopupMenu(x
, y
)
1091 hit
= self
._parent
.HitTest(x
, y
)
1093 attachment
, dist
= hit
1094 self
._parent
.GetEventHandler().OnRightClick(x
, y
, keys
, attachment
)
1096 # Divide wx.HORIZONTALly or wx.VERTICALly
1097 def Divide(self
, direction
):
1098 """Divide this division into two further divisions,
1099 horizontally (direction is wxHORIZONTAL) or
1100 vertically (direction is wxVERTICAL).
1102 # Calculate existing top-left, bottom-right
1103 x1
= self
.GetX()-self
.GetWidth() / 2
1104 y1
= self
.GetY()-self
.GetHeight() / 2
1106 compositeParent
= self
.GetParent()
1107 oldWidth
= self
.GetWidth()
1108 oldHeight
= self
.GetHeight()
1112 dc
= wx
.ClientDC(self
.GetCanvas())
1113 self
.GetCanvas().PrepareDC(dc
)
1115 if direction
== wx
.VERTICAL
:
1116 # Dividing vertically means notionally putting a horizontal
1118 # Break existing piece into two.
1119 newXPos1
= self
.GetX()
1120 newYPos1
= y1
+ self
.GetHeight() / 4
1121 newXPos2
= self
.GetX()
1122 newYPos2
= y1
+ 3 * self
.GetHeight() / 4
1123 newDivision
= compositeParent
.OnCreateDivision()
1124 newDivision
.Show(True)
1128 # Anything adjoining the bottom of this division now adjoins the
1129 # bottom of the new division.
1130 for obj
in compositeParent
.GetDivisions():
1131 if obj
.GetTopSide() == self
:
1132 obj
.SetTopSide(newDivision
)
1134 newDivision
.SetTopSide(self
)
1135 newDivision
.SetBottomSide(self
._bottomSide
)
1136 newDivision
.SetLeftSide(self
._leftSide
)
1137 newDivision
.SetRightSide(self
._rightSide
)
1138 self
._bottomSide
= newDivision
1140 compositeParent
.GetDivisions().append(newDivision
)
1142 # CHANGE: Need to insert this division at start of divisions in the
1143 # object list, because e.g.:
1145 # 2) Add contained object
1147 # Division is now receiving mouse events _before_ the contained
1148 # object, because it was added last (on top of all others)
1150 # Add after the image that visualizes the container
1151 compositeParent
.AddChild(newDivision
, compositeParent
.FindContainerImage())
1153 self
._handleSide
= DIVISION_SIDE_BOTTOM
1154 newDivision
.SetHandleSide(DIVISION_SIDE_TOP
)
1156 self
.SetSize(oldWidth
, oldHeight
/ 2)
1157 self
.Move(dc
, newXPos1
, newYPos1
)
1159 newDivision
.SetSize(oldWidth
, oldHeight
/ 2)
1160 newDivision
.Move(dc
, newXPos2
, newYPos2
)
1162 # Dividing horizontally means notionally putting a vertical line
1164 # Break existing piece into two.
1165 newXPos1
= x1
+ self
.GetWidth() / 4
1166 newYPos1
= self
.GetY()
1167 newXPos2
= x1
+ 3 * self
.GetWidth() / 4
1168 newYPos2
= self
.GetY()
1169 newDivision
= compositeParent
.OnCreateDivision()
1170 newDivision
.Show(True)
1174 # Anything adjoining the left of this division now adjoins the
1175 # left of the new division.
1176 for obj
in compositeParent
.GetDivisions():
1177 if obj
.GetLeftSide() == self
:
1178 obj
.SetLeftSide(newDivision
)
1180 newDivision
.SetTopSide(self
._topSide
)
1181 newDivision
.SetBottomSide(self
._bottomSide
)
1182 newDivision
.SetLeftSide(self
)
1183 newDivision
.SetRightSide(self
._rightSide
)
1184 self
._rightSide
= newDivision
1186 compositeParent
.GetDivisions().append(newDivision
)
1187 compositeParent
.AddChild(newDivision
, compositeParent
.FindContainerImage())
1189 self
._handleSide
= DIVISION_SIDE_RIGHT
1190 newDivision
.SetHandleSide(DIVISION_SIDE_LEFT
)
1192 self
.SetSize(oldWidth
/ 2, oldHeight
)
1193 self
.Move(dc
, newXPos1
, newYPos1
)
1195 newDivision
.SetSize(oldWidth
/ 2, oldHeight
)
1196 newDivision
.Move(dc
, newXPos2
, newYPos2
)
1198 if compositeParent
.Selected():
1199 compositeParent
.DeleteControlPoints(dc
)
1200 compositeParent
.MakeControlPoints()
1201 compositeParent
.MakeMandatoryControlPoints()
1203 compositeParent
.Draw(dc
)
1206 def MakeControlPoints(self
):
1207 self
.MakeMandatoryControlPoints()
1209 def MakeMandatoryControlPoints(self
):
1210 maxX
, maxY
= self
.GetBoundingBoxMax()
1214 if self
._handleSide
== DIVISION_SIDE_LEFT
:
1216 direction
= CONTROL_POINT_HORIZONTAL
1217 elif self
._handleSide
== DIVISION_SIDE_TOP
:
1219 direction
= CONTROL_POINT_VERTICAL
1220 elif self
._handleSide
== DIVISION_SIDE_RIGHT
:
1222 direction
= CONTROL_POINT_HORIZONTAL
1223 elif self
._handleSide
== DIVISION_SIDE_BOTTOM
:
1225 direction
= CONTROL_POINT_VERTICAL
1227 if self
._handleSide
!= DIVISION_SIDE_NONE
:
1228 control
= DivisionControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, x
, y
, direction
)
1229 self
._canvas
.AddShape(control
)
1230 self
._controlPoints
.append(control
)
1232 def ResetControlPoints(self
):
1233 self
.ResetMandatoryControlPoints()
1235 def ResetMandatoryControlPoints(self
):
1236 if not self
._controlPoints
:
1239 maxX
, maxY
= self
.GetBoundingBoxMax()
1241 node
= self
._controlPoints
[0]
1243 if self
._handleSide
== DIVISION_SIDE_LEFT
and node
:
1244 node
._xoffset
=-maxX
/ 2
1247 if self
._handleSide
== DIVISION_SIDE_TOP
and node
:
1249 node
._yoffset
=-maxY
/ 2
1251 if self
._handleSide
== DIVISION_SIDE_RIGHT
and node
:
1252 node
._xoffset
= maxX
/ 2
1255 if self
._handleSide
== DIVISION_SIDE_BOTTOM
and node
:
1257 node
._yoffset
= maxY
/ 2
1259 def AdjustLeft(self
, left
, test
):
1262 Returns FALSE if it's not physically possible to adjust it to
1265 x2
= self
.GetX() + self
.GetWidth() / 2
1274 newX
= left
+ newW
/ 2
1275 self
.SetSize(newW
, self
.GetHeight())
1277 dc
= wx
.ClientDC(self
.GetCanvas())
1278 self
.GetCanvas().PrepareDC(dc
)
1280 self
.Move(dc
, newX
, self
.GetY())
1283 def AdjustTop(self
, top
, test
):
1286 Returns FALSE if it's not physically possible to adjust it to
1289 y2
= self
.GetY() + self
.GetHeight() / 2
1298 newY
= top
+ newH
/ 2
1299 self
.SetSize(self
.GetWidth(), newH
)
1301 dc
= wx
.ClientDC(self
.GetCanvas())
1302 self
.GetCanvas().PrepareDC(dc
)
1304 self
.Move(dc
, self
.GetX(), newY
)
1307 def AdjustRight(self
, right
, test
):
1310 Returns FALSE if it's not physically possible to adjust it to
1313 x1
= self
.GetX()-self
.GetWidth() / 2
1322 newX
= x1
+ newW
/ 2
1323 self
.SetSize(newW
, self
.GetHeight())
1325 dc
= wx
.ClientDC(self
.GetCanvas())
1326 self
.GetCanvas().PrepareDC(dc
)
1328 self
.Move(dc
, newX
, self
.GetY())
1331 def AdjustTop(self
, top
, test
):
1334 Returns FALSE if it's not physically possible to adjust it to
1337 y1
= self
.GetY()-self
.GetHeight() / 2
1346 newY
= y1
+ newH
/ 2
1347 self
.SetSize(self
.GetWidth(), newH
)
1349 dc
= wx
.ClientDC(self
.GetCanvas())
1350 self
.GetCanvas().PrepareDC(dc
)
1352 self
.Move(dc
, self
.GetX(), newY
)
1355 # Resize adjoining divisions.
1357 # Behaviour should be as follows:
1358 # If right edge moves, find all objects whose left edge
1359 # adjoins this object, and move left edge accordingly.
1360 # If left..., move ... right.
1361 # If top..., move ... bottom.
1362 # If bottom..., move top.
1363 # If size goes to zero or end position is other side of start position,
1364 # resize to original size and return.
1366 def ResizeAdjoining(self
, side
, newPos
, test
):
1367 """Resize adjoining divisions at the given side.
1369 If test is TRUE, just see whether it's possible for each adjoining
1370 region, returning FALSE if it's not.
1374 * DIVISION_SIDE_NONE
1375 * DIVISION_SIDE_LEFT
1377 * DIVISION_SIDE_RIGHT
1378 * DIVISION_SIDE_BOTTOM
1380 divisionParent
= self
.GetParent()
1381 for division
in divisionParent
.GetDivisions():
1382 if side
== DIVISION_SIDE_LEFT
:
1383 if division
._rightSide
== self
:
1384 success
= division
.AdjustRight(newPos
, test
)
1385 if not success
and test
:
1387 elif side
== DIVISION_SIDE_TOP
:
1388 if division
._bottomSide
== self
:
1389 success
= division
.AdjustBottom(newPos
, test
)
1390 if not success
and test
:
1392 elif side
== DIVISION_SIDE_RIGHT
:
1393 if division
._leftSide
== self
:
1394 success
= division
.AdjustLeft(newPos
, test
)
1395 if not success
and test
:
1397 elif side
== DIVISION_SIDE_BOTTOM
:
1398 if division
._topSide
== self
:
1399 success
= division
.AdjustTop(newPos
, test
)
1400 if not success
and test
:
1404 def EditEdge(self
, side
):
1405 print "EditEdge() not implemented."
1407 def PopupMenu(self
, x
, y
):
1408 menu
= PopupDivisionMenu()
1409 menu
.SetClientData(self
)
1411 menu
.Enable(DIVISION_MENU_EDIT_LEFT_EDGE
, True)
1413 menu
.Enable(DIVISION_MENU_EDIT_LEFT_EDGE
, False)
1415 menu
.Enable(DIVISION_MENU_EDIT_TOP_EDGE
, True)
1417 menu
.Enable(DIVISION_MENU_EDIT_TOP_EDGE
, False)
1419 x1
, y1
= self
._canvas
.GetViewStart()
1420 unit_x
, unit_y
= self
._canvas
.GetScrollPixelsPerUnit()
1422 dc
= wx
.ClientDC(self
.GetCanvas())
1423 self
.GetCanvas().PrepareDC(dc
)
1425 mouse_x
= dc
.LogicalToDeviceX(x
-x1
* unit_x
)
1426 mouse_y
= dc
.LogicalToDeviceY(y
-y1
* unit_y
)
1428 self
._canvas
.PopupMenu(menu
, (mouse_x
, mouse_y
))