]> git.saurik.com Git - wxWidgets.git/commitdiff
Since everything in the submodules is to appear in the pacakge
authorRobin Dunn <robin@alldunn.com>
Fri, 4 Jun 2004 20:12:01 +0000 (20:12 +0000)
committerRobin Dunn <robin@alldunn.com>
Fri, 4 Jun 2004 20:12:01 +0000 (20:12 +0000)
namespace rename the submodule to have a leading underscore to make it
easier to document it that way.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@27634 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

17 files changed:
wxPython/wx/lib/ogl/__init__.py
wxPython/wx/lib/ogl/_basic.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_bmpshape.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_canvas.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_composit.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_diagram.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_divided.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_lines.py [new file with mode: 0644]
wxPython/wx/lib/ogl/_oglmisc.py [new file with mode: 0644]
wxPython/wx/lib/ogl/basic.py [deleted file]
wxPython/wx/lib/ogl/bmpshape.py [deleted file]
wxPython/wx/lib/ogl/canvas.py [deleted file]
wxPython/wx/lib/ogl/composit.py [deleted file]
wxPython/wx/lib/ogl/diagram.py [deleted file]
wxPython/wx/lib/ogl/divided.py [deleted file]
wxPython/wx/lib/ogl/lines.py [deleted file]
wxPython/wx/lib/ogl/oglmisc.py [deleted file]

index 667ed0afe15b5b8aee1d9b3c39bdae4ade170f84..c295f493238b82da6eb34f028b4284c53c5f9d80 100644 (file)
@@ -3,12 +3,20 @@ The Object Graphics Library provides for simple drawing and manipulation
 of 2D objects.
 """
 
 of 2D objects.
 """
 
-__all__ = ["basic", "diagram", "canvas", "lines", "bmpshape", "divided", "composit"]
-
-from basic import *
-from diagram import *
-from canvas import *
-from lines import *
-from bmpshape import *
-from divided import *
-from composit import *
+from _basic import *
+from _diagram import *
+from _canvas import *
+from _lines import *
+from _bmpshape import *
+from _divided import *
+from _composit import *
+
+
+
+# Set things up for documenting with epydoc.  The __docfilter__ will
+# prevent some things from beign documented, and anything in __all__
+# will appear to actually exist in this module.
+import wx._core as _wx
+__docfilter__ = _wx.__DocFilter(globals())
+__all__ = [name for name in dir() if not name.startswith('_')]
+
diff --git a/wxPython/wx/lib/ogl/_basic.py b/wxPython/wx/lib/ogl/_basic.py
new file mode 100644 (file)
index 0000000..5b6fca3
--- /dev/null
@@ -0,0 +1,3173 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         basic.py
+# Purpose:      The basic OGL shapes
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+import wx
+import math
+
+from _oglmisc import *
+
+DragOffsetX = 0.0
+DragOffsetY = 0.0
+
+
+def OGLInitialize():
+    global WhiteBackgroundPen, WhiteBackgroundBrush, TransparentPen
+    global BlackForegroundPen, NormalFont
+    
+    WhiteBackgroundPen = wx.Pen(wx.WHITE, 1, wx.SOLID)
+    WhiteBackgroundBrush = wx.Brush(wx.WHITE, wx.SOLID)
+
+    TransparentPen = wx.Pen(wx.WHITE, 1, wx.TRANSPARENT)
+    BlackForegroundPen = wx.Pen(wx.BLACK, 1, wx.SOLID)
+
+    NormalFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
+
+
+def OGLCleanUp():
+    pass
+
+
+class ShapeTextLine(object):
+    def __init__(self, the_x, the_y, the_line):
+        self._x = the_x
+        self._y = the_y
+        self._line = the_line
+
+    def GetX(self):
+        return self._x
+
+    def GetY(self):
+        return self._y
+
+    def SetX(self, x):
+        self._x = x
+
+    def SetY(self, y):
+        self._y = y
+
+    def SetText(self, text):
+        self._line = text
+
+    def GetText(self):
+        return self._line
+
+
+    
+class ShapeEvtHandler(object):
+    def __init__(self, prev = None, shape = None):
+        self._previousHandler = prev
+        self._handlerShape = shape
+
+    def __del__(self):
+        pass
+
+    def SetShape(self, sh):
+        self._handlerShape = sh
+
+    def GetShape(self):
+        return self._handlerShape
+
+    def SetPreviousHandler(self, handler):
+        self._previousHandler = handler
+
+    def GetPreviousHandler(self):
+        return self._previousHandler
+    
+    def OnDraw(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnDraw(dc)
+
+    def OnMoveLinks(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnMoveLinks(dc)
+            
+    def OnMoveLink(self, dc, moveControlPoints = True):
+        if self._previousHandler:
+            self._previousHandler.OnMoveLink(dc, moveControlPoints)
+            
+    def OnDrawContents(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnDrawContents(dc)
+            
+    def OnDrawBranches(self, dc, erase = False):
+        if self._previousHandler:
+            self._previousHandler.OnDrawBranches(dc, erase = erase)
+
+    def OnSize(self, x, y):
+        if self._previousHandler:
+            self._previousHandler.OnSize(x, y)
+            
+    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
+        if self._previousHandler:
+            return self._previousHandler.OnMovePre(dc, x, y, old_x, old_y, display)
+        else:
+            return True
+
+    def OnMovePost(self, dc, x, y, old_x, old_y, display = True):
+        if self._previousHandler:
+            return self._previousHandler.OnMovePost(dc, x, y, old_x, old_y, display)
+        else:
+            return True
+
+    def OnErase(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnErase(dc)
+
+    def OnEraseContents(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnEraseContents(dc)
+
+    def OnHighlight(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnHighlight(dc)
+
+    def OnLeftClick(self, x, y, keys, attachment):
+        if self._previousHandler:
+            self._previousHandler.OnLeftClick(x, y, keys, attachment)
+            
+    def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnLeftDoubleClick(x, y, keys, attachment)
+
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnRightClick(x, y, keys, attachment)
+
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnDragLeft(draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
+        
+    def OnDragRight(self, draw, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnDragRight(draw, x, y, keys, attachment)
+
+    def OnBeginDragRight(self, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnBeginDragRight(x, y, keys, attachment)
+
+    def OnEndDragRight(self, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnEndDragRight(x, y, keys, attachment)
+
+    # Control points ('handles') redirect control to the actual shape,
+    # to make it easier to override sizing behaviour.
+    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnSizingDragLeft(pt, draw, x, y, keys, attachment)
+
+    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnSizingBeginDragLeft(pt, x, y, keys, attachment)
+            
+    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        if self._previousHandler:
+            self._previousHandler.OnSizingEndDragLeft(pt, x, y, keys, attachment)
+
+    def OnBeginSize(self, w, h):
+        pass
+    
+    def OnEndSize(self, w, h):
+        pass
+    
+    def OnDrawOutline(self, dc, x, y, w, h):
+        if self._previousHandler:
+            self._previousHandler.OnDrawOutline(dc, x, y, w, h)
+
+    def OnDrawControlPoints(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnDrawControlPoints(dc)
+
+    def OnEraseControlPoints(self, dc):
+        if self._previousHandler:
+            self._previousHandler.OnEraseControlPoints(dc)
+
+    # Can override this to prevent or intercept line reordering.
+    def OnChangeAttachment(self, attachment, line, ordering):
+        if self._previousHandler:
+            self._previousHandler.OnChangeAttachment(attachment, line, ordering)
+
+
+            
+class Shape(ShapeEvtHandler):
+    """OGL base class
+
+    Shape(canvas = None)
+    
+    The wxShape is the top-level, abstract object that all other objects
+    are derived from. All common functionality is represented by wxShape's
+    members, and overriden members that appear in derived classes and have
+    behaviour as documented for wxShape, are not documented separately.
+    """
+    
+    GraphicsInSizeToContents = False
+    
+    def __init__(self, canvas = None):
+        ShapeEvtHandler.__init__(self)
+        
+        self._eventHandler = self
+        self.SetShape(self)
+        self._id = 0
+        self._formatted = False
+        self._canvas = canvas
+        self._xpos = 0.0
+        self._ypos = 0.0
+        self._pen = wx.Pen(wx.BLACK, 1, wx.SOLID)
+        self._brush = wx.WHITE_BRUSH
+        self._font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
+        self._textColour = wx.BLACK
+        self._textColourName = wx.BLACK
+        self._visible = False
+        self._selected = False
+        self._attachmentMode = ATTACHMENT_MODE_NONE
+        self._spaceAttachments = True
+        self._disableLabel = False
+        self._fixedWidth = False
+        self._fixedHeight = False
+        self._drawHandles = True
+        self._sensitivity = OP_ALL
+        self._draggable = True
+        self._parent = None
+        self._formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT
+        self._shadowMode = SHADOW_NONE
+        self._shadowOffsetX = 6
+        self._shadowOffsetY = 6
+        self._shadowBrush = wx.BLACK_BRUSH
+        self._textMarginX = 5
+        self._textMarginY = 5
+        self._regionName="0"
+        self._centreResize = True
+        self._maintainAspectRatio = False
+        self._highlighted = False
+        self._rotation = 0.0
+        self._branchNeckLength = 10
+        self._branchStemLength = 10
+        self._branchSpacing = 10
+        self._branchStyle = BRANCHING_ATTACHMENT_NORMAL
+        
+        self._regions = []
+        self._lines = []
+        self._controlPoints = []
+        self._attachmentPoints = []
+        self._text = []
+        self._children = []
+        
+        # Set up a default region. Much of the above will be put into
+        # the region eventually (the duplication is for compatibility)
+        region = ShapeRegion()
+        region.SetName("0")
+        region.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))
+        region.SetFormatMode(FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT)
+        region.SetColour("BLACK")
+        self._regions.append(region)
+
+    def __str__(self):
+        return "<%s.%s>" % (self.__class__.__module__, self.__class__.__name__)
+
+    def GetClassName(self):
+        return str(self.__class__).split(".")[-1][:-2]
+    
+    def __del__(self):
+        if self._parent:
+            i = self._parent.GetChildren().index(self)
+            self._parent.GetChildren(i).remove(self)
+
+        self.ClearText()
+        self.ClearRegions()
+        self.ClearAttachments()
+
+        if self._canvas:
+            self._canvas.RemoveShape(self)
+
+        self.GetEventHandler().OnDelete()
+
+    def Draggable(self):
+        """TRUE if the shape may be dragged by the user."""
+        return True
+    
+    def SetShape(self, sh):
+        self._handlerShape = sh
+
+    def GetCanvas(self):
+        """Get the internal canvas."""
+        return self._canvas
+
+    def GetBranchStyle(self):
+        return self._branchStyle
+
+    def GetRotation(self):
+        """Return the angle of rotation in radians."""
+        return self._rotation
+
+    def SetRotation(self, rotation):
+        self._rotation = rotation
+        
+    def SetHighlight(self, hi, recurse = False):
+        """Set the highlight for a shape. Shape highlighting is unimplemented."""
+        self._highlighted = hi
+        if recurse:
+            for shape in self._children:
+                shape.SetHighlight(hi, recurse)
+
+    def SetSensitivityFilter(self, sens = OP_ALL, recursive = False):
+        """Set the shape to be sensitive or insensitive to specific mouse
+        operations.
+
+        sens is a bitlist of the following:
+
+        * OP_CLICK_LEFT
+        * OP_CLICK_RIGHT
+        * OP_DRAG_LEFT
+        * OP_DRAG_RIGHT
+        * OP_ALL (equivalent to a combination of all the above).
+        """
+        self._draggable = sens & OP_DRAG_LEFT
+
+        self._sensitivity = sens
+        if recursive:
+            for shape in self._children:
+                shape.SetSensitivityFilter(sens, True)
+
+    def SetDraggable(self, drag, recursive = False):
+        """Set the shape to be draggable or not draggable."""
+        self._draggable = drag
+        if drag:
+            self._sensitivity |= OP_DRAG_LEFT
+        elif self._sensitivity & OP_DRAG_LEFT:
+            self._sensitivity -= OP_DRAG_LEFT
+
+        if recursive:
+            for shape in self._children:
+                shape.SetDraggable(drag, True)
+
+    def SetDrawHandles(self, drawH):
+        """Set the drawHandles flag for this shape and all descendants.
+        If drawH is TRUE (the default), any handles (control points) will
+        be drawn. Otherwise, the handles will not be drawn.
+        """
+        self._drawHandles = drawH
+        for shape in self._children:
+            shape.SetDrawHandles(drawH)
+
+    def SetShadowMode(self, mode, redraw = False):
+        """Set the shadow mode (whether a shadow is drawn or not).
+        mode can be one of the following:
+
+        SHADOW_NONE
+          No shadow (the default). 
+        SHADOW_LEFT
+          Shadow on the left side. 
+        SHADOW_RIGHT
+          Shadow on the right side.
+        """
+        if redraw and self.GetCanvas():
+            dc = wx.ClientDC(self.GetCanvas())
+            self.GetCanvas().PrepareDC(dc)
+            self.Erase(dc)
+            self._shadowMode = mode
+            self.Draw(dc)
+        else:
+            self._shadowMode = mode
+
+    def SetCanvas(self, theCanvas):
+        """Identical to Shape.Attach."""
+        self._canvas = theCanvas
+        for shape in self._children:
+            shape.SetCanvas(theCanvas)
+
+    def AddToCanvas(self, theCanvas, addAfter = None):
+        """Add the shape to the canvas's shape list.
+        If addAfter is non-NULL, will add the shape after this one.
+        """
+        theCanvas.AddShape(self, addAfter)
+
+        lastImage = self
+        for object in self._children:
+            object.AddToCanvas(theCanvas, lastImage)
+            lastImage = object
+
+    def InsertInCanvas(self, theCanvas):
+        """Insert the shape at the front of the shape list of canvas."""
+        theCanvas.InsertShape(self)
+
+        lastImage = self
+        for object in self._children:
+            object.AddToCanvas(theCanvas, lastImage)
+            lastImage = object
+
+    def RemoveFromCanvas(self, theCanvas):
+        """Remove the shape from the canvas."""
+        if self.Selected():
+            self.Select(False)
+        theCanvas.RemoveShape(self)
+        for object in self._children:
+            object.RemoveFromCanvas(theCanvas)
+
+    def ClearAttachments(self):
+        """Clear internal custom attachment point shapes (of class
+        wxAttachmentPoint).
+        """
+        self._attachmentPoints = []
+
+    def ClearText(self, regionId = 0):
+        """Clear the text from the specified text region."""
+        if regionId == 0:
+            self._text=""
+        if regionId<len(self._regions):
+            self._regions[regionId].ClearText()
+            
+    def ClearRegions(self):
+        """Clear the ShapeRegions from the shape."""
+        self._regions = []
+            
+    def AddRegion(self, region):
+        """Add a region to the shape."""
+        self._regions.append(region)
+
+    def SetDefaultRegionSize(self):
+        """Set the default region to be consistent with the shape size."""
+        if not self._regions:
+            return
+        w, h = self.GetBoundingBoxMax()
+        self._regions[0].SetSize(w, h)
+
+    def HitTest(self, x, y):
+        """Given a point on a canvas, returns TRUE if the point was on the
+        shape, and returns the nearest attachment point and distance from
+        the given point and target.
+        """
+        width, height = self.GetBoundingBoxMax()
+        if abs(width)<4:
+            width = 4.0
+        if abs(height)<4:
+            height = 4.0
+
+        width += 4 # Allowance for inaccurate mousing
+        height += 4
+        
+        left = self._xpos - (width / 2)
+        top = self._ypos - (height / 2)
+        right = self._xpos + (width / 2)
+        bottom = self._ypos + (height / 2)
+
+        nearest_attachment = 0
+
+        # If within the bounding box, check the attachment points
+        # within the object.
+        if x >= left and x <= right and y >= top and y <= bottom:
+            n = self.GetNumberOfAttachments()
+            nearest = 999999
+
+            # GetAttachmentPosition[Edge] takes a logical attachment position,
+            # i.e. if it's rotated through 90%, position 0 is East-facing.
+
+            for i in range(n):
+                e = self.GetAttachmentPositionEdge(i)
+                if e:
+                    xp, yp = e
+                    l = math.sqrt(((xp - x) * (xp - x)) + (yp - y) * (yp - y))
+                    if l<nearest:
+                        nearest = l
+                        nearest_attachment = i
+
+            return nearest_attachment, nearest
+        return False
+    
+    # Format a text string according to the region size, adding
+    # strings with positions to region text list
+    
+    def FormatText(self, dc, s, i = 0):
+        """Reformat the given text region; defaults to formatting the
+        default region.
+        """
+        self.ClearText(i)
+
+        if not self._regions:
+            return
+
+        if i>len(self._regions):
+            return
+
+        region = self._regions[i]
+        region._regionText = s
+        dc.SetFont(region.GetFont())
+
+        w, h = region.GetSize()
+
+        stringList = FormatText(dc, s, (w - 2 * self._textMarginX), (h - 2 * self._textMarginY), region.GetFormatMode())
+        for s in stringList:
+            line = ShapeTextLine(0.0, 0.0, s)
+            region.GetFormattedText().append(line)
+
+        actualW = w
+        actualH = h
+        # Don't try to resize an object with more than one image (this
+        # case should be dealt with by overriden handlers)
+        if (region.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS) and \
+           region.GetFormattedText().GetCount() and \
+           len(self._regions) == 1 and \
+           not Shape.GraphicsInSizeToContents:
+
+            actualW, actualH = GetCentredTextExtent(dc, region.GetFormattedText())
+            if actualW + 2 * self._textMarginX != w or actualH + 2 * self._textMarginY != h:
+                # If we are a descendant of a composite, must make sure
+                # the composite gets resized properly
+
+                topAncestor = self.GetTopAncestor()
+                if topAncestor != self:
+                    Shape.GraphicsInSizeToContents = True
+
+                    composite = topAncestor
+                    composite.Erase(dc)
+                    self.SetSize(actualW + 2 * self._textMarginX, actualH + 2 * self._textMarginY)
+                    self.Move(dc, self._xpos, self._ypos)
+                    composite.CalculateSize()
+                    if composite.Selected():
+                        composite.DeleteControlPoints(dc)
+                        composite.MakeControlPoints()
+                        composite.MakeMandatoryControlPoints()
+                    # Where infinite recursion might happen if we didn't stop it
+                    composite.Draw(dc)
+                    Shape.GraphicsInSizeToContents = False
+                else:
+                    self.Erase(dc)
+                    
+                self.SetSize(actualW + 2 * self._textMarginX, actualH + 2 * self._textMarginY)
+                self.Move(dc, self._xpos, self._ypos)
+                self.EraseContents(dc)
+        CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, actualW - 2 * self._textMarginX, actualH - 2 * self._textMarginY, region.GetFormatMode())
+        self._formatted = True
+
+    def Recentre(self, dc):
+        """Do recentring (or other formatting) for all the text regions
+        for this shape.
+        """
+        w, h = self.GetBoundingBoxMin()
+        for region in self._regions:
+            CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, w - 2 * self._textMarginX, h - 2 * self._textMarginY, region.GetFormatMode())
+
+    def GetPerimeterPoint(self, x1, y1, x2, y2):
+        """Get the point at which the line from (x1, y1) to (x2, y2) hits
+        the shape. Returns False if the line doesn't hit the perimeter.
+        """
+        return False
+
+    def SetPen(self, the_pen):
+        """Set the pen for drawing the shape's outline."""
+        self._pen = the_pen
+
+    def SetBrush(self, the_brush):
+        """Set the brush for filling the shape's shape."""
+        self._brush = the_brush
+
+    # Get the top - most (non-division) ancestor, or self
+    def GetTopAncestor(self):
+        """Return the top-most ancestor of this shape (the root of
+        the composite).
+        """
+        if not self.GetParent():
+            return self
+
+        if isinstance(self.GetParent(), DivisionShape):
+            return self
+        return self.GetParent().GetTopAncestor()
+
+    # Region functions
+    def SetFont(self, the_font, regionId = 0):
+        """Set the font for the specified text region."""
+        self._font = the_font
+        if regionId<len(self._regions):
+            self._regions[regionId].SetFont(the_font)
+
+    def GetFont(self, regionId = 0):
+        """Get the font for the specified text region."""
+        if regionId >= len(self._regions):
+            return None
+        return self._regions[regionId].GetFont()
+
+    def SetFormatMode(self, mode, regionId = 0):
+        """Set the format mode of the default text region. The argument
+        can be a bit list of the following:
+
+        FORMAT_NONE
+          No formatting. 
+        FORMAT_CENTRE_HORIZ
+          Horizontal centring. 
+        FORMAT_CENTRE_VERT
+          Vertical centring.
+        """
+        if regionId<len(self._regions):
+            self._regions[regionId].SetFormatMode(mode)
+
+    def GetFormatMode(self, regionId = 0):
+        if regionId >= len(self._regions):
+            return 0
+        return self._regions[regionId].GetFormatMode()
+
+    def SetTextColour(self, the_colour, regionId = 0):
+        """Set the colour for the specified text region."""
+        self._textColour = wx.TheColourDatabase.Find(the_colour)
+        self._textColourName = the_colour
+
+        if regionId<len(self._regions):
+            self._regions[regionId].SetColour(the_colour)
+            
+    def GetTextColour(self, regionId = 0):
+        """Get the colour for the specified text region."""
+        if regionId >= len(self._regions):
+            return ""
+        return self._regions[regionId].GetTextColour()
+
+    def SetRegionName(self, name, regionId = 0):
+        """Set the name for this region.
+        The name for a region is unique within the scope of the whole
+        composite, whereas a region id is unique only for a single image.
+        """
+        if regionId<len(self._regions):
+            self._regions[regionId].SetName(name)
+
+    def GetRegionName(self, regionId = 0):
+        """Get the region's name.
+        A region's name can be used to uniquely determine a region within
+        an entire composite image hierarchy. See also Shape.SetRegionName.
+        """
+        if regionId >= len(self._regions):
+            return ""
+        return self._regions[regionId].GetName()
+
+    def GetRegionId(self, name):
+        """Get the region's identifier by name.
+        This is not unique for within an entire composite, but is unique
+        for the image.
+        """
+        for i, r in enumerate(self._regions):
+            if r.GetName() == name:
+                return i
+        return -1
+
+    # Name all _regions in all subimages recursively
+    def NameRegions(self, parentName=""):
+        """Make unique names for all the regions in a shape or composite shape."""
+        n = self.GetNumberOfTextRegions()
+        for i in range(n):
+            if parentName:
+                buff = parentName+"."+str(i)
+            else:
+                buff = str(i)
+            self.SetRegionName(buff, i)
+
+        for j, child in enumerate(self._children):
+            if parentName:
+                buff = parentName+"."+str(j)
+            else:
+                buff = str(j)
+            child.NameRegions(buff)
+
+    # Get a region by name, possibly looking recursively into composites
+    def FindRegion(self, name):
+        """Find the actual image ('this' if non-composite) and region id
+        for the given region name.
+        """
+        id = self.GetRegionId(name)
+        if id>-1:
+            return self, id
+
+        for child in self._children:
+            actualImage, regionId = child.FindRegion(name)
+            if actualImage:
+                return actualImage, regionId
+
+        return None,-1
+
+    # Finds all region names for this image (composite or simple).
+    def FindRegionNames(self):
+        """Get a list of all region names for this image (composite or simple)."""
+        list = []
+        n = self.GetNumberOfTextRegions()
+        for i in range(n):
+            list.append(self.GetRegionName(i))
+
+        for child in self._children:
+            list += child.FindRegionNames()
+
+        return list
+
+    def AssignNewIds(self):
+        """Assign new ids to this image and its children."""
+        self._id = wx.NewId()
+        for child in self._children:
+            child.AssignNewIds()
+
+    def OnDraw(self, dc):
+        pass
+
+    def OnMoveLinks(self, dc):
+        # Want to set the ends of all attached links
+        # to point to / from this object
+
+        for line in self._lines:
+            line.GetEventHandler().OnMoveLink(dc)
+
+    def OnDrawContents(self, dc):
+        if not self._regions:
+            return
+        
+        bound_x, bound_y = self.GetBoundingBoxMin()
+
+        if self._pen:
+            dc.SetPen(self._pen)
+
+        region = self._regions[0]
+        if region.GetFont():
+            dc.SetFont(region.GetFont())
+
+        dc.SetTextForeground(region.GetActualColourObject())
+        dc.SetBackgroundMode(wx.TRANSPARENT)
+        if not self._formatted:
+            CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, bound_x - 2 * self._textMarginX, bound_y - 2 * self._textMarginY, region.GetFormatMode())
+            self._formatted = True
+
+        if not self.GetDisableLabel():
+            DrawFormattedText(dc, region.GetFormattedText(), self._xpos, self._ypos, bound_x - 2 * self._textMarginX, bound_y - 2 * self._textMarginY, region.GetFormatMode())
+            
+
+    def DrawContents(self, dc):
+        """Draw the internal graphic of the shape (such as text).
+
+        Do not override this function: override OnDrawContents, which
+        is called by this function.
+        """
+        self.GetEventHandler().OnDrawContents(dc)
+
+    def OnSize(self, x, y):
+        pass
+
+    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
+        return True
+
+    def OnErase(self, dc):
+        if not self._visible:
+            return
+
+        # Erase links
+        for line in self._lines:
+            line.GetEventHandler().OnErase(dc)
+
+        self.GetEventHandler().OnEraseContents(dc)
+
+    def OnEraseContents(self, dc):
+        if not self._visible:
+            return
+
+        xp, yp = self.GetX(), self.GetY()
+        minX, minY = self.GetBoundingBoxMin()
+        maxX, maxY = self.GetBoundingBoxMax()
+
+        topLeftX = xp - maxX / 2 - 2
+        topLeftY = yp - maxY / 2 - 2
+
+        penWidth = 0
+        if self._pen:
+            penWidth = self._pen.GetWidth()
+
+        dc.SetPen(self.GetBackgroundPen())
+        dc.SetBrush(self.GetBackgroundBrush())
+
+        dc.DrawRectangle(topLeftX - penWidth, topLeftY - penWidth, maxX + penWidth * 2 + 4, maxY + penWidth * 2 + 4)
+
+    def EraseLinks(self, dc, attachment=-1, recurse = False):
+        """Erase links attached to this shape, but do not repair damage
+        caused to other shapes.
+        """
+        if not self._visible:
+            return
+
+        for line in self._lines:
+            if attachment==-1 or (line.GetTo() == self and line.GetAttachmentTo() == attachment or line.GetFrom() == self and line.GetAttachmentFrom() == attachment):
+                line.GetEventHandler().OnErase(dc)
+
+        if recurse:
+            for child in self._children:
+                child.EraseLinks(dc, attachment, recurse)
+
+    def DrawLinks(self, dc, attachment=-1, recurse = False):
+        """Draws any lines linked to this shape."""
+        if not self._visible:
+            return
+
+        for line in self._lines:
+            if attachment==-1 or (line.GetTo() == self and line.GetAttachmentTo() == attachment or line.GetFrom() == self and line.GetAttachmentFrom() == attachment):
+                line.GetEventHandler().Draw(dc)
+                
+        if recurse:
+            for child in self._children:
+                child.DrawLinks(dc, attachment, recurse)
+
+    #  Returns TRUE if pt1 <= pt2 in the sense that one point comes before
+    #  another on an edge of the shape.
+    # attachmentPoint is the attachment point (= side) in question.
+
+    # This is the default, rectangular implementation.
+    def AttachmentSortTest(self, attachmentPoint, pt1, pt2):
+        """Return TRUE if pt1 is less than or equal to pt2, in the sense
+        that one point comes before another on an edge of the shape.
+
+        attachment is the attachment point (side) in question.
+
+        This function is used in Shape.MoveLineToNewAttachment to determine
+        the new line ordering.
+        """
+        physicalAttachment = self.LogicalToPhysicalAttachment(attachmentPoint)
+        if physicalAttachment in [0, 2]:
+            return pt1.x <= pt2.x
+        elif physicalAttachment in [1, 3]:
+            return pt1.y <= pt2.y
+
+        return False
+
+    def MoveLineToNewAttachment(self, dc, to_move, x, y):
+        """Move the given line (which must already be attached to the shape)
+        to a different attachment point on the shape, or a different order
+        on the same attachment.
+
+        Calls Shape.AttachmentSortTest and then
+        ShapeEvtHandler.OnChangeAttachment.
+        """
+        if self.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
+            return False
+
+        # Is (x, y) on this object? If so, find the new attachment point
+        # the user has moved the point to
+        hit = self.HitTest(x, y)
+        if not hit:
+            return False
+
+        newAttachment, distance = hit
+        
+        self.EraseLinks(dc)
+
+        if to_move.GetTo() == self:
+            oldAttachment = to_move.GetAttachmentTo()
+        else:
+            oldAttachment = to_move.GetAttachmentFrom()
+
+        # The links in a new ordering
+        # First, add all links to the new list
+        newOrdering = self._lines[:]
+        
+        # Delete the line object from the list of links; we're going to move
+        # it to another position in the list
+        del newOrdering[newOrdering.index(to_move)]
+
+        old_x=-99999.9
+        old_y=-99999.9
+
+        found = False
+
+        for line in newOrdering:
+            if line.GetTo() == self and oldAttachment == line.GetAttachmentTo() or \
+               line.GetFrom() == self and oldAttachment == line.GetAttachmentFrom():
+                startX, startY, endX, endY = line.GetEnds()
+                if line.GetTo() == self:
+                    xp = endX
+                    yp = endY
+                else:
+                    xp = startX
+                    yp = startY
+
+                thisPoint = wx.RealPoint(xp, yp)
+                lastPoint = wx.RealPoint(old_x, old_y)
+                newPoint = wx.RealPoint(x, y)
+
+                if self.AttachmentSortTest(newAttachment, newPoint, thisPoint) and self.AttachmentSortTest(newAttachment, lastPoint, newPoint):
+                    found = True
+                    newOrdering.insert(newOrdering.index(line), to_move)
+
+                old_x = xp
+                old_y = yp
+            if found:
+                break
+
+        if not found:
+            newOrdering.append(to_move)
+
+        self.GetEventHandler().OnChangeAttachment(newAttachment, to_move, newOrdering)
+        return True
+
+    def OnChangeAttachment(self, attachment, line, ordering):
+        if line.GetTo() == self:
+            line.SetAttachmentTo(attachment)
+        else:
+            line.SetAttachmentFrom(attachment)
+
+        self.ApplyAttachmentOrdering(ordering)
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+        self.MoveLinks(dc)
+
+        if not self.GetCanvas().GetQuickEditMode():
+            self.GetCanvas().Redraw(dc)
+
+    # Reorders the lines according to the given list
+    def ApplyAttachmentOrdering(self, linesToSort):
+        """Apply the line ordering in linesToSort to the shape, to reorder
+        the way lines are attached.
+        """
+        linesStore = self._lines[:]
+
+        self._lines = []
+
+        for line in linesToSort:
+            if line in linesStore:
+                del linesStore[linesStore.index(line)]
+                self._lines.append(line)
+
+        # Now add any lines that haven't been listed in linesToSort
+        self._lines += linesStore
+
+    def SortLines(self, attachment, linesToSort):
+        """ Reorder the lines coming into the node image at this attachment
+        position, in the order in which they appear in linesToSort.
+
+        Any remaining lines not in the list will be added to the end.
+        """
+        # This is a temporary store of all the lines at this attachment
+        # point. We'll tick them off as we've processed them.
+        linesAtThisAttachment = []
+
+        for line in self._lines[:]:
+            if line.GetTo() == self and line.GetAttachmentTo() == attachment or \
+               line.GetFrom() == self and line.GetAttachmentFrom() == attachment:
+                linesAtThisAttachment.append(line)
+                del self._lines[self._lines.index(line)]
+
+        for line in linesToSort:
+            if line in linesAtThisAttachment:
+                # Done this one
+                del linesAtThisAttachment[linesAtThisAttachment.index(line)]
+                self._lines.Append(line)
+
+        # Now add any lines that haven't been listed in linesToSort
+        self._lines += linesAtThisAttachment
+
+    def OnHighlight(self, dc):
+        pass
+
+    def OnLeftClick(self, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_CLICK_LEFT != OP_CLICK_LEFT:
+            if self._parent:
+                attachment, dist = self._parent.HitTest(x, y)
+                self._parent.GetEventHandler().OnLeftClick(x, y, keys, attachment)
+
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_CLICK_RIGHT != OP_CLICK_RIGHT:
+            attachment, dist = self._parent.HitTest(x, y)
+            self._parent.GetEventHandler().OnRightClick(x, y, keys, attachment)
+            
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+                self._parent.GetEventHandler().OnDragLeft(draw, x, y, keys, attachment)
+            return
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+        dc.SetLogicalFunction(OGLRBLF)
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        xx = x + DragOffsetX
+        yy = y + DragOffsetY
+
+        xx, yy = self._canvas.Snap(xx, yy)
+        w, h = self.GetBoundingBoxMax()
+        self.GetEventHandler().OnDrawOutline(dc, xx, yy, w, h)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        global DragOffsetX, DragOffsetY
+        
+        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+                self._parent.GetEventHandler().OnBeginDragLeft(x, y, keys, attachment)
+            return
+
+        DragOffsetX = self._xpos - x
+        DragOffsetY = self._ypos - y
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+        
+        # New policy: don't erase shape until end of drag.
+        # self.Erase(dc)
+        xx = x + DragOffsetX
+        yy = y + DragOffsetY
+        xx, yy = self._canvas.Snap(xx, yy)
+        dc.SetLogicalFunction(OGLRBLF)
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        w, h = self.GetBoundingBoxMax()
+        self.GetEventHandler().OnDrawOutline(dc, xx, yy, w, h)
+        self._canvas.CaptureMouse()
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        if self._canvas.HasCapture():
+            self._canvas.ReleaseMouse()
+        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+            self._parent.GetEventHandler().OnEndDragLeft(x, y, keys, attachment)
+            return
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(wx.COPY)
+        xx = x + DragOffsetX
+        yy = y + DragOffsetY
+        xx, yy = self._canvas.Snap(xx, yy)
+
+        # New policy: erase shape at end of drag.
+        self.Erase(dc)
+
+        self.Move(dc, xx, yy)
+        if self._canvas and not self._canvas.GetQuickEditMode():
+            self._canvas.Redraw(dc)
+
+    def OnDragRight(self, draw, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_DRAG_RIGHT != OP_DRAG_RIGHT:
+            if self._parent:
+                attachment, dist = self._parent.HitTest(x, y)
+                self._parent.GetEventHandler().OnDragRight(draw, x, y, keys, attachment)
+            return
+
+    def OnBeginDragRight(self, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_DRAG_RIGHT != OP_DRAG_RIGHT:
+            if self._parent:
+                attachment, dist = self._parent.HitTest(x, y)
+                self._parent.GetEventHandler().OnBeginDragRight(x, y, keys, attachment)
+            return
+
+    def OnEndDragRight(self, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_DRAG_RIGHT != OP_DRAG_RIGHT:
+            if self._parent:
+                attachment, dist = self._parent.HitTest(x, y)
+                self._parent.GetEventHandler().OnEndDragRight(x, y, keys, attachment)
+            return
+
+    def OnDrawOutline(self, dc, x, y, w, h):
+        points = [[x - w / 2, y - h / 2],
+                [x + w / 2, y - h / 2],
+                [x + w / 2, y + h / 2],
+                [x - w / 2, y + h / 2],
+                [x - w / 2, y - h / 2],
+                ]
+
+        #dc.DrawLines([[round(x), round(y)] for x, y in points])
+        dc.DrawLines(points)
+        
+    def Attach(self, can):
+        """Set the shape's internal canvas pointer to point to the given canvas."""
+        self._canvas = can
+
+    def Detach(self):
+        """Disassociates the shape from its canvas."""
+        self._canvas = None
+
+    def Move(self, dc, x, y, display = True):
+        """Move the shape to the given position.
+        Redraw if display is TRUE.
+        """
+        old_x = self._xpos
+        old_y = self._ypos
+
+        if not self.GetEventHandler().OnMovePre(dc, x, y, old_x, old_y, display):
+            return
+
+        self._xpos, self._ypos = x, y
+
+        self.ResetControlPoints()
+
+        if display:
+            self.Draw(dc)
+
+        self.MoveLinks(dc)
+
+        self.GetEventHandler().OnMovePost(dc, x, y, old_x, old_y, display)
+
+    def MoveLinks(self, dc):
+        """Redraw all the lines attached to the shape."""
+        self.GetEventHandler().OnMoveLinks(dc)
+
+    def Draw(self, dc):
+        """Draw the whole shape and any lines attached to it.
+
+        Do not override this function: override OnDraw, which is called
+        by this function.
+        """
+        if self._visible:
+            self.GetEventHandler().OnDraw(dc)
+            self.GetEventHandler().OnDrawContents(dc)
+            self.GetEventHandler().OnDrawControlPoints(dc)
+            self.GetEventHandler().OnDrawBranches(dc)
+
+    def Flash(self):
+        """Flash the shape."""
+        if self.GetCanvas():
+            dc = wx.ClientDC(self.GetCanvas())
+            self.GetCanvas.PrepareDC(dc)
+
+            dc.SetLogicalFunction(OGLRBLF)
+            self.Draw(dc)
+            dc.SetLogicalFunction(wx.COPY)
+            self.Draw(dc)
+
+    def Show(self, show):
+        """Set a flag indicating whether the shape should be drawn."""
+        self._visible = show
+        for child in self._children:
+            child.Show(show)
+
+    def Erase(self, dc):
+        """Erase the shape.
+        Does not repair damage caused to other shapes.
+        """
+        self.GetEventHandler().OnErase(dc)
+        self.GetEventHandler().OnEraseControlPoints(dc)
+        self.GetEventHandler().OnDrawBranches(dc, erase = True)
+
+    def EraseContents(self, dc):
+        """Erase the shape contents, that is, the area within the shape's
+        minimum bounding box.
+        """
+        self.GetEventHandler().OnEraseContents(dc)
+
+    def AddText(self, string):
+        """Add a line of text to the shape's default text region."""
+        if not self._regions:
+            return
+
+        region = self._regions[0]
+        region.ClearText()
+        new_line = ShapeTextLine(0, 0, string)
+        text = region.GetFormattedText()
+        text.append(new_line)
+
+        self._formatted = False
+
+    def SetSize(self, x, y, recursive = True):
+        """Set the shape's size."""
+        self.SetAttachmentSize(x, y)
+        self.SetDefaultRegionSize()
+
+    def SetAttachmentSize(self, w, h):
+        width, height = self.GetBoundingBoxMin()
+        if width == 0:
+            scaleX = 1.0
+        else:
+            scaleX = long(w) / width
+        if height == 0:
+            scaleY = 1.0
+        else:
+            scaleY = long(h) / height
+
+        for point in self._attachmentPoints:
+            point._x = point._x * scaleX
+            point._y = point._y * scaleY
+
+    # Add line FROM this object
+    def AddLine(self, line, other, attachFrom = 0, attachTo = 0, positionFrom=-1, positionTo=-1):
+        """Add a line between this shape and the given other shape, at the
+        specified attachment points.
+
+        The position in the list of lines at each end can also be specified,
+        so that the line will be drawn at a particular point on its attachment
+        point.
+        """
+        if positionFrom==-1:
+            if not line in self._lines:
+                self._lines.append(line)
+        else:
+            # Don't preserve old ordering if we have new ordering instructions
+            try:
+                self._lines.remove(line)
+            except ValueError:
+                pass
+            if positionFrom<len(self._lines):
+                self._lines.insert(positionFrom, line)
+            else:
+                self._lines.append(line)
+
+        if positionTo==-1:
+            if not other in other._lines:
+                other._lines.append(line)
+        else:
+            # Don't preserve old ordering if we have new ordering instructions
+            try:
+                other._lines.remove(line)
+            except ValueError:
+                pass
+            if positionTo<len(other._lines):
+                other._lines.insert(positionTo, line)
+            else:
+                other._lines.append(line)
+            
+        line.SetFrom(self)
+        line.SetTo(other)
+        line.SetAttachments(attachFrom, attachTo)
+
+        dc = wx.ClientDC(self._canvas)
+        self._canvas.PrepareDC(dc)
+        self.MoveLinks(dc)
+        
+    def RemoveLine(self, line):
+        """Remove the given line from the shape's list of attached lines."""
+        if line.GetFrom() == self:
+            line.GetTo()._lines.remove(line)
+        else:
+            line.GetFrom()._lines.remove(line)
+
+        self._lines.remove(line)
+
+    # Default - make 6 control points
+    def MakeControlPoints(self):
+        """Make a list of control points (draggable handles) appropriate to
+        the shape.
+        """
+        maxX, maxY = self.GetBoundingBoxMax()
+        minX, minY = self.GetBoundingBoxMin()
+
+        widthMin = minX + CONTROL_POINT_SIZE + 2
+        heightMin = minY + CONTROL_POINT_SIZE + 2
+
+        # Offsets from main object
+        top=-heightMin / 2
+        bottom = heightMin / 2 + (maxY - minY)
+        left=-widthMin / 2
+        right = widthMin / 2 + (maxX - minX)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, top, CONTROL_POINT_DIAGONAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, 0, top, CONTROL_POINT_VERTICAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, top, CONTROL_POINT_DIAGONAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, 0, CONTROL_POINT_HORIZONTAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, bottom, CONTROL_POINT_DIAGONAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, 0, bottom, CONTROL_POINT_VERTICAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, bottom, CONTROL_POINT_DIAGONAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, 0, CONTROL_POINT_HORIZONTAL)
+        self._canvas.AddShape(control)
+        self._controlPoints.append(control)
+
+    def MakeMandatoryControlPoints(self):
+        """Make the mandatory control points.
+
+        For example, the control point on a dividing line should appear even
+        if the divided rectangle shape's handles should not appear (because
+        it is the child of a composite, and children are not resizable).
+        """
+        for child in self._children:
+            child.MakeMandatoryControlPoints()
+
+    def ResetMandatoryControlPoints(self):
+        """Reset the mandatory control points."""
+        for child in self._children:
+            child.ResetMandatoryControlPoints()
+
+    def ResetControlPoints(self):
+        """Reset the positions of the control points (for instance when the
+        shape's shape has changed).
+        """
+        self.ResetMandatoryControlPoints()
+
+        if len(self._controlPoints) == 0:
+            return
+
+        maxX, maxY = self.GetBoundingBoxMax()
+        minX, minY = self.GetBoundingBoxMin()
+
+        widthMin = minX + CONTROL_POINT_SIZE + 2
+        heightMin = minY + CONTROL_POINT_SIZE + 2
+        
+        # Offsets from main object
+        top=-heightMin / 2
+        bottom = heightMin / 2 + (maxY - minY)
+        left=-widthMin / 2
+        right = widthMin / 2 + (maxX - minX)
+
+        self._controlPoints[0]._xoffset = left
+        self._controlPoints[0]._yoffset = top
+
+        self._controlPoints[1]._xoffset = 0
+        self._controlPoints[1]._yoffset = top
+
+        self._controlPoints[2]._xoffset = right
+        self._controlPoints[2]._yoffset = top
+
+        self._controlPoints[3]._xoffset = right
+        self._controlPoints[3]._yoffset = 0
+
+        self._controlPoints[4]._xoffset = right
+        self._controlPoints[4]._yoffset = bottom
+
+        self._controlPoints[5]._xoffset = 0
+        self._controlPoints[5]._yoffset = bottom
+
+        self._controlPoints[6]._xoffset = left
+        self._controlPoints[6]._yoffset = bottom
+
+        self._controlPoints[7]._xoffset = left
+        self._controlPoints[7]._yoffset = 0
+
+    def DeleteControlPoints(self, dc = None):
+        """Delete the control points (or handles) for the shape.
+
+        Does not redraw the shape.
+        """
+        for control in self._controlPoints:
+            if dc:
+                control.GetEventHandler().OnErase(dc)
+            self._canvas.RemoveShape(control)
+            del control
+        self._controlPoints = []
+
+        # Children of divisions are contained objects,
+        # so stop here
+        if not isinstance(self, DivisionShape):
+            for child in self._children:
+                child.DeleteControlPoints(dc)
+
+    def OnDrawControlPoints(self, dc):
+        if not self._drawHandles:
+            return
+
+        dc.SetBrush(wx.BLACK_BRUSH)
+        dc.SetPen(wx.BLACK_PEN)
+
+        for control in self._controlPoints:
+            control.Draw(dc)
+
+        # Children of divisions are contained objects,
+        # so stop here.
+        # This test bypasses the type facility for speed
+        # (critical when drawing)
+
+        if not isinstance(self, DivisionShape):
+            for child in self._children:
+                child.GetEventHandler().OnDrawControlPoints(dc)
+
+    def OnEraseControlPoints(self, dc):
+        for control in self._controlPoints:
+            control.Erase(dc)
+
+        if not isinstance(self, DivisionShape):
+            for child in self._children:
+                child.GetEventHandler().OnEraseControlPoints(dc)
+
+    def Select(self, select, dc = None):
+        """Select or deselect the given shape, drawing or erasing control points
+        (handles) as necessary.
+        """
+        self._selected = select
+        if select:
+            self.MakeControlPoints()
+            # Children of divisions are contained objects,
+            # so stop here
+            if not isinstance(self, DivisionShape):
+                for child in self._children:
+                    child.MakeMandatoryControlPoints()
+            if dc:
+                self.GetEventHandler().OnDrawControlPoints(dc)
+        else:
+            self.DeleteControlPoints(dc)
+            if not isinstance(self, DivisionShape):
+                for child in self._children:
+                    child.DeleteControlPoints(dc)
+
+    def Selected(self):
+        """TRUE if the shape is currently selected."""
+        return self._selected
+
+    def AncestorSelected(self):
+        """TRUE if the shape's ancestor is currently selected."""
+        if self._selected:
+            return True
+        if not self.GetParent():
+            return False
+        return self.GetParent().AncestorSelected()
+
+    def GetNumberOfAttachments(self):
+        """Get the number of attachment points for this shape."""
+        # Should return the MAXIMUM attachment point id here,
+        # so higher-level functions can iterate through all attachments,
+        # even if they're not contiguous.
+
+        if len(self._attachmentPoints) == 0:
+            return 4
+        else:
+            maxN = 3
+            for point in self._attachmentPoints:
+                if point._id>maxN:
+                    maxN = point._id
+            return maxN + 1
+
+    def AttachmentIsValid(self, attachment):
+        """TRUE if attachment is a valid attachment point."""
+        if len(self._attachmentPoints) == 0:
+            return attachment in range(4)
+
+        for point in self._attachmentPoints:
+            if point._id == attachment:
+                return True
+        return False
+
+    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
+        """Get the position at which the given attachment point should be drawn.
+
+        If attachment isn't found among the attachment points of the shape,
+        returns None.
+        """
+        if self._attachmentMode == ATTACHMENT_MODE_NONE:
+            return self._xpos, self._ypos
+        elif self._attachmentMode == ATTACHMENT_MODE_BRANCHING:
+            pt, stemPt = self.GetBranchingAttachmentPoint(attachment, nth)
+            return pt.x, pt.y
+        elif self._attachmentMode == ATTACHMENT_MODE_EDGE:
+            if len(self._attachmentPoints):
+                for point in self._attachmentPoints:
+                    if point._id == attachment:
+                        return self._xpos + point._x, self._ypos + point._y
+                return None
+            else:
+                # Assume is rectangular
+                w, h = self.GetBoundingBoxMax()
+                top = self._ypos + h / 2
+                bottom = self._ypos - h / 2
+                left = self._xpos - w / 2
+                right = self._xpos + w / 2
+
+                # wtf?
+                line and line.IsEnd(self)
+
+                physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
+
+                # Simplified code
+                if physicalAttachment == 0:
+                    pt = self.CalcSimpleAttachment((left, bottom), (right, bottom), nth, no_arcs, line)
+                elif physicalAttachment == 1:
+                    pt = self.CalcSimpleAttachment((right, bottom), (right, top), nth, no_arcs, line)
+                elif physicalAttachment == 2:
+                    pt = self.CalcSimpleAttachment((left, top), (right, top), nth, no_arcs, line)
+                elif physicalAttachment == 3:
+                    pt = self.CalcSimpleAttachment((left, bottom), (left, top), nth, no_arcs, line)
+                else:
+                    return None
+                return pt[0], pt[1]
+        return None
+
+    def GetBoundingBoxMax(self):
+        """Get the maximum bounding box for the shape, taking into account
+        external features such as shadows.
+        """
+        ww, hh = self.GetBoundingBoxMin()
+        if self._shadowMode != SHADOW_NONE:
+            ww += self._shadowOffsetX
+            hh += self._shadowOffsetY
+        return ww, hh
+
+    def GetBoundingBoxMin(self):
+        """Get the minimum bounding box for the shape, that defines the area
+        available for drawing the contents (such as text).
+
+        Must be overridden.
+        """
+        return 0, 0
+    
+    def HasDescendant(self, image):
+        """TRUE if image is a descendant of this composite."""
+        if image == self:
+            return True
+        for child in self._children:
+            if child.HasDescendant(image):
+                return True
+        return False
+
+    # Clears points from a list of wxRealPoints, and clears list
+    # Useless in python? /pi
+    def ClearPointList(self, list):
+        list = []
+
+    # Assuming the attachment lies along a vertical or horizontal line,
+    # calculate the position on that point.
+    def CalcSimpleAttachment(self, pt1, pt2, nth, noArcs, line):
+        """Assuming the attachment lies along a vertical or horizontal line,
+        calculate the position on that point.
+
+        Parameters:
+
+        pt1
+            The first point of the line repesenting the edge of the shape.
+
+        pt2
+            The second point of the line representing the edge of the shape.
+
+        nth
+            The position on the edge (for example there may be 6 lines at
+            this attachment point, and this may be the 2nd line.
+
+        noArcs
+            The number of lines at this edge.
+
+        line
+            The line shape.
+
+        Remarks
+
+        This function expects the line to be either vertical or horizontal,
+        and determines which.
+        """
+        isEnd = line and line.IsEnd(self)
+
+        # Are we horizontal or vertical?
+        isHorizontal = RoughlyEqual(pt1[1], pt2[1])
+
+        if isHorizontal:
+            if pt1[0]>pt2[0]:
+                firstPoint = pt2
+                secondPoint = pt1
+            else:
+                firstPoint = pt1
+                secondPoint = pt2
+
+            if self._spaceAttachments:
+                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                    # Align line according to the next handle along
+                    point = line.GetNextControlPoint(self)
+                    if point[0]<firstPoint[0]:
+                        x = firstPoint[0]
+                    elif point[0]>secondPoint[0]:
+                        x = secondPoint[0]
+                    else:
+                        x = point[0]
+                else:
+                    x = firstPoint[0] + (nth + 1) * (secondPoint[0] - firstPoint[0]) / (noArcs + 1)
+            else:
+                x = (secondPoint[0] - firstPoint[0]) / 2 # Midpoint
+            y = pt1[1]
+        else:
+            assert RoughlyEqual(pt1[0], pt2[0])
+
+            if pt1[1]>pt2[1]:
+                firstPoint = pt2
+                secondPoint = pt1
+            else:
+                firstPoint = pt1
+                secondPoint = pt2
+
+            if self._spaceAttachments:
+                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                    # Align line according to the next handle along
+                    point = line.GetNextControlPoint(self)
+                    if point[1]<firstPoint[1]:
+                        y = firstPoint[1]
+                    elif point[1]>secondPoint[1]:
+                        y = secondPoint[1]
+                    else:
+                        y = point[1]
+                else:
+                    y = firstPoint[1] + (nth + 1) * (secondPoint[1] - firstPoint[1]) / (noArcs + 1)
+            else:
+                y = (secondPoint[1] - firstPoint[1]) / 2 # Midpoint
+            x = pt1[0]
+
+        return x, y
+
+    # Return the zero-based position in m_lines of line
+    def GetLinePosition(self, line):
+        """Get the zero-based position of line in the list of lines
+        for this shape.
+        """
+        try:
+            return self._lines.index(line)
+        except:
+            return 0
+
+
+    #            |________|
+    #                 | <- root
+    #                 | <- neck
+    # shoulder1 ->---------<- shoulder2
+    #             | | | | |
+    #                      <- branching attachment point N-1
+
+    def GetBranchingAttachmentInfo(self, attachment):
+        """Get information about where branching connections go.
+        
+        Returns FALSE if there are no lines at this attachment.
+        """
+        physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
+
+        # Number of lines at this attachment
+        lineCount = self.GetAttachmentLineCount(attachment)
+
+        if not lineCount:
+            return False
+
+        totalBranchLength = self._branchSpacing * (lineCount - 1)
+        root = self.GetBranchingAttachmentRoot(attachment)
+
+        neck = wx.RealPoint()
+        shoulder1 = wx.RealPoint()
+        shoulder2 = wx.RealPoint()
+        
+        # Assume that we have attachment points 0 to 3: top, right, bottom, left
+        if physicalAttachment == 0:
+            neck.x = self.GetX()
+            neck.y = root.y - self._branchNeckLength
+
+            shoulder1.x = root.x - totalBranchLength / 2
+            shoulder2.x = root.x + totalBranchLength / 2
+
+            shoulder1.y = neck.y
+            shoulder2.y = neck.y
+        elif physicalAttachment == 1:
+            neck.x = root.x + self._branchNeckLength
+            neck.y = root.y
+            
+            shoulder1.x = neck.x
+            shoulder2.x = neck.x
+
+            shoulder1.y = neck.y - totalBranchLength / 2
+            shoulder1.y = neck.y + totalBranchLength / 2
+        elif physicalAttachment == 2:
+            neck.x = self.GetX()
+            neck.y = root.y + self._branchNeckLength
+
+            shoulder1.x = root.x - totalBranchLength / 2
+            shoulder2.x = root.x + totalBranchLength / 2
+
+            shoulder1.y = neck.y
+            shoulder2.y = neck.y
+        elif physicalAttachment == 3:
+            neck.x = root.x - self._branchNeckLength
+            neck.y = root.y
+
+            shoulder1.x = neck.x
+            shoulder2.x = neck.x
+
+            shoulder1.y = neck.y - totalBranchLength / 2
+            shoulder2.y = neck.y + totalBranchLength / 2
+        else:
+            raise "Unrecognised attachment point in GetBranchingAttachmentInfo"
+        return root, neck, shoulder1, shoulder2
+
+    def GetBranchingAttachmentPoint(self, attachment, n):
+        physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
+
+        root, neck, shoulder1, shoulder2 = self.GetBranchingAttachmentInfo(attachment)
+        pt = wx.RealPoint()
+        stemPt = wx.RealPoint()
+
+        if physicalAttachment == 0:
+            pt.y = neck.y - self._branchStemLength
+            pt.x = shoulder1.x + n * self._branchSpacing
+
+            stemPt.x = pt.x
+            stemPt.y = neck.y
+        elif physicalAttachment == 2:
+            pt.y = neck.y + self._branchStemLength
+            pt.x = shoulder1.x + n * self._branchStemLength
+            
+            stemPt.x = pt.x
+            stemPt.y = neck.y
+        elif physicalAttachment == 1:
+            pt.x = neck.x + self._branchStemLength
+            pt.y = shoulder1.y + n * self._branchSpacing
+
+            stemPt.x = neck.x
+            stemPt.y = pt.y
+        elif physicalAttachment == 3:
+            pt.x = neck.x - self._branchStemLength
+            pt.y = shoulder1.y + n * self._branchSpacing
+
+            stemPt.x = neck.x
+            stemPt.y = pt.y
+        else:
+            raise "Unrecognised attachment point in GetBranchingAttachmentPoint"
+
+        return pt, stemPt
+
+    def GetAttachmentLineCount(self, attachment):
+        """Get the number of lines at this attachment position."""
+        count = 0
+        for lineShape in self._lines:
+            if lineShape.GetFrom() == self and lineShape.GetAttachmentFrom() == attachment:
+                count += 1
+            elif lineShape.GetTo() == self and lineShape.GetAttachmentTo() == attachment:
+                count += 1
+        return count
+    
+    def GetBranchingAttachmentRoot(self, attachment):
+        """Get the root point at the given attachment."""
+        physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
+
+        root = wx.RealPoint()
+
+        width, height = self.GetBoundingBoxMax()
+
+        # Assume that we have attachment points 0 to 3: top, right, bottom, left
+        if physicalAttachment == 0:
+            root.x = self.GetX()
+            root.y = self.GetY() - height / 2
+        elif physicalAttachment == 1:
+            root.x = self.GetX() + width / 2
+            root.y = self.GetY()
+        elif physicalAttachment == 2:
+            root.x = self.GetX()
+            root.y = self.GetY() + height / 2
+        elif physicalAttachment == 3:
+            root.x = self.GetX() - width / 2
+            root.y = self.GetY()
+        else:
+            raise "Unrecognised attachment point in GetBranchingAttachmentRoot"
+
+        return root
+
+    # Draw or erase the branches (not the actual arcs though)
+    def OnDrawBranchesAttachment(self, dc, attachment, erase = False):
+        count = self.GetAttachmentLineCount(attachment)
+        if count == 0:
+            return
+
+        root, neck, shoulder1, shoulder2 = self.GetBranchingAttachmentInfo(attachment)
+
+        if erase:
+            dc.SetPen(wx.WHITE_PEN)
+            dc.SetBrush(wx.WHITE_BRUSH)
+        else:
+            dc.SetPen(wx.BLACK_PEN)
+            dc.SetBrush(wx.BLACK_BRUSH)
+
+        # Draw neck
+        dc.DrawLine(root, neck)
+
+        if count>1:
+            # Draw shoulder-to-shoulder line
+            dc.DrawLine(shoulder1, shoulder2)
+        # Draw all the little branches
+        for i in range(count):
+            pt, stemPt = self.GetBranchingAttachmentPoint(attachment, i)
+            dc.DrawLine(stemPt, pt)
+
+            if self.GetBranchStyle() & BRANCHING_ATTACHMENT_BLOB and count>1:
+                blobSize = 6
+                dc.DrawEllipse(stemPt.x - blobSize / 2, stemPt.y - blobSize / 2, blobSize, blobSize)
+
+    def OnDrawBranches(self, dc, erase = False):
+        if self._attachmentMode != ATTACHMENT_MODE_BRANCHING:
+            return
+        for i in range(self.GetNumberOfAttachments()):
+            self.OnDrawBranchesAttachment(dc, i, erase)
+
+    def GetAttachmentPositionEdge(self, attachment, nth = 0, no_arcs = 1, line = None):
+        """ Only get the attachment position at the _edge_ of the shape,
+        ignoring branching mode. This is used e.g. to indicate the edge of
+        interest, not the point on the attachment branch.
+        """
+        oldMode = self._attachmentMode
+
+        # Calculate as if to edge, not branch
+        if self._attachmentMode == ATTACHMENT_MODE_BRANCHING:
+            self._attachmentMode = ATTACHMENT_MODE_EDGE
+        res = self.GetAttachmentPosition(attachment, nth, no_arcs, line)
+        self._attachmentMode = oldMode
+
+        return res
+
+    def PhysicalToLogicalAttachment(self, physicalAttachment):
+        """ Rotate the standard attachment point from physical
+        (0 is always North) to logical (0 -> 1 if rotated by 90 degrees)
+        """
+        if RoughlyEqual(self.GetRotation(), 0):
+            i = physicalAttachment
+        elif RoughlyEqual(self.GetRotation(), math.pi / 2):
+            i = physicalAttachment - 1
+        elif RoughlyEqual(self.GetRotation(), math.pi):
+            i = physicalAttachment - 2
+        elif RoughlyEqual(self.GetRotation(), 3 * math.pi / 2):
+            i = physicalAttachment - 3
+        else:
+            # Can't handle -- assume the same
+            return physicalAttachment
+
+        if i<0:
+            i += 4
+
+        return i
+
+    def LogicalToPhysicalAttachment(self, logicalAttachment):
+        """Rotate the standard attachment point from logical
+        to physical (0 is always North).
+        """
+        if RoughlyEqual(self.GetRotation(), 0):
+            i = logicalAttachment
+        elif RoughlyEqual(self.GetRotation(), math.pi / 2):
+            i = logicalAttachment + 1
+        elif RoughlyEqual(self.GetRotation(), math.pi):
+            i = logicalAttachment + 2
+        elif RoughlyEqual(self.GetRotation(), 3 * math.pi / 2):
+            i = logicalAttachment + 3
+        else:
+            return logicalAttachment
+
+        if i>3:
+            i -= 4
+
+        return i
+
+    def Rotate(self, x, y, theta):
+        """Rotate about the given axis by the given amount in radians."""
+        self._rotation = theta
+        if self._rotation<0:
+            self._rotation += 2 * math.pi
+        elif self._rotation>2 * math.pi:
+            self._rotation -= 2 * math.pi
+
+    def GetBackgroundPen(self):
+        """Return pen of the right colour for the background."""
+        if self.GetCanvas():
+            return wx.Pen(self.GetCanvas().GetBackgroundColour(), 1, wx.SOLID)
+        return WhiteBackgroundPen
+
+    def GetBackgroundBrush(self):
+        """Return brush of the right colour for the background."""
+        if self.GetCanvas():
+            return wx.Brush(self.GetCanvas().GetBackgroundColour(), wx.SOLID)
+        return WhiteBackgroundBrush
+
+    def GetX(self):
+        """Get the x position of the centre of the shape."""
+        return self._xpos
+
+    def GetY(self):
+        """Get the y position of the centre of the shape."""
+        return self._ypos
+
+    def SetX(self, x):
+        """Set the x position of the shape."""
+        self._xpos = x
+
+    def SetY(self, y):
+        """Set the y position of the shape."""
+        self._ypos = y
+
+    def GetParent(self):
+        """Return the parent of this shape, if it is part of a composite."""
+        return self._parent
+
+    def SetParent(self, p):
+        self._parent = p
+
+    def GetChildren(self):
+        """Return the list of children for this shape."""
+        return self._children
+
+    def GetDrawHandles(self):
+        """Return the list of drawhandles."""
+        return self._drawHandles
+
+    def GetEventHandler(self):
+        """Return the event handler for this shape."""
+        return self._eventHandler
+
+    def SetEventHandler(self, handler):
+        """Set the event handler for this shape."""
+        self._eventHandler = handler
+
+    def Recompute(self):
+        """Recomputes any constraints associated with the shape.
+
+        Normally applicable to CompositeShapes only, but harmless for
+        other classes of Shape.
+        """
+        return True
+
+    def IsHighlighted(self):
+        """TRUE if the shape is highlighted. Shape highlighting is unimplemented."""
+        return self._highlighted
+
+    def GetSensitivityFilter(self):
+        """Return the sensitivity filter, a bitlist of values.
+
+        See Shape.SetSensitivityFilter.
+        """
+        return self._sensitivity
+
+    def SetFixedSize(self, x, y):
+        """Set the shape to be fixed size."""
+        self._fixedWidth = x
+        self._fixedHeight = y
+
+    def GetFixedSize(self):
+        """Return flags indicating whether the shape is of fixed size in
+        either direction.
+        """
+        return self._fixedWidth, self._fixedHeight
+
+    def GetFixedWidth(self):
+        """TRUE if the shape cannot be resized in the horizontal plane."""
+        return self._fixedWidth
+
+    def GetFixedHeight(self):
+        """TRUE if the shape cannot be resized in the vertical plane."""
+        return self._fixedHeight
+    
+    def SetSpaceAttachments(self, sp):
+        """Indicate whether lines should be spaced out evenly at the point
+        they touch the node (sp = True), or whether they should join at a single
+        point (sp = False).
+        """
+        self._spaceAttachments = sp
+
+    def GetSpaceAttachments(self):
+        """Return whether lines should be spaced out evenly at the point they
+        touch the node (True), or whether they should join at a single point
+        (False).
+        """
+        return self._spaceAttachments
+
+    def SetCentreResize(self, cr):
+        """Specify whether the shape is to be resized from the centre (the
+        centre stands still) or from the corner or side being dragged (the
+        other corner or side stands still).
+        """
+        self._centreResize = cr
+
+    def GetCentreResize(self):
+        """TRUE if the shape is to be resized from the centre (the centre stands
+        still), or FALSE if from the corner or side being dragged (the other
+        corner or side stands still)
+        """
+        return self._centreResize
+
+    def SetMaintainAspectRatio(self, ar):
+        """Set whether a shape that resizes should not change the aspect ratio
+        (width and height should be in the original proportion).
+        """
+        self._maintainAspectRatio = ar
+
+    def GetMaintainAspectRatio(self):
+        """TRUE if shape keeps aspect ratio during resize."""
+        return self._maintainAspectRatio
+
+    def GetLines(self):
+        """Return the list of lines connected to this shape."""
+        return self._lines
+
+    def SetDisableLabel(self, flag):
+        """Set flag to TRUE to stop the default region being shown."""
+        self._disableLabel = flag
+
+    def GetDisableLabel(self):
+        """TRUE if the default region will not be shown, FALSE otherwise."""
+        return self._disableLabel
+
+    def SetAttachmentMode(self, mode):
+        """Set the attachment mode.
+
+        If TRUE, attachment points will be significant when drawing lines to
+        and from this shape.
+        If FALSE, lines will be drawn as if to the centre of the shape.
+        """
+        self._attachmentMode = mode
+
+    def GetAttachmentMode(self):
+        """Return the attachment mode.
+
+        See Shape.SetAttachmentMode.
+        """
+        return self._attachmentMode
+
+    def SetId(self, i):
+        """Set the integer identifier for this shape."""
+        self._id = i
+
+    def GetId(self):
+        """Return the integer identifier for this shape."""
+        return self._id
+
+    def IsShown(self):
+        """TRUE if the shape is in a visible state, FALSE otherwise.
+
+        Note that this has nothing to do with whether the window is hidden
+        or the shape has scrolled off the canvas; it refers to the internal
+        visibility flag.
+        """
+        return self._visible
+
+    def GetPen(self):
+        """Return the pen used for drawing the shape's outline."""
+        return self._pen
+
+    def GetBrush(self):
+        """Return the brush used for filling the shape."""
+        return self._brush
+
+    def GetNumberOfTextRegions(self):
+        """Return the number of text regions for this shape."""
+        return len(self._regions)
+
+    def GetRegions(self):
+        """Return the list of ShapeRegions."""
+        return self._regions
+
+    # Control points ('handles') redirect control to the actual shape, to
+    # make it easier to override sizing behaviour.
+    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
+        bound_x, bound_y = self.GetBoundingBoxMin()
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        if self.GetCentreResize():
+            # Maintain the same centre point
+            new_width = 2.0 * abs(x - self.GetX())
+            new_height = 2.0 * abs(y - self.GetY())
+
+            # Constrain sizing according to what control point you're dragging
+            if pt._type == CONTROL_POINT_HORIZONTAL:
+                if self.GetMaintainAspectRatio():
+                    new_height = bound_y * (new_width / bound_x)
+                else:
+                    new_height = bound_y
+            elif pt._type == CONTROL_POINT_VERTICAL:
+                if self.GetMaintainAspectRatio():
+                    new_width = bound_x * (new_height / bound_y)
+                else:
+                    new_width = bound_x
+            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEY_SHIFT):
+                new_height = bound_y * (new_width / bound_x)
+
+            if self.GetFixedWidth():
+                new_width = bound_x
+
+            if self.GetFixedHeight():
+                new_height = bound_y
+
+            pt._controlPointDragEndWidth = new_width
+            pt._controlPointDragEndHeight = new_height
+
+            self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), new_width, new_height)
+        else:
+            # Don't maintain the same centre point
+            newX1 = min(pt._controlPointDragStartX, x)
+            newY1 = min(pt._controlPointDragStartY, y)
+            newX2 = max(pt._controlPointDragStartX, x)
+            newY2 = max(pt._controlPointDragStartY, y)
+            if pt._type == CONTROL_POINT_HORIZONTAL:
+                newY1 = pt._controlPointDragStartY
+                newY2 = newY1 + pt._controlPointDragStartHeight
+            elif pt._type == CONTROL_POINT_VERTICAL:
+                newX1 = pt._controlPointDragStartX
+                newX2 = newX1 + pt._controlPointDragStartWidth
+            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEY_SHIFT or self.GetMaintainAspectRatio()):
+                newH = (newX2 - newX1) * (pt._controlPointDragStartHeight / pt._controlPointDragStartWidth)
+                if self.GetY()>pt._controlPointDragStartY:
+                    newY2 = newY1 + newH
+                else:
+                    newY1 = newY2 - newH
+
+            newWidth = newX2 - newX1
+            newHeight = newY2 - newY1
+
+            if pt._type == CONTROL_POINT_VERTICAL and self.GetMaintainAspectRatio():
+                newWidth = bound_x * (newHeight / bound_y)
+
+            if pt._type == CONTROL_POINT_HORIZONTAL and self.GetMaintainAspectRatio():
+                newHeight = bound_y * (newWidth / bound_x)
+
+            pt._controlPointDragPosX = newX1 + newWidth / 2
+            pt._controlPointDragPosY = newY1 + newHeight / 2
+            if self.GetFixedWidth():
+                newWidth = bound_x
+
+            if self.GetFixedHeight():
+                newHeight = bound_y
+
+            pt._controlPointDragEndWidth = newWidth
+            pt._controlPointDragEndHeight = newHeight
+            self.GetEventHandler().OnDrawOutline(dc, pt._controlPointDragPosX, pt._controlPointDragPosY, newWidth, newHeight)
+
+    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        self._canvas.CaptureMouse()
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+
+        bound_x, bound_y = self.GetBoundingBoxMin()
+        self.GetEventHandler().OnEndSize(bound_x, bound_y)
+
+        # Choose the 'opposite corner' of the object as the stationary
+        # point in case this is non-centring resizing.
+        if pt.GetX()<self.GetX():
+            pt._controlPointDragStartX = self.GetX() + bound_x / 2
+        else:
+            pt._controlPointDragStartX = self.GetX() - bound_x / 2
+
+        if pt.GetY()<self.GetY():
+            pt._controlPointDragStartY = self.GetY() + bound_y / 2
+        else:
+            pt._controlPointDragStartY = self.GetY() - bound_y / 2
+
+        if pt._type == CONTROL_POINT_HORIZONTAL:
+            pt._controlPointDragStartY = self.GetY() - bound_y / 2
+        elif pt._type == CONTROL_POINT_VERTICAL:
+            pt._controlPointDragStartX = self.GetX() - bound_x / 2
+
+        # We may require the old width and height
+        pt._controlPointDragStartWidth = bound_x
+        pt._controlPointDragStartHeight = bound_y
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        if self.GetCentreResize():
+            new_width = 2.0 * abs(x - self.GetX())
+            new_height = 2.0 * abs(y - self.GetY())
+
+            # Constrain sizing according to what control point you're dragging
+            if pt._type == CONTROL_POINT_HORIZONTAL:
+                if self.GetMaintainAspectRatio():
+                    new_height = bound_y * (new_width / bound_x)
+                else:
+                    new_height = bound_y
+            elif pt._type == CONTROL_POINT_VERTICAL:
+                if self.GetMaintainAspectRatio():
+                    new_width = bound_x * (new_height / bound_y)
+                else:
+                    new_width = bound_x
+            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEY_SHIFT):
+                new_height = bound_y * (new_width / bound_x)
+
+            if self.GetFixedWidth():
+                new_width = bound_x
+
+            if self.GetFixedHeight():
+                new_height = bound_y
+
+            pt._controlPointDragEndWidth = new_width
+            pt._controlPointDragEndHeight = new_height
+            self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), new_width, new_height)
+        else:
+            # Don't maintain the same centre point
+            newX1 = min(pt._controlPointDragStartX, x)
+            newY1 = min(pt._controlPointDragStartY, y)
+            newX2 = max(pt._controlPointDragStartX, x)
+            newY2 = max(pt._controlPointDragStartY, y)
+            if pt._type == CONTROL_POINT_HORIZONTAL:
+                newY1 = pt._controlPointDragStartY
+                newY2 = newY1 + pt._controlPointDragStartHeight
+            elif pt._type == CONTROL_POINT_VERTICAL:
+                newX1 = pt._controlPointDragStartX
+                newX2 = newX1 + pt._controlPointDragStartWidth
+            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEYS or self.GetMaintainAspectRatio()):
+                newH = (newX2 - newX1) * (pt._controlPointDragStartHeight / pt._controlPointDragStartWidth)
+                if pt.GetY()>pt._controlPointDragStartY:
+                    newY2 = newY1 + newH
+                else:
+                    newY1 = newY2 - newH
+
+            newWidth = newX2 - newX1
+            newHeight = newY2 - newY1
+
+            if pt._type == CONTROL_POINT_VERTICAL and self.GetMaintainAspectRatio():
+                newWidth = bound_x * (newHeight / bound_y)
+
+            if pt._type == CONTROL_POINT_HORIZONTAL and self.GetMaintainAspectRatio():
+                newHeight = bound_y * (newWidth / bound_x)
+
+            pt._controlPointDragPosX = newX1 + newWidth / 2
+            pt._controlPointDragPosY = newY1 + newHeight / 2
+            if self.GetFixedWidth():
+                newWidth = bound_x
+
+            if self.GetFixedHeight():
+                newHeight = bound_y
+
+            pt._controlPointDragEndWidth = newWidth
+            pt._controlPointDragEndHeight = newHeight
+            self.GetEventHandler().OnDrawOutline(dc, pt._controlPointDragPosX, pt._controlPointDragPosY, newWidth, newHeight)
+            
+    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        if self._canvas.HasCapture():
+            self._canvas.ReleaseMouse()
+        dc.SetLogicalFunction(wx.COPY)
+        self.Recompute()
+        self.ResetControlPoints()
+
+        self.Erase(dc)
+
+        self.SetSize(pt._controlPointDragEndWidth, pt._controlPointDragEndHeight)
+
+        # The next operation could destroy this control point (it does for
+        # label objects, via formatting the text), so save all values we're
+        # going to use, or we'll be accessing garbage.
+
+        #return
+
+        if self.GetCentreResize():
+            self.Move(dc, self.GetX(), self.GetY())
+        else:
+            self.Move(dc, pt._controlPointDragPosX, pt._controlPointDragPosY)
+
+        # Recursively redraw links if we have a composite
+        if len(self.GetChildren()):
+            self.DrawLinks(dc,-1, True)
+
+        width, height = self.GetBoundingBoxMax()
+        self.GetEventHandler().OnEndSize(width, height)
+
+        if not self._canvas.GetQuickEditMode() and pt._eraseObject:
+            self._canvas.Redraw(dc)
+
+
+            
+class RectangleShape(Shape):
+    """
+    The wxRectangleShape has rounded or square corners.
+
+    Derived from:
+      Shape
+    """
+    def __init__(self, w = 0.0, h = 0.0):
+        Shape.__init__(self)
+        self._width = w
+        self._height = h
+        self._cornerRadius = 0.0
+        self.SetDefaultRegionSize()
+
+    def OnDraw(self, dc):
+        x1 = self._xpos - self._width / 2
+        y1 = self._ypos - self._height / 2
+
+        if self._shadowMode != SHADOW_NONE:
+            if self._shadowBrush:
+                dc.SetBrush(self._shadowBrush)
+            dc.SetPen(TransparentPen)
+
+            if self._cornerRadius:
+                dc.DrawRoundedRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height, self._cornerRadius)
+            else:
+                dc.DrawRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height)
+
+        if self._pen:
+            if self._pen.GetWidth() == 0:
+                dc.SetPen(TransparentPen)
+            else:
+                dc.SetPen(self._pen)
+        if self._brush:
+            dc.SetBrush(self._brush)
+
+        if self._cornerRadius:
+            dc.DrawRoundedRectangle(x1, y1, self._width, self._height, self._cornerRadius)
+        else:
+            dc.DrawRectangle(x1, y1, self._width, self._height)
+
+    def GetBoundingBoxMin(self):
+        return self._width, self._height
+
+    def SetSize(self, x, y, recursive = False):
+        self.SetAttachmentSize(x, y)
+        self._width = max(x, 1)
+        self._height = max(y, 1)
+        self.SetDefaultRegionSize()
+
+    def GetCornerRadius(self):
+        """Get the radius of the rectangle's rounded corners."""
+        return self._cornerRadius
+    
+    def SetCornerRadius(self, rad):
+        """Set the radius of the rectangle's rounded corners.
+
+        If the radius is zero, a non-rounded rectangle will be drawn.
+        If the radius is negative, the value is the proportion of the smaller
+        dimension of the rectangle.
+        """
+        self._cornerRadius = rad
+
+    # Assume (x1, y1) is centre of box (most generally, line end at box)
+    def GetPerimeterPoint(self, x1, y1, x2, y2):
+        bound_x, bound_y = self.GetBoundingBoxMax()
+        return FindEndForBox(bound_x, bound_y, self._xpos, self._ypos, x2, y2)
+
+    def GetWidth(self):
+        return self._width
+
+    def GetHeight(self):
+        return self._height
+
+    def SetWidth(self, w):
+        self._width = w
+
+    def SetHeight(self, h):
+        self._height = h
+
+
+        
+class PolygonShape(Shape):
+    """A PolygonShape's shape is defined by a number of points passed to
+    the object's constructor. It can be used to create new shapes such as
+    diamonds and triangles.
+    """
+    def __init__(self):
+        Shape.__init__(self)
+        
+        self._points = None
+        self._originalPoints = None
+
+    def Create(self, the_points = None):
+        """Takes a list of wx.RealPoints or tuples; each point is an offset
+        from the centre.
+        """
+        self.ClearPoints()
+
+        if not the_points:
+            self._originalPoints = []
+            self._points = []
+        else:
+            self._originalPoints = the_points
+
+            # Duplicate the list of points
+            self._points = []
+            for point in the_points:
+                new_point = wx.Point(point[0], point[1])
+                self._points.append(new_point)
+            self.CalculateBoundingBox()
+            self._originalWidth = self._boundWidth
+            self._originalHeight = self._boundHeight
+            self.SetDefaultRegionSize()
+
+    def ClearPoints(self):
+        self._points = []
+        self._originalPoints = []
+
+    # Width and height. Centre of object is centre of box
+    def GetBoundingBoxMin(self):
+        return self._boundWidth, self._boundHeight
+
+    def GetPoints(self):
+        """Return the internal list of polygon vertices."""
+        return self._points
+
+    def GetOriginalPoints(self):
+        return self._originalPoints
+
+    def GetOriginalWidth(self):
+        return self._originalWidth
+
+    def GetOriginalHeight(self):
+        return self._originalHeight
+
+    def SetOriginalWidth(self, w):
+        self._originalWidth = w
+
+    def SetOriginalHeight(self, h):
+        self._originalHeight = h
+        
+    def CalculateBoundingBox(self):
+        # Calculate bounding box at construction (and presumably resize) time
+        left = 10000
+        right=-10000
+        top = 10000
+        bottom=-10000
+
+        for point in self._points:
+            if point.x<left:
+                left = point.x
+            if point.x>right:
+                right = point.x
+
+            if point.y<top:
+                top = point.y
+            if point.y>bottom:
+                bottom = point.y
+
+        self._boundWidth = right - left
+        self._boundHeight = bottom - top
+
+    def CalculatePolygonCentre(self):
+        """Recalculates the centre of the polygon, and
+        readjusts the point offsets accordingly.
+        Necessary since the centre of the polygon
+        is expected to be the real centre of the bounding
+        box.
+        """
+        left = 10000
+        right=-10000
+        top = 10000
+        bottom=-10000
+
+        for point in self._points:
+            if point.x<left:
+                left = point.x
+            if point.x>right:
+                right = point.x
+
+            if point.y<top:
+                top = point.y
+            if point.y>bottom:
+                bottom = point.y
+
+        bwidth = right - left
+        bheight = bottom - top
+
+        newCentreX = left + bwidth / 2
+        newCentreY = top + bheight / 2
+
+        for point in self._points:
+            point.x -= newCentreX
+            point.y -= newCentreY
+        self._xpos += newCentreX
+        self._ypos += newCentreY
+
+    def HitTest(self, x, y):
+        # Imagine four lines radiating from this point. If all of these lines
+        # hit the polygon, we're inside it, otherwise we're not. Obviously
+        # we'd need more radiating lines to be sure of correct results for
+        # very strange (concave) shapes.
+        endPointsX = [x, x + 1000, x, x - 1000]
+        endPointsY = [y - 1000, y, y + 1000, y]
+
+        xpoints = []
+        ypoints = []
+
+        for point in self._points:
+            xpoints.append(point.x + self._xpos)
+            ypoints.append(point.y + self._ypos)
+
+        # We assume it's inside the polygon UNLESS one or more
+        # lines don't hit the outline.
+        isContained = True
+
+        for i in range(4):
+            if not PolylineHitTest(xpoints, ypoints, x, y, endPointsX[i], endPointsY[i]):
+                isContained = False
+
+        if not isContained:
+            return False
+
+        nearest_attachment = 0
+
+        # If a hit, check the attachment points within the object
+        nearest = 999999
+
+        for i in range(self.GetNumberOfAttachments()):
+            e = self.GetAttachmentPositionEdge(i)
+            if e:
+                xp, yp = e
+                l = math.sqrt((xp - x) * (xp - x) + (yp - y) * (yp - y))
+                if l<nearest:
+                    nearest = l
+                    nearest_attachment = i
+
+        return nearest_attachment, nearest
+
+    # Really need to be able to reset the shape! Otherwise, if the
+    # points ever go to zero, we've lost it, and can't resize.
+    def SetSize(self, new_width, new_height, recursive = True):
+        self.SetAttachmentSize(new_width, new_height)
+
+        # Multiply all points by proportion of new size to old size
+        x_proportion = abs(new_width / self._originalWidth)
+        y_proportion = abs(new_height / self._originalHeight)
+
+        for i in range(max(len(self._points), len(self._originalPoints))):
+            self._points[i].x = self._originalPoints[i][0] * x_proportion
+            self._points[i].y = self._originalPoints[i][1] * y_proportion
+
+        self._boundWidth = abs(new_width)
+        self._boundHeight = abs(new_height)
+        self.SetDefaultRegionSize()
+
+    # Make the original points the same as the working points
+    def UpdateOriginalPoints(self):
+        """If we've changed the shape, must make the original points match the
+        working points with this function.
+        """
+        self._originalPoints = []
+
+        for point in self._points:
+            original_point = wx.RealPoint(point.x, point.y)
+            self._originalPoints.append(original_point)
+
+        self.CalculateBoundingBox()
+        self._originalWidth = self._boundWidth
+        self._originalHeight = self._boundHeight
+
+    def AddPolygonPoint(self, pos):
+        """Add a control point after the given point."""
+        try:
+            firstPoint = self._points[pos]
+        except ValueError:
+            firstPoint = self._points[0]
+
+        try:
+            secondPoint = self._points[pos + 1]
+        except ValueError:
+            secondPoint = self._points[0]
+
+        x = (secondPoint.x - firstPoint.x) / 2 + firstPoint.x
+        y = (secondPoint.y - firstPoint.y) / 2 + firstPoint.y
+        point = wx.RealPoint(x, y)
+
+        if pos >= len(self._points) - 1:
+            self._points.append(point)
+        else:
+            self._points.insert(pos + 1, point)
+
+        self.UpdateOriginalPoints()
+
+        if self._selected:
+            self.DeleteControlPoints()
+            self.MakeControlPoints()
+
+    def DeletePolygonPoint(self, pos):
+        """Delete the given control point."""
+        if pos<len(self._points):
+            del self._points[pos]
+            self.UpdateOriginalPoints()
+            if self._selected:
+                self.DeleteControlPoints()
+                self.MakeControlPoints()
+
+    # Assume (x1, y1) is centre of box (most generally, line end at box)
+    def GetPerimeterPoint(self, x1, y1, x2, y2):
+        # First check for situation where the line is vertical,
+        # and we would want to connect to a point on that vertical --
+        # oglFindEndForPolyline can't cope with this (the arrow
+        # gets drawn to the wrong place).
+        if self._attachmentMode == ATTACHMENT_MODE_NONE and x1 == x2:
+            # Look for the point we'd be connecting to. This is
+            # a heuristic...
+            for point in self._points:
+                if point.x == 0:
+                    if y2>y1 and point.y>0:
+                        return point.x + self._xpos, point.y + self._ypos
+                    elif y2<y1 and point.y<0:
+                        return point.x + self._xpos, point.y + self._ypos
+
+        xpoints = []
+        ypoints = []
+        for point in self._points:
+            xpoints.append(point.x + self._xpos)
+            ypoints.append(point.y + self._ypos)
+
+        return FindEndForPolyline(xpoints, ypoints, x1, y1, x2, y2)
+
+    def OnDraw(self, dc):
+        if self._shadowMode != SHADOW_NONE:
+            if self._shadowBrush:
+                dc.SetBrush(self._shadowBrush)
+            dc.SetPen(TransparentPen)
+
+            dc.DrawPolygon(self._points, self._xpos + self._shadowOffsetX, self._ypos, self._shadowOffsetY)
+
+        if self._pen:
+            if self._pen.GetWidth() == 0:
+                dc.SetPen(TransparentPen)
+            else:
+                dc.SetPen(self._pen)
+        if self._brush:
+            dc.SetBrush(self._brush)
+        dc.DrawPolygon(self._points, self._xpos, self._ypos)
+
+    def OnDrawOutline(self, dc, x, y, w, h):
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        # Multiply all points by proportion of new size to old size
+        x_proportion = abs(w / self._originalWidth)
+        y_proportion = abs(h / self._originalHeight)
+
+        intPoints = []
+        for point in self._originalPoints:
+            intPoints.append(wx.Point(x_proportion * point[0], y_proportion * point[1]))
+        dc.DrawPolygon(intPoints, x, y)
+
+    # Make as many control points as there are vertices
+    def MakeControlPoints(self):
+        for point in self._points:
+            control = PolygonControlPoint(self._canvas, self, CONTROL_POINT_SIZE, point, point.x, point.y)
+            self._canvas.AddShape(control)
+            self._controlPoints.append(control)
+
+    def ResetControlPoints(self):
+        for i in range(min(len(self._points), len(self._controlPoints))):
+            point = self._points[i]
+            self._controlPoints[i]._xoffset = point.x
+            self._controlPoints[i]._yoffset = point.y
+            self._controlPoints[i].polygonVertex = point
+
+    def GetNumberOfAttachments(self):
+        maxN = max(len(self._points) - 1, 0)
+        for point in self._attachmentPoints:
+            if point._id>maxN:
+                maxN = point._id
+        return maxN + 1
+
+    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
+        if self._attachmentMode == ATTACHMENT_MODE_EDGE and self._points and attachment<len(self._points):
+            point = self._points[0]
+            return point.x + self._xpos, point.y + self._ypos
+        return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs, line)
+
+    def AttachmentIsValid(self, attachment):
+        if not self._points:
+            return False
+
+        if attachment >= 0 and attachment<len(self._points):
+            return True
+
+        for point in self._attachmentPoints:
+            if point._id == attachment:
+                return True
+
+        return False
+
+    # Rotate about the given axis by the given amount in radians
+    def Rotate(self, x, y, theta):
+        actualTheta = theta - self._rotation
+
+        # Rotate attachment points
+        sinTheta = math.sin(actualTheta)
+        cosTheta = math.cos(actualTheta)
+
+        for point in self._attachmentPoints:
+            x1 = point._x
+            y1 = point._y
+
+            point._x = x1 * cosTheta - y1 * sinTheta + x * (1 - cosTheta) + y * sinTheta
+            point._y = x1 * sinTheta + y1 * cosTheta + y * (1 - cosTheta) + x * sinTheta
+
+        for point in self._points:
+            x1 = point.x
+            y1 = point.y
+
+            point.x = x1 * cosTheta - y1 * sinTheta + x * (1 - cosTheta) + y * sinTheta
+            point.y = x1 * sinTheta + y1 * cosTheta + y * (1 - cosTheta) + x * sinTheta
+
+        for point in self._originalPoints:
+            x1 = point.x
+            y1 = point.y
+
+            point.x = x1 * cosTheta - y1 * sinTheta + x * (1 - cosTheta) + y * sinTheta
+            point.y = x1 * sinTheta + y1 * cosTheta + y * (1 - cosTheta) + x * sinTheta
+
+        self._rotation = theta
+
+        self.CalculatePolygonCentre()
+        self.CalculateBoundingBox()
+        self.ResetControlPoints()
+
+    # Control points ('handles') redirect control to the actual shape, to
+    # make it easier to override sizing behaviour.
+    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        # Code for CTRL-drag in C++ version commented out
+        
+        pt.CalculateNewSize(x, y)
+
+        self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), pt.GetNewSize().x, pt.GetNewSize().y)
+
+    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        self.Erase(dc)
+        
+        dc.SetLogicalFunction(OGLRBLF)
+
+        bound_x, bound_y = self.GetBoundingBoxMin()
+
+        dist = math.sqrt((x - self.GetX()) * (x - self.GetX()) + (y - self.GetY()) * (y - self.GetY()))
+
+        pt._originalDistance = dist
+        pt._originalSize.x = bound_x
+        pt._originalSize.y = bound_y
+
+        if pt._originalDistance == 0:
+            pt._originalDistance = 0.0001
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        # Code for CTRL-drag in C++ version commented out
+
+        pt.CalculateNewSize(x, y)
+
+        self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), pt.GetNewSize().x, pt.GetNewSize().y)
+
+        self._canvas.CaptureMouse()
+
+    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        if self._canvas.HasCapture():
+            self._canvas.ReleaseMouse()
+        dc.SetLogicalFunction(wx.COPY)
+
+        # If we're changing shape, must reset the original points
+        if keys & KEY_CTRL:
+            self.CalculateBoundingBox()
+            self.CalculatePolygonCentre()
+        else:
+            self.SetSize(pt.GetNewSize().x, pt.GetNewSize().y)
+
+        self.Recompute()
+        self.ResetControlPoints()
+        self.Move(dc, self.GetX(), self.GetY())
+        if not self._canvas.GetQuickEditMode():
+            self._canvas.Redraw(dc)
+
+
+
+class EllipseShape(Shape):
+    """The EllipseShape behaves similarly to the RectangleShape but is
+    elliptical.
+
+    Derived from:
+      wxShape
+    """
+    def __init__(self, w, h):
+        Shape.__init__(self)
+        self._width = w
+        self._height = h
+        self.SetDefaultRegionSize()
+
+    def GetBoundingBoxMin(self):
+        return self._width, self._height
+
+    def GetPerimeterPoint(self, x1, y1, x2, y2):
+        bound_x, bound_y = self.GetBoundingBoxMax()
+
+        return DrawArcToEllipse(self._xpos, self._ypos, bound_x, bound_y, x2, y2, x1, y1)
+
+    def GetWidth(self):
+        return self._width
+
+    def GetHeight(self):
+        return self._height
+
+    def SetWidth(self, w):
+        self._width = w
+
+    def SetHeight(self, h):
+        self._height = h
+        
+    def OnDraw(self, dc):
+        if self._shadowMode != SHADOW_NONE:
+            if self._shadowBrush:
+                dc.SetBrush(self._shadowBrush)
+            dc.SetPen(TransparentPen)
+            dc.DrawEllipse(self._xpos - self.GetWidth() / 2 + self._shadowOffsetX,
+                           self._ypos - self.GetHeight() / 2 + self._shadowOffsetY,
+                           self.GetWidth(), self.GetHeight())
+
+        if self._pen:
+            if self._pen.GetWidth() == 0:
+                dc.SetPen(TransparentPen)
+            else:
+                dc.SetPen(self._pen)
+        if self._brush:
+            dc.SetBrush(self._brush)
+        dc.DrawEllipse(self._xpos - self.GetWidth() / 2, self._ypos - self.GetHeight() / 2, self.GetWidth(), self.GetHeight())
+
+    def SetSize(self, x, y, recursive = True):
+        self.SetAttachmentSize(x, y)
+        self._width = x
+        self._height = y
+        self.SetDefaultRegionSize()
+
+    def GetNumberOfAttachments(self):
+        return Shape.GetNumberOfAttachments(self)
+
+    # There are 4 attachment points on an ellipse - 0 = top, 1 = right,
+    # 2 = bottom, 3 = left.
+    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
+        if self._attachmentMode == ATTACHMENT_MODE_BRANCHING:
+            return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs, line)
+
+        if self._attachmentMode != ATTACHMENT_MODE_NONE:
+            top = self._ypos + self._height / 2
+            bottom = self._ypos - self._height / 2
+            left = self._xpos - self._width / 2
+            right = self._xpos + self._width / 2
+
+            physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
+
+            if physicalAttachment == 0:
+                if self._spaceAttachments:
+                    x = left + (nth + 1) * self._width / (no_arcs + 1)
+                else:
+                    x = self._xpos
+                y = top
+                # We now have the point on the bounding box: but get the point
+                # on the ellipse by imagining a vertical line from
+                # (x, self._ypos - self_height - 500) to (x, self._ypos) intersecting
+                # the ellipse.
+
+                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, x, self._ypos - self._height - 500, x, self._ypos)
+            elif physicalAttachment == 1:
+                x = right
+                if self._spaceAttachments:
+                    y = bottom + (nth + 1) * self._height / (no_arcs + 1)
+                else:
+                    y = self._ypos
+                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, self._xpos + self._width + 500, y, self._xpos, y)
+            elif physicalAttachment == 2:
+                if self._spaceAttachments:
+                    x = left + (nth + 1) * self._width / (no_arcs + 1)
+                else:
+                    x = self._xpos
+                y = bottom
+                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, x, self._ypos + self._height + 500, x, self._ypos)
+            elif physicalAttachment == 3:
+                x = left
+                if self._spaceAttachments:
+                    y = bottom + (nth + 1) * self._height / (no_arcs + 1)
+                else:
+                    y = self._ypos
+                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, self._xpos - self._width - 500, y, self._xpos, y)
+            else:
+                return Shape.GetAttachmentPosition(self, attachment, x, y, nth, no_arcs, line)
+        else:
+            return self._xpos, self._ypos
+
+
+
+class CircleShape(EllipseShape):
+    """An EllipseShape whose width and height are the same."""
+    def __init__(self, diameter):
+        EllipseShape.__init__(self, diameter, diameter)
+        self.SetMaintainAspectRatio(True)
+
+    def GetPerimeterPoint(self, x1, y1, x2, y2):
+        return FindEndForCircle(self._width / 2, self._xpos, self._ypos, x2, y2)
+
+
+
+class TextShape(RectangleShape):
+    """As wxRectangleShape, but only the text is displayed."""
+    def __init__(self, width, height):
+        RectangleShape.__init__(self, width, height)
+
+    def OnDraw(self, dc):
+        pass
+
+
+
+class ShapeRegion(object):
+    """Object region."""
+    def __init__(self, region = None):
+        if region:
+            self._regionText = region._regionText
+            self._regionName = region._regionName
+            self._textColour = region._textColour
+
+            self._font = region._font
+            self._minHeight = region._minHeight
+            self._minWidth = region._minWidth
+            self._width = region._width
+            self._height = region._height
+            self._x = region._x
+            self._y = region._y
+
+            self._regionProportionX = region._regionProportionX
+            self._regionProportionY = region._regionProportionY
+            self._formatMode = region._formatMode
+            self._actualColourObject = region._actualColourObject
+            self._actualPenObject = None
+            self._penStyle = region._penStyle
+            self._penColour = region._penColour
+
+            self.ClearText()
+            for line in region._formattedText:
+                new_line = ShapeTextLine(line.GetX(), line.GetY(), line.GetText())
+                self._formattedText.append(new_line)
+        else:
+            self._regionText=""
+            self._font = NormalFont
+            self._minHeight = 5.0
+            self._minWidth = 5.0
+            self._width = 0.0
+            self._height = 0.0
+            self._x = 0.0
+            self._y = 0.0
+
+            self._regionProportionX=-1.0
+            self._regionProportionY=-1.0
+            self._formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT
+            self._regionName=""
+            self._textColour="BLACK"
+            self._penColour="BLACK"
+            self._penStyle = wx.SOLID
+            self._actualColourObject = wx.TheColourDatabase.Find("BLACK")
+            self._actualPenObject = None
+
+        self._formattedText = []
+
+    def ClearText(self):
+        self._formattedText = []
+
+    def SetFont(self, f):
+        self._font = f
+
+    def SetMinSize(self, w, h):
+        self._minWidth = w
+        self._minHeight = h
+
+    def SetSize(self, w, h):
+        self._width = w
+        self._height = h
+
+    def SetPosition(self, xp, yp):
+        self._x = xp
+        self._y = yp
+
+    def SetProportions(self, xp, yp):
+        self._regionProportionX = xp
+        self._regionProportionY = yp
+
+    def SetFormatMode(self, mode):
+        self._formatMode = mode
+
+    def SetColour(self, col):
+        self._textColour = col
+        self._actualColourObject = col
+
+    def GetActualColourObject(self):
+        self._actualColourObject = wx.TheColourDatabase.Find(self.GetColour())
+        return self._actualColourObject
+
+    def SetPenColour(self, col):
+        self._penColour = col
+        self._actualPenObject = None
+
+    # Returns NULL if the pen is invisible
+    # (different to pen being transparent; indicates that
+    # region boundary should not be drawn.)
+    def GetActualPen(self):
+        if self._actualPenObject:
+            return self._actualPenObject
+
+        if not self._penColour:
+            return None
+        if self._penColour=="Invisible":
+            return None
+        self._actualPenObject = wx.ThePenList.FindOrCreatePen(self._penColour, 1, self._penStyle)
+        return self._actualPenObject
+
+    def SetText(self, s):
+        self._regionText = s
+
+    def SetName(self, s):
+        self._regionName = s
+
+    def GetText(self):
+        return self._regionText
+
+    def GetFont(self):
+        return self._font
+
+    def GetMinSize(self):
+        return self._minWidth, self._minHeight
+
+    def GetProportion(self):
+        return self._regionProportionX, self._regionProportionY
+
+    def GetSize(self):
+        return self._width, self._height
+
+    def GetPosition(self):
+        return self._x, self._y
+
+    def GetFormatMode(self):
+        return self._formatMode
+
+    def GetName(self):
+        return self._regionName
+
+    def GetColour(self):
+        return self._textColour
+
+    def GetFormattedText(self):
+        return self._formattedText
+
+    def GetPenColour(self):
+        return self._penColour
+
+    def GetPenStyle(self):
+        return self._penStyle
+
+    def SetPenStyle(self, style):
+        self._penStyle = style
+        self._actualPenObject = None
+
+    def GetWidth(self):
+        return self._width
+
+    def GetHeight(self):
+        return self._height
+
+
+
+class ControlPoint(RectangleShape):
+    def __init__(self, theCanvas, object, size, the_xoffset, the_yoffset, the_type):
+        RectangleShape.__init__(self, size, size)
+
+        self._canvas = theCanvas
+        self._shape = object
+        self._xoffset = the_xoffset
+        self._yoffset = the_yoffset
+        self._type = the_type
+        self.SetPen(BlackForegroundPen)
+        self.SetBrush(wx.BLACK_BRUSH)
+        self._oldCursor = None
+        self._visible = True
+        self._eraseObject = True
+        
+    # Don't even attempt to draw any text - waste of time
+    def OnDrawContents(self, dc):
+        pass
+
+    def OnDraw(self, dc):
+        self._xpos = self._shape.GetX() + self._xoffset
+        self._ypos = self._shape.GetY() + self._yoffset
+        RectangleShape.OnDraw(self, dc)
+
+    def OnErase(self, dc):
+        RectangleShape.OnErase(self, dc)
+
+    # Implement resizing of canvas object
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
+
+    def GetNumberOfAttachments(self):
+        return 1
+
+    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
+        return self._xpos, self._ypos
+
+    def SetEraseObject(self, er):
+        self._eraseObject = er
+
+        
+class PolygonControlPoint(ControlPoint):
+    def __init__(self, theCanvas, object, size, vertex, the_xoffset, the_yoffset):
+        ControlPoint.__init__(self, theCanvas, object, size, the_xoffset, the_yoffset, 0)
+        self._polygonVertex = vertex
+        self._originalDistance = 0.0
+        self._newSize = wx.RealPoint()
+        self._originalSize = wx.RealPoint()
+
+    def GetNewSize(self):
+        return self._newSize
+    
+    # Calculate what new size would be, at end of resize
+    def CalculateNewSize(self, x, y):
+        bound_x, bound_y = self.GetShape().GetBoundingBoxMax()
+        dist = math.sqrt((x - self._shape.GetX()) * (x - self._shape.GetX()) + (y - self._shape.GetY()) * (y - self._shape.GetY()))
+
+        self._newSize.x = dist / self._originalDistance * self._originalSize.x
+        self._newSize.y = dist / self._originalDistance * self._originalSize.y
+
+    # Implement resizing polygon or moving the vertex
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
+
+from _canvas import *
+from _lines import *
+from _composit import *
diff --git a/wxPython/wx/lib/ogl/_bmpshape.py b/wxPython/wx/lib/ogl/_bmpshape.py
new file mode 100644 (file)
index 0000000..8a12d85
--- /dev/null
@@ -0,0 +1,66 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         bmpshape.py
+# Purpose:      Bitmap shape
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+from _basic import RectangleShape
+
+
+class BitmapShape(RectangleShape):
+    """Draws a bitmap (non-resizable)."""
+    def __init__(self):
+        RectangleShape.__init__(self, 100, 50)
+        self._filename=""
+
+    def OnDraw(self, dc):
+        if not self._bitmap.Ok():
+            return
+
+        x = self._xpos-self._bitmap.GetWidth() / 2
+        y = self._ypos-self._bitmap.GetHeight() / 2
+        dc.DrawBitmap(self._bitmap, x, y, True)
+
+    def SetSize(self, w, h, recursive = True):
+        if self._bitmap.Ok():
+            w = self._bitmap.GetWidth()
+            h = self._bitmap.GetHeight()
+
+        self.SetAttachmentSize(w, h)
+
+        self._width = w
+        self._height = h
+
+        self.SetDefaultRegionSize()
+
+    def GetBitmap(self):
+        """Return a the bitmap associated with this shape."""
+        return self._bitmap
+    
+    def SetBitmap(self, bitmap):
+        """Set the bitmap associated with this shape.
+
+        You can delete the bitmap from the calling application, since
+        reference counting will take care of holding on to the internal bitmap
+        data.
+        """
+        self._bitmap = bitmap
+        if self._bitmap.Ok():
+            self.SetSize(self._bitmap.GetWidth(), self._bitmap.GetHeight())
+            
+    def SetFilename(self, f):
+        """Set the bitmap filename."""
+        self._filename = f
+
+    def GetFilename(self):
+        """Return the bitmap filename."""
+        return self._filename
diff --git a/wxPython/wx/lib/ogl/_canvas.py b/wxPython/wx/lib/ogl/_canvas.py
new file mode 100644 (file)
index 0000000..105cfa2
--- /dev/null
@@ -0,0 +1,360 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         canvas.py
+# Purpose:      The canvas class
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+import wx
+from _lines import LineShape
+from _composit import *
+
+NoDragging, StartDraggingLeft, ContinueDraggingLeft, StartDraggingRight, ContinueDraggingRight = 0, 1, 2, 3, 4
+
+KEY_SHIFT, KEY_CTRL = 1, 2
+
+
+
+# Helper function: True if 'contains' wholly contains 'contained'.
+def WhollyContains(contains, contained):
+    xp1, yp1 = contains.GetX(), contains.GetY()
+    xp2, yp2 = contained.GetX(), contained.GetY()
+    
+    w1, h1 = contains.GetBoundingBoxMax()
+    w2, h2 = contained.GetBoundingBoxMax()
+    
+    left1 = xp1-w1 / 2.0
+    top1 = yp1-h1 / 2.0
+    right1 = xp1 + w1 / 2.0
+    bottom1 = yp1 + h1 / 2.0
+    
+    left2 = xp2-w2 / 2.0
+    top2 = yp2-h2 / 2.0
+    right2 = xp2 + w2 / 2.0
+    bottom2 = yp2 + h2 / 2.0
+    
+    return ((left1 <= left2) and (top1 <= top2) and (right1 >= right2) and (bottom1 >= bottom2))
+    
+
+
+class ShapeCanvas(wx.ScrolledWindow):
+    def __init__(self, parent = None, id=-1, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.BORDER, name="ShapeCanvas"):
+        wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name)
+
+        self._shapeDiagram = None
+        self._dragState = NoDragging
+        self._draggedShape = None
+        self._oldDragX = 0
+        self._oldDragY = 0
+        self._firstDragX = 0
+        self._firstDragY = 0
+        self._checkTolerance = True
+
+        wx.EVT_PAINT(self, self.OnPaint)
+        wx.EVT_MOUSE_EVENTS(self, self.OnMouseEvent)
+
+    def SetDiagram(self, diag):
+        self._shapeDiagram = diag
+
+    def GetDiagram(self):
+        return self._shapeDiagram
+    
+    def OnPaint(self, evt):
+        dc = wx.PaintDC(self)
+        self.PrepareDC(dc)
+        
+        dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
+        dc.Clear()
+
+        if self.GetDiagram():
+            self.GetDiagram().Redraw(dc)
+
+    def OnMouseEvent(self, evt):
+        dc = wx.ClientDC(self)
+        self.PrepareDC(dc)
+        
+        x, y = evt.GetLogicalPosition(dc)
+
+        keys = 0
+        if evt.ShiftDown():
+            keys |= KEY_SHIFT
+        if evt.ControlDown():
+            keys |= KEY_CTRL
+
+        dragging = evt.Dragging()
+
+        # Check if we're within the tolerance for mouse movements.
+        # If we're very close to the position we started dragging
+        # from, this may not be an intentional drag at all.
+        if dragging:
+            dx = abs(dc.LogicalToDeviceX(x-self._firstDragX))
+            dy = abs(dc.LogicalToDeviceY(y-self._firstDragY))
+            if self._checkTolerance and (dx <= self.GetDiagram().GetMouseTolerance()) and (dy <= self.GetDiagram().GetMouseTolerance()):
+                return
+            # If we've ignored the tolerance once, then ALWAYS ignore
+            # tolerance in this drag, even if we come back within
+            # the tolerance range.
+            self._checkTolerance = False
+
+        # Dragging - note that the effect of dragging is left entirely up
+        # to the object, so no movement is done unless explicitly done by
+        # object.
+        if dragging and self._draggedShape and self._dragState == StartDraggingLeft:
+            self._dragState = ContinueDraggingLeft
+
+            # If the object isn't m_draggable, transfer message to canvas
+            if self._draggedShape.Draggable():
+                self._draggedShape.GetEventHandler().OnBeginDragLeft(x, y, keys, self._draggedAttachment)
+            else:
+                self._draggedShape = None
+                self.OnBeginDragLeft(x, y, keys)
+
+            self._oldDragX, self._oldDragY = x, y
+
+        elif dragging and self._draggedShape and self._dragState == ContinueDraggingLeft:
+            # Continue dragging
+            self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
+            self._draggedShape.GetEventHandler().OnDragLeft(True, x, y, keys, self._draggedAttachment)
+            self._oldDragX, self._oldDragY = x, y
+
+        elif evt.LeftUp and self._draggedShape and self._dragState == ContinueDraggingLeft:
+            self._dragState = NoDragging
+            self._checkTolerance = True
+
+            self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
+            self._draggedShape.GetEventHandler().OnEndDragLeft(x, y, keys, self._draggedAttachment)
+            self._draggedShape = None
+
+        elif dragging and self._draggedShape and self._dragState == StartDraggingRight:
+            self._dragState = ContinueDraggingRight
+            if self._draggedShape.Draggable:
+                self._draggedShape.GetEventHandler().OnBeginDragRight(x, y, keys, self._draggedAttachment)
+            else:
+                self._draggedShape = None
+                self.OnBeginDragRight(x, y, keys)
+            self._oldDragX, self._oldDragY = x, y
+
+        elif dragging and self._draggedShape and self._dragState == ContinueDraggingRight:
+            # Continue dragging
+            self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
+            self._draggedShape.GetEventHandler().OnDragRight(True, x, y, keys, self._draggedAttachment)
+            self._oldDragX, self._oldDragY = x, y
+
+        elif evt.RightUp() and self._draggedShape and self._dragState == ContinueDraggingRight:
+            self._dragState = NoDragging
+            self._checkTolerance = True
+
+            self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
+            self._draggedShape.GetEventHandler().OnEndDragRight(x, y, keys, self._draggedAttachment)
+            self._draggedShape = None
+
+        # All following events sent to canvas, not object
+        elif dragging and not self._draggedShape and self._dragState == StartDraggingLeft:
+            self._dragState = ContinueDraggingLeft
+            self.OnBeginDragLeft(x, y, keys)
+            self._oldDragX, self._oldDragY = x, y
+
+        elif dragging and not self._draggedShape and self._dragState == ContinueDraggingLeft:
+            # Continue dragging
+            self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
+            self.OnDragLeft(True, x, y, keys)
+            self._oldDragX, self._oldDragY = x, y                
+
+        elif evt.LeftUp() and not self._draggedShape and self._dragState == ContinueDraggingLeft:
+            self._dragState = NoDragging
+            self._checkTolerance = True
+
+            self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
+            self.OnEndDragLeft(x, y, keys)
+            self._draggedShape = None
+
+        elif dragging and not self._draggedShape and self._dragState == StartDraggingRight:
+            self._dragState = ContinueDraggingRight
+            self.OnBeginDragRight(x, y, keys)
+            self._oldDragX, self._oldDragY = x, y
+
+        elif dragging and not self._draggedShape and self._dragState == ContinueDraggingRight:
+            # Continue dragging
+            self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
+            self.OnDragRight(True, x, y, keys)
+            self._oldDragX, self._oldDragY = x, y
+
+        elif evt.RightUp() and not self._draggedShape and self._dragState == ContinueDraggingRight:
+            self._dragState = NoDragging
+            self._checkTolerance = True
+
+            self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
+            self.OnEndDragRight(x, y, keys)
+            self._draggedShape = None
+
+        # Non-dragging events
+        elif evt.IsButton():
+            self._checkTolerance = True
+
+            # Find the nearest object
+            attachment = 0
+
+            nearest_object, attachment = self.FindShape(x, y)
+            if nearest_object: # Object event
+                if evt.LeftDown():
+                    self._draggedShape = nearest_object
+                    self._draggedAttachment = attachment
+                    self._dragState = StartDraggingLeft
+                    self._firstDragX = x
+                    self._firstDragY = y
+
+                elif evt.LeftUp():
+                    # N.B. Only register a click if the same object was
+                    # identified for down *and* up.
+                    if nearest_object == self._draggedShape:
+                        nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
+                    self._draggedShape = None
+                    self._dragState = NoDragging
+
+                elif evt.LeftDClick():
+                    nearest_object.GetEventHandler().OnLeftDoubleClick(x, y, keys, attachment)
+                    self._draggedShape = None
+                    self._dragState = NoDragging
+
+                elif evt.RightDown():
+                    self._draggedShape = nearest_object
+                    self._draggedAttachment = attachment
+                    self._dragState = StartDraggingRight
+                    self._firstDragX = x
+                    self._firstDragY = y
+
+                elif evt.RightUp():
+                    if nearest_object == self._draggedShape:
+                        nearest_object.GetEventHandler().OnRightClick(x, y, keys, attachment)
+                    self._draggedShape = None
+                    self._dragState = NoDragging
+
+            else: # Canvas event
+                if evt.LeftDown():
+                    self._draggedShape = None
+                    self._dragState = StartDraggingLeft
+                    self._firstDragX = x
+                    self._firstDragY = y
+
+                elif evt.LeftUp():
+                    self.OnLeftClick(x, y, keys)
+                    self._draggedShape = None
+                    self._dragState = NoDragging
+
+                elif evt.RightDown():
+                    self._draggedShape = None
+                    self._dragState = StartDraggingRight
+                    self._firstDragX = x
+                    self._firstDragY = y
+
+                elif evt.RightUp():
+                    self.OnRightClick(x, y, keys)
+                    self._draggedShape = None
+                    self._dragState = NoDragging
+
+    def FindShape(self, x, y, info = None, notObject = None):
+        nearest = 100000.0
+        nearest_attachment = 0
+        nearest_object = None
+
+        # Go backward through the object list, since we want:
+        # (a) to have the control points drawn LAST to overlay
+        #     the other objects
+        # (b) to find the control points FIRST if they exist
+
+        for object in self.GetDiagram().GetShapeList()[::-1]:
+            # First pass for lines, which might be inside a container, so we
+            # want lines to take priority over containers. This first loop
+            # could fail if we clickout side a line, so then we'll
+            # try other shapes.
+            if object.IsShown() and \
+               isinstance(object, LineShape) and \
+               object.HitTest(x, y) and \
+               ((info == None) or isinstance(object, info)) and \
+               (not notObject or not notObject.HasDescendant(object)):
+                temp_attachment, dist = object.HitTest(x, y)
+                # A line is trickier to spot than a normal object.
+                # For a line, since it's the diagonal of the box
+                # we use for the hit test, we may have several
+                # lines in the box and therefore we need to be able
+                # to specify the nearest point to the centre of the line
+                # as our hit criterion, to give the user some room for
+                # manouevre.
+                if dist<nearest:
+                    nearest = dist
+                    nearest_object = object
+                    nearest_attachment = temp_attachment
+
+        for object in self.GetDiagram().GetShapeList()[::-1]:
+            # On second pass, only ever consider non-composites or
+            # divisions. If children want to pass up control to
+            # the composite, that's up to them.
+            if (object.IsShown() and 
+                   (isinstance(object, DivisionShape) or 
+                    not isinstance(object, CompositeShape)) and 
+                    object.HitTest(x, y) and 
+                    (info == None or isinstance(object, info)) and 
+                    (not notObject or not notObject.HasDescendant(object))):
+                temp_attachment, dist = object.HitTest(x, y)
+                if not isinstance(object, LineShape):
+                    # If we've hit a container, and we have already
+                    # found a line in the first pass, then ignore
+                    # the container in case the line is in the container.
+                    # Check for division in case line straddles divisions
+                    # (i.e. is not wholly contained).
+                    if not nearest_object or not (isinstance(object, DivisionShape) or WhollyContains(object, nearest_object)):
+                        nearest_object = object
+                        nearest_attachment = temp_attachment
+                        break
+
+        return nearest_object, nearest_attachment
+
+    def AddShape(self, object, addAfter = None):
+        self.GetDiagram().AddShape(object, addAfter)
+
+    def InsertShape(self, object):
+        self.GetDiagram().InsertShape(object)
+
+    def RemoveShape(self, object):
+        self.GetDiagram().RemoveShape(object)
+
+    def GetQuickEditMode(self):
+        return self.GetDiagram().GetQuickEditMode()
+    
+    def Redraw(self, dc):
+        self.GetDiagram().Redraw(dc)
+
+    def Snap(self, x, y):
+        return self.GetDiagram().Snap(x, y)
+
+    def OnLeftClick(self, x, y, keys = 0):
+        pass
+
+    def OnRightClick(self, x, y, keys = 0):
+        pass
+
+    def OnDragLeft(self, draw, x, y, keys = 0):
+        pass
+
+    def OnBeginDragLeft(self, x, y, keys = 0):
+        pass
+
+    def OnEndDragLeft(self, x, y, keys = 0):
+        pass
+
+    def OnDragRight(self, draw, x, y, keys = 0):
+        pass
+
+    def OnBeginDragRight(self, x, y, keys = 0):
+        pass
+
+    def OnEndDragRight(self, x, y, keys = 0):
+        pass
diff --git a/wxPython/wx/lib/ogl/_composit.py b/wxPython/wx/lib/ogl/_composit.py
new file mode 100644 (file)
index 0000000..dbc20e6
--- /dev/null
@@ -0,0 +1,1430 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         composit.py
+# Purpose:      Composite class
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+import sys
+import wx
+
+from _basic import RectangleShape, Shape, ControlPoint
+from _oglmisc import *
+
+KEY_SHIFT, KEY_CTRL = 1, 2
+
+_objectStartX = 0.0
+_objectStartY = 0.0
+
+CONSTRAINT_CENTRED_VERTICALLY   = 1
+CONSTRAINT_CENTRED_HORIZONTALLY = 2
+CONSTRAINT_CENTRED_BOTH         = 3
+CONSTRAINT_LEFT_OF              = 4
+CONSTRAINT_RIGHT_OF             = 5
+CONSTRAINT_ABOVE                = 6
+CONSTRAINT_BELOW                = 7
+CONSTRAINT_ALIGNED_TOP          = 8
+CONSTRAINT_ALIGNED_BOTTOM       = 9
+CONSTRAINT_ALIGNED_LEFT         = 10
+CONSTRAINT_ALIGNED_RIGHT        = 11
+
+# Like aligned, but with the objects centred on the respective edge
+# of the reference object.
+CONSTRAINT_MIDALIGNED_TOP       = 12
+CONSTRAINT_MIDALIGNED_BOTTOM    = 13
+CONSTRAINT_MIDALIGNED_LEFT      = 14
+CONSTRAINT_MIDALIGNED_RIGHT     = 15
+
+
+# Backwards compatibility names.  These should be removed eventually.
+gyCONSTRAINT_CENTRED_VERTICALLY   = CONSTRAINT_CENTRED_VERTICALLY   
+gyCONSTRAINT_CENTRED_HORIZONTALLY = CONSTRAINT_CENTRED_HORIZONTALLY 
+gyCONSTRAINT_CENTRED_BOTH         = CONSTRAINT_CENTRED_BOTH         
+gyCONSTRAINT_LEFT_OF              = CONSTRAINT_LEFT_OF              
+gyCONSTRAINT_RIGHT_OF             = CONSTRAINT_RIGHT_OF             
+gyCONSTRAINT_ABOVE                = CONSTRAINT_ABOVE                
+gyCONSTRAINT_BELOW                = CONSTRAINT_BELOW                
+gyCONSTRAINT_ALIGNED_TOP          = CONSTRAINT_ALIGNED_TOP          
+gyCONSTRAINT_ALIGNED_BOTTOM       = CONSTRAINT_ALIGNED_BOTTOM       
+gyCONSTRAINT_ALIGNED_LEFT         = CONSTRAINT_ALIGNED_LEFT         
+gyCONSTRAINT_ALIGNED_RIGHT        = CONSTRAINT_ALIGNED_RIGHT        
+gyCONSTRAINT_MIDALIGNED_TOP       = CONSTRAINT_MIDALIGNED_TOP       
+gyCONSTRAINT_MIDALIGNED_BOTTOM    = CONSTRAINT_MIDALIGNED_BOTTOM    
+gyCONSTRAINT_MIDALIGNED_LEFT      = CONSTRAINT_MIDALIGNED_LEFT      
+gyCONSTRAINT_MIDALIGNED_RIGHT     = CONSTRAINT_MIDALIGNED_RIGHT     
+
+
+
+class ConstraintType(object):
+    def __init__(self, theType, theName, thePhrase):
+        self._type = theType
+        self._name = theName
+        self._phrase = thePhrase
+
+
+
+ConstraintTypes = [
+    [CONSTRAINT_CENTRED_VERTICALLY,
+        ConstraintType(CONSTRAINT_CENTRED_VERTICALLY, "Centre vertically", "centred vertically w.r.t.")],
+
+    [CONSTRAINT_CENTRED_HORIZONTALLY,
+        ConstraintType(CONSTRAINT_CENTRED_HORIZONTALLY, "Centre horizontally", "centred horizontally w.r.t.")],
+
+    [CONSTRAINT_CENTRED_BOTH,
+        ConstraintType(CONSTRAINT_CENTRED_BOTH, "Centre", "centred w.r.t.")],
+
+    [CONSTRAINT_LEFT_OF,
+        ConstraintType(CONSTRAINT_LEFT_OF, "Left of", "left of")],
+
+    [CONSTRAINT_RIGHT_OF,
+        ConstraintType(CONSTRAINT_RIGHT_OF, "Right of", "right of")],
+
+    [CONSTRAINT_ABOVE,
+        ConstraintType(CONSTRAINT_ABOVE, "Above", "above")],
+
+    [CONSTRAINT_BELOW,
+        ConstraintType(CONSTRAINT_BELOW, "Below", "below")],
+
+    # Alignment
+    [CONSTRAINT_ALIGNED_TOP,
+        ConstraintType(CONSTRAINT_ALIGNED_TOP, "Top-aligned", "aligned to the top of")],
+
+    [CONSTRAINT_ALIGNED_BOTTOM,
+        ConstraintType(CONSTRAINT_ALIGNED_BOTTOM, "Bottom-aligned", "aligned to the bottom of")],
+
+    [CONSTRAINT_ALIGNED_LEFT,
+        ConstraintType(CONSTRAINT_ALIGNED_LEFT, "Left-aligned", "aligned to the left of")],
+
+    [CONSTRAINT_ALIGNED_RIGHT,
+        ConstraintType(CONSTRAINT_ALIGNED_RIGHT, "Right-aligned", "aligned to the right of")],
+
+    # Mid-alignment
+    [CONSTRAINT_MIDALIGNED_TOP,
+        ConstraintType(CONSTRAINT_MIDALIGNED_TOP, "Top-midaligned", "centred on the top of")],
+
+    [CONSTRAINT_MIDALIGNED_BOTTOM,
+        ConstraintType(CONSTRAINT_MIDALIGNED_BOTTOM, "Bottom-midaligned", "centred on the bottom of")],
+
+    [CONSTRAINT_MIDALIGNED_LEFT,
+        ConstraintType(CONSTRAINT_MIDALIGNED_LEFT, "Left-midaligned", "centred on the left of")],
+
+    [CONSTRAINT_MIDALIGNED_RIGHT,
+        ConstraintType(CONSTRAINT_MIDALIGNED_RIGHT, "Right-midaligned", "centred on the right of")]
+    ]
+
+
+
+
+class Constraint(object):
+    """A Constraint object helps specify how child shapes are laid out with
+    respect to siblings and parents.
+
+    Derived from:
+      wxObject
+    """
+    def __init__(self, type, constraining, constrained):
+        self._xSpacing = 0.0
+        self._ySpacing = 0.0
+
+        self._constraintType = type
+        self._constraintingObject = constraining
+
+        self._constraintId = 0
+        self._constraintName="noname"
+
+        self._constrainedObjects = constrained[:]
+
+    def __repr__(self):
+        return "<%s.%s>" % (self.__class__.__module__, self.__class__.__name__)
+
+    def SetSpacing(self, x, y):
+        """Sets the horizontal and vertical spacing for the constraint."""
+        self._xSpacing = x
+        self._ySpacing = y
+        
+    def Equals(self, a, b):
+        """Return TRUE if x and y are approximately equal (for the purposes
+        of evaluating the constraint).
+        """
+        marg = 0.5
+
+        return b <= a + marg and b >= a-marg
+
+    def Evaluate(self):
+        """Evaluate this constraint and return TRUE if anything changed."""
+        maxWidth, maxHeight = self._constraintingObject.GetBoundingBoxMax()
+        minWidth, minHeight = self._constraintingObject.GetBoundingBoxMin()
+        x = self._constraintingObject.GetX()
+        y = self._constraintingObject.GetY()
+
+        dc = wx.ClientDC(self._constraintingObject.GetCanvas())
+        self._constraintingObject.GetCanvas().PrepareDC(dc)
+
+        if self._constraintType == CONSTRAINT_CENTRED_VERTICALLY:
+            n = len(self._constrainedObjects)
+            totalObjectHeight = 0.0
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                totalObjectHeight += height2
+
+            # Check if within the constraining object...
+            if totalObjectHeight + (n + 1) * self._ySpacing <= minHeight:
+                spacingY = (minHeight-totalObjectHeight) / (n + 1)
+                startY = y-minHeight / 2
+            else: # Otherwise, use default spacing
+                spacingY = self._ySpacing
+                startY = y-(totalObjectHeight + (n + 1) * spacingY) / 2
+
+            # Now position the objects
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                startY += spacingY + height2 / 2
+                if not self.Equals(startY, constrainedObject.GetY()):
+                    constrainedObject.Move(dc, constrainedObject.GetX(), startY, False)
+                    changed = True
+                startY += height2 / 2
+            return changed
+        elif self._constraintType == CONSTRAINT_CENTRED_HORIZONTALLY:
+            n = len(self._constrainedObjects)
+            totalObjectWidth = 0.0
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                totalObjectWidth += width2
+
+            # Check if within the constraining object...
+            if totalObjectWidth + (n + 1) * self._xSpacing<minWidth:
+                spacingX = (minWidth-totalObjectWidth) / (n + 1)
+                startX = x-minWidth / 2
+            else: # Otherwise, use default spacing
+                spacingX = self._xSpacing
+                startX = x-(totalObjectWidth + (n + 1) * spacingX) / 2
+
+            # Now position the objects
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                startX += spacingX + width2 / 2
+                if not self.Equals(startX, constrainedObject.GetX()):
+                    constrainedObject.Move(dc, startX, constrainedObject.GetY(), False)
+                    changed = True
+                startX += width2 / 2
+            return changed
+        elif self._constraintType == CONSTRAINT_CENTRED_BOTH:
+            n = len(self._constrainedObjects)
+            totalObjectWidth = 0.0
+            totalObjectHeight = 0.0
+
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                totalObjectWidth += width2
+                totalObjectHeight += height2
+
+            # Check if within the constraining object...
+            if totalObjectHeight + (n + 1) * self._xSpacing <= minWidth:
+                spacingX = (minWidth-totalObjectWidth) / (n + 1)
+                startX = x-minWidth / 2
+            else: # Otherwise, use default spacing
+                spacingX = self._xSpacing
+                startX = x-(totalObjectWidth + (n + 1) * spacingX) / 2
+
+            # Check if within the constraining object...
+            if totalObjectHeight + (n + 1) * self._ySpacing <= minHeight:
+                spacingY = (minHeight-totalObjectHeight) / (n + 1)
+                startY = y-minHeight / 2
+            else: # Otherwise, use default spacing
+                spacingY = self._ySpacing
+                startY = y-(totalObjectHeight + (n + 1) * spacingY) / 2
+
+            # Now position the objects
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                startX += spacingX + width2 / 2
+                startY += spacingY + height2 / 2
+
+                if not self.Equals(startX, constrainedObject.GetX()) or not self.Equals(startY, constrainedObject.GetY()):
+                    constrainedObject.Move(dc, startX, startY, False)
+                    changed = True
+
+                startX += width2 / 2
+                startY += height2 / 2
+            return changed
+        elif self._constraintType == CONSTRAINT_LEFT_OF:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+
+                x3 = x-minWidth / 2-width2 / 2-self._xSpacing
+                if not self.Equals(x3, constrainedObject.GetX()):
+                    changed = True
+                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
+            return changed
+        elif self._constraintType == CONSTRAINT_RIGHT_OF:
+            changed = False
+
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                x3 = x + minWidth / 2 + width2 / 2 + self._xSpacing
+                if not self.Equals(x3, constrainedObject.GetX()):
+                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
+                    changed = True
+            return changed
+        elif self._constraintType == CONSTRAINT_ABOVE:
+            changed = False
+
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+
+                y3 = y-minHeight / 2-height2 / 2-self._ySpacing
+                if not self.Equals(y3, constrainedObject.GetY()):
+                    changed = True
+                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
+            return changed
+        elif self._constraintType == CONSTRAINT_BELOW:
+            changed = False
+
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+
+                y3 = y + minHeight / 2 + height2 / 2 + self._ySpacing
+                if not self.Equals(y3, constrainedObject.GetY()):
+                    changed = True
+                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
+            return changed
+        elif self._constraintType == CONSTRAINT_ALIGNED_LEFT:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                x3 = x-minWidth / 2 + width2 / 2 + self._xSpacing
+                if not self.Equals(x3, constrainedObject.GetX()):
+                    changed = True
+                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
+            return changed
+        elif self._constraintType == CONSTRAINT_ALIGNED_RIGHT:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                x3 = x + minWidth / 2-width2 / 2-self._xSpacing
+                if not self.Equals(x3, constrainedObject.GetX()):
+                    changed = True
+                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
+            return changed
+        elif self._constraintType == CONSTRAINT_ALIGNED_TOP:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                y3 = y-minHeight / 2 + height2 / 2 + self._ySpacing
+                if not self.Equals(y3, constrainedObject.GetY()):
+                    changed = True
+                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
+            return changed
+        elif self._constraintType == CONSTRAINT_ALIGNED_BOTTOM:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                width2, height2 = constrainedObject.GetBoundingBoxMax()
+                y3 = y + minHeight / 2-height2 / 2-self._ySpacing
+                if not self.Equals(y3, constrainedObject.GetY()):
+                    changed = True
+                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
+            return changed
+        elif self._constraintType == CONSTRAINT_MIDALIGNED_LEFT:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                x3 = x-minWidth / 2
+                if not self.Equals(x3, constrainedObject.GetX()):
+                    changed = True
+                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
+            return changed
+        elif self._constraintType == CONSTRAINT_MIDALIGNED_RIGHT:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                x3 = x + minWidth / 2
+                if not self.Equals(x3, constrainedObject.GetX()):
+                    changed = True
+                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
+            return changed
+        elif self._constraintType == CONSTRAINT_MIDALIGNED_TOP:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                y3 = y-minHeight / 2
+                if not self.Equals(y3, constrainedObject.GetY()):
+                    changed = True
+                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
+            return changed
+        elif self._constraintType == CONSTRAINT_MIDALIGNED_BOTTOM:
+            changed = False
+            for constrainedObject in self._constrainedObjects:
+                y3 = y + minHeight / 2
+                if not self.Equals(y3, constrainedObject.GetY()):
+                    changed = True
+                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
+            return changed
+        
+        return False
+    
+OGLConstraint = wx._core._deprecated(Constraint,
+                     "The OGLConstraint name is deprecated, use `ogl.Constraint` instead.")
+
+
+class CompositeShape(RectangleShape):
+    """This is an object with a list of child objects, and a list of size
+    and positioning constraints between the children.
+
+    Derived from:
+      wxRectangleShape
+    """
+    def __init__(self):
+        RectangleShape.__init__(self, 100.0, 100.0)
+
+        self._oldX = self._xpos
+        self._oldY = self._ypos
+
+        self._constraints = [] 
+        self._divisions = [] # In case it's a container
+        
+    def OnDraw(self, dc):
+        x1 = self._xpos-self._width / 2
+        y1 = self._ypos-self._height / 2
+
+        if self._shadowMode != SHADOW_NONE:
+            if self._shadowBrush:
+                dc.SetBrush(self._shadowBrush)
+            dc.SetPen(wx.Pen(wx.WHITE, 1, wx.TRANSPARENT))
+
+            if self._cornerRadius:
+                dc.DrawRoundedRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height, self._cornerRadius)
+            else:
+                dc.DrawRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height)
+
+        # For debug purposes /pi
+        #dc.DrawRectangle(x1, y1, self._width, self._height)
+        
+    def OnDrawContents(self, dc):
+        for object in self._children:
+            object.Draw(dc)
+            object.DrawLinks(dc)
+
+        Shape.OnDrawContents(self, dc)
+
+    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
+        diffX = x-old_x
+        diffY = y-old_y
+
+        for object in self._children:
+            object.Erase(dc)
+            object.Move(dc, object.GetX() + diffX, object.GetY() + diffY, display)
+
+        return True
+
+    def OnErase(self, dc):
+        RectangleShape.OnErase(self, dc)
+        for object in self._children:
+            object.Erase(dc)
+
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        xx, yy = self._canvas.Snap(x, y)
+        offsetX = xx - _objectStartX
+        offsetY = yy - _objectStartY
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        self.GetEventHandler().OnDrawOutline(dc, self.GetX() + offsetX, self.GetY() + offsetY, self.GetWidth(), self.GetHeight())
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        global _objectStartX, _objectStartY
+
+        _objectStartX = x
+        _objectStartY = y
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        #self.Erase(dc)
+        
+        dc.SetLogicalFunction(OGLRBLF)
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        self._canvas.CaptureMouse()
+
+        xx, yy = self._canvas.Snap(x, y)
+        offsetX = xx - _objectStartX
+        offsetY = yy - _objectStartY
+
+        self.GetEventHandler().OnDrawOutline(dc, self.GetX() + offsetX, self.GetY() + offsetY, self.GetWidth(), self.GetHeight())
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        if self._canvas.HasCapture():
+            self._canvas.ReleaseMouse()
+
+        if not self._draggable:
+            if self._parent:
+                self._parent.GetEventHandler().OnEndDragLeft(x, y, keys, 0)
+            return
+            
+        self.Erase(dc)
+        
+        dc.SetLogicalFunction(wx.COPY)
+        
+        xx, yy = self._canvas.Snap(x, y)
+        offsetX = xx - _objectStartX
+        offsetY = yy - _objectStartY
+
+        self.Move(dc, self.GetX() + offsetX, self.GetY() + offsetY)
+
+        if self._canvas and not self._canvas.GetQuickEditMode():
+            self._canvas.Redraw(dc)
+
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        # If we get a ctrl-right click, this means send the message to
+        # the division, so we can invoke a user interface for dealing
+        # with regions.
+        if keys & KEY_CTRL:
+            for division in self._divisions:
+                hit = division.HitTest(x, y)
+                if hit:
+                    division.GetEventHandler().OnRightClick(x, y, keys, hit[0])
+                    break
+
+    def SetSize(self, w, h, recursive = True):
+        self.SetAttachmentSize(w, h)
+
+        xScale = w / max(1, self.GetWidth())
+        yScale = h / max(1, self.GetHeight())
+
+        self._width = w
+        self._height = h
+
+        if not recursive:
+            return
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        for object in self._children:
+            # Scale the position first
+            newX = (object.GetX()-self.GetX()) * xScale + self.GetX()
+            newY = (object.GetY()-self.GetY()) * yScale + self.GetY()
+            object.Show(False)
+            object.Move(dc, newX, newY)
+            object.Show(True)
+
+            # Now set the scaled size
+            xbound, ybound = object.GetBoundingBoxMax()
+            if not object.GetFixedWidth():
+                xbound *= xScale
+            if not object.GetFixedHeight():
+                ybound *= yScale
+            object.SetSize(xbound, ybound)
+
+        self.SetDefaultRegionSize()
+
+    def AddChild(self, child, addAfter = None):
+        """Adds a child shape to the composite.
+
+        If addAfter is not None, the shape will be added after this shape.
+        """
+        self._children.append(child)
+        child.SetParent(self)
+        if self._canvas:
+            # Ensure we add at the right position
+            if addAfter:
+                child.RemoveFromCanvas(self._canvas)
+            child.AddToCanvas(self._canvas, addAfter)
+
+    def RemoveChild(self, child):
+        """Removes the child from the composite and any constraint
+        relationships, but does not delete the child.
+        """
+        self._children.remove(child)
+        self._divisions.remove(child)
+        self.RemoveChildFromConstraints(child)
+        child.SetParent(None)
+
+    def DeleteConstraintsInvolvingChild(self, child):
+        """This function deletes constraints which mention the given child.
+
+        Used when deleting a child from the composite.
+        """
+        for constraint in self._constraints:
+            if constraint._constrainingObject == child or child in constraint._constrainedObjects:
+                self._constraints.remove(constraint)
+
+    def RemoveChildFromConstraints(self, child):
+        for constraint in self._constraints:
+            if child in constraint._constrainedObjects:
+                constraint._constrainedObjects.remove(child)
+            if constraint._constrainingObject == child:
+                constraint._constrainingObject = None
+
+            # Delete the constraint if no participants left
+            if not constraint._constrainingObject:
+                self._constraints.remove(constraint)
+
+    def AddConstraint(self, constraint):
+        """Adds a constraint to the composite."""
+        self._constraints.append(constraint)
+        if constraint._constraintId == 0:
+            constraint._constraintId = wx.NewId()
+        return constraint
+
+    def AddSimpleConstraint(self, type, constraining, constrained):
+        """Add a constraint of the given type to the composite.
+
+        constraining is the shape doing the constraining
+        constrained is a list of shapes being constrained
+        """
+        constraint = Constraint(type, constraining, constrained)
+        if constraint._constraintId == 0:
+            constraint._constraintId = wx.NewId()
+        self._constraints.append(constraint)
+        return constraint
+
+    def FindConstraint(self, cId):
+        """Finds the constraint with the given id.
+
+        Returns a tuple of the constraint and the actual composite the
+        constraint was in, in case that composite was a descendant of
+        this composit.
+
+        Returns None if not found.
+        """
+        for constraint in self._constraints:
+            if constraint._constraintId == cId:
+                return constraint, self
+
+        # If not found, try children
+        for child in self._children:
+            if isinstance(child, CompositeShape):
+                constraint = child.FindConstraint(cId)
+                if constraint:
+                    return constraint[0], child
+
+        return None
+
+    def DeleteConstraint(self, constraint):
+        """Deletes constraint from composite."""
+        self._constraints.remove(constraint)
+
+    def CalculateSize(self):
+        """Calculates the size and position of the composite based on
+        child sizes and positions.
+        """
+        maxX=-999999.9
+        maxY=-999999.9
+        minX = 999999.9
+        minY = 999999.9
+
+        for child in self._children:
+            # Recalculate size of composite objects because may not conform
+            # to size it was set to - depends on the children.
+            if isinstance(child, CompositeShape):
+                child.CalculateSize()
+
+            w, h = child.GetBoundingBoxMax()
+            if child.GetX() + w / 2>maxX:
+                maxX = child.GetX() + w / 2
+            if child.GetX()-w / 2<minX:
+                minX = child.GetX()-w / 2
+            if child.GetY() + h / 2>maxY:
+                maxY = child.GetY() + h / 2
+            if child.GetY()-h / 2<minY:
+                minY = child.GetY()-h / 2
+
+        self._width = maxX-minX
+        self._height = maxY-minY
+        self._xpos = self._width / 2 + minX
+        self._ypos = self._height / 2 + minY
+
+    def Recompute(self):
+        """Recomputes any constraints associated with the object. If FALSE is
+        returned, the constraints could not be satisfied (there was an
+        inconsistency).
+        """
+        noIterations = 0
+        changed = True
+        while changed and noIterations<500:
+            changed = self.Constrain()
+            noIterations += 1
+
+        return not changed
+
+    def Constrain(self):
+        self.CalculateSize()
+
+        changed = False
+        for child in self._children:
+            if isinstance(child, CompositeShape) and child.Constrain():
+                changed = True
+
+        for constraint in self._constraints:
+            if constraint.Evaluate():
+                changed = True
+
+        return changed
+
+    def MakeContainer(self):
+        """Makes this composite into a container by creating one child
+        DivisionShape.
+        """
+        division = self.OnCreateDivision()
+        self._divisions.append(division)
+        self.AddChild(division)
+
+        division.SetSize(self._width, self._height)
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+        
+        division.Move(dc, self.GetX(), self.GetY())
+        self.Recompute()
+        division.Show(True)
+
+    def OnCreateDivision(self):
+        return DivisionShape()
+
+    def FindContainerImage(self):
+        """Finds the image used to visualize a container. This is any child of
+        the composite that is not in the divisions list.
+        """
+        for child in self._children:
+            if child in self._divisions:
+                return child
+
+        return None
+
+    def ContainsDivision(self, division):
+        """Returns TRUE if division is a descendant of this container."""
+        if division in self._divisions:
+            return True
+
+        for child in self._children:
+            if isinstance(child, CompositeShape):
+                return child.ContainsDivision(division)
+
+        return False
+
+    def GetDivisions(self):
+        """Return the list of divisions."""
+        return self._divisions
+
+    def GetConstraints(self):
+        """Return the list of constraints."""
+        return self._constraints
+
+
+#  A division object is a composite with special properties,
+#  to be used for containment. It's a subdivision of a container.
+#  A containing node image consists of a composite with a main child shape
+#  such as rounded rectangle, plus a list of division objects.
+#  It needs to be a composite because a division contains pieces
+#  of diagram.
+#  NOTE a container has at least one wxDivisionShape for consistency.
+#  This can be subdivided, so it turns into two objects, then each of
+#  these can be subdivided, etc.
+
+DIVISION_SIDE_NONE      =0
+DIVISION_SIDE_LEFT      =1
+DIVISION_SIDE_TOP       =2
+DIVISION_SIDE_RIGHT     =3
+DIVISION_SIDE_BOTTOM    =4
+
+originalX = 0.0
+originalY = 0.0
+originalW = 0.0
+originalH = 0.0
+
+
+
+class DivisionControlPoint(ControlPoint):
+    def __init__(self, the_canvas, object, size, the_xoffset, the_yoffset, the_type):
+        ControlPoint.__init__(self, the_canvas, object, size, the_xoffset, the_yoffset, the_type)
+        self.SetEraseObject(False)
+
+    # Implement resizing of canvas object
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        ControlPoint.OnDragLeft(self, draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        global originalX, originalY, originalW, originalH
+
+        originalX = self._shape.GetX()
+        originalY = self._shape.GetY()
+        originalW = self._shape.GetWidth()
+        originalH = self._shape.GetHeight()
+
+        ControlPoint.OnBeginDragLeft(self, x, y, keys, attachment)
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        ControlPoint.OnEndDragLeft(self, x, y, keys, attachment)
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        division = self._shape
+        divisionParent = division.GetParent()
+
+        # Need to check it's within the bounds of the parent composite
+        x1 = divisionParent.GetX()-divisionParent.GetWidth() / 2
+        y1 = divisionParent.GetY()-divisionParent.GetHeight() / 2
+        x2 = divisionParent.GetX() + divisionParent.GetWidth() / 2
+        y2 = divisionParent.GetY() + divisionParent.GetHeight() / 2
+
+        # Need to check it has not made the division zero or negative
+        # width / height
+        dx1 = division.GetX()-division.GetWidth() / 2
+        dy1 = division.GetY()-division.GetHeight() / 2
+        dx2 = division.GetX() + division.GetWidth() / 2
+        dy2 = division.GetY() + division.GetHeight() / 2
+
+        success = True
+        if division.GetHandleSide() == DIVISION_SIDE_LEFT:
+            if x <= x1 or x >= x2 or x >= dx2:
+                success = False
+            # Try it out first...
+            elif not division.ResizeAdjoining(DIVISION_SIDE_LEFT, x, True):
+                success = False
+            else:
+                division.ResizeAdjoining(DIVISION_SIDE_LEFT, x, False)
+        elif division.GetHandleSide() == DIVISION_SIDE_TOP:
+            if y <= y1 or y >= y2 or y >= dy2:
+                success = False
+            elif not division.ResizeAdjoining(DIVISION_SIDE_TOP, y, True):
+                success = False
+            else:
+                division.ResizingAdjoining(DIVISION_SIDE_TOP, y, False)
+        elif division.GetHandleSide() == DIVISION_SIDE_RIGHT:
+            if x <= x1 or x >= x2 or x <= dx1:
+                success = False
+            elif not division.ResizeAdjoining(DIVISION_SIDE_RIGHT, x, True):
+                success = False
+            else:
+                division.ResizeAdjoining(DIVISION_SIDE_RIGHT, x, False)
+        elif division.GetHandleSide() == DIVISION_SIDE_BOTTOM:
+            if y <= y1 or y >= y2 or y <= dy1:
+                success = False
+            elif not division.ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, True):
+                success = False
+            else:
+                division.ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, False)
+
+        if not success:
+            division.SetSize(originalW, originalH)
+            division.Move(dc, originalX, originalY)
+
+        divisionParent.Draw(dc)
+        division.GetEventHandler().OnDrawControlPoints(dc)
+
+
+
+DIVISION_MENU_SPLIT_HORIZONTALLY    =1
+DIVISION_MENU_SPLIT_VERTICALLY      =2
+DIVISION_MENU_EDIT_LEFT_EDGE        =3
+DIVISION_MENU_EDIT_TOP_EDGE         =4
+DIVISION_MENU_EDIT_RIGHT_EDGE       =5
+DIVISION_MENU_EDIT_BOTTOM_EDGE      =6
+DIVISION_MENU_DELETE_ALL            =7
+    
+
+
+class PopupDivisionMenu(wx.Menu):
+    def __init__(self):
+        wx.Menu.__init__(self)
+        self.Append(DIVISION_MENU_SPLIT_HORIZONTALLY,"Split horizontally")
+        self.Append(DIVISION_MENU_SPLIT_VERTICALLY,"Split vertically")
+        self.AppendSeparator()
+        self.Append(DIVISION_MENU_EDIT_LEFT_EDGE,"Edit left edge")
+        self.Append(DIVISION_MENU_EDIT_TOP_EDGE,"Edit top edge")
+
+        wx.EVT_MENU_RANGE(self, DIVISION_MENU_SPLIT_HORIZONTALLY, DIVISION_MENU_EDIT_BOTTOM_EDGE, self.OnMenu)
+
+    def SetClientData(self, data):
+        self._clientData = data
+
+    def GetClientData(self):
+        return self._clientData
+    
+    def OnMenu(self, event):
+        division = self.GetClientData()
+        if event.GetId() == DIVISION_MENU_SPLIT_HORIZONTALLY:
+            division.Divide(wx.HORIZONTAL)
+        elif event.GetId() == DIVISION_MENU_SPLIT_VERTICALLY:
+            division.Divide(wx.VERTICAL)
+        elif event.GetId() == DIVISION_MENU_EDIT_LEFT_EDGE:
+            division.EditEdge(DIVISION_SIDE_LEFT)
+        elif event.GetId() == DIVISION_MENU_EDIT_TOP_EDGE:
+            division.EditEdge(DIVISION_SIDE_TOP)
+            
+
+
+class DivisionShape(CompositeShape):
+    """A division shape is like a composite in that it can contain further
+    objects, but is used exclusively to divide another shape into regions,
+    or divisions. A wxDivisionShape is never free-standing.
+
+    Derived from:
+      wxCompositeShape
+    """
+    def __init__(self):
+        CompositeShape.__init__(self)
+        self.SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT)
+        self.SetCentreResize(False)
+        self.SetAttachmentMode(True)
+        self._leftSide = None
+        self._rightSide = None
+        self._topSide = None
+        self._bottomSide = None
+        self._handleSide = DIVISION_SIDE_NONE
+        self._leftSidePen = wx.BLACK_PEN
+        self._topSidePen = wx.BLACK_PEN
+        self._leftSideColour="BLACK"
+        self._topSideColour="BLACK"
+        self._leftSideStyle="Solid"
+        self._topSideStyle="Solid"
+        self.ClearRegions()
+
+    def SetLeftSide(self, shape):
+        """Set the the division on the left side of this division."""
+        self._leftSide = shape
+
+    def SetTopSide(self, shape):
+        """Set the the division on the top side of this division."""
+        self._topSide = shape
+        
+    def SetRightSide(self, shape):
+        """Set the the division on the right side of this division."""
+        self._rightSide = shape
+        
+    def SetBottomSide(self, shape):
+        """Set the the division on the bottom side of this division."""
+        self._bottomSide = shape
+
+    def GetLeftSide(self):
+        """Return the division on the left side of this division."""
+        return self._leftSide
+    
+    def GetTopSide(self):
+        """Return the division on the top side of this division."""
+        return self._topSide
+    
+    def GetRightSide(self):
+        """Return the division on the right side of this division."""
+        return self._rightSide
+    
+    def GetBottomSide(self):
+        """Return the division on the bottom side of this division."""
+        return self._bottomSide
+
+    def SetHandleSide(self, side):
+        """Sets the side which the handle appears on.
+
+        Either DIVISION_SIDE_LEFT or DIVISION_SIDE_TOP.
+        """
+        self._handleSide = side
+
+    def GetHandleSide(self):
+        """Return the side which the handle appears on."""
+        return self._handleSide
+
+    def SetLeftSidePen(self, pen):
+        """Set the colour for drawing the left side of the division."""
+        self._leftSidePen = pen
+        
+    def SetTopSidePen(self, pen):
+        """Set the colour for drawing the top side of the division."""
+        self._topSidePen = pen
+        
+    def GetLeftSidePen(self):
+        """Return the pen used for drawing the left side of the division."""
+        return self._leftSidePen
+
+    def GetTopSidePen(self):
+        """Return the pen used for drawing the top side of the division."""
+        return self._topSidePen
+
+    def GetLeftSideColour(self):
+        """Return the colour used for drawing the left side of the division."""
+        return self._leftSideColour
+    
+    def GetTopSideColour(self):
+        """Return the colour used for drawing the top side of the division."""
+        return self._topSideColour
+
+    def SetLeftSideColour(self, colour):
+        """Set the colour for drawing the left side of the division."""
+        self._leftSideColour = colour
+        
+    def SetTopSideColour(self, colour):
+        """Set the colour for drawing the top side of the division."""
+        self._topSideColour = colour
+        
+    def GetLeftSideStyle(self):
+        """Return the style used for the left side of the division."""
+        return self._leftSideStyle
+    
+    def GetTopSideStyle(self):
+        """Return the style used for the top side of the division."""
+        return self._topSideStyle
+
+    def SetLeftSideStyle(self, style):
+        self._leftSideStyle = style
+        
+    def SetTopSideStyle(self, style):
+        self._lefttopStyle = style
+        
+    def OnDraw(self, dc):
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        dc.SetBackgroundMode(wx.TRANSPARENT)
+
+        x1 = self.GetX()-self.GetWidth() / 2
+        y1 = self.GetY()-self.GetHeight() / 2
+        x2 = self.GetX() + self.GetWidth() / 2
+        y2 = self.GetY() + self.GetHeight() / 2
+
+        # Should subtract 1 pixel if drawing under Windows
+        if sys.platform[:3]=="win":
+            y2 -= 1
+
+        if self._leftSide:
+            dc.SetPen(self._leftSidePen)
+            dc.DrawLine(x1, y2, x1, y1)
+
+        if self._topSide:
+            dc.SetPen(self._topSidePen)
+            dc.DrawLine(x1, y1, x2, y1)
+
+        # For testing purposes, draw a rectangle so we know
+        # how big the division is.
+        #dc.SetBrush(wx.RED_BRUSH)
+        #dc.DrawRectangle(x1, y1, self.GetWidth(), self.GetHeight())
+        
+    def OnDrawContents(self, dc):
+        CompositeShape.OnDrawContents(self, dc)
+
+    def OnMovePre(self, dc, x, y, oldx, oldy, display = True):
+        diffX = x-oldx
+        diffY = y-oldy
+        for object in self._children:
+            object.Erase(dc)
+            object.Move(dc, object.GetX() + diffX, object.GetY() + diffY, display)
+        return True
+
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+                self._parent.GetEventHandler().OnDragLeft(draw, x, y, keys, attachment)
+            return
+        Shape.OnDragLeft(self, draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+                self._parent.GetEventHandler().OnBeginDragLeft(x, y, keys, attachment)
+            return
+        Shape.OnBeginDragLeft(x, y, keys, attachment)
+            
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        if self._canvas.HasCapture():
+            self._canvas.ReleaseMouse()
+        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+                self._parent.GetEventHandler().OnEndDragLeft(x, y, keys, attachment)
+            return
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(wx.COPY)
+
+        self._xpos, self._ypos = self._canvas.Snap(self._xpos, self._ypos)
+        self.GetEventHandler().OnMovePre(dc, x, y, self._oldX, self._oldY)
+
+        self.ResetControlPoints()
+        self.Draw(dc)
+        self.MoveLinks(dc)
+        self.GetEventHandler().OnDrawControlPoints(dc)
+
+        if self._canvas and not self._canvas.GetQuickEditMode():
+            self._canvas.Redraw(dc)
+
+    def SetSize(self, w, h, recursive = True):
+        self._width = w
+        self._height = h
+        RectangleShape.SetSize(self, w, h, recursive)
+
+    def CalculateSize(self):
+        pass
+
+    # Experimental
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        if keys & KEY_CTRL:
+            self.PopupMenu(x, y)
+        else:
+            if self._parent:
+                hit = self._parent.HitTest(x, y)
+                if hit:
+                    attachment, dist = hit
+                self._parent.GetEventHandler().OnRightClick(x, y, keys, attachment)
+
+    # Divide wx.HORIZONTALly or wx.VERTICALly
+    def Divide(self, direction):
+        """Divide this division into two further divisions,
+        horizontally (direction is wxHORIZONTAL) or
+        vertically (direction is wxVERTICAL).
+        """
+        # Calculate existing top-left, bottom-right
+        x1 = self.GetX()-self.GetWidth() / 2
+        y1 = self.GetY()-self.GetHeight() / 2
+
+        compositeParent = self.GetParent()
+        oldWidth = self.GetWidth()
+        oldHeight = self.GetHeight()
+        if self.Selected():
+            self.Select(False)
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        if direction == wx.VERTICAL:
+            # Dividing vertically means notionally putting a horizontal
+            # line through it.
+            # Break existing piece into two.
+            newXPos1 = self.GetX()
+            newYPos1 = y1 + self.GetHeight() / 4
+            newXPos2 = self.GetX()
+            newYPos2 = y1 + 3 * self.GetHeight() / 4
+            newDivision = compositeParent.OnCreateDivision()
+            newDivision.Show(True)
+
+            self.Erase(dc)
+
+            # Anything adjoining the bottom of this division now adjoins the
+            # bottom of the new division.
+            for obj in compositeParent.GetDivisions():
+                if obj.GetTopSide() == self:
+                    obj.SetTopSide(newDivision)
+
+            newDivision.SetTopSide(self)
+            newDivision.SetBottomSide(self._bottomSide)
+            newDivision.SetLeftSide(self._leftSide)
+            newDivision.SetRightSide(self._rightSide)
+            self._bottomSide = newDivision
+
+            compositeParent.GetDivisions().append(newDivision)
+
+            # CHANGE: Need to insert this division at start of divisions in the
+            # object list, because e.g.:
+            # 1) Add division
+            # 2) Add contained object
+            # 3) Add division
+            # Division is now receiving mouse events _before_ the contained
+            # object, because it was added last (on top of all others)
+
+            # Add after the image that visualizes the container
+            compositeParent.AddChild(newDivision, compositeParent.FindContainerImage())
+
+            self._handleSide = DIVISION_SIDE_BOTTOM
+            newDivision.SetHandleSide(DIVISION_SIDE_TOP)
+
+            self.SetSize(oldWidth, oldHeight / 2)
+            self.Move(dc, newXPos1, newYPos1)
+
+            newDivision.SetSize(oldWidth, oldHeight / 2)
+            newDivision.Move(dc, newXPos2, newYPos2)
+        else:
+            # Dividing horizontally means notionally putting a vertical line
+            # through it.
+            # Break existing piece into two.
+            newXPos1 = x1 + self.GetWidth() / 4
+            newYPos1 = self.GetY()
+            newXPos2 = x1 + 3 * self.GetWidth() / 4
+            newYPos2 = self.GetY()
+            newDivision = compositeParent.OnCreateDivision()
+            newDivision.Show(True)
+
+            self.Erase(dc)
+
+            # Anything adjoining the left of this division now adjoins the
+            # left of the new division.
+            for obj in compositeParent.GetDivisions():
+                if obj.GetLeftSide() == self:
+                    obj.SetLeftSide(newDivision)
+
+            newDivision.SetTopSide(self._topSide)
+            newDivision.SetBottomSide(self._bottomSide)
+            newDivision.SetLeftSide(self)
+            newDivision.SetRightSide(self._rightSide)
+            self._rightSide = newDivision
+
+            compositeParent.GetDivisions().append(newDivision)
+            compositeParent.AddChild(newDivision, compositeParent.FindContainerImage())
+
+            self._handleSide = DIVISION_SIDE_RIGHT
+            newDivision.SetHandleSide(DIVISION_SIDE_LEFT)
+
+            self.SetSize(oldWidth / 2, oldHeight)
+            self.Move(dc, newXPos1, newYPos1)
+
+            newDivision.SetSize(oldWidth / 2, oldHeight)
+            newDivision.Move(dc, newXPos2, newYPos2)
+
+        if compositeParent.Selected():
+            compositeParent.DeleteControlPoints(dc)
+            compositeParent.MakeControlPoints()
+            compositeParent.MakeMandatoryControlPoints()
+
+        compositeParent.Draw(dc)
+        return True
+
+    def MakeControlPoints(self):
+        self.MakeMandatoryControlPoints()
+
+    def MakeMandatoryControlPoints(self):
+        maxX, maxY = self.GetBoundingBoxMax()
+        x = y = 0.0
+        direction = 0
+
+        if self._handleSide == DIVISION_SIDE_LEFT:
+            x=-maxX / 2
+            direction = CONTROL_POINT_HORIZONTAL
+        elif self._handleSide == DIVISION_SIDE_TOP:
+            y=-maxY / 2
+            direction = CONTROL_POINT_VERTICAL
+        elif self._handleSide == DIVISION_SIDE_RIGHT:
+            x = maxX / 2
+            direction = CONTROL_POINT_HORIZONTAL
+        elif self._handleSide == DIVISION_SIDE_BOTTOM:
+            y = maxY / 2
+            direction = CONTROL_POINT_VERTICAL
+
+        if self._handleSide != DIVISION_SIDE_NONE:
+            control = DivisionControlPoint(self._canvas, self, CONTROL_POINT_SIZE, x, y, direction)
+            self._canvas.AddShape(control)
+            self._controlPoints.append(control)
+
+    def ResetControlPoints(self):
+        self.ResetMandatoryControlPoints()
+
+    def ResetMandatoryControlPoints(self):
+        if not self._controlPoints:
+            return
+
+        maxX, maxY = self.GetBoundingBoxMax()
+
+        node = self._controlPoints[0]
+
+        if self._handleSide == DIVISION_SIDE_LEFT and node:
+            node._xoffset=-maxX / 2
+            node._yoffset = 0.0
+
+        if self._handleSide == DIVISION_SIDE_TOP and node:
+            node._xoffset = 0.0
+            node._yoffset=-maxY / 2
+
+        if self._handleSide == DIVISION_SIDE_RIGHT and node:
+            node._xoffset = maxX / 2
+            node._yoffset = 0.0
+
+        if self._handleSide == DIVISION_SIDE_BOTTOM and node:
+            node._xoffset = 0.0
+            node._yoffset = maxY / 2
+
+    def AdjustLeft(self, left, test):
+        """Adjust a side.
+
+        Returns FALSE if it's not physically possible to adjust it to
+        this point.
+        """
+        x2 = self.GetX() + self.GetWidth() / 2
+
+        if left >= x2:
+            return False
+
+        if test:
+            return True
+
+        newW = x2-left
+        newX = left + newW / 2
+        self.SetSize(newW, self.GetHeight())
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        self.Move(dc, newX, self.GetY())
+        return True
+
+    def AdjustTop(self, top, test):
+        """Adjust a side.
+
+        Returns FALSE if it's not physically possible to adjust it to
+        this point.
+        """
+        y2 = self.GetY() + self.GetHeight() / 2
+
+        if top >= y2:
+            return False
+
+        if test:
+            return True
+
+        newH = y2-top
+        newY = top + newH / 2
+        self.SetSize(self.GetWidth(), newH)
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        self.Move(dc, self.GetX(), newY)
+        return True
+
+    def AdjustRight(self, right, test):
+        """Adjust a side.
+
+        Returns FALSE if it's not physically possible to adjust it to
+        this point.
+        """
+        x1 = self.GetX()-self.GetWidth() / 2
+
+        if right <= x1:
+            return False
+
+        if test:
+            return True
+
+        newW = right-x1
+        newX = x1 + newW / 2
+        self.SetSize(newW, self.GetHeight())
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        self.Move(dc, newX, self.GetY())
+        return True
+    
+    def AdjustTop(self, top, test):
+        """Adjust a side.
+
+        Returns FALSE if it's not physically possible to adjust it to
+        this point.
+        """
+        y1 = self.GetY()-self.GetHeight() / 2
+
+        if bottom <= y1:
+            return False
+
+        if test:
+            return True
+
+        newH = bottom-y1
+        newY = y1 + newH / 2
+        self.SetSize(self.GetWidth(), newH)
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        self.Move(dc, self.GetX(), newY)
+        return True
+
+    # Resize adjoining divisions.
+    
+    # Behaviour should be as follows:
+    # If right edge moves, find all objects whose left edge
+    # adjoins this object, and move left edge accordingly.
+    # If left..., move ... right.
+    # If top..., move ... bottom.
+    # If bottom..., move top.
+    # If size goes to zero or end position is other side of start position,
+    # resize to original size and return.
+    #
+    def ResizeAdjoining(self, side, newPos, test):
+        """Resize adjoining divisions at the given side.
+
+        If test is TRUE, just see whether it's possible for each adjoining
+        region, returning FALSE if it's not.
+
+        side can be one of:
+
+        * DIVISION_SIDE_NONE
+        * DIVISION_SIDE_LEFT
+        * DIVISION_SIDE_TOP
+        * DIVISION_SIDE_RIGHT
+        * DIVISION_SIDE_BOTTOM
+        """
+        divisionParent = self.GetParent()
+        for division in divisionParent.GetDivisions():
+            if side == DIVISION_SIDE_LEFT:
+                if division._rightSide == self:
+                    success = division.AdjustRight(newPos, test)
+                    if not success and test:
+                        return false
+            elif side == DIVISION_SIDE_TOP:
+                if division._bottomSide == self:
+                    success = division.AdjustBottom(newPos, test)
+                    if not success and test:
+                        return False
+            elif side == DIVISION_SIDE_RIGHT:
+                if division._leftSide == self:
+                    success = division.AdjustLeft(newPos, test)
+                    if not success and test:
+                        return False
+            elif side == DIVISION_SIDE_BOTTOM:
+                if division._topSide == self:
+                    success = division.AdjustTop(newPos, test)
+                    if not success and test:
+                        return False
+        return True
+    
+    def EditEdge(self, side):
+        print "EditEdge() not implemented."
+
+    def PopupMenu(self, x, y):
+        menu = PopupDivisionMenu()
+        menu.SetClientData(self)
+        if self._leftSide:
+            menu.Enable(DIVISION_MENU_EDIT_LEFT_EDGE, True)
+        else:
+            menu.Enable(DIVISION_MENU_EDIT_LEFT_EDGE, False)
+        if self._topSide:
+            menu.Enable(DIVISION_MENU_EDIT_TOP_EDGE, True)
+        else:
+            menu.Enable(DIVISION_MENU_EDIT_TOP_EDGE, False)
+
+        x1, y1 = self._canvas.GetViewStart()
+        unit_x, unit_y = self._canvas.GetScrollPixelsPerUnit()
+
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        mouse_x = dc.LogicalToDeviceX(x-x1 * unit_x)
+        mouse_y = dc.LogicalToDeviceY(y-y1 * unit_y)
+
+        self._canvas.PopupMenu(menu, (mouse_x, mouse_y))
+
+        
diff --git a/wxPython/wx/lib/ogl/_diagram.py b/wxPython/wx/lib/ogl/_diagram.py
new file mode 100644 (file)
index 0000000..95ef272
--- /dev/null
@@ -0,0 +1,160 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         diagram.py
+# Purpose:      Diagram class
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+import wx
+
+DEFAULT_MOUSE_TOLERANCE = 3
+
+
+
+class Diagram(object):
+    """Encapsulates an entire diagram, with methods for drawing. A diagram has
+    an associated ShapeCanvas.
+
+    Derived from:
+      Object
+    """
+    def __init__(self):
+        self._diagramCanvas = None
+        self._quickEditMode = False
+        self._snapToGrid = True
+        self._gridSpacing = 5.0
+        self._shapeList = []
+        self._mouseTolerance = DEFAULT_MOUSE_TOLERANCE
+
+    def Redraw(self, dc):
+        """Draw the shapes in the diagram on the specified device context."""
+        if self._shapeList:
+            if self.GetCanvas():
+                self.GetCanvas().SetCursor(wx.HOURGLASS_CURSOR)
+            for object in self._shapeList:
+                object.Draw(dc)
+            if self.GetCanvas():
+                self.GetCanvas().SetCursor(wx.STANDARD_CURSOR)
+
+    def Clear(self, dc):
+        """Clear the specified device context."""
+        dc.Clear()
+
+    def AddShape(self, object, addAfter = None):
+        """Adds a shape to the diagram. If addAfter is not None, the shape
+        will be added after addAfter.
+        """
+        if not object in self._shapeList:
+            if addAfter:
+                self._shapeList.insert(self._shapeList.index(addAfter) + 1, object)
+            else:
+                self._shapeList.append(object)
+
+            object.SetCanvas(self.GetCanvas())
+
+    def InsertShape(self, object):
+        """Insert a shape at the front of the shape list."""
+        self._shapeList.insert(0, object)
+
+    def RemoveShape(self, object):
+        """Remove the shape from the diagram (non-recursively) but do not
+        delete it.
+        """
+        if object in self._shapeList:
+            self._shapeList.remove(object)
+            
+    def RemoveAllShapes(self):
+        """Remove all shapes from the diagram but do not delete the shapes."""
+        self._shapeList = []
+
+    def DeleteAllShapes(self):
+        """Remove and delete all shapes in the diagram."""
+        for shape in self._shapeList[:]:
+            if not shape.GetParent():
+                self.RemoveShape(shape)
+                
+    def ShowAll(self, show):
+        """Call Show for each shape in the diagram."""
+        for shape in self._shapeList:
+            shape.Show()
+
+    def DrawOutLine(self, dc, x1, y1, x2, y2):
+        """Draw an outline rectangle on the current device context."""
+        dc.SetPen(wx.Pen(wx.Color(0, 0, 0), 1, wx.DOT))
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        dc.DrawLines([[x1, y1], [x2, y1], [x2, y2], [x1, y2], [x1, y1]])
+
+    def RecentreAll(self, dc):
+        """Make sure all text that should be centred, is centred."""
+        for shape in self._shapeList:
+            shape.Recentre(dc)
+
+    def SetCanvas(self, canvas):
+        """Set the canvas associated with this diagram."""
+        self._diagramCanvas = canvas
+
+    def GetCanvas(self):
+        """Return the shape canvas associated with this diagram."""
+        return self._diagramCanvas
+        
+    def FindShape(self, id):
+        """Return the shape for the given identifier."""
+        for shape in self._shapeList:
+            if shape.GetId() == id:
+                return shape
+        return None
+
+    def Snap(self, x, y):
+        """'Snaps' the coordinate to the nearest grid position, if
+        snap-to-grid is on."""
+        if self._snapToGrid:
+            return self._gridSpacing * int(x / self._gridSpacing + 0.5), self._gridSpacing * int(y / self._gridSpacing + 0.5)
+        return x, y
+
+    def GetGridSpacing(self):
+        """Return the grid spacing."""
+        return self._gridSpacing
+
+    def GetSnapToGrid(self):
+        """Return snap-to-grid mode."""
+        return self._snapToGrid
+
+    def SetQuickEditMode(self, mode):
+        """Set quick-edit-mode on of off.
+
+        In this mode, refreshes are minimized, but the diagram may need
+        manual refreshing occasionally.
+        """
+        self._quickEditMode = mode
+
+    def GetQuickEditMode(self):
+        """Return quick edit mode."""
+        return self._quickEditMode
+
+    def SetMouseTolerance(self, tolerance):
+        """Set the tolerance within which a mouse move is ignored.
+
+        The default is 3 pixels.
+        """
+        self._mouseTolerance = tolerance
+
+    def GetMouseTolerance(self):
+        """Return the tolerance within which a mouse move is ignored."""
+        return self._mouseTolerance
+
+    def GetShapeList(self):
+        """Return the internal shape list."""
+        return self._shapeList
+
+    def GetCount(self):
+        """Return the number of shapes in the diagram."""
+        return len(self._shapeList)
diff --git a/wxPython/wx/lib/ogl/_divided.py b/wxPython/wx/lib/ogl/_divided.py
new file mode 100644 (file)
index 0000000..9d58222
--- /dev/null
@@ -0,0 +1,404 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         divided.py
+# Purpose:      DividedShape class
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+import sys
+import wx
+
+from _basic import ControlPoint, RectangleShape, Shape
+from _oglmisc import *
+
+
+
+class DividedShapeControlPoint(ControlPoint):
+    def __init__(self, the_canvas, object, region, size, the_m_xoffset, the_m_yoffset, the_type):
+        ControlPoint.__init__(self, the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type)
+        self.regionId = region
+
+    # Implement resizing of divided object division
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        dividedObject = self._shape
+        x1 = dividedObject.GetX()-dividedObject.GetWidth() / 2
+        y1 = y
+        x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2
+        y2 = y
+
+        dc.DrawLine(x1, y1, x2, y2)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        dividedObject = self._shape
+        
+        x1 = dividedObject.GetX()-dividedObject.GetWidth() / 2
+        y1 = y
+        x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2
+        y2 = y
+
+        dc.DrawLine(x1, y1, x2, y2)
+        self._canvas.CaptureMouse()
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dividedObject = self._shape
+        if not dividedObject.GetRegions()[self.regionId]:
+            return
+        
+        thisRegion = dividedObject.GetRegions()[self.regionId]
+        nextRegion = None
+
+        dc.SetLogicalFunction(wx.COPY)
+
+        if self._canvas.HasCapture():
+            self._canvas.ReleaseMouse()
+
+        # Find the old top and bottom of this region,
+        # and calculate the new proportion for this region
+        # if legal.
+        currentY = dividedObject.GetY()-dividedObject.GetHeight() / 2
+        maxY = dividedObject.GetY() + dividedObject.GetHeight() / 2
+
+        # Save values
+        theRegionTop = 0
+        nextRegionBottom = 0
+        
+        for i in range(len(dividedObject.GetRegions())):
+            region = dividedObject.GetRegions()[i]
+            proportion = region._regionProportionY
+            yy = currentY + dividedObject.GetHeight() * proportion
+            actualY = min(maxY, yy)
+
+            if region == thisRegion:
+                thisRegionTop = currentY
+                
+                if i + 1<len(dividedObject.GetRegions()):
+                    nextRegion = dividedObject.GetRegions()[i + 1]
+            if region == nextRegion:
+                nextRegionBottom = actualY
+
+            currentY = actualY
+
+        if not nextRegion:
+            return
+
+        # Check that we haven't gone above this region or below
+        # next region.
+        if y <= thisRegionTop or y >= nextRegionBottom:
+            return
+
+        dividedObject.EraseLinks(dc)
+
+        # Now calculate the new proportions of this region and the next region
+        thisProportion = (y-thisRegionTop) / dividedObject.GetHeight()
+        nextProportion = (nextRegionBottom-y) / dividedObject.GetHeight()
+
+        thisRegion.SetProportions(0, thisProportion)
+        nextRegion.SetProportions(0, nextProportion)
+        self._yoffset = y-dividedObject.GetY()
+
+        # Now reformat text
+        for i, region in enumerate(dividedObject.GetRegions()):
+            if region.GetText():
+                s = region.GetText()
+                dividedObject.FormatText(dc, s, i)
+
+        dividedObject.SetRegionSizes()
+        dividedObject.Draw(dc)
+        dividedObject.GetEventHandler().OnMoveLinks(dc)
+        
+
+
+class DividedShape(RectangleShape):
+    """A DividedShape is a rectangle with a number of vertical divisions.
+    Each division may have its text formatted with independent characteristics,
+    and the size of each division relative to the whole image may be specified.
+
+    Derived from:
+      RectangleShape
+    """
+    def __init__(self, w, h):
+        RectangleShape.__init__(self, w, h)
+        self.ClearRegions()
+
+    def OnDraw(self, dc):
+        RectangleShape.OnDraw(self, dc)
+
+    def OnDrawContents(self, dc):
+        if self.GetRegions():
+            defaultProportion = 1 / len(self.GetRegions())
+        else:
+            defaultProportion = 0
+        currentY = self._ypos-self._height / 2
+        maxY = self._ypos + self._height / 2
+
+        leftX = self._xpos-self._width / 2
+        rightX = self._xpos + self._width / 2
+
+        if self._pen:
+            dc.SetPen(self._pen)
+
+        dc.SetTextForeground(self._textColour)
+
+        # For efficiency, don't do this under X - doesn't make
+        # any visible difference for our purposes.
+        if sys.platform[:3]=="win":
+            dc.SetTextBackground(self._brush.GetColour())
+
+        if self.GetDisableLabel():
+            return
+
+        xMargin = 2
+        yMargin = 2
+
+        dc.SetBackgroundMode(wx.TRANSPARENT)
+
+        for region in self.GetRegions():
+            dc.SetFont(region.GetFont())
+            dc.SetTextForeground(region.GetActualColourObject())
+
+            if region._regionProportionY<0:
+                proportion = defaultProportion
+            else:
+                proportion = region._regionProportionY
+
+            y = currentY + self._height * proportion
+            actualY = min(maxY, y)
+
+            centreX = self._xpos
+            centreY = currentY + (actualY-currentY) / 2
+
+            DrawFormattedText(dc, region._formattedText, centreX, centreY, self._width-2 * xMargin, actualY-currentY-2 * yMargin, region._formatMode)
+
+            if y <= maxY and region != self.GetRegions()[-1]:
+                regionPen = region.GetActualPen()
+                if regionPen:
+                    dc.SetPen(regionPen)
+                    dc.DrawLine(leftX, y, rightX, y)
+
+            currentY = actualY
+
+    def SetSize(self, w, h, recursive = True):
+        self.SetAttachmentSize(w, h)
+        self._width = w
+        self._height = h
+        self.SetRegionSizes()
+
+    def SetRegionSizes(self):
+        """Set all region sizes according to proportions and this object
+        total size.
+        """
+        if not self.GetRegions():
+            return
+
+        if self.GetRegions():
+            defaultProportion = 1 / len(self.GetRegions())
+        else:
+            defaultProportion = 0
+        currentY = self._ypos-self._height / 2
+        maxY = self._ypos + self._height / 2
+        
+        for region in self.GetRegions():
+            if region._regionProportionY <= 0:
+                proportion = defaultProportion
+            else:
+                proportion = region._regionProportionY
+
+            sizeY = proportion * self._height
+            y = currentY + sizeY
+            actualY = min(maxY, y)
+
+            centreY = currentY + (actualY-currentY) / 2
+
+            region.SetSize(self._width, sizeY)
+            region.SetPosition(0, centreY-self._ypos)
+
+            currentY = actualY
+
+    # Attachment points correspond to regions in the divided box
+    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
+        totalNumberAttachments = len(self.GetRegions()) * 2 + 2
+        if self.GetAttachmentMode() == ATTACHMENT_MODE_NONE or attachment >= totalNumberAttachments:
+            return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs)
+
+        n = len(self.GetRegions())
+        isEnd = line and line.IsEnd(self)
+
+        left = self._xpos-self._width / 2
+        right = self._xpos + self._width / 2
+        top = self._ypos-self._height / 2
+        bottom = self._ypos + self._height / 2
+
+        # Zero is top, n + 1 is bottom
+        if attachment == 0:
+            y = top
+            if self._spaceAttachments:
+                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                    # Align line according to the next handle along
+                    point = line.GetNextControlPoint(self)
+                    if point.x<left:
+                        x = left
+                    elif point.x>right:
+                        x = right
+                    else:
+                        x = point.x
+                else:
+                    x = left + (nth + 1) * self._width / (no_arcs + 1)
+            else:
+                x = self._xpos
+        elif attachment == n + 1:
+            y = bottom
+            if self._spaceAttachments:
+                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                    # Align line according to the next handle along
+                    point = line.GetNextControlPoint(self)
+                    if point.x<left:
+                        x = left
+                    elif point.x>right:
+                        x = right
+                    else:
+                        x = point.x
+                else:
+                    x = left + (nth + 1) * self._width / (no_arcs + 1)
+            else:
+                x = self._xpos
+        else: # Left or right
+            isLeft = not attachment<(n + 1)
+            if isLeft:
+                i = totalNumberAttachments-attachment-1
+            else:
+                i = attachment-1
+
+            region = self.GetRegions()[i]
+            if region:
+                if isLeft:
+                    x = left
+                else:
+                    x = right
+
+                # Calculate top and bottom of region
+                top = self._ypos + region._y-region._height / 2
+                bottom = self._ypos + region._y + region._height / 2
+
+                # Assuming we can trust the absolute size and
+                # position of these regions
+                if self._spaceAttachments:
+                    if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                        # Align line according to the next handle along
+                        point = line.GetNextControlPoint(self)
+                        if point.y<bottom:
+                            y = bottom
+                        elif point.y>top:
+                            y = top
+                        else:
+                            y = point.y
+                    else:
+                        y = top + (nth + 1) * region._height / (no_arcs + 1)
+                else:
+                    y = self._ypos + region._y
+            else:
+                return False
+        return x, y
+
+    def GetNumberOfAttachments(self):
+        # There are two attachments for each region (left and right),
+        # plus one on the top and one on the bottom.
+        n = len(self.GetRegions()) * 2 + 2
+
+        maxN = n-1
+        for point in self._attachmentPoints:
+            if point._id>maxN:
+                maxN = point._id
+
+        return maxN + 1
+
+    def AttachmentIsValid(self, attachment):
+        totalNumberAttachments = len(self.GetRegions()) * 2 + 2
+        if attachment >= totalNumberAttachments:
+            return Shape.AttachmentIsValid(self, attachment)
+        else:
+            return attachment >= 0
+
+    def MakeControlPoints(self):
+        RectangleShape.MakeControlPoints(self)
+        self.MakeMandatoryControlPoints()
+
+    def MakeMandatoryControlPoints(self):
+        currentY = self.GetY()-self._height / 2
+        maxY = self.GetY() + self._height / 2
+
+        for i, region in enumerate(self.GetRegions()):
+            proportion = region._regionProportionY
+
+            y = currentY + self._height * proportion
+            actualY = min(maxY, y)
+
+            if region != self.GetRegions()[-1]:
+                controlPoint = DividedShapeControlPoint(self._canvas, self, i, CONTROL_POINT_SIZE, 0, actualY-self.GetY(), 0)
+                self._canvas.AddShape(controlPoint)
+                self._controlPoints.append(controlPoint)
+
+            currentY = actualY
+
+    def ResetControlPoints(self):
+        # May only have the region handles, (n - 1) of them
+        if len(self._controlPoints)>len(self.GetRegions())-1:
+            RectangleShape.ResetControlPoints(self)
+
+        self.ResetMandatoryControlPoints()
+
+    def ResetMandatoryControlPoints(self):
+        currentY = self.GetY()-self._height / 2
+        maxY = self.GetY() + self._height / 2
+
+        i = 0
+        for controlPoint in self._controlPoints:
+            if isinstance(controlPoint, DividedShapeControlPoint):
+                region = self.GetRegions()[i]
+                proportion = region._regionProportionY
+
+                y = currentY + self._height * proportion
+                actualY = min(maxY, y)
+
+                controlPoint._xoffset = 0
+                controlPoint._yoffset = actualY-self.GetY()
+
+                currentY = actualY
+
+                i += 1
+                
+    def EditRegions(self):
+        """Edit the region colours and styles. Not implemented."""
+        print "EditRegions() is unimplemented"
+        
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        if keys & KEY_CTRL:
+            self.EditRegions()
+        else:
+            RectangleShape.OnRightClick(self, x, y, keys, attachment)
diff --git a/wxPython/wx/lib/ogl/_lines.py b/wxPython/wx/lib/ogl/_lines.py
new file mode 100644 (file)
index 0000000..32ebb9c
--- /dev/null
@@ -0,0 +1,1533 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         lines.py
+# Purpose:      LineShape class
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+
+import sys
+import math
+
+from _basic import Shape, ShapeRegion, ControlPoint, RectangleShape
+from _oglmisc import *
+
+# Line alignment flags
+# Vertical by default
+LINE_ALIGNMENT_HORIZ=              1
+LINE_ALIGNMENT_VERT=               0
+LINE_ALIGNMENT_TO_NEXT_HANDLE=     2
+LINE_ALIGNMENT_NONE=               0
+
+
+
+class LineControlPoint(ControlPoint):
+    def __init__(self, theCanvas = None, object = None, size = 0.0, x = 0.0, y = 0.0, the_type = 0):
+        ControlPoint.__init__(self, theCanvas, object, size, x, y, the_type)
+        self._xpos = x
+        self._ypos = y
+        self._type = the_type
+        self._point = None
+        self._originalPos = None
+
+    def OnDraw(self, dc):
+        RectangleShape.OnDraw(self, dc)
+
+    # Implement movement of Line point
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
+
+
+
+class ArrowHead(object):
+    def __init__(self, type = 0, end = 0, size = 0.0, dist = 0.0, name="",mf = None, arrowId=-1):
+        if isinstance(type, ArrowHead):
+            pass
+        else:
+            self._arrowType = type
+            self._arrowEnd = end
+            self._arrowSize = size
+            self._xOffset = dist
+            self._yOffset = 0.0
+            self._spacing = 5.0
+
+            self._arrowName = name
+            self._metaFile = mf
+            self._id = arrowId
+            if self._id==-1:
+                self._id = wx.NewId()
+            
+    def _GetType(self):
+        return self._arrowType
+
+    def GetPosition(self):
+        return self._arrowEnd
+
+    def SetPosition(self, pos):
+        self._arrowEnd = pos
+
+    def GetXOffset(self):
+        return self._xOffset
+
+    def GetYOffset(self):
+        return self._yOffset
+    
+    def GetSpacing(self):
+        return self._spacing
+
+    def GetSize(self):
+        return self._arrowSize
+
+    def SetSize(self, size):
+        self._arrowSize = size
+        if self._arrowType == ARROW_METAFILE and self._metaFile:
+            oldWidth = self._metaFile._width
+            if oldWidth == 0:
+                return
+
+            scale = size / oldWidth
+            if scale != 1:
+                self._metaFile.Scale(scale, scale)
+                
+    def GetName(self):
+        return self._arrowName
+
+    def SetXOffset(self, x):
+        self._xOffset = x
+
+    def SetYOffset(self, y):
+        self._yOffset = y
+
+    def GetMetaFile(self):
+        return self._metaFile
+
+    def GetId(self):
+        return self._id
+
+    def GetArrowEnd(self):
+        return self._arrowEnd
+
+    def GetArrowSize(self):
+        return self._arrowSize
+
+    def SetSpacing(self, sp):
+        self._spacing = sp
+
+
+
+class LabelShape(RectangleShape):
+    def __init__(self, parent, region, w, h):
+        RectangleShape.__init__(self, w, h)
+        self._lineShape = parent
+        self._shapeRegion = region
+        self.SetPen(wx.ThePenList.FindOrCreatePen(wx.Colour(0, 0, 0), 1, wx.DOT))
+
+    def OnDraw(self, dc):
+        if self._lineShape and not self._lineShape.GetDrawHandles():
+            return
+
+        x1 = self._xpos-self._width / 2
+        y1 = self._ypos-self._height / 2
+
+        if self._pen:
+            if self._pen.GetWidth() == 0:
+                dc.SetPen(wx.Pen(wx.WHITE, 1, wx.TRANSPARENT))
+            else:
+                dc.SetPen(self._pen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        if self._cornerRadius>0:
+            dc.DrawRoundedRectangle(x1, y1, self._width, self._height, self._cornerRadius)
+        else:
+            dc.DrawRectangle(x1, y1, self._width, self._height)
+
+    def OnDrawContents(self, dc):
+        pass
+
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        RectangleShape.OnDragLeft(self, draw, x, y, keys, attachment)
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        RectangleShape.OnBeginDragLeft(self, x, y, keys, attachment)
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        RectangleShape.OnEndDragLeft(self, x, y, keys, attachment)
+
+    def OnMovePre(self, dc, x, y, old_x, old_y, display):
+        return self._lineShape.OnLabelMovePre(dc, self, x, y, old_x, old_y, display)
+
+    # Divert left and right clicks to line object
+    def OnLeftClick(self, x, y, keys = 0, attachment = 0):
+        self._lineShape.GetEventHandler().OnLeftClick(x, y, keys, attachment)
+
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        self._lineShape.GetEventHandler().OnRightClick(x, y, keys, attachment)
+        
+
+
+class LineShape(Shape):
+    """LineShape may be attached to two nodes;
+    it may be segmented, in which case a control point is drawn for each joint.
+
+    A wxLineShape may have arrows at the beginning, end and centre.
+
+    Derived from:
+      Shape
+    """
+    def __init__(self):
+        Shape.__init__(self)
+
+        self._sensitivity = OP_CLICK_LEFT | OP_CLICK_RIGHT
+        self._draggable = False
+        self._attachmentTo = 0
+        self._attachmentFrom = 0
+        self._from = None
+        self._to = None
+        self._erasing = False
+        self._arrowSpacing = 5.0
+        self._ignoreArrowOffsets = False
+        self._isSpline = False
+        self._maintainStraightLines = False
+        self._alignmentStart = 0
+        self._alignmentEnd = 0
+
+        self._lineControlPoints = None
+
+        # Clear any existing regions (created in an earlier constructor)
+        # and make the three line regions.
+        self.ClearRegions()
+        for name in ["Middle","Start","End"]:
+            newRegion = ShapeRegion()
+            newRegion.SetName(name)
+            newRegion.SetSize(150, 50)
+            self._regions.append(newRegion)
+
+        self._labelObjects = [None, None, None]
+        self._lineOrientations = []
+        self._lineControlPoints = []
+        self._arcArrows = []
+
+    def __del__(self):
+        if self._lineControlPoints:
+            self.ClearPointList(self._lineControlPoints)
+            self._lineControlPoints = []
+        for i in range(3):
+            if self._labelObjects[i]:
+                self._labelObjects[i].Select(False)
+                self._labelObjects[i].RemoveFromCanvas(self._canvas)
+        self._labelObjects = []
+        self.ClearArrowsAtPosition(-1)
+
+    def GetFrom(self):
+        """Return the 'from' object."""
+        return self._from
+    
+    def GetTo(self):
+        """Return the 'to' object."""
+        return self._to
+
+    def GetAttachmentFrom(self):
+        """Return the attachment point on the 'from' node."""
+        return self._attachmentFrom
+
+    def GetAttachmentTo(self):
+        """Return the attachment point on the 'to' node."""
+        return self._attachmentTo
+
+    def GetLineControlPoints(self):
+        return self._lineControlPoints
+
+    def SetSpline(self, spline):
+        """Specifies whether a spline is to be drawn through the control points."""
+        self._isSpline = spline
+
+    def IsSpline(self):
+        """TRUE if a spline is drawn through the control points."""
+        return self._isSpline
+
+    def SetAttachmentFrom(self, attach):
+        """Set the 'from' shape attachment."""
+        self._attachmentFrom = attach
+
+    def SetAttachmentTo(self, attach):
+        """Set the 'to' shape attachment."""
+        self._attachmentTo = attach
+
+    # This is really to distinguish between lines and other images.
+    # For lines, want to pass drag to canvas, since lines tend to prevent
+    # dragging on a canvas (they get in the way.)
+    def Draggable(self):
+        return False
+
+    def SetIgnoreOffsets(self, ignore):
+        """Set whether to ignore offsets from the end of the line when drawing."""
+        self._ignoreArrowOffsets = ignore
+
+    def GetArrows(self):
+        return self._arcArrows
+
+    def GetAlignmentStart(self):
+        return self._alignmentStart
+
+    def GetAlignmentEnd(self):
+        return self._alignmentEnd
+
+    def IsEnd(self, nodeObject):
+        """TRUE if shape is at the end of the line."""
+        return self._to == nodeObject
+
+    def MakeLineControlPoints(self, n):
+        """Make a given number of control points (minimum of two)."""
+        if self._lineControlPoints:
+            self.ClearPointList(self._lineControlPoints)
+        self._lineControlPoints = []
+        
+        for _ in range(n):
+            point = wx.RealPoint(-999,-999)
+            self._lineControlPoints.append(point)
+
+    def InsertLineControlPoint(self, dc = None):
+        """Insert a control point at an arbitrary position."""
+        if dc:
+            self.Erase(dc)
+
+        last_point = self._lineControlPoints[-1]
+        second_last_point = self._lineControlPoints[-2]
+
+        line_x = (last_point[0] + second_last_point[0]) / 2
+        line_y = (last_point[1] + second_last_point[1]) / 2
+
+        point = wx.RealPoint(line_x, line_y)
+        self._lineControlPoints.insert(len(self._lineControlPoints), point)
+
+    def DeleteLineControlPoint(self):
+        """Delete an arbitary point on the line."""
+        if len(self._lineControlPoints)<3:
+            return False
+
+        del self._lineControlPoints[-2]
+        return True
+
+    def Initialise(self):
+        """Initialise the line object."""
+        if self._lineControlPoints:
+            # Just move the first and last control points
+            first_point = self._lineControlPoints[0]
+            last_point = self._lineControlPoints[-1]
+
+            # If any of the line points are at -999, we must
+            # initialize them by placing them half way between the first
+            # and the last.
+
+            for point in self._lineControlPoints[1:]:
+                if point[0]==-999:
+                    if first_point[0]<last_point[0]:
+                        x1 = first_point[0]
+                        x2 = last_point[0]
+                    else:
+                        x2 = first_point[0]
+                        x1 = last_point[0]
+                    if first_point[1]<last_point[1]:
+                        y1 = first_point[1]
+                        y2 = last_point[1]
+                    else:
+                        y2 = first_point[1]
+                        y1 = last_point[1]
+                    point[0] = (x2-x1) / 2 + x1
+                    point[1] = (y2-y1) / 2 + y1
+                    
+    def FormatText(self, dc, s, i):
+        """Format a text string according to the region size, adding
+        strings with positions to region text list.
+        """
+        self.ClearText(i)
+
+        if len(self._regions) == 0 or i >= len(self._regions):
+            return
+
+        region = self._regions[i]
+        region.SetText(s)
+        dc.SetFont(region.GetFont())
+
+        w, h = region.GetSize()
+        # Initialize the size if zero
+        if (w == 0 or h == 0) and s:
+            w, h = 100, 50
+            region.SetSize(w, h)
+
+        string_list = FormatText(dc, s, w-5, h-5, region.GetFormatMode())
+        for s in string_list:
+            line = ShapeTextLine(0.0, 0.0, s)
+            region.GetFormattedText().append(line)
+
+        actualW = w
+        actualH = h
+        if region.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS:
+            actualW, actualH = GetCentredTextExtent(dc, region.GetFormattedText(), self._xpos, self._ypos, w, h)
+            if actualW != w or actualH != h:
+                xx, yy = self.GetLabelPosition(i)
+                self.EraseRegion(dc, region, xx, yy)
+                if len(self._labelObjects)<i:
+                    self._labelObjects[i].Select(False, dc)
+                    self._labelObjects[i].Erase(dc)
+                    self._labelObjects[i].SetSize(actualW, actualH)
+
+                region.SetSize(actualW, actualH)
+
+                if len(self._labelObjects)<i:
+                    self._labelObjects[i].Select(True, dc)
+                    self._labelObjects[i].Draw(dc)
+
+        CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, actualW, actualH, region.GetFormatMode())
+        self._formatted = True
+
+    def DrawRegion(self, dc, region, x, y):
+        """Format one region at this position."""
+        if self.GetDisableLabel():
+            return
+
+        w, h = region.GetSize()
+
+        # Get offset from x, y
+        xx, yy = region.GetPosition()
+
+        xp = xx + x
+        yp = yy + y
+
+        # First, clear a rectangle for the text IF there is any
+        if len(region.GetFormattedText()):
+            dc.SetPen(self.GetBackgroundPen())
+            dc.SetBrush(self.GetBackgroundBrush())
+
+            # Now draw the text
+            if region.GetFont():
+                dc.SetFont(region.GetFont())
+                dc.DrawRectangle(xp-w / 2, yp-h / 2, w, h)
+
+                if self._pen:
+                    dc.SetPen(self._pen)
+                dc.SetTextForeground(region.GetActualColourObject())
+
+                DrawFormattedText(dc, region.GetFormattedText(), xp, yp, w, h, region.GetFormatMode())
+
+    def EraseRegion(self, dc, region, x, y):
+        """Erase one region at this position."""
+        if self.GetDisableLabel():
+            return
+
+        w, h = region.GetSize()
+
+        # Get offset from x, y
+        xx, yy = region.GetPosition()
+
+        xp = xx + x
+        yp = yy + y
+
+        if region.GetFormattedText():
+            dc.SetPen(self.GetBackgroundPen())
+            dc.SetBrush(self.GetBackgroundBrush())
+
+            dc.DrawRectangle(xp-w / 2, yp-h / 2, w, h)
+
+    def GetLabelPosition(self, position):
+        """Get the reference point for a label.
+
+        Region x and y are offsets from this.
+        position is 0 (middle), 1 (start), 2 (end).
+        """
+        if position == 0:
+            # Want to take the middle section for the label
+            half_way = int(len(self._lineControlPoints) / 2)
+
+            # Find middle of this line
+            point = self._lineControlPoints[half_way-1]
+            next_point = self._lineControlPoints[half_way]
+
+            dx = next_point[0]-point[0]
+            dy = next_point[1]-point[1]
+
+            return point[0] + dx / 2, point[1] + dy / 2
+        elif position == 1:
+            return self._lineControlPoints[0][0], self._lineControlPoints[0][1]
+        elif position == 2:
+            return self._lineControlPoints[-1][0], self._lineControlPoints[-1][1]
+
+    def Straighten(self, dc = None):
+        """Straighten verticals and horizontals."""
+        if len(self._lineControlPoints)<3:
+            return
+
+        if dc:
+            self.Erase(dc)
+
+        GraphicsStraightenLine(self._lineControlPoints[-1], self._lineControlPoints[-2])
+
+        for i in range(len(self._lineControlPoints)-2):
+            GraphicsStraightenLine(self._lineControlPoints[i], self._lineControlPoints[i + 1])
+                
+        if dc:
+            self.Draw(dc)
+
+    def Unlink(self):
+        """Unlink the line from the nodes at either end."""
+        if self._to:
+            self._to.GetLines().remove(self)
+        if self._from:
+            self._from.GetLines().remove(self)
+        self._to = None
+        self._from = None
+
+    def SetEnds(self, x1, y1, x2, y2):
+        """Set the end positions of the line."""
+        # Find centre point
+        first_point = self._lineControlPoints[0]
+        last_point = self._lineControlPoints[-1]
+
+        first_point[0] = x1
+        first_point[1] = y1
+        last_point[0] = x2
+        last_point[1] = y2
+
+        self._xpos = (x1 + x2) / 2
+        self._ypos = (y1 + y2) / 2
+
+    # Get absolute positions of ends
+    def GetEnds(self):
+        """Get the visible endpoints of the lines for drawing between two objects."""
+        first_point = self._lineControlPoints[0]
+        last_point = self._lineControlPoints[-1]
+
+        return (first_point[0], first_point[1]), (last_point[0], last_point[1])
+
+    def SetAttachments(self, from_attach, to_attach):
+        """Specify which object attachment points should be used at each end
+        of the line.
+        """
+        self._attachmentFrom = from_attach
+        self._attachmentTo = to_attach
+
+    def HitTest(self, x, y):
+        if not self._lineControlPoints:
+            return False
+
+        # Look at label regions in case mouse is over a label
+        inLabelRegion = False
+        for i in range(3):
+            if self._regions[i]:
+                region = self._regions[i]
+                if len(region._formattedText):
+                    xp, yp = self.GetLabelPosition(i)
+                    # Offset region from default label position
+                    cx, cy = region.GetPosition()
+                    cw, ch = region.GetSize()
+                    cx += xp
+                    cy += yp
+                    
+                    rLeft = cx-cw / 2
+                    rTop = cy-ch / 2
+                    rRight = cx + cw / 2
+                    rBottom = cy + ch / 2
+                    if x>rLeft and x<rRight and y>rTop and y<rBottom:
+                        inLabelRegion = True
+                        break
+
+        for i in range(len(self._lineControlPoints)-1):
+            point1 = self._lineControlPoints[i]
+            point2 = self._lineControlPoints[i + 1]
+
+            # For inaccurate mousing allow 8 pixel corridor
+            extra = 4
+
+            dx = point2[0]-point1[0]
+            dy = point2[1]-point1[1]
+
+            seg_len = math.sqrt(dx * dx + dy * dy)
+            if dy == 0 or dx == 0:
+                return False
+            distance_from_seg = seg_len * ((x-point1[0]) * dy-(y-point1[1]) * dx) / (dy * dy + dx * dx)
+            distance_from_prev = seg_len * ((y-point1[1]) * dy + (x-point1[0]) * dx) / (dy * dy + dx * dx)
+
+            if abs(distance_from_seg)<extra and distance_from_prev >= 0 and distance_from_prev <= seg_len or inLabelRegion:
+                return 0, distance_from_seg
+
+        return False
+
+    def DrawArrows(self, dc):
+        """Draw all arrows."""
+        # Distance along line of each arrow: space them out evenly
+        startArrowPos = 0.0
+        endArrowPos = 0.0
+        middleArrowPos = 0.0
+
+        for arrow in self._arcArrows:
+            ah = arrow.GetArrowEnd()
+            if ah == ARROW_POSITION_START:
+                if arrow.GetXOffset() and not self._ignoreArrowOffsets:
+                    # If specified, x offset is proportional to line length
+                    self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
+                else:
+                    self.DrawArrow(dc, arrow, startArrowPos, False)
+                    startArrowPos += arrow.GetSize() + arrow.GetSpacing()
+            elif ah == ARROW_POSITION_END:
+                if arrow.GetXOffset() and not self._ignoreArrowOffsets:
+                    self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
+                else:
+                    self.DrawArrow(dc, arrow, endArrowPos, False)
+                    endArrowPos += arrow.GetSize() + arrow.GetSpacing()
+            elif ah == ARROW_POSITION_MIDDLE:
+                arrow.SetXOffset(middleArrowPos)
+                if arrow.GetXOffset() and not self._ignoreArrowOffsets:
+                    self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
+                else:
+                    self.DrawArrow(dc, arrow, middleArrowPos, False)
+                    middleArrowPos += arrow.GetSize() + arrow.GetSpacing()
+
+    def DrawArrow(self, dc, arrow, XOffset, proportionalOffset):
+        """Draw the given arrowhead (or annotation)."""
+        first_line_point = self._lineControlPoints[0]
+        second_line_point = self._lineControlPoints[1]
+
+        last_line_point = self._lineControlPoints[-1]
+        second_last_line_point = self._lineControlPoints[-2]
+
+        # Position of start point of line, at the end of which we draw the arrow
+        startPositionX, startPositionY = 0.0, 0.0
+
+        ap = arrow.GetPosition()
+        if ap == ARROW_POSITION_START:
+            # If we're using a proportional offset, calculate just where this
+            # will be on the line.
+            realOffset = XOffset
+            if proportionalOffset:
+                totalLength = math.sqrt((second_line_point[0]-first_line_point[0]) * (second_line_point[0]-first_line_point[0]) + (second_line_point[1]-first_line_point[1]) * (second_line_point[1]-first_line_point[1]))
+                realOffset = XOffset * totalLength
+
+            positionOnLineX, positionOnLineY = GetPointOnLine(second_line_point[0], second_line_point[1], first_line_point[0], first_line_point[1], realOffset)
+            
+            startPositionX = second_line_point[0]
+            startPositionY = second_line_point[1]
+        elif ap == ARROW_POSITION_END:
+            # If we're using a proportional offset, calculate just where this
+            # will be on the line.
+            realOffset = XOffset
+            if proportionalOffset:
+                totalLength = math.sqrt((second_last_line_point[0]-last_line_point[0]) * (second_last_line_point[0]-last_line_point[0]) + (second_last_line_point[1]-last_line_point[1]) * (second_last_line_point[1]-last_line_point[1]));
+                realOffset = XOffset * totalLength
+            
+            positionOnLineX, positionOnLineY = GetPointOnLine(second_last_line_point[0], second_last_line_point[1], last_line_point[0], last_line_point[1], realOffset)
+            
+            startPositionX = second_last_line_point[0]
+            startPositionY = second_last_line_point[1]
+        elif ap == ARROW_POSITION_MIDDLE:
+            # Choose a point half way between the last and penultimate points
+            x = (last_line_point[0] + second_last_line_point[0]) / 2
+            y = (last_line_point[1] + second_last_line_point[1]) / 2
+
+            # If we're using a proportional offset, calculate just where this
+            # will be on the line.
+            realOffset = XOffset
+            if proportionalOffset:
+                totalLength = math.sqrt((second_last_line_point[0]-x) * (second_last_line_point[0]-x) + (second_last_line_point[1]-y) * (second_last_line_point[1]-y));
+                realOffset = XOffset * totalLength
+
+            positionOnLineX, positionOnLineY = GetPointOnLine(second_last_line_point[0], second_last_line_point[1], x, y, realOffset)
+            startPositionX = second_last_line_point[0]
+            startPositionY = second_last_line_point[1]
+
+        # Add yOffset to arrow, if any
+
+        # The translation that the y offset may give
+        deltaX = 0.0
+        deltaY = 0.0
+        if arrow.GetYOffset and not self._ignoreArrowOffsets:
+            #                             |(x4, y4)
+            #                             |d
+            #                             |
+            #   (x1, y1)--------------(x3, y3)------------------(x2, y2)
+            #   x4 = x3 - d * math.sin(theta)
+            #   y4 = y3 + d * math.cos(theta)
+            #
+            #   Where theta = math.tan(-1) of (y3-y1) / (x3-x1)
+            x1 = startPositionX
+            y1 = startPositionY
+            x3 = positionOnLineX
+            y3 = positionOnLineY
+            d=-arrow.GetYOffset() # Negate so +offset is above line
+
+            if x3 == x1:
+                theta = pi / 2
+            else:
+                theta = math.atan((y3-y1) / (x3-x1))
+
+            x4 = x3-d * math.sin(theta)
+            y4 = y3 + d * math.cos(theta)
+            
+            deltaX = x4-positionOnLineX
+            deltaY = y4-positionOnLineY
+
+        at = arrow._GetType()
+        if at == ARROW_ARROW:
+            arrowLength = arrow.GetSize()
+            arrowWidth = arrowLength / 3
+
+            tip_x, tip_y, side1_x, side1_y, side2_x, side2_y = GetArrowPoints(startPositionX + deltaX, startPositionY + deltaY, positionOnLineX + deltaX, positionOnLineY + deltaY, arrowLength, arrowWidth)
+
+            points = [[tip_x, tip_y],
+                    [side1_x, side1_y],
+                    [side2_x, side2_y],
+                    [tip_x, tip_y]]
+
+            dc.SetPen(self._pen)
+            dc.SetBrush(self._brush)
+            dc.DrawPolygon(points)
+        elif at in [ARROW_HOLLOW_CIRCLE, ARROW_FILLED_CIRCLE]:
+            # Find point on line of centre of circle, which is a radius away
+            # from the end position
+            diameter = arrow.GetSize()
+            x, y = GetPointOnLine(startPositionX + deltaX, startPositionY + deltaY,
+                               positionOnLineX + deltaX, positionOnLineY + deltaY,
+                               diameter / 2)
+            x1 = x-diameter / 2
+            y1 = y-diameter / 2
+            dc.SetPen(self._pen)
+            if arrow._GetType() == ARROW_HOLLOW_CIRCLE:
+                dc.SetBrush(self.GetBackgroundBrush())
+            else:
+                dc.SetBrush(self._brush)
+
+            dc.DrawEllipse(x1, y1, diameter, diameter)
+        elif at == ARROW_SINGLE_OBLIQUE:
+            pass
+        elif at == ARROW_METAFILE:
+            if arrow.GetMetaFile():
+                # Find point on line of centre of object, which is a half-width away
+                # from the end position
+                #
+                #                 width
+                #  <-- start pos  <-----><-- positionOnLineX
+                #                 _____
+                #  --------------|  x  | <-- e.g. rectangular arrowhead
+                #                 -----
+                #
+                x, y = GetPointOnLine(startPositionX, startPositionY,
+                                   positionOnLineX, positionOnLineY,
+                                   arrow.GetMetaFile()._width / 2)
+                # Calculate theta for rotating the metafile.
+                #
+                # |
+                # |     o(x2, y2)   'o' represents the arrowhead.
+                # |    /
+                # |   /
+                # |  /theta
+                # | /(x1, y1)
+                # |______________________
+                #
+                theta = 0.0
+                x1 = startPositionX
+                y1 = startPositionY
+                x2 = positionOnLineX
+                y2 = positionOnLineY
+
+                if x1 == x2 and y1 == y2:
+                    theta = 0.0
+                elif x1 == x2 and y1>y2:
+                    theta = 3.0 * pi / 2
+                elif x1 == x2 and y2>y1:
+                    theta = pi / 2
+                elif x2>x1 and y2 >= y1:
+                    theta = math.atan((y2-y1) / (x2-x1))
+                elif x2<x1:
+                    theta = pi + math.atan((y2-y1) / (x2-x1))
+                elif x2>x1 and y2<y1:
+                    theta = 2 * pi + math.atan((y2-y1) / (x2-x1))
+                else:
+                    raise "Unknown arrowhead rotation case"
+
+                # Rotate about the centre of the object, then place
+                # the object on the line.
+                if arrow.GetMetaFile().GetRotateable():
+                    arrow.GetMetaFile().Rotate(0.0, 0.0, theta)
+
+                if self._erasing:
+                    # If erasing, just draw a rectangle
+                    minX, minY, maxX, maxY = arrow.GetMetaFile().GetBounds()
+                    # Make erasing rectangle slightly bigger or you get droppings
+                    extraPixels = 4
+                    dc.DrawRectangle(deltaX + x + minX-extraPixels / 2, deltaY + y + minY-extraPixels / 2, maxX-minX + extraPixels, maxY-minY + extraPixels)
+                else:
+                    arrow.GetMetaFile().Draw(dc, x + deltaX, y + deltaY)
+
+    def OnErase(self, dc):
+        old_pen = self._pen
+        old_brush = self._brush
+
+        bg_pen = self.GetBackgroundPen()
+        bg_brush = self.GetBackgroundBrush()
+        self.SetPen(bg_pen)
+        self.SetBrush(bg_brush)
+
+        bound_x, bound_y = self.GetBoundingBoxMax()
+        if self._font:
+            dc.SetFont(self._font)
+
+        # Undraw text regions
+        for i in range(3):
+            if self._regions[i]:
+                x, y = self.GetLabelPosition(i)
+                self.EraseRegion(dc, self._regions[i], x, y)
+
+        # Undraw line
+        dc.SetPen(self.GetBackgroundPen())
+        dc.SetBrush(self.GetBackgroundBrush())
+
+        # Drawing over the line only seems to work if the line has a thickness
+        # of 1.
+        if old_pen and old_pen.GetWidth()>1:
+            dc.DrawRectangle(self._xpos-bound_x / 2-2, self._ypos-bound_y / 2-2,
+                             bound_x + 4, bound_y + 4)
+        else:
+            self._erasing = True
+            self.GetEventHandler().OnDraw(dc)
+            self.GetEventHandler().OnEraseControlPoints(dc)
+            self._erasing = False
+
+        if old_pen:
+            self.SetPen(old_pen)
+        if old_brush:
+            self.SetBrush(old_brush)
+
+    def GetBoundingBoxMin(self):
+        x1, y1 = 10000, 10000
+        x2, y2=-10000,-10000
+
+        for point in self._lineControlPoints:
+            if point[0]<x1:
+                x1 = point[0]
+            if point[1]<y1:
+                y1 = point[1]
+            if point[0]>x2:
+                x2 = point[0]
+            if point[1]>y2:
+                y2 = point[1]
+
+        return x2-x1, y2-y1
+        
+    # For a node image of interest, finds the position of this arc
+    # amongst all the arcs which are attached to THIS SIDE of the node image,
+    # and the number of same.
+    def FindNth(self, image, incoming):
+        """Find the position of the line on the given object.
+
+        Specify whether incoming or outgoing lines are being considered
+        with incoming.
+        """
+        n=-1
+        num = 0
+        
+        if image == self._to:
+            this_attachment = self._attachmentTo
+        else:
+            this_attachment = self._attachmentFrom
+
+        # Find number of lines going into / out of this particular attachment point
+        for line in image.GetLines():
+            if line._from == image:
+                # This is the nth line attached to 'image'
+                if line == self and not incoming:
+                    n = num
+
+                # Increment num count if this is the same side (attachment number)
+                if line._attachmentFrom == this_attachment:
+                    num += 1
+
+            if line._to == image:
+                # This is the nth line attached to 'image'
+                if line == self and incoming:
+                    n = num
+
+                # Increment num count if this is the same side (attachment number)
+                if line._attachmentTo == this_attachment:
+                    num += 1
+
+        return n, num
+
+    def OnDrawOutline(self, dc, x, y, w, h):
+        old_pen = self._pen
+        old_brush = self._brush
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        self.SetPen(dottedPen)
+        self.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        self.GetEventHandler().OnDraw(dc)
+
+        if old_pen:
+            self.SetPen(old_pen)
+        else:
+            self.SetPen(None)
+        if old_brush:
+            self.SetBrush(old_brush)
+        else:
+            self.SetBrush(None)
+
+    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
+        x_offset = x-old_x
+        y_offset = y-old_y
+
+        if self._lineControlPoints and not (x_offset == 0 and y_offset == 0):
+            for point in self._lineControlPoints:
+                point[0] += x_offset
+                point[1] += y_offset
+
+        # Move temporary label rectangles if necessary
+        for i in range(3):
+            if self._labelObjects[i]:
+                self._labelObjects[i].Erase(dc)
+                xp, yp = self.GetLabelPosition(i)
+                if i<len(self._regions):
+                    xr, yr = self._regions[i].GetPosition()
+                else:
+                    xr, yr = 0, 0
+                self._labelObjects[i].Move(dc, xp + xr, yp + yr)
+        return True
+
+    def OnMoveLink(self, dc, moveControlPoints = True):
+        """Called when a connected object has moved, to move the link to
+        correct position
+        """
+        if not self._from or not self._to:
+            return
+
+        if len(self._lineControlPoints)>2:
+            self.Initialise()
+
+        # Do each end - nothing in the middle. User has to move other points
+        # manually if necessary
+        end_x, end_y, other_end_x, other_end_y = self.FindLineEndPoints()
+
+        first = self._lineControlPoints[0]
+        last = self._lineControlPoints[-1]
+
+        oldX, oldY = self._xpos, self._ypos
+
+        self.SetEnds(end_x, end_y, other_end_x, other_end_y)
+
+        # Do a second time, because one may depend on the other
+        end_x, end_y, other_end_x, other_end_y = self.FindLineEndPoints()
+        self.SetEnds(end_x, end_y, other_end_x, other_end_y)
+
+        # Try to move control points with the arc
+        x_offset = self._xpos-oldX
+        y_offset = self._ypos-oldY
+
+        # Only move control points if it's a self link. And only works
+        # if attachment mode is ON
+        if self._from == self._to and self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE and moveControlPoints and self._lineControlPoints and not (x_offset == 0 and y_offset == 0):
+            for point in self._lineControlPoints[1:-1]:
+                point.x += x_offset
+                point.y += y_offset
+
+        self.Move(dc, self._xpos, self._ypos)
+
+    def FindLineEndPoints(self):
+        """Finds the x, y points at the two ends of the line.
+
+        This function can be used by e.g. line-routing routines to
+        get the actual points on the two node images where the lines will be
+        drawn to / from.
+        """
+        if not self._from or not self._to:
+            return
+
+        # Do each end - nothing in the middle. User has to move other points
+        # manually if necessary.
+        second_point = self._lineControlPoints[1]
+        second_last_point = self._lineControlPoints[-2]
+        
+        if len(self._lineControlPoints)>2:
+            if self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
+                nth, no_arcs = self.FindNth(self._from, False) # Not incoming
+                end_x, end_y = self._from.GetAttachmentPosition(self._attachmentFrom, nth, no_arcs, self)
+            else:
+                end_x, end_y = self._from.GetPerimeterPoint(self._from.GetX(), self._from.GetY(), second_point[0], second_point[1])
+
+            if self._to.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
+                nth, no_arch = self.FindNth(self._to, True) # Incoming
+                other_end_x, other_end_y = self._to.GetAttachmentPosition(self._attachmentTo, nth, no_arch, self)
+            else:
+                other_end_x, other_end_y = self._to.GetPerimeterPoint(self._to.GetX(), self._to.GetY(), second_last_point[0], second_last_point[1])
+        else:
+            fromX = self._from.GetX()
+            fromY = self._from.GetY()
+            toX = self._to.GetX()
+            toY = self._to.GetY()
+
+            if self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
+                nth, no_arcs = self.FindNth(self._from, False)
+                end_x, end_y = self._from.GetAttachmentPosition(self._attachmentFrom, nth, no_arcs, self)
+                fromX = end_x
+                fromY = end_y
+
+            if self._to.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
+                nth, no_arcs = self.FindNth(self._to, True)
+                other_end_x, other_end_y = self._to.GetAttachmentPosition(self._attachmentTo, nth, no_arcs, self)
+                toX = other_end_x
+                toY = other_end_y
+
+            if self._from.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
+                end_x, end_y = self._from.GetPerimeterPoint(self._from.GetX(), self._from.GetY(), toX, toY)
+
+            if self._to.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
+                other_end_x, other_end_y = self._to.GetPerimeterPoint(self._to.GetX(), self._to.GetY(), fromX, fromY)
+
+            #print type(self._from), type(self._to), end_x, end_y, other_end_x, other_end_y
+            return end_x, end_y, other_end_x, other_end_y
+
+    def OnDraw(self, dc):
+        if not self._lineControlPoints:
+            return
+
+        if self._pen:
+            dc.SetPen(self._pen)
+        if self._brush:
+            dc.SetBrush(self._brush)
+
+        points = []
+        for point in self._lineControlPoints:
+            points.append(wx.Point(point.x, point.y))
+
+        #print points
+        if self._isSpline:
+            dc.DrawSpline(points)
+        else:
+            dc.DrawLines(points)
+
+        if sys.platform[:3]=="win":
+            # For some reason, last point isn't drawn under Windows
+            pt = points[-1]
+            dc.DrawPoint(pt.x, pt.y)
+
+        # Problem with pen - if not a solid pen, does strange things
+        # to the arrowhead. So make (get) a new pen that's solid.
+        if self._pen and self._pen.GetStyle() != wx.SOLID:
+            solid_pen = wx.ThePenList().FindOrCreatePen(self._pen.GetColour(), 1, wx.SOLID)
+            if solid_pen:
+                dc.SetPen(solid_pen)
+
+        self.DrawArrows(dc)
+
+    def OnDrawControlPoints(self, dc):
+        if not self._drawHandles:
+            return
+
+        # Draw temporary label rectangles if necessary
+        for i in range(3):
+            if self._labelObjects[i]:
+                self._labelObjects[i].Draw(dc)
+
+        Shape.OnDrawControlPoints(self, dc)
+
+    def OnEraseControlPoints(self, dc):
+        # Erase temporary label rectangles if necessary
+        
+        for i in range(3):
+            if self._labelObjects[i]:
+                self._labelObjects[i].Erase(dc)
+
+        Shape.OnEraseControlPoints(self, dc)
+
+    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
+        pass
+
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        pass
+
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        pass
+
+    def OnDrawContents(self, dc):
+        if self.GetDisableLabel():
+            return
+
+        for i in range(3):
+            if self._regions[i]:
+                x, y = self.GetLabelPosition(i)
+                self.DrawRegion(dc, self._regions[i], x, y)
+
+    def SetTo(self, object):
+        """Set the 'to' object for the line."""
+        self._to = object
+
+    def SetFrom(self, object):
+        """Set the 'from' object for the line."""
+        self._from = object
+
+    def MakeControlPoints(self):
+        """Make handle control points."""
+        if self._canvas and self._lineControlPoints:
+            first = self._lineControlPoints[0]
+            last = self._lineControlPoints[-1]
+
+            control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, first[0], first[1], CONTROL_POINT_ENDPOINT_FROM)
+            control._point = first
+            self._canvas.AddShape(control)
+            self._controlPoints.Append(control)
+
+            for point in self._lineControlPoints[1:-1]:
+                control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, point[0], point[1], CONTROL_POINT_LINE)
+                control._point = point
+                self._canvas.AddShape(control)
+                self._controlPoints.Append(control)
+
+            control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, last[0], last[1], CONTROL_POINT_ENDPOINT_TO)
+            control._point = last
+            self._canvas.AddShape(control)
+            self._controlPoints.Append(control)
+
+    def ResetControlPoints(self):
+        if self._canvas and self._lineControlPoints:
+            for i in range(min(len(self._controlPoints), len(self._lineControlPoints))):
+                point = self._lineControlPoints[i]
+                control = self._controlPoints[i]
+                control.SetX(point[0])
+                control.SetY(point[1])
+
+    # Override select, to create / delete temporary label-moving objects
+    def Select(self, select, dc = None):
+        Shape.Select(self, select, dc)
+        if select:
+            for i in range(3):
+                if self._regions[i]:
+                    region = self._regions[i]
+                    if region._formattedText:
+                        w, h = region.GetSize()
+                        x, y = region.GetPosition()
+                        xx, yy = self.GetLabelPosition(i)
+
+                        if self._labelObjects[i]:
+                            self._labelObjects[i].Select(False)
+                            self._labelObjects[i].RemoveFromCanvas(self._canvas)
+
+                        self._labelObjects[i] = self.OnCreateLabelShape(self, region, w, h)
+                        self._labelObjects[i].AddToCanvas(self._canvas)
+                        self._labelObjects[i].Show(True)
+                        if dc:
+                            self._labelObjects[i].Move(dc, x + xx, y + yy)
+                        self._labelObjects[i].Select(True, dc)
+        else:
+            for i in range(3):
+                if self._labelObjects[i]:
+                    self._labelObjects[i].Select(False, dc)
+                    self._labelObjects[i].Erase(dc)
+                    self._labelObjects[i].RemoveFromCanvas(self._canvas)
+                    self._labelObjects[i] = None
+
+    # Control points ('handles') redirect control to the actual shape, to
+    # make it easier to override sizing behaviour.
+    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        dc.SetLogicalFunction(OGLRBLF)
+
+        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+        dc.SetPen(dottedPen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        if pt._type == CONTROL_POINT_LINE:
+            x, y = self._canvas.Snap()
+
+            pt.SetX(x)
+            pt.SetY(y)
+            pt._point[0] = x
+            pt._point[1] = y
+
+            old_pen = self.GetPen()
+            old_brush = self.GetBrush()
+
+            self.SetPen(dottedPen)
+            self.SetBrush(wx.TRANSPARENT_BRUSH)
+
+            self.GetEventHandler().OnMoveLink(dc, False)
+            
+            self.SetPen(old_pen)
+            self.SetBrush(old_brush)
+
+    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        if pt._type == CONTROL_POINT_LINE:
+            pt._originalPos = pt._point
+            x, y = self._canvas.Snap()
+
+            self.Erase(dc)
+
+            # Redraw start and end objects because we've left holes
+            # when erasing the line
+            self.GetFrom().OnDraw(dc)
+            self.GetFrom().OnDrawContents(dc)
+            self.GetTo().OnDraw(dc)
+            self.GetTo().OnDrawContents(dc)
+
+            self.SetDisableLabel(True)
+            dc.SetLogicalFunction(OGLRBLF)
+
+            pt._xpos = x
+            pt._ypos = y
+            pt._point[0] = x
+            pt._point[1] = y
+
+            old_pen = self.GetPen()
+            old_brush = self.GetBrush()
+
+            dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
+            self.SetPen(dottedPen)
+            self.SetBrush(wx.TRANSPARENT_BRUSH)
+
+            self.GetEventHandler().OnMoveLink(dc, False)
+
+            self.SetPen(old_pen)
+            self.SetBrush(old_brush)
+
+        if pt._type == CONTROL_POINT_ENDPOINT_FROM or pt._type == CONTROL_POINT_ENDPOINT_TO:
+            self._canvas.SetCursor(wx.Cursor(wx.CURSOR_BULLSEYE))
+            pt._oldCursor = wx.STANDARD_CURSOR
+
+    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
+        dc = wx.ClientDC(self.GetCanvas())
+        self.GetCanvas().PrepareDC(dc)
+
+        self.SetDisableLabel(False)
+
+        if pt._type == CONTROL_POINT_LINE:
+            x, y = self._canvas.Snap()
+
+            rpt = wx.RealPoint(x, y)
+
+            # Move the control point back to where it was;
+            # MoveControlPoint will move it to the new position
+            # if it decides it wants. We only moved the position
+            # during user feedback so we could redraw the line
+            # as it changed shape.
+            pt._xpos = pt._originalPos[0]
+            pt._ypos = pt._originalPos[1]
+            pt._point[0] = pt._originalPos[0]
+            pt._point[1] = pt._originalPos[1]
+
+            self.OnMoveMiddleControlPoint(dc, pt, rpt)
+
+        if pt._type == CONTROL_POINT_ENDPOINT_FROM:
+            if pt._oldCursor:
+                self._canvas.SetCursor(pt._oldCursor)
+
+                if self.GetFrom():
+                    self.GetFrom().MoveLineToNewAttachment(dc, self, x, y)
+
+        if pt._type == CONTROL_POINT_ENDPOINT_TO:
+            if pt._oldCursor:
+                self._canvas.SetCursor(pt._oldCursor)
+
+                if self.GetTo():
+                    self.GetTo().MoveLineToNewAttachment(dc, self, x, y)
+
+    # This is called only when a non-end control point is moved
+    def OnMoveMiddleControlPoint(self, dc, lpt, pt):
+        lpt._xpos = pt[0]
+        lpt._ypos = pt[1]
+
+        lpt._point[0] = pt[0]
+        lpt._point[1] = pt[1]
+
+        self.GetEventHandler().OnMoveLink(dc)
+
+        return True
+
+    def AddArrow(self, type, end = ARROW_POSITION_END, size = 10.0, xOffset = 0.0, name="",mf = None, arrowId=-1):
+        """Add an arrow (or annotation) to the line.
+
+        type may currently be one of:
+
+        ARROW_HOLLOW_CIRCLE
+          Hollow circle. 
+        ARROW_FILLED_CIRCLE
+          Filled circle. 
+        ARROW_ARROW
+          Conventional arrowhead. 
+        ARROW_SINGLE_OBLIQUE
+          Single oblique stroke. 
+        ARROW_DOUBLE_OBLIQUE
+          Double oblique stroke. 
+        ARROW_DOUBLE_METAFILE
+          Custom arrowhead. 
+
+        end may currently be one of:
+
+        ARROW_POSITION_END
+          Arrow appears at the end. 
+        ARROW_POSITION_START
+          Arrow appears at the start. 
+
+        arrowSize specifies the length of the arrow.
+
+        xOffset specifies the offset from the end of the line.
+
+        name specifies a name for the arrow.
+
+        mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
+        metafile.
+
+        arrowId is the id for the arrow.
+        """
+        arrow = ArrowHead(type, end, size, xOffset, name, mf, arrowId)
+        self._arcArrows.append(arrow)
+        return arrow
+
+    # Add arrowhead at a particular position in the arrowhead list
+    def AddArrowOrdered(self, arrow, referenceList, end):
+        """Add an arrowhead in the position indicated by the reference list
+        of arrowheads, which contains all legal arrowheads for this line, in
+        the correct order. E.g.
+
+        Reference list:      a b c d e
+        Current line list:   a d
+
+        Add c, then line list is: a c d.
+
+        If no legal arrowhead position, return FALSE. Assume reference list
+        is for one end only, since it potentially defines the ordering for
+        any one of the 3 positions. So we don't check the reference list for
+        arrowhead position.
+        """
+        if not referenceList:
+            return False
+
+        targetName = arrow.GetName()
+
+        # First check whether we need to insert in front of list,
+        # because this arrowhead is the first in the reference
+        # list and should therefore be first in the current list.
+        refArrow = referenceList[0]
+        if refArrow.GetName() == targetName:
+            self._arcArrows.insert(0, arrow)
+            return True
+
+        i1 = i2 = 0
+        while i1<len(referenceList) and i2<len(self._arcArrows):
+            refArrow = referenceList[i1]
+            currArrow = self._arcArrows[i2]
+
+            # Matching: advance current arrow pointer
+            if currArrow.GetArrowEnd() == end and currArrow.GetName() == refArrow.GetName():
+                i2 += 1
+
+            # Check if we're at the correct position in the
+            # reference list
+            if targetName == refArrow.GetName():
+                if i2<len(self._arcArrows):
+                    self._arcArrows.insert(i2, arrow)
+                else:
+                    self._arcArrows.append(arrow)
+                return True
+            i1 += 1
+
+        self._arcArrows.append(arrow)
+        return True
+
+    def ClearArrowsAtPosition(self, end):
+        """Delete the arrows at the specified position, or at any position
+        if position is -1.
+        """
+        if end==-1:
+            self._arcArrows = []
+            return
+
+        for arrow in self._arcArrows:
+            if arrow.GetArrowEnd() == end:
+                self._arcArrows.remove(arrow)
+
+    def ClearArrow(self, name):
+        """Delete the arrow with the given name."""
+        for arrow in self._arcArrows:
+            if arrow.GetName() == name:
+                self._arcArrows.remove(arrow)
+                return True
+        return False
+
+    def FindArrowHead(self, position, name):
+        """Find arrowhead by position and name.
+
+        if position is -1, matches any position.
+        """
+        for arrow in self._arcArrows:
+            if (position==-1 or position == arrow.GetArrowEnd()) and arrow.GetName() == name:
+                return arow
+
+        return None
+
+    def FindArrowHeadId(self, arrowId):
+        """Find arrowhead by id."""
+        for arrow in self._arcArrows:
+            if arrowId == arrow.GetId():
+                return arrow
+
+        return None
+
+    def DeleteArrowHead(self, position, name):
+        """Delete arrowhead by position and name.
+
+        if position is -1, matches any position.
+        """
+        for arrow in self._arcArrows:
+            if (position==-1 or position == arrow.GetArrowEnd()) and arrow.GetName() == name:
+                self._arcArrows.remove(arrow)
+                return True
+        return False
+    
+    def DeleteArrowHeadId(self, id):
+        """Delete arrowhead by id."""
+        for arrow in self._arcArrows:
+            if arrowId == arrow.GetId():
+                self._arcArrows.remove(arrow)
+                return True
+        return False
+
+    # Calculate the minimum width a line
+    # occupies, for the purposes of drawing lines in tools.
+    def FindMinimumWidth(self):
+        """Find the horizontal width for drawing a line with arrows in
+        minimum space. Assume arrows at end only.
+        """
+        minWidth = 0.0
+        for arrowHead in self._arcArrows:
+            minWidth += arrowHead.GetSize()
+            if arrowHead != self._arcArrows[-1]:
+                minWidth += arrowHead + GetSpacing
+
+        # We have ABSOLUTE minimum now. So
+        # scale it to give it reasonable aesthetics
+        # when drawing with line.
+        if minWidth>0:
+            minWidth = minWidth * 1.4
+        else:
+            minWidth = 20.0
+
+        self.SetEnds(0.0, 0.0, minWidth, 0.0)
+        self.Initialise()
+
+        return minWidth
+
+    def FindLinePosition(self, x, y):
+        """Find which position we're talking about at this x, y.
+
+        Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
+        """
+        startX, startY, endX, endY = self.GetEnds()
+
+        # Find distances from centre, start and end. The smallest wins
+        centreDistance = math.sqrt((x-self._xpos) * (x-self._xpos) + (y-self._ypos) * (y-self._ypos))
+        startDistance = math.sqrt((x-startX) * (x-startX) + (y-startY) * (y-startY))
+        endDistance = math.sqrt((x-endX) * (x-endX) + (y-endY) * (y-endY))
+
+        if centreDistance<startDistance and centreDistance<endDistance:
+            return ARROW_POSITION_MIDDLE
+        elif startDistance<endDistance:
+            return ARROW_POSITION_START
+        else:
+            return ARROW_POSITION_END
+
+    def SetAlignmentOrientation(self, isEnd, isHoriz):
+        if isEnd:
+            if isHoriz and self._alignmentEnd & LINE_ALIGNMENT_HORIZ != LINE_ALIGNMENT_HORIZ:
+                self._alignmentEnd != LINE_ALIGNMENT_HORIZ
+            elif not isHoriz and self._alignmentEnd & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ:
+                self._alignmentEnd -= LINE_ALIGNMENT_HORIZ
+        else:
+            if isHoriz and self._alignmentStart & LINE_ALIGNMENT_HORIZ != LINE_ALIGNMENT_HORIZ:
+                self._alignmentStart != LINE_ALIGNMENT_HORIZ
+            elif not isHoriz and self._alignmentStart & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ:
+                self._alignmentStart -= LINE_ALIGNMENT_HORIZ
+            
+    def SetAlignmentType(self, isEnd, alignType):
+        if isEnd:
+            if alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                if self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE != LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                    self._alignmentEnd |= LINE_ALIGNMENT_TO_NEXT_HANDLE
+            elif self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                self._alignmentEnd -= LINE_ALIGNMENT_TO_NEXT_HANDLE
+        else:
+            if alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                if self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE != LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                    self._alignmentStart |= LINE_ALIGNMENT_TO_NEXT_HANDLE
+            elif self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE == LINE_ALIGNMENT_TO_NEXT_HANDLE:
+                self._alignmentStart -= LINE_ALIGNMENT_TO_NEXT_HANDLE
+            
+    def GetAlignmentOrientation(self, isEnd):
+        if isEnd:
+            return self._alignmentEnd & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ
+        else:
+            return self._alignmentStart & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ
+
+    def GetAlignmentType(self, isEnd):
+        if isEnd:
+            return self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE
+        else:
+            return self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE
+
+    def GetNextControlPoint(self, shape):
+        """Find the next control point in the line after the start / end point,
+        depending on whether the shape is at the start or end.
+        """
+        n = len(self._lineControlPoints)
+        if self._to == shape:
+            # Must be END of line, so we want (n - 1)th control point.
+            # But indexing ends at n-1, so subtract 2.
+            nn = n-2
+        else:
+            nn = 1
+        if nn<len(self._lineControlPoints):
+            return self._lineControlPoints[nn]
+        return None
+
+    def OnCreateLabelShape(self, parent, region, w, h):
+        return LabelShape(parent, region, w, h)
+
+    
+    def OnLabelMovePre(self, dc, labelShape, x, y, old_x, old_y, display):
+        labelShape._shapeRegion.SetSize(labelShape.GetWidth(), labelShape.GetHeight())
+
+        # Find position in line's region list
+        i = 0
+        for region in self.GetRegions():
+            if labelShape._shapeRegion == region:
+                self.GetRegions().remove(region)
+            else:
+                i += 1
+                
+        xx, yy = self.GetLabelPosition(i)
+        # Set the region's offset, relative to the default position for
+        # each region.
+        labelShape._shapeRegion.SetPosition(x-xx, y-yy)
+        labelShape.SetX(x)
+        labelShape.SetY(y)
+
+        # Need to reformat to fit region
+        if labelShape._shapeRegion.GetText():
+            s = labelShape._shapeRegion.GetText()
+            labelShape.FormatText(dc, s, i)
+            self.DrawRegion(dc, labelShape._shapeRegion, xx, yy)
+        return True
+    
diff --git a/wxPython/wx/lib/ogl/_oglmisc.py b/wxPython/wx/lib/ogl/_oglmisc.py
new file mode 100644 (file)
index 0000000..42a79b2
--- /dev/null
@@ -0,0 +1,416 @@
+# -*- coding: iso-8859-1 -*-
+#----------------------------------------------------------------------------
+# Name:         oglmisc.py
+# Purpose:      Miscellaneous OGL support functions
+#
+# Author:       Pierre Hjälm (from C++ original by Julian Smart)
+#
+# Created:      2004-05-08
+# RCS-ID:       $Id$
+# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
+# Licence:      wxWindows license
+#----------------------------------------------------------------------------
+
+from __future__ import division
+import math
+
+import wx
+
+# Control point types
+# Rectangle and most other shapes
+CONTROL_POINT_VERTICAL = 1
+CONTROL_POINT_HORIZONTAL = 2
+CONTROL_POINT_DIAGONAL = 3
+
+# Line
+CONTROL_POINT_ENDPOINT_TO = 4
+CONTROL_POINT_ENDPOINT_FROM = 5
+CONTROL_POINT_LINE = 6
+
+# Types of formatting: can be combined in a bit list
+FORMAT_NONE = 0             # Left justification
+FORMAT_CENTRE_HORIZ = 1     # Centre horizontally
+FORMAT_CENTRE_VERT = 2      # Centre vertically
+FORMAT_SIZE_TO_CONTENTS = 4 # Resize shape to contents
+
+# Attachment modes
+ATTACHMENT_MODE_NONE, ATTACHMENT_MODE_EDGE, ATTACHMENT_MODE_BRANCHING = 0, 1, 2
+
+# Shadow mode
+SHADOW_NONE, SHADOW_LEFT, SHADOW_RIGHT = 0, 1, 2
+
+OP_CLICK_LEFT, OP_CLICK_RIGHT, OP_DRAG_LEFT, OP_DRAG_RIGHT = 1, 2, 4, 8
+OP_ALL = OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_LEFT | OP_DRAG_RIGHT
+
+# Sub-modes for branching attachment mode
+BRANCHING_ATTACHMENT_NORMAL = 1
+BRANCHING_ATTACHMENT_BLOB = 2
+
+# logical function to use when drawing rubberband boxes, etc.
+OGLRBLF = wx.INVERT
+
+CONTROL_POINT_SIZE = 6
+
+# Types of arrowhead
+# (i) Built-in
+ARROW_HOLLOW_CIRCLE   = 1
+ARROW_FILLED_CIRCLE   = 2
+ARROW_ARROW           = 3
+ARROW_SINGLE_OBLIQUE  = 4
+ARROW_DOUBLE_OBLIQUE  = 5
+# (ii) Custom
+ARROW_METAFILE        = 20
+
+# Position of arrow on line
+ARROW_POSITION_START  = 0
+ARROW_POSITION_END    = 1
+ARROW_POSITION_MIDDLE = 2
+
+# Line alignment flags
+# Vertical by default
+LINE_ALIGNMENT_HORIZ              = 1
+LINE_ALIGNMENT_VERT               = 0
+LINE_ALIGNMENT_TO_NEXT_HANDLE     = 2
+LINE_ALIGNMENT_NONE               = 0
+
+
+
+# Format a string to a list of strings that fit in the given box.
+# Interpret %n and 10 or 13 as a new line.
+def FormatText(dc, text, width, height, formatMode):
+    i = 0
+    word=""
+    word_list = []
+    end_word = False
+    new_line = False
+    while i<len(text):
+        if text[i]=="%":
+            i += 1
+            if i == len(text):
+                word+="%"
+            else:
+                if text[i]=="n":
+                    new_line = True
+                    end_word = True
+                    i += 1
+                else:
+                    word+="%"+text[i]
+                    i += 1
+        elif text[i] in ["\012","\015"]:
+            new_line = True
+            end_word = True
+            i += 1
+        elif text[i]==" ":
+            end_word = True
+            i += 1
+        else:
+            word += text[i]
+            i += 1
+
+        if i == len(text):
+            end_word = True
+
+        if end_word:
+            word_list.append(word)
+            word=""
+            end_word = False
+        if new_line:
+            word_list.append(None)
+            new_line = False
+
+    # Now, make a list of strings which can fit in the box
+    string_list = []
+    buffer=""
+    for s in word_list:
+        oldBuffer = buffer
+        if s is None:
+            # FORCE NEW LINE
+            if len(buffer)>0:
+                string_list.append(buffer)
+            buffer=""
+        else:
+            if len(buffer):
+                buffer+=" "
+            buffer += s
+            x, y = dc.GetTextExtent(buffer)
+
+            # Don't fit within the bounding box if we're fitting
+            # shape to contents
+            if (x>width) and not (formatMode & FORMAT_SIZE_TO_CONTENTS):
+                # Deal with first word being wider than box
+                if len(oldBuffer):
+                    string_list.append(oldBuffer)
+                buffer = s
+    if len(buffer):
+        string_list.append(buffer)
+
+    return string_list
+
+
+
+def GetCentredTextExtent(dc, text_list, xpos = 0, ypos = 0, width = 0, height = 0):
+    if not text_list:
+        return 0, 0
+
+    max_width = 0
+    for line in text_list:
+        current_width, char_height = dc.GetTextExtent(line)
+        if current_width>max_width:
+            max_width = current_width
+
+    return max_width, len(text_list) * char_height
+
+
+
+def CentreText(dc, text_list, xpos, ypos, width, height, formatMode):
+    if not text_list:
+        return
+
+    # First, get maximum dimensions of box enclosing text
+    char_height = 0
+    max_width = 0
+    current_width = 0
+
+    # Store text extents for speed
+    widths = []
+    for line in text_list:
+        current_width, char_height = dc.GetTextExtent(line.GetText())
+        widths.append(current_width)
+        if current_width>max_width:
+            max_width = current_width
+
+    max_height = len(text_list) * char_height
+
+    if formatMode & FORMAT_CENTRE_VERT:
+        if max_height<height:
+            yoffset = ypos - height / 2 + (height - max_height) / 2
+        else:
+            yoffset = ypos - height / 2
+        yOffset = ypos
+    else:
+        yoffset = 0.0
+        yOffset = 0.0
+
+    if formatMode & FORMAT_CENTRE_HORIZ:
+        xoffset = xpos - width / 2
+        xOffset = xpos
+    else:
+        xoffset = 0.0
+        xOffset = 0.0
+
+    for i, line in enumerate(text_list):
+        if formatMode & FORMAT_CENTRE_HORIZ and widths[i]<width:
+            x = (width - widths[i]) / 2 + xoffset
+        else:
+            x = xoffset
+        y = i * char_height + yoffset
+
+        line.SetX(x - xOffset)
+        line.SetY(y - yOffset)
+        
+
+
+def DrawFormattedText(dc, text_list, xpos, ypos, width, height, formatMode):
+    if formatMode & FORMAT_CENTRE_HORIZ:
+        xoffset = xpos
+    else:
+        xoffset = xpos - width / 2
+
+    if formatMode & FORMAT_CENTRE_VERT:
+        yoffset = ypos
+    else:
+        yoffset = ypos - height / 2
+
+    # +1 to allow for rounding errors
+    dc.SetClippingRegion(xpos - width / 2, ypos - height / 2, width + 1, height + 1)
+
+    for line in text_list:
+        dc.DrawText(line.GetText(), xoffset + line.GetX(), yoffset + line.GetY())
+
+    dc.DestroyClippingRegion()
+
+
+
+def RoughlyEqual(val1, val2, tol = 0.00001):
+    return val1<(val2 + tol) and val1>(val2 - tol) and \
+           val2<(val1 + tol) and val2>(val1 - tol)
+
+
+
+def FindEndForBox(width, height, x1, y1, x2, y2):
+    xvec = [x1 - width / 2, x1 - width / 2, x1 + width / 2, x1 + width / 2, x1 - width / 2]
+    yvec = [y1 - height / 2, y1 + height / 2, y1 + height / 2, y1 - height / 2, y1 - height / 2]
+
+    return FindEndForPolyline(xvec, yvec, x2, y2, x1, y1)
+
+
+
+def CheckLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
+    denominator_term = (y4 - y3) * (x2 - x1) - (y2 - y1) * (x4 - x3)
+    numerator_term = (x3 - x1) * (y4 - y3) + (x4 - x3) * (y1 - y3)
+
+    length_ratio = 1.0
+    k_line = 1.0
+
+    # Check for parallel lines
+    if denominator_term<0.005 and denominator_term>-0.005:
+        line_constant=-1.0
+    else:
+        line_constant = float(numerator_term) / denominator_term
+
+    # Check for intersection
+    if line_constant<1.0 and line_constant>0.0:
+        # Now must check that other line hits
+        if (y4 - y3)<0.005 and (y4 - y3)>-0.005:
+            k_line = (x1 - x3 + line_constant * (x2 - x1)) / (x4 - x3)
+        else:
+            k_line = (y1 - y3 + line_constant * (y2 - y1)) / (y4 - y3)
+        if k_line >= 0 and k_line<1:
+            length_ratio = line_constant
+        else:
+            k_line = 1
+
+    return length_ratio, k_line
+
+
+
+def FindEndForPolyline(xvec, yvec, x1, y1, x2, y2):
+    lastx = xvec[0]
+    lasty = yvec[0]
+
+    min_ratio = 1.0
+
+    for i in range(1, len(xvec)):
+        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
+        lastx = xvec[i]
+        lasty = yvec[i]
+
+        if line_ratio<min_ratio:
+            min_ratio = line_ratio
+
+    # Do last (implicit) line if last and first doubles are not identical
+    if not (xvec[0] == lastx and yvec[0] == lasty):
+        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
+        if line_ratio<min_ratio:
+            min_ratio = line_ratio
+
+    return x1 + (x2 - x1) * min_ratio, y1 + (y2 - y1) * min_ratio
+
+
+
+def PolylineHitTest(xvec, yvec, x1, y1, x2, y2):
+    isAHit = False
+    lastx = xvec[0]
+    lasty = yvec[0]
+
+    min_ratio = 1.0
+
+    for i in range(1, len(xvec)):
+        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
+        if line_ratio != 1.0:
+            isAHit = True
+        lastx = xvec[i]
+        lasty = yvec[i]
+
+        if line_ratio<min_ratio:
+            min_ratio = line_ratio
+
+    # Do last (implicit) line if last and first doubles are not identical
+    if not (xvec[0] == lastx and yvec[0] == lasty):
+        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
+        if line_ratio != 1.0:
+            isAHit = True
+
+    return isAHit
+
+
+
+def GraphicsStraightenLine(point1, point2):
+    dx = point2[0] - point1[0]
+    dy = point2[1] - point1[1]
+
+    if dx == 0:
+        return
+    elif abs(dy / dx)>1:
+        point2[0] = point1[0]
+    else:
+        point2[1] = point1[0]
+
+
+
+def GetPointOnLine(x1, y1, x2, y2, length):
+    l = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
+    if l<0.01:
+        l = 0.01
+
+    i_bar = (x2 - x1) / l
+    j_bar = (y2 - y1) / l
+
+    return -length * i_bar + x2,-length * j_bar + y2
+
+
+
+def GetArrowPoints(x1, y1, x2, y2, length, width):
+    l = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
+
+    if l<0.01:
+        l = 0.01
+
+    i_bar = (x2 - x1) / l
+    j_bar = (y2 - y1) / l
+    
+    x3=-length * i_bar + x2
+    y3=-length * j_bar + y2
+
+    return x2, y2, width*-j_bar + x3, width * i_bar + y3,-width*-j_bar + x3,-width * i_bar + y3
+
+
+
+def DrawArcToEllipse(x1, y1, width1, height1, x2, y2, x3, y3):
+    a1 = width1 / 2
+    b1 = height1 / 2
+
+    # Check that x2 != x3
+    if abs(x2 - x3)<0.05:
+        x4 = x2
+        if y3>y2:
+            y4 = y1 - math.sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
+        else:
+            y4 = y1 + math.sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
+        return x4, y4
+
+    # Calculate the x and y coordinates of the point where arc intersects ellipse
+    A = (1 / (a1 * a1))
+    B = ((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1)
+    C = (2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1)
+    D = ((y2 - y1) * (y2 - y1)) / (b1 * b1)
+    E = (A + B)
+    F = (C - (2 * A * x1) - (2 * B * x2))
+    G = ((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1)
+    H = ((y3 - y2) / (x2 - x2))
+    K = ((F * F) - (4 * E * G))
+
+    if K >= 0:
+        # In this case the line intersects the ellipse, so calculate intersection
+        if x2 >= x1:
+            ellipse1_x = ((F*-1) + math.sqrt(K)) / (2 * E)
+            ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
+        else:
+            ellipse1_x = (((F*-1) - math.sqrt(K)) / (2 * E))
+            ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
+    else:
+        # in this case, arc does not intersect ellipse, so just draw arc
+        ellipse1_x = x3
+        ellipse1_y = y3
+
+    return ellipse1_x, ellipse1_y
+
+
+
+def FindEndForCircle(radius, x1, y1, x2, y2):
+    H = math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
+
+    if H == 0:
+        return x1, y1
+    else:
+        return radius * (x2 - x1) / H + x1, radius * (y2 - y1) / H + y1
diff --git a/wxPython/wx/lib/ogl/basic.py b/wxPython/wx/lib/ogl/basic.py
deleted file mode 100644 (file)
index 43bdb74..0000000
+++ /dev/null
@@ -1,3173 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         basic.py
-# Purpose:      The basic OGL shapes
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-import wx
-from math import pi, sqrt, atan, sin, cos
-
-from oglmisc import *
-
-DragOffsetX = 0.0
-DragOffsetY = 0.0
-
-
-def OGLInitialize():
-    global WhiteBackgroundPen, WhiteBackgroundBrush, TransparentPen
-    global BlackForegroundPen, NormalFont
-    
-    WhiteBackgroundPen = wx.Pen(wx.WHITE, 1, wx.SOLID)
-    WhiteBackgroundBrush = wx.Brush(wx.WHITE, wx.SOLID)
-
-    TransparentPen = wx.Pen(wx.WHITE, 1, wx.TRANSPARENT)
-    BlackForegroundPen = wx.Pen(wx.BLACK, 1, wx.SOLID)
-
-    NormalFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
-
-
-def OGLCleanUp():
-    pass
-
-
-class ShapeTextLine(object):
-    def __init__(self, the_x, the_y, the_line):
-        self._x = the_x
-        self._y = the_y
-        self._line = the_line
-
-    def GetX(self):
-        return self._x
-
-    def GetY(self):
-        return self._y
-
-    def SetX(self, x):
-        self._x = x
-
-    def SetY(self, y):
-        self._y = y
-
-    def SetText(self, text):
-        self._line = text
-
-    def GetText(self):
-        return self._line
-
-
-    
-class ShapeEvtHandler(object):
-    def __init__(self, prev = None, shape = None):
-        self._previousHandler = prev
-        self._handlerShape = shape
-
-    def __del__(self):
-        pass
-
-    def SetShape(self, sh):
-        self._handlerShape = sh
-
-    def GetShape(self):
-        return self._handlerShape
-
-    def SetPreviousHandler(self, handler):
-        self._previousHandler = handler
-
-    def GetPreviousHandler(self):
-        return self._previousHandler
-    
-    def OnDraw(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnDraw(dc)
-
-    def OnMoveLinks(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnMoveLinks(dc)
-            
-    def OnMoveLink(self, dc, moveControlPoints = True):
-        if self._previousHandler:
-            self._previousHandler.OnMoveLink(dc, moveControlPoints)
-            
-    def OnDrawContents(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnDrawContents(dc)
-            
-    def OnDrawBranches(self, dc, erase = False):
-        if self._previousHandler:
-            self._previousHandler.OnDrawBranches(dc, erase = erase)
-
-    def OnSize(self, x, y):
-        if self._previousHandler:
-            self._previousHandler.OnSize(x, y)
-            
-    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
-        if self._previousHandler:
-            return self._previousHandler.OnMovePre(dc, x, y, old_x, old_y, display)
-        else:
-            return True
-
-    def OnMovePost(self, dc, x, y, old_x, old_y, display = True):
-        if self._previousHandler:
-            return self._previousHandler.OnMovePost(dc, x, y, old_x, old_y, display)
-        else:
-            return True
-
-    def OnErase(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnErase(dc)
-
-    def OnEraseContents(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnEraseContents(dc)
-
-    def OnHighlight(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnHighlight(dc)
-
-    def OnLeftClick(self, x, y, keys, attachment):
-        if self._previousHandler:
-            self._previousHandler.OnLeftClick(x, y, keys, attachment)
-            
-    def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnLeftDoubleClick(x, y, keys, attachment)
-
-    def OnRightClick(self, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnRightClick(x, y, keys, attachment)
-
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnDragLeft(draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
-        
-    def OnDragRight(self, draw, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnDragRight(draw, x, y, keys, attachment)
-
-    def OnBeginDragRight(self, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnBeginDragRight(x, y, keys, attachment)
-
-    def OnEndDragRight(self, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnEndDragRight(x, y, keys, attachment)
-
-    # Control points ('handles') redirect control to the actual shape,
-    # to make it easier to override sizing behaviour.
-    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnSizingDragLeft(pt, draw, x, y, keys, attachment)
-
-    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnSizingBeginDragLeft(pt, x, y, keys, attachment)
-            
-    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        if self._previousHandler:
-            self._previousHandler.OnSizingEndDragLeft(pt, x, y, keys, attachment)
-
-    def OnBeginSize(self, w, h):
-        pass
-    
-    def OnEndSize(self, w, h):
-        pass
-    
-    def OnDrawOutline(self, dc, x, y, w, h):
-        if self._previousHandler:
-            self._previousHandler.OnDrawOutline(dc, x, y, w, h)
-
-    def OnDrawControlPoints(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnDrawControlPoints(dc)
-
-    def OnEraseControlPoints(self, dc):
-        if self._previousHandler:
-            self._previousHandler.OnEraseControlPoints(dc)
-
-    # Can override this to prevent or intercept line reordering.
-    def OnChangeAttachment(self, attachment, line, ordering):
-        if self._previousHandler:
-            self._previousHandler.OnChangeAttachment(attachment, line, ordering)
-
-
-            
-class Shape(ShapeEvtHandler):
-    """OGL base class
-
-    Shape(canvas = None)
-    
-    The wxShape is the top-level, abstract object that all other objects
-    are derived from. All common functionality is represented by wxShape's
-    members, and overriden members that appear in derived classes and have
-    behaviour as documented for wxShape, are not documented separately.
-    """
-    
-    GraphicsInSizeToContents = False
-    
-    def __init__(self, canvas = None):
-        ShapeEvtHandler.__init__(self)
-        
-        self._eventHandler = self
-        self.SetShape(self)
-        self._id = 0
-        self._formatted = False
-        self._canvas = canvas
-        self._xpos = 0.0
-        self._ypos = 0.0
-        self._pen = wx.Pen(wx.BLACK, 1, wx.SOLID)
-        self._brush = wx.WHITE_BRUSH
-        self._font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
-        self._textColour = wx.BLACK
-        self._textColourName = wx.BLACK
-        self._visible = False
-        self._selected = False
-        self._attachmentMode = ATTACHMENT_MODE_NONE
-        self._spaceAttachments = True
-        self._disableLabel = False
-        self._fixedWidth = False
-        self._fixedHeight = False
-        self._drawHandles = True
-        self._sensitivity = OP_ALL
-        self._draggable = True
-        self._parent = None
-        self._formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT
-        self._shadowMode = SHADOW_NONE
-        self._shadowOffsetX = 6
-        self._shadowOffsetY = 6
-        self._shadowBrush = wx.BLACK_BRUSH
-        self._textMarginX = 5
-        self._textMarginY = 5
-        self._regionName="0"
-        self._centreResize = True
-        self._maintainAspectRatio = False
-        self._highlighted = False
-        self._rotation = 0.0
-        self._branchNeckLength = 10
-        self._branchStemLength = 10
-        self._branchSpacing = 10
-        self._branchStyle = BRANCHING_ATTACHMENT_NORMAL
-        
-        self._regions = []
-        self._lines = []
-        self._controlPoints = []
-        self._attachmentPoints = []
-        self._text = []
-        self._children = []
-        
-        # Set up a default region. Much of the above will be put into
-        # the region eventually (the duplication is for compatibility)
-        region = ShapeRegion()
-        region.SetName("0")
-        region.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))
-        region.SetFormatMode(FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT)
-        region.SetColour("BLACK")
-        self._regions.append(region)
-
-    def __str__(self):
-        return "<%s.%s>" % (self.__class__.__module__, self.__class__.__name__)
-
-    def GetClassName(self):
-        return str(self.__class__).split(".")[-1][:-2]
-    
-    def __del__(self):
-        if self._parent:
-            i = self._parent.GetChildren().index(self)
-            self._parent.GetChildren(i).remove(self)
-
-        self.ClearText()
-        self.ClearRegions()
-        self.ClearAttachments()
-
-        if self._canvas:
-            self._canvas.RemoveShape(self)
-
-        self.GetEventHandler().OnDelete()
-
-    def Draggable(self):
-        """TRUE if the shape may be dragged by the user."""
-        return True
-    
-    def SetShape(self, sh):
-        self._handlerShape = sh
-
-    def GetCanvas(self):
-        """Get the internal canvas."""
-        return self._canvas
-
-    def GetBranchStyle(self):
-        return self._branchStyle
-
-    def GetRotation(self):
-        """Return the angle of rotation in radians."""
-        return self._rotation
-
-    def SetRotation(self, rotation):
-        self._rotation = rotation
-        
-    def SetHighlight(self, hi, recurse = False):
-        """Set the highlight for a shape. Shape highlighting is unimplemented."""
-        self._highlighted = hi
-        if recurse:
-            for shape in self._children:
-                shape.SetHighlight(hi, recurse)
-
-    def SetSensitivityFilter(self, sens = OP_ALL, recursive = False):
-        """Set the shape to be sensitive or insensitive to specific mouse
-        operations.
-
-        sens is a bitlist of the following:
-
-        * OP_CLICK_LEFT
-        * OP_CLICK_RIGHT
-        * OP_DRAG_LEFT
-        * OP_DRAG_RIGHT
-        * OP_ALL (equivalent to a combination of all the above).
-        """
-        self._draggable = sens & OP_DRAG_LEFT
-
-        self._sensitivity = sens
-        if recursive:
-            for shape in self._children:
-                shape.SetSensitivityFilter(sens, True)
-
-    def SetDraggable(self, drag, recursive = False):
-        """Set the shape to be draggable or not draggable."""
-        self._draggable = drag
-        if drag:
-            self._sensitivity |= OP_DRAG_LEFT
-        elif self._sensitivity & OP_DRAG_LEFT:
-            self._sensitivity -= OP_DRAG_LEFT
-
-        if recursive:
-            for shape in self._children:
-                shape.SetDraggable(drag, True)
-
-    def SetDrawHandles(self, drawH):
-        """Set the drawHandles flag for this shape and all descendants.
-        If drawH is TRUE (the default), any handles (control points) will
-        be drawn. Otherwise, the handles will not be drawn.
-        """
-        self._drawHandles = drawH
-        for shape in self._children:
-            shape.SetDrawHandles(drawH)
-
-    def SetShadowMode(self, mode, redraw = False):
-        """Set the shadow mode (whether a shadow is drawn or not).
-        mode can be one of the following:
-
-        SHADOW_NONE
-          No shadow (the default). 
-        SHADOW_LEFT
-          Shadow on the left side. 
-        SHADOW_RIGHT
-          Shadow on the right side.
-        """
-        if redraw and self.GetCanvas():
-            dc = wx.ClientDC(self.GetCanvas())
-            self.GetCanvas().PrepareDC(dc)
-            self.Erase(dc)
-            self._shadowMode = mode
-            self.Draw(dc)
-        else:
-            self._shadowMode = mode
-
-    def SetCanvas(self, theCanvas):
-        """Identical to Shape.Attach."""
-        self._canvas = theCanvas
-        for shape in self._children:
-            shape.SetCanvas(theCanvas)
-
-    def AddToCanvas(self, theCanvas, addAfter = None):
-        """Add the shape to the canvas's shape list.
-        If addAfter is non-NULL, will add the shape after this one.
-        """
-        theCanvas.AddShape(self, addAfter)
-
-        lastImage = self
-        for object in self._children:
-            object.AddToCanvas(theCanvas, lastImage)
-            lastImage = object
-
-    def InsertInCanvas(self, theCanvas):
-        """Insert the shape at the front of the shape list of canvas."""
-        theCanvas.InsertShape(self)
-
-        lastImage = self
-        for object in self._children:
-            object.AddToCanvas(theCanvas, lastImage)
-            lastImage = object
-
-    def RemoveFromCanvas(self, theCanvas):
-        """Remove the shape from the canvas."""
-        if self.Selected():
-            self.Select(False)
-        theCanvas.RemoveShape(self)
-        for object in self._children:
-            object.RemoveFromCanvas(theCanvas)
-
-    def ClearAttachments(self):
-        """Clear internal custom attachment point shapes (of class
-        wxAttachmentPoint).
-        """
-        self._attachmentPoints = []
-
-    def ClearText(self, regionId = 0):
-        """Clear the text from the specified text region."""
-        if regionId == 0:
-            self._text=""
-        if regionId<len(self._regions):
-            self._regions[regionId].ClearText()
-            
-    def ClearRegions(self):
-        """Clear the ShapeRegions from the shape."""
-        self._regions = []
-            
-    def AddRegion(self, region):
-        """Add a region to the shape."""
-        self._regions.append(region)
-
-    def SetDefaultRegionSize(self):
-        """Set the default region to be consistent with the shape size."""
-        if not self._regions:
-            return
-        w, h = self.GetBoundingBoxMax()
-        self._regions[0].SetSize(w, h)
-
-    def HitTest(self, x, y):
-        """Given a point on a canvas, returns TRUE if the point was on the
-        shape, and returns the nearest attachment point and distance from
-        the given point and target.
-        """
-        width, height = self.GetBoundingBoxMax()
-        if abs(width)<4:
-            width = 4.0
-        if abs(height)<4:
-            height = 4.0
-
-        width += 4 # Allowance for inaccurate mousing
-        height += 4
-        
-        left = self._xpos - (width / 2)
-        top = self._ypos - (height / 2)
-        right = self._xpos + (width / 2)
-        bottom = self._ypos + (height / 2)
-
-        nearest_attachment = 0
-
-        # If within the bounding box, check the attachment points
-        # within the object.
-        if x >= left and x <= right and y >= top and y <= bottom:
-            n = self.GetNumberOfAttachments()
-            nearest = 999999
-
-            # GetAttachmentPosition[Edge] takes a logical attachment position,
-            # i.e. if it's rotated through 90%, position 0 is East-facing.
-
-            for i in range(n):
-                e = self.GetAttachmentPositionEdge(i)
-                if e:
-                    xp, yp = e
-                    l = sqrt(((xp - x) * (xp - x)) + (yp - y) * (yp - y))
-                    if l<nearest:
-                        nearest = l
-                        nearest_attachment = i
-
-            return nearest_attachment, nearest
-        return False
-    
-    # Format a text string according to the region size, adding
-    # strings with positions to region text list
-    
-    def FormatText(self, dc, s, i = 0):
-        """Reformat the given text region; defaults to formatting the
-        default region.
-        """
-        self.ClearText(i)
-
-        if not self._regions:
-            return
-
-        if i>len(self._regions):
-            return
-
-        region = self._regions[i]
-        region._regionText = s
-        dc.SetFont(region.GetFont())
-
-        w, h = region.GetSize()
-
-        stringList = FormatText(dc, s, (w - 2 * self._textMarginX), (h - 2 * self._textMarginY), region.GetFormatMode())
-        for s in stringList:
-            line = ShapeTextLine(0.0, 0.0, s)
-            region.GetFormattedText().append(line)
-
-        actualW = w
-        actualH = h
-        # Don't try to resize an object with more than one image (this
-        # case should be dealt with by overriden handlers)
-        if (region.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS) and \
-           region.GetFormattedText().GetCount() and \
-           len(self._regions) == 1 and \
-           not Shape.GraphicsInSizeToContents:
-
-            actualW, actualH = GetCentredTextExtent(dc, region.GetFormattedText())
-            if actualW + 2 * self._textMarginX != w or actualH + 2 * self._textMarginY != h:
-                # If we are a descendant of a composite, must make sure
-                # the composite gets resized properly
-
-                topAncestor = self.GetTopAncestor()
-                if topAncestor != self:
-                    Shape.GraphicsInSizeToContents = True
-
-                    composite = topAncestor
-                    composite.Erase(dc)
-                    self.SetSize(actualW + 2 * self._textMarginX, actualH + 2 * self._textMarginY)
-                    self.Move(dc, self._xpos, self._ypos)
-                    composite.CalculateSize()
-                    if composite.Selected():
-                        composite.DeleteControlPoints(dc)
-                        composite.MakeControlPoints()
-                        composite.MakeMandatoryControlPoints()
-                    # Where infinite recursion might happen if we didn't stop it
-                    composite.Draw(dc)
-                    Shape.GraphicsInSizeToContents = False
-                else:
-                    self.Erase(dc)
-                    
-                self.SetSize(actualW + 2 * self._textMarginX, actualH + 2 * self._textMarginY)
-                self.Move(dc, self._xpos, self._ypos)
-                self.EraseContents(dc)
-        CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, actualW - 2 * self._textMarginX, actualH - 2 * self._textMarginY, region.GetFormatMode())
-        self._formatted = True
-
-    def Recentre(self, dc):
-        """Do recentring (or other formatting) for all the text regions
-        for this shape.
-        """
-        w, h = self.GetBoundingBoxMin()
-        for region in self._regions:
-            CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, w - 2 * self._textMarginX, h - 2 * self._textMarginY, region.GetFormatMode())
-
-    def GetPerimeterPoint(self, x1, y1, x2, y2):
-        """Get the point at which the line from (x1, y1) to (x2, y2) hits
-        the shape. Returns False if the line doesn't hit the perimeter.
-        """
-        return False
-
-    def SetPen(self, the_pen):
-        """Set the pen for drawing the shape's outline."""
-        self._pen = the_pen
-
-    def SetBrush(self, the_brush):
-        """Set the brush for filling the shape's shape."""
-        self._brush = the_brush
-
-    # Get the top - most (non-division) ancestor, or self
-    def GetTopAncestor(self):
-        """Return the top-most ancestor of this shape (the root of
-        the composite).
-        """
-        if not self.GetParent():
-            return self
-
-        if isinstance(self.GetParent(), DivisionShape):
-            return self
-        return self.GetParent().GetTopAncestor()
-
-    # Region functions
-    def SetFont(self, the_font, regionId = 0):
-        """Set the font for the specified text region."""
-        self._font = the_font
-        if regionId<len(self._regions):
-            self._regions[regionId].SetFont(the_font)
-
-    def GetFont(self, regionId = 0):
-        """Get the font for the specified text region."""
-        if regionId >= len(self._regions):
-            return None
-        return self._regions[regionId].GetFont()
-
-    def SetFormatMode(self, mode, regionId = 0):
-        """Set the format mode of the default text region. The argument
-        can be a bit list of the following:
-
-        FORMAT_NONE
-          No formatting. 
-        FORMAT_CENTRE_HORIZ
-          Horizontal centring. 
-        FORMAT_CENTRE_VERT
-          Vertical centring.
-        """
-        if regionId<len(self._regions):
-            self._regions[regionId].SetFormatMode(mode)
-
-    def GetFormatMode(self, regionId = 0):
-        if regionId >= len(self._regions):
-            return 0
-        return self._regions[regionId].GetFormatMode()
-
-    def SetTextColour(self, the_colour, regionId = 0):
-        """Set the colour for the specified text region."""
-        self._textColour = wx.TheColourDatabase.Find(the_colour)
-        self._textColourName = the_colour
-
-        if regionId<len(self._regions):
-            self._regions[regionId].SetColour(the_colour)
-            
-    def GetTextColour(self, regionId = 0):
-        """Get the colour for the specified text region."""
-        if regionId >= len(self._regions):
-            return ""
-        return self._regions[regionId].GetTextColour()
-
-    def SetRegionName(self, name, regionId = 0):
-        """Set the name for this region.
-        The name for a region is unique within the scope of the whole
-        composite, whereas a region id is unique only for a single image.
-        """
-        if regionId<len(self._regions):
-            self._regions[regionId].SetName(name)
-
-    def GetRegionName(self, regionId = 0):
-        """Get the region's name.
-        A region's name can be used to uniquely determine a region within
-        an entire composite image hierarchy. See also Shape.SetRegionName.
-        """
-        if regionId >= len(self._regions):
-            return ""
-        return self._regions[regionId].GetName()
-
-    def GetRegionId(self, name):
-        """Get the region's identifier by name.
-        This is not unique for within an entire composite, but is unique
-        for the image.
-        """
-        for i, r in enumerate(self._regions):
-            if r.GetName() == name:
-                return i
-        return -1
-
-    # Name all _regions in all subimages recursively
-    def NameRegions(self, parentName=""):
-        """Make unique names for all the regions in a shape or composite shape."""
-        n = self.GetNumberOfTextRegions()
-        for i in range(n):
-            if parentName:
-                buff = parentName+"."+str(i)
-            else:
-                buff = str(i)
-            self.SetRegionName(buff, i)
-
-        for j, child in enumerate(self._children):
-            if parentName:
-                buff = parentName+"."+str(j)
-            else:
-                buff = str(j)
-            child.NameRegions(buff)
-
-    # Get a region by name, possibly looking recursively into composites
-    def FindRegion(self, name):
-        """Find the actual image ('this' if non-composite) and region id
-        for the given region name.
-        """
-        id = self.GetRegionId(name)
-        if id>-1:
-            return self, id
-
-        for child in self._children:
-            actualImage, regionId = child.FindRegion(name)
-            if actualImage:
-                return actualImage, regionId
-
-        return None,-1
-
-    # Finds all region names for this image (composite or simple).
-    def FindRegionNames(self):
-        """Get a list of all region names for this image (composite or simple)."""
-        list = []
-        n = self.GetNumberOfTextRegions()
-        for i in range(n):
-            list.append(self.GetRegionName(i))
-
-        for child in self._children:
-            list += child.FindRegionNames()
-
-        return list
-
-    def AssignNewIds(self):
-        """Assign new ids to this image and its children."""
-        self._id = wx.NewId()
-        for child in self._children:
-            child.AssignNewIds()
-
-    def OnDraw(self, dc):
-        pass
-
-    def OnMoveLinks(self, dc):
-        # Want to set the ends of all attached links
-        # to point to / from this object
-
-        for line in self._lines:
-            line.GetEventHandler().OnMoveLink(dc)
-
-    def OnDrawContents(self, dc):
-        if not self._regions:
-            return
-        
-        bound_x, bound_y = self.GetBoundingBoxMin()
-
-        if self._pen:
-            dc.SetPen(self._pen)
-
-        region = self._regions[0]
-        if region.GetFont():
-            dc.SetFont(region.GetFont())
-
-        dc.SetTextForeground(region.GetActualColourObject())
-        dc.SetBackgroundMode(wx.TRANSPARENT)
-        if not self._formatted:
-            CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, bound_x - 2 * self._textMarginX, bound_y - 2 * self._textMarginY, region.GetFormatMode())
-            self._formatted = True
-
-        if not self.GetDisableLabel():
-            DrawFormattedText(dc, region.GetFormattedText(), self._xpos, self._ypos, bound_x - 2 * self._textMarginX, bound_y - 2 * self._textMarginY, region.GetFormatMode())
-            
-
-    def DrawContents(self, dc):
-        """Draw the internal graphic of the shape (such as text).
-
-        Do not override this function: override OnDrawContents, which
-        is called by this function.
-        """
-        self.GetEventHandler().OnDrawContents(dc)
-
-    def OnSize(self, x, y):
-        pass
-
-    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
-        return True
-
-    def OnErase(self, dc):
-        if not self._visible:
-            return
-
-        # Erase links
-        for line in self._lines:
-            line.GetEventHandler().OnErase(dc)
-
-        self.GetEventHandler().OnEraseContents(dc)
-
-    def OnEraseContents(self, dc):
-        if not self._visible:
-            return
-
-        xp, yp = self.GetX(), self.GetY()
-        minX, minY = self.GetBoundingBoxMin()
-        maxX, maxY = self.GetBoundingBoxMax()
-
-        topLeftX = xp - maxX / 2 - 2
-        topLeftY = yp - maxY / 2 - 2
-
-        penWidth = 0
-        if self._pen:
-            penWidth = self._pen.GetWidth()
-
-        dc.SetPen(self.GetBackgroundPen())
-        dc.SetBrush(self.GetBackgroundBrush())
-
-        dc.DrawRectangle(topLeftX - penWidth, topLeftY - penWidth, maxX + penWidth * 2 + 4, maxY + penWidth * 2 + 4)
-
-    def EraseLinks(self, dc, attachment=-1, recurse = False):
-        """Erase links attached to this shape, but do not repair damage
-        caused to other shapes.
-        """
-        if not self._visible:
-            return
-
-        for line in self._lines:
-            if attachment==-1 or (line.GetTo() == self and line.GetAttachmentTo() == attachment or line.GetFrom() == self and line.GetAttachmentFrom() == attachment):
-                line.GetEventHandler().OnErase(dc)
-
-        if recurse:
-            for child in self._children:
-                child.EraseLinks(dc, attachment, recurse)
-
-    def DrawLinks(self, dc, attachment=-1, recurse = False):
-        """Draws any lines linked to this shape."""
-        if not self._visible:
-            return
-
-        for line in self._lines:
-            if attachment==-1 or (line.GetTo() == self and line.GetAttachmentTo() == attachment or line.GetFrom() == self and line.GetAttachmentFrom() == attachment):
-                line.GetEventHandler().Draw(dc)
-                
-        if recurse:
-            for child in self._children:
-                child.DrawLinks(dc, attachment, recurse)
-
-    #  Returns TRUE if pt1 <= pt2 in the sense that one point comes before
-    #  another on an edge of the shape.
-    # attachmentPoint is the attachment point (= side) in question.
-
-    # This is the default, rectangular implementation.
-    def AttachmentSortTest(self, attachmentPoint, pt1, pt2):
-        """Return TRUE if pt1 is less than or equal to pt2, in the sense
-        that one point comes before another on an edge of the shape.
-
-        attachment is the attachment point (side) in question.
-
-        This function is used in Shape.MoveLineToNewAttachment to determine
-        the new line ordering.
-        """
-        physicalAttachment = self.LogicalToPhysicalAttachment(attachmentPoint)
-        if physicalAttachment in [0, 2]:
-            return pt1.x <= pt2.x
-        elif physicalAttachment in [1, 3]:
-            return pt1.y <= pt2.y
-
-        return False
-
-    def MoveLineToNewAttachment(self, dc, to_move, x, y):
-        """Move the given line (which must already be attached to the shape)
-        to a different attachment point on the shape, or a different order
-        on the same attachment.
-
-        Calls Shape.AttachmentSortTest and then
-        ShapeEvtHandler.OnChangeAttachment.
-        """
-        if self.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
-            return False
-
-        # Is (x, y) on this object? If so, find the new attachment point
-        # the user has moved the point to
-        hit = self.HitTest(x, y)
-        if not hit:
-            return False
-
-        newAttachment, distance = hit
-        
-        self.EraseLinks(dc)
-
-        if to_move.GetTo() == self:
-            oldAttachment = to_move.GetAttachmentTo()
-        else:
-            oldAttachment = to_move.GetAttachmentFrom()
-
-        # The links in a new ordering
-        # First, add all links to the new list
-        newOrdering = self._lines[:]
-        
-        # Delete the line object from the list of links; we're going to move
-        # it to another position in the list
-        del newOrdering[newOrdering.index(to_move)]
-
-        old_x=-99999.9
-        old_y=-99999.9
-
-        found = False
-
-        for line in newOrdering:
-            if line.GetTo() == self and oldAttachment == line.GetAttachmentTo() or \
-               line.GetFrom() == self and oldAttachment == line.GetAttachmentFrom():
-                startX, startY, endX, endY = line.GetEnds()
-                if line.GetTo() == self:
-                    xp = endX
-                    yp = endY
-                else:
-                    xp = startX
-                    yp = startY
-
-                thisPoint = wx.RealPoint(xp, yp)
-                lastPoint = wx.RealPoint(old_x, old_y)
-                newPoint = wx.RealPoint(x, y)
-
-                if self.AttachmentSortTest(newAttachment, newPoint, thisPoint) and self.AttachmentSortTest(newAttachment, lastPoint, newPoint):
-                    found = True
-                    newOrdering.insert(newOrdering.index(line), to_move)
-
-                old_x = xp
-                old_y = yp
-            if found:
-                break
-
-        if not found:
-            newOrdering.append(to_move)
-
-        self.GetEventHandler().OnChangeAttachment(newAttachment, to_move, newOrdering)
-        return True
-
-    def OnChangeAttachment(self, attachment, line, ordering):
-        if line.GetTo() == self:
-            line.SetAttachmentTo(attachment)
-        else:
-            line.SetAttachmentFrom(attachment)
-
-        self.ApplyAttachmentOrdering(ordering)
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-        self.MoveLinks(dc)
-
-        if not self.GetCanvas().GetQuickEditMode():
-            self.GetCanvas().Redraw(dc)
-
-    # Reorders the lines according to the given list
-    def ApplyAttachmentOrdering(self, linesToSort):
-        """Apply the line ordering in linesToSort to the shape, to reorder
-        the way lines are attached.
-        """
-        linesStore = self._lines[:]
-
-        self._lines = []
-
-        for line in linesToSort:
-            if line in linesStore:
-                del linesStore[linesStore.index(line)]
-                self._lines.append(line)
-
-        # Now add any lines that haven't been listed in linesToSort
-        self._lines += linesStore
-
-    def SortLines(self, attachment, linesToSort):
-        """ Reorder the lines coming into the node image at this attachment
-        position, in the order in which they appear in linesToSort.
-
-        Any remaining lines not in the list will be added to the end.
-        """
-        # This is a temporary store of all the lines at this attachment
-        # point. We'll tick them off as we've processed them.
-        linesAtThisAttachment = []
-
-        for line in self._lines[:]:
-            if line.GetTo() == self and line.GetAttachmentTo() == attachment or \
-               line.GetFrom() == self and line.GetAttachmentFrom() == attachment:
-                linesAtThisAttachment.append(line)
-                del self._lines[self._lines.index(line)]
-
-        for line in linesToSort:
-            if line in linesAtThisAttachment:
-                # Done this one
-                del linesAtThisAttachment[linesAtThisAttachment.index(line)]
-                self._lines.Append(line)
-
-        # Now add any lines that haven't been listed in linesToSort
-        self._lines += linesAtThisAttachment
-
-    def OnHighlight(self, dc):
-        pass
-
-    def OnLeftClick(self, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_CLICK_LEFT != OP_CLICK_LEFT:
-            if self._parent:
-                attachment, dist = self._parent.HitTest(x, y)
-                self._parent.GetEventHandler().OnLeftClick(x, y, keys, attachment)
-
-    def OnRightClick(self, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_CLICK_RIGHT != OP_CLICK_RIGHT:
-            attachment, dist = self._parent.HitTest(x, y)
-            self._parent.GetEventHandler().OnRightClick(x, y, keys, attachment)
-            
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-                self._parent.GetEventHandler().OnDragLeft(draw, x, y, keys, attachment)
-            return
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-        dc.SetLogicalFunction(OGLRBLF)
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        xx = x + DragOffsetX
-        yy = y + DragOffsetY
-
-        xx, yy = self._canvas.Snap(xx, yy)
-        w, h = self.GetBoundingBoxMax()
-        self.GetEventHandler().OnDrawOutline(dc, xx, yy, w, h)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        global DragOffsetX, DragOffsetY
-        
-        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-                self._parent.GetEventHandler().OnBeginDragLeft(x, y, keys, attachment)
-            return
-
-        DragOffsetX = self._xpos - x
-        DragOffsetY = self._ypos - y
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-        
-        # New policy: don't erase shape until end of drag.
-        # self.Erase(dc)
-        xx = x + DragOffsetX
-        yy = y + DragOffsetY
-        xx, yy = self._canvas.Snap(xx, yy)
-        dc.SetLogicalFunction(OGLRBLF)
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        w, h = self.GetBoundingBoxMax()
-        self.GetEventHandler().OnDrawOutline(dc, xx, yy, w, h)
-        self._canvas.CaptureMouse()
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        if self._canvas.HasCapture():
-            self._canvas.ReleaseMouse()
-        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-            self._parent.GetEventHandler().OnEndDragLeft(x, y, keys, attachment)
-            return
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(wx.COPY)
-        xx = x + DragOffsetX
-        yy = y + DragOffsetY
-        xx, yy = self._canvas.Snap(xx, yy)
-
-        # New policy: erase shape at end of drag.
-        self.Erase(dc)
-
-        self.Move(dc, xx, yy)
-        if self._canvas and not self._canvas.GetQuickEditMode():
-            self._canvas.Redraw(dc)
-
-    def OnDragRight(self, draw, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_DRAG_RIGHT != OP_DRAG_RIGHT:
-            if self._parent:
-                attachment, dist = self._parent.HitTest(x, y)
-                self._parent.GetEventHandler().OnDragRight(draw, x, y, keys, attachment)
-            return
-
-    def OnBeginDragRight(self, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_DRAG_RIGHT != OP_DRAG_RIGHT:
-            if self._parent:
-                attachment, dist = self._parent.HitTest(x, y)
-                self._parent.GetEventHandler().OnBeginDragRight(x, y, keys, attachment)
-            return
-
-    def OnEndDragRight(self, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_DRAG_RIGHT != OP_DRAG_RIGHT:
-            if self._parent:
-                attachment, dist = self._parent.HitTest(x, y)
-                self._parent.GetEventHandler().OnEndDragRight(x, y, keys, attachment)
-            return
-
-    def OnDrawOutline(self, dc, x, y, w, h):
-        points = [[x - w / 2, y - h / 2],
-                [x + w / 2, y - h / 2],
-                [x + w / 2, y + h / 2],
-                [x - w / 2, y + h / 2],
-                [x - w / 2, y - h / 2],
-                ]
-
-        #dc.DrawLines([[round(x), round(y)] for x, y in points])
-        dc.DrawLines(points)
-        
-    def Attach(self, can):
-        """Set the shape's internal canvas pointer to point to the given canvas."""
-        self._canvas = can
-
-    def Detach(self):
-        """Disassociates the shape from its canvas."""
-        self._canvas = None
-
-    def Move(self, dc, x, y, display = True):
-        """Move the shape to the given position.
-        Redraw if display is TRUE.
-        """
-        old_x = self._xpos
-        old_y = self._ypos
-
-        if not self.GetEventHandler().OnMovePre(dc, x, y, old_x, old_y, display):
-            return
-
-        self._xpos, self._ypos = x, y
-
-        self.ResetControlPoints()
-
-        if display:
-            self.Draw(dc)
-
-        self.MoveLinks(dc)
-
-        self.GetEventHandler().OnMovePost(dc, x, y, old_x, old_y, display)
-
-    def MoveLinks(self, dc):
-        """Redraw all the lines attached to the shape."""
-        self.GetEventHandler().OnMoveLinks(dc)
-
-    def Draw(self, dc):
-        """Draw the whole shape and any lines attached to it.
-
-        Do not override this function: override OnDraw, which is called
-        by this function.
-        """
-        if self._visible:
-            self.GetEventHandler().OnDraw(dc)
-            self.GetEventHandler().OnDrawContents(dc)
-            self.GetEventHandler().OnDrawControlPoints(dc)
-            self.GetEventHandler().OnDrawBranches(dc)
-
-    def Flash(self):
-        """Flash the shape."""
-        if self.GetCanvas():
-            dc = wx.ClientDC(self.GetCanvas())
-            self.GetCanvas.PrepareDC(dc)
-
-            dc.SetLogicalFunction(OGLRBLF)
-            self.Draw(dc)
-            dc.SetLogicalFunction(wx.COPY)
-            self.Draw(dc)
-
-    def Show(self, show):
-        """Set a flag indicating whether the shape should be drawn."""
-        self._visible = show
-        for child in self._children:
-            child.Show(show)
-
-    def Erase(self, dc):
-        """Erase the shape.
-        Does not repair damage caused to other shapes.
-        """
-        self.GetEventHandler().OnErase(dc)
-        self.GetEventHandler().OnEraseControlPoints(dc)
-        self.GetEventHandler().OnDrawBranches(dc, erase = True)
-
-    def EraseContents(self, dc):
-        """Erase the shape contents, that is, the area within the shape's
-        minimum bounding box.
-        """
-        self.GetEventHandler().OnEraseContents(dc)
-
-    def AddText(self, string):
-        """Add a line of text to the shape's default text region."""
-        if not self._regions:
-            return
-
-        region = self._regions[0]
-        region.ClearText()
-        new_line = ShapeTextLine(0, 0, string)
-        text = region.GetFormattedText()
-        text.append(new_line)
-
-        self._formatted = False
-
-    def SetSize(self, x, y, recursive = True):
-        """Set the shape's size."""
-        self.SetAttachmentSize(x, y)
-        self.SetDefaultRegionSize()
-
-    def SetAttachmentSize(self, w, h):
-        width, height = self.GetBoundingBoxMin()
-        if width == 0:
-            scaleX = 1.0
-        else:
-            scaleX = long(w) / width
-        if height == 0:
-            scaleY = 1.0
-        else:
-            scaleY = long(h) / height
-
-        for point in self._attachmentPoints:
-            point._x = point._x * scaleX
-            point._y = point._y * scaleY
-
-    # Add line FROM this object
-    def AddLine(self, line, other, attachFrom = 0, attachTo = 0, positionFrom=-1, positionTo=-1):
-        """Add a line between this shape and the given other shape, at the
-        specified attachment points.
-
-        The position in the list of lines at each end can also be specified,
-        so that the line will be drawn at a particular point on its attachment
-        point.
-        """
-        if positionFrom==-1:
-            if not line in self._lines:
-                self._lines.append(line)
-        else:
-            # Don't preserve old ordering if we have new ordering instructions
-            try:
-                self._lines.remove(line)
-            except ValueError:
-                pass
-            if positionFrom<len(self._lines):
-                self._lines.insert(positionFrom, line)
-            else:
-                self._lines.append(line)
-
-        if positionTo==-1:
-            if not other in other._lines:
-                other._lines.append(line)
-        else:
-            # Don't preserve old ordering if we have new ordering instructions
-            try:
-                other._lines.remove(line)
-            except ValueError:
-                pass
-            if positionTo<len(other._lines):
-                other._lines.insert(positionTo, line)
-            else:
-                other._lines.append(line)
-            
-        line.SetFrom(self)
-        line.SetTo(other)
-        line.SetAttachments(attachFrom, attachTo)
-
-        dc = wx.ClientDC(self._canvas)
-        self._canvas.PrepareDC(dc)
-        self.MoveLinks(dc)
-        
-    def RemoveLine(self, line):
-        """Remove the given line from the shape's list of attached lines."""
-        if line.GetFrom() == self:
-            line.GetTo()._lines.remove(line)
-        else:
-            line.GetFrom()._lines.remove(line)
-
-        self._lines.remove(line)
-
-    # Default - make 6 control points
-    def MakeControlPoints(self):
-        """Make a list of control points (draggable handles) appropriate to
-        the shape.
-        """
-        maxX, maxY = self.GetBoundingBoxMax()
-        minX, minY = self.GetBoundingBoxMin()
-
-        widthMin = minX + CONTROL_POINT_SIZE + 2
-        heightMin = minY + CONTROL_POINT_SIZE + 2
-
-        # Offsets from main object
-        top=-heightMin / 2
-        bottom = heightMin / 2 + (maxY - minY)
-        left=-widthMin / 2
-        right = widthMin / 2 + (maxX - minX)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, top, CONTROL_POINT_DIAGONAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, 0, top, CONTROL_POINT_VERTICAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, top, CONTROL_POINT_DIAGONAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, 0, CONTROL_POINT_HORIZONTAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, right, bottom, CONTROL_POINT_DIAGONAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, 0, bottom, CONTROL_POINT_VERTICAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, bottom, CONTROL_POINT_DIAGONAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-        control = ControlPoint(self._canvas, self, CONTROL_POINT_SIZE, left, 0, CONTROL_POINT_HORIZONTAL)
-        self._canvas.AddShape(control)
-        self._controlPoints.append(control)
-
-    def MakeMandatoryControlPoints(self):
-        """Make the mandatory control points.
-
-        For example, the control point on a dividing line should appear even
-        if the divided rectangle shape's handles should not appear (because
-        it is the child of a composite, and children are not resizable).
-        """
-        for child in self._children:
-            child.MakeMandatoryControlPoints()
-
-    def ResetMandatoryControlPoints(self):
-        """Reset the mandatory control points."""
-        for child in self._children:
-            child.ResetMandatoryControlPoints()
-
-    def ResetControlPoints(self):
-        """Reset the positions of the control points (for instance when the
-        shape's shape has changed).
-        """
-        self.ResetMandatoryControlPoints()
-
-        if len(self._controlPoints) == 0:
-            return
-
-        maxX, maxY = self.GetBoundingBoxMax()
-        minX, minY = self.GetBoundingBoxMin()
-
-        widthMin = minX + CONTROL_POINT_SIZE + 2
-        heightMin = minY + CONTROL_POINT_SIZE + 2
-        
-        # Offsets from main object
-        top=-heightMin / 2
-        bottom = heightMin / 2 + (maxY - minY)
-        left=-widthMin / 2
-        right = widthMin / 2 + (maxX - minX)
-
-        self._controlPoints[0]._xoffset = left
-        self._controlPoints[0]._yoffset = top
-
-        self._controlPoints[1]._xoffset = 0
-        self._controlPoints[1]._yoffset = top
-
-        self._controlPoints[2]._xoffset = right
-        self._controlPoints[2]._yoffset = top
-
-        self._controlPoints[3]._xoffset = right
-        self._controlPoints[3]._yoffset = 0
-
-        self._controlPoints[4]._xoffset = right
-        self._controlPoints[4]._yoffset = bottom
-
-        self._controlPoints[5]._xoffset = 0
-        self._controlPoints[5]._yoffset = bottom
-
-        self._controlPoints[6]._xoffset = left
-        self._controlPoints[6]._yoffset = bottom
-
-        self._controlPoints[7]._xoffset = left
-        self._controlPoints[7]._yoffset = 0
-
-    def DeleteControlPoints(self, dc = None):
-        """Delete the control points (or handles) for the shape.
-
-        Does not redraw the shape.
-        """
-        for control in self._controlPoints:
-            if dc:
-                control.GetEventHandler().OnErase(dc)
-            self._canvas.RemoveShape(control)
-            del control
-        self._controlPoints = []
-
-        # Children of divisions are contained objects,
-        # so stop here
-        if not isinstance(self, DivisionShape):
-            for child in self._children:
-                child.DeleteControlPoints(dc)
-
-    def OnDrawControlPoints(self, dc):
-        if not self._drawHandles:
-            return
-
-        dc.SetBrush(wx.BLACK_BRUSH)
-        dc.SetPen(wx.BLACK_PEN)
-
-        for control in self._controlPoints:
-            control.Draw(dc)
-
-        # Children of divisions are contained objects,
-        # so stop here.
-        # This test bypasses the type facility for speed
-        # (critical when drawing)
-
-        if not isinstance(self, DivisionShape):
-            for child in self._children:
-                child.GetEventHandler().OnDrawControlPoints(dc)
-
-    def OnEraseControlPoints(self, dc):
-        for control in self._controlPoints:
-            control.Erase(dc)
-
-        if not isinstance(self, DivisionShape):
-            for child in self._children:
-                child.GetEventHandler().OnEraseControlPoints(dc)
-
-    def Select(self, select, dc = None):
-        """Select or deselect the given shape, drawing or erasing control points
-        (handles) as necessary.
-        """
-        self._selected = select
-        if select:
-            self.MakeControlPoints()
-            # Children of divisions are contained objects,
-            # so stop here
-            if not isinstance(self, DivisionShape):
-                for child in self._children:
-                    child.MakeMandatoryControlPoints()
-            if dc:
-                self.GetEventHandler().OnDrawControlPoints(dc)
-        else:
-            self.DeleteControlPoints(dc)
-            if not isinstance(self, DivisionShape):
-                for child in self._children:
-                    child.DeleteControlPoints(dc)
-
-    def Selected(self):
-        """TRUE if the shape is currently selected."""
-        return self._selected
-
-    def AncestorSelected(self):
-        """TRUE if the shape's ancestor is currently selected."""
-        if self._selected:
-            return True
-        if not self.GetParent():
-            return False
-        return self.GetParent().AncestorSelected()
-
-    def GetNumberOfAttachments(self):
-        """Get the number of attachment points for this shape."""
-        # Should return the MAXIMUM attachment point id here,
-        # so higher-level functions can iterate through all attachments,
-        # even if they're not contiguous.
-
-        if len(self._attachmentPoints) == 0:
-            return 4
-        else:
-            maxN = 3
-            for point in self._attachmentPoints:
-                if point._id>maxN:
-                    maxN = point._id
-            return maxN + 1
-
-    def AttachmentIsValid(self, attachment):
-        """TRUE if attachment is a valid attachment point."""
-        if len(self._attachmentPoints) == 0:
-            return attachment in range(4)
-
-        for point in self._attachmentPoints:
-            if point._id == attachment:
-                return True
-        return False
-
-    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
-        """Get the position at which the given attachment point should be drawn.
-
-        If attachment isn't found among the attachment points of the shape,
-        returns None.
-        """
-        if self._attachmentMode == ATTACHMENT_MODE_NONE:
-            return self._xpos, self._ypos
-        elif self._attachmentMode == ATTACHMENT_MODE_BRANCHING:
-            pt, stemPt = self.GetBranchingAttachmentPoint(attachment, nth)
-            return pt.x, pt.y
-        elif self._attachmentMode == ATTACHMENT_MODE_EDGE:
-            if len(self._attachmentPoints):
-                for point in self._attachmentPoints:
-                    if point._id == attachment:
-                        return self._xpos + point._x, self._ypos + point._y
-                return None
-            else:
-                # Assume is rectangular
-                w, h = self.GetBoundingBoxMax()
-                top = self._ypos + h / 2
-                bottom = self._ypos - h / 2
-                left = self._xpos - w / 2
-                right = self._xpos + w / 2
-
-                # wtf?
-                line and line.IsEnd(self)
-
-                physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
-
-                # Simplified code
-                if physicalAttachment == 0:
-                    pt = self.CalcSimpleAttachment((left, bottom), (right, bottom), nth, no_arcs, line)
-                elif physicalAttachment == 1:
-                    pt = self.CalcSimpleAttachment((right, bottom), (right, top), nth, no_arcs, line)
-                elif physicalAttachment == 2:
-                    pt = self.CalcSimpleAttachment((left, top), (right, top), nth, no_arcs, line)
-                elif physicalAttachment == 3:
-                    pt = self.CalcSimpleAttachment((left, bottom), (left, top), nth, no_arcs, line)
-                else:
-                    return None
-                return pt[0], pt[1]
-        return None
-
-    def GetBoundingBoxMax(self):
-        """Get the maximum bounding box for the shape, taking into account
-        external features such as shadows.
-        """
-        ww, hh = self.GetBoundingBoxMin()
-        if self._shadowMode != SHADOW_NONE:
-            ww += self._shadowOffsetX
-            hh += self._shadowOffsetY
-        return ww, hh
-
-    def GetBoundingBoxMin(self):
-        """Get the minimum bounding box for the shape, that defines the area
-        available for drawing the contents (such as text).
-
-        Must be overridden.
-        """
-        return 0, 0
-    
-    def HasDescendant(self, image):
-        """TRUE if image is a descendant of this composite."""
-        if image == self:
-            return True
-        for child in self._children:
-            if child.HasDescendant(image):
-                return True
-        return False
-
-    # Clears points from a list of wxRealPoints, and clears list
-    # Useless in python? /pi
-    def ClearPointList(self, list):
-        list = []
-
-    # Assuming the attachment lies along a vertical or horizontal line,
-    # calculate the position on that point.
-    def CalcSimpleAttachment(self, pt1, pt2, nth, noArcs, line):
-        """Assuming the attachment lies along a vertical or horizontal line,
-        calculate the position on that point.
-
-        Parameters:
-
-        pt1
-            The first point of the line repesenting the edge of the shape.
-
-        pt2
-            The second point of the line representing the edge of the shape.
-
-        nth
-            The position on the edge (for example there may be 6 lines at
-            this attachment point, and this may be the 2nd line.
-
-        noArcs
-            The number of lines at this edge.
-
-        line
-            The line shape.
-
-        Remarks
-
-        This function expects the line to be either vertical or horizontal,
-        and determines which.
-        """
-        isEnd = line and line.IsEnd(self)
-
-        # Are we horizontal or vertical?
-        isHorizontal = RoughlyEqual(pt1[1], pt2[1])
-
-        if isHorizontal:
-            if pt1[0]>pt2[0]:
-                firstPoint = pt2
-                secondPoint = pt1
-            else:
-                firstPoint = pt1
-                secondPoint = pt2
-
-            if self._spaceAttachments:
-                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                    # Align line according to the next handle along
-                    point = line.GetNextControlPoint(self)
-                    if point[0]<firstPoint[0]:
-                        x = firstPoint[0]
-                    elif point[0]>secondPoint[0]:
-                        x = secondPoint[0]
-                    else:
-                        x = point[0]
-                else:
-                    x = firstPoint[0] + (nth + 1) * (secondPoint[0] - firstPoint[0]) / (noArcs + 1)
-            else:
-                x = (secondPoint[0] - firstPoint[0]) / 2 # Midpoint
-            y = pt1[1]
-        else:
-            assert RoughlyEqual(pt1[0], pt2[0])
-
-            if pt1[1]>pt2[1]:
-                firstPoint = pt2
-                secondPoint = pt1
-            else:
-                firstPoint = pt1
-                secondPoint = pt2
-
-            if self._spaceAttachments:
-                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                    # Align line according to the next handle along
-                    point = line.GetNextControlPoint(self)
-                    if point[1]<firstPoint[1]:
-                        y = firstPoint[1]
-                    elif point[1]>secondPoint[1]:
-                        y = secondPoint[1]
-                    else:
-                        y = point[1]
-                else:
-                    y = firstPoint[1] + (nth + 1) * (secondPoint[1] - firstPoint[1]) / (noArcs + 1)
-            else:
-                y = (secondPoint[1] - firstPoint[1]) / 2 # Midpoint
-            x = pt1[0]
-
-        return x, y
-
-    # Return the zero-based position in m_lines of line
-    def GetLinePosition(self, line):
-        """Get the zero-based position of line in the list of lines
-        for this shape.
-        """
-        try:
-            return self._lines.index(line)
-        except:
-            return 0
-
-
-    #            |________|
-    #                 | <- root
-    #                 | <- neck
-    # shoulder1 ->---------<- shoulder2
-    #             | | | | |
-    #                      <- branching attachment point N-1
-
-    def GetBranchingAttachmentInfo(self, attachment):
-        """Get information about where branching connections go.
-        
-        Returns FALSE if there are no lines at this attachment.
-        """
-        physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
-
-        # Number of lines at this attachment
-        lineCount = self.GetAttachmentLineCount(attachment)
-
-        if not lineCount:
-            return False
-
-        totalBranchLength = self._branchSpacing * (lineCount - 1)
-        root = self.GetBranchingAttachmentRoot(attachment)
-
-        neck = wx.RealPoint()
-        shoulder1 = wx.RealPoint()
-        shoulder2 = wx.RealPoint()
-        
-        # Assume that we have attachment points 0 to 3: top, right, bottom, left
-        if physicalAttachment == 0:
-            neck.x = self.GetX()
-            neck.y = root.y - self._branchNeckLength
-
-            shoulder1.x = root.x - totalBranchLength / 2
-            shoulder2.x = root.x + totalBranchLength / 2
-
-            shoulder1.y = neck.y
-            shoulder2.y = neck.y
-        elif physicalAttachment == 1:
-            neck.x = root.x + self._branchNeckLength
-            neck.y = root.y
-            
-            shoulder1.x = neck.x
-            shoulder2.x = neck.x
-
-            shoulder1.y = neck.y - totalBranchLength / 2
-            shoulder1.y = neck.y + totalBranchLength / 2
-        elif physicalAttachment == 2:
-            neck.x = self.GetX()
-            neck.y = root.y + self._branchNeckLength
-
-            shoulder1.x = root.x - totalBranchLength / 2
-            shoulder2.x = root.x + totalBranchLength / 2
-
-            shoulder1.y = neck.y
-            shoulder2.y = neck.y
-        elif physicalAttachment == 3:
-            neck.x = root.x - self._branchNeckLength
-            neck.y = root.y
-
-            shoulder1.x = neck.x
-            shoulder2.x = neck.x
-
-            shoulder1.y = neck.y - totalBranchLength / 2
-            shoulder2.y = neck.y + totalBranchLength / 2
-        else:
-            raise "Unrecognised attachment point in GetBranchingAttachmentInfo"
-        return root, neck, shoulder1, shoulder2
-
-    def GetBranchingAttachmentPoint(self, attachment, n):
-        physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
-
-        root, neck, shoulder1, shoulder2 = self.GetBranchingAttachmentInfo(attachment)
-        pt = wx.RealPoint()
-        stemPt = wx.RealPoint()
-
-        if physicalAttachment == 0:
-            pt.y = neck.y - self._branchStemLength
-            pt.x = shoulder1.x + n * self._branchSpacing
-
-            stemPt.x = pt.x
-            stemPt.y = neck.y
-        elif physicalAttachment == 2:
-            pt.y = neck.y + self._branchStemLength
-            pt.x = shoulder1.x + n * self._branchStemLength
-            
-            stemPt.x = pt.x
-            stemPt.y = neck.y
-        elif physicalAttachment == 1:
-            pt.x = neck.x + self._branchStemLength
-            pt.y = shoulder1.y + n * self._branchSpacing
-
-            stemPt.x = neck.x
-            stemPt.y = pt.y
-        elif physicalAttachment == 3:
-            pt.x = neck.x - self._branchStemLength
-            pt.y = shoulder1.y + n * self._branchSpacing
-
-            stemPt.x = neck.x
-            stemPt.y = pt.y
-        else:
-            raise "Unrecognised attachment point in GetBranchingAttachmentPoint"
-
-        return pt, stemPt
-
-    def GetAttachmentLineCount(self, attachment):
-        """Get the number of lines at this attachment position."""
-        count = 0
-        for lineShape in self._lines:
-            if lineShape.GetFrom() == self and lineShape.GetAttachmentFrom() == attachment:
-                count += 1
-            elif lineShape.GetTo() == self and lineShape.GetAttachmentTo() == attachment:
-                count += 1
-        return count
-    
-    def GetBranchingAttachmentRoot(self, attachment):
-        """Get the root point at the given attachment."""
-        physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
-
-        root = wx.RealPoint()
-
-        width, height = self.GetBoundingBoxMax()
-
-        # Assume that we have attachment points 0 to 3: top, right, bottom, left
-        if physicalAttachment == 0:
-            root.x = self.GetX()
-            root.y = self.GetY() - height / 2
-        elif physicalAttachment == 1:
-            root.x = self.GetX() + width / 2
-            root.y = self.GetY()
-        elif physicalAttachment == 2:
-            root.x = self.GetX()
-            root.y = self.GetY() + height / 2
-        elif physicalAttachment == 3:
-            root.x = self.GetX() - width / 2
-            root.y = self.GetY()
-        else:
-            raise "Unrecognised attachment point in GetBranchingAttachmentRoot"
-
-        return root
-
-    # Draw or erase the branches (not the actual arcs though)
-    def OnDrawBranchesAttachment(self, dc, attachment, erase = False):
-        count = self.GetAttachmentLineCount(attachment)
-        if count == 0:
-            return
-
-        root, neck, shoulder1, shoulder2 = self.GetBranchingAttachmentInfo(attachment)
-
-        if erase:
-            dc.SetPen(wx.WHITE_PEN)
-            dc.SetBrush(wx.WHITE_BRUSH)
-        else:
-            dc.SetPen(wx.BLACK_PEN)
-            dc.SetBrush(wx.BLACK_BRUSH)
-
-        # Draw neck
-        dc.DrawLine(root, neck)
-
-        if count>1:
-            # Draw shoulder-to-shoulder line
-            dc.DrawLine(shoulder1, shoulder2)
-        # Draw all the little branches
-        for i in range(count):
-            pt, stemPt = self.GetBranchingAttachmentPoint(attachment, i)
-            dc.DrawLine(stemPt, pt)
-
-            if self.GetBranchStyle() & BRANCHING_ATTACHMENT_BLOB and count>1:
-                blobSize = 6
-                dc.DrawEllipse(stemPt.x - blobSize / 2, stemPt.y - blobSize / 2, blobSize, blobSize)
-
-    def OnDrawBranches(self, dc, erase = False):
-        if self._attachmentMode != ATTACHMENT_MODE_BRANCHING:
-            return
-        for i in range(self.GetNumberOfAttachments()):
-            self.OnDrawBranchesAttachment(dc, i, erase)
-
-    def GetAttachmentPositionEdge(self, attachment, nth = 0, no_arcs = 1, line = None):
-        """ Only get the attachment position at the _edge_ of the shape,
-        ignoring branching mode. This is used e.g. to indicate the edge of
-        interest, not the point on the attachment branch.
-        """
-        oldMode = self._attachmentMode
-
-        # Calculate as if to edge, not branch
-        if self._attachmentMode == ATTACHMENT_MODE_BRANCHING:
-            self._attachmentMode = ATTACHMENT_MODE_EDGE
-        res = self.GetAttachmentPosition(attachment, nth, no_arcs, line)
-        self._attachmentMode = oldMode
-
-        return res
-
-    def PhysicalToLogicalAttachment(self, physicalAttachment):
-        """ Rotate the standard attachment point from physical
-        (0 is always North) to logical (0 -> 1 if rotated by 90 degrees)
-        """
-        if RoughlyEqual(self.GetRotation(), 0):
-            i = physicalAttachment
-        elif RoughlyEqual(self.GetRotation(), pi / 2):
-            i = physicalAttachment - 1
-        elif RoughlyEqual(self.GetRotation(), pi):
-            i = physicalAttachment - 2
-        elif RoughlyEqual(self.GetRotation(), 3 * pi / 2):
-            i = physicalAttachment - 3
-        else:
-            # Can't handle -- assume the same
-            return physicalAttachment
-
-        if i<0:
-            i += 4
-
-        return i
-
-    def LogicalToPhysicalAttachment(self, logicalAttachment):
-        """Rotate the standard attachment point from logical
-        to physical (0 is always North).
-        """
-        if RoughlyEqual(self.GetRotation(), 0):
-            i = logicalAttachment
-        elif RoughlyEqual(self.GetRotation(), pi / 2):
-            i = logicalAttachment + 1
-        elif RoughlyEqual(self.GetRotation(), pi):
-            i = logicalAttachment + 2
-        elif RoughlyEqual(self.GetRotation(), 3 * pi / 2):
-            i = logicalAttachment + 3
-        else:
-            return logicalAttachment
-
-        if i>3:
-            i -= 4
-
-        return i
-
-    def Rotate(self, x, y, theta):
-        """Rotate about the given axis by the given amount in radians."""
-        self._rotation = theta
-        if self._rotation<0:
-            self._rotation += 2 * pi
-        elif self._rotation>2 * pi:
-            self._rotation -= 2 * pi
-
-    def GetBackgroundPen(self):
-        """Return pen of the right colour for the background."""
-        if self.GetCanvas():
-            return wx.Pen(self.GetCanvas().GetBackgroundColour(), 1, wx.SOLID)
-        return WhiteBackgroundPen
-
-    def GetBackgroundBrush(self):
-        """Return brush of the right colour for the background."""
-        if self.GetCanvas():
-            return wx.Brush(self.GetCanvas().GetBackgroundColour(), wx.SOLID)
-        return WhiteBackgroundBrush
-
-    def GetX(self):
-        """Get the x position of the centre of the shape."""
-        return self._xpos
-
-    def GetY(self):
-        """Get the y position of the centre of the shape."""
-        return self._ypos
-
-    def SetX(self, x):
-        """Set the x position of the shape."""
-        self._xpos = x
-
-    def SetY(self, y):
-        """Set the y position of the shape."""
-        self._ypos = y
-
-    def GetParent(self):
-        """Return the parent of this shape, if it is part of a composite."""
-        return self._parent
-
-    def SetParent(self, p):
-        self._parent = p
-
-    def GetChildren(self):
-        """Return the list of children for this shape."""
-        return self._children
-
-    def GetDrawHandles(self):
-        """Return the list of drawhandles."""
-        return self._drawHandles
-
-    def GetEventHandler(self):
-        """Return the event handler for this shape."""
-        return self._eventHandler
-
-    def SetEventHandler(self, handler):
-        """Set the event handler for this shape."""
-        self._eventHandler = handler
-
-    def Recompute(self):
-        """Recomputes any constraints associated with the shape.
-
-        Normally applicable to CompositeShapes only, but harmless for
-        other classes of Shape.
-        """
-        return True
-
-    def IsHighlighted(self):
-        """TRUE if the shape is highlighted. Shape highlighting is unimplemented."""
-        return self._highlighted
-
-    def GetSensitivityFilter(self):
-        """Return the sensitivity filter, a bitlist of values.
-
-        See Shape.SetSensitivityFilter.
-        """
-        return self._sensitivity
-
-    def SetFixedSize(self, x, y):
-        """Set the shape to be fixed size."""
-        self._fixedWidth = x
-        self._fixedHeight = y
-
-    def GetFixedSize(self):
-        """Return flags indicating whether the shape is of fixed size in
-        either direction.
-        """
-        return self._fixedWidth, self._fixedHeight
-
-    def GetFixedWidth(self):
-        """TRUE if the shape cannot be resized in the horizontal plane."""
-        return self._fixedWidth
-
-    def GetFixedHeight(self):
-        """TRUE if the shape cannot be resized in the vertical plane."""
-        return self._fixedHeight
-    
-    def SetSpaceAttachments(self, sp):
-        """Indicate whether lines should be spaced out evenly at the point
-        they touch the node (sp = True), or whether they should join at a single
-        point (sp = False).
-        """
-        self._spaceAttachments = sp
-
-    def GetSpaceAttachments(self):
-        """Return whether lines should be spaced out evenly at the point they
-        touch the node (True), or whether they should join at a single point
-        (False).
-        """
-        return self._spaceAttachments
-
-    def SetCentreResize(self, cr):
-        """Specify whether the shape is to be resized from the centre (the
-        centre stands still) or from the corner or side being dragged (the
-        other corner or side stands still).
-        """
-        self._centreResize = cr
-
-    def GetCentreResize(self):
-        """TRUE if the shape is to be resized from the centre (the centre stands
-        still), or FALSE if from the corner or side being dragged (the other
-        corner or side stands still)
-        """
-        return self._centreResize
-
-    def SetMaintainAspectRatio(self, ar):
-        """Set whether a shape that resizes should not change the aspect ratio
-        (width and height should be in the original proportion).
-        """
-        self._maintainAspectRatio = ar
-
-    def GetMaintainAspectRatio(self):
-        """TRUE if shape keeps aspect ratio during resize."""
-        return self._maintainAspectRatio
-
-    def GetLines(self):
-        """Return the list of lines connected to this shape."""
-        return self._lines
-
-    def SetDisableLabel(self, flag):
-        """Set flag to TRUE to stop the default region being shown."""
-        self._disableLabel = flag
-
-    def GetDisableLabel(self):
-        """TRUE if the default region will not be shown, FALSE otherwise."""
-        return self._disableLabel
-
-    def SetAttachmentMode(self, mode):
-        """Set the attachment mode.
-
-        If TRUE, attachment points will be significant when drawing lines to
-        and from this shape.
-        If FALSE, lines will be drawn as if to the centre of the shape.
-        """
-        self._attachmentMode = mode
-
-    def GetAttachmentMode(self):
-        """Return the attachment mode.
-
-        See Shape.SetAttachmentMode.
-        """
-        return self._attachmentMode
-
-    def SetId(self, i):
-        """Set the integer identifier for this shape."""
-        self._id = i
-
-    def GetId(self):
-        """Return the integer identifier for this shape."""
-        return self._id
-
-    def IsShown(self):
-        """TRUE if the shape is in a visible state, FALSE otherwise.
-
-        Note that this has nothing to do with whether the window is hidden
-        or the shape has scrolled off the canvas; it refers to the internal
-        visibility flag.
-        """
-        return self._visible
-
-    def GetPen(self):
-        """Return the pen used for drawing the shape's outline."""
-        return self._pen
-
-    def GetBrush(self):
-        """Return the brush used for filling the shape."""
-        return self._brush
-
-    def GetNumberOfTextRegions(self):
-        """Return the number of text regions for this shape."""
-        return len(self._regions)
-
-    def GetRegions(self):
-        """Return the list of ShapeRegions."""
-        return self._regions
-
-    # Control points ('handles') redirect control to the actual shape, to
-    # make it easier to override sizing behaviour.
-    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
-        bound_x, bound_y = self.GetBoundingBoxMin()
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        if self.GetCentreResize():
-            # Maintain the same centre point
-            new_width = 2.0 * abs(x - self.GetX())
-            new_height = 2.0 * abs(y - self.GetY())
-
-            # Constrain sizing according to what control point you're dragging
-            if pt._type == CONTROL_POINT_HORIZONTAL:
-                if self.GetMaintainAspectRatio():
-                    new_height = bound_y * (new_width / bound_x)
-                else:
-                    new_height = bound_y
-            elif pt._type == CONTROL_POINT_VERTICAL:
-                if self.GetMaintainAspectRatio():
-                    new_width = bound_x * (new_height / bound_y)
-                else:
-                    new_width = bound_x
-            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEY_SHIFT):
-                new_height = bound_y * (new_width / bound_x)
-
-            if self.GetFixedWidth():
-                new_width = bound_x
-
-            if self.GetFixedHeight():
-                new_height = bound_y
-
-            pt._controlPointDragEndWidth = new_width
-            pt._controlPointDragEndHeight = new_height
-
-            self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), new_width, new_height)
-        else:
-            # Don't maintain the same centre point
-            newX1 = min(pt._controlPointDragStartX, x)
-            newY1 = min(pt._controlPointDragStartY, y)
-            newX2 = max(pt._controlPointDragStartX, x)
-            newY2 = max(pt._controlPointDragStartY, y)
-            if pt._type == CONTROL_POINT_HORIZONTAL:
-                newY1 = pt._controlPointDragStartY
-                newY2 = newY1 + pt._controlPointDragStartHeight
-            elif pt._type == CONTROL_POINT_VERTICAL:
-                newX1 = pt._controlPointDragStartX
-                newX2 = newX1 + pt._controlPointDragStartWidth
-            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEY_SHIFT or self.GetMaintainAspectRatio()):
-                newH = (newX2 - newX1) * (pt._controlPointDragStartHeight / pt._controlPointDragStartWidth)
-                if self.GetY()>pt._controlPointDragStartY:
-                    newY2 = newY1 + newH
-                else:
-                    newY1 = newY2 - newH
-
-            newWidth = newX2 - newX1
-            newHeight = newY2 - newY1
-
-            if pt._type == CONTROL_POINT_VERTICAL and self.GetMaintainAspectRatio():
-                newWidth = bound_x * (newHeight / bound_y)
-
-            if pt._type == CONTROL_POINT_HORIZONTAL and self.GetMaintainAspectRatio():
-                newHeight = bound_y * (newWidth / bound_x)
-
-            pt._controlPointDragPosX = newX1 + newWidth / 2
-            pt._controlPointDragPosY = newY1 + newHeight / 2
-            if self.GetFixedWidth():
-                newWidth = bound_x
-
-            if self.GetFixedHeight():
-                newHeight = bound_y
-
-            pt._controlPointDragEndWidth = newWidth
-            pt._controlPointDragEndHeight = newHeight
-            self.GetEventHandler().OnDrawOutline(dc, pt._controlPointDragPosX, pt._controlPointDragPosY, newWidth, newHeight)
-
-    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        self._canvas.CaptureMouse()
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-
-        bound_x, bound_y = self.GetBoundingBoxMin()
-        self.GetEventHandler().OnEndSize(bound_x, bound_y)
-
-        # Choose the 'opposite corner' of the object as the stationary
-        # point in case this is non-centring resizing.
-        if pt.GetX()<self.GetX():
-            pt._controlPointDragStartX = self.GetX() + bound_x / 2
-        else:
-            pt._controlPointDragStartX = self.GetX() - bound_x / 2
-
-        if pt.GetY()<self.GetY():
-            pt._controlPointDragStartY = self.GetY() + bound_y / 2
-        else:
-            pt._controlPointDragStartY = self.GetY() - bound_y / 2
-
-        if pt._type == CONTROL_POINT_HORIZONTAL:
-            pt._controlPointDragStartY = self.GetY() - bound_y / 2
-        elif pt._type == CONTROL_POINT_VERTICAL:
-            pt._controlPointDragStartX = self.GetX() - bound_x / 2
-
-        # We may require the old width and height
-        pt._controlPointDragStartWidth = bound_x
-        pt._controlPointDragStartHeight = bound_y
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        if self.GetCentreResize():
-            new_width = 2.0 * abs(x - self.GetX())
-            new_height = 2.0 * abs(y - self.GetY())
-
-            # Constrain sizing according to what control point you're dragging
-            if pt._type == CONTROL_POINT_HORIZONTAL:
-                if self.GetMaintainAspectRatio():
-                    new_height = bound_y * (new_width / bound_x)
-                else:
-                    new_height = bound_y
-            elif pt._type == CONTROL_POINT_VERTICAL:
-                if self.GetMaintainAspectRatio():
-                    new_width = bound_x * (new_height / bound_y)
-                else:
-                    new_width = bound_x
-            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEY_SHIFT):
-                new_height = bound_y * (new_width / bound_x)
-
-            if self.GetFixedWidth():
-                new_width = bound_x
-
-            if self.GetFixedHeight():
-                new_height = bound_y
-
-            pt._controlPointDragEndWidth = new_width
-            pt._controlPointDragEndHeight = new_height
-            self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), new_width, new_height)
-        else:
-            # Don't maintain the same centre point
-            newX1 = min(pt._controlPointDragStartX, x)
-            newY1 = min(pt._controlPointDragStartY, y)
-            newX2 = max(pt._controlPointDragStartX, x)
-            newY2 = max(pt._controlPointDragStartY, y)
-            if pt._type == CONTROL_POINT_HORIZONTAL:
-                newY1 = pt._controlPointDragStartY
-                newY2 = newY1 + pt._controlPointDragStartHeight
-            elif pt._type == CONTROL_POINT_VERTICAL:
-                newX1 = pt._controlPointDragStartX
-                newX2 = newX1 + pt._controlPointDragStartWidth
-            elif pt._type == CONTROL_POINT_DIAGONAL and (keys & KEYS or self.GetMaintainAspectRatio()):
-                newH = (newX2 - newX1) * (pt._controlPointDragStartHeight / pt._controlPointDragStartWidth)
-                if pt.GetY()>pt._controlPointDragStartY:
-                    newY2 = newY1 + newH
-                else:
-                    newY1 = newY2 - newH
-
-            newWidth = newX2 - newX1
-            newHeight = newY2 - newY1
-
-            if pt._type == CONTROL_POINT_VERTICAL and self.GetMaintainAspectRatio():
-                newWidth = bound_x * (newHeight / bound_y)
-
-            if pt._type == CONTROL_POINT_HORIZONTAL and self.GetMaintainAspectRatio():
-                newHeight = bound_y * (newWidth / bound_x)
-
-            pt._controlPointDragPosX = newX1 + newWidth / 2
-            pt._controlPointDragPosY = newY1 + newHeight / 2
-            if self.GetFixedWidth():
-                newWidth = bound_x
-
-            if self.GetFixedHeight():
-                newHeight = bound_y
-
-            pt._controlPointDragEndWidth = newWidth
-            pt._controlPointDragEndHeight = newHeight
-            self.GetEventHandler().OnDrawOutline(dc, pt._controlPointDragPosX, pt._controlPointDragPosY, newWidth, newHeight)
-            
-    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        if self._canvas.HasCapture():
-            self._canvas.ReleaseMouse()
-        dc.SetLogicalFunction(wx.COPY)
-        self.Recompute()
-        self.ResetControlPoints()
-
-        self.Erase(dc)
-
-        self.SetSize(pt._controlPointDragEndWidth, pt._controlPointDragEndHeight)
-
-        # The next operation could destroy this control point (it does for
-        # label objects, via formatting the text), so save all values we're
-        # going to use, or we'll be accessing garbage.
-
-        #return
-
-        if self.GetCentreResize():
-            self.Move(dc, self.GetX(), self.GetY())
-        else:
-            self.Move(dc, pt._controlPointDragPosX, pt._controlPointDragPosY)
-
-        # Recursively redraw links if we have a composite
-        if len(self.GetChildren()):
-            self.DrawLinks(dc,-1, True)
-
-        width, height = self.GetBoundingBoxMax()
-        self.GetEventHandler().OnEndSize(width, height)
-
-        if not self._canvas.GetQuickEditMode() and pt._eraseObject:
-            self._canvas.Redraw(dc)
-
-
-            
-class RectangleShape(Shape):
-    """
-    The wxRectangleShape has rounded or square corners.
-
-    Derived from:
-      Shape
-    """
-    def __init__(self, w = 0.0, h = 0.0):
-        Shape.__init__(self)
-        self._width = w
-        self._height = h
-        self._cornerRadius = 0.0
-        self.SetDefaultRegionSize()
-
-    def OnDraw(self, dc):
-        x1 = self._xpos - self._width / 2
-        y1 = self._ypos - self._height / 2
-
-        if self._shadowMode != SHADOW_NONE:
-            if self._shadowBrush:
-                dc.SetBrush(self._shadowBrush)
-            dc.SetPen(TransparentPen)
-
-            if self._cornerRadius:
-                dc.DrawRoundedRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height, self._cornerRadius)
-            else:
-                dc.DrawRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height)
-
-        if self._pen:
-            if self._pen.GetWidth() == 0:
-                dc.SetPen(TransparentPen)
-            else:
-                dc.SetPen(self._pen)
-        if self._brush:
-            dc.SetBrush(self._brush)
-
-        if self._cornerRadius:
-            dc.DrawRoundedRectangle(x1, y1, self._width, self._height, self._cornerRadius)
-        else:
-            dc.DrawRectangle(x1, y1, self._width, self._height)
-
-    def GetBoundingBoxMin(self):
-        return self._width, self._height
-
-    def SetSize(self, x, y, recursive = False):
-        self.SetAttachmentSize(x, y)
-        self._width = max(x, 1)
-        self._height = max(y, 1)
-        self.SetDefaultRegionSize()
-
-    def GetCornerRadius(self):
-        """Get the radius of the rectangle's rounded corners."""
-        return self._cornerRadius
-    
-    def SetCornerRadius(self, rad):
-        """Set the radius of the rectangle's rounded corners.
-
-        If the radius is zero, a non-rounded rectangle will be drawn.
-        If the radius is negative, the value is the proportion of the smaller
-        dimension of the rectangle.
-        """
-        self._cornerRadius = rad
-
-    # Assume (x1, y1) is centre of box (most generally, line end at box)
-    def GetPerimeterPoint(self, x1, y1, x2, y2):
-        bound_x, bound_y = self.GetBoundingBoxMax()
-        return FindEndForBox(bound_x, bound_y, self._xpos, self._ypos, x2, y2)
-
-    def GetWidth(self):
-        return self._width
-
-    def GetHeight(self):
-        return self._height
-
-    def SetWidth(self, w):
-        self._width = w
-
-    def SetHeight(self, h):
-        self._height = h
-
-
-        
-class PolygonShape(Shape):
-    """A PolygonShape's shape is defined by a number of points passed to
-    the object's constructor. It can be used to create new shapes such as
-    diamonds and triangles.
-    """
-    def __init__(self):
-        Shape.__init__(self)
-        
-        self._points = None
-        self._originalPoints = None
-
-    def Create(self, the_points = None):
-        """Takes a list of wx.RealPoints or tuples; each point is an offset
-        from the centre.
-        """
-        self.ClearPoints()
-
-        if not the_points:
-            self._originalPoints = []
-            self._points = []
-        else:
-            self._originalPoints = the_points
-
-            # Duplicate the list of points
-            self._points = []
-            for point in the_points:
-                new_point = wx.Point(point[0], point[1])
-                self._points.append(new_point)
-            self.CalculateBoundingBox()
-            self._originalWidth = self._boundWidth
-            self._originalHeight = self._boundHeight
-            self.SetDefaultRegionSize()
-
-    def ClearPoints(self):
-        self._points = []
-        self._originalPoints = []
-
-    # Width and height. Centre of object is centre of box
-    def GetBoundingBoxMin(self):
-        return self._boundWidth, self._boundHeight
-
-    def GetPoints(self):
-        """Return the internal list of polygon vertices."""
-        return self._points
-
-    def GetOriginalPoints(self):
-        return self._originalPoints
-
-    def GetOriginalWidth(self):
-        return self._originalWidth
-
-    def GetOriginalHeight(self):
-        return self._originalHeight
-
-    def SetOriginalWidth(self, w):
-        self._originalWidth = w
-
-    def SetOriginalHeight(self, h):
-        self._originalHeight = h
-        
-    def CalculateBoundingBox(self):
-        # Calculate bounding box at construction (and presumably resize) time
-        left = 10000
-        right=-10000
-        top = 10000
-        bottom=-10000
-
-        for point in self._points:
-            if point.x<left:
-                left = point.x
-            if point.x>right:
-                right = point.x
-
-            if point.y<top:
-                top = point.y
-            if point.y>bottom:
-                bottom = point.y
-
-        self._boundWidth = right - left
-        self._boundHeight = bottom - top
-
-    def CalculatePolygonCentre(self):
-        """Recalculates the centre of the polygon, and
-        readjusts the point offsets accordingly.
-        Necessary since the centre of the polygon
-        is expected to be the real centre of the bounding
-        box.
-        """
-        left = 10000
-        right=-10000
-        top = 10000
-        bottom=-10000
-
-        for point in self._points:
-            if point.x<left:
-                left = point.x
-            if point.x>right:
-                right = point.x
-
-            if point.y<top:
-                top = point.y
-            if point.y>bottom:
-                bottom = point.y
-
-        bwidth = right - left
-        bheight = bottom - top
-
-        newCentreX = left + bwidth / 2
-        newCentreY = top + bheight / 2
-
-        for point in self._points:
-            point.x -= newCentreX
-            point.y -= newCentreY
-        self._xpos += newCentreX
-        self._ypos += newCentreY
-
-    def HitTest(self, x, y):
-        # Imagine four lines radiating from this point. If all of these lines
-        # hit the polygon, we're inside it, otherwise we're not. Obviously
-        # we'd need more radiating lines to be sure of correct results for
-        # very strange (concave) shapes.
-        endPointsX = [x, x + 1000, x, x - 1000]
-        endPointsY = [y - 1000, y, y + 1000, y]
-
-        xpoints = []
-        ypoints = []
-
-        for point in self._points:
-            xpoints.append(point.x + self._xpos)
-            ypoints.append(point.y + self._ypos)
-
-        # We assume it's inside the polygon UNLESS one or more
-        # lines don't hit the outline.
-        isContained = True
-
-        for i in range(4):
-            if not PolylineHitTest(xpoints, ypoints, x, y, endPointsX[i], endPointsY[i]):
-                isContained = False
-
-        if not isContained:
-            return False
-
-        nearest_attachment = 0
-
-        # If a hit, check the attachment points within the object
-        nearest = 999999
-
-        for i in range(self.GetNumberOfAttachments()):
-            e = self.GetAttachmentPositionEdge(i)
-            if e:
-                xp, yp = e
-                l = sqrt((xp - x) * (xp - x) + (yp - y) * (yp - y))
-                if l<nearest:
-                    nearest = l
-                    nearest_attachment = i
-
-        return nearest_attachment, nearest
-
-    # Really need to be able to reset the shape! Otherwise, if the
-    # points ever go to zero, we've lost it, and can't resize.
-    def SetSize(self, new_width, new_height, recursive = True):
-        self.SetAttachmentSize(new_width, new_height)
-
-        # Multiply all points by proportion of new size to old size
-        x_proportion = abs(new_width / self._originalWidth)
-        y_proportion = abs(new_height / self._originalHeight)
-
-        for i in range(max(len(self._points), len(self._originalPoints))):
-            self._points[i].x = self._originalPoints[i][0] * x_proportion
-            self._points[i].y = self._originalPoints[i][1] * y_proportion
-
-        self._boundWidth = abs(new_width)
-        self._boundHeight = abs(new_height)
-        self.SetDefaultRegionSize()
-
-    # Make the original points the same as the working points
-    def UpdateOriginalPoints(self):
-        """If we've changed the shape, must make the original points match the
-        working points with this function.
-        """
-        self._originalPoints = []
-
-        for point in self._points:
-            original_point = wx.RealPoint(point.x, point.y)
-            self._originalPoints.append(original_point)
-
-        self.CalculateBoundingBox()
-        self._originalWidth = self._boundWidth
-        self._originalHeight = self._boundHeight
-
-    def AddPolygonPoint(self, pos):
-        """Add a control point after the given point."""
-        try:
-            firstPoint = self._points[pos]
-        except ValueError:
-            firstPoint = self._points[0]
-
-        try:
-            secondPoint = self._points[pos + 1]
-        except ValueError:
-            secondPoint = self._points[0]
-
-        x = (secondPoint.x - firstPoint.x) / 2 + firstPoint.x
-        y = (secondPoint.y - firstPoint.y) / 2 + firstPoint.y
-        point = wx.RealPoint(x, y)
-
-        if pos >= len(self._points) - 1:
-            self._points.append(point)
-        else:
-            self._points.insert(pos + 1, point)
-
-        self.UpdateOriginalPoints()
-
-        if self._selected:
-            self.DeleteControlPoints()
-            self.MakeControlPoints()
-
-    def DeletePolygonPoint(self, pos):
-        """Delete the given control point."""
-        if pos<len(self._points):
-            del self._points[pos]
-            self.UpdateOriginalPoints()
-            if self._selected:
-                self.DeleteControlPoints()
-                self.MakeControlPoints()
-
-    # Assume (x1, y1) is centre of box (most generally, line end at box)
-    def GetPerimeterPoint(self, x1, y1, x2, y2):
-        # First check for situation where the line is vertical,
-        # and we would want to connect to a point on that vertical --
-        # oglFindEndForPolyline can't cope with this (the arrow
-        # gets drawn to the wrong place).
-        if self._attachmentMode == ATTACHMENT_MODE_NONE and x1 == x2:
-            # Look for the point we'd be connecting to. This is
-            # a heuristic...
-            for point in self._points:
-                if point.x == 0:
-                    if y2>y1 and point.y>0:
-                        return point.x + self._xpos, point.y + self._ypos
-                    elif y2<y1 and point.y<0:
-                        return point.x + self._xpos, point.y + self._ypos
-
-        xpoints = []
-        ypoints = []
-        for point in self._points:
-            xpoints.append(point.x + self._xpos)
-            ypoints.append(point.y + self._ypos)
-
-        return FindEndForPolyline(xpoints, ypoints, x1, y1, x2, y2)
-
-    def OnDraw(self, dc):
-        if self._shadowMode != SHADOW_NONE:
-            if self._shadowBrush:
-                dc.SetBrush(self._shadowBrush)
-            dc.SetPen(TransparentPen)
-
-            dc.DrawPolygon(self._points, self._xpos + self._shadowOffsetX, self._ypos, self._shadowOffsetY)
-
-        if self._pen:
-            if self._pen.GetWidth() == 0:
-                dc.SetPen(TransparentPen)
-            else:
-                dc.SetPen(self._pen)
-        if self._brush:
-            dc.SetBrush(self._brush)
-        dc.DrawPolygon(self._points, self._xpos, self._ypos)
-
-    def OnDrawOutline(self, dc, x, y, w, h):
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-        # Multiply all points by proportion of new size to old size
-        x_proportion = abs(w / self._originalWidth)
-        y_proportion = abs(h / self._originalHeight)
-
-        intPoints = []
-        for point in self._originalPoints:
-            intPoints.append(wx.Point(x_proportion * point[0], y_proportion * point[1]))
-        dc.DrawPolygon(intPoints, x, y)
-
-    # Make as many control points as there are vertices
-    def MakeControlPoints(self):
-        for point in self._points:
-            control = PolygonControlPoint(self._canvas, self, CONTROL_POINT_SIZE, point, point.x, point.y)
-            self._canvas.AddShape(control)
-            self._controlPoints.append(control)
-
-    def ResetControlPoints(self):
-        for i in range(min(len(self._points), len(self._controlPoints))):
-            point = self._points[i]
-            self._controlPoints[i]._xoffset = point.x
-            self._controlPoints[i]._yoffset = point.y
-            self._controlPoints[i].polygonVertex = point
-
-    def GetNumberOfAttachments(self):
-        maxN = max(len(self._points) - 1, 0)
-        for point in self._attachmentPoints:
-            if point._id>maxN:
-                maxN = point._id
-        return maxN + 1
-
-    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
-        if self._attachmentMode == ATTACHMENT_MODE_EDGE and self._points and attachment<len(self._points):
-            point = self._points[0]
-            return point.x + self._xpos, point.y + self._ypos
-        return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs, line)
-
-    def AttachmentIsValid(self, attachment):
-        if not self._points:
-            return False
-
-        if attachment >= 0 and attachment<len(self._points):
-            return True
-
-        for point in self._attachmentPoints:
-            if point._id == attachment:
-                return True
-
-        return False
-
-    # Rotate about the given axis by the given amount in radians
-    def Rotate(self, x, y, theta):
-        actualTheta = theta - self._rotation
-
-        # Rotate attachment points
-        sinTheta = sin(actualTheta)
-        cosTheta = cos(actualTheta)
-
-        for point in self._attachmentPoints:
-            x1 = point._x
-            y1 = point._y
-
-            point._x = x1 * cosTheta - y1 * sinTheta + x * (1 - cosTheta) + y * sinTheta
-            point._y = x1 * sinTheta + y1 * cosTheta + y * (1 - cosTheta) + x * sinTheta
-
-        for point in self._points:
-            x1 = point.x
-            y1 = point.y
-
-            point.x = x1 * cosTheta - y1 * sinTheta + x * (1 - cosTheta) + y * sinTheta
-            point.y = x1 * sinTheta + y1 * cosTheta + y * (1 - cosTheta) + x * sinTheta
-
-        for point in self._originalPoints:
-            x1 = point.x
-            y1 = point.y
-
-            point.x = x1 * cosTheta - y1 * sinTheta + x * (1 - cosTheta) + y * sinTheta
-            point.y = x1 * sinTheta + y1 * cosTheta + y * (1 - cosTheta) + x * sinTheta
-
-        self._rotation = theta
-
-        self.CalculatePolygonCentre()
-        self.CalculateBoundingBox()
-        self.ResetControlPoints()
-
-    # Control points ('handles') redirect control to the actual shape, to
-    # make it easier to override sizing behaviour.
-    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        # Code for CTRL-drag in C++ version commented out
-        
-        pt.CalculateNewSize(x, y)
-
-        self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), pt.GetNewSize().x, pt.GetNewSize().y)
-
-    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        self.Erase(dc)
-        
-        dc.SetLogicalFunction(OGLRBLF)
-
-        bound_x, bound_y = self.GetBoundingBoxMin()
-
-        dist = sqrt((x - self.GetX()) * (x - self.GetX()) + (y - self.GetY()) * (y - self.GetY()))
-
-        pt._originalDistance = dist
-        pt._originalSize.x = bound_x
-        pt._originalSize.y = bound_y
-
-        if pt._originalDistance == 0:
-            pt._originalDistance = 0.0001
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        # Code for CTRL-drag in C++ version commented out
-
-        pt.CalculateNewSize(x, y)
-
-        self.GetEventHandler().OnDrawOutline(dc, self.GetX(), self.GetY(), pt.GetNewSize().x, pt.GetNewSize().y)
-
-        self._canvas.CaptureMouse()
-
-    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        if self._canvas.HasCapture():
-            self._canvas.ReleaseMouse()
-        dc.SetLogicalFunction(wx.COPY)
-
-        # If we're changing shape, must reset the original points
-        if keys & KEY_CTRL:
-            self.CalculateBoundingBox()
-            self.CalculatePolygonCentre()
-        else:
-            self.SetSize(pt.GetNewSize().x, pt.GetNewSize().y)
-
-        self.Recompute()
-        self.ResetControlPoints()
-        self.Move(dc, self.GetX(), self.GetY())
-        if not self._canvas.GetQuickEditMode():
-            self._canvas.Redraw(dc)
-
-
-
-class EllipseShape(Shape):
-    """The EllipseShape behaves similarly to the RectangleShape but is
-    elliptical.
-
-    Derived from:
-      wxShape
-    """
-    def __init__(self, w, h):
-        Shape.__init__(self)
-        self._width = w
-        self._height = h
-        self.SetDefaultRegionSize()
-
-    def GetBoundingBoxMin(self):
-        return self._width, self._height
-
-    def GetPerimeterPoint(self, x1, y1, x2, y2):
-        bound_x, bound_y = self.GetBoundingBoxMax()
-
-        return DrawArcToEllipse(self._xpos, self._ypos, bound_x, bound_y, x2, y2, x1, y1)
-
-    def GetWidth(self):
-        return self._width
-
-    def GetHeight(self):
-        return self._height
-
-    def SetWidth(self, w):
-        self._width = w
-
-    def SetHeight(self, h):
-        self._height = h
-        
-    def OnDraw(self, dc):
-        if self._shadowMode != SHADOW_NONE:
-            if self._shadowBrush:
-                dc.SetBrush(self._shadowBrush)
-            dc.SetPen(TransparentPen)
-            dc.DrawEllipse(self._xpos - self.GetWidth() / 2 + self._shadowOffsetX,
-                           self._ypos - self.GetHeight() / 2 + self._shadowOffsetY,
-                           self.GetWidth(), self.GetHeight())
-
-        if self._pen:
-            if self._pen.GetWidth() == 0:
-                dc.SetPen(TransparentPen)
-            else:
-                dc.SetPen(self._pen)
-        if self._brush:
-            dc.SetBrush(self._brush)
-        dc.DrawEllipse(self._xpos - self.GetWidth() / 2, self._ypos - self.GetHeight() / 2, self.GetWidth(), self.GetHeight())
-
-    def SetSize(self, x, y, recursive = True):
-        self.SetAttachmentSize(x, y)
-        self._width = x
-        self._height = y
-        self.SetDefaultRegionSize()
-
-    def GetNumberOfAttachments(self):
-        return Shape.GetNumberOfAttachments(self)
-
-    # There are 4 attachment points on an ellipse - 0 = top, 1 = right,
-    # 2 = bottom, 3 = left.
-    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
-        if self._attachmentMode == ATTACHMENT_MODE_BRANCHING:
-            return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs, line)
-
-        if self._attachmentMode != ATTACHMENT_MODE_NONE:
-            top = self._ypos + self._height / 2
-            bottom = self._ypos - self._height / 2
-            left = self._xpos - self._width / 2
-            right = self._xpos + self._width / 2
-
-            physicalAttachment = self.LogicalToPhysicalAttachment(attachment)
-
-            if physicalAttachment == 0:
-                if self._spaceAttachments:
-                    x = left + (nth + 1) * self._width / (no_arcs + 1)
-                else:
-                    x = self._xpos
-                y = top
-                # We now have the point on the bounding box: but get the point
-                # on the ellipse by imagining a vertical line from
-                # (x, self._ypos - self_height - 500) to (x, self._ypos) intersecting
-                # the ellipse.
-
-                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, x, self._ypos - self._height - 500, x, self._ypos)
-            elif physicalAttachment == 1:
-                x = right
-                if self._spaceAttachments:
-                    y = bottom + (nth + 1) * self._height / (no_arcs + 1)
-                else:
-                    y = self._ypos
-                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, self._xpos + self._width + 500, y, self._xpos, y)
-            elif physicalAttachment == 2:
-                if self._spaceAttachments:
-                    x = left + (nth + 1) * self._width / (no_arcs + 1)
-                else:
-                    x = self._xpos
-                y = bottom
-                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, x, self._ypos + self._height + 500, x, self._ypos)
-            elif physicalAttachment == 3:
-                x = left
-                if self._spaceAttachments:
-                    y = bottom + (nth + 1) * self._height / (no_arcs + 1)
-                else:
-                    y = self._ypos
-                return DrawArcToEllipse(self._xpos, self._ypos, self._width, self._height, self._xpos - self._width - 500, y, self._xpos, y)
-            else:
-                return Shape.GetAttachmentPosition(self, attachment, x, y, nth, no_arcs, line)
-        else:
-            return self._xpos, self._ypos
-
-
-
-class CircleShape(EllipseShape):
-    """An EllipseShape whose width and height are the same."""
-    def __init__(self, diameter):
-        EllipseShape.__init__(self, diameter, diameter)
-        self.SetMaintainAspectRatio(True)
-
-    def GetPerimeterPoint(self, x1, y1, x2, y2):
-        return FindEndForCircle(self._width / 2, self._xpos, self._ypos, x2, y2)
-
-
-
-class TextShape(RectangleShape):
-    """As wxRectangleShape, but only the text is displayed."""
-    def __init__(self, width, height):
-        RectangleShape.__init__(self, width, height)
-
-    def OnDraw(self, dc):
-        pass
-
-
-
-class ShapeRegion(object):
-    """Object region."""
-    def __init__(self, region = None):
-        if region:
-            self._regionText = region._regionText
-            self._regionName = region._regionName
-            self._textColour = region._textColour
-
-            self._font = region._font
-            self._minHeight = region._minHeight
-            self._minWidth = region._minWidth
-            self._width = region._width
-            self._height = region._height
-            self._x = region._x
-            self._y = region._y
-
-            self._regionProportionX = region._regionProportionX
-            self._regionProportionY = region._regionProportionY
-            self._formatMode = region._formatMode
-            self._actualColourObject = region._actualColourObject
-            self._actualPenObject = None
-            self._penStyle = region._penStyle
-            self._penColour = region._penColour
-
-            self.ClearText()
-            for line in region._formattedText:
-                new_line = ShapeTextLine(line.GetX(), line.GetY(), line.GetText())
-                self._formattedText.append(new_line)
-        else:
-            self._regionText=""
-            self._font = NormalFont
-            self._minHeight = 5.0
-            self._minWidth = 5.0
-            self._width = 0.0
-            self._height = 0.0
-            self._x = 0.0
-            self._y = 0.0
-
-            self._regionProportionX=-1.0
-            self._regionProportionY=-1.0
-            self._formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT
-            self._regionName=""
-            self._textColour="BLACK"
-            self._penColour="BLACK"
-            self._penStyle = wx.SOLID
-            self._actualColourObject = wx.TheColourDatabase.Find("BLACK")
-            self._actualPenObject = None
-
-        self._formattedText = []
-
-    def ClearText(self):
-        self._formattedText = []
-
-    def SetFont(self, f):
-        self._font = f
-
-    def SetMinSize(self, w, h):
-        self._minWidth = w
-        self._minHeight = h
-
-    def SetSize(self, w, h):
-        self._width = w
-        self._height = h
-
-    def SetPosition(self, xp, yp):
-        self._x = xp
-        self._y = yp
-
-    def SetProportions(self, xp, yp):
-        self._regionProportionX = xp
-        self._regionProportionY = yp
-
-    def SetFormatMode(self, mode):
-        self._formatMode = mode
-
-    def SetColour(self, col):
-        self._textColour = col
-        self._actualColourObject = col
-
-    def GetActualColourObject(self):
-        self._actualColourObject = wx.TheColourDatabase.Find(self.GetColour())
-        return self._actualColourObject
-
-    def SetPenColour(self, col):
-        self._penColour = col
-        self._actualPenObject = None
-
-    # Returns NULL if the pen is invisible
-    # (different to pen being transparent; indicates that
-    # region boundary should not be drawn.)
-    def GetActualPen(self):
-        if self._actualPenObject:
-            return self._actualPenObject
-
-        if not self._penColour:
-            return None
-        if self._penColour=="Invisible":
-            return None
-        self._actualPenObject = wx.ThePenList.FindOrCreatePen(self._penColour, 1, self._penStyle)
-        return self._actualPenObject
-
-    def SetText(self, s):
-        self._regionText = s
-
-    def SetName(self, s):
-        self._regionName = s
-
-    def GetText(self):
-        return self._regionText
-
-    def GetFont(self):
-        return self._font
-
-    def GetMinSize(self):
-        return self._minWidth, self._minHeight
-
-    def GetProportion(self):
-        return self._regionProportionX, self._regionProportionY
-
-    def GetSize(self):
-        return self._width, self._height
-
-    def GetPosition(self):
-        return self._x, self._y
-
-    def GetFormatMode(self):
-        return self._formatMode
-
-    def GetName(self):
-        return self._regionName
-
-    def GetColour(self):
-        return self._textColour
-
-    def GetFormattedText(self):
-        return self._formattedText
-
-    def GetPenColour(self):
-        return self._penColour
-
-    def GetPenStyle(self):
-        return self._penStyle
-
-    def SetPenStyle(self, style):
-        self._penStyle = style
-        self._actualPenObject = None
-
-    def GetWidth(self):
-        return self._width
-
-    def GetHeight(self):
-        return self._height
-
-
-
-class ControlPoint(RectangleShape):
-    def __init__(self, theCanvas, object, size, the_xoffset, the_yoffset, the_type):
-        RectangleShape.__init__(self, size, size)
-
-        self._canvas = theCanvas
-        self._shape = object
-        self._xoffset = the_xoffset
-        self._yoffset = the_yoffset
-        self._type = the_type
-        self.SetPen(BlackForegroundPen)
-        self.SetBrush(wx.BLACK_BRUSH)
-        self._oldCursor = None
-        self._visible = True
-        self._eraseObject = True
-        
-    # Don't even attempt to draw any text - waste of time
-    def OnDrawContents(self, dc):
-        pass
-
-    def OnDraw(self, dc):
-        self._xpos = self._shape.GetX() + self._xoffset
-        self._ypos = self._shape.GetY() + self._yoffset
-        RectangleShape.OnDraw(self, dc)
-
-    def OnErase(self, dc):
-        RectangleShape.OnErase(self, dc)
-
-    # Implement resizing of canvas object
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
-
-    def GetNumberOfAttachments(self):
-        return 1
-
-    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
-        return self._xpos, self._ypos
-
-    def SetEraseObject(self, er):
-        self._eraseObject = er
-
-        
-class PolygonControlPoint(ControlPoint):
-    def __init__(self, theCanvas, object, size, vertex, the_xoffset, the_yoffset):
-        ControlPoint.__init__(self, theCanvas, object, size, the_xoffset, the_yoffset, 0)
-        self._polygonVertex = vertex
-        self._originalDistance = 0.0
-        self._newSize = wx.RealPoint()
-        self._originalSize = wx.RealPoint()
-
-    def GetNewSize(self):
-        return self._newSize
-    
-    # Calculate what new size would be, at end of resize
-    def CalculateNewSize(self, x, y):
-        bound_x, bound_y = self.GetShape().GetBoundingBoxMax()
-        dist = sqrt((x - self._shape.GetX()) * (x - self._shape.GetX()) + (y - self._shape.GetY()) * (y - self._shape.GetY()))
-
-        self._newSize.x = dist / self._originalDistance * self._originalSize.x
-        self._newSize.y = dist / self._originalDistance * self._originalSize.y
-
-    # Implement resizing polygon or moving the vertex
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
-
-from canvas import *
-from lines import *
-from composit import *
diff --git a/wxPython/wx/lib/ogl/bmpshape.py b/wxPython/wx/lib/ogl/bmpshape.py
deleted file mode 100644 (file)
index 9346bc7..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         bmpshape.py
-# Purpose:      Bitmap shape
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-from basic import RectangleShape
-
-
-class BitmapShape(RectangleShape):
-    """Draws a bitmap (non-resizable)."""
-    def __init__(self):
-        RectangleShape.__init__(self, 100, 50)
-        self._filename=""
-
-    def OnDraw(self, dc):
-        if not self._bitmap.Ok():
-            return
-
-        x = self._xpos-self._bitmap.GetWidth() / 2
-        y = self._ypos-self._bitmap.GetHeight() / 2
-        dc.DrawBitmap(self._bitmap, x, y, True)
-
-    def SetSize(self, w, h, recursive = True):
-        if self._bitmap.Ok():
-            w = self._bitmap.GetWidth()
-            h = self._bitmap.GetHeight()
-
-        self.SetAttachmentSize(w, h)
-
-        self._width = w
-        self._height = h
-
-        self.SetDefaultRegionSize()
-
-    def GetBitmap(self):
-        """Return a the bitmap associated with this shape."""
-        return self._bitmap
-    
-    def SetBitmap(self, bitmap):
-        """Set the bitmap associated with this shape.
-
-        You can delete the bitmap from the calling application, since
-        reference counting will take care of holding on to the internal bitmap
-        data.
-        """
-        self._bitmap = bitmap
-        if self._bitmap.Ok():
-            self.SetSize(self._bitmap.GetWidth(), self._bitmap.GetHeight())
-            
-    def SetFilename(self, f):
-        """Set the bitmap filename."""
-        self._filename = f
-
-    def GetFilename(self):
-        """Return the bitmap filename."""
-        return self._filename
diff --git a/wxPython/wx/lib/ogl/canvas.py b/wxPython/wx/lib/ogl/canvas.py
deleted file mode 100644 (file)
index d9ad610..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         canvas.py
-# Purpose:      The canvas class
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-import wx
-from lines import LineShape
-from composit import *
-
-NoDragging, StartDraggingLeft, ContinueDraggingLeft, StartDraggingRight, ContinueDraggingRight = 0, 1, 2, 3, 4
-
-KEY_SHIFT, KEY_CTRL = 1, 2
-
-
-
-# Helper function: True if 'contains' wholly contains 'contained'.
-def WhollyContains(contains, contained):
-    xp1, yp1 = contains.GetX(), contains.GetY()
-    xp2, yp2 = contained.GetX(), contained.GetY()
-    
-    w1, h1 = contains.GetBoundingBoxMax()
-    w2, h2 = contained.GetBoundingBoxMax()
-    
-    left1 = xp1-w1 / 2.0
-    top1 = yp1-h1 / 2.0
-    right1 = xp1 + w1 / 2.0
-    bottom1 = yp1 + h1 / 2.0
-    
-    left2 = xp2-w2 / 2.0
-    top2 = yp2-h2 / 2.0
-    right2 = xp2 + w2 / 2.0
-    bottom2 = yp2 + h2 / 2.0
-    
-    return ((left1 <= left2) and (top1 <= top2) and (right1 >= right2) and (bottom1 >= bottom2))
-    
-
-
-class ShapeCanvas(wx.ScrolledWindow):
-    def __init__(self, parent = None, id=-1, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.BORDER, name="ShapeCanvas"):
-        wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name)
-
-        self._shapeDiagram = None
-        self._dragState = NoDragging
-        self._draggedShape = None
-        self._oldDragX = 0
-        self._oldDragY = 0
-        self._firstDragX = 0
-        self._firstDragY = 0
-        self._checkTolerance = True
-
-        wx.EVT_PAINT(self, self.OnPaint)
-        wx.EVT_MOUSE_EVENTS(self, self.OnMouseEvent)
-
-    def SetDiagram(self, diag):
-        self._shapeDiagram = diag
-
-    def GetDiagram(self):
-        return self._shapeDiagram
-    
-    def OnPaint(self, evt):
-        dc = wx.PaintDC(self)
-        self.PrepareDC(dc)
-        
-        dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
-        dc.Clear()
-
-        if self.GetDiagram():
-            self.GetDiagram().Redraw(dc)
-
-    def OnMouseEvent(self, evt):
-        dc = wx.ClientDC(self)
-        self.PrepareDC(dc)
-        
-        x, y = evt.GetLogicalPosition(dc)
-
-        keys = 0
-        if evt.ShiftDown():
-            keys |= KEY_SHIFT
-        if evt.ControlDown():
-            keys |= KEY_CTRL
-
-        dragging = evt.Dragging()
-
-        # Check if we're within the tolerance for mouse movements.
-        # If we're very close to the position we started dragging
-        # from, this may not be an intentional drag at all.
-        if dragging:
-            dx = abs(dc.LogicalToDeviceX(x-self._firstDragX))
-            dy = abs(dc.LogicalToDeviceY(y-self._firstDragY))
-            if self._checkTolerance and (dx <= self.GetDiagram().GetMouseTolerance()) and (dy <= self.GetDiagram().GetMouseTolerance()):
-                return
-            # If we've ignored the tolerance once, then ALWAYS ignore
-            # tolerance in this drag, even if we come back within
-            # the tolerance range.
-            self._checkTolerance = False
-
-        # Dragging - note that the effect of dragging is left entirely up
-        # to the object, so no movement is done unless explicitly done by
-        # object.
-        if dragging and self._draggedShape and self._dragState == StartDraggingLeft:
-            self._dragState = ContinueDraggingLeft
-
-            # If the object isn't m_draggable, transfer message to canvas
-            if self._draggedShape.Draggable():
-                self._draggedShape.GetEventHandler().OnBeginDragLeft(x, y, keys, self._draggedAttachment)
-            else:
-                self._draggedShape = None
-                self.OnBeginDragLeft(x, y, keys)
-
-            self._oldDragX, self._oldDragY = x, y
-
-        elif dragging and self._draggedShape and self._dragState == ContinueDraggingLeft:
-            # Continue dragging
-            self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
-            self._draggedShape.GetEventHandler().OnDragLeft(True, x, y, keys, self._draggedAttachment)
-            self._oldDragX, self._oldDragY = x, y
-
-        elif evt.LeftUp and self._draggedShape and self._dragState == ContinueDraggingLeft:
-            self._dragState = NoDragging
-            self._checkTolerance = True
-
-            self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
-            self._draggedShape.GetEventHandler().OnEndDragLeft(x, y, keys, self._draggedAttachment)
-            self._draggedShape = None
-
-        elif dragging and self._draggedShape and self._dragState == StartDraggingRight:
-            self._dragState = ContinueDraggingRight
-            if self._draggedShape.Draggable:
-                self._draggedShape.GetEventHandler().OnBeginDragRight(x, y, keys, self._draggedAttachment)
-            else:
-                self._draggedShape = None
-                self.OnBeginDragRight(x, y, keys)
-            self._oldDragX, self._oldDragY = x, y
-
-        elif dragging and self._draggedShape and self._dragState == ContinueDraggingRight:
-            # Continue dragging
-            self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
-            self._draggedShape.GetEventHandler().OnDragRight(True, x, y, keys, self._draggedAttachment)
-            self._oldDragX, self._oldDragY = x, y
-
-        elif evt.RightUp() and self._draggedShape and self._dragState == ContinueDraggingRight:
-            self._dragState = NoDragging
-            self._checkTolerance = True
-
-            self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
-            self._draggedShape.GetEventHandler().OnEndDragRight(x, y, keys, self._draggedAttachment)
-            self._draggedShape = None
-
-        # All following events sent to canvas, not object
-        elif dragging and not self._draggedShape and self._dragState == StartDraggingLeft:
-            self._dragState = ContinueDraggingLeft
-            self.OnBeginDragLeft(x, y, keys)
-            self._oldDragX, self._oldDragY = x, y
-
-        elif dragging and not self._draggedShape and self._dragState == ContinueDraggingLeft:
-            # Continue dragging
-            self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
-            self.OnDragLeft(True, x, y, keys)
-            self._oldDragX, self._oldDragY = x, y                
-
-        elif evt.LeftUp() and not self._draggedShape and self._dragState == ContinueDraggingLeft:
-            self._dragState = NoDragging
-            self._checkTolerance = True
-
-            self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
-            self.OnEndDragLeft(x, y, keys)
-            self._draggedShape = None
-
-        elif dragging and not self._draggedShape and self._dragState == StartDraggingRight:
-            self._dragState = ContinueDraggingRight
-            self.OnBeginDragRight(x, y, keys)
-            self._oldDragX, self._oldDragY = x, y
-
-        elif dragging and not self._draggedShape and self._dragState == ContinueDraggingRight:
-            # Continue dragging
-            self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
-            self.OnDragRight(True, x, y, keys)
-            self._oldDragX, self._oldDragY = x, y
-
-        elif evt.RightUp() and not self._draggedShape and self._dragState == ContinueDraggingRight:
-            self._dragState = NoDragging
-            self._checkTolerance = True
-
-            self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
-            self.OnEndDragRight(x, y, keys)
-            self._draggedShape = None
-
-        # Non-dragging events
-        elif evt.IsButton():
-            self._checkTolerance = True
-
-            # Find the nearest object
-            attachment = 0
-
-            nearest_object, attachment = self.FindShape(x, y)
-            if nearest_object: # Object event
-                if evt.LeftDown():
-                    self._draggedShape = nearest_object
-                    self._draggedAttachment = attachment
-                    self._dragState = StartDraggingLeft
-                    self._firstDragX = x
-                    self._firstDragY = y
-
-                elif evt.LeftUp():
-                    # N.B. Only register a click if the same object was
-                    # identified for down *and* up.
-                    if nearest_object == self._draggedShape:
-                        nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
-                    self._draggedShape = None
-                    self._dragState = NoDragging
-
-                elif evt.LeftDClick():
-                    nearest_object.GetEventHandler().OnLeftDoubleClick(x, y, keys, attachment)
-                    self._draggedShape = None
-                    self._dragState = NoDragging
-
-                elif evt.RightDown():
-                    self._draggedShape = nearest_object
-                    self._draggedAttachment = attachment
-                    self._dragState = StartDraggingRight
-                    self._firstDragX = x
-                    self._firstDragY = y
-
-                elif evt.RightUp():
-                    if nearest_object == self._draggedShape:
-                        nearest_object.GetEventHandler().OnRightClick(x, y, keys, attachment)
-                    self._draggedShape = None
-                    self._dragState = NoDragging
-
-            else: # Canvas event
-                if evt.LeftDown():
-                    self._draggedShape = None
-                    self._dragState = StartDraggingLeft
-                    self._firstDragX = x
-                    self._firstDragY = y
-
-                elif evt.LeftUp():
-                    self.OnLeftClick(x, y, keys)
-                    self._draggedShape = None
-                    self._dragState = NoDragging
-
-                elif evt.RightDown():
-                    self._draggedShape = None
-                    self._dragState = StartDraggingRight
-                    self._firstDragX = x
-                    self._firstDragY = y
-
-                elif evt.RightUp():
-                    self.OnRightClick(x, y, keys)
-                    self._draggedShape = None
-                    self._dragState = NoDragging
-
-    def FindShape(self, x, y, info = None, notObject = None):
-        nearest = 100000.0
-        nearest_attachment = 0
-        nearest_object = None
-
-        # Go backward through the object list, since we want:
-        # (a) to have the control points drawn LAST to overlay
-        #     the other objects
-        # (b) to find the control points FIRST if they exist
-
-        for object in self.GetDiagram().GetShapeList()[::-1]:
-            # First pass for lines, which might be inside a container, so we
-            # want lines to take priority over containers. This first loop
-            # could fail if we clickout side a line, so then we'll
-            # try other shapes.
-            if object.IsShown() and \
-               isinstance(object, LineShape) and \
-               object.HitTest(x, y) and \
-               ((info == None) or isinstance(object, info)) and \
-               (not notObject or not notObject.HasDescendant(object)):
-                temp_attachment, dist = object.HitTest(x, y)
-                # A line is trickier to spot than a normal object.
-                # For a line, since it's the diagonal of the box
-                # we use for the hit test, we may have several
-                # lines in the box and therefore we need to be able
-                # to specify the nearest point to the centre of the line
-                # as our hit criterion, to give the user some room for
-                # manouevre.
-                if dist<nearest:
-                    nearest = dist
-                    nearest_object = object
-                    nearest_attachment = temp_attachment
-
-        for object in self.GetDiagram().GetShapeList()[::-1]:
-            # On second pass, only ever consider non-composites or
-            # divisions. If children want to pass up control to
-            # the composite, that's up to them.
-            if (object.IsShown() and 
-                   (isinstance(object, DivisionShape) or 
-                    not isinstance(object, CompositeShape)) and 
-                    object.HitTest(x, y) and 
-                    (info == None or isinstance(object, info)) and 
-                    (not notObject or not notObject.HasDescendant(object))):
-                temp_attachment, dist = object.HitTest(x, y)
-                if not isinstance(object, LineShape):
-                    # If we've hit a container, and we have already
-                    # found a line in the first pass, then ignore
-                    # the container in case the line is in the container.
-                    # Check for division in case line straddles divisions
-                    # (i.e. is not wholly contained).
-                    if not nearest_object or not (isinstance(object, DivisionShape) or WhollyContains(object, nearest_object)):
-                        nearest_object = object
-                        nearest_attachment = temp_attachment
-                        break
-
-        return nearest_object, nearest_attachment
-
-    def AddShape(self, object, addAfter = None):
-        self.GetDiagram().AddShape(object, addAfter)
-
-    def InsertShape(self, object):
-        self.GetDiagram().InsertShape(object)
-
-    def RemoveShape(self, object):
-        self.GetDiagram().RemoveShape(object)
-
-    def GetQuickEditMode(self):
-        return self.GetDiagram().GetQuickEditMode()
-    
-    def Redraw(self, dc):
-        self.GetDiagram().Redraw(dc)
-
-    def Snap(self, x, y):
-        return self.GetDiagram().Snap(x, y)
-
-    def OnLeftClick(self, x, y, keys = 0):
-        pass
-
-    def OnRightClick(self, x, y, keys = 0):
-        pass
-
-    def OnDragLeft(self, draw, x, y, keys = 0):
-        pass
-
-    def OnBeginDragLeft(self, x, y, keys = 0):
-        pass
-
-    def OnEndDragLeft(self, x, y, keys = 0):
-        pass
-
-    def OnDragRight(self, draw, x, y, keys = 0):
-        pass
-
-    def OnBeginDragRight(self, x, y, keys = 0):
-        pass
-
-    def OnEndDragRight(self, x, y, keys = 0):
-        pass
diff --git a/wxPython/wx/lib/ogl/composit.py b/wxPython/wx/lib/ogl/composit.py
deleted file mode 100644 (file)
index 6f502ab..0000000
+++ /dev/null
@@ -1,1430 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         composit.py
-# Purpose:      Composite class
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-import sys
-import wx
-
-from basic import RectangleShape, Shape, ControlPoint
-from oglmisc import *
-
-KEY_SHIFT, KEY_CTRL = 1, 2
-
-_objectStartX = 0.0
-_objectStartY = 0.0
-
-CONSTRAINT_CENTRED_VERTICALLY   = 1
-CONSTRAINT_CENTRED_HORIZONTALLY = 2
-CONSTRAINT_CENTRED_BOTH         = 3
-CONSTRAINT_LEFT_OF              = 4
-CONSTRAINT_RIGHT_OF             = 5
-CONSTRAINT_ABOVE                = 6
-CONSTRAINT_BELOW                = 7
-CONSTRAINT_ALIGNED_TOP          = 8
-CONSTRAINT_ALIGNED_BOTTOM       = 9
-CONSTRAINT_ALIGNED_LEFT         = 10
-CONSTRAINT_ALIGNED_RIGHT        = 11
-
-# Like aligned, but with the objects centred on the respective edge
-# of the reference object.
-CONSTRAINT_MIDALIGNED_TOP       = 12
-CONSTRAINT_MIDALIGNED_BOTTOM    = 13
-CONSTRAINT_MIDALIGNED_LEFT      = 14
-CONSTRAINT_MIDALIGNED_RIGHT     = 15
-
-
-# Backwards compatibility names.  These should be removed eventually.
-gyCONSTRAINT_CENTRED_VERTICALLY   = CONSTRAINT_CENTRED_VERTICALLY   
-gyCONSTRAINT_CENTRED_HORIZONTALLY = CONSTRAINT_CENTRED_HORIZONTALLY 
-gyCONSTRAINT_CENTRED_BOTH         = CONSTRAINT_CENTRED_BOTH         
-gyCONSTRAINT_LEFT_OF              = CONSTRAINT_LEFT_OF              
-gyCONSTRAINT_RIGHT_OF             = CONSTRAINT_RIGHT_OF             
-gyCONSTRAINT_ABOVE                = CONSTRAINT_ABOVE                
-gyCONSTRAINT_BELOW                = CONSTRAINT_BELOW                
-gyCONSTRAINT_ALIGNED_TOP          = CONSTRAINT_ALIGNED_TOP          
-gyCONSTRAINT_ALIGNED_BOTTOM       = CONSTRAINT_ALIGNED_BOTTOM       
-gyCONSTRAINT_ALIGNED_LEFT         = CONSTRAINT_ALIGNED_LEFT         
-gyCONSTRAINT_ALIGNED_RIGHT        = CONSTRAINT_ALIGNED_RIGHT        
-gyCONSTRAINT_MIDALIGNED_TOP       = CONSTRAINT_MIDALIGNED_TOP       
-gyCONSTRAINT_MIDALIGNED_BOTTOM    = CONSTRAINT_MIDALIGNED_BOTTOM    
-gyCONSTRAINT_MIDALIGNED_LEFT      = CONSTRAINT_MIDALIGNED_LEFT      
-gyCONSTRAINT_MIDALIGNED_RIGHT     = CONSTRAINT_MIDALIGNED_RIGHT     
-
-
-
-class ConstraintType(object):
-    def __init__(self, theType, theName, thePhrase):
-        self._type = theType
-        self._name = theName
-        self._phrase = thePhrase
-
-
-
-ConstraintTypes = [
-    [CONSTRAINT_CENTRED_VERTICALLY,
-        ConstraintType(CONSTRAINT_CENTRED_VERTICALLY, "Centre vertically", "centred vertically w.r.t.")],
-
-    [CONSTRAINT_CENTRED_HORIZONTALLY,
-        ConstraintType(CONSTRAINT_CENTRED_HORIZONTALLY, "Centre horizontally", "centred horizontally w.r.t.")],
-
-    [CONSTRAINT_CENTRED_BOTH,
-        ConstraintType(CONSTRAINT_CENTRED_BOTH, "Centre", "centred w.r.t.")],
-
-    [CONSTRAINT_LEFT_OF,
-        ConstraintType(CONSTRAINT_LEFT_OF, "Left of", "left of")],
-
-    [CONSTRAINT_RIGHT_OF,
-        ConstraintType(CONSTRAINT_RIGHT_OF, "Right of", "right of")],
-
-    [CONSTRAINT_ABOVE,
-        ConstraintType(CONSTRAINT_ABOVE, "Above", "above")],
-
-    [CONSTRAINT_BELOW,
-        ConstraintType(CONSTRAINT_BELOW, "Below", "below")],
-
-    # Alignment
-    [CONSTRAINT_ALIGNED_TOP,
-        ConstraintType(CONSTRAINT_ALIGNED_TOP, "Top-aligned", "aligned to the top of")],
-
-    [CONSTRAINT_ALIGNED_BOTTOM,
-        ConstraintType(CONSTRAINT_ALIGNED_BOTTOM, "Bottom-aligned", "aligned to the bottom of")],
-
-    [CONSTRAINT_ALIGNED_LEFT,
-        ConstraintType(CONSTRAINT_ALIGNED_LEFT, "Left-aligned", "aligned to the left of")],
-
-    [CONSTRAINT_ALIGNED_RIGHT,
-        ConstraintType(CONSTRAINT_ALIGNED_RIGHT, "Right-aligned", "aligned to the right of")],
-
-    # Mid-alignment
-    [CONSTRAINT_MIDALIGNED_TOP,
-        ConstraintType(CONSTRAINT_MIDALIGNED_TOP, "Top-midaligned", "centred on the top of")],
-
-    [CONSTRAINT_MIDALIGNED_BOTTOM,
-        ConstraintType(CONSTRAINT_MIDALIGNED_BOTTOM, "Bottom-midaligned", "centred on the bottom of")],
-
-    [CONSTRAINT_MIDALIGNED_LEFT,
-        ConstraintType(CONSTRAINT_MIDALIGNED_LEFT, "Left-midaligned", "centred on the left of")],
-
-    [CONSTRAINT_MIDALIGNED_RIGHT,
-        ConstraintType(CONSTRAINT_MIDALIGNED_RIGHT, "Right-midaligned", "centred on the right of")]
-    ]
-
-
-
-
-class Constraint(object):
-    """A Constraint object helps specify how child shapes are laid out with
-    respect to siblings and parents.
-
-    Derived from:
-      wxObject
-    """
-    def __init__(self, type, constraining, constrained):
-        self._xSpacing = 0.0
-        self._ySpacing = 0.0
-
-        self._constraintType = type
-        self._constraintingObject = constraining
-
-        self._constraintId = 0
-        self._constraintName="noname"
-
-        self._constrainedObjects = constrained[:]
-
-    def __repr__(self):
-        return "<%s.%s>" % (self.__class__.__module__, self.__class__.__name__)
-
-    def SetSpacing(self, x, y):
-        """Sets the horizontal and vertical spacing for the constraint."""
-        self._xSpacing = x
-        self._ySpacing = y
-        
-    def Equals(self, a, b):
-        """Return TRUE if x and y are approximately equal (for the purposes
-        of evaluating the constraint).
-        """
-        marg = 0.5
-
-        return b <= a + marg and b >= a-marg
-
-    def Evaluate(self):
-        """Evaluate this constraint and return TRUE if anything changed."""
-        maxWidth, maxHeight = self._constraintingObject.GetBoundingBoxMax()
-        minWidth, minHeight = self._constraintingObject.GetBoundingBoxMin()
-        x = self._constraintingObject.GetX()
-        y = self._constraintingObject.GetY()
-
-        dc = wx.ClientDC(self._constraintingObject.GetCanvas())
-        self._constraintingObject.GetCanvas().PrepareDC(dc)
-
-        if self._constraintType == CONSTRAINT_CENTRED_VERTICALLY:
-            n = len(self._constrainedObjects)
-            totalObjectHeight = 0.0
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                totalObjectHeight += height2
-
-            # Check if within the constraining object...
-            if totalObjectHeight + (n + 1) * self._ySpacing <= minHeight:
-                spacingY = (minHeight-totalObjectHeight) / (n + 1)
-                startY = y-minHeight / 2
-            else: # Otherwise, use default spacing
-                spacingY = self._ySpacing
-                startY = y-(totalObjectHeight + (n + 1) * spacingY) / 2
-
-            # Now position the objects
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                startY += spacingY + height2 / 2
-                if not self.Equals(startY, constrainedObject.GetY()):
-                    constrainedObject.Move(dc, constrainedObject.GetX(), startY, False)
-                    changed = True
-                startY += height2 / 2
-            return changed
-        elif self._constraintType == CONSTRAINT_CENTRED_HORIZONTALLY:
-            n = len(self._constrainedObjects)
-            totalObjectWidth = 0.0
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                totalObjectWidth += width2
-
-            # Check if within the constraining object...
-            if totalObjectWidth + (n + 1) * self._xSpacing<minWidth:
-                spacingX = (minWidth-totalObjectWidth) / (n + 1)
-                startX = x-minWidth / 2
-            else: # Otherwise, use default spacing
-                spacingX = self._xSpacing
-                startX = x-(totalObjectWidth + (n + 1) * spacingX) / 2
-
-            # Now position the objects
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                startX += spacingX + width2 / 2
-                if not self.Equals(startX, constrainedObject.GetX()):
-                    constrainedObject.Move(dc, startX, constrainedObject.GetY(), False)
-                    changed = True
-                startX += width2 / 2
-            return changed
-        elif self._constraintType == CONSTRAINT_CENTRED_BOTH:
-            n = len(self._constrainedObjects)
-            totalObjectWidth = 0.0
-            totalObjectHeight = 0.0
-
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                totalObjectWidth += width2
-                totalObjectHeight += height2
-
-            # Check if within the constraining object...
-            if totalObjectHeight + (n + 1) * self._xSpacing <= minWidth:
-                spacingX = (minWidth-totalObjectWidth) / (n + 1)
-                startX = x-minWidth / 2
-            else: # Otherwise, use default spacing
-                spacingX = self._xSpacing
-                startX = x-(totalObjectWidth + (n + 1) * spacingX) / 2
-
-            # Check if within the constraining object...
-            if totalObjectHeight + (n + 1) * self._ySpacing <= minHeight:
-                spacingY = (minHeight-totalObjectHeight) / (n + 1)
-                startY = y-minHeight / 2
-            else: # Otherwise, use default spacing
-                spacingY = self._ySpacing
-                startY = y-(totalObjectHeight + (n + 1) * spacingY) / 2
-
-            # Now position the objects
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                startX += spacingX + width2 / 2
-                startY += spacingY + height2 / 2
-
-                if not self.Equals(startX, constrainedObject.GetX()) or not self.Equals(startY, constrainedObject.GetY()):
-                    constrainedObject.Move(dc, startX, startY, False)
-                    changed = True
-
-                startX += width2 / 2
-                startY += height2 / 2
-            return changed
-        elif self._constraintType == CONSTRAINT_LEFT_OF:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-
-                x3 = x-minWidth / 2-width2 / 2-self._xSpacing
-                if not self.Equals(x3, constrainedObject.GetX()):
-                    changed = True
-                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
-            return changed
-        elif self._constraintType == CONSTRAINT_RIGHT_OF:
-            changed = False
-
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                x3 = x + minWidth / 2 + width2 / 2 + self._xSpacing
-                if not self.Equals(x3, constrainedObject.GetX()):
-                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
-                    changed = True
-            return changed
-        elif self._constraintType == CONSTRAINT_ABOVE:
-            changed = False
-
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-
-                y3 = y-minHeight / 2-height2 / 2-self._ySpacing
-                if not self.Equals(y3, constrainedObject.GetY()):
-                    changed = True
-                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
-            return changed
-        elif self._constraintType == CONSTRAINT_BELOW:
-            changed = False
-
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-
-                y3 = y + minHeight / 2 + height2 / 2 + self._ySpacing
-                if not self.Equals(y3, constrainedObject.GetY()):
-                    changed = True
-                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
-            return changed
-        elif self._constraintType == CONSTRAINT_ALIGNED_LEFT:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                x3 = x-minWidth / 2 + width2 / 2 + self._xSpacing
-                if not self.Equals(x3, constrainedObject.GetX()):
-                    changed = True
-                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
-            return changed
-        elif self._constraintType == CONSTRAINT_ALIGNED_RIGHT:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                x3 = x + minWidth / 2-width2 / 2-self._xSpacing
-                if not self.Equals(x3, constrainedObject.GetX()):
-                    changed = True
-                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
-            return changed
-        elif self._constraintType == CONSTRAINT_ALIGNED_TOP:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                y3 = y-minHeight / 2 + height2 / 2 + self._ySpacing
-                if not self.Equals(y3, constrainedObject.GetY()):
-                    changed = True
-                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
-            return changed
-        elif self._constraintType == CONSTRAINT_ALIGNED_BOTTOM:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                width2, height2 = constrainedObject.GetBoundingBoxMax()
-                y3 = y + minHeight / 2-height2 / 2-self._ySpacing
-                if not self.Equals(y3, constrainedObject.GetY()):
-                    changed = True
-                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
-            return changed
-        elif self._constraintType == CONSTRAINT_MIDALIGNED_LEFT:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                x3 = x-minWidth / 2
-                if not self.Equals(x3, constrainedObject.GetX()):
-                    changed = True
-                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
-            return changed
-        elif self._constraintType == CONSTRAINT_MIDALIGNED_RIGHT:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                x3 = x + minWidth / 2
-                if not self.Equals(x3, constrainedObject.GetX()):
-                    changed = True
-                    constrainedObject.Move(dc, x3, constrainedObject.GetY(), False)
-            return changed
-        elif self._constraintType == CONSTRAINT_MIDALIGNED_TOP:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                y3 = y-minHeight / 2
-                if not self.Equals(y3, constrainedObject.GetY()):
-                    changed = True
-                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
-            return changed
-        elif self._constraintType == CONSTRAINT_MIDALIGNED_BOTTOM:
-            changed = False
-            for constrainedObject in self._constrainedObjects:
-                y3 = y + minHeight / 2
-                if not self.Equals(y3, constrainedObject.GetY()):
-                    changed = True
-                    constrainedObject.Move(dc, constrainedObject.GetX(), y3, False)
-            return changed
-        
-        return False
-    
-OGLConstraint = wx._core._deprecated(Constraint,
-                     "The OGLConstraint name is deprecated, use `ogl.Constraint` instead.")
-
-
-class CompositeShape(RectangleShape):
-    """This is an object with a list of child objects, and a list of size
-    and positioning constraints between the children.
-
-    Derived from:
-      wxRectangleShape
-    """
-    def __init__(self):
-        RectangleShape.__init__(self, 100.0, 100.0)
-
-        self._oldX = self._xpos
-        self._oldY = self._ypos
-
-        self._constraints = [] 
-        self._divisions = [] # In case it's a container
-        
-    def OnDraw(self, dc):
-        x1 = self._xpos-self._width / 2
-        y1 = self._ypos-self._height / 2
-
-        if self._shadowMode != SHADOW_NONE:
-            if self._shadowBrush:
-                dc.SetBrush(self._shadowBrush)
-            dc.SetPen(wx.Pen(wx.WHITE, 1, wx.TRANSPARENT))
-
-            if self._cornerRadius:
-                dc.DrawRoundedRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height, self._cornerRadius)
-            else:
-                dc.DrawRectangle(x1 + self._shadowOffsetX, y1 + self._shadowOffsetY, self._width, self._height)
-
-        # For debug purposes /pi
-        #dc.DrawRectangle(x1, y1, self._width, self._height)
-        
-    def OnDrawContents(self, dc):
-        for object in self._children:
-            object.Draw(dc)
-            object.DrawLinks(dc)
-
-        Shape.OnDrawContents(self, dc)
-
-    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
-        diffX = x-old_x
-        diffY = y-old_y
-
-        for object in self._children:
-            object.Erase(dc)
-            object.Move(dc, object.GetX() + diffX, object.GetY() + diffY, display)
-
-        return True
-
-    def OnErase(self, dc):
-        RectangleShape.OnErase(self, dc)
-        for object in self._children:
-            object.Erase(dc)
-
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        xx, yy = self._canvas.Snap(x, y)
-        offsetX = xx - _objectStartX
-        offsetY = yy - _objectStartY
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        self.GetEventHandler().OnDrawOutline(dc, self.GetX() + offsetX, self.GetY() + offsetY, self.GetWidth(), self.GetHeight())
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        global _objectStartX, _objectStartY
-
-        _objectStartX = x
-        _objectStartY = y
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        #self.Erase(dc)
-        
-        dc.SetLogicalFunction(OGLRBLF)
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-        self._canvas.CaptureMouse()
-
-        xx, yy = self._canvas.Snap(x, y)
-        offsetX = xx - _objectStartX
-        offsetY = yy - _objectStartY
-
-        self.GetEventHandler().OnDrawOutline(dc, self.GetX() + offsetX, self.GetY() + offsetY, self.GetWidth(), self.GetHeight())
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        if self._canvas.HasCapture():
-            self._canvas.ReleaseMouse()
-
-        if not self._draggable:
-            if self._parent:
-                self._parent.GetEventHandler().OnEndDragLeft(x, y, keys, 0)
-            return
-            
-        self.Erase(dc)
-        
-        dc.SetLogicalFunction(wx.COPY)
-        
-        xx, yy = self._canvas.Snap(x, y)
-        offsetX = xx - _objectStartX
-        offsetY = yy - _objectStartY
-
-        self.Move(dc, self.GetX() + offsetX, self.GetY() + offsetY)
-
-        if self._canvas and not self._canvas.GetQuickEditMode():
-            self._canvas.Redraw(dc)
-
-    def OnRightClick(self, x, y, keys = 0, attachment = 0):
-        # If we get a ctrl-right click, this means send the message to
-        # the division, so we can invoke a user interface for dealing
-        # with regions.
-        if keys & KEY_CTRL:
-            for division in self._divisions:
-                hit = division.HitTest(x, y)
-                if hit:
-                    division.GetEventHandler().OnRightClick(x, y, keys, hit[0])
-                    break
-
-    def SetSize(self, w, h, recursive = True):
-        self.SetAttachmentSize(w, h)
-
-        xScale = w / max(1, self.GetWidth())
-        yScale = h / max(1, self.GetHeight())
-
-        self._width = w
-        self._height = h
-
-        if not recursive:
-            return
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        for object in self._children:
-            # Scale the position first
-            newX = (object.GetX()-self.GetX()) * xScale + self.GetX()
-            newY = (object.GetY()-self.GetY()) * yScale + self.GetY()
-            object.Show(False)
-            object.Move(dc, newX, newY)
-            object.Show(True)
-
-            # Now set the scaled size
-            xbound, ybound = object.GetBoundingBoxMax()
-            if not object.GetFixedWidth():
-                xbound *= xScale
-            if not object.GetFixedHeight():
-                ybound *= yScale
-            object.SetSize(xbound, ybound)
-
-        self.SetDefaultRegionSize()
-
-    def AddChild(self, child, addAfter = None):
-        """Adds a child shape to the composite.
-
-        If addAfter is not None, the shape will be added after this shape.
-        """
-        self._children.append(child)
-        child.SetParent(self)
-        if self._canvas:
-            # Ensure we add at the right position
-            if addAfter:
-                child.RemoveFromCanvas(self._canvas)
-            child.AddToCanvas(self._canvas, addAfter)
-
-    def RemoveChild(self, child):
-        """Removes the child from the composite and any constraint
-        relationships, but does not delete the child.
-        """
-        self._children.remove(child)
-        self._divisions.remove(child)
-        self.RemoveChildFromConstraints(child)
-        child.SetParent(None)
-
-    def DeleteConstraintsInvolvingChild(self, child):
-        """This function deletes constraints which mention the given child.
-
-        Used when deleting a child from the composite.
-        """
-        for constraint in self._constraints:
-            if constraint._constrainingObject == child or child in constraint._constrainedObjects:
-                self._constraints.remove(constraint)
-
-    def RemoveChildFromConstraints(self, child):
-        for constraint in self._constraints:
-            if child in constraint._constrainedObjects:
-                constraint._constrainedObjects.remove(child)
-            if constraint._constrainingObject == child:
-                constraint._constrainingObject = None
-
-            # Delete the constraint if no participants left
-            if not constraint._constrainingObject:
-                self._constraints.remove(constraint)
-
-    def AddConstraint(self, constraint):
-        """Adds a constraint to the composite."""
-        self._constraints.append(constraint)
-        if constraint._constraintId == 0:
-            constraint._constraintId = wx.NewId()
-        return constraint
-
-    def AddSimpleConstraint(self, type, constraining, constrained):
-        """Add a constraint of the given type to the composite.
-
-        constraining is the shape doing the constraining
-        constrained is a list of shapes being constrained
-        """
-        constraint = Constraint(type, constraining, constrained)
-        if constraint._constraintId == 0:
-            constraint._constraintId = wx.NewId()
-        self._constraints.append(constraint)
-        return constraint
-
-    def FindConstraint(self, cId):
-        """Finds the constraint with the given id.
-
-        Returns a tuple of the constraint and the actual composite the
-        constraint was in, in case that composite was a descendant of
-        this composit.
-
-        Returns None if not found.
-        """
-        for constraint in self._constraints:
-            if constraint._constraintId == cId:
-                return constraint, self
-
-        # If not found, try children
-        for child in self._children:
-            if isinstance(child, CompositeShape):
-                constraint = child.FindConstraint(cId)
-                if constraint:
-                    return constraint[0], child
-
-        return None
-
-    def DeleteConstraint(self, constraint):
-        """Deletes constraint from composite."""
-        self._constraints.remove(constraint)
-
-    def CalculateSize(self):
-        """Calculates the size and position of the composite based on
-        child sizes and positions.
-        """
-        maxX=-999999.9
-        maxY=-999999.9
-        minX = 999999.9
-        minY = 999999.9
-
-        for child in self._children:
-            # Recalculate size of composite objects because may not conform
-            # to size it was set to - depends on the children.
-            if isinstance(child, CompositeShape):
-                child.CalculateSize()
-
-            w, h = child.GetBoundingBoxMax()
-            if child.GetX() + w / 2>maxX:
-                maxX = child.GetX() + w / 2
-            if child.GetX()-w / 2<minX:
-                minX = child.GetX()-w / 2
-            if child.GetY() + h / 2>maxY:
-                maxY = child.GetY() + h / 2
-            if child.GetY()-h / 2<minY:
-                minY = child.GetY()-h / 2
-
-        self._width = maxX-minX
-        self._height = maxY-minY
-        self._xpos = self._width / 2 + minX
-        self._ypos = self._height / 2 + minY
-
-    def Recompute(self):
-        """Recomputes any constraints associated with the object. If FALSE is
-        returned, the constraints could not be satisfied (there was an
-        inconsistency).
-        """
-        noIterations = 0
-        changed = True
-        while changed and noIterations<500:
-            changed = self.Constrain()
-            noIterations += 1
-
-        return not changed
-
-    def Constrain(self):
-        self.CalculateSize()
-
-        changed = False
-        for child in self._children:
-            if isinstance(child, CompositeShape) and child.Constrain():
-                changed = True
-
-        for constraint in self._constraints:
-            if constraint.Evaluate():
-                changed = True
-
-        return changed
-
-    def MakeContainer(self):
-        """Makes this composite into a container by creating one child
-        DivisionShape.
-        """
-        division = self.OnCreateDivision()
-        self._divisions.append(division)
-        self.AddChild(division)
-
-        division.SetSize(self._width, self._height)
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-        
-        division.Move(dc, self.GetX(), self.GetY())
-        self.Recompute()
-        division.Show(True)
-
-    def OnCreateDivision(self):
-        return DivisionShape()
-
-    def FindContainerImage(self):
-        """Finds the image used to visualize a container. This is any child of
-        the composite that is not in the divisions list.
-        """
-        for child in self._children:
-            if child in self._divisions:
-                return child
-
-        return None
-
-    def ContainsDivision(self, division):
-        """Returns TRUE if division is a descendant of this container."""
-        if division in self._divisions:
-            return True
-
-        for child in self._children:
-            if isinstance(child, CompositeShape):
-                return child.ContainsDivision(division)
-
-        return False
-
-    def GetDivisions(self):
-        """Return the list of divisions."""
-        return self._divisions
-
-    def GetConstraints(self):
-        """Return the list of constraints."""
-        return self._constraints
-
-
-#  A division object is a composite with special properties,
-#  to be used for containment. It's a subdivision of a container.
-#  A containing node image consists of a composite with a main child shape
-#  such as rounded rectangle, plus a list of division objects.
-#  It needs to be a composite because a division contains pieces
-#  of diagram.
-#  NOTE a container has at least one wxDivisionShape for consistency.
-#  This can be subdivided, so it turns into two objects, then each of
-#  these can be subdivided, etc.
-
-DIVISION_SIDE_NONE      =0
-DIVISION_SIDE_LEFT      =1
-DIVISION_SIDE_TOP       =2
-DIVISION_SIDE_RIGHT     =3
-DIVISION_SIDE_BOTTOM    =4
-
-originalX = 0.0
-originalY = 0.0
-originalW = 0.0
-originalH = 0.0
-
-
-
-class DivisionControlPoint(ControlPoint):
-    def __init__(self, the_canvas, object, size, the_xoffset, the_yoffset, the_type):
-        ControlPoint.__init__(self, the_canvas, object, size, the_xoffset, the_yoffset, the_type)
-        self.SetEraseObject(False)
-
-    # Implement resizing of canvas object
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        ControlPoint.OnDragLeft(self, draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        global originalX, originalY, originalW, originalH
-
-        originalX = self._shape.GetX()
-        originalY = self._shape.GetY()
-        originalW = self._shape.GetWidth()
-        originalH = self._shape.GetHeight()
-
-        ControlPoint.OnBeginDragLeft(self, x, y, keys, attachment)
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        ControlPoint.OnEndDragLeft(self, x, y, keys, attachment)
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        division = self._shape
-        divisionParent = division.GetParent()
-
-        # Need to check it's within the bounds of the parent composite
-        x1 = divisionParent.GetX()-divisionParent.GetWidth() / 2
-        y1 = divisionParent.GetY()-divisionParent.GetHeight() / 2
-        x2 = divisionParent.GetX() + divisionParent.GetWidth() / 2
-        y2 = divisionParent.GetY() + divisionParent.GetHeight() / 2
-
-        # Need to check it has not made the division zero or negative
-        # width / height
-        dx1 = division.GetX()-division.GetWidth() / 2
-        dy1 = division.GetY()-division.GetHeight() / 2
-        dx2 = division.GetX() + division.GetWidth() / 2
-        dy2 = division.GetY() + division.GetHeight() / 2
-
-        success = True
-        if division.GetHandleSide() == DIVISION_SIDE_LEFT:
-            if x <= x1 or x >= x2 or x >= dx2:
-                success = False
-            # Try it out first...
-            elif not division.ResizeAdjoining(DIVISION_SIDE_LEFT, x, True):
-                success = False
-            else:
-                division.ResizeAdjoining(DIVISION_SIDE_LEFT, x, False)
-        elif division.GetHandleSide() == DIVISION_SIDE_TOP:
-            if y <= y1 or y >= y2 or y >= dy2:
-                success = False
-            elif not division.ResizeAdjoining(DIVISION_SIDE_TOP, y, True):
-                success = False
-            else:
-                division.ResizingAdjoining(DIVISION_SIDE_TOP, y, False)
-        elif division.GetHandleSide() == DIVISION_SIDE_RIGHT:
-            if x <= x1 or x >= x2 or x <= dx1:
-                success = False
-            elif not division.ResizeAdjoining(DIVISION_SIDE_RIGHT, x, True):
-                success = False
-            else:
-                division.ResizeAdjoining(DIVISION_SIDE_RIGHT, x, False)
-        elif division.GetHandleSide() == DIVISION_SIDE_BOTTOM:
-            if y <= y1 or y >= y2 or y <= dy1:
-                success = False
-            elif not division.ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, True):
-                success = False
-            else:
-                division.ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, False)
-
-        if not success:
-            division.SetSize(originalW, originalH)
-            division.Move(dc, originalX, originalY)
-
-        divisionParent.Draw(dc)
-        division.GetEventHandler().OnDrawControlPoints(dc)
-
-
-
-DIVISION_MENU_SPLIT_HORIZONTALLY    =1
-DIVISION_MENU_SPLIT_VERTICALLY      =2
-DIVISION_MENU_EDIT_LEFT_EDGE        =3
-DIVISION_MENU_EDIT_TOP_EDGE         =4
-DIVISION_MENU_EDIT_RIGHT_EDGE       =5
-DIVISION_MENU_EDIT_BOTTOM_EDGE      =6
-DIVISION_MENU_DELETE_ALL            =7
-    
-
-
-class PopupDivisionMenu(wx.Menu):
-    def __init__(self):
-        wx.Menu.__init__(self)
-        self.Append(DIVISION_MENU_SPLIT_HORIZONTALLY,"Split horizontally")
-        self.Append(DIVISION_MENU_SPLIT_VERTICALLY,"Split vertically")
-        self.AppendSeparator()
-        self.Append(DIVISION_MENU_EDIT_LEFT_EDGE,"Edit left edge")
-        self.Append(DIVISION_MENU_EDIT_TOP_EDGE,"Edit top edge")
-
-        wx.EVT_MENU_RANGE(self, DIVISION_MENU_SPLIT_HORIZONTALLY, DIVISION_MENU_EDIT_BOTTOM_EDGE, self.OnMenu)
-
-    def SetClientData(self, data):
-        self._clientData = data
-
-    def GetClientData(self):
-        return self._clientData
-    
-    def OnMenu(self, event):
-        division = self.GetClientData()
-        if event.GetId() == DIVISION_MENU_SPLIT_HORIZONTALLY:
-            division.Divide(wx.HORIZONTAL)
-        elif event.GetId() == DIVISION_MENU_SPLIT_VERTICALLY:
-            division.Divide(wx.VERTICAL)
-        elif event.GetId() == DIVISION_MENU_EDIT_LEFT_EDGE:
-            division.EditEdge(DIVISION_SIDE_LEFT)
-        elif event.GetId() == DIVISION_MENU_EDIT_TOP_EDGE:
-            division.EditEdge(DIVISION_SIDE_TOP)
-            
-
-
-class DivisionShape(CompositeShape):
-    """A division shape is like a composite in that it can contain further
-    objects, but is used exclusively to divide another shape into regions,
-    or divisions. A wxDivisionShape is never free-standing.
-
-    Derived from:
-      wxCompositeShape
-    """
-    def __init__(self):
-        CompositeShape.__init__(self)
-        self.SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT)
-        self.SetCentreResize(False)
-        self.SetAttachmentMode(True)
-        self._leftSide = None
-        self._rightSide = None
-        self._topSide = None
-        self._bottomSide = None
-        self._handleSide = DIVISION_SIDE_NONE
-        self._leftSidePen = wx.BLACK_PEN
-        self._topSidePen = wx.BLACK_PEN
-        self._leftSideColour="BLACK"
-        self._topSideColour="BLACK"
-        self._leftSideStyle="Solid"
-        self._topSideStyle="Solid"
-        self.ClearRegions()
-
-    def SetLeftSide(self, shape):
-        """Set the the division on the left side of this division."""
-        self._leftSide = shape
-
-    def SetTopSide(self, shape):
-        """Set the the division on the top side of this division."""
-        self._topSide = shape
-        
-    def SetRightSide(self, shape):
-        """Set the the division on the right side of this division."""
-        self._rightSide = shape
-        
-    def SetBottomSide(self, shape):
-        """Set the the division on the bottom side of this division."""
-        self._bottomSide = shape
-
-    def GetLeftSide(self):
-        """Return the division on the left side of this division."""
-        return self._leftSide
-    
-    def GetTopSide(self):
-        """Return the division on the top side of this division."""
-        return self._topSide
-    
-    def GetRightSide(self):
-        """Return the division on the right side of this division."""
-        return self._rightSide
-    
-    def GetBottomSide(self):
-        """Return the division on the bottom side of this division."""
-        return self._bottomSide
-
-    def SetHandleSide(self, side):
-        """Sets the side which the handle appears on.
-
-        Either DIVISION_SIDE_LEFT or DIVISION_SIDE_TOP.
-        """
-        self._handleSide = side
-
-    def GetHandleSide(self):
-        """Return the side which the handle appears on."""
-        return self._handleSide
-
-    def SetLeftSidePen(self, pen):
-        """Set the colour for drawing the left side of the division."""
-        self._leftSidePen = pen
-        
-    def SetTopSidePen(self, pen):
-        """Set the colour for drawing the top side of the division."""
-        self._topSidePen = pen
-        
-    def GetLeftSidePen(self):
-        """Return the pen used for drawing the left side of the division."""
-        return self._leftSidePen
-
-    def GetTopSidePen(self):
-        """Return the pen used for drawing the top side of the division."""
-        return self._topSidePen
-
-    def GetLeftSideColour(self):
-        """Return the colour used for drawing the left side of the division."""
-        return self._leftSideColour
-    
-    def GetTopSideColour(self):
-        """Return the colour used for drawing the top side of the division."""
-        return self._topSideColour
-
-    def SetLeftSideColour(self, colour):
-        """Set the colour for drawing the left side of the division."""
-        self._leftSideColour = colour
-        
-    def SetTopSideColour(self, colour):
-        """Set the colour for drawing the top side of the division."""
-        self._topSideColour = colour
-        
-    def GetLeftSideStyle(self):
-        """Return the style used for the left side of the division."""
-        return self._leftSideStyle
-    
-    def GetTopSideStyle(self):
-        """Return the style used for the top side of the division."""
-        return self._topSideStyle
-
-    def SetLeftSideStyle(self, style):
-        self._leftSideStyle = style
-        
-    def SetTopSideStyle(self, style):
-        self._lefttopStyle = style
-        
-    def OnDraw(self, dc):
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-        dc.SetBackgroundMode(wx.TRANSPARENT)
-
-        x1 = self.GetX()-self.GetWidth() / 2
-        y1 = self.GetY()-self.GetHeight() / 2
-        x2 = self.GetX() + self.GetWidth() / 2
-        y2 = self.GetY() + self.GetHeight() / 2
-
-        # Should subtract 1 pixel if drawing under Windows
-        if sys.platform[:3]=="win":
-            y2 -= 1
-
-        if self._leftSide:
-            dc.SetPen(self._leftSidePen)
-            dc.DrawLine(x1, y2, x1, y1)
-
-        if self._topSide:
-            dc.SetPen(self._topSidePen)
-            dc.DrawLine(x1, y1, x2, y1)
-
-        # For testing purposes, draw a rectangle so we know
-        # how big the division is.
-        #dc.SetBrush(wx.RED_BRUSH)
-        #dc.DrawRectangle(x1, y1, self.GetWidth(), self.GetHeight())
-        
-    def OnDrawContents(self, dc):
-        CompositeShape.OnDrawContents(self, dc)
-
-    def OnMovePre(self, dc, x, y, oldx, oldy, display = True):
-        diffX = x-oldx
-        diffY = y-oldy
-        for object in self._children:
-            object.Erase(dc)
-            object.Move(dc, object.GetX() + diffX, object.GetY() + diffY, display)
-        return True
-
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-                self._parent.GetEventHandler().OnDragLeft(draw, x, y, keys, attachment)
-            return
-        Shape.OnDragLeft(self, draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-                self._parent.GetEventHandler().OnBeginDragLeft(x, y, keys, attachment)
-            return
-        Shape.OnBeginDragLeft(x, y, keys, attachment)
-            
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        if self._canvas.HasCapture():
-            self._canvas.ReleaseMouse()
-        if self._sensitivity & OP_DRAG_LEFT != OP_DRAG_LEFT:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-                self._parent.GetEventHandler().OnEndDragLeft(x, y, keys, attachment)
-            return
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(wx.COPY)
-
-        self._xpos, self._ypos = self._canvas.Snap(self._xpos, self._ypos)
-        self.GetEventHandler().OnMovePre(dc, x, y, self._oldX, self._oldY)
-
-        self.ResetControlPoints()
-        self.Draw(dc)
-        self.MoveLinks(dc)
-        self.GetEventHandler().OnDrawControlPoints(dc)
-
-        if self._canvas and not self._canvas.GetQuickEditMode():
-            self._canvas.Redraw(dc)
-
-    def SetSize(self, w, h, recursive = True):
-        self._width = w
-        self._height = h
-        RectangleShape.SetSize(self, w, h, recursive)
-
-    def CalculateSize(self):
-        pass
-
-    # Experimental
-    def OnRightClick(self, x, y, keys = 0, attachment = 0):
-        if keys & KEY_CTRL:
-            self.PopupMenu(x, y)
-        else:
-            if self._parent:
-                hit = self._parent.HitTest(x, y)
-                if hit:
-                    attachment, dist = hit
-                self._parent.GetEventHandler().OnRightClick(x, y, keys, attachment)
-
-    # Divide wx.HORIZONTALly or wx.VERTICALly
-    def Divide(self, direction):
-        """Divide this division into two further divisions,
-        horizontally (direction is wxHORIZONTAL) or
-        vertically (direction is wxVERTICAL).
-        """
-        # Calculate existing top-left, bottom-right
-        x1 = self.GetX()-self.GetWidth() / 2
-        y1 = self.GetY()-self.GetHeight() / 2
-
-        compositeParent = self.GetParent()
-        oldWidth = self.GetWidth()
-        oldHeight = self.GetHeight()
-        if self.Selected():
-            self.Select(False)
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        if direction == wx.VERTICAL:
-            # Dividing vertically means notionally putting a horizontal
-            # line through it.
-            # Break existing piece into two.
-            newXPos1 = self.GetX()
-            newYPos1 = y1 + self.GetHeight() / 4
-            newXPos2 = self.GetX()
-            newYPos2 = y1 + 3 * self.GetHeight() / 4
-            newDivision = compositeParent.OnCreateDivision()
-            newDivision.Show(True)
-
-            self.Erase(dc)
-
-            # Anything adjoining the bottom of this division now adjoins the
-            # bottom of the new division.
-            for obj in compositeParent.GetDivisions():
-                if obj.GetTopSide() == self:
-                    obj.SetTopSide(newDivision)
-
-            newDivision.SetTopSide(self)
-            newDivision.SetBottomSide(self._bottomSide)
-            newDivision.SetLeftSide(self._leftSide)
-            newDivision.SetRightSide(self._rightSide)
-            self._bottomSide = newDivision
-
-            compositeParent.GetDivisions().append(newDivision)
-
-            # CHANGE: Need to insert this division at start of divisions in the
-            # object list, because e.g.:
-            # 1) Add division
-            # 2) Add contained object
-            # 3) Add division
-            # Division is now receiving mouse events _before_ the contained
-            # object, because it was added last (on top of all others)
-
-            # Add after the image that visualizes the container
-            compositeParent.AddChild(newDivision, compositeParent.FindContainerImage())
-
-            self._handleSide = DIVISION_SIDE_BOTTOM
-            newDivision.SetHandleSide(DIVISION_SIDE_TOP)
-
-            self.SetSize(oldWidth, oldHeight / 2)
-            self.Move(dc, newXPos1, newYPos1)
-
-            newDivision.SetSize(oldWidth, oldHeight / 2)
-            newDivision.Move(dc, newXPos2, newYPos2)
-        else:
-            # Dividing horizontally means notionally putting a vertical line
-            # through it.
-            # Break existing piece into two.
-            newXPos1 = x1 + self.GetWidth() / 4
-            newYPos1 = self.GetY()
-            newXPos2 = x1 + 3 * self.GetWidth() / 4
-            newYPos2 = self.GetY()
-            newDivision = compositeParent.OnCreateDivision()
-            newDivision.Show(True)
-
-            self.Erase(dc)
-
-            # Anything adjoining the left of this division now adjoins the
-            # left of the new division.
-            for obj in compositeParent.GetDivisions():
-                if obj.GetLeftSide() == self:
-                    obj.SetLeftSide(newDivision)
-
-            newDivision.SetTopSide(self._topSide)
-            newDivision.SetBottomSide(self._bottomSide)
-            newDivision.SetLeftSide(self)
-            newDivision.SetRightSide(self._rightSide)
-            self._rightSide = newDivision
-
-            compositeParent.GetDivisions().append(newDivision)
-            compositeParent.AddChild(newDivision, compositeParent.FindContainerImage())
-
-            self._handleSide = DIVISION_SIDE_RIGHT
-            newDivision.SetHandleSide(DIVISION_SIDE_LEFT)
-
-            self.SetSize(oldWidth / 2, oldHeight)
-            self.Move(dc, newXPos1, newYPos1)
-
-            newDivision.SetSize(oldWidth / 2, oldHeight)
-            newDivision.Move(dc, newXPos2, newYPos2)
-
-        if compositeParent.Selected():
-            compositeParent.DeleteControlPoints(dc)
-            compositeParent.MakeControlPoints()
-            compositeParent.MakeMandatoryControlPoints()
-
-        compositeParent.Draw(dc)
-        return True
-
-    def MakeControlPoints(self):
-        self.MakeMandatoryControlPoints()
-
-    def MakeMandatoryControlPoints(self):
-        maxX, maxY = self.GetBoundingBoxMax()
-        x = y = 0.0
-        direction = 0
-
-        if self._handleSide == DIVISION_SIDE_LEFT:
-            x=-maxX / 2
-            direction = CONTROL_POINT_HORIZONTAL
-        elif self._handleSide == DIVISION_SIDE_TOP:
-            y=-maxY / 2
-            direction = CONTROL_POINT_VERTICAL
-        elif self._handleSide == DIVISION_SIDE_RIGHT:
-            x = maxX / 2
-            direction = CONTROL_POINT_HORIZONTAL
-        elif self._handleSide == DIVISION_SIDE_BOTTOM:
-            y = maxY / 2
-            direction = CONTROL_POINT_VERTICAL
-
-        if self._handleSide != DIVISION_SIDE_NONE:
-            control = DivisionControlPoint(self._canvas, self, CONTROL_POINT_SIZE, x, y, direction)
-            self._canvas.AddShape(control)
-            self._controlPoints.append(control)
-
-    def ResetControlPoints(self):
-        self.ResetMandatoryControlPoints()
-
-    def ResetMandatoryControlPoints(self):
-        if not self._controlPoints:
-            return
-
-        maxX, maxY = self.GetBoundingBoxMax()
-
-        node = self._controlPoints[0]
-
-        if self._handleSide == DIVISION_SIDE_LEFT and node:
-            node._xoffset=-maxX / 2
-            node._yoffset = 0.0
-
-        if self._handleSide == DIVISION_SIDE_TOP and node:
-            node._xoffset = 0.0
-            node._yoffset=-maxY / 2
-
-        if self._handleSide == DIVISION_SIDE_RIGHT and node:
-            node._xoffset = maxX / 2
-            node._yoffset = 0.0
-
-        if self._handleSide == DIVISION_SIDE_BOTTOM and node:
-            node._xoffset = 0.0
-            node._yoffset = maxY / 2
-
-    def AdjustLeft(self, left, test):
-        """Adjust a side.
-
-        Returns FALSE if it's not physically possible to adjust it to
-        this point.
-        """
-        x2 = self.GetX() + self.GetWidth() / 2
-
-        if left >= x2:
-            return False
-
-        if test:
-            return True
-
-        newW = x2-left
-        newX = left + newW / 2
-        self.SetSize(newW, self.GetHeight())
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        self.Move(dc, newX, self.GetY())
-        return True
-
-    def AdjustTop(self, top, test):
-        """Adjust a side.
-
-        Returns FALSE if it's not physically possible to adjust it to
-        this point.
-        """
-        y2 = self.GetY() + self.GetHeight() / 2
-
-        if top >= y2:
-            return False
-
-        if test:
-            return True
-
-        newH = y2-top
-        newY = top + newH / 2
-        self.SetSize(self.GetWidth(), newH)
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        self.Move(dc, self.GetX(), newY)
-        return True
-
-    def AdjustRight(self, right, test):
-        """Adjust a side.
-
-        Returns FALSE if it's not physically possible to adjust it to
-        this point.
-        """
-        x1 = self.GetX()-self.GetWidth() / 2
-
-        if right <= x1:
-            return False
-
-        if test:
-            return True
-
-        newW = right-x1
-        newX = x1 + newW / 2
-        self.SetSize(newW, self.GetHeight())
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        self.Move(dc, newX, self.GetY())
-        return True
-    
-    def AdjustTop(self, top, test):
-        """Adjust a side.
-
-        Returns FALSE if it's not physically possible to adjust it to
-        this point.
-        """
-        y1 = self.GetY()-self.GetHeight() / 2
-
-        if bottom <= y1:
-            return False
-
-        if test:
-            return True
-
-        newH = bottom-y1
-        newY = y1 + newH / 2
-        self.SetSize(self.GetWidth(), newH)
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        self.Move(dc, self.GetX(), newY)
-        return True
-
-    # Resize adjoining divisions.
-    
-    # Behaviour should be as follows:
-    # If right edge moves, find all objects whose left edge
-    # adjoins this object, and move left edge accordingly.
-    # If left..., move ... right.
-    # If top..., move ... bottom.
-    # If bottom..., move top.
-    # If size goes to zero or end position is other side of start position,
-    # resize to original size and return.
-    #
-    def ResizeAdjoining(self, side, newPos, test):
-        """Resize adjoining divisions at the given side.
-
-        If test is TRUE, just see whether it's possible for each adjoining
-        region, returning FALSE if it's not.
-
-        side can be one of:
-
-        * DIVISION_SIDE_NONE
-        * DIVISION_SIDE_LEFT
-        * DIVISION_SIDE_TOP
-        * DIVISION_SIDE_RIGHT
-        * DIVISION_SIDE_BOTTOM
-        """
-        divisionParent = self.GetParent()
-        for division in divisionParent.GetDivisions():
-            if side == DIVISION_SIDE_LEFT:
-                if division._rightSide == self:
-                    success = division.AdjustRight(newPos, test)
-                    if not success and test:
-                        return false
-            elif side == DIVISION_SIDE_TOP:
-                if division._bottomSide == self:
-                    success = division.AdjustBottom(newPos, test)
-                    if not success and test:
-                        return False
-            elif side == DIVISION_SIDE_RIGHT:
-                if division._leftSide == self:
-                    success = division.AdjustLeft(newPos, test)
-                    if not success and test:
-                        return False
-            elif side == DIVISION_SIDE_BOTTOM:
-                if division._topSide == self:
-                    success = division.AdjustTop(newPos, test)
-                    if not success and test:
-                        return False
-        return True
-    
-    def EditEdge(self, side):
-        print "EditEdge() not implemented."
-
-    def PopupMenu(self, x, y):
-        menu = PopupDivisionMenu()
-        menu.SetClientData(self)
-        if self._leftSide:
-            menu.Enable(DIVISION_MENU_EDIT_LEFT_EDGE, True)
-        else:
-            menu.Enable(DIVISION_MENU_EDIT_LEFT_EDGE, False)
-        if self._topSide:
-            menu.Enable(DIVISION_MENU_EDIT_TOP_EDGE, True)
-        else:
-            menu.Enable(DIVISION_MENU_EDIT_TOP_EDGE, False)
-
-        x1, y1 = self._canvas.GetViewStart()
-        unit_x, unit_y = self._canvas.GetScrollPixelsPerUnit()
-
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        mouse_x = dc.LogicalToDeviceX(x-x1 * unit_x)
-        mouse_y = dc.LogicalToDeviceY(y-y1 * unit_y)
-
-        self._canvas.PopupMenu(menu, (mouse_x, mouse_y))
-
-        
diff --git a/wxPython/wx/lib/ogl/diagram.py b/wxPython/wx/lib/ogl/diagram.py
deleted file mode 100644 (file)
index 95ef272..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         diagram.py
-# Purpose:      Diagram class
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-import wx
-
-DEFAULT_MOUSE_TOLERANCE = 3
-
-
-
-class Diagram(object):
-    """Encapsulates an entire diagram, with methods for drawing. A diagram has
-    an associated ShapeCanvas.
-
-    Derived from:
-      Object
-    """
-    def __init__(self):
-        self._diagramCanvas = None
-        self._quickEditMode = False
-        self._snapToGrid = True
-        self._gridSpacing = 5.0
-        self._shapeList = []
-        self._mouseTolerance = DEFAULT_MOUSE_TOLERANCE
-
-    def Redraw(self, dc):
-        """Draw the shapes in the diagram on the specified device context."""
-        if self._shapeList:
-            if self.GetCanvas():
-                self.GetCanvas().SetCursor(wx.HOURGLASS_CURSOR)
-            for object in self._shapeList:
-                object.Draw(dc)
-            if self.GetCanvas():
-                self.GetCanvas().SetCursor(wx.STANDARD_CURSOR)
-
-    def Clear(self, dc):
-        """Clear the specified device context."""
-        dc.Clear()
-
-    def AddShape(self, object, addAfter = None):
-        """Adds a shape to the diagram. If addAfter is not None, the shape
-        will be added after addAfter.
-        """
-        if not object in self._shapeList:
-            if addAfter:
-                self._shapeList.insert(self._shapeList.index(addAfter) + 1, object)
-            else:
-                self._shapeList.append(object)
-
-            object.SetCanvas(self.GetCanvas())
-
-    def InsertShape(self, object):
-        """Insert a shape at the front of the shape list."""
-        self._shapeList.insert(0, object)
-
-    def RemoveShape(self, object):
-        """Remove the shape from the diagram (non-recursively) but do not
-        delete it.
-        """
-        if object in self._shapeList:
-            self._shapeList.remove(object)
-            
-    def RemoveAllShapes(self):
-        """Remove all shapes from the diagram but do not delete the shapes."""
-        self._shapeList = []
-
-    def DeleteAllShapes(self):
-        """Remove and delete all shapes in the diagram."""
-        for shape in self._shapeList[:]:
-            if not shape.GetParent():
-                self.RemoveShape(shape)
-                
-    def ShowAll(self, show):
-        """Call Show for each shape in the diagram."""
-        for shape in self._shapeList:
-            shape.Show()
-
-    def DrawOutLine(self, dc, x1, y1, x2, y2):
-        """Draw an outline rectangle on the current device context."""
-        dc.SetPen(wx.Pen(wx.Color(0, 0, 0), 1, wx.DOT))
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        dc.DrawLines([[x1, y1], [x2, y1], [x2, y2], [x1, y2], [x1, y1]])
-
-    def RecentreAll(self, dc):
-        """Make sure all text that should be centred, is centred."""
-        for shape in self._shapeList:
-            shape.Recentre(dc)
-
-    def SetCanvas(self, canvas):
-        """Set the canvas associated with this diagram."""
-        self._diagramCanvas = canvas
-
-    def GetCanvas(self):
-        """Return the shape canvas associated with this diagram."""
-        return self._diagramCanvas
-        
-    def FindShape(self, id):
-        """Return the shape for the given identifier."""
-        for shape in self._shapeList:
-            if shape.GetId() == id:
-                return shape
-        return None
-
-    def Snap(self, x, y):
-        """'Snaps' the coordinate to the nearest grid position, if
-        snap-to-grid is on."""
-        if self._snapToGrid:
-            return self._gridSpacing * int(x / self._gridSpacing + 0.5), self._gridSpacing * int(y / self._gridSpacing + 0.5)
-        return x, y
-
-    def GetGridSpacing(self):
-        """Return the grid spacing."""
-        return self._gridSpacing
-
-    def GetSnapToGrid(self):
-        """Return snap-to-grid mode."""
-        return self._snapToGrid
-
-    def SetQuickEditMode(self, mode):
-        """Set quick-edit-mode on of off.
-
-        In this mode, refreshes are minimized, but the diagram may need
-        manual refreshing occasionally.
-        """
-        self._quickEditMode = mode
-
-    def GetQuickEditMode(self):
-        """Return quick edit mode."""
-        return self._quickEditMode
-
-    def SetMouseTolerance(self, tolerance):
-        """Set the tolerance within which a mouse move is ignored.
-
-        The default is 3 pixels.
-        """
-        self._mouseTolerance = tolerance
-
-    def GetMouseTolerance(self):
-        """Return the tolerance within which a mouse move is ignored."""
-        return self._mouseTolerance
-
-    def GetShapeList(self):
-        """Return the internal shape list."""
-        return self._shapeList
-
-    def GetCount(self):
-        """Return the number of shapes in the diagram."""
-        return len(self._shapeList)
diff --git a/wxPython/wx/lib/ogl/divided.py b/wxPython/wx/lib/ogl/divided.py
deleted file mode 100644 (file)
index 2c7a5b8..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         divided.py
-# Purpose:      DividedShape class
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-import sys
-import wx
-
-from basic import ControlPoint, RectangleShape, Shape
-from oglmisc import *
-
-
-
-class DividedShapeControlPoint(ControlPoint):
-    def __init__(self, the_canvas, object, region, size, the_m_xoffset, the_m_yoffset, the_type):
-        ControlPoint.__init__(self, the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type)
-        self.regionId = region
-
-    # Implement resizing of divided object division
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        dividedObject = self._shape
-        x1 = dividedObject.GetX()-dividedObject.GetWidth() / 2
-        y1 = y
-        x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2
-        y2 = y
-
-        dc.DrawLine(x1, y1, x2, y2)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        dividedObject = self._shape
-        
-        x1 = dividedObject.GetX()-dividedObject.GetWidth() / 2
-        y1 = y
-        x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2
-        y2 = y
-
-        dc.DrawLine(x1, y1, x2, y2)
-        self._canvas.CaptureMouse()
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dividedObject = self._shape
-        if not dividedObject.GetRegions()[self.regionId]:
-            return
-        
-        thisRegion = dividedObject.GetRegions()[self.regionId]
-        nextRegion = None
-
-        dc.SetLogicalFunction(wx.COPY)
-
-        if self._canvas.HasCapture():
-            self._canvas.ReleaseMouse()
-
-        # Find the old top and bottom of this region,
-        # and calculate the new proportion for this region
-        # if legal.
-        currentY = dividedObject.GetY()-dividedObject.GetHeight() / 2
-        maxY = dividedObject.GetY() + dividedObject.GetHeight() / 2
-
-        # Save values
-        theRegionTop = 0
-        nextRegionBottom = 0
-        
-        for i in range(len(dividedObject.GetRegions())):
-            region = dividedObject.GetRegions()[i]
-            proportion = region._regionProportionY
-            yy = currentY + dividedObject.GetHeight() * proportion
-            actualY = min(maxY, yy)
-
-            if region == thisRegion:
-                thisRegionTop = currentY
-                
-                if i + 1<len(dividedObject.GetRegions()):
-                    nextRegion = dividedObject.GetRegions()[i + 1]
-            if region == nextRegion:
-                nextRegionBottom = actualY
-
-            currentY = actualY
-
-        if not nextRegion:
-            return
-
-        # Check that we haven't gone above this region or below
-        # next region.
-        if y <= thisRegionTop or y >= nextRegionBottom:
-            return
-
-        dividedObject.EraseLinks(dc)
-
-        # Now calculate the new proportions of this region and the next region
-        thisProportion = (y-thisRegionTop) / dividedObject.GetHeight()
-        nextProportion = (nextRegionBottom-y) / dividedObject.GetHeight()
-
-        thisRegion.SetProportions(0, thisProportion)
-        nextRegion.SetProportions(0, nextProportion)
-        self._yoffset = y-dividedObject.GetY()
-
-        # Now reformat text
-        for i, region in enumerate(dividedObject.GetRegions()):
-            if region.GetText():
-                s = region.GetText()
-                dividedObject.FormatText(dc, s, i)
-
-        dividedObject.SetRegionSizes()
-        dividedObject.Draw(dc)
-        dividedObject.GetEventHandler().OnMoveLinks(dc)
-        
-
-
-class DividedShape(RectangleShape):
-    """A DividedShape is a rectangle with a number of vertical divisions.
-    Each division may have its text formatted with independent characteristics,
-    and the size of each division relative to the whole image may be specified.
-
-    Derived from:
-      RectangleShape
-    """
-    def __init__(self, w, h):
-        RectangleShape.__init__(self, w, h)
-        self.ClearRegions()
-
-    def OnDraw(self, dc):
-        RectangleShape.OnDraw(self, dc)
-
-    def OnDrawContents(self, dc):
-        if self.GetRegions():
-            defaultProportion = 1 / len(self.GetRegions())
-        else:
-            defaultProportion = 0
-        currentY = self._ypos-self._height / 2
-        maxY = self._ypos + self._height / 2
-
-        leftX = self._xpos-self._width / 2
-        rightX = self._xpos + self._width / 2
-
-        if self._pen:
-            dc.SetPen(self._pen)
-
-        dc.SetTextForeground(self._textColour)
-
-        # For efficiency, don't do this under X - doesn't make
-        # any visible difference for our purposes.
-        if sys.platform[:3]=="win":
-            dc.SetTextBackground(self._brush.GetColour())
-
-        if self.GetDisableLabel():
-            return
-
-        xMargin = 2
-        yMargin = 2
-
-        dc.SetBackgroundMode(wx.TRANSPARENT)
-
-        for region in self.GetRegions():
-            dc.SetFont(region.GetFont())
-            dc.SetTextForeground(region.GetActualColourObject())
-
-            if region._regionProportionY<0:
-                proportion = defaultProportion
-            else:
-                proportion = region._regionProportionY
-
-            y = currentY + self._height * proportion
-            actualY = min(maxY, y)
-
-            centreX = self._xpos
-            centreY = currentY + (actualY-currentY) / 2
-
-            DrawFormattedText(dc, region._formattedText, centreX, centreY, self._width-2 * xMargin, actualY-currentY-2 * yMargin, region._formatMode)
-
-            if y <= maxY and region != self.GetRegions()[-1]:
-                regionPen = region.GetActualPen()
-                if regionPen:
-                    dc.SetPen(regionPen)
-                    dc.DrawLine(leftX, y, rightX, y)
-
-            currentY = actualY
-
-    def SetSize(self, w, h, recursive = True):
-        self.SetAttachmentSize(w, h)
-        self._width = w
-        self._height = h
-        self.SetRegionSizes()
-
-    def SetRegionSizes(self):
-        """Set all region sizes according to proportions and this object
-        total size.
-        """
-        if not self.GetRegions():
-            return
-
-        if self.GetRegions():
-            defaultProportion = 1 / len(self.GetRegions())
-        else:
-            defaultProportion = 0
-        currentY = self._ypos-self._height / 2
-        maxY = self._ypos + self._height / 2
-        
-        for region in self.GetRegions():
-            if region._regionProportionY <= 0:
-                proportion = defaultProportion
-            else:
-                proportion = region._regionProportionY
-
-            sizeY = proportion * self._height
-            y = currentY + sizeY
-            actualY = min(maxY, y)
-
-            centreY = currentY + (actualY-currentY) / 2
-
-            region.SetSize(self._width, sizeY)
-            region.SetPosition(0, centreY-self._ypos)
-
-            currentY = actualY
-
-    # Attachment points correspond to regions in the divided box
-    def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
-        totalNumberAttachments = len(self.GetRegions()) * 2 + 2
-        if self.GetAttachmentMode() == ATTACHMENT_MODE_NONE or attachment >= totalNumberAttachments:
-            return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs)
-
-        n = len(self.GetRegions())
-        isEnd = line and line.IsEnd(self)
-
-        left = self._xpos-self._width / 2
-        right = self._xpos + self._width / 2
-        top = self._ypos-self._height / 2
-        bottom = self._ypos + self._height / 2
-
-        # Zero is top, n + 1 is bottom
-        if attachment == 0:
-            y = top
-            if self._spaceAttachments:
-                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                    # Align line according to the next handle along
-                    point = line.GetNextControlPoint(self)
-                    if point.x<left:
-                        x = left
-                    elif point.x>right:
-                        x = right
-                    else:
-                        x = point.x
-                else:
-                    x = left + (nth + 1) * self._width / (no_arcs + 1)
-            else:
-                x = self._xpos
-        elif attachment == n + 1:
-            y = bottom
-            if self._spaceAttachments:
-                if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                    # Align line according to the next handle along
-                    point = line.GetNextControlPoint(self)
-                    if point.x<left:
-                        x = left
-                    elif point.x>right:
-                        x = right
-                    else:
-                        x = point.x
-                else:
-                    x = left + (nth + 1) * self._width / (no_arcs + 1)
-            else:
-                x = self._xpos
-        else: # Left or right
-            isLeft = not attachment<(n + 1)
-            if isLeft:
-                i = totalNumberAttachments-attachment-1
-            else:
-                i = attachment-1
-
-            region = self.GetRegions()[i]
-            if region:
-                if isLeft:
-                    x = left
-                else:
-                    x = right
-
-                # Calculate top and bottom of region
-                top = self._ypos + region._y-region._height / 2
-                bottom = self._ypos + region._y + region._height / 2
-
-                # Assuming we can trust the absolute size and
-                # position of these regions
-                if self._spaceAttachments:
-                    if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                        # Align line according to the next handle along
-                        point = line.GetNextControlPoint(self)
-                        if point.y<bottom:
-                            y = bottom
-                        elif point.y>top:
-                            y = top
-                        else:
-                            y = point.y
-                    else:
-                        y = top + (nth + 1) * region._height / (no_arcs + 1)
-                else:
-                    y = self._ypos + region._y
-            else:
-                return False
-        return x, y
-
-    def GetNumberOfAttachments(self):
-        # There are two attachments for each region (left and right),
-        # plus one on the top and one on the bottom.
-        n = len(self.GetRegions()) * 2 + 2
-
-        maxN = n-1
-        for point in self._attachmentPoints:
-            if point._id>maxN:
-                maxN = point._id
-
-        return maxN + 1
-
-    def AttachmentIsValid(self, attachment):
-        totalNumberAttachments = len(self.GetRegions()) * 2 + 2
-        if attachment >= totalNumberAttachments:
-            return Shape.AttachmentIsValid(self, attachment)
-        else:
-            return attachment >= 0
-
-    def MakeControlPoints(self):
-        RectangleShape.MakeControlPoints(self)
-        self.MakeMandatoryControlPoints()
-
-    def MakeMandatoryControlPoints(self):
-        currentY = self.GetY()-self._height / 2
-        maxY = self.GetY() + self._height / 2
-
-        for i, region in enumerate(self.GetRegions()):
-            proportion = region._regionProportionY
-
-            y = currentY + self._height * proportion
-            actualY = min(maxY, y)
-
-            if region != self.GetRegions()[-1]:
-                controlPoint = DividedShapeControlPoint(self._canvas, self, i, CONTROL_POINT_SIZE, 0, actualY-self.GetY(), 0)
-                self._canvas.AddShape(controlPoint)
-                self._controlPoints.append(controlPoint)
-
-            currentY = actualY
-
-    def ResetControlPoints(self):
-        # May only have the region handles, (n - 1) of them
-        if len(self._controlPoints)>len(self.GetRegions())-1:
-            RectangleShape.ResetControlPoints(self)
-
-        self.ResetMandatoryControlPoints()
-
-    def ResetMandatoryControlPoints(self):
-        currentY = self.GetY()-self._height / 2
-        maxY = self.GetY() + self._height / 2
-
-        i = 0
-        for controlPoint in self._controlPoints:
-            if isinstance(controlPoint, DividedShapeControlPoint):
-                region = self.GetRegions()[i]
-                proportion = region._regionProportionY
-
-                y = currentY + self._height * proportion
-                actualY = min(maxY, y)
-
-                controlPoint._xoffset = 0
-                controlPoint._yoffset = actualY-self.GetY()
-
-                currentY = actualY
-
-                i += 1
-                
-    def EditRegions(self):
-        """Edit the region colours and styles. Not implemented."""
-        print "EditRegions() is unimplemented"
-        
-    def OnRightClick(self, x, y, keys = 0, attachment = 0):
-        if keys & KEY_CTRL:
-            self.EditRegions()
-        else:
-            RectangleShape.OnRightClick(self, x, y, keys, attachment)
diff --git a/wxPython/wx/lib/ogl/lines.py b/wxPython/wx/lib/ogl/lines.py
deleted file mode 100644 (file)
index f4a191b..0000000
+++ /dev/null
@@ -1,1534 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         lines.py
-# Purpose:      LineShape class
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-
-import sys
-
-from math import sqrt
-
-from basic import Shape, ShapeRegion, ControlPoint, RectangleShape
-from oglmisc import *
-
-# Line alignment flags
-# Vertical by default
-LINE_ALIGNMENT_HORIZ=              1
-LINE_ALIGNMENT_VERT=               0
-LINE_ALIGNMENT_TO_NEXT_HANDLE=     2
-LINE_ALIGNMENT_NONE=               0
-
-
-
-class LineControlPoint(ControlPoint):
-    def __init__(self, theCanvas = None, object = None, size = 0.0, x = 0.0, y = 0.0, the_type = 0):
-        ControlPoint.__init__(self, theCanvas, object, size, x, y, the_type)
-        self._xpos = x
-        self._ypos = y
-        self._type = the_type
-        self._point = None
-        self._originalPos = None
-
-    def OnDraw(self, dc):
-        RectangleShape.OnDraw(self, dc)
-
-    # Implement movement of Line point
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingDragLeft(self, draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingBeginDragLeft(self, x, y, keys, attachment)
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        self._shape.GetEventHandler().OnSizingEndDragLeft(self, x, y, keys, attachment)
-
-
-
-class ArrowHead(object):
-    def __init__(self, type = 0, end = 0, size = 0.0, dist = 0.0, name="",mf = None, arrowId=-1):
-        if isinstance(type, ArrowHead):
-            pass
-        else:
-            self._arrowType = type
-            self._arrowEnd = end
-            self._arrowSize = size
-            self._xOffset = dist
-            self._yOffset = 0.0
-            self._spacing = 5.0
-
-            self._arrowName = name
-            self._metaFile = mf
-            self._id = arrowId
-            if self._id==-1:
-                self._id = wx.NewId()
-            
-    def _GetType(self):
-        return self._arrowType
-
-    def GetPosition(self):
-        return self._arrowEnd
-
-    def SetPosition(self, pos):
-        self._arrowEnd = pos
-
-    def GetXOffset(self):
-        return self._xOffset
-
-    def GetYOffset(self):
-        return self._yOffset
-    
-    def GetSpacing(self):
-        return self._spacing
-
-    def GetSize(self):
-        return self._arrowSize
-
-    def SetSize(self, size):
-        self._arrowSize = size
-        if self._arrowType == ARROW_METAFILE and self._metaFile:
-            oldWidth = self._metaFile._width
-            if oldWidth == 0:
-                return
-
-            scale = size / oldWidth
-            if scale != 1:
-                self._metaFile.Scale(scale, scale)
-                
-    def GetName(self):
-        return self._arrowName
-
-    def SetXOffset(self, x):
-        self._xOffset = x
-
-    def SetYOffset(self, y):
-        self._yOffset = y
-
-    def GetMetaFile(self):
-        return self._metaFile
-
-    def GetId(self):
-        return self._id
-
-    def GetArrowEnd(self):
-        return self._arrowEnd
-
-    def GetArrowSize(self):
-        return self._arrowSize
-
-    def SetSpacing(self, sp):
-        self._spacing = sp
-
-
-
-class LabelShape(RectangleShape):
-    def __init__(self, parent, region, w, h):
-        RectangleShape.__init__(self, w, h)
-        self._lineShape = parent
-        self._shapeRegion = region
-        self.SetPen(wx.ThePenList.FindOrCreatePen(wx.Colour(0, 0, 0), 1, wx.DOT))
-
-    def OnDraw(self, dc):
-        if self._lineShape and not self._lineShape.GetDrawHandles():
-            return
-
-        x1 = self._xpos-self._width / 2
-        y1 = self._ypos-self._height / 2
-
-        if self._pen:
-            if self._pen.GetWidth() == 0:
-                dc.SetPen(wx.Pen(wx.WHITE, 1, wx.TRANSPARENT))
-            else:
-                dc.SetPen(self._pen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        if self._cornerRadius>0:
-            dc.DrawRoundedRectangle(x1, y1, self._width, self._height, self._cornerRadius)
-        else:
-            dc.DrawRectangle(x1, y1, self._width, self._height)
-
-    def OnDrawContents(self, dc):
-        pass
-
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        RectangleShape.OnDragLeft(self, draw, x, y, keys, attachment)
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        RectangleShape.OnBeginDragLeft(self, x, y, keys, attachment)
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        RectangleShape.OnEndDragLeft(self, x, y, keys, attachment)
-
-    def OnMovePre(self, dc, x, y, old_x, old_y, display):
-        return self._lineShape.OnLabelMovePre(dc, self, x, y, old_x, old_y, display)
-
-    # Divert left and right clicks to line object
-    def OnLeftClick(self, x, y, keys = 0, attachment = 0):
-        self._lineShape.GetEventHandler().OnLeftClick(x, y, keys, attachment)
-
-    def OnRightClick(self, x, y, keys = 0, attachment = 0):
-        self._lineShape.GetEventHandler().OnRightClick(x, y, keys, attachment)
-        
-
-
-class LineShape(Shape):
-    """LineShape may be attached to two nodes;
-    it may be segmented, in which case a control point is drawn for each joint.
-
-    A wxLineShape may have arrows at the beginning, end and centre.
-
-    Derived from:
-      Shape
-    """
-    def __init__(self):
-        Shape.__init__(self)
-
-        self._sensitivity = OP_CLICK_LEFT | OP_CLICK_RIGHT
-        self._draggable = False
-        self._attachmentTo = 0
-        self._attachmentFrom = 0
-        self._from = None
-        self._to = None
-        self._erasing = False
-        self._arrowSpacing = 5.0
-        self._ignoreArrowOffsets = False
-        self._isSpline = False
-        self._maintainStraightLines = False
-        self._alignmentStart = 0
-        self._alignmentEnd = 0
-
-        self._lineControlPoints = None
-
-        # Clear any existing regions (created in an earlier constructor)
-        # and make the three line regions.
-        self.ClearRegions()
-        for name in ["Middle","Start","End"]:
-            newRegion = ShapeRegion()
-            newRegion.SetName(name)
-            newRegion.SetSize(150, 50)
-            self._regions.append(newRegion)
-
-        self._labelObjects = [None, None, None]
-        self._lineOrientations = []
-        self._lineControlPoints = []
-        self._arcArrows = []
-
-    def __del__(self):
-        if self._lineControlPoints:
-            self.ClearPointList(self._lineControlPoints)
-            self._lineControlPoints = []
-        for i in range(3):
-            if self._labelObjects[i]:
-                self._labelObjects[i].Select(False)
-                self._labelObjects[i].RemoveFromCanvas(self._canvas)
-        self._labelObjects = []
-        self.ClearArrowsAtPosition(-1)
-
-    def GetFrom(self):
-        """Return the 'from' object."""
-        return self._from
-    
-    def GetTo(self):
-        """Return the 'to' object."""
-        return self._to
-
-    def GetAttachmentFrom(self):
-        """Return the attachment point on the 'from' node."""
-        return self._attachmentFrom
-
-    def GetAttachmentTo(self):
-        """Return the attachment point on the 'to' node."""
-        return self._attachmentTo
-
-    def GetLineControlPoints(self):
-        return self._lineControlPoints
-
-    def SetSpline(self, spline):
-        """Specifies whether a spline is to be drawn through the control points."""
-        self._isSpline = spline
-
-    def IsSpline(self):
-        """TRUE if a spline is drawn through the control points."""
-        return self._isSpline
-
-    def SetAttachmentFrom(self, attach):
-        """Set the 'from' shape attachment."""
-        self._attachmentFrom = attach
-
-    def SetAttachmentTo(self, attach):
-        """Set the 'to' shape attachment."""
-        self._attachmentTo = attach
-
-    # This is really to distinguish between lines and other images.
-    # For lines, want to pass drag to canvas, since lines tend to prevent
-    # dragging on a canvas (they get in the way.)
-    def Draggable(self):
-        return False
-
-    def SetIgnoreOffsets(self, ignore):
-        """Set whether to ignore offsets from the end of the line when drawing."""
-        self._ignoreArrowOffsets = ignore
-
-    def GetArrows(self):
-        return self._arcArrows
-
-    def GetAlignmentStart(self):
-        return self._alignmentStart
-
-    def GetAlignmentEnd(self):
-        return self._alignmentEnd
-
-    def IsEnd(self, nodeObject):
-        """TRUE if shape is at the end of the line."""
-        return self._to == nodeObject
-
-    def MakeLineControlPoints(self, n):
-        """Make a given number of control points (minimum of two)."""
-        if self._lineControlPoints:
-            self.ClearPointList(self._lineControlPoints)
-        self._lineControlPoints = []
-        
-        for _ in range(n):
-            point = wx.RealPoint(-999,-999)
-            self._lineControlPoints.append(point)
-
-    def InsertLineControlPoint(self, dc = None):
-        """Insert a control point at an arbitrary position."""
-        if dc:
-            self.Erase(dc)
-
-        last_point = self._lineControlPoints[-1]
-        second_last_point = self._lineControlPoints[-2]
-
-        line_x = (last_point[0] + second_last_point[0]) / 2
-        line_y = (last_point[1] + second_last_point[1]) / 2
-
-        point = wx.RealPoint(line_x, line_y)
-        self._lineControlPoints.insert(len(self._lineControlPoints), point)
-
-    def DeleteLineControlPoint(self):
-        """Delete an arbitary point on the line."""
-        if len(self._lineControlPoints)<3:
-            return False
-
-        del self._lineControlPoints[-2]
-        return True
-
-    def Initialise(self):
-        """Initialise the line object."""
-        if self._lineControlPoints:
-            # Just move the first and last control points
-            first_point = self._lineControlPoints[0]
-            last_point = self._lineControlPoints[-1]
-
-            # If any of the line points are at -999, we must
-            # initialize them by placing them half way between the first
-            # and the last.
-
-            for point in self._lineControlPoints[1:]:
-                if point[0]==-999:
-                    if first_point[0]<last_point[0]:
-                        x1 = first_point[0]
-                        x2 = last_point[0]
-                    else:
-                        x2 = first_point[0]
-                        x1 = last_point[0]
-                    if first_point[1]<last_point[1]:
-                        y1 = first_point[1]
-                        y2 = last_point[1]
-                    else:
-                        y2 = first_point[1]
-                        y1 = last_point[1]
-                    point[0] = (x2-x1) / 2 + x1
-                    point[1] = (y2-y1) / 2 + y1
-                    
-    def FormatText(self, dc, s, i):
-        """Format a text string according to the region size, adding
-        strings with positions to region text list.
-        """
-        self.ClearText(i)
-
-        if len(self._regions) == 0 or i >= len(self._regions):
-            return
-
-        region = self._regions[i]
-        region.SetText(s)
-        dc.SetFont(region.GetFont())
-
-        w, h = region.GetSize()
-        # Initialize the size if zero
-        if (w == 0 or h == 0) and s:
-            w, h = 100, 50
-            region.SetSize(w, h)
-
-        string_list = FormatText(dc, s, w-5, h-5, region.GetFormatMode())
-        for s in string_list:
-            line = ShapeTextLine(0.0, 0.0, s)
-            region.GetFormattedText().append(line)
-
-        actualW = w
-        actualH = h
-        if region.GetFormatMode() & FORMAT_SIZE_TO_CONTENTS:
-            actualW, actualH = GetCentredTextExtent(dc, region.GetFormattedText(), self._xpos, self._ypos, w, h)
-            if actualW != w or actualH != h:
-                xx, yy = self.GetLabelPosition(i)
-                self.EraseRegion(dc, region, xx, yy)
-                if len(self._labelObjects)<i:
-                    self._labelObjects[i].Select(False, dc)
-                    self._labelObjects[i].Erase(dc)
-                    self._labelObjects[i].SetSize(actualW, actualH)
-
-                region.SetSize(actualW, actualH)
-
-                if len(self._labelObjects)<i:
-                    self._labelObjects[i].Select(True, dc)
-                    self._labelObjects[i].Draw(dc)
-
-        CentreText(dc, region.GetFormattedText(), self._xpos, self._ypos, actualW, actualH, region.GetFormatMode())
-        self._formatted = True
-
-    def DrawRegion(self, dc, region, x, y):
-        """Format one region at this position."""
-        if self.GetDisableLabel():
-            return
-
-        w, h = region.GetSize()
-
-        # Get offset from x, y
-        xx, yy = region.GetPosition()
-
-        xp = xx + x
-        yp = yy + y
-
-        # First, clear a rectangle for the text IF there is any
-        if len(region.GetFormattedText()):
-            dc.SetPen(self.GetBackgroundPen())
-            dc.SetBrush(self.GetBackgroundBrush())
-
-            # Now draw the text
-            if region.GetFont():
-                dc.SetFont(region.GetFont())
-                dc.DrawRectangle(xp-w / 2, yp-h / 2, w, h)
-
-                if self._pen:
-                    dc.SetPen(self._pen)
-                dc.SetTextForeground(region.GetActualColourObject())
-
-                DrawFormattedText(dc, region.GetFormattedText(), xp, yp, w, h, region.GetFormatMode())
-
-    def EraseRegion(self, dc, region, x, y):
-        """Erase one region at this position."""
-        if self.GetDisableLabel():
-            return
-
-        w, h = region.GetSize()
-
-        # Get offset from x, y
-        xx, yy = region.GetPosition()
-
-        xp = xx + x
-        yp = yy + y
-
-        if region.GetFormattedText():
-            dc.SetPen(self.GetBackgroundPen())
-            dc.SetBrush(self.GetBackgroundBrush())
-
-            dc.DrawRectangle(xp-w / 2, yp-h / 2, w, h)
-
-    def GetLabelPosition(self, position):
-        """Get the reference point for a label.
-
-        Region x and y are offsets from this.
-        position is 0 (middle), 1 (start), 2 (end).
-        """
-        if position == 0:
-            # Want to take the middle section for the label
-            half_way = int(len(self._lineControlPoints) / 2)
-
-            # Find middle of this line
-            point = self._lineControlPoints[half_way-1]
-            next_point = self._lineControlPoints[half_way]
-
-            dx = next_point[0]-point[0]
-            dy = next_point[1]-point[1]
-
-            return point[0] + dx / 2, point[1] + dy / 2
-        elif position == 1:
-            return self._lineControlPoints[0][0], self._lineControlPoints[0][1]
-        elif position == 2:
-            return self._lineControlPoints[-1][0], self._lineControlPoints[-1][1]
-
-    def Straighten(self, dc = None):
-        """Straighten verticals and horizontals."""
-        if len(self._lineControlPoints)<3:
-            return
-
-        if dc:
-            self.Erase(dc)
-
-        GraphicsStraightenLine(self._lineControlPoints[-1], self._lineControlPoints[-2])
-
-        for i in range(len(self._lineControlPoints)-2):
-            GraphicsStraightenLine(self._lineControlPoints[i], self._lineControlPoints[i + 1])
-                
-        if dc:
-            self.Draw(dc)
-
-    def Unlink(self):
-        """Unlink the line from the nodes at either end."""
-        if self._to:
-            self._to.GetLines().remove(self)
-        if self._from:
-            self._from.GetLines().remove(self)
-        self._to = None
-        self._from = None
-
-    def SetEnds(self, x1, y1, x2, y2):
-        """Set the end positions of the line."""
-        # Find centre point
-        first_point = self._lineControlPoints[0]
-        last_point = self._lineControlPoints[-1]
-
-        first_point[0] = x1
-        first_point[1] = y1
-        last_point[0] = x2
-        last_point[1] = y2
-
-        self._xpos = (x1 + x2) / 2
-        self._ypos = (y1 + y2) / 2
-
-    # Get absolute positions of ends
-    def GetEnds(self):
-        """Get the visible endpoints of the lines for drawing between two objects."""
-        first_point = self._lineControlPoints[0]
-        last_point = self._lineControlPoints[-1]
-
-        return (first_point[0], first_point[1]), (last_point[0], last_point[1])
-
-    def SetAttachments(self, from_attach, to_attach):
-        """Specify which object attachment points should be used at each end
-        of the line.
-        """
-        self._attachmentFrom = from_attach
-        self._attachmentTo = to_attach
-
-    def HitTest(self, x, y):
-        if not self._lineControlPoints:
-            return False
-
-        # Look at label regions in case mouse is over a label
-        inLabelRegion = False
-        for i in range(3):
-            if self._regions[i]:
-                region = self._regions[i]
-                if len(region._formattedText):
-                    xp, yp = self.GetLabelPosition(i)
-                    # Offset region from default label position
-                    cx, cy = region.GetPosition()
-                    cw, ch = region.GetSize()
-                    cx += xp
-                    cy += yp
-                    
-                    rLeft = cx-cw / 2
-                    rTop = cy-ch / 2
-                    rRight = cx + cw / 2
-                    rBottom = cy + ch / 2
-                    if x>rLeft and x<rRight and y>rTop and y<rBottom:
-                        inLabelRegion = True
-                        break
-
-        for i in range(len(self._lineControlPoints)-1):
-            point1 = self._lineControlPoints[i]
-            point2 = self._lineControlPoints[i + 1]
-
-            # For inaccurate mousing allow 8 pixel corridor
-            extra = 4
-
-            dx = point2[0]-point1[0]
-            dy = point2[1]-point1[1]
-
-            seg_len = sqrt(dx * dx + dy * dy)
-            if dy == 0 or dx == 0:
-                return False
-            distance_from_seg = seg_len * ((x-point1[0]) * dy-(y-point1[1]) * dx) / (dy * dy + dx * dx)
-            distance_from_prev = seg_len * ((y-point1[1]) * dy + (x-point1[0]) * dx) / (dy * dy + dx * dx)
-
-            if abs(distance_from_seg)<extra and distance_from_prev >= 0 and distance_from_prev <= seg_len or inLabelRegion:
-                return 0, distance_from_seg
-
-        return False
-
-    def DrawArrows(self, dc):
-        """Draw all arrows."""
-        # Distance along line of each arrow: space them out evenly
-        startArrowPos = 0.0
-        endArrowPos = 0.0
-        middleArrowPos = 0.0
-
-        for arrow in self._arcArrows:
-            ah = arrow.GetArrowEnd()
-            if ah == ARROW_POSITION_START:
-                if arrow.GetXOffset() and not self._ignoreArrowOffsets:
-                    # If specified, x offset is proportional to line length
-                    self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
-                else:
-                    self.DrawArrow(dc, arrow, startArrowPos, False)
-                    startArrowPos += arrow.GetSize() + arrow.GetSpacing()
-            elif ah == ARROW_POSITION_END:
-                if arrow.GetXOffset() and not self._ignoreArrowOffsets:
-                    self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
-                else:
-                    self.DrawArrow(dc, arrow, endArrowPos, False)
-                    endArrowPos += arrow.GetSize() + arrow.GetSpacing()
-            elif ah == ARROW_POSITION_MIDDLE:
-                arrow.SetXOffset(middleArrowPos)
-                if arrow.GetXOffset() and not self._ignoreArrowOffsets:
-                    self.DrawArrow(dc, arrow, arrow.GetXOffset(), True)
-                else:
-                    self.DrawArrow(dc, arrow, middleArrowPos, False)
-                    middleArrowPos += arrow.GetSize() + arrow.GetSpacing()
-
-    def DrawArrow(self, dc, arrow, XOffset, proportionalOffset):
-        """Draw the given arrowhead (or annotation)."""
-        first_line_point = self._lineControlPoints[0]
-        second_line_point = self._lineControlPoints[1]
-
-        last_line_point = self._lineControlPoints[-1]
-        second_last_line_point = self._lineControlPoints[-2]
-
-        # Position of start point of line, at the end of which we draw the arrow
-        startPositionX, startPositionY = 0.0, 0.0
-
-        ap = arrow.GetPosition()
-        if ap == ARROW_POSITION_START:
-            # If we're using a proportional offset, calculate just where this
-            # will be on the line.
-            realOffset = XOffset
-            if proportionalOffset:
-                totalLength = sqrt((second_line_point[0]-first_line_point[0]) * (second_line_point[0]-first_line_point[0]) + (second_line_point[1]-first_line_point[1]) * (second_line_point[1]-first_line_point[1]))
-                realOffset = XOffset * totalLength
-
-            positionOnLineX, positionOnLineY = GetPointOnLine(second_line_point[0], second_line_point[1], first_line_point[0], first_line_point[1], realOffset)
-            
-            startPositionX = second_line_point[0]
-            startPositionY = second_line_point[1]
-        elif ap == ARROW_POSITION_END:
-            # If we're using a proportional offset, calculate just where this
-            # will be on the line.
-            realOffset = XOffset
-            if proportionalOffset:
-                totalLength = sqrt((second_last_line_point[0]-last_line_point[0]) * (second_last_line_point[0]-last_line_point[0]) + (second_last_line_point[1]-last_line_point[1]) * (second_last_line_point[1]-last_line_point[1]));
-                realOffset = XOffset * totalLength
-            
-            positionOnLineX, positionOnLineY = GetPointOnLine(second_last_line_point[0], second_last_line_point[1], last_line_point[0], last_line_point[1], realOffset)
-            
-            startPositionX = second_last_line_point[0]
-            startPositionY = second_last_line_point[1]
-        elif ap == ARROW_POSITION_MIDDLE:
-            # Choose a point half way between the last and penultimate points
-            x = (last_line_point[0] + second_last_line_point[0]) / 2
-            y = (last_line_point[1] + second_last_line_point[1]) / 2
-
-            # If we're using a proportional offset, calculate just where this
-            # will be on the line.
-            realOffset = XOffset
-            if proportionalOffset:
-                totalLength = sqrt((second_last_line_point[0]-x) * (second_last_line_point[0]-x) + (second_last_line_point[1]-y) * (second_last_line_point[1]-y));
-                realOffset = XOffset * totalLength
-
-            positionOnLineX, positionOnLineY = GetPointOnLine(second_last_line_point[0], second_last_line_point[1], x, y, realOffset)
-            startPositionX = second_last_line_point[0]
-            startPositionY = second_last_line_point[1]
-
-        # Add yOffset to arrow, if any
-
-        # The translation that the y offset may give
-        deltaX = 0.0
-        deltaY = 0.0
-        if arrow.GetYOffset and not self._ignoreArrowOffsets:
-            #                             |(x4, y4)
-            #                             |d
-            #                             |
-            #   (x1, y1)--------------(x3, y3)------------------(x2, y2)
-            #   x4 = x3 - d * sin(theta)
-            #   y4 = y3 + d * cos(theta)
-            #
-            #   Where theta = tan(-1) of (y3-y1) / (x3-x1)
-            x1 = startPositionX
-            y1 = startPositionY
-            x3 = positionOnLineX
-            y3 = positionOnLineY
-            d=-arrow.GetYOffset() # Negate so +offset is above line
-
-            if x3 == x1:
-                theta = pi / 2
-            else:
-                theta = atan((y3-y1) / (x3-x1))
-
-            x4 = x3-d * sin(theta)
-            y4 = y3 + d * cos(theta)
-            
-            deltaX = x4-positionOnLineX
-            deltaY = y4-positionOnLineY
-
-        at = arrow._GetType()
-        if at == ARROW_ARROW:
-            arrowLength = arrow.GetSize()
-            arrowWidth = arrowLength / 3
-
-            tip_x, tip_y, side1_x, side1_y, side2_x, side2_y = GetArrowPoints(startPositionX + deltaX, startPositionY + deltaY, positionOnLineX + deltaX, positionOnLineY + deltaY, arrowLength, arrowWidth)
-
-            points = [[tip_x, tip_y],
-                    [side1_x, side1_y],
-                    [side2_x, side2_y],
-                    [tip_x, tip_y]]
-
-            dc.SetPen(self._pen)
-            dc.SetBrush(self._brush)
-            dc.DrawPolygon(points)
-        elif at in [ARROW_HOLLOW_CIRCLE, ARROW_FILLED_CIRCLE]:
-            # Find point on line of centre of circle, which is a radius away
-            # from the end position
-            diameter = arrow.GetSize()
-            x, y = GetPointOnLine(startPositionX + deltaX, startPositionY + deltaY,
-                               positionOnLineX + deltaX, positionOnLineY + deltaY,
-                               diameter / 2)
-            x1 = x-diameter / 2
-            y1 = y-diameter / 2
-            dc.SetPen(self._pen)
-            if arrow._GetType() == ARROW_HOLLOW_CIRCLE:
-                dc.SetBrush(self.GetBackgroundBrush())
-            else:
-                dc.SetBrush(self._brush)
-
-            dc.DrawEllipse(x1, y1, diameter, diameter)
-        elif at == ARROW_SINGLE_OBLIQUE:
-            pass
-        elif at == ARROW_METAFILE:
-            if arrow.GetMetaFile():
-                # Find point on line of centre of object, which is a half-width away
-                # from the end position
-                #
-                #                 width
-                #  <-- start pos  <-----><-- positionOnLineX
-                #                 _____
-                #  --------------|  x  | <-- e.g. rectangular arrowhead
-                #                 -----
-                #
-                x, y = GetPointOnLine(startPositionX, startPositionY,
-                                   positionOnLineX, positionOnLineY,
-                                   arrow.GetMetaFile()._width / 2)
-                # Calculate theta for rotating the metafile.
-                #
-                # |
-                # |     o(x2, y2)   'o' represents the arrowhead.
-                # |    /
-                # |   /
-                # |  /theta
-                # | /(x1, y1)
-                # |______________________
-                #
-                theta = 0.0
-                x1 = startPositionX
-                y1 = startPositionY
-                x2 = positionOnLineX
-                y2 = positionOnLineY
-
-                if x1 == x2 and y1 == y2:
-                    theta = 0.0
-                elif x1 == x2 and y1>y2:
-                    theta = 3.0 * pi / 2
-                elif x1 == x2 and y2>y1:
-                    theta = pi / 2
-                elif x2>x1 and y2 >= y1:
-                    theta = atan((y2-y1) / (x2-x1))
-                elif x2<x1:
-                    theta = pi + atan((y2-y1) / (x2-x1))
-                elif x2>x1 and y2<y1:
-                    theta = 2 * pi + atan((y2-y1) / (x2-x1))
-                else:
-                    raise "Unknown arrowhead rotation case"
-
-                # Rotate about the centre of the object, then place
-                # the object on the line.
-                if arrow.GetMetaFile().GetRotateable():
-                    arrow.GetMetaFile().Rotate(0.0, 0.0, theta)
-
-                if self._erasing:
-                    # If erasing, just draw a rectangle
-                    minX, minY, maxX, maxY = arrow.GetMetaFile().GetBounds()
-                    # Make erasing rectangle slightly bigger or you get droppings
-                    extraPixels = 4
-                    dc.DrawRectangle(deltaX + x + minX-extraPixels / 2, deltaY + y + minY-extraPixels / 2, maxX-minX + extraPixels, maxY-minY + extraPixels)
-                else:
-                    arrow.GetMetaFile().Draw(dc, x + deltaX, y + deltaY)
-
-    def OnErase(self, dc):
-        old_pen = self._pen
-        old_brush = self._brush
-
-        bg_pen = self.GetBackgroundPen()
-        bg_brush = self.GetBackgroundBrush()
-        self.SetPen(bg_pen)
-        self.SetBrush(bg_brush)
-
-        bound_x, bound_y = self.GetBoundingBoxMax()
-        if self._font:
-            dc.SetFont(self._font)
-
-        # Undraw text regions
-        for i in range(3):
-            if self._regions[i]:
-                x, y = self.GetLabelPosition(i)
-                self.EraseRegion(dc, self._regions[i], x, y)
-
-        # Undraw line
-        dc.SetPen(self.GetBackgroundPen())
-        dc.SetBrush(self.GetBackgroundBrush())
-
-        # Drawing over the line only seems to work if the line has a thickness
-        # of 1.
-        if old_pen and old_pen.GetWidth()>1:
-            dc.DrawRectangle(self._xpos-bound_x / 2-2, self._ypos-bound_y / 2-2,
-                             bound_x + 4, bound_y + 4)
-        else:
-            self._erasing = True
-            self.GetEventHandler().OnDraw(dc)
-            self.GetEventHandler().OnEraseControlPoints(dc)
-            self._erasing = False
-
-        if old_pen:
-            self.SetPen(old_pen)
-        if old_brush:
-            self.SetBrush(old_brush)
-
-    def GetBoundingBoxMin(self):
-        x1, y1 = 10000, 10000
-        x2, y2=-10000,-10000
-
-        for point in self._lineControlPoints:
-            if point[0]<x1:
-                x1 = point[0]
-            if point[1]<y1:
-                y1 = point[1]
-            if point[0]>x2:
-                x2 = point[0]
-            if point[1]>y2:
-                y2 = point[1]
-
-        return x2-x1, y2-y1
-        
-    # For a node image of interest, finds the position of this arc
-    # amongst all the arcs which are attached to THIS SIDE of the node image,
-    # and the number of same.
-    def FindNth(self, image, incoming):
-        """Find the position of the line on the given object.
-
-        Specify whether incoming or outgoing lines are being considered
-        with incoming.
-        """
-        n=-1
-        num = 0
-        
-        if image == self._to:
-            this_attachment = self._attachmentTo
-        else:
-            this_attachment = self._attachmentFrom
-
-        # Find number of lines going into / out of this particular attachment point
-        for line in image.GetLines():
-            if line._from == image:
-                # This is the nth line attached to 'image'
-                if line == self and not incoming:
-                    n = num
-
-                # Increment num count if this is the same side (attachment number)
-                if line._attachmentFrom == this_attachment:
-                    num += 1
-
-            if line._to == image:
-                # This is the nth line attached to 'image'
-                if line == self and incoming:
-                    n = num
-
-                # Increment num count if this is the same side (attachment number)
-                if line._attachmentTo == this_attachment:
-                    num += 1
-
-        return n, num
-
-    def OnDrawOutline(self, dc, x, y, w, h):
-        old_pen = self._pen
-        old_brush = self._brush
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        self.SetPen(dottedPen)
-        self.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        self.GetEventHandler().OnDraw(dc)
-
-        if old_pen:
-            self.SetPen(old_pen)
-        else:
-            self.SetPen(None)
-        if old_brush:
-            self.SetBrush(old_brush)
-        else:
-            self.SetBrush(None)
-
-    def OnMovePre(self, dc, x, y, old_x, old_y, display = True):
-        x_offset = x-old_x
-        y_offset = y-old_y
-
-        if self._lineControlPoints and not (x_offset == 0 and y_offset == 0):
-            for point in self._lineControlPoints:
-                point[0] += x_offset
-                point[1] += y_offset
-
-        # Move temporary label rectangles if necessary
-        for i in range(3):
-            if self._labelObjects[i]:
-                self._labelObjects[i].Erase(dc)
-                xp, yp = self.GetLabelPosition(i)
-                if i<len(self._regions):
-                    xr, yr = self._regions[i].GetPosition()
-                else:
-                    xr, yr = 0, 0
-                self._labelObjects[i].Move(dc, xp + xr, yp + yr)
-        return True
-
-    def OnMoveLink(self, dc, moveControlPoints = True):
-        """Called when a connected object has moved, to move the link to
-        correct position
-        """
-        if not self._from or not self._to:
-            return
-
-        if len(self._lineControlPoints)>2:
-            self.Initialise()
-
-        # Do each end - nothing in the middle. User has to move other points
-        # manually if necessary
-        end_x, end_y, other_end_x, other_end_y = self.FindLineEndPoints()
-
-        first = self._lineControlPoints[0]
-        last = self._lineControlPoints[-1]
-
-        oldX, oldY = self._xpos, self._ypos
-
-        self.SetEnds(end_x, end_y, other_end_x, other_end_y)
-
-        # Do a second time, because one may depend on the other
-        end_x, end_y, other_end_x, other_end_y = self.FindLineEndPoints()
-        self.SetEnds(end_x, end_y, other_end_x, other_end_y)
-
-        # Try to move control points with the arc
-        x_offset = self._xpos-oldX
-        y_offset = self._ypos-oldY
-
-        # Only move control points if it's a self link. And only works
-        # if attachment mode is ON
-        if self._from == self._to and self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE and moveControlPoints and self._lineControlPoints and not (x_offset == 0 and y_offset == 0):
-            for point in self._lineControlPoints[1:-1]:
-                point.x += x_offset
-                point.y += y_offset
-
-        self.Move(dc, self._xpos, self._ypos)
-
-    def FindLineEndPoints(self):
-        """Finds the x, y points at the two ends of the line.
-
-        This function can be used by e.g. line-routing routines to
-        get the actual points on the two node images where the lines will be
-        drawn to / from.
-        """
-        if not self._from or not self._to:
-            return
-
-        # Do each end - nothing in the middle. User has to move other points
-        # manually if necessary.
-        second_point = self._lineControlPoints[1]
-        second_last_point = self._lineControlPoints[-2]
-        
-        if len(self._lineControlPoints)>2:
-            if self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
-                nth, no_arcs = self.FindNth(self._from, False) # Not incoming
-                end_x, end_y = self._from.GetAttachmentPosition(self._attachmentFrom, nth, no_arcs, self)
-            else:
-                end_x, end_y = self._from.GetPerimeterPoint(self._from.GetX(), self._from.GetY(), second_point[0], second_point[1])
-
-            if self._to.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
-                nth, no_arch = self.FindNth(self._to, True) # Incoming
-                other_end_x, other_end_y = self._to.GetAttachmentPosition(self._attachmentTo, nth, no_arch, self)
-            else:
-                other_end_x, other_end_y = self._to.GetPerimeterPoint(self._to.GetX(), self._to.GetY(), second_last_point[0], second_last_point[1])
-        else:
-            fromX = self._from.GetX()
-            fromY = self._from.GetY()
-            toX = self._to.GetX()
-            toY = self._to.GetY()
-
-            if self._from.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
-                nth, no_arcs = self.FindNth(self._from, False)
-                end_x, end_y = self._from.GetAttachmentPosition(self._attachmentFrom, nth, no_arcs, self)
-                fromX = end_x
-                fromY = end_y
-
-            if self._to.GetAttachmentMode() != ATTACHMENT_MODE_NONE:
-                nth, no_arcs = self.FindNth(self._to, True)
-                other_end_x, other_end_y = self._to.GetAttachmentPosition(self._attachmentTo, nth, no_arcs, self)
-                toX = other_end_x
-                toY = other_end_y
-
-            if self._from.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
-                end_x, end_y = self._from.GetPerimeterPoint(self._from.GetX(), self._from.GetY(), toX, toY)
-
-            if self._to.GetAttachmentMode() == ATTACHMENT_MODE_NONE:
-                other_end_x, other_end_y = self._to.GetPerimeterPoint(self._to.GetX(), self._to.GetY(), fromX, fromY)
-
-            #print type(self._from), type(self._to), end_x, end_y, other_end_x, other_end_y
-            return end_x, end_y, other_end_x, other_end_y
-
-    def OnDraw(self, dc):
-        if not self._lineControlPoints:
-            return
-
-        if self._pen:
-            dc.SetPen(self._pen)
-        if self._brush:
-            dc.SetBrush(self._brush)
-
-        points = []
-        for point in self._lineControlPoints:
-            points.append(wx.Point(point.x, point.y))
-
-        #print points
-        if self._isSpline:
-            dc.DrawSpline(points)
-        else:
-            dc.DrawLines(points)
-
-        if sys.platform[:3]=="win":
-            # For some reason, last point isn't drawn under Windows
-            pt = points[-1]
-            dc.DrawPoint(pt.x, pt.y)
-
-        # Problem with pen - if not a solid pen, does strange things
-        # to the arrowhead. So make (get) a new pen that's solid.
-        if self._pen and self._pen.GetStyle() != wx.SOLID:
-            solid_pen = wx.ThePenList().FindOrCreatePen(self._pen.GetColour(), 1, wx.SOLID)
-            if solid_pen:
-                dc.SetPen(solid_pen)
-
-        self.DrawArrows(dc)
-
-    def OnDrawControlPoints(self, dc):
-        if not self._drawHandles:
-            return
-
-        # Draw temporary label rectangles if necessary
-        for i in range(3):
-            if self._labelObjects[i]:
-                self._labelObjects[i].Draw(dc)
-
-        Shape.OnDrawControlPoints(self, dc)
-
-    def OnEraseControlPoints(self, dc):
-        # Erase temporary label rectangles if necessary
-        
-        for i in range(3):
-            if self._labelObjects[i]:
-                self._labelObjects[i].Erase(dc)
-
-        Shape.OnEraseControlPoints(self, dc)
-
-    def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
-        pass
-
-    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
-        pass
-
-    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
-        pass
-
-    def OnDrawContents(self, dc):
-        if self.GetDisableLabel():
-            return
-
-        for i in range(3):
-            if self._regions[i]:
-                x, y = self.GetLabelPosition(i)
-                self.DrawRegion(dc, self._regions[i], x, y)
-
-    def SetTo(self, object):
-        """Set the 'to' object for the line."""
-        self._to = object
-
-    def SetFrom(self, object):
-        """Set the 'from' object for the line."""
-        self._from = object
-
-    def MakeControlPoints(self):
-        """Make handle control points."""
-        if self._canvas and self._lineControlPoints:
-            first = self._lineControlPoints[0]
-            last = self._lineControlPoints[-1]
-
-            control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, first[0], first[1], CONTROL_POINT_ENDPOINT_FROM)
-            control._point = first
-            self._canvas.AddShape(control)
-            self._controlPoints.Append(control)
-
-            for point in self._lineControlPoints[1:-1]:
-                control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, point[0], point[1], CONTROL_POINT_LINE)
-                control._point = point
-                self._canvas.AddShape(control)
-                self._controlPoints.Append(control)
-
-            control = LineControlPoint(self._canvas, self, CONTROL_POINT_SIZE, last[0], last[1], CONTROL_POINT_ENDPOINT_TO)
-            control._point = last
-            self._canvas.AddShape(control)
-            self._controlPoints.Append(control)
-
-    def ResetControlPoints(self):
-        if self._canvas and self._lineControlPoints:
-            for i in range(min(len(self._controlPoints), len(self._lineControlPoints))):
-                point = self._lineControlPoints[i]
-                control = self._controlPoints[i]
-                control.SetX(point[0])
-                control.SetY(point[1])
-
-    # Override select, to create / delete temporary label-moving objects
-    def Select(self, select, dc = None):
-        Shape.Select(self, select, dc)
-        if select:
-            for i in range(3):
-                if self._regions[i]:
-                    region = self._regions[i]
-                    if region._formattedText:
-                        w, h = region.GetSize()
-                        x, y = region.GetPosition()
-                        xx, yy = self.GetLabelPosition(i)
-
-                        if self._labelObjects[i]:
-                            self._labelObjects[i].Select(False)
-                            self._labelObjects[i].RemoveFromCanvas(self._canvas)
-
-                        self._labelObjects[i] = self.OnCreateLabelShape(self, region, w, h)
-                        self._labelObjects[i].AddToCanvas(self._canvas)
-                        self._labelObjects[i].Show(True)
-                        if dc:
-                            self._labelObjects[i].Move(dc, x + xx, y + yy)
-                        self._labelObjects[i].Select(True, dc)
-        else:
-            for i in range(3):
-                if self._labelObjects[i]:
-                    self._labelObjects[i].Select(False, dc)
-                    self._labelObjects[i].Erase(dc)
-                    self._labelObjects[i].RemoveFromCanvas(self._canvas)
-                    self._labelObjects[i] = None
-
-    # Control points ('handles') redirect control to the actual shape, to
-    # make it easier to override sizing behaviour.
-    def OnSizingDragLeft(self, pt, draw, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        dc.SetLogicalFunction(OGLRBLF)
-
-        dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-        dc.SetPen(dottedPen)
-        dc.SetBrush(wx.TRANSPARENT_BRUSH)
-
-        if pt._type == CONTROL_POINT_LINE:
-            x, y = self._canvas.Snap()
-
-            pt.SetX(x)
-            pt.SetY(y)
-            pt._point[0] = x
-            pt._point[1] = y
-
-            old_pen = self.GetPen()
-            old_brush = self.GetBrush()
-
-            self.SetPen(dottedPen)
-            self.SetBrush(wx.TRANSPARENT_BRUSH)
-
-            self.GetEventHandler().OnMoveLink(dc, False)
-            
-            self.SetPen(old_pen)
-            self.SetBrush(old_brush)
-
-    def OnSizingBeginDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        if pt._type == CONTROL_POINT_LINE:
-            pt._originalPos = pt._point
-            x, y = self._canvas.Snap()
-
-            self.Erase(dc)
-
-            # Redraw start and end objects because we've left holes
-            # when erasing the line
-            self.GetFrom().OnDraw(dc)
-            self.GetFrom().OnDrawContents(dc)
-            self.GetTo().OnDraw(dc)
-            self.GetTo().OnDrawContents(dc)
-
-            self.SetDisableLabel(True)
-            dc.SetLogicalFunction(OGLRBLF)
-
-            pt._xpos = x
-            pt._ypos = y
-            pt._point[0] = x
-            pt._point[1] = y
-
-            old_pen = self.GetPen()
-            old_brush = self.GetBrush()
-
-            dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
-            self.SetPen(dottedPen)
-            self.SetBrush(wx.TRANSPARENT_BRUSH)
-
-            self.GetEventHandler().OnMoveLink(dc, False)
-
-            self.SetPen(old_pen)
-            self.SetBrush(old_brush)
-
-        if pt._type == CONTROL_POINT_ENDPOINT_FROM or pt._type == CONTROL_POINT_ENDPOINT_TO:
-            self._canvas.SetCursor(wx.Cursor(wx.CURSOR_BULLSEYE))
-            pt._oldCursor = wx.STANDARD_CURSOR
-
-    def OnSizingEndDragLeft(self, pt, x, y, keys = 0, attachment = 0):
-        dc = wx.ClientDC(self.GetCanvas())
-        self.GetCanvas().PrepareDC(dc)
-
-        self.SetDisableLabel(False)
-
-        if pt._type == CONTROL_POINT_LINE:
-            x, y = self._canvas.Snap()
-
-            rpt = wx.RealPoint(x, y)
-
-            # Move the control point back to where it was;
-            # MoveControlPoint will move it to the new position
-            # if it decides it wants. We only moved the position
-            # during user feedback so we could redraw the line
-            # as it changed shape.
-            pt._xpos = pt._originalPos[0]
-            pt._ypos = pt._originalPos[1]
-            pt._point[0] = pt._originalPos[0]
-            pt._point[1] = pt._originalPos[1]
-
-            self.OnMoveMiddleControlPoint(dc, pt, rpt)
-
-        if pt._type == CONTROL_POINT_ENDPOINT_FROM:
-            if pt._oldCursor:
-                self._canvas.SetCursor(pt._oldCursor)
-
-                if self.GetFrom():
-                    self.GetFrom().MoveLineToNewAttachment(dc, self, x, y)
-
-        if pt._type == CONTROL_POINT_ENDPOINT_TO:
-            if pt._oldCursor:
-                self._canvas.SetCursor(pt._oldCursor)
-
-                if self.GetTo():
-                    self.GetTo().MoveLineToNewAttachment(dc, self, x, y)
-
-    # This is called only when a non-end control point is moved
-    def OnMoveMiddleControlPoint(self, dc, lpt, pt):
-        lpt._xpos = pt[0]
-        lpt._ypos = pt[1]
-
-        lpt._point[0] = pt[0]
-        lpt._point[1] = pt[1]
-
-        self.GetEventHandler().OnMoveLink(dc)
-
-        return True
-
-    def AddArrow(self, type, end = ARROW_POSITION_END, size = 10.0, xOffset = 0.0, name="",mf = None, arrowId=-1):
-        """Add an arrow (or annotation) to the line.
-
-        type may currently be one of:
-
-        ARROW_HOLLOW_CIRCLE
-          Hollow circle. 
-        ARROW_FILLED_CIRCLE
-          Filled circle. 
-        ARROW_ARROW
-          Conventional arrowhead. 
-        ARROW_SINGLE_OBLIQUE
-          Single oblique stroke. 
-        ARROW_DOUBLE_OBLIQUE
-          Double oblique stroke. 
-        ARROW_DOUBLE_METAFILE
-          Custom arrowhead. 
-
-        end may currently be one of:
-
-        ARROW_POSITION_END
-          Arrow appears at the end. 
-        ARROW_POSITION_START
-          Arrow appears at the start. 
-
-        arrowSize specifies the length of the arrow.
-
-        xOffset specifies the offset from the end of the line.
-
-        name specifies a name for the arrow.
-
-        mf can be a wxPseduoMetaFile, perhaps loaded from a simple Windows
-        metafile.
-
-        arrowId is the id for the arrow.
-        """
-        arrow = ArrowHead(type, end, size, xOffset, name, mf, arrowId)
-        self._arcArrows.append(arrow)
-        return arrow
-
-    # Add arrowhead at a particular position in the arrowhead list
-    def AddArrowOrdered(self, arrow, referenceList, end):
-        """Add an arrowhead in the position indicated by the reference list
-        of arrowheads, which contains all legal arrowheads for this line, in
-        the correct order. E.g.
-
-        Reference list:      a b c d e
-        Current line list:   a d
-
-        Add c, then line list is: a c d.
-
-        If no legal arrowhead position, return FALSE. Assume reference list
-        is for one end only, since it potentially defines the ordering for
-        any one of the 3 positions. So we don't check the reference list for
-        arrowhead position.
-        """
-        if not referenceList:
-            return False
-
-        targetName = arrow.GetName()
-
-        # First check whether we need to insert in front of list,
-        # because this arrowhead is the first in the reference
-        # list and should therefore be first in the current list.
-        refArrow = referenceList[0]
-        if refArrow.GetName() == targetName:
-            self._arcArrows.insert(0, arrow)
-            return True
-
-        i1 = i2 = 0
-        while i1<len(referenceList) and i2<len(self._arcArrows):
-            refArrow = referenceList[i1]
-            currArrow = self._arcArrows[i2]
-
-            # Matching: advance current arrow pointer
-            if currArrow.GetArrowEnd() == end and currArrow.GetName() == refArrow.GetName():
-                i2 += 1
-
-            # Check if we're at the correct position in the
-            # reference list
-            if targetName == refArrow.GetName():
-                if i2<len(self._arcArrows):
-                    self._arcArrows.insert(i2, arrow)
-                else:
-                    self._arcArrows.append(arrow)
-                return True
-            i1 += 1
-
-        self._arcArrows.append(arrow)
-        return True
-
-    def ClearArrowsAtPosition(self, end):
-        """Delete the arrows at the specified position, or at any position
-        if position is -1.
-        """
-        if end==-1:
-            self._arcArrows = []
-            return
-
-        for arrow in self._arcArrows:
-            if arrow.GetArrowEnd() == end:
-                self._arcArrows.remove(arrow)
-
-    def ClearArrow(self, name):
-        """Delete the arrow with the given name."""
-        for arrow in self._arcArrows:
-            if arrow.GetName() == name:
-                self._arcArrows.remove(arrow)
-                return True
-        return False
-
-    def FindArrowHead(self, position, name):
-        """Find arrowhead by position and name.
-
-        if position is -1, matches any position.
-        """
-        for arrow in self._arcArrows:
-            if (position==-1 or position == arrow.GetArrowEnd()) and arrow.GetName() == name:
-                return arow
-
-        return None
-
-    def FindArrowHeadId(self, arrowId):
-        """Find arrowhead by id."""
-        for arrow in self._arcArrows:
-            if arrowId == arrow.GetId():
-                return arrow
-
-        return None
-
-    def DeleteArrowHead(self, position, name):
-        """Delete arrowhead by position and name.
-
-        if position is -1, matches any position.
-        """
-        for arrow in self._arcArrows:
-            if (position==-1 or position == arrow.GetArrowEnd()) and arrow.GetName() == name:
-                self._arcArrows.remove(arrow)
-                return True
-        return False
-    
-    def DeleteArrowHeadId(self, id):
-        """Delete arrowhead by id."""
-        for arrow in self._arcArrows:
-            if arrowId == arrow.GetId():
-                self._arcArrows.remove(arrow)
-                return True
-        return False
-
-    # Calculate the minimum width a line
-    # occupies, for the purposes of drawing lines in tools.
-    def FindMinimumWidth(self):
-        """Find the horizontal width for drawing a line with arrows in
-        minimum space. Assume arrows at end only.
-        """
-        minWidth = 0.0
-        for arrowHead in self._arcArrows:
-            minWidth += arrowHead.GetSize()
-            if arrowHead != self._arcArrows[-1]:
-                minWidth += arrowHead + GetSpacing
-
-        # We have ABSOLUTE minimum now. So
-        # scale it to give it reasonable aesthetics
-        # when drawing with line.
-        if minWidth>0:
-            minWidth = minWidth * 1.4
-        else:
-            minWidth = 20.0
-
-        self.SetEnds(0.0, 0.0, minWidth, 0.0)
-        self.Initialise()
-
-        return minWidth
-
-    def FindLinePosition(self, x, y):
-        """Find which position we're talking about at this x, y.
-
-        Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END.
-        """
-        startX, startY, endX, endY = self.GetEnds()
-
-        # Find distances from centre, start and end. The smallest wins
-        centreDistance = sqrt((x-self._xpos) * (x-self._xpos) + (y-self._ypos) * (y-self._ypos))
-        startDistance = sqrt((x-startX) * (x-startX) + (y-startY) * (y-startY))
-        endDistance = sqrt((x-endX) * (x-endX) + (y-endY) * (y-endY))
-
-        if centreDistance<startDistance and centreDistance<endDistance:
-            return ARROW_POSITION_MIDDLE
-        elif startDistance<endDistance:
-            return ARROW_POSITION_START
-        else:
-            return ARROW_POSITION_END
-
-    def SetAlignmentOrientation(self, isEnd, isHoriz):
-        if isEnd:
-            if isHoriz and self._alignmentEnd & LINE_ALIGNMENT_HORIZ != LINE_ALIGNMENT_HORIZ:
-                self._alignmentEnd != LINE_ALIGNMENT_HORIZ
-            elif not isHoriz and self._alignmentEnd & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ:
-                self._alignmentEnd -= LINE_ALIGNMENT_HORIZ
-        else:
-            if isHoriz and self._alignmentStart & LINE_ALIGNMENT_HORIZ != LINE_ALIGNMENT_HORIZ:
-                self._alignmentStart != LINE_ALIGNMENT_HORIZ
-            elif not isHoriz and self._alignmentStart & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ:
-                self._alignmentStart -= LINE_ALIGNMENT_HORIZ
-            
-    def SetAlignmentType(self, isEnd, alignType):
-        if isEnd:
-            if alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                if self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE != LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                    self._alignmentEnd |= LINE_ALIGNMENT_TO_NEXT_HANDLE
-            elif self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                self._alignmentEnd -= LINE_ALIGNMENT_TO_NEXT_HANDLE
-        else:
-            if alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                if self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE != LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                    self._alignmentStart |= LINE_ALIGNMENT_TO_NEXT_HANDLE
-            elif self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE == LINE_ALIGNMENT_TO_NEXT_HANDLE:
-                self._alignmentStart -= LINE_ALIGNMENT_TO_NEXT_HANDLE
-            
-    def GetAlignmentOrientation(self, isEnd):
-        if isEnd:
-            return self._alignmentEnd & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ
-        else:
-            return self._alignmentStart & LINE_ALIGNMENT_HORIZ == LINE_ALIGNMENT_HORIZ
-
-    def GetAlignmentType(self, isEnd):
-        if isEnd:
-            return self._alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE
-        else:
-            return self._alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE
-
-    def GetNextControlPoint(self, shape):
-        """Find the next control point in the line after the start / end point,
-        depending on whether the shape is at the start or end.
-        """
-        n = len(self._lineControlPoints)
-        if self._to == shape:
-            # Must be END of line, so we want (n - 1)th control point.
-            # But indexing ends at n-1, so subtract 2.
-            nn = n-2
-        else:
-            nn = 1
-        if nn<len(self._lineControlPoints):
-            return self._lineControlPoints[nn]
-        return None
-
-    def OnCreateLabelShape(self, parent, region, w, h):
-        return LabelShape(parent, region, w, h)
-
-    
-    def OnLabelMovePre(self, dc, labelShape, x, y, old_x, old_y, display):
-        labelShape._shapeRegion.SetSize(labelShape.GetWidth(), labelShape.GetHeight())
-
-        # Find position in line's region list
-        i = 0
-        for region in self.GetRegions():
-            if labelShape._shapeRegion == region:
-                self.GetRegions().remove(region)
-            else:
-                i += 1
-                
-        xx, yy = self.GetLabelPosition(i)
-        # Set the region's offset, relative to the default position for
-        # each region.
-        labelShape._shapeRegion.SetPosition(x-xx, y-yy)
-        labelShape.SetX(x)
-        labelShape.SetY(y)
-
-        # Need to reformat to fit region
-        if labelShape._shapeRegion.GetText():
-            s = labelShape._shapeRegion.GetText()
-            labelShape.FormatText(dc, s, i)
-            self.DrawRegion(dc, labelShape._shapeRegion, xx, yy)
-        return True
-    
diff --git a/wxPython/wx/lib/ogl/oglmisc.py b/wxPython/wx/lib/ogl/oglmisc.py
deleted file mode 100644 (file)
index 478f2ee..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-#----------------------------------------------------------------------------
-# Name:         oglmisc.py
-# Purpose:      Miscellaneous OGL support functions
-#
-# Author:       Pierre Hjälm (from C++ original by Julian Smart)
-#
-# Created:      2004-05-08
-# RCS-ID:       $Id$
-# Copyright:    (c) 2004 Pierre Hjälm - 1998 Julian Smart
-# Licence:      wxWindows license
-#----------------------------------------------------------------------------
-
-from __future__ import division
-from math import *
-
-import wx
-
-# Control point types
-# Rectangle and most other shapes
-CONTROL_POINT_VERTICAL = 1
-CONTROL_POINT_HORIZONTAL = 2
-CONTROL_POINT_DIAGONAL = 3
-
-# Line
-CONTROL_POINT_ENDPOINT_TO = 4
-CONTROL_POINT_ENDPOINT_FROM = 5
-CONTROL_POINT_LINE = 6
-
-# Types of formatting: can be combined in a bit list
-FORMAT_NONE = 0             # Left justification
-FORMAT_CENTRE_HORIZ = 1     # Centre horizontally
-FORMAT_CENTRE_VERT = 2      # Centre vertically
-FORMAT_SIZE_TO_CONTENTS = 4 # Resize shape to contents
-
-# Attachment modes
-ATTACHMENT_MODE_NONE, ATTACHMENT_MODE_EDGE, ATTACHMENT_MODE_BRANCHING = 0, 1, 2
-
-# Shadow mode
-SHADOW_NONE, SHADOW_LEFT, SHADOW_RIGHT = 0, 1, 2
-
-OP_CLICK_LEFT, OP_CLICK_RIGHT, OP_DRAG_LEFT, OP_DRAG_RIGHT = 1, 2, 4, 8
-OP_ALL = OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_LEFT | OP_DRAG_RIGHT
-
-# Sub-modes for branching attachment mode
-BRANCHING_ATTACHMENT_NORMAL = 1
-BRANCHING_ATTACHMENT_BLOB = 2
-
-# logical function to use when drawing rubberband boxes, etc.
-OGLRBLF = wx.INVERT
-
-CONTROL_POINT_SIZE = 6
-
-# Types of arrowhead
-# (i) Built-in
-ARROW_HOLLOW_CIRCLE   = 1
-ARROW_FILLED_CIRCLE   = 2
-ARROW_ARROW           = 3
-ARROW_SINGLE_OBLIQUE  = 4
-ARROW_DOUBLE_OBLIQUE  = 5
-# (ii) Custom
-ARROW_METAFILE        = 20
-
-# Position of arrow on line
-ARROW_POSITION_START  = 0
-ARROW_POSITION_END    = 1
-ARROW_POSITION_MIDDLE = 2
-
-# Line alignment flags
-# Vertical by default
-LINE_ALIGNMENT_HORIZ              = 1
-LINE_ALIGNMENT_VERT               = 0
-LINE_ALIGNMENT_TO_NEXT_HANDLE     = 2
-LINE_ALIGNMENT_NONE               = 0
-
-
-
-# Format a string to a list of strings that fit in the given box.
-# Interpret %n and 10 or 13 as a new line.
-def FormatText(dc, text, width, height, formatMode):
-    i = 0
-    word=""
-    word_list = []
-    end_word = False
-    new_line = False
-    while i<len(text):
-        if text[i]=="%":
-            i += 1
-            if i == len(text):
-                word+="%"
-            else:
-                if text[i]=="n":
-                    new_line = True
-                    end_word = True
-                    i += 1
-                else:
-                    word+="%"+text[i]
-                    i += 1
-        elif text[i] in ["\012","\015"]:
-            new_line = True
-            end_word = True
-            i += 1
-        elif text[i]==" ":
-            end_word = True
-            i += 1
-        else:
-            word += text[i]
-            i += 1
-
-        if i == len(text):
-            end_word = True
-
-        if end_word:
-            word_list.append(word)
-            word=""
-            end_word = False
-        if new_line:
-            word_list.append(None)
-            new_line = False
-
-    # Now, make a list of strings which can fit in the box
-    string_list = []
-    buffer=""
-    for s in word_list:
-        oldBuffer = buffer
-        if s is None:
-            # FORCE NEW LINE
-            if len(buffer)>0:
-                string_list.append(buffer)
-            buffer=""
-        else:
-            if len(buffer):
-                buffer+=" "
-            buffer += s
-            x, y = dc.GetTextExtent(buffer)
-
-            # Don't fit within the bounding box if we're fitting
-            # shape to contents
-            if (x>width) and not (formatMode & FORMAT_SIZE_TO_CONTENTS):
-                # Deal with first word being wider than box
-                if len(oldBuffer):
-                    string_list.append(oldBuffer)
-                buffer = s
-    if len(buffer):
-        string_list.append(buffer)
-
-    return string_list
-
-
-
-def GetCentredTextExtent(dc, text_list, xpos = 0, ypos = 0, width = 0, height = 0):
-    if not text_list:
-        return 0, 0
-
-    max_width = 0
-    for line in text_list:
-        current_width, char_height = dc.GetTextExtent(line)
-        if current_width>max_width:
-            max_width = current_width
-
-    return max_width, len(text_list) * char_height
-
-
-
-def CentreText(dc, text_list, xpos, ypos, width, height, formatMode):
-    if not text_list:
-        return
-
-    # First, get maximum dimensions of box enclosing text
-    char_height = 0
-    max_width = 0
-    current_width = 0
-
-    # Store text extents for speed
-    widths = []
-    for line in text_list:
-        current_width, char_height = dc.GetTextExtent(line.GetText())
-        widths.append(current_width)
-        if current_width>max_width:
-            max_width = current_width
-
-    max_height = len(text_list) * char_height
-
-    if formatMode & FORMAT_CENTRE_VERT:
-        if max_height<height:
-            yoffset = ypos - height / 2 + (height - max_height) / 2
-        else:
-            yoffset = ypos - height / 2
-        yOffset = ypos
-    else:
-        yoffset = 0.0
-        yOffset = 0.0
-
-    if formatMode & FORMAT_CENTRE_HORIZ:
-        xoffset = xpos - width / 2
-        xOffset = xpos
-    else:
-        xoffset = 0.0
-        xOffset = 0.0
-
-    for i, line in enumerate(text_list):
-        if formatMode & FORMAT_CENTRE_HORIZ and widths[i]<width:
-            x = (width - widths[i]) / 2 + xoffset
-        else:
-            x = xoffset
-        y = i * char_height + yoffset
-
-        line.SetX(x - xOffset)
-        line.SetY(y - yOffset)
-        
-
-
-def DrawFormattedText(dc, text_list, xpos, ypos, width, height, formatMode):
-    if formatMode & FORMAT_CENTRE_HORIZ:
-        xoffset = xpos
-    else:
-        xoffset = xpos - width / 2
-
-    if formatMode & FORMAT_CENTRE_VERT:
-        yoffset = ypos
-    else:
-        yoffset = ypos - height / 2
-
-    # +1 to allow for rounding errors
-    dc.SetClippingRegion(xpos - width / 2, ypos - height / 2, width + 1, height + 1)
-
-    for line in text_list:
-        dc.DrawText(line.GetText(), xoffset + line.GetX(), yoffset + line.GetY())
-
-    dc.DestroyClippingRegion()
-
-
-
-def RoughlyEqual(val1, val2, tol = 0.00001):
-    return val1<(val2 + tol) and val1>(val2 - tol) and \
-           val2<(val1 + tol) and val2>(val1 - tol)
-
-
-
-def FindEndForBox(width, height, x1, y1, x2, y2):
-    xvec = [x1 - width / 2, x1 - width / 2, x1 + width / 2, x1 + width / 2, x1 - width / 2]
-    yvec = [y1 - height / 2, y1 + height / 2, y1 + height / 2, y1 - height / 2, y1 - height / 2]
-
-    return FindEndForPolyline(xvec, yvec, x2, y2, x1, y1)
-
-
-
-def CheckLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4):
-    denominator_term = (y4 - y3) * (x2 - x1) - (y2 - y1) * (x4 - x3)
-    numerator_term = (x3 - x1) * (y4 - y3) + (x4 - x3) * (y1 - y3)
-
-    length_ratio = 1.0
-    k_line = 1.0
-
-    # Check for parallel lines
-    if denominator_term<0.005 and denominator_term>-0.005:
-        line_constant=-1.0
-    else:
-        line_constant = float(numerator_term) / denominator_term
-
-    # Check for intersection
-    if line_constant<1.0 and line_constant>0.0:
-        # Now must check that other line hits
-        if (y4 - y3)<0.005 and (y4 - y3)>-0.005:
-            k_line = (x1 - x3 + line_constant * (x2 - x1)) / (x4 - x3)
-        else:
-            k_line = (y1 - y3 + line_constant * (y2 - y1)) / (y4 - y3)
-        if k_line >= 0 and k_line<1:
-            length_ratio = line_constant
-        else:
-            k_line = 1
-
-    return length_ratio, k_line
-
-
-
-def FindEndForPolyline(xvec, yvec, x1, y1, x2, y2):
-    lastx = xvec[0]
-    lasty = yvec[0]
-
-    min_ratio = 1.0
-
-    for i in range(1, len(xvec)):
-        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
-        lastx = xvec[i]
-        lasty = yvec[i]
-
-        if line_ratio<min_ratio:
-            min_ratio = line_ratio
-
-    # Do last (implicit) line if last and first doubles are not identical
-    if not (xvec[0] == lastx and yvec[0] == lasty):
-        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
-        if line_ratio<min_ratio:
-            min_ratio = line_ratio
-
-    return x1 + (x2 - x1) * min_ratio, y1 + (y2 - y1) * min_ratio
-
-
-
-def PolylineHitTest(xvec, yvec, x1, y1, x2, y2):
-    isAHit = False
-    lastx = xvec[0]
-    lasty = yvec[0]
-
-    min_ratio = 1.0
-
-    for i in range(1, len(xvec)):
-        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i])
-        if line_ratio != 1.0:
-            isAHit = True
-        lastx = xvec[i]
-        lasty = yvec[i]
-
-        if line_ratio<min_ratio:
-            min_ratio = line_ratio
-
-    # Do last (implicit) line if last and first doubles are not identical
-    if not (xvec[0] == lastx and yvec[0] == lasty):
-        line_ratio, other_ratio = CheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0])
-        if line_ratio != 1.0:
-            isAHit = True
-
-    return isAHit
-
-
-
-def GraphicsStraightenLine(point1, point2):
-    dx = point2[0] - point1[0]
-    dy = point2[1] - point1[1]
-
-    if dx == 0:
-        return
-    elif abs(dy / dx)>1:
-        point2[0] = point1[0]
-    else:
-        point2[1] = point1[0]
-
-
-
-def GetPointOnLine(x1, y1, x2, y2, length):
-    l = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
-    if l<0.01:
-        l = 0.01
-
-    i_bar = (x2 - x1) / l
-    j_bar = (y2 - y1) / l
-
-    return -length * i_bar + x2,-length * j_bar + y2
-
-
-
-def GetArrowPoints(x1, y1, x2, y2, length, width):
-    l = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
-
-    if l<0.01:
-        l = 0.01
-
-    i_bar = (x2 - x1) / l
-    j_bar = (y2 - y1) / l
-    
-    x3=-length * i_bar + x2
-    y3=-length * j_bar + y2
-
-    return x2, y2, width*-j_bar + x3, width * i_bar + y3,-width*-j_bar + x3,-width * i_bar + y3
-
-
-
-def DrawArcToEllipse(x1, y1, width1, height1, x2, y2, x3, y3):
-    a1 = width1 / 2
-    b1 = height1 / 2
-
-    # Check that x2 != x3
-    if abs(x2 - x3)<0.05:
-        x4 = x2
-        if y3>y2:
-            y4 = y1 - sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
-        else:
-            y4 = y1 + sqrt((b1 * b1 - (((x2 - x1) * (x2 - x1)) * (b1 * b1) / (a1 * a1))))
-        return x4, y4
-
-    # Calculate the x and y coordinates of the point where arc intersects ellipse
-    A = (1 / (a1 * a1))
-    B = ((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1)
-    C = (2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1)
-    D = ((y2 - y1) * (y2 - y1)) / (b1 * b1)
-    E = (A + B)
-    F = (C - (2 * A * x1) - (2 * B * x2))
-    G = ((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1)
-    H = ((y3 - y2) / (x2 - x2))
-    K = ((F * F) - (4 * E * G))
-
-    if K >= 0:
-        # In this case the line intersects the ellipse, so calculate intersection
-        if x2 >= x1:
-            ellipse1_x = ((F*-1) + sqrt(K)) / (2 * E)
-            ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
-        else:
-            ellipse1_x = (((F*-1) - sqrt(K)) / (2 * E))
-            ellipse1_y = ((H * (ellipse1_x - x2)) + y2)
-    else:
-        # in this case, arc does not intersect ellipse, so just draw arc
-        ellipse1_x = x3
-        ellipse1_y = y3
-
-    return ellipse1_x, ellipse1_y
-
-
-
-def FindEndForCircle(radius, x1, y1, x2, y2):
-    H = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
-
-    if H == 0:
-        return x1, y1
-    else:
-        return radius * (x2 - x1) / H + x1, radius * (y2 - y1) / H + y1