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
._constraintingObject
= 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
._constraintingObject
.GetBoundingBoxMax()
162 minWidth
, minHeight
= self
._constraintingObject
.GetBoundingBoxMin()
163 x
= self
._constraintingObject
.GetX()
164 y
= self
._constraintingObject
.GetY()
166 dc
= wx
.ClientDC(self
._constraintingObject
.GetCanvas())
167 self
._constraintingObject
.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 self
._children
.remove(child
)
556 self
._divisions
.remove(child
)
557 self
.RemoveChildFromConstraints(child
)
558 child
.SetParent(None)
560 def DeleteConstraintsInvolvingChild(self
, child
):
561 """This function deletes constraints which mention the given child.
563 Used when deleting a child from the composite.
565 for constraint
in self
._constraints
:
566 if constraint
._constrainingObject
== child
or child
in constraint
._constrainedObjects
:
567 self
._constraints
.remove(constraint
)
569 def RemoveChildFromConstraints(self
, child
):
570 for constraint
in self
._constraints
:
571 if child
in constraint
._constrainedObjects
:
572 constraint
._constrainedObjects
.remove(child
)
573 if constraint
._constrainingObject
== child
:
574 constraint
._constrainingObject
= None
576 # Delete the constraint if no participants left
577 if not constraint
._constrainingObject
:
578 self
._constraints
.remove(constraint
)
580 def AddConstraint(self
, constraint
):
581 """Adds a constraint to the composite."""
582 self
._constraints
.append(constraint
)
583 if constraint
._constraintId
== 0:
584 constraint
._constraintId
= wx
.NewId()
587 def AddSimpleConstraint(self
, type, constraining
, constrained
):
588 """Add a constraint of the given type to the composite.
590 constraining is the shape doing the constraining
591 constrained is a list of shapes being constrained
593 constraint
= Constraint(type, constraining
, constrained
)
594 if constraint
._constraintId
== 0:
595 constraint
._constraintId
= wx
.NewId()
596 self
._constraints
.append(constraint
)
599 def FindConstraint(self
, cId
):
600 """Finds the constraint with the given id.
602 Returns a tuple of the constraint and the actual composite the
603 constraint was in, in case that composite was a descendant of
606 Returns None if not found.
608 for constraint
in self
._constraints
:
609 if constraint
._constraintId
== cId
:
610 return constraint
, self
612 # If not found, try children
613 for child
in self
._children
:
614 if isinstance(child
, CompositeShape
):
615 constraint
= child
.FindConstraint(cId
)
617 return constraint
[0], child
621 def DeleteConstraint(self
, constraint
):
622 """Deletes constraint from composite."""
623 self
._constraints
.remove(constraint
)
625 def CalculateSize(self
):
626 """Calculates the size and position of the composite based on
627 child sizes and positions.
634 for child
in self
._children
:
635 # Recalculate size of composite objects because may not conform
636 # to size it was set to - depends on the children.
637 if isinstance(child
, CompositeShape
):
638 child
.CalculateSize()
640 w
, h
= child
.GetBoundingBoxMax()
641 if child
.GetX() + w
/ 2.0 > maxX
:
642 maxX
= child
.GetX() + w
/ 2.0
643 if child
.GetX() - w
/ 2.0 < minX
:
644 minX
= child
.GetX() - w
/ 2.0
645 if child
.GetY() + h
/ 2.0 > maxY
:
646 maxY
= child
.GetY() + h
/ 2.0
647 if child
.GetY() - h
/ 2.0 < minY
:
648 minY
= child
.GetY() - h
/ 2.0
650 self
._width
= maxX
- minX
651 self
._height
= maxY
- minY
652 self
._xpos
= self
._width
/ 2.0 + minX
653 self
._ypos
= self
._height
/ 2.0 + minY
656 """Recomputes any constraints associated with the object. If FALSE is
657 returned, the constraints could not be satisfied (there was an
662 while changed
and noIterations
< 500:
663 changed
= self
.Constrain()
672 for child
in self
._children
:
673 if isinstance(child
, CompositeShape
) and child
.Constrain():
676 for constraint
in self
._constraints
:
677 if constraint
.Evaluate():
682 def MakeContainer(self
):
683 """Makes this composite into a container by creating one child
686 division
= self
.OnCreateDivision()
687 self
._divisions
.append(division
)
688 self
.AddChild(division
)
690 division
.SetSize(self
._width
, self
._height
)
692 dc
= wx
.ClientDC(self
.GetCanvas())
693 self
.GetCanvas().PrepareDC(dc
)
695 division
.Move(dc
, self
.GetX(), self
.GetY())
699 def OnCreateDivision(self
):
700 return DivisionShape()
702 def FindContainerImage(self
):
703 """Finds the image used to visualize a container. This is any child of
704 the composite that is not in the divisions list.
706 for child
in self
._children
:
707 if child
in self
._divisions
:
712 def ContainsDivision(self
, division
):
713 """Returns TRUE if division is a descendant of this container."""
714 if division
in self
._divisions
:
717 for child
in self
._children
:
718 if isinstance(child
, CompositeShape
):
719 return child
.ContainsDivision(division
)
723 def GetDivisions(self
):
724 """Return the list of divisions."""
725 return self
._divisions
727 def GetConstraints(self
):
728 """Return the list of constraints."""
729 return self
._constraints
732 # A division object is a composite with special properties,
733 # to be used for containment. It's a subdivision of a container.
734 # A containing node image consists of a composite with a main child shape
735 # such as rounded rectangle, plus a list of division objects.
736 # It needs to be a composite because a division contains pieces
738 # NOTE a container has at least one wxDivisionShape for consistency.
739 # This can be subdivided, so it turns into two objects, then each of
740 # these can be subdivided, etc.
742 DIVISION_SIDE_NONE
=0
743 DIVISION_SIDE_LEFT
=1
745 DIVISION_SIDE_RIGHT
=3
746 DIVISION_SIDE_BOTTOM
=4
755 class DivisionControlPoint(ControlPoint
):
756 def __init__(self
, the_canvas
, object, size
, the_xoffset
, the_yoffset
, the_type
):
757 ControlPoint
.__init
__(self
, the_canvas
, object, size
, the_xoffset
, the_yoffset
, the_type
)
758 self
.SetEraseObject(False)
760 # Implement resizing of canvas object
761 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
762 ControlPoint
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
764 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
765 global originalX
, originalY
, originalW
, originalH
767 originalX
= self
._shape
.GetX()
768 originalY
= self
._shape
.GetY()
769 originalW
= self
._shape
.GetWidth()
770 originalH
= self
._shape
.GetHeight()
772 ControlPoint
.OnBeginDragLeft(self
, x
, y
, keys
, attachment
)
774 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
775 ControlPoint
.OnEndDragLeft(self
, x
, y
, keys
, attachment
)
777 dc
= wx
.ClientDC(self
.GetCanvas())
778 self
.GetCanvas().PrepareDC(dc
)
780 division
= self
._shape
781 divisionParent
= division
.GetParent()
783 # Need to check it's within the bounds of the parent composite
784 x1
= divisionParent
.GetX() - divisionParent
.GetWidth() / 2.0
785 y1
= divisionParent
.GetY() - divisionParent
.GetHeight() / 2.0
786 x2
= divisionParent
.GetX() + divisionParent
.GetWidth() / 2.0
787 y2
= divisionParent
.GetY() + divisionParent
.GetHeight() / 2.0
789 # Need to check it has not made the division zero or negative
791 dx1
= division
.GetX() - division
.GetWidth() / 2.0
792 dy1
= division
.GetY() - division
.GetHeight() / 2.0
793 dx2
= division
.GetX() + division
.GetWidth() / 2.0
794 dy2
= division
.GetY() + division
.GetHeight() / 2.0
797 if division
.GetHandleSide() == DIVISION_SIDE_LEFT
:
798 if x
<= x1
or x
>= x2
or x
>= dx2
:
800 # Try it out first...
801 elif not division
.ResizeAdjoining(DIVISION_SIDE_LEFT
, x
, True):
804 division
.ResizeAdjoining(DIVISION_SIDE_LEFT
, x
, False)
805 elif division
.GetHandleSide() == DIVISION_SIDE_TOP
:
806 if y
<= y1
or y
>= y2
or y
>= dy2
:
808 elif not division
.ResizeAdjoining(DIVISION_SIDE_TOP
, y
, True):
811 division
.ResizingAdjoining(DIVISION_SIDE_TOP
, y
, False)
812 elif division
.GetHandleSide() == DIVISION_SIDE_RIGHT
:
813 if x
<= x1
or x
>= x2
or x
<= dx1
:
815 elif not division
.ResizeAdjoining(DIVISION_SIDE_RIGHT
, x
, True):
818 division
.ResizeAdjoining(DIVISION_SIDE_RIGHT
, x
, False)
819 elif division
.GetHandleSide() == DIVISION_SIDE_BOTTOM
:
820 if y
<= y1
or y
>= y2
or y
<= dy1
:
822 elif not division
.ResizeAdjoining(DIVISION_SIDE_BOTTOM
, y
, True):
825 division
.ResizeAdjoining(DIVISION_SIDE_BOTTOM
, y
, False)
828 division
.SetSize(originalW
, originalH
)
829 division
.Move(dc
, originalX
, originalY
)
831 divisionParent
.Draw(dc
)
832 division
.GetEventHandler().OnDrawControlPoints(dc
)
836 DIVISION_MENU_SPLIT_HORIZONTALLY
=1
837 DIVISION_MENU_SPLIT_VERTICALLY
=2
838 DIVISION_MENU_EDIT_LEFT_EDGE
=3
839 DIVISION_MENU_EDIT_TOP_EDGE
=4
840 DIVISION_MENU_EDIT_RIGHT_EDGE
=5
841 DIVISION_MENU_EDIT_BOTTOM_EDGE
=6
842 DIVISION_MENU_DELETE_ALL
=7
846 class PopupDivisionMenu(wx
.Menu
):
848 wx
.Menu
.__init
__(self
)
849 self
.Append(DIVISION_MENU_SPLIT_HORIZONTALLY
,"Split horizontally")
850 self
.Append(DIVISION_MENU_SPLIT_VERTICALLY
,"Split vertically")
851 self
.AppendSeparator()
852 self
.Append(DIVISION_MENU_EDIT_LEFT_EDGE
,"Edit left edge")
853 self
.Append(DIVISION_MENU_EDIT_TOP_EDGE
,"Edit top edge")
855 wx
.EVT_MENU_RANGE(self
, DIVISION_MENU_SPLIT_HORIZONTALLY
, DIVISION_MENU_EDIT_BOTTOM_EDGE
, self
.OnMenu
)
857 def SetClientData(self
, data
):
858 self
._clientData
= data
860 def GetClientData(self
):
861 return self
._clientData
863 def OnMenu(self
, event
):
864 division
= self
.GetClientData()
865 if event
.GetId() == DIVISION_MENU_SPLIT_HORIZONTALLY
:
866 division
.Divide(wx
.HORIZONTAL
)
867 elif event
.GetId() == DIVISION_MENU_SPLIT_VERTICALLY
:
868 division
.Divide(wx
.VERTICAL
)
869 elif event
.GetId() == DIVISION_MENU_EDIT_LEFT_EDGE
:
870 division
.EditEdge(DIVISION_SIDE_LEFT
)
871 elif event
.GetId() == DIVISION_MENU_EDIT_TOP_EDGE
:
872 division
.EditEdge(DIVISION_SIDE_TOP
)
876 class DivisionShape(CompositeShape
):
877 """A division shape is like a composite in that it can contain further
878 objects, but is used exclusively to divide another shape into regions,
879 or divisions. A wxDivisionShape is never free-standing.
885 CompositeShape
.__init
__(self
)
886 self
.SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT
)
887 self
.SetCentreResize(False)
888 self
.SetAttachmentMode(True)
889 self
._leftSide
= None
890 self
._rightSide
= None
892 self
._bottomSide
= None
893 self
._handleSide
= DIVISION_SIDE_NONE
894 self
._leftSidePen
= wx
.BLACK_PEN
895 self
._topSidePen
= wx
.BLACK_PEN
896 self
._leftSideColour
= "BLACK"
897 self
._topSideColour
= "BLACK"
898 self
._leftSideStyle
= "Solid"
899 self
._topSideStyle
= "Solid"
902 def SetLeftSide(self
, shape
):
903 """Set the the division on the left side of this division."""
904 self
._leftSide
= shape
906 def SetTopSide(self
, shape
):
907 """Set the the division on the top side of this division."""
908 self
._topSide
= shape
910 def SetRightSide(self
, shape
):
911 """Set the the division on the right side of this division."""
912 self
._rightSide
= shape
914 def SetBottomSide(self
, shape
):
915 """Set the the division on the bottom side of this division."""
916 self
._bottomSide
= shape
918 def GetLeftSide(self
):
919 """Return the division on the left side of this division."""
920 return self
._leftSide
922 def GetTopSide(self
):
923 """Return the division on the top side of this division."""
926 def GetRightSide(self
):
927 """Return the division on the right side of this division."""
928 return self
._rightSide
930 def GetBottomSide(self
):
931 """Return the division on the bottom side of this division."""
932 return self
._bottomSide
934 def SetHandleSide(self
, side
):
935 """Sets the side which the handle appears on.
937 Either DIVISION_SIDE_LEFT or DIVISION_SIDE_TOP.
939 self
._handleSide
= side
941 def GetHandleSide(self
):
942 """Return the side which the handle appears on."""
943 return self
._handleSide
945 def SetLeftSidePen(self
, pen
):
946 """Set the colour for drawing the left side of the division."""
947 self
._leftSidePen
= pen
949 def SetTopSidePen(self
, pen
):
950 """Set the colour for drawing the top side of the division."""
951 self
._topSidePen
= pen
953 def GetLeftSidePen(self
):
954 """Return the pen used for drawing the left side of the division."""
955 return self
._leftSidePen
957 def GetTopSidePen(self
):
958 """Return the pen used for drawing the top side of the division."""
959 return self
._topSidePen
961 def GetLeftSideColour(self
):
962 """Return the colour used for drawing the left side of the division."""
963 return self
._leftSideColour
965 def GetTopSideColour(self
):
966 """Return the colour used for drawing the top side of the division."""
967 return self
._topSideColour
969 def SetLeftSideColour(self
, colour
):
970 """Set the colour for drawing the left side of the division."""
971 self
._leftSideColour
= colour
973 def SetTopSideColour(self
, colour
):
974 """Set the colour for drawing the top side of the division."""
975 self
._topSideColour
= colour
977 def GetLeftSideStyle(self
):
978 """Return the style used for the left side of the division."""
979 return self
._leftSideStyle
981 def GetTopSideStyle(self
):
982 """Return the style used for the top side of the division."""
983 return self
._topSideStyle
985 def SetLeftSideStyle(self
, style
):
986 self
._leftSideStyle
= style
988 def SetTopSideStyle(self
, style
):
989 self
._lefttopStyle
= style
991 def OnDraw(self
, dc
):
992 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
993 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
995 x1
= self
.GetX() - self
.GetWidth() / 2.0
996 y1
= self
.GetY() - self
.GetHeight() / 2.0
997 x2
= self
.GetX() + self
.GetWidth() / 2.0
998 y2
= self
.GetY() + self
.GetHeight() / 2.0
1000 # Should subtract 1 pixel if drawing under Windows
1001 if sys
.platform
[:3] == "win":
1005 dc
.SetPen(self
._leftSidePen
)
1006 dc
.DrawLine(x1
, y2
, x1
, y1
)
1009 dc
.SetPen(self
._topSidePen
)
1010 dc
.DrawLine(x1
, y1
, x2
, y1
)
1012 # For testing purposes, draw a rectangle so we know
1013 # how big the division is.
1014 #dc.SetBrush(wx.RED_BRUSH)
1015 #dc.DrawRectangle(x1, y1, self.GetWidth(), self.GetHeight())
1017 def OnDrawContents(self
, dc
):
1018 CompositeShape
.OnDrawContents(self
, dc
)
1020 def OnMovePre(self
, dc
, x
, y
, oldx
, oldy
, display
= True):
1023 for object in self
._children
:
1025 object.Move(dc
, object.GetX() + diffX
, object.GetY() + diffY
, display
)
1028 def OnDragLeft(self
, draw
, x
, y
, keys
= 0, attachment
= 0):
1029 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1031 hit
= self
._parent
.HitTest(x
, y
)
1033 attachment
, dist
= hit
1034 self
._parent
.GetEventHandler().OnDragLeft(draw
, x
, y
, keys
, attachment
)
1036 Shape
.OnDragLeft(self
, draw
, x
, y
, keys
, attachment
)
1038 def OnBeginDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1039 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1041 hit
= self
._parent
.HitTest(x
, y
)
1043 attachment
, dist
= hit
1044 self
._parent
.GetEventHandler().OnBeginDragLeft(x
, y
, keys
, attachment
)
1046 Shape
.OnBeginDragLeft(x
, y
, keys
, attachment
)
1048 def OnEndDragLeft(self
, x
, y
, keys
= 0, attachment
= 0):
1049 if self
._canvas
.HasCapture():
1050 self
._canvas
.ReleaseMouse()
1051 if self
._sensitivity
& OP_DRAG_LEFT
!= OP_DRAG_LEFT
:
1053 hit
= self
._parent
.HitTest(x
, y
)
1055 attachment
, dist
= hit
1056 self
._parent
.GetEventHandler().OnEndDragLeft(x
, y
, keys
, attachment
)
1059 dc
= wx
.ClientDC(self
.GetCanvas())
1060 self
.GetCanvas().PrepareDC(dc
)
1062 dc
.SetLogicalFunction(wx
.COPY
)
1064 self
._xpos
, self
._ypos
= self
._canvas
.Snap(self
._xpos
, self
._ypos
)
1065 self
.GetEventHandler().OnMovePre(dc
, x
, y
, self
._oldX
, self
._oldY
)
1067 self
.ResetControlPoints()
1070 self
.GetEventHandler().OnDrawControlPoints(dc
)
1072 if self
._canvas
and not self
._canvas
.GetQuickEditMode():
1073 self
._canvas
.Redraw(dc
)
1075 def SetSize(self
, w
, h
, recursive
= True):
1078 RectangleShape
.SetSize(self
, w
, h
, recursive
)
1080 def CalculateSize(self
):
1084 def OnRightClick(self
, x
, y
, keys
= 0, attachment
= 0):
1086 self
.PopupMenu(x
, y
)
1089 hit
= self
._parent
.HitTest(x
, y
)
1091 attachment
, dist
= hit
1092 self
._parent
.GetEventHandler().OnRightClick(x
, y
, keys
, attachment
)
1094 # Divide wx.HORIZONTALly or wx.VERTICALly
1095 def Divide(self
, direction
):
1096 """Divide this division into two further divisions,
1097 horizontally (direction is wxHORIZONTAL) or
1098 vertically (direction is wxVERTICAL).
1100 # Calculate existing top-left, bottom-right
1101 x1
= self
.GetX() - self
.GetWidth() / 2.0
1102 y1
= self
.GetY() - self
.GetHeight() / 2.0
1104 compositeParent
= self
.GetParent()
1105 oldWidth
= self
.GetWidth()
1106 oldHeight
= self
.GetHeight()
1110 dc
= wx
.ClientDC(self
.GetCanvas())
1111 self
.GetCanvas().PrepareDC(dc
)
1113 if direction
== wx
.VERTICAL
:
1114 # Dividing vertically means notionally putting a horizontal
1116 # Break existing piece into two.
1117 newXPos1
= self
.GetX()
1118 newYPos1
= y1
+ self
.GetHeight() / 4.0
1119 newXPos2
= self
.GetX()
1120 newYPos2
= y1
+ 3 * self
.GetHeight() / 4.0
1121 newDivision
= compositeParent
.OnCreateDivision()
1122 newDivision
.Show(True)
1126 # Anything adjoining the bottom of this division now adjoins the
1127 # bottom of the new division.
1128 for obj
in compositeParent
.GetDivisions():
1129 if obj
.GetTopSide() == self
:
1130 obj
.SetTopSide(newDivision
)
1132 newDivision
.SetTopSide(self
)
1133 newDivision
.SetBottomSide(self
._bottomSide
)
1134 newDivision
.SetLeftSide(self
._leftSide
)
1135 newDivision
.SetRightSide(self
._rightSide
)
1136 self
._bottomSide
= newDivision
1138 compositeParent
.GetDivisions().append(newDivision
)
1140 # CHANGE: Need to insert this division at start of divisions in the
1141 # object list, because e.g.:
1143 # 2) Add contained object
1145 # Division is now receiving mouse events _before_ the contained
1146 # object, because it was added last (on top of all others)
1148 # Add after the image that visualizes the container
1149 compositeParent
.AddChild(newDivision
, compositeParent
.FindContainerImage())
1151 self
._handleSide
= DIVISION_SIDE_BOTTOM
1152 newDivision
.SetHandleSide(DIVISION_SIDE_TOP
)
1154 self
.SetSize(oldWidth
, oldHeight
/ 2.0)
1155 self
.Move(dc
, newXPos1
, newYPos1
)
1157 newDivision
.SetSize(oldWidth
, oldHeight
/ 2.0)
1158 newDivision
.Move(dc
, newXPos2
, newYPos2
)
1160 # Dividing horizontally means notionally putting a vertical line
1162 # Break existing piece into two.
1163 newXPos1
= x1
+ self
.GetWidth() / 4.0
1164 newYPos1
= self
.GetY()
1165 newXPos2
= x1
+ 3 * self
.GetWidth() / 4.0
1166 newYPos2
= self
.GetY()
1167 newDivision
= compositeParent
.OnCreateDivision()
1168 newDivision
.Show(True)
1172 # Anything adjoining the left of this division now adjoins the
1173 # left of the new division.
1174 for obj
in compositeParent
.GetDivisions():
1175 if obj
.GetLeftSide() == self
:
1176 obj
.SetLeftSide(newDivision
)
1178 newDivision
.SetTopSide(self
._topSide
)
1179 newDivision
.SetBottomSide(self
._bottomSide
)
1180 newDivision
.SetLeftSide(self
)
1181 newDivision
.SetRightSide(self
._rightSide
)
1182 self
._rightSide
= newDivision
1184 compositeParent
.GetDivisions().append(newDivision
)
1185 compositeParent
.AddChild(newDivision
, compositeParent
.FindContainerImage())
1187 self
._handleSide
= DIVISION_SIDE_RIGHT
1188 newDivision
.SetHandleSide(DIVISION_SIDE_LEFT
)
1190 self
.SetSize(oldWidth
/ 2.0, oldHeight
)
1191 self
.Move(dc
, newXPos1
, newYPos1
)
1193 newDivision
.SetSize(oldWidth
/ 2.0, oldHeight
)
1194 newDivision
.Move(dc
, newXPos2
, newYPos2
)
1196 if compositeParent
.Selected():
1197 compositeParent
.DeleteControlPoints(dc
)
1198 compositeParent
.MakeControlPoints()
1199 compositeParent
.MakeMandatoryControlPoints()
1201 compositeParent
.Draw(dc
)
1204 def MakeControlPoints(self
):
1205 self
.MakeMandatoryControlPoints()
1207 def MakeMandatoryControlPoints(self
):
1208 maxX
, maxY
= self
.GetBoundingBoxMax()
1212 if self
._handleSide
== DIVISION_SIDE_LEFT
:
1214 direction
= CONTROL_POINT_HORIZONTAL
1215 elif self
._handleSide
== DIVISION_SIDE_TOP
:
1217 direction
= CONTROL_POINT_VERTICAL
1218 elif self
._handleSide
== DIVISION_SIDE_RIGHT
:
1220 direction
= CONTROL_POINT_HORIZONTAL
1221 elif self
._handleSide
== DIVISION_SIDE_BOTTOM
:
1223 direction
= CONTROL_POINT_VERTICAL
1225 if self
._handleSide
!= DIVISION_SIDE_NONE
:
1226 control
= DivisionControlPoint(self
._canvas
, self
, CONTROL_POINT_SIZE
, x
, y
, direction
)
1227 self
._canvas
.AddShape(control
)
1228 self
._controlPoints
.append(control
)
1230 def ResetControlPoints(self
):
1231 self
.ResetMandatoryControlPoints()
1233 def ResetMandatoryControlPoints(self
):
1234 if not self
._controlPoints
:
1237 maxX
, maxY
= self
.GetBoundingBoxMax()
1239 node
= self
._controlPoints
[0]
1241 if self
._handleSide
== DIVISION_SIDE_LEFT
and node
:
1242 node
._xoffset
= -maxX
/ 2.0
1245 if self
._handleSide
== DIVISION_SIDE_TOP
and node
:
1247 node
._yoffset
= -maxY
/ 2.0
1249 if self
._handleSide
== DIVISION_SIDE_RIGHT
and node
:
1250 node
._xoffset
= maxX
/ 2.0
1253 if self
._handleSide
== DIVISION_SIDE_BOTTOM
and node
:
1255 node
._yoffset
= maxY
/ 2.0
1257 def AdjustLeft(self
, left
, test
):
1260 Returns FALSE if it's not physically possible to adjust it to
1263 x2
= self
.GetX() + self
.GetWidth() / 2.0
1272 newX
= left
+ newW
/ 2.0
1273 self
.SetSize(newW
, self
.GetHeight())
1275 dc
= wx
.ClientDC(self
.GetCanvas())
1276 self
.GetCanvas().PrepareDC(dc
)
1278 self
.Move(dc
, newX
, self
.GetY())
1281 def AdjustTop(self
, top
, test
):
1284 Returns FALSE if it's not physically possible to adjust it to
1287 y2
= self
.GetY() + self
.GetHeight() / 2.0
1296 newY
= top
+ newH
/ 2.0
1297 self
.SetSize(self
.GetWidth(), newH
)
1299 dc
= wx
.ClientDC(self
.GetCanvas())
1300 self
.GetCanvas().PrepareDC(dc
)
1302 self
.Move(dc
, self
.GetX(), newY
)
1305 def AdjustRight(self
, right
, test
):
1308 Returns FALSE if it's not physically possible to adjust it to
1311 x1
= self
.GetX() - self
.GetWidth() / 2.0
1320 newX
= x1
+ newW
/ 2.0
1321 self
.SetSize(newW
, self
.GetHeight())
1323 dc
= wx
.ClientDC(self
.GetCanvas())
1324 self
.GetCanvas().PrepareDC(dc
)
1326 self
.Move(dc
, newX
, self
.GetY())
1329 def AdjustTop(self
, top
, test
):
1332 Returns FALSE if it's not physically possible to adjust it to
1335 y1
= self
.GetY() - self
.GetHeight() / 2.0
1344 newY
= y1
+ newH
/ 2.0
1345 self
.SetSize(self
.GetWidth(), newH
)
1347 dc
= wx
.ClientDC(self
.GetCanvas())
1348 self
.GetCanvas().PrepareDC(dc
)
1350 self
.Move(dc
, self
.GetX(), newY
)
1353 # Resize adjoining divisions.
1355 # Behaviour should be as follows:
1356 # If right edge moves, find all objects whose left edge
1357 # adjoins this object, and move left edge accordingly.
1358 # If left..., move ... right.
1359 # If top..., move ... bottom.
1360 # If bottom..., move top.
1361 # If size goes to zero or end position is other side of start position,
1362 # resize to original size and return.
1364 def ResizeAdjoining(self
, side
, newPos
, test
):
1365 """Resize adjoining divisions at the given side.
1367 If test is TRUE, just see whether it's possible for each adjoining
1368 region, returning FALSE if it's not.
1372 * DIVISION_SIDE_NONE
1373 * DIVISION_SIDE_LEFT
1375 * DIVISION_SIDE_RIGHT
1376 * DIVISION_SIDE_BOTTOM
1378 divisionParent
= self
.GetParent()
1379 for division
in divisionParent
.GetDivisions():
1380 if side
== DIVISION_SIDE_LEFT
:
1381 if division
._rightSide
== self
:
1382 success
= division
.AdjustRight(newPos
, test
)
1383 if not success
and test
:
1385 elif side
== DIVISION_SIDE_TOP
:
1386 if division
._bottomSide
== self
:
1387 success
= division
.AdjustBottom(newPos
, test
)
1388 if not success
and test
:
1390 elif side
== DIVISION_SIDE_RIGHT
:
1391 if division
._leftSide
== self
:
1392 success
= division
.AdjustLeft(newPos
, test
)
1393 if not success
and test
:
1395 elif side
== DIVISION_SIDE_BOTTOM
:
1396 if division
._topSide
== self
:
1397 success
= division
.AdjustTop(newPos
, test
)
1398 if not success
and test
:
1402 def EditEdge(self
, side
):
1403 print "EditEdge() not implemented."
1405 def PopupMenu(self
, x
, y
):
1406 menu
= PopupDivisionMenu()
1407 menu
.SetClientData(self
)
1409 menu
.Enable(DIVISION_MENU_EDIT_LEFT_EDGE
, True)
1411 menu
.Enable(DIVISION_MENU_EDIT_LEFT_EDGE
, False)
1413 menu
.Enable(DIVISION_MENU_EDIT_TOP_EDGE
, True)
1415 menu
.Enable(DIVISION_MENU_EDIT_TOP_EDGE
, False)
1417 x1
, y1
= self
._canvas
.GetViewStart()
1418 unit_x
, unit_y
= self
._canvas
.GetScrollPixelsPerUnit()
1420 dc
= wx
.ClientDC(self
.GetCanvas())
1421 self
.GetCanvas().PrepareDC(dc
)
1423 mouse_x
= dc
.LogicalToDeviceX(x
- x1
* unit_x
)
1424 mouse_y
= dc
.LogicalToDeviceY(y
- y1
* unit_y
)
1426 self
._canvas
.PopupMenu(menu
, (mouse_x
, mouse_y
))