1 # -*- coding: iso-8859-1 -*- 
   2 #---------------------------------------------------------------------------- 
   4 # Purpose:      Composite class 
   6 # Author:       Pierre Hjälm (from C++ original by Julian Smart) 
  10 # Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart 
  11 # Licence:      wxWindows license 
  12 #---------------------------------------------------------------------------- 
  17 from _basic 
import RectangleShape
, Shape
, ControlPoint
 
  18 from _oglmisc 
import * 
  20 KEY_SHIFT
, KEY_CTRL 
= 1, 2 
  25 CONSTRAINT_CENTRED_VERTICALLY   
= 1 
  26 CONSTRAINT_CENTRED_HORIZONTALLY 
= 2 
  27 CONSTRAINT_CENTRED_BOTH         
= 3 
  28 CONSTRAINT_LEFT_OF              
= 4 
  29 CONSTRAINT_RIGHT_OF             
= 5 
  32 CONSTRAINT_ALIGNED_TOP          
= 8 
  33 CONSTRAINT_ALIGNED_BOTTOM       
= 9 
  34 CONSTRAINT_ALIGNED_LEFT         
= 10 
  35 CONSTRAINT_ALIGNED_RIGHT        
= 11 
  37 # Like aligned, but with the objects centred on the respective edge 
  38 # of the reference object. 
  39 CONSTRAINT_MIDALIGNED_TOP       
= 12 
  40 CONSTRAINT_MIDALIGNED_BOTTOM    
= 13 
  41 CONSTRAINT_MIDALIGNED_LEFT      
= 14 
  42 CONSTRAINT_MIDALIGNED_RIGHT     
= 15 
  45 # Backwards compatibility names.  These should be removed eventually. 
  46 gyCONSTRAINT_CENTRED_VERTICALLY   
= CONSTRAINT_CENTRED_VERTICALLY   
 
  47 gyCONSTRAINT_CENTRED_HORIZONTALLY 
= CONSTRAINT_CENTRED_HORIZONTALLY 
 
  48 gyCONSTRAINT_CENTRED_BOTH         
= CONSTRAINT_CENTRED_BOTH         
 
  49 gyCONSTRAINT_LEFT_OF              
= CONSTRAINT_LEFT_OF              
 
  50 gyCONSTRAINT_RIGHT_OF             
= CONSTRAINT_RIGHT_OF             
 
  51 gyCONSTRAINT_ABOVE                
= CONSTRAINT_ABOVE                
 
  52 gyCONSTRAINT_BELOW                
= CONSTRAINT_BELOW                
 
  53 gyCONSTRAINT_ALIGNED_TOP          
= CONSTRAINT_ALIGNED_TOP          
 
  54 gyCONSTRAINT_ALIGNED_BOTTOM       
= CONSTRAINT_ALIGNED_BOTTOM       
 
  55 gyCONSTRAINT_ALIGNED_LEFT         
= CONSTRAINT_ALIGNED_LEFT         
 
  56 gyCONSTRAINT_ALIGNED_RIGHT        
= CONSTRAINT_ALIGNED_RIGHT        
 
  57 gyCONSTRAINT_MIDALIGNED_TOP       
= CONSTRAINT_MIDALIGNED_TOP       
 
  58 gyCONSTRAINT_MIDALIGNED_BOTTOM    
= CONSTRAINT_MIDALIGNED_BOTTOM    
 
  59 gyCONSTRAINT_MIDALIGNED_LEFT      
= CONSTRAINT_MIDALIGNED_LEFT      
 
  60 gyCONSTRAINT_MIDALIGNED_RIGHT     
= CONSTRAINT_MIDALIGNED_RIGHT     
 
  64 class ConstraintType(object): 
  65     def __init__(self
, theType
, theName
, thePhrase
): 
  68         self
._phrase 
= thePhrase
 
  73     [CONSTRAINT_CENTRED_VERTICALLY
, 
  74         ConstraintType(CONSTRAINT_CENTRED_VERTICALLY
, "Centre vertically", "centred vertically w.r.t.")], 
  76     [CONSTRAINT_CENTRED_HORIZONTALLY
, 
  77         ConstraintType(CONSTRAINT_CENTRED_HORIZONTALLY
, "Centre horizontally", "centred horizontally w.r.t.")], 
  79     [CONSTRAINT_CENTRED_BOTH
, 
  80         ConstraintType(CONSTRAINT_CENTRED_BOTH
, "Centre", "centred w.r.t.")], 
  83         ConstraintType(CONSTRAINT_LEFT_OF
, "Left of", "left of")], 
  86         ConstraintType(CONSTRAINT_RIGHT_OF
, "Right of", "right of")], 
  89         ConstraintType(CONSTRAINT_ABOVE
, "Above", "above")], 
  92         ConstraintType(CONSTRAINT_BELOW
, "Below", "below")], 
  95     [CONSTRAINT_ALIGNED_TOP
, 
  96         ConstraintType(CONSTRAINT_ALIGNED_TOP
, "Top-aligned", "aligned to the top of")], 
  98     [CONSTRAINT_ALIGNED_BOTTOM
, 
  99         ConstraintType(CONSTRAINT_ALIGNED_BOTTOM
, "Bottom-aligned", "aligned to the bottom of")], 
 101     [CONSTRAINT_ALIGNED_LEFT
, 
 102         ConstraintType(CONSTRAINT_ALIGNED_LEFT
, "Left-aligned", "aligned to the left of")], 
 104     [CONSTRAINT_ALIGNED_RIGHT
, 
 105         ConstraintType(CONSTRAINT_ALIGNED_RIGHT
, "Right-aligned", "aligned to the right of")], 
 108     [CONSTRAINT_MIDALIGNED_TOP
, 
 109         ConstraintType(CONSTRAINT_MIDALIGNED_TOP
, "Top-midaligned", "centred on the top of")], 
 111     [CONSTRAINT_MIDALIGNED_BOTTOM
, 
 112         ConstraintType(CONSTRAINT_MIDALIGNED_BOTTOM
, "Bottom-midaligned", "centred on the bottom of")], 
 114     [CONSTRAINT_MIDALIGNED_LEFT
, 
 115         ConstraintType(CONSTRAINT_MIDALIGNED_LEFT
, "Left-midaligned", "centred on the left of")], 
 117     [CONSTRAINT_MIDALIGNED_RIGHT
, 
 118         ConstraintType(CONSTRAINT_MIDALIGNED_RIGHT
, "Right-midaligned", "centred on the right of")] 
 124 class Constraint(object): 
 125     """A Constraint object helps specify how child shapes are laid out with 
 126     respect to siblings and parents. 
 131     def __init__(self
, type, constraining
, constrained
): 
 135         self
._constraintType 
= type 
 136         self
._constraintingObject 
= constraining
 
 138         self
._constraintId 
= 0 
 139         self
._constraintName 
= "noname" 
 141         self
._constrainedObjects 
= constrained
[:] 
 144         return "<%s.%s>" % (self
.__class
__.__module
__, self
.__class
__.__name
__) 
 146     def SetSpacing(self
, x
, y
): 
 147         """Sets the horizontal and vertical spacing for the constraint.""" 
 151     def Equals(self
, a
, b
): 
 152         """Return TRUE if x and y are approximately equal (for the purposes 
 153         of evaluating the constraint). 
 157         return b 
<= a 
+ marg 
and b 
>= a 
- marg
 
 160         """Evaluate this constraint and return TRUE if anything changed.""" 
 161         maxWidth
, maxHeight 
= self
._constraintingObject
.GetBoundingBoxMax() 
 162         minWidth
, minHeight 
= self
._constraintingObject
.GetBoundingBoxMin() 
 163         x 
= self
._constraintingObject
.GetX() 
 164         y 
= self
._constraintingObject
.GetY() 
 166         dc 
= wx
.ClientDC(self
._constraintingObject
.GetCanvas()) 
 167         self
._constraintingObject
.GetCanvas().PrepareDC(dc
) 
 169         if self
._constraintType 
== CONSTRAINT_CENTRED_VERTICALLY
: 
 170             n 
= len(self
._constrainedObjects
) 
 171             totalObjectHeight 
