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 #----------------------------------------------------------------------------
17 from _basic
import RectangleShape
, Shape
, ControlPoint
18 from _oglmisc
import *
20 KEY_SHIFT
, KEY_CTRL
= 1, 2
25 CONSTRAINT_CENTRED_VERTICALLY
= 1
26 CONSTRAINT_CENTRED_HORIZONTALLY
= 2
27 CONSTRAINT_CENTRED_BOTH
= 3
28 CONSTRAINT_LEFT_OF
= 4
29 CONSTRAINT_RIGHT_OF
= 5
32 CONSTRAINT_ALIGNED_TOP
= 8
33 CONSTRAINT_ALIGNED_BOTTOM
= 9
34 CONSTRAINT_ALIGNED_LEFT
= 10
35 CONSTRAINT_ALIGNED_RIGHT
= 11
37 # Like aligned, but with the objects centred on the respective edge
38 # of the reference object.
39 CONSTRAINT_MIDALIGNED_TOP
= 12
40 CONSTRAINT_MIDALIGNED_BOTTOM
= 13
41 CONSTRAINT_MIDALIGNED_LEFT
= 14
42 CONSTRAINT_MIDALIGNED_RIGHT
= 15
45 # Backwards compatibility names. These should be removed eventually.
46 gyCONSTRAINT_CENTRED_VERTICALLY
= CONSTRAINT_CENTRED_VERTICALLY
47 gyCONSTRAINT_CENTRED_HORIZONTALLY
= CONSTRAINT_CENTRED_HORIZONTALLY
48 gyCONSTRAINT_CENTRED_BOTH
= CONSTRAINT_CENTRED_BOTH
49 gyCONSTRAINT_LEFT_OF
= CONSTRAINT_LEFT_OF
50 gyCONSTRAINT_RIGHT_OF
= CONSTRAINT_RIGHT_OF
51 gyCONSTRAINT_ABOVE
= CONSTRAINT_ABOVE
52 gyCONSTRAINT_BELOW
= CONSTRAINT_BELOW
53 gyCONSTRAINT_ALIGNED_TOP
= CONSTRAINT_ALIGNED_TOP
54 gyCONSTRAINT_ALIGNED_BOTTOM
= CONSTRAINT_ALIGNED_BOTTOM
55 gyCONSTRAINT_ALIGNED_LEFT
= CONSTRAINT_ALIGNED_LEFT
56 gyCONSTRAINT_ALIGNED_RIGHT
= CONSTRAINT_ALIGNED_RIGHT
57 gyCONSTRAINT_MIDALIGNED_TOP
= CONSTRAINT_MIDALIGNED_TOP
58 gyCONSTRAINT_MIDALIGNED_BOTTOM
= CONSTRAINT_MIDALIGNED_BOTTOM
59 gyCONSTRAINT_MIDALIGNED_LEFT
= CONSTRAINT_MIDALIGNED_LEFT
60 gyCONSTRAINT_MIDALIGNED_RIGHT
= CONSTRAINT_MIDALIGNED_RIGHT
64 class ConstraintType(object):
65 def __init__(self
, theType
, theName
, thePhrase
):
68 self
._phrase
= thePhrase
73 [CONSTRAINT_CENTRED_VERTICALLY
,
74 ConstraintType(CONSTRAINT_CENTRED_VERTICALLY
, "Centre vertically", "centred vertically w.r.t.")],
76 [CONSTRAINT_CENTRED_HORIZONTALLY
,
77 ConstraintType(CONSTRAINT_CENTRED_HORIZONTALLY
, "Centre horizontally", "centred horizontally w.r.t.")],
79 [CONSTRAINT_CENTRED_BOTH
,
80 ConstraintType(CONSTRAINT_CENTRED_BOTH
, "Centre", "centred w.r.t.")],
83 ConstraintType(CONSTRAINT_LEFT_OF
, "Left of", "left of")],
86 ConstraintType(CONSTRAINT_RIGHT_OF
, "Right of", "right of")],
89 ConstraintType(CONSTRAINT_ABOVE
, "Above", "above")],
92 ConstraintType(CONSTRAINT_BELOW
, "Below", "below")],
95 [CONSTRAINT_ALIGNED_TOP
,
96 ConstraintType(CONSTRAINT_ALIGNED_TOP
, "Top-aligned", "aligned to the top of")],
98 [CONSTRAINT_ALIGNED_BOTTOM
,
99 ConstraintType(CONSTRAINT_ALIGNED_BOTTOM
, "Bottom-aligned", "aligned to the bottom of")],
101 [CONSTRAINT_ALIGNED_LEFT
,
102 ConstraintType(CONSTRAINT_ALIGNED_LEFT
, "Left-aligned", "aligned to the left of")],
104 [CONSTRAINT_ALIGNED_RIGHT
,
105 ConstraintType(CONSTRAINT_ALIGNED_RIGHT
, "Right-aligned", "aligned to the right of")],
108 [CONSTRAINT_MIDALIGNED_TOP
,
109 ConstraintType(CONSTRAINT_MIDALIGNED_TOP
, "Top-midaligned", "centred on the top of")],
111 [CONSTRAINT_MIDALIGNED_BOTTOM
,
112 ConstraintType(CONSTRAINT_MIDALIGNED_BOTTOM
, "Bottom-midaligned", "centred on the bottom of")],
114 [CONSTRAINT_MIDALIGNED_LEFT
,
115 ConstraintType(CONSTRAINT_MIDALIGNED_LEFT
, "Left-midaligned", "centred on the left of")],
117 [CONSTRAINT_MIDALIGNED_RIGHT
,
118 ConstraintType(CONSTRAINT_MIDALIGNED_RIGHT
, "Right-midaligned", "centred on the right of")]
124 class Constraint(object):
125 """A Constraint object helps specify how child shapes are laid out with
126 respect to siblings and parents.
131 def __init__(self
, type, constraining
, constrained
):
135 self
._constraintType
= type
136 self
._constrainingObject
= constraining
138 self
._constraintId
= 0
139 self
._constraintName
= "noname"
141 self
._constrainedObjects
= constrained
[:]
144 return "<%s.%s>" % (self
.__class
__.__module
__, self
.__class
__.__name
__)
146 def SetSpacing(self
, x
, y
):
147 """Sets the horizontal and vertical spacing for the constraint."""
151 def Equals(self
, a
, b
):
152 """Return TRUE if x and y are approximately equal (for the purposes
153 of evaluating the constraint).
157 return b
<= a
+ marg
and b
>= a
- marg
160 """Evaluate this constraint and return TRUE if anything changed."""
161 maxWidth
, maxHeight
= self
._constrainingObject
.GetBoundingBoxMax()
162 minWidth
, minHeight
= self
._constrainingObject
.GetBoundingBoxMin()
163 x
= self
._constrainingObject
.GetX()
164 y
= self
._constrainingObject
.GetY()
166 dc
= wx
.ClientDC(self
._constrainingObject
.GetCanvas())
167 self
._constrainingObject
.GetCanvas().PrepareDC(dc
)
169 if self
._constraintType
== CONSTRAINT_CENTRED_VERTICALLY
:
170 n
= len(self
._constrainedObjects
)
171 totalObjectHeight
= 0.0
172 for constrainedObject
in self
._constrainedObjects
:
173 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
174 totalObjectHeight
+= height2
176 # Check if within the constraining object...
177 if totalObjectHeight
+ (n
+ 1) * self
._ySpacing
<= minHeight
:
178 spacingY
= (minHeight
- totalObjectHeight
) / (n
+ 1.0)
179 startY
= y
- minHeight
/ 2.0
180 else: # Otherwise, use default spacing
181 spacingY
= self
._ySpacing
182 startY
= y
- (totalObjectHeight
+ (n
+ 1) * spacingY
) / 2.0
184 # Now position the objects
186 for constrainedObject
in self
._constrainedObjects
:
187 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
188 startY
+= spacingY
+ height2
/ 2.0
189 if not self
.Equals(startY
, constrainedObject
.GetY()):
190 constrainedObject
.Move(dc
, constrainedObject
.GetX(), startY
, False)
192 startY
+= height2
/ 2.0
194 elif self
._constraintType
== CONSTRAINT_CENTRED_HORIZONTALLY
:
195 n
= len(self
._constrainedObjects
)
196 totalObjectWidth
= 0.0
197 for constrainedObject
in self
._constrainedObjects
:
198 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
199 totalObjectWidth
+= width2
201 # Check if within the constraining object...
202 if totalObjectWidth
+ (n
+ 1) * self
._xSpacing
<= minWidth
:
203 spacingX
= (minWidth
- totalObjectWidth
) / (n
+ 1.0)
204 startX
= x
- minWidth
/ 2.0
205 else: # Otherwise, use default spacing
206 spacingX
= self
._xSpacing
207 startX
= x
- (totalObjectWidth
+ (n
+ 1) * spacingX
) / 2.0
209 # Now position the objects
211 for constrainedObject
in self
._constrainedObjects
:
212 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
213 startX
+= spacingX
+ width2
/ 2.0
214 if not self
.Equals(startX
, constrainedObject
.GetX()):
215 constrainedObject
.Move(dc
, startX
, constrainedObject
.GetY(), False)
217 startX
+= width2
/ 2.0
219 elif self
._constraintType
== CONSTRAINT_CENTRED_BOTH
:
220 n
= len(self
._constrainedObjects
)
221 totalObjectWidth
= 0.0
222 totalObjectHeight
= 0.0
224 for constrainedObject
in self
._constrainedObjects
:
225 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
226 totalObjectWidth
+= width2
227 totalObjectHeight
+= height2
229 # Check if within the constraining object...
230 if totalObjectHeight
+ (n
+ 1) * self
._xSpacing
<= minWidth
:
231 spacingX
= (minWidth
- totalObjectWidth
) / (n
+ 1.0)
232 startX
= x
- minWidth
/ 2.0
233 else: # Otherwise, use default spacing
234 spacingX
= self
._xSpacing
235 startX
= x
- (totalObjectWidth
+ (n
+ 1) * spacingX
) / 2.0
237 # Check if within the constraining object...
238 if totalObjectHeight
+ (n
+ 1) * self
._ySpacing
<= minHeight
:
239 spacingY
= (minHeight
- totalObjectHeight
) / (n
+ 1.0)
240 startY
= y
- minHeight
/ 2.0
241 else: # Otherwise, use default spacing
242 spacingY
= self
._ySpacing
243 startY
= y
- (totalObjectHeight
+ (n
+ 1) * spacingY
) / 2.0
245 # Now position the objects
247 for constrainedObject
in self
._constrainedObjects
:
248 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
249 startX
+= spacingX
+ width2
/ 2.0
250 startY
+= spacingY
+ height2
/ 2.0
252 if not self
.Equals(startX
, constrainedObject
.GetX()) or not self
.Equals(startY
, constrainedObject
.GetY()):
253 constrainedObject
.Move(dc
, startX
, startY
, False)
256 startX
+= width2
/ 2.0
257 startY
+= height2
/ 2.0
259 elif self
._constraintType
== CONSTRAINT_LEFT_OF
:
261 for constrainedObject
in self
._constrainedObjects
:
262 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
264 x3
= x
- minWidth
/ 2.0 - width2
/ 2.0 - self
._xSpacing
265 if not self
.Equals(x3
, constrainedObject
.GetX()):
267 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
269 elif self
._constraintType
== CONSTRAINT_RIGHT_OF
:
272 for constrainedObject
in self
._constrainedObjects
:
273 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
274 x3
= x
+ minWidth
/ 2.0 + width2
/ 2.0 + self
._xSpacing
275 if not self
.Equals(x3
, constrainedObject
.GetX()):
276 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
279 elif self
._constraintType
== CONSTRAINT_ABOVE
:
282 for constrainedObject
in self
._constrainedObjects
:
283 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
285 y3
= y
- minHeight
/ 2.0 - height2
/ 2.0 - self
._ySpacing
286 if not self
.Equals(y3
, constrainedObject
.GetY()):
288 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
290 elif self
._constraintType
== CONSTRAINT_BELOW
:
293 for constrainedObject
in self
._constrainedObjects
:
294 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
296 y3
= y
+ minHeight
/ 2.0 + height2
/ 2.0 + self
._ySpacing
297 if not self
.Equals(y3
, constrainedObject
.GetY()):
299 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
301 elif self
._constraintType
== CONSTRAINT_ALIGNED_LEFT
:
303 for constrainedObject
in self
._constrainedObjects
:
304 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
305 x3
= x
- minWidth
/ 2.0 + width2
/ 2.0 + self
._xSpacing
306 if not self
.Equals(x3
, constrainedObject
.GetX()):
308 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
310 elif self
._constraintType
== CONSTRAINT_ALIGNED_RIGHT
:
312 for constrainedObject
in self
._constrainedObjects
:
313 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
314 x3
= x
+ minWidth
/ 2.0 - width2
/ 2.0 - self
._xSpacing
315 if not self
.Equals(x3
, constrainedObject
.GetX()):
317 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
319 elif self
._constraintType
== CONSTRAINT_ALIGNED_TOP
:
321 for constrainedObject
in self
._constrainedObjects
:
322 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
323 y3
= y
- minHeight
/ 2.0 + height2
/ 2.0 + self
._ySpacing
324 if not self
.Equals(y3
, constrainedObject
.GetY()):
326 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
328 elif self
._constraintType
== CONSTRAINT_ALIGNED_BOTTOM
:
330 for constrainedObject
in self
._constrainedObjects
:
331 width2
, height2
= constrainedObject
.GetBoundingBoxMax()
332 y3
= y
+ minHeight
/ 2.0 - height2
/ 2.0 - self
._ySpacing
333 if not self
.Equals(y3
, constrainedObject
.GetY()):
335 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
337 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_LEFT
:
339 for constrainedObject
in self
._constrainedObjects
:
340 x3
= x
- minWidth
/ 2.0
341 if not self
.Equals(x3
, constrainedObject
.GetX()):
343 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
345 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_RIGHT
:
347 for constrainedObject
in self
._constrainedObjects
:
348 x3
= x
+ minWidth
/ 2.0
349 if not self
.Equals(x3
, constrainedObject
.GetX()):
351 constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False)
353 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_TOP
:
355 for constrainedObject
in self
._constrainedObjects
:
356 y3
= y
- minHeight
/ 2.0
357 if not self
.Equals(y3
, constrainedObject
.GetY()):
359 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
361 elif self
._constraintType
== CONSTRAINT_MIDALIGNED_BOTTOM
:
363 for constrainedObject
in self
._constrainedObjects
:
364 y3
= y
+ minHeight
/ 2.0
365 if not self
.Equals(y3
, constrainedObject
.GetY()):
367 constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False)
372 OGLConstraint
= wx
._core
._deprecated
(Constraint
,
373 "The OGLConstraint name is deprecated, use `ogl.Constraint` instead.")
376 class CompositeShape(RectangleShape
):
377 """This is an object with a list of child objects, and a list of size
378 and positioning constraints between the children.
384 RectangleShape
.__init
__(self
, 100.0, 100.0)
386 self
._oldX
= self
._xpos
387 self
._oldY
= self
._ypos
389 self
._constraints
= []
390 self
._divisions
= [] # In case it's a container
392 def OnDraw(self
, dc
):
393 x1
= self
._xpos
- self
._width
/ 2.0
394 y1
= self
._ypos
- self
._height
/ 2.0
396 if self
._shadowMode
!= SHADOW_NONE
:
397 if self
._shadowBrush
:
398 dc
.SetBrush(self
._shadowBrush
)
399 dc
.SetPen(wx
.Pen(wx
.WHITE
, 1, wx
.TRANSPARENT
))
401 if self
._cornerRadius
:
402 dc
.DrawRoundedRectangle(x1
+ self
._shadowOffsetX
, y1
+ self
._shadowOffsetY
, self
._width
, self
._height
, self
._cornerRadius
)
404 dc
.DrawRectangle(x1
+ self
._shadowOffsetX
, y1
+ self
._shadowOffsetY
, self
._width
, self
._height
)
406 # For debug purposes /pi
407 #dc.DrawRectangle(x1, y1, self._width, self._height)
409 def OnDrawContents(self
, dc
):
410 for object in self
._children
:
414 Shape
.OnDrawContents(self
, dc
)
416 def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display
= True):
420 for object in self
._children
:
422 object.Move(dc
, object.GetX() + diffX
, object.GetY() + diffY
, display
)
426 def OnErase(self
, dc
):
427 RectangleShape
.OnErase(self
, dc
)
428 for object in self
._children
:
431 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
432 xx
, yy
= self
._canvas
.Snap(x
, y
)
433 offsetX
= xx
- _objectStartX
434 offsetY
= yy
- _objectStartY
436 dc
= wx
.ClientDC(self
.GetCanvas())
437 self
.GetCanvas().PrepareDC(dc
)
439 dc
.SetLogicalFunction(OGLRBLF
)
440 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
442 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
444 self
.GetEventHandler().OnDrawOutline(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
, self
.GetWidth(), self
.GetHeight())
446 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
447 global _objectStartX
, _objectStartY
452 dc
= wx
.ClientDC(self
.GetCanvas())
453 self
.GetCanvas().PrepareDC(dc
)
457 dc
.SetLogicalFunction(OGLRBLF
)
458 dottedPen
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
)
460 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
461 self
._canvas
.CaptureMouse()
463 xx
, yy
= self
._canvas
.Snap(x
, y
)
464 offsetX
= xx
- _objectStartX
465 offsetY
= yy
- _objectStartY
467 self
.GetEventHandler().OnDrawOutline(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
, self
.GetWidth(), self
.GetHeight())
469 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
470 dc
= wx
.ClientDC(self
.GetCanvas())
471 self
.GetCanvas().PrepareDC(dc
)
473 if self
._canvas
.HasCapture():
474 self
._canvas
.ReleaseMouse()
476 if not self
._draggable
:
478 self
._parent
.GetEventHandler().OnEndDragLeft(x
, y
, keys
, 0)
483 dc
.SetLogicalFunction(wx
.COPY
)
485 xx
, yy
= self
._canvas
.Snap(x
, y
)
486 offsetX
= xx
- _objectStartX
487 offsetY
= yy
- _objectStartY
489 self
.Move(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
)
491 if self
._canvas
and not self
._canvas
.GetQuickEditMode():
492 self
._canvas
.Redraw(dc
)
494 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
495 # If we get a ctrl-right click, this means send the message to
496 # the division, so we can invoke a user interface for dealing
499 for division
in self
._divisions
:
500 hit
= division
.HitTest(x
, y
)
502 division
.GetEventHandler().OnRightClick(x
, y
, keys
, hit
[0])
505 def SetSize(self
, w
, h
, recursive
= True):
506 self
.SetAttachmentSize(w
, h
)
508 xScale
= float(w
) / max(1, self
.GetWidth())
509 yScale
= float(h
) / max(1, self
.GetHeight())
517 dc
= wx
.ClientDC(self
.GetCanvas())
518 self
.GetCanvas().PrepareDC(dc
)
520 for object in self
._children
:
521 # Scale the position first
522 newX
= (object.GetX() - self
.GetX()) * xScale
+ self
.GetX()
523 newY
= (object.GetY() - self
.GetY()) * yScale
+ self
.GetY()
525 object.Move(dc
, newX
, newY
)
528 # Now set the scaled size
529 xbound
, ybound
= object.GetBoundingBoxMax()
530 if not object.GetFixedWidth():
532 if not object.GetFixedHeight():
534 object.SetSize(xbound
, ybound
)
536 self
.SetDefaultRegionSize()
538 def AddChild(self
, child
, addAfter
= None):
539 """Adds a child shape to the composite.
541 If addAfter is not None, the shape will be added after this shape.
543 self
._children
.append(child
)
544 child
.SetParent(self
)
546 # Ensure we add at the right position
548 child
.RemoveFromCanvas(self
._canvas
)
549 child
.AddToCanvas(self
._canvas
, addAfter
)
551 def RemoveChild(self
, child
):
552 """Removes the child from the composite and any constraint
553 relationships, but does not delete the child.
555 if child
in self
._children
:
556 self
._children
.remove(child
)
557 if child
in self
._divisions
:
558 self
._divisions
.remove(child
)
559 self
.RemoveChildFromConstraints(child
)
560 child
.SetParent(None)
564 Fully disconnect this shape from parents, children, the
567 for child
in self
.GetChildren():
568 self
.RemoveChild(child
)
570 RectangleShape
.Delete(self
)
571 self
._constraints
= []
574 def DeleteConstraintsInvolvingChild(self
, child
):
575 """This function deletes constraints which mention the given child.
577 Used when deleting a child from the composite.
579 for constraint
in self
._constraints
:
580 if constraint
._constrainingObject
== child
or child
in constraint
._constrainedObjects
:
581 self
._constraints
.remove(constraint
)
583 def RemoveChildFromConstraints(self
, child
):
584 for constraint
in self
._constraints
:
585 if child
in constraint
._constrainedObjects
:
586 constraint
._constrainedObjects
.remove(child
)
587 if constraint
._constrainingObject
== child
:
588 constraint
._constrainingObject
= None
590 # Delete the constraint if no participants left
591 if not constraint
._constrainingObject
:
592 self
._constraints
.remove(constraint
)
594 def AddConstraint(self
, constraint
):
595 """Adds a constraint to the composite."""
596 self
._constraints
.append(constraint
)
597 if constraint
._constraintId
== 0:
598 constraint
._constraintId
= wx
.NewId()
601 def AddSimpleConstraint(self
, type, constraining
, constrained
):
602 """Add a constraint of the given type to the composite.
604 constraining is the shape doing the constraining
605 constrained is a list of shapes being constrained
607 constraint
= Constraint(type, constraining
, constrained
)
608 if constraint
._constraintId
== 0:
609 constraint
._constraintId
= wx
.NewId()
610 self
._constraints
.append(constraint
)
613 def FindConstraint(self
, cId
):
614 """Finds the constraint with the given id.
616 Returns a tuple of the constraint and the actual composite the
617 constraint was in, in case that composite was a descendant of
620 Returns None if not found.
622 for constraint
in self
._constraints
:
623 if constraint
._constraintId
== cId
:
624 return constraint
, self
626 # If not found, try children
627 for child
in self
._children
:
628 if isinstance(child
, CompositeShape
):
629 constraint
= child
.FindConstraint(cId
)
631 return constraint
[0], child
635 def DeleteConstraint(self
, constraint
):
636 """Deletes constraint from composite."""
637 self
._constraints
.remove(constraint
)
639 def CalculateSize(self
):
640 """Calculates the size and position of the composite based on
641 child sizes and positions.
648 for child
in self
._children
:
649 # Recalculate size of composite objects because may not conform
650 # to size it was set to - depends on the children.
651 if isinstance(child
, CompositeShape
):
652 child
.CalculateSize()
654 w
, h
= child
.GetBoundingBoxMax()
655 if child
.GetX() + w
/ 2.0 > maxX
:
656 maxX
= child
.GetX() + w
/ 2.0
657 if child
.GetX() - w
/ 2.0 < minX
:
658 minX
= child
.GetX() - w
/ 2.0
659 if child
.GetY() + h
/ 2.0 > maxY
:
660 maxY
= child
.GetY() + h
/ 2.0
661 if child
.GetY() - h
/ 2.0 < minY
:
662 minY
= child
.GetY() - h
/ 2.0
664 self
._width
= maxX
- minX
665 self
._height
= maxY
- minY
666 self
._xpos
= self
._width
/ 2.0 + minX
667 self
._ypos
= self
._height
/ 2.0 + minY
670 """Recomputes any constraints associated with the object. If FALSE is
671 returned, the constraints could not be satisfied (there was an
676 while changed
and noIterations
< 500:
677 changed
= self
.Constrain()
686 for child
in self
._children
:
687 if isinstance(child
, CompositeShape
) and child
.Constrain():
690 for constraint
in self
._constraints
:
691 if constraint
.Evaluate():
696 def MakeContainer(self
):
697 """Makes this composite into a container by creating one child
700 division
= self
.OnCreateDivision()
701 self
._divisions
.append(division
)
702 self
.AddChild(division
)
704 division
.SetSize(self
._width
, self
._height
)
706 dc
= wx
.ClientDC(self
.GetCanvas())
707 self
.GetCanvas().PrepareDC(dc
)
709 division
.Move(dc
, self
.GetX(), self
.GetY())
713 def OnCreateDivision(self
):
714 return DivisionShape()
716 def FindContainerImage(self
):
717 """Finds the image used to visualize a container. This is any child of
718 the composite that is not in the divisions list.
720 for child
in self
._children
:
721 if child
in self
._divisions
:
726 def ContainsDivision(self
, division
):
727 """Returns TRUE if division is a descendant of this container."""
728 if division
in self
._divisions
:
731 for child
in self
._children
:
732 if isinstance(child
, CompositeShape
):
733 return child
.ContainsDivision(division
)
737 def GetDivisions(self
):
738 """Return the list of divisions."""
739 return self
._divisions
741 def GetConstraints(self
):
742 """Return the list of constraints."""
743 return self
._constraints
746 # A division object is a composite with special properties,
747 # to be used for containment. It's a subdivision of a container.
748 # A containing node image consists of a composite with a main child shape
749 # such as rounded rectangle, plus a list of division objects.
750 # It needs to be a composite because a division contains pieces
752 # NOTE a container has at least one wxDivisionShape for consistency.
753 # This can be subdivided, so it turns into two objects, then each of
754 # these can be subdivided, etc.
756 DIVISION_SIDE_NONE
=0
757 DIVISION_SIDE_LEFT
=1
759 DIVISION_SIDE_RIGHT
=3
760 DIVISION_SIDE_BOTTOM
=4
769 class DivisionControlPoint(ControlPoint
):
770 def __init__(self
, the_canvas
, object, size
, the_xoffset
, the_yoffset
, the_type
):
771 ControlPoint
.__init
__(self
, the_canvas
, object, size
, the_xoffset
, the_yoffset
, the_type
)
772 self
.SetEraseObject(False)
774 # Implement resizing of canvas object
775 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
776 ControlPoint
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
778 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
779 global originalX
, originalY
, originalW
, originalH
781 originalX
= self
._shape
.GetX()
782 originalY
= self
._shape
.GetY()
783 originalW
= self
._shape
.GetWidth()
784 originalH
= self
._shape
.GetHeight()
786 ControlPoint
.OnBeginDragLeft(self
, x
, y
, keys
, attachment
)
788 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
789 ControlPoint
.OnEndDragLeft(self
, x
, y
, keys
, attachment
)
791 dc
= wx
.ClientDC(self
.GetCanvas())
792 self
.GetCanvas().PrepareDC(dc
)
794 division
= self
._shape
795 divisionParent
= division
.GetParent()
797 # Need to check it's within the bounds of the parent composite
798 x1
= divisionParent
.GetX() - divisionParent
.GetWidth() / 2.0
799 y1
= divisionParent
.GetY() - divisionParent
.GetHeight() / 2.0
800 x2
= divisionParent
.GetX() + divisionParent
.GetWidth() / 2.0
801 y2
= divisionParent
.GetY() + divisionParent
.GetHeight() / 2.0
803 # Need to check it has not made the division zero or negative
805 dx1
= division
.GetX() - division
.GetWidth() / 2.0
806 dy1
= division
.GetY() - division
.GetHeight() / 2.0
807 dx2
= division
.GetX() + division
.GetWidth() / 2.0
808 dy2
= division
.GetY() + division
.GetHeight() / 2.0
811 if division
.GetHandleSide() == DIVISION_SIDE_LEFT
:
812 if x
<= x1
or x
>= x2
or x
>= dx2
:
814 # Try it out first...
815 elif not division
.ResizeAdjoining(DIVISION_SIDE_LEFT
, x
, True):
818 division
.ResizeAdjoining(DIVISION_SIDE_LEFT
, x
, False)
819 elif division
.GetHandleSide() == DIVISION_SIDE_TOP
:
820 if y
<= y1
or y
>= y2
or y
>= dy2
:
822 elif not division
.ResizeAdjoining(DIVISION_SIDE_TOP
, y
, True):
825 division
.ResizingAdjoining(DIVISION_SIDE_TOP
, y
, False)
826 elif division
.GetHandleSide() == DIVISION_SIDE_RIGHT
:
827 if x
<= x1
or x
>= x2
or x
<= dx1
:
829 elif not division
.ResizeAdjoining(DIVISION_SIDE_RIGHT
, x
, True):
832 division
.ResizeAdjoining(DIVISION_SIDE_RIGHT
, x
, False)
833 elif division
.GetHandleSide() == DIVISION_SIDE_BOTTOM
:
834 if y
<= y1
or y
>= y2
or y
<= dy1
:
836 elif not division
.ResizeAdjoining(DIVISION_SIDE_BOTTOM
, y
, True):
839 division
.ResizeAdjoining(DIVISION_SIDE_BOTTOM
, y
, False)
842 division
.SetSize(originalW
, originalH
)
843 division
.Move(dc
, originalX
, originalY
)
845 divisionParent
.Draw(dc
)
846 division
.GetEventHandler().OnDrawControlPoints(dc
)
850 DIVISION_MENU_SPLIT_HORIZONTALLY
=1
851 DIVISION_MENU_SPLIT_VERTICALLY
=2
852 DIVISION_MENU_EDIT_LEFT_EDGE
=3
853 DIVISION_MENU_EDIT_TOP_EDGE
=4
854 DIVISION_MENU_EDIT_RIGHT_EDGE
=5
855 DIVISION_MENU_EDIT_BOTTOM_EDGE
=6
856 DIVISION_MENU_DELETE_ALL
=7
860 class PopupDivisionMenu(wx
.Menu
):
862 wx
.Menu
.__init
__(self
)
863 self
.Append(DIVISION_MENU_SPLIT_HORIZONTALLY
,"Split horizontally")
864 self
.Append(DIVISION_MENU_SPLIT_VERTICALLY
,"Split vertically")
865 self
.AppendSeparator()
866 self
.Append(DIVISION_MENU_EDIT_LEFT_EDGE
,"Edit left edge")
867 self
.Append(DIVISION_MENU_EDIT_TOP_EDGE
,"Edit top edge")
869 wx
.EVT_MENU_RANGE(self
, DIVISION_MENU_SPLIT_HORIZONTALLY
, DIVISION_MENU_EDIT_BOTTOM_EDGE
, self
.OnMenu
)
871 def SetClientData(self
, data
):
872 self
._clientData
= data
874 def GetClientData(self
):
875 return self
._clientData
877 def OnMenu(self
, event
):
878 division
= self
.GetClientData()
879 if event
.GetId() == DIVISION_MENU_SPLIT_HORIZONTALLY
:
880 division
.Divide(wx
.HORIZONTAL
)
881 elif event
.GetId() == DIVISION_MENU_SPLIT_VERTICALLY
:
882 division
.Divide(wx
.VERTICAL
)
883 elif event
.GetId() == DIVISION_MENU_EDIT_LEFT_EDGE
:
884 division
.EditEdge(DIVISION_SIDE_LEFT
)
885 elif event
.GetId() == DIVISION_MENU_EDIT_TOP_EDGE
:
886 division
.EditEdge(DIVISION_SIDE_TOP
)
890 class DivisionShape(CompositeShape
):
891 """A division shape is like a composite in that it can contain further
892 objects, but is used exclusively to divide another shape into regions,
893 or divisions. A wxDivisionShape is never free-standing.
899 CompositeShape
.__init
__(self
)
900 self
.SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT
)
901 self
.SetCentreResize(False)
902 self
.SetAttachmentMode(True)
903 self
._leftSide
= None
904 self
._rightSide
= None
906 self
._bottomSide
= None
907 self
._handleSide
= DIVISION_SIDE_NONE
908 self
._leftSidePen
= wx
.BLACK_PEN
909 self
._topSidePen
= wx
.BLACK_PEN
910 self
._leftSideColour
= "BLACK"
911 self
._topSideColour
= "BLACK"
912 self
._leftSideStyle
= "Solid"
913 self
._topSideStyle
= "Solid"
916 def SetLeftSide(self
, shape
):
917 """Set the the division on the left side of this division."""
918 self
._leftSide
= shape
920 def SetTopSide(self
, shape
):
921 """Set the the division on the top side of this division."""
922 self
._topSide
= shape
924 def SetRightSide(self
, shape
):
925 """Set the the division on the right side of this division."""
926 self
._rightSide
= shape
928 def SetBottomSide(self
, shape
):
929 """Set the the division on the bottom side of this division."""
930 self
._bottomSide
= shape
932 def GetLeftSide(self
):
933 """Return the division on the left side of this division."""
934 return self
._leftSide
936 def GetTopSide(self
):
937 """Return the division on the top side of this division."""
940 def GetRightSide(self
):
941 """Return the division on the right side of this division."""
942 return self
._rightSide
944 def GetBottomSide(self
):
945 """Return the division on the bottom side of this division."""
946 return self
._bottomSide
948 def SetHandleSide(self
, side
):
949 """Sets the side which the handle appears on.
951 Either DIVISION_SIDE_LEFT or DIVISION_SIDE_TOP.
953 self
._handleSide
= side
955 def GetHandleSide(self
):
956 """Return the side which the handle appears on."""
957 return self
._handleSide
959 def SetLeftSidePen(self
, pen
):
960 """Set the colour for drawing the left side of the division."""
961 self
._leftSidePen
= pen
963 def SetTopSidePen(self
, pen
):
964 """Set the colour for drawing the top side of the division."""
965 self
._topSidePen
= pen
967 def GetLeftSidePen(self
):
968 """Return the pen used for drawing the left side of the division."""
969 return self
._leftSidePen
971 def GetTopSidePen(self
):
972 """Return the pen used for drawing the top side of the division."""
973 return self
._topSidePen
975 def GetLeftSideColour(self
):
976 """Return the colour used for drawing the left side of the division."""
977 return self
._leftSideColour
979 def GetTopSideColour(self
):
980 """Return the colour used for drawing the top side of the division."""
981 return self
._topSideColour
983 def SetLeftSideColour(self
, colour
):
984 """Set the colour for drawing the left side of the division."""
985 self
._leftSideColour
= colour
987 def SetTopSideColour(self
, colour
):
988 """Set the colour for drawing the top side of the division."""
989 self
._topSideColour
= colour
991 def GetLeftSideStyle(self
):
992 """Return the style used for the left side of the division."""
993 return self
._leftSideStyle
995 def GetTopSideStyle(self
):
996 """Return the style used for the top side of the division."""
997 return self
._topSideStyle
999 def SetLeftSideStyle(self
, style
):
1000 self
._leftSideStyle
= style
1002 def SetTopSideStyle(self
, style
):
1003 self
._lefttopStyle
= style
1005 def OnDraw(self
, dc
):
1006 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1007 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
1009 x1
= self
.GetX() - self
.GetWidth() / 2.0
1010 y1
= self
.GetY() - self
.GetHeight() / 2.0
1011 x2
= self
.GetX() + self
.GetWidth() / 2.0
1012 y2
= self
.GetY() + self
.GetHeight() / 2.0
1014 # Should subtract 1 pixel if drawing under Windows
1015 if sys
.platform
[:3] == "win":
1019 dc
.SetPen(self
._leftSidePen
)
1020 dc
.DrawLine(x1
, y2
, x1
, y1
)
1023 dc
.SetPen(self
._topSidePen
)
1024 dc
.DrawLine(x1
, y1
, x2
, y1
)
1026 # For testing purposes, draw a rectangle so we know
1027 # how big the division is.
1028 #dc.SetBrush(wx.RED_BRUSH)
1029 #dc.DrawRectangle(x1, y1, self.GetWidth(), self.GetHeight())
1031 def OnDrawContents(self
, dc
):
1032 CompositeShape
.OnDrawContents(self
, dc
)
1034 def OnMovePre(self
, dc
, x
, y
, oldx
, oldy
, display
= True):
1037 for object in self
._children
:
1039 object.Move(dc
, object.GetX() + diffX
, object.GetY() + diffY
, display
)
1042 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1043 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1045 hit
= self
._parent
.HitTest(x
, y
)
1047 attachment
, dist
= hit
1048 self
._parent
.GetEventHandler().OnDragLeft(draw
, x
, y
, keys
, attachment
)
1050 Shape
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
1052 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
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().OnBeginDragLeft(x
, y
, keys
, attachment
)
1060 Shape
.OnBeginDragLeft(x
, y
, keys
, attachment
)
1062 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1063 if self
._canvas
.HasCapture():
1064 self
._canvas
.ReleaseMouse()
1065 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1067 hit
= self
._parent
.HitTest(x
, y
)
1069 attachment
, dist
= hit
1070 self
._parent
.GetEventHandler().OnEndDragLeft(x
, y
, keys
, attachment
)
1073 dc
= wx
.ClientDC(self
.GetCanvas())
1074 self
.GetCanvas().PrepareDC(dc
)
1076 dc
.SetLogicalFunction(wx
.COPY
)
1078 self
._xpos
, self
._ypos
= self
._canvas
.Snap(self
._xpos
, self
._ypos
)
1079 self
.GetEventHandler().OnMovePre(dc
, x
, y
, self
._oldX
, self
._oldY
)
1081 self
.ResetControlPoints()
1084 self
.GetEventHandler().OnDrawControlPoints(dc
)
1086 if self
._canvas
and not self
._canvas
.GetQuickEditMode():
1087 self
._canvas
.Redraw(dc
)
1089 def SetSize(self
, w
, h
, recursive
= True):
1092 RectangleShape
.SetSize(self
, w
, h
, recursive
)
1094 def CalculateSize(self
):
1098 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
1100 self
.PopupMenu(x
, y
)
1103 hit
= self
._parent
.HitTest(x
, y
)
1105 attachment
, dist
= hit
1106 self
._parent
.GetEventHandler().OnRightClick(x
, y
, keys
, attachment
)
1108 # Divide wx.HORIZONTALly or wx.VERTICALly
1109 def Divide(self
, direction
):
1110 """Divide this division into two further divisions,
1111 horizontally (direction is wxHORIZONTAL) or
1112 vertically (direction is wxVERTICAL).
1114 # Calculate existing top-left, bottom-right
1115 x1
= self
.GetX() - self
.GetWidth() / 2.0
1116 y1
= self
.GetY() - self
.GetHeight() / 2.0
1118 compositeParent
= self
.GetParent()
1119 oldWidth
= self
.GetWidth()
1120 oldHeight
= self
.GetHeight()
1124 dc
= wx
.ClientDC(self
.GetCanvas())
1125 self
.GetCanvas().PrepareDC(dc
)
1127 if direction
== wx
.VERTICAL
:
1128 # Dividing vertically means notionally putting a horizontal
1130 # Break existing piece into two.
1131 newXPos1
= self
.GetX()
1132 newYPos1
= y1
+ self
.GetHeight() / 4.0
1133 newXPos2
= self
.GetX()
1134 newYPos2
= y1
+ 3 * self
.GetHeight() / 4.0
1135 newDivision
= compositeParent
.OnCreateDivision()
1136 newDivision
.Show(True)
1140 # Anything adjoining the bottom of this division now adjoins the
1141 # bottom of the new division.
1142 for obj
in compositeParent
.GetDivisions():
1143 if obj
.GetTopSide() == self
:
1144 obj
.SetTopSide(newDivision
)
1146 newDivision
.SetTopSide(self
)
1147 newDivision
.SetBottomSide(self
._bottomSide
)
1148 newDivision
.SetLeftSide(self
._leftSide
)
1149 newDivision
.SetRightSide(self
._rightSide
)
1150 self
._bottomSide
= newDivision
1152 compositeParent
.GetDivisions().append(newDivision
)
1154 # CHANGE: Need to insert this division at start of divisions in the
1155 # object list, because e.g.:
1157 # 2) Add contained object
1159 # Division is now receiving mouse events _before_ the contained
1160 # object, because it was added last (on top of all others)
1162 # Add after the image that visualizes the container
1163 compositeParent
.AddChild(newDivision
, compositeParent
.FindContainerImage())
1165 self
._handleSide
= DIVISION_SIDE_BOTTOM
1166 newDivision
.SetHandleSide(DIVISION_SIDE_TOP
)
1168 self
.SetSize(oldWidth
, oldHeight
/ 2.0)
1169 self
.Move(dc
, newXPos1
, newYPos1
)
1171 newDivision
.SetSize(oldWidth
, oldHeight
/ 2.0)
1172 newDivision
.Move(dc
, newXPos2
, newYPos2
)
1174 # Dividing horizontally means notionally putting a vertical line
1176 # Break existing piece into two.
1177 newXPos1
= x1
+ self
.GetWidth() / 4.0
1178 newYPos1
= self
.GetY()
1179 newXPos2
= x1
+ 3 * self
.GetWidth() / 4.0
1180 newYPos2
= self
.GetY()
1181 newDivision
= compositeParent
.OnCreateDivision()
1182 newDivision
.Show(True)
1186 # Anything adjoining the left of this division now adjoins the
1187 # left of the new division.
1188 for obj
in compositeParent
.GetDivisions():
1189 if obj
.GetLeftSide() == self
:
1190 obj
.SetLeftSide(newDivision
)
1192 newDivision
.SetTopSide(self
._topSide
)
1193 newDivision
.SetBottomSide(self
._bottomSide
)
1194 newDivision
.SetLeftSide(self
)
1195 newDivision
.SetRightSide(self
._rightSide
)
1196 self
._rightSide
= newDivision
1198 compositeParent
.GetDivisions().append(newDivision
)
1199 compositeParent
.AddChild(newDivision
, compositeParent
.FindContainerImage())
1201 self
._handleSide
= DIVISION_SIDE_RIGHT
1202 newDivision
.SetHandleSide(DIVISION_SIDE_LEFT
)
1204 self
.SetSize(oldWidth
/ 2.0, oldHeight
)
1205 self
.Move(dc
, newXPos1
, newYPos1
)
1207 newDivision
.SetSize(oldWidth
/ 2.0, oldHeight
)
1208 newDivision
.Move(dc
, newXPos2
, newYPos2
)
1210 if compositeParent
.Selected():
1211 compositeParent
.DeleteControlPoints(dc
)
1212 compositeParent
.MakeControlPoints()
1213 compositeParent
.MakeMandatoryControlPoints()
1215 compositeParent
.Draw(dc
)
1218 def MakeControlPoints(self
):
1219 self
.MakeMandatoryControlPoints()
1221 def MakeMandatoryControlPoints(self
):
1222 maxX
, maxY
= self
.GetBoundingBoxMax()
1226 if self
._handleSide
== DIVISION_SIDE_LEFT
:
1228 direction
= CONTROL_POINT_HORIZONTAL
1229 elif self
._handleSide
== DIVISION_SIDE_TOP
:
1231 direction
= CONTROL_POINT_VERTICAL
1232 elif self
._handleSide
== DIVISION_SIDE_RIGHT
:
1234 direction
= CONTROL_POINT_HORIZONTAL
1235 elif self
._handleSide
== DIVISION_SIDE_BOTTOM
:
1237 direction
= CONTROL_POINT_VERTICAL
1239 if self
._handleSide
!= DIVISION_SIDE_NONE
:
1240 control
= DivisionControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, x
, y
, direction
)
1241 self
._canvas
.AddShape(control
)
1242 self
._controlPoints
.append(control
)
1244 def ResetControlPoints(self
):
1245 self
.ResetMandatoryControlPoints()
1247 def ResetMandatoryControlPoints(self
):
1248 if not self
._controlPoints
:
1251 maxX
, maxY
= self
.GetBoundingBoxMax()
1253 node
= self
._controlPoints
[0]
1255 if self
._handleSide
== DIVISION_SIDE_LEFT
and node
:
1256 node
._xoffset
= -maxX
/ 2.0
1259 if self
._handleSide
== DIVISION_SIDE_TOP
and node
:
1261 node
._yoffset
= -maxY
/ 2.0
1263 if self
._handleSide
== DIVISION_SIDE_RIGHT
and node
:
1264 node
._xoffset
= maxX
/ 2.0
1267 if self
._handleSide
== DIVISION_SIDE_BOTTOM
and node
:
1269 node
._yoffset
= maxY
/ 2.0
1271 def AdjustLeft(self
, left
, test
):
1274 Returns FALSE if it's not physically possible to adjust it to
1277 x2
= self
.GetX() + self
.GetWidth() / 2.0
1286 newX
= left
+ newW
/ 2.0
1287 self
.SetSize(newW
, self
.GetHeight())
1289 dc
= wx
.ClientDC(self
.GetCanvas())
1290 self
.GetCanvas().PrepareDC(dc
)
1292 self
.Move(dc
, newX
, self
.GetY())
1295 def AdjustTop(self
, top
, test
):
1298 Returns FALSE if it's not physically possible to adjust it to
1301 y2
= self
.GetY() + self
.GetHeight() / 2.0
1310 newY
= top
+ newH
/ 2.0
1311 self
.SetSize(self
.GetWidth(), newH
)
1313 dc
= wx
.ClientDC(self
.GetCanvas())
1314 self
.GetCanvas().PrepareDC(dc
)
1316 self
.Move(dc
, self
.GetX(), newY
)
1319 def AdjustRight(self
, right
, test
):
1322 Returns FALSE if it's not physically possible to adjust it to
1325 x1
= self
.GetX() - self
.GetWidth() / 2.0
1334 newX
= x1
+ newW
/ 2.0
1335 self
.SetSize(newW
, self
.GetHeight())
1337 dc
= wx
.ClientDC(self
.GetCanvas())
1338 self
.GetCanvas().PrepareDC(dc
)
1340 self
.Move(dc
, newX
, self
.GetY())
1343 def AdjustTop(self
, top
, test
):
1346 Returns FALSE if it's not physically possible to adjust it to
1349 y1
= self
.GetY() - self
.GetHeight() / 2.0
1358 newY
= y1
+ newH
/ 2.0
1359 self
.SetSize(self
.GetWidth(), newH
)
1361 dc
= wx
.ClientDC(self
.GetCanvas())
1362 self
.GetCanvas().PrepareDC(dc
)
1364 self
.Move(dc
, self
.GetX(), newY
)
1367 # Resize adjoining divisions.
1369 # Behaviour should be as follows:
1370 # If right edge moves, find all objects whose left edge
1371 # adjoins this object, and move left edge accordingly.
1372 # If left..., move ... right.
1373 # If top..., move ... bottom.
1374 # If bottom..., move top.
1375 # If size goes to zero or end position is other side of start position,
1376 # resize to original size and return.
1378 def ResizeAdjoining(self
, side
, newPos
, test
):
1379 """Resize adjoining divisions at the given side.
1381 If test is TRUE, just see whether it's possible for each adjoining
1382 region, returning FALSE if it's not.
1386 * DIVISION_SIDE_NONE
1387 * DIVISION_SIDE_LEFT
1389 * DIVISION_SIDE_RIGHT
1390 * DIVISION_SIDE_BOTTOM
1392 divisionParent
= self
.GetParent()
1393 for division
in divisionParent
.GetDivisions():
1394 if side
== DIVISION_SIDE_LEFT
:
1395 if division
._rightSide
== self
:
1396 success
= division
.AdjustRight(newPos
, test
)
1397 if not success
and test
:
1399 elif side
== DIVISION_SIDE_TOP
:
1400 if division
._bottomSide
== self
:
1401 success
= division
.AdjustBottom(newPos
, test
)
1402 if not success
and test
:
1404 elif side
== DIVISION_SIDE_RIGHT
:
1405 if division
._leftSide
== self
:
1406 success
= division
.AdjustLeft(newPos
, test
)
1407 if not success
and test
:
1409 elif side
== DIVISION_SIDE_BOTTOM
:
1410 if division
._topSide
== self
:
1411 success
= division
.AdjustTop(newPos
, test
)
1412 if not success
and test
:
1416 def EditEdge(self
, side
):
1417 print "EditEdge() not implemented."
1419 def PopupMenu(self
, x
, y
):
1420 menu
= PopupDivisionMenu()
1421 menu
.SetClientData(self
)
1423 menu
.Enable(DIVISION_MENU_EDIT_LEFT_EDGE
, True)
1425 menu
.Enable(DIVISION_MENU_EDIT_LEFT_EDGE
, False)
1427 menu
.Enable(DIVISION_MENU_EDIT_TOP_EDGE
, True)
1429 menu
.Enable(DIVISION_MENU_EDIT_TOP_EDGE
, False)
1431 x1
, y1
= self
._canvas
.GetViewStart()
1432 unit_x
, unit_y
= self
._canvas
.GetScrollPixelsPerUnit()
1434 dc
= wx
.ClientDC(self
.GetCanvas())
1435 self
.GetCanvas().PrepareDC(dc
)
1437 mouse_x
= dc
.LogicalToDeviceX(x
- x1
* unit_x
)
1438 mouse_y
= dc
.LogicalToDeviceY(y
- y1
* unit_y
)
1440 self
._canvas
.PopupMenu(menu
, (mouse_x
, mouse_y
))