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)
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.0 > maxX
:
644 maxX
= child
.GetX() + w
/ 2.0
645 if child
.GetX() - w
/ 2.0 < minX
:
646 minX
= child
.GetX() - w
/ 2.0
647 if child
.GetY() + h
/ 2.0 > maxY
:
648 maxY
= child
.GetY() + h
/ 2.0
649 if child
.GetY() - h
/ 2.0 < minY
:
650 minY
= child
.GetY() - h
/ 2.0
652 self
._width
= maxX
- minX
653 self
._height
= maxY
- minY
654 self
._xpos
= self
._width
/ 2.0 + minX
655 self
._ypos
= self
._height
/ 2.0 + 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.0
787 y1
= divisionParent
.GetY() - divisionParent
.GetHeight() / 2.0
788 x2
= divisionParent
.GetX() + divisionParent
.GetWidth() / 2.0
789 y2
= divisionParent
.GetY() + divisionParent
.GetHeight() / 2.0
791 # Need to check it has not made the division zero or negative
793 dx1
= division
.GetX() - division
.GetWidth() / 2.0
794 dy1
= division
.GetY() - division
.GetHeight() / 2.0
795 dx2
= division
.GetX() + division
.GetWidth() / 2.0
796 dy2
= division
.GetY() + division
.GetHeight() / 2.0
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.0
998 y1
= self
.GetY() - self
.GetHeight() / 2.0
999 x2
= self
.GetX() + self
.GetWidth() / 2.0
1000 y2
= self
.GetY() + self
.GetHeight() / 2.0
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.0
1104 y1
= self
.GetY() - self
.GetHeight() / 2.0
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.0
1121 newXPos2
= self
.GetX()
1122 newYPos2
= y1
+ 3 * self
.GetHeight() / 4.0
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.0)
1157 self
.Move(dc
, newXPos1
, newYPos1
)
1159 newDivision
.SetSize(oldWidth
, oldHeight
/ 2.0)
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.0
1166 newYPos1
= self
.GetY()
1167 newXPos2
= x1
+ 3 * self
.GetWidth() / 4.0
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.0, oldHeight
)
1193 self
.Move(dc
, newXPos1
, newYPos1
)
1195 newDivision
.SetSize(oldWidth
/ 2.0, 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.0
1247 if self
._handleSide
== DIVISION_SIDE_TOP
and node
:
1249 node
._yoffset
= -maxY
/ 2.0
1251 if self
._handleSide
== DIVISION_SIDE_RIGHT
and node
:
1252 node
._xoffset
= maxX
/ 2.0
1255 if self
._handleSide
== DIVISION_SIDE_BOTTOM
and node
:
1257 node
._yoffset
= maxY
/ 2.0
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.0
1274 newX
= left
+ newW
/ 2.0
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.0
1298 newY
= top
+ newH
/ 2.0
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.0
1322 newX
= x1
+ newW
/ 2.0
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.0
1346 newY
= y1
+ newH
/ 2.0
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
))