= 0.0 
 172             for constrainedObject 
in self
._constrainedObjects
: 
 173                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 174                 totalObjectHeight 
+= height2
 
 176             # Check if within the constraining object... 
 177             if totalObjectHeight 
+ (n 
+ 1) * self
._ySpacing 
<= minHeight
: 
 178                 spacingY 
= (minHeight 
- totalObjectHeight
) / (n 
+ 1.0) 
 179                 startY 
= y 
- minHeight 
/ 2.0 
 180             else: # Otherwise, use default spacing 
 181                 spacingY 
= self
._ySpacing
 
 182                 startY 
= y 
- (totalObjectHeight 
+ (n 
+ 1) * spacingY
) / 2.0 
 184             # Now position the objects 
 186             for constrainedObject 
in self
._constrainedObjects
: 
 187                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 188                 startY 
+= spacingY 
+ height2 
/ 2.0 
 189                 if not self
.Equals(startY
, constrainedObject
.GetY()): 
 190                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), startY
, False) 
 192                 startY 
+= height2 
/ 2.0 
 194         elif self
._constraintType 
== CONSTRAINT_CENTRED_HORIZONTALLY
: 
 195             n 
= len(self
._constrainedObjects
) 
 196             totalObjectWidth 
= 0.0 
 197             for constrainedObject 
in self
._constrainedObjects
: 
 198                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 199                 totalObjectWidth 
+= width2
 
 201             # Check if within the constraining object... 
 202             if totalObjectWidth 
+ (n 
+ 1) * self
._xSpacing 
<= minWidth
: 
 203                 spacingX 
= (minWidth 
- totalObjectWidth
) / (n 
+ 1.0) 
 204                 startX 
= x 
- minWidth 
/ 2.0 
 205             else: # Otherwise, use default spacing 
 206                 spacingX 
= self
._xSpacing
 
 207                 startX 
= x 
- (totalObjectWidth 
+ (n 
+ 1) * spacingX
) / 2.0 
 209             # Now position the objects 
 211             for constrainedObject 
in self
._constrainedObjects
: 
 212                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 213                 startX 
+= spacingX 
+ width2 
/ 2.0 
 214                 if not self
.Equals(startX
, constrainedObject
.GetX()): 
 215                     constrainedObject
.Move(dc
, startX
, constrainedObject
.GetY(), False) 
 217                 startX 
+= width2 
/ 2.0 
 219         elif self
._constraintType 
== CONSTRAINT_CENTRED_BOTH
: 
 220             n 
= len(self
._constrainedObjects
) 
 221             totalObjectWidth 
= 0.0 
 222             totalObjectHeight 
= 0.0 
 224             for constrainedObject 
in self
._constrainedObjects
: 
 225                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 226                 totalObjectWidth 
+= width2
 
 227                 totalObjectHeight 
+= height2
 
 229             # Check if within the constraining object... 
 230             if totalObjectHeight 
+ (n 
+ 1) * self
._xSpacing 
<= minWidth
: 
 231                 spacingX 
= (minWidth 
- totalObjectWidth
) / (n 
+ 1.0) 
 232                 startX 
= x 
- minWidth 
/ 2.0 
 233             else: # Otherwise, use default spacing 
 234                 spacingX 
= self
._xSpacing
 
 235                 startX 
= x 
- (totalObjectWidth 
+ (n 
+ 1) * spacingX
) / 2.0 
 237             # Check if within the constraining object... 
 238             if totalObjectHeight 
+ (n 
+ 1) * self
._ySpacing 
<= minHeight
: 
 239                 spacingY 
= (minHeight 
- totalObjectHeight
) / (n 
+ 1.0) 
 240                 startY 
= y 
- minHeight 
/ 2.0 
 241             else: # Otherwise, use default spacing 
 242                 spacingY 
= self
._ySpacing
 
 243                 startY 
= y 
- (totalObjectHeight 
+ (n 
+ 1) * spacingY
) / 2.0 
 245             # Now position the objects 
 247             for constrainedObject 
in self
._constrainedObjects
: 
 248                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 249                 startX 
