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
))