+= spacingX 
+ width2 
/ 2.0 
 250                 startY 
+= spacingY 
+ height2 
/ 2.0 
 252                 if not self
.Equals(startX
, constrainedObject
.GetX()) or not self
.Equals(startY
, constrainedObject
.GetY()): 
 253                     constrainedObject
.Move(dc
, startX
, startY
, False) 
 256                 startX 
+= width2 
/ 2.0 
 257                 startY 
+= height2 
/ 2.0 
 259         elif self
._constraintType 
== CONSTRAINT_LEFT_OF
: 
 261             for constrainedObject 
in self
._constrainedObjects
: 
 262                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 264                 x3 
= x 
- minWidth 
/ 2.0 - width2 
/ 2.0 - self
._xSpacing
 
 265                 if not self
.Equals(x3
, constrainedObject
.GetX()): 
 267                     constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False) 
 269         elif self
._constraintType 
== CONSTRAINT_RIGHT_OF
: 
 272             for constrainedObject 
in self
._constrainedObjects
: 
 273                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 274                 x3 
= x 
+ minWidth 
/ 2.0 + width2 
/ 2.0 + self
._xSpacing
 
 275                 if not self
.Equals(x3
, constrainedObject
.GetX()): 
 276                     constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False) 
 279         elif self
._constraintType 
== CONSTRAINT_ABOVE
: 
 282             for constrainedObject 
in self
._constrainedObjects
: 
 283                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 285                 y3 
= y 
- minHeight 
/ 2.0 - height2 
/ 2.0 - self
._ySpacing
 
 286                 if not self
.Equals(y3
, constrainedObject
.GetY()): 
 288                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False) 
 290         elif self
._constraintType 
== CONSTRAINT_BELOW
: 
 293             for constrainedObject 
in self
._constrainedObjects
: 
 294                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 296                 y3 
= y 
+ minHeight 
/ 2.0 + height2 
/ 2.0 + self
._ySpacing
 
 297                 if not self
.Equals(y3
, constrainedObject
.GetY()): 
 299                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False) 
 301         elif self
._constraintType 
== CONSTRAINT_ALIGNED_LEFT
: 
 303             for constrainedObject 
in self
._constrainedObjects
: 
 304                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 305                 x3 
= x 
- minWidth 
/ 2.0 + width2 
/ 2.0 + self
._xSpacing
 
 306                 if not self
.Equals(x3
, constrainedObject
.GetX()): 
 308                     constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False) 
 310         elif self
._constraintType 
== CONSTRAINT_ALIGNED_RIGHT
: 
 312             for constrainedObject 
in self
._constrainedObjects
: 
 313                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 314                 x3 
= x 
+ minWidth 
/ 2.0 - width2 
/ 2.0 - self
._xSpacing
 
 315                 if not self
.Equals(x3
, constrainedObject
.GetX()): 
 317                     constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False) 
 319         elif self
._constraintType 
== CONSTRAINT_ALIGNED_TOP
: 
 321             for constrainedObject 
in self
._constrainedObjects
: 
 322                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 323                 y3 
= y 
-  minHeight 
/ 2.0 + height2 
/ 2.0 + self
._ySpacing
 
 324                 if not self
.Equals(y3
, constrainedObject
.GetY()): 
 326                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False) 
 328         elif self
._constraintType 
== CONSTRAINT_ALIGNED_BOTTOM
: 
 330             for constrainedObject 
in self
._constrainedObjects
: 
 331                 width2
, height2 
= constrainedObject
.GetBoundingBoxMax() 
 332                 y3 
= y 
+ minHeight 
/ 2.0 - height2 
/ 2.0 - self
._ySpacing
 
 333                 if not self
.Equals(y3
, constrainedObject
.GetY()): 
 335                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False) 
 337         elif self
._constraintType 
== CONSTRAINT_MIDALIGNED_LEFT
: 
 339             for constrainedObject 
in self
._constrainedObjects
: 
 340                 x3 
= x 
- minWidth 
/ 2.0 
 341                 if not self
.Equals(x3
, constrainedObject
.GetX()): 
 343                     constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False) 
 345         elif self
._constraintType 
== CONSTRAINT_MIDALIGNED_RIGHT
: 
 347             for constrainedObject 
in self
._constrainedObjects
: 
 348                 x3 
= x 
+ minWidth 
/ 2.0 
 349                 if not self
.Equals(x3
, constrainedObject
.GetX()): 
 351                     constrainedObject
.Move(dc
, x3
, constrainedObject
.GetY(), False) 
 353         elif self
._constraintType 
== CONSTRAINT_MIDALIGNED_TOP
: 
 355             for constrainedObject 
in self
._constrainedObjects
: 
 356                 y3 
= y 
- minHeight 
/ 2.0 
 357                 if not self
.Equals(y3
, constrainedObject
.GetY()): 
 359                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False) 
 361         elif self
._constraintType 
== CONSTRAINT_MIDALIGNED_BOTTOM
: 
 363             for constrainedObject 
in self
._constrainedObjects
: 
 364                 y3 
= y 
+ minHeight 
/ 2.0 
 365                 if not self
.Equals(y3
, constrainedObject
.GetY()): 
 367                     constrainedObject
.Move(dc
, constrainedObject
.GetX(), y3
, False) 
 372 OGLConstraint 
= wx
._core
._deprecated
(Constraint
, 
 373                      "The OGLConstraint name is deprecated, use `ogl.Constraint` instead.") 
 376 class CompositeShape(RectangleShape
): 
 377     """This is an object with a list of child objects, and a list of size 
 378     and positioning constraints between the children. 
 384         RectangleShape
.__init
__(self
, 100.0, 100.0) 
 386         self
._oldX 
= self
._xpos
 
 387         self
._oldY 
= self
._ypos
 
 389         self
._constraints 
= []  
 390         self
._divisions 
= [] # In case it's a container 
 392     def OnDraw(self
, dc
): 
 393         x1 
= self
._xpos 
- self
._width 
/ 2.0 
 394         y1 
= self
._ypos 
- self
._height 
/ 2.0 
 396         if self
._shadowMode 
!= SHADOW_NONE
: 
 397             if self
._shadowBrush
: 
 398                 dc
.SetBrush(self
._shadowBrush
) 
 399             dc
.SetPen(wx
.Pen(wx
.WHITE
, 1, wx
.TRANSPARENT
)) 
 401             if self
._cornerRadius
: 
 402                 dc
.DrawRoundedRectangle(x1 
+ self
._shadowOffsetX
, y1 
+ self
._shadowOffsetY
, self
._width
, self
._height
, self
._cornerRadius
) 
 404                 dc
.DrawRectangle(x1 
+ self
._shadowOffsetX
, y1 
+ self
._shadowOffsetY
, self
._width
, self
._height
) 
 406         # For debug purposes /pi 
 407         #dc.DrawRectangle(x1, y1, self._width, self._height) 
 409     def OnDrawContents(self
, dc
): 
 410         for object in self
._children
: 
 414         Shape
.OnDrawContents(self
, dc
) 
 416     def OnMovePre(self
, dc
, x
, y
, old_x
, old_y
, display 
= True): 
 420         for object in self
._children
: 
 422             object.Move(dc
, object.GetX() + diffX
, object.GetY() + diffY
, display
) 
 426     def OnErase(self
, dc
): 
 427         RectangleShape
.OnErase(self
, dc
) 
 428         for object in self
._children
: 
 431     def OnDragLeft(self
, draw
, x
, y
, keys 
= 0, attachment 
= 0): 
 432         xx
, yy 
= self
._canvas
.Snap(x
, y
) 
 433         offsetX 
= xx 
- _objectStartX
 
 434         offsetY 
= yy 
- _objectStartY
 
 436         dc 
= wx
.ClientDC(self
.GetCanvas()) 
 437         self
.GetCanvas().PrepareDC(dc
) 
 439         dc
.SetLogicalFunction(OGLRBLF
) 
 440         dottedPen 
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
) 
 442         dc
.SetBrush(wx
.TRANSPARENT_BRUSH
) 
 444         self
.GetEventHandler().OnDrawOutline(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
, self
.GetWidth(), self
.GetHeight()) 
 446     def OnBeginDragLeft(self
, x
, y
, keys 
= 0, attachment 
= 0): 
 447         global _objectStartX
, _objectStartY
 
 452         dc 
= wx
.ClientDC(self
.GetCanvas()) 
 453         self
.GetCanvas().PrepareDC(dc
) 
 457         dc
.SetLogicalFunction(OGLRBLF
) 
 458         dottedPen 
= wx
.Pen(wx
.Colour(0, 0, 0), 1, wx
.DOT
) 
 460         dc
.SetBrush(wx
.TRANSPARENT_BRUSH
) 
 461         self
._canvas
.CaptureMouse() 
 463         xx
, yy 
= self
._canvas
.Snap(x
, y
) 
 464         offsetX 
= xx 
- _objectStartX
 
 465         offsetY 
= yy 
- _objectStartY
 
 467         self
.GetEventHandler().OnDrawOutline(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
, self
.GetWidth(), self
.GetHeight()) 
 469     def OnEndDragLeft(self
, x
, y
, keys 
= 0, attachment 
= 0): 
 470         dc 
= wx
.ClientDC(self
.GetCanvas()) 
 471         self
.GetCanvas().PrepareDC(dc
) 
 473         if self
._canvas
.HasCapture(): 
 474             self
._canvas
.ReleaseMouse() 
 476         if not self
._draggable
: 
 478                 self
._parent
.GetEventHandler().OnEndDragLeft(x
, y
, keys
, 0) 
 483         dc
.SetLogicalFunction(wx
.COPY
) 
 485         xx
, yy 
= self
._canvas
.Snap(x
, y
) 
 486         offsetX 
= xx 
- _objectStartX
 
 487         offsetY 
= yy 
- _objectStartY
 
 489         self
.Move(dc
, self
.GetX() + offsetX
, self
.GetY() + offsetY
) 
 491         if self
._canvas 
and not self
._canvas
.GetQuickEditMode(): 
 492             self
._canvas
.Redraw(dc
) 
 494     def OnRightClick(self
, x
, y
, keys 
= 0, attachment 
= 0): 
 495         # If we get a ctrl-right click, this means send the message to 
 496         # the division, so we can invoke a user interface for dealing 
 499             for division 
in self
._divisions
: 
 500                 hit 
= division
.HitTest(x
, y
) 
 502                     division
.GetEventHandler().OnRightClick(x
, y
, keys
, hit
[0]) 
 505     def SetSize(self
, w
, h
, recursive 
= True): 
 506         self
.SetAttachmentSize(w
, h
) 
 508         xScale 
= float(w
) / max(1, self
.GetWidth()) 
 509         yScale 
= float(h
) / max(1, self
.GetHeight()) 
 517         dc 
= wx
.ClientDC(self
.GetCanvas()) 
 518         self
.GetCanvas().PrepareDC(dc
) 
 520         for object in self
._children
: 
 521             # Scale the position first 
 522             newX 
= (object.GetX() - self
.GetX()) * xScale 
+ self
.GetX() 
 523             newY 
= (object.GetY() - self
.GetY()) * yScale 
+ self
.GetY() 
 525             object.Move(dc
, newX
, newY
) 
 528             # Now set the scaled size 
 529             xbound
, ybound 
= object.GetBoundingBoxMax() 
 530             if not object.GetFixedWidth(): 
 532             if not object.GetFixedHeight(): 
 534             object.SetSize(xbound
, ybound
) 
 536         self
.SetDefaultRegionSize() 
 538     def AddChild(self
, child
, addAfter 
= None): 
 539         """Adds a child shape to the composite. 
 541         If addAfter is not None, the shape will be added after this shape. 
 543         self
._children
.append(child
) 
 544         child
.SetParent(self
) 
 546             # Ensure we add at the right position 
 548                 child
.RemoveFromCanvas(self
._canvas
) 
 549             child
.AddToCanvas(self
._canvas
, addAfter
) 
 551     def RemoveChild(self
, child
): 
 552         """Removes the child from the composite and any constraint 
 553         relationships, but does not delete the child. 
 555         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
))