]> git.saurik.com Git - wxWidgets.git/commitdiff
Moved OGL to new locations.
authorJulian Smart <julian@anthemion.co.uk>
Mon, 28 Feb 2000 11:33:39 +0000 (11:33 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Mon, 28 Feb 2000 11:33:39 +0000 (11:33 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@6327 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

161 files changed:
distrib/msw/ogl.rsp
docs/latex/ogl/back.gif [new file with mode: 0644]
docs/latex/ogl/books.bmp [new file with mode: 0644]
docs/latex/ogl/bugs.tex [new file with mode: 0644]
docs/latex/ogl/bullet.bmp [new file with mode: 0644]
docs/latex/ogl/changes.tex [new file with mode: 0644]
docs/latex/ogl/classes.tex [new file with mode: 0644]
docs/latex/ogl/contents.gif [new file with mode: 0644]
docs/latex/ogl/forward.gif [new file with mode: 0644]
docs/latex/ogl/intro.tex [new file with mode: 0644]
docs/latex/ogl/ogl.hpj [new file with mode: 0644]
docs/latex/ogl/ogl.tex [new file with mode: 0644]
docs/latex/ogl/ogledit.bmp [new file with mode: 0644]
docs/latex/ogl/ogledit.gif [new file with mode: 0644]
docs/latex/ogl/sample.tex [new file with mode: 0644]
docs/latex/ogl/tex2rtf.ini [new file with mode: 0644]
docs/latex/ogl/texhelp.sty [new file with mode: 0644]
docs/latex/ogl/topics.tex [new file with mode: 0644]
docs/latex/ogl/up.gif [new file with mode: 0644]
include/wx/ogl/basic.h [new file with mode: 0644]
include/wx/ogl/basicp.h [new file with mode: 0644]
include/wx/ogl/bmpshape.h [new file with mode: 0644]
include/wx/ogl/canvas.h [new file with mode: 0644]
include/wx/ogl/composit.h [new file with mode: 0644]
include/wx/ogl/constrnt.h [new file with mode: 0644]
include/wx/ogl/divided.h [new file with mode: 0644]
include/wx/ogl/drawn.h [new file with mode: 0644]
include/wx/ogl/drawnp.h [new file with mode: 0644]
include/wx/ogl/lines.h [new file with mode: 0644]
include/wx/ogl/linesp.h [new file with mode: 0644]
include/wx/ogl/mfutils.h [new file with mode: 0644]
include/wx/ogl/misc.h [new file with mode: 0644]
include/wx/ogl/ogl.h [new file with mode: 0644]
include/wx/ogl/ogldiag.h [new file with mode: 0644]
samples/ogl/ogledit/Makefile [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/arrow.bmp [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/arrow.xpm [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool1.bmp [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool1.xpm [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool2.bmp [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool2.xpm [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool3.bmp [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool3.xpm [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool4.bmp [new file with mode: 0644]
samples/ogl/ogledit/bitmaps/tool4.xpm [new file with mode: 0644]
samples/ogl/ogledit/doc.cpp [new file with mode: 0644]
samples/ogl/ogledit/doc.h [new file with mode: 0644]
samples/ogl/ogledit/makefile.b32 [new file with mode: 0644]
samples/ogl/ogledit/makefile.bcc [new file with mode: 0644]
samples/ogl/ogledit/makefile.dos [new file with mode: 0644]
samples/ogl/ogledit/makefile.g95 [new file with mode: 0644]
samples/ogl/ogledit/makefile.sc [new file with mode: 0644]
samples/ogl/ogledit/makefile.unx [new file with mode: 0644]
samples/ogl/ogledit/makefile.vc [new file with mode: 0644]
samples/ogl/ogledit/makefile.vms [new file with mode: 0644]
samples/ogl/ogledit/makefile.wat [new file with mode: 0644]
samples/ogl/ogledit/ogl.ico [new file with mode: 0644]
samples/ogl/ogledit/ogl.xpm [new file with mode: 0644]
samples/ogl/ogledit/ogledit.cpp [new file with mode: 0644]
samples/ogl/ogledit/ogledit.def [new file with mode: 0644]
samples/ogl/ogledit/ogledit.h [new file with mode: 0644]
samples/ogl/ogledit/ogledit.rc [new file with mode: 0644]
samples/ogl/ogledit/palette.cpp [new file with mode: 0644]
samples/ogl/ogledit/palette.h [new file with mode: 0644]
samples/ogl/ogledit/view.cpp [new file with mode: 0644]
samples/ogl/ogledit/view.h [new file with mode: 0644]
samples/ogl/studio/Makefile [new file with mode: 0644]
samples/ogl/studio/cspalette.cpp [new file with mode: 0644]
samples/ogl/studio/cspalette.h [new file with mode: 0644]
samples/ogl/studio/csprint.cpp [new file with mode: 0644]
samples/ogl/studio/dialogs.cpp [new file with mode: 0644]
samples/ogl/studio/dialogs.h [new file with mode: 0644]
samples/ogl/studio/doc.cpp [new file with mode: 0644]
samples/ogl/studio/doc.h [new file with mode: 0644]
samples/ogl/studio/mainfrm.cpp [new file with mode: 0644]
samples/ogl/studio/mainfrm.h [new file with mode: 0644]
samples/ogl/studio/makefile.b32 [new file with mode: 0644]
samples/ogl/studio/makefile.bcc [new file with mode: 0644]
samples/ogl/studio/makefile.g95 [new file with mode: 0644]
samples/ogl/studio/makefile.unx [new file with mode: 0644]
samples/ogl/studio/makefile.vc [new file with mode: 0644]
samples/ogl/studio/manual/alignb.bmp [new file with mode: 0644]
samples/ogl/studio/manual/alignl.bmp [new file with mode: 0644]
samples/ogl/studio/manual/alignr.bmp [new file with mode: 0644]
samples/ogl/studio/manual/alignt.bmp [new file with mode: 0644]
samples/ogl/studio/manual/arrow.bmp [new file with mode: 0644]
samples/ogl/studio/manual/back.gif [new file with mode: 0644]
samples/ogl/studio/manual/bitmap1.bmp [new file with mode: 0644]
samples/ogl/studio/manual/bitmap2.bmp [new file with mode: 0644]
samples/ogl/studio/manual/bullet.bmp [new file with mode: 0644]
samples/ogl/studio/manual/contents.gif [new file with mode: 0644]
samples/ogl/studio/manual/copy.bmp [new file with mode: 0644]
samples/ogl/studio/manual/copysize.bmp [new file with mode: 0644]
samples/ogl/studio/manual/cut.bmp [new file with mode: 0644]
samples/ogl/studio/manual/cutpoint.bmp [new file with mode: 0644]
samples/ogl/studio/manual/forward.gif [new file with mode: 0644]
samples/ogl/studio/manual/help.bmp [new file with mode: 0644]
samples/ogl/studio/manual/helpcs.bmp [new file with mode: 0644]
samples/ogl/studio/manual/horiz.bmp [new file with mode: 0644]
samples/ogl/studio/manual/linearrow.bmp [new file with mode: 0644]
samples/ogl/studio/manual/new.bmp [new file with mode: 0644]
samples/ogl/studio/manual/newpoint.bmp [new file with mode: 0644]
samples/ogl/studio/manual/open.bmp [new file with mode: 0644]
samples/ogl/studio/manual/paste.bmp [new file with mode: 0644]
samples/ogl/studio/manual/pointsize.bmp [new file with mode: 0644]
samples/ogl/studio/manual/preview.bmp [new file with mode: 0644]
samples/ogl/studio/manual/print.bmp [new file with mode: 0644]
samples/ogl/studio/manual/redo.bmp [new file with mode: 0644]
samples/ogl/studio/manual/save.bmp [new file with mode: 0644]
samples/ogl/studio/manual/straight.bmp [new file with mode: 0644]
samples/ogl/studio/manual/studio.hpj [new file with mode: 0644]
samples/ogl/studio/manual/studio.tex [new file with mode: 0644]
samples/ogl/studio/manual/tex2rtf.ini [new file with mode: 0644]
samples/ogl/studio/manual/texttool.bmp [new file with mode: 0644]
samples/ogl/studio/manual/tick.bmp [new file with mode: 0644]
samples/ogl/studio/manual/toback.bmp [new file with mode: 0644]
samples/ogl/studio/manual/tofront.bmp [new file with mode: 0644]
samples/ogl/studio/manual/tool1.bmp [new file with mode: 0644]
samples/ogl/studio/manual/tool2.bmp [new file with mode: 0644]
samples/ogl/studio/manual/tool3.bmp [new file with mode: 0644]
samples/ogl/studio/manual/tool4.bmp [new file with mode: 0644]
samples/ogl/studio/manual/undo.bmp [new file with mode: 0644]
samples/ogl/studio/manual/up.gif [new file with mode: 0644]
samples/ogl/studio/manual/vert.bmp [new file with mode: 0644]
samples/ogl/studio/manual/zoom.bmp [new file with mode: 0644]
samples/ogl/studio/project.cpp [new file with mode: 0644]
samples/ogl/studio/project.h [new file with mode: 0644]
samples/ogl/studio/shapes.cpp [new file with mode: 0644]
samples/ogl/studio/shapes.h [new file with mode: 0644]
samples/ogl/studio/studio.cpp [new file with mode: 0644]
samples/ogl/studio/studio.h [new file with mode: 0644]
samples/ogl/studio/studio.ico [new file with mode: 0644]
samples/ogl/studio/studio.rc [new file with mode: 0644]
samples/ogl/studio/studio.xpm [new file with mode: 0644]
samples/ogl/studio/studio_resources.h [new file with mode: 0644]
samples/ogl/studio/studio_resources.wxr [new file with mode: 0644]
samples/ogl/studio/symbols.cpp [new file with mode: 0644]
samples/ogl/studio/symbols.h [new file with mode: 0644]
samples/ogl/studio/view.cpp [new file with mode: 0644]
samples/ogl/studio/view.h [new file with mode: 0644]
src/ogl/Makefile.in [new file with mode: 0644]
src/ogl/basic.cpp [new file with mode: 0644]
src/ogl/basic2.cpp [new file with mode: 0644]
src/ogl/bmpshape.cpp [new file with mode: 0644]
src/ogl/canvas.cpp [new file with mode: 0644]
src/ogl/composit.cpp [new file with mode: 0644]
src/ogl/constrnt.cpp [new file with mode: 0644]
src/ogl/divided.cpp [new file with mode: 0644]
src/ogl/drawn.cpp [new file with mode: 0644]
src/ogl/lines.cpp [new file with mode: 0644]
src/ogl/makefile.b32 [new file with mode: 0644]
src/ogl/makefile.bcc [new file with mode: 0644]
src/ogl/makefile.dos [new file with mode: 0644]
src/ogl/makefile.g95 [new file with mode: 0644]
src/ogl/makefile.unx [new file with mode: 0644]
src/ogl/makefile.vc [new file with mode: 0644]
src/ogl/makefile.wat [new file with mode: 0644]
src/ogl/mfutils.cpp [new file with mode: 0644]
src/ogl/misc.cpp [new file with mode: 0644]
src/ogl/ogldiag.cpp [new file with mode: 0644]
utils/projgen/makeproj.cpp

index de8ca90dfd068c56909797ea52fadeed77b7d93f..b6ef4608c9a348f6cd9c7bc1daa06b838872d06e 100644 (file)
@@ -1,68 +1,68 @@
-utils/ogl/Makefile.in
+src/ogl/*.cpp
+src/ogl/*.h
+src/ogl/*.rc
+src/ogl/*.def
+src/ogl/*.xbm
+src/ogl/*.xpm
+src/ogl/make*.*
+src/ogl/*.txt
+src/ogl/*.ico
+src/ogl/*.bmp
 
-utils/ogl/src/*.cpp
-utils/ogl/src/*.h
-utils/ogl/src/*.rc
-utils/ogl/src/*.def
-utils/ogl/src/*.xbm
-utils/ogl/src/*.xpm
-utils/ogl/src/make*.*
-utils/ogl/src/*.txt
-utils/ogl/src/*.ico
-utils/ogl/src/*.bmp
+include/wx/ogl/*.h
 
-utils/ogl/samples/ogledit/*.cpp
-utils/ogl/samples/ogledit/*.h
-utils/ogl/samples/ogledit/*.rc
-utils/ogl/samples/ogledit/*.def
-utils/ogl/samples/ogledit/*.xbm
-utils/ogl/samples/ogledit/make*.*
-utils/ogl/samples/ogledit/Makefile
-utils/ogl/samples/ogledit/*.txt
-utils/ogl/samples/ogledit/*.ico
-utils/ogl/samples/ogledit/*.bmp
-utils/ogl/samples/ogledit/*.xpm
-utils/ogl/samples/ogledit/bitmaps/*.bmp
-utils/ogl/samples/ogledit/bitmaps/*.gif
-utils/ogl/samples/ogledit/bitmaps/*.xbm
-utils/ogl/samples/ogledit/bitmaps/*.xpm
+samples/ogl/ogledit/*.cpp
+samples/ogl/ogledit/*.h
+samples/ogl/ogledit/*.rc
+samples/ogl/ogledit/*.def
+samples/ogl/ogledit/*.xbm
+samples/ogl/ogledit/make*.*
+samples/ogl/ogledit/Makefile
+samples/ogl/ogledit/*.txt
+samples/ogl/ogledit/*.ico
+samples/ogl/ogledit/*.bmp
+samples/ogl/ogledit/*.xpm
+samples/ogl/ogledit/bitmaps/*.bmp
+samples/ogl/ogledit/bitmaps/*.gif
+samples/ogl/ogledit/bitmaps/*.xbm
+samples/ogl/ogledit/bitmaps/*.xpm
 
-utils/ogl/samples/studio/*.cpp
-utils/ogl/samples/studio/*.h
-utils/ogl/samples/studio/*.rc
-utils/ogl/samples/studio/*.def
-utils/ogl/samples/studio/*.xbm
-utils/ogl/samples/studio/make*.*
-utils/ogl/samples/studio/Makefile
-utils/ogl/samples/studio/*.txt
-utils/ogl/samples/studio/*.ico
-utils/ogl/samples/studio/*.bmp
-utils/ogl/samples/studio/*.xpm
-utils/ogl/samples/studio/*.wxr
-utils/ogl/samples/studio/bitmaps/*.bmp
-utils/ogl/samples/studio/bitmaps/*.gif
-utils/ogl/samples/studio/bitmaps/*.xbm
-utils/ogl/samples/studio/bitmaps/*.xpm
-utils/ogl/samples/studio/manual/*.tex
-utils/ogl/samples/studio/manual/*.ini
-utils/ogl/samples/studio/manual/*.gif
-utils/ogl/samples/studio/manual/*.bmp
-utils/ogl/samples/studio/manual/*.htm
-utils/ogl/samples/studio/manual/*.hlp
-utils/ogl/samples/studio/manual/*.cnt
-utils/ogl/samples/studio/manual/Makefile
+samples/ogl/studio/*.cpp
+samples/ogl/studio/*.h
+samples/ogl/studio/*.rc
+samples/ogl/studio/*.def
+samples/ogl/studio/*.xbm
+samples/ogl/studio/make*.*
+samples/ogl/studio/Makefile
+samples/ogl/studio/*.txt
+samples/ogl/studio/*.ico
+samples/ogl/studio/*.bmp
+samples/ogl/studio/*.xpm
+samples/ogl/studio/*.wxr
+samples/ogl/studio/bitmaps/*.bmp
+samples/ogl/studio/bitmaps/*.gif
+samples/ogl/studio/bitmaps/*.xbm
+samples/ogl/studio/bitmaps/*.xpm
+samples/ogl/studio/manual/*.tex
+samples/ogl/studio/manual/*.ini
+samples/ogl/studio/manual/*.gif
+samples/ogl/studio/manual/*.bmp
+samples/ogl/studio/manual/*.htm
+samples/ogl/studio/manual/*.hlp
+samples/ogl/studio/manual/*.cnt
+samples/ogl/studio/manual/Makefile
 
-utils/ogl/distrib/*.rsp
-utils/ogl/distrib/*.bat
+distrib/msw/ogl.rsp
+distrib/msw/zipogl.bat
 
-utils/ogl/docs/*.txt
-utils/ogl/docs/*.tex
-utils/ogl/docs/*.ini
-utils/ogl/docs/*.hpj
-utils/ogl/docs/*.ps
-utils/ogl/docs/*.eps
-utils/ogl/docs/*.bmp
-utils/ogl/docs/*.gif
+docs/latex/ogl/*.txt
+docs/latex/ogl/*.tex
+docs/latex/ogl/*.ini
+docs/latex/ogl/*.hpj
+docs/latex/ogl/*.ps
+docs/latex/ogl/*.eps
+docs/latex/ogl/*.bmp
+docs/latex/ogl/*.gif
 
 docs/html/ogl/*.*
 docs/winhelp/ogl.hlp
diff --git a/docs/latex/ogl/back.gif b/docs/latex/ogl/back.gif
new file mode 100644 (file)
index 0000000..8a61076
Binary files /dev/null and b/docs/latex/ogl/back.gif differ
diff --git a/docs/latex/ogl/books.bmp b/docs/latex/ogl/books.bmp
new file mode 100644 (file)
index 0000000..cf1e148
Binary files /dev/null and b/docs/latex/ogl/books.bmp differ
diff --git a/docs/latex/ogl/bugs.tex b/docs/latex/ogl/bugs.tex
new file mode 100644 (file)
index 0000000..d66f64e
--- /dev/null
@@ -0,0 +1,11 @@
+\chapter{Bugs}\label{bugs}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+These are the known bugs.
+
+\begin{itemize}\itemsep=0pt
+\item In the OGLEdit sample, .dia files are output double-spaced
+due to an unidentified bug in the way a stream is converted to a file.
+\end{itemize}
+
diff --git a/docs/latex/ogl/bullet.bmp b/docs/latex/ogl/bullet.bmp
new file mode 100644 (file)
index 0000000..aad8fc7
Binary files /dev/null and b/docs/latex/ogl/bullet.bmp differ
diff --git a/docs/latex/ogl/changes.tex b/docs/latex/ogl/changes.tex
new file mode 100644 (file)
index 0000000..5590ce3
--- /dev/null
@@ -0,0 +1,19 @@
+\chapter{Change log}
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+Version 3.0, September 8th 1998
+
+\begin{itemize}\itemsep=0pt
+\item Version for wxWindows 2.0.
+\item Various enhancements especially to wxDrawnShape
+(multiple metafiles, for different orientations).
+\item More ability to override functions e.g. OnSizeDragLeft, so events can be
+intercepted for Do/Undo.
+\end{itemize}
+
+Version 2.0, June 1st 1996
+
+\begin{itemize}\itemsep=0pt
+\item First publicly released version.
+\end{itemize}
diff --git a/docs/latex/ogl/classes.tex b/docs/latex/ogl/classes.tex
new file mode 100644 (file)
index 0000000..a72adb3
--- /dev/null
@@ -0,0 +1,2861 @@
+\chapter{Class reference}\label{classref}
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+These are the main \ogl\ classes.
+
+\section{\class{wxOGLConstraint}}\label{wxoglconstraint}
+
+\overview{wxCompositeShape overview}{compositeshapeoverview}
+
+An wxOGLConstraint object helps specify how child shapes are laid out with respect
+to siblings and parents.
+
+\wxheading{Derived from}
+
+wxObject
+
+\wxheading{See also}
+
+\helpref{wxCompositeShape}{wxcompositeshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxOGLConstraint::wxOGLConstraint}\label{wxoglconstraintconstr}
+
+\func{}{wxOGLConstraint}{\void}
+
+Default constructor.
+
+\func{}{wxOGLConstraint}{\param{int}{ type}, \param{wxShape *}{constraining}, \param{wxList\& }{constrained}}
+
+Constructor.
+
+\wxheading{Parameters}
+
+\docparam{constraining}{The shape which is used as the reference for positioning the {\it constrained} objects.}
+
+\docparam{constrained}{Contains a list of wxShapes which are to be constrained (with respect
+to {\it constraining}) using {\it type}.}
+
+\docparam{type}{Can be one of:
+
+\begin{itemize}\itemsep=0pt
+\item {\bf gyCONSTRAINT\_CENTRED\_VERTICALLY}: the Y co-ordinates of the centres of the
+bounding boxes of the constrained objects and the constraining object
+will be the same
+\item {\bf gyCONSTRAINT\_CENTRED\_HORIZONTALLY}: the X co-ordinates of the centres of the
+bounding boxes of the constrained objects and the constraining object
+will be the same
+\item {\bf gyCONSTRAINT\_CENTRED\_BOTH}: the co-ordinates of the centres of the bounding boxes
+of the constrained objects and the constraining object will be the same
+\item {\bf gyCONSTRAINT\_LEFT\_OF}: the X co-ordinates of the right hand vertical edges
+of the bounding boxes of the constrained objects will be less than
+the X co-ordinate of the left hand vertical edge of the bounding box
+of the constraining object
+\item {\bf gyCONSTRAINT\_RIGHT\_OF}: the X co-ordinates of the left hand vertical edges
+of the bounding boxes of the constrained objects will be greater than
+the X co-ordinate of the right hand vertical edge of the bounding box
+of the constraining object
+\item {\bf gyCONSTRAINT\_ABOVE}: the Y co-ordinates of the bottom horizontal edges of the
+bounding boxes of the constrained objects will be less than the
+Y co-ordinate of the top horizontal edge of the bounding box of the
+constraining object
+\item {\bf gyCONSTRAINT\_BELOW}: the Y co-ordinates of the top horizontal edges of the
+bounding boxes of the constrained objects will be greater than
+the X co-ordinate of the bottom horizontal edge of the bounding box
+of the constraining object
+\item {\bf gyCONSTRAINT\_ALIGNED\_TOP}: the Y co-ordinates of the top horizontal edges of the
+bounding boxes of the constrained objects will be the same as the
+Y co-ordinate of the top horizontal edge of the bounding box of the
+constraining object
+\item {\bf gyCONSTRAINT\_ALIGNED\_BOTTOM}: the Y co-ordinates of the bottom horizontal edges
+of the bounding boxes of the constrained objects will be the same as
+the Y co-ordinate of the bottom horizontal edge of the bounding box
+of the constraining object
+\item {\bf gyCONSTRAINT\_ALIGNED\_LEFT}: the X co-ordinates of the left hand vertical edges
+of the bounding boxes of the constrained objects will be the same as
+the X co-ordinate of the left hand vertical edge of the bounding box
+of the constraining object
+\item {\bf gyCONSTRAINT\_ALIGNED\_RIGHT}: the X co-ordinates of the right hand vertical edges
+of the bounding boxes of the constrained objects will be the same as
+the X co-ordinate of the right hand vertical edge of the bounding box
+of the constraining object
+\item {\bf gyCONSTRAINT\_MIDALIGNED\_TOP}: the Y co-ordinates of the centres of
+the bounding boxes of the constrained objects will be the same
+as the Y co-ordinate of the top horizontal edge of
+the bounding box of the constraining object
+\item {\bf gyCONSTRAINT\_MIDALIGNED\_BOTTOM}: the Y co-ordinates of the centres of
+the bounding boxes of the constrained objects will be the same
+as the Y co-ordinate of the bottom horizontal edge of
+the bounding box of the constraining object
+\item {\bf gyCONSTRAINT\_MIDALIGNED\_LEFT}: the X co-ordinates of the centres of
+the bounding boxes of the constrained objects will be the same
+as the X co-ordinate of the left hand vertical edge of
+the bounding box of the constraining object
+\item {\bf gyCONSTRAINT\_MIDALIGNED\_RIGHT}: the X co-ordinates of the centres of
+the bounding boxes of the constrained objects will be the same as
+the X co-ordinate of the right hand vertical edge of
+the bounding box of the constraining object
+\end{itemize}
+}
+
+\membersection{wxOGLConstraint::\destruct{wxOGLConstraint}}
+
+\func{}{\destruct{wxOGLConstraint}}{\void}
+
+Destructor.
+
+\membersection{wxOGLConstraint::Equals}
+
+\func{bool}{Equals}{\param{double}{ x}, \param{double}{ y}}
+
+Returns TRUE if {\it x} and {\it y} are approximately equal (for the purposes
+of evaluating the constraint).
+
+\membersection{wxOGLConstraint::Evaluate}
+
+\func{bool}{Evaluate}{\void}
+
+Evaluates this constraint, returning TRUE if anything changed.
+
+\membersection{wxOGLConstraint::SetSpacing}\label{wxoglconstraintsetspacing}
+
+\func{void}{SetSpacing}{\param{double}{ x}, \param{double}{ y}}
+
+Sets the horizontal and vertical spacing for the constraint.
+
+\section{\class{wxBitmapShape}}\label{wxbitmapshape}
+
+Draws a bitmap (non-resizable).
+
+\wxheading{Derived from}
+
+\helpref{wxRectangleShape}{wxrectangleshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxBitmapShape::wxBitmapShape}
+
+\func{}{wxBitmapShape}{\void}
+
+Constructor.
+
+\membersection{wxBitmapShape::\destruct{wxBitmapShape}}
+
+\func{}{\destruct{wxBitmapShape}}{\void}
+
+Destructor.
+
+\membersection{wxBitmapShape::GetBitmap}
+
+\constfunc{wxBitmap\&}{GetBitmap}{\void}
+
+Returns a reference to the bitmap associated with this shape.
+
+\membersection{wxBitmapShape::GetFilename}
+
+\constfunc{wxString}{GetFilename}{\void}
+
+Returns the bitmap filename.
+
+\membersection{wxBitmapShape::SetBitmap}
+
+\func{void}{SetBitmap}{\param{const wxBitmap\&}{ bitmap}}
+
+Sets 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.
+
+\membersection{wxBitmapShape::SetFilename}
+
+\func{void}{SetFilename}{\param{const wxString\& }{filename}}
+
+Sets the bitmap filename.
+
+\section{\class{wxDiagram}}\label{wxdiagram}
+
+Encapsulates an entire diagram, with methods for reading/writing and drawing.
+A diagram has an associated wxShapeCanvas.
+
+\wxheading{Derived from}
+
+wxObject
+
+\wxheading{See also}
+
+\helpref{wxShapeCanvas}{wxshapecanvas}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxDiagram::wxDiagram}
+
+\func{}{wxDiagram}{\void}
+
+Constructor.
+
+\membersection{wxDiagram::\destruct{wxDiagram}}
+
+\func{}{\destruct{wxDiagram}}{\void}
+
+Destructor.
+
+\membersection{wxDiagram::AddShape}
+
+\func{void}{AddShape}{\param{wxShape*}{shape}, \param{wxShape *}{addAfter = NULL}}
+
+Adds a shape to the diagram. If {\it addAfter} is non-NULL, the shape will be added after this
+one.
+
+\membersection{wxDiagram::Clear}
+
+\func{void}{Clear}{\param{wxDC\&}{ dc}}
+
+Clears the specified device context.
+
+\membersection{wxDiagram::DeleteAllShapes}
+
+\func{void}{DeletesAllShapes}{\void}
+
+Removes and deletes all shapes in the diagram.
+
+\membersection{wxDiagram::DrawOutline}
+
+\func{void}{DrawOutline}{\param{wxDC\&}{ dc}, \param{double}{ x1}, \param{double}{ y1}, \param{double}{ x2}, \param{double}{ y2}}
+
+Draws an outline rectangle on the current device context.
+
+\membersection{wxDiagram::FindShape}\label{wxdiagramfindshape}
+
+\constfunc{wxShape*}{FindShape}{\param{long}{ id}}
+
+Returns the shape for the given identifier.
+
+\membersection{wxDiagram::GetCanvas}
+
+\constfunc{wxShapeCanvas*}{GetCanvas}{\void}
+
+Returns the shape canvas associated with this diagram.
+
+\membersection{wxDiagram::GetCount}\label{wxdiagramgetcount}
+
+\constfunc{int}{GetCount}{\void}
+
+Returns the number of shapes in the diagram.
+
+\membersection{wxDiagram::GetGridSpacing}
+
+\constfunc{double}{GetGridSpacing}{\void}
+
+Returns the grid spacing.
+
+\membersection{wxDiagram::GetMouseTolerance}
+
+\func{int}{GetMouseTolerance}{\void}
+
+Returns the tolerance within which a mouse move is ignored.
+
+\membersection{wxDiagram::GetShapeList}
+
+\constfunc{wxList*}{GetShapeList}{\void}
+
+Returns a pointer to the internal shape list.
+
+\membersection{wxDiagram::GetQuickEditMode}
+
+\constfunc{bool}{GetQuickEditMode}{\void}
+
+Returns quick edit mode.
+
+\membersection{wxDiagram::GetSnapToGrid}
+
+\constfunc{bool}{GetSnapToGrid}{\void}
+
+Returns snap-to-grid mode.
+
+\membersection{wxDiagram::InsertShape}
+
+\func{void}{InsertShape}{\param{wxShape *}{shape}}
+
+Inserts a shape at the front of the shape list.
+
+\membersection{wxDiagram::LoadFile}
+
+\func{bool}{LoadFile}{\param{const wxString\& }{filename}}
+
+Loads the diagram from a file.
+
+\membersection{wxDiagram::OnDatabaseLoad}
+
+\func{void}{OnDatabaseLoad}{\param{wxExprDatabase\&}{ database}}
+
+Called just after the nodes and lines have been read from the wxExprDatabase. You may override this;
+the default member does nothing.
+
+\membersection{wxDiagram::OnDatabaseSave}
+
+\func{void}{OnDatabaseSave}{\param{wxExprDatabase\&}{ database}}
+
+Called just after the nodes and lines have been written to the wxExprDatabase. You may override this;
+the default member does nothing.
+
+\membersection{wxDiagram::OnHeaderLoad}
+
+\func{bool}{OnHeaderLoad}{\param{wxExprDatabase\&}{ database}, \param{wxExpr\&}{ expr}}
+
+Called to allow the `diagram' header object to be read. The default member reads no further information.
+You may wish to override this to read version information, author name, etc.
+
+\membersection{wxDiagram::OnHeaderSave}
+
+\func{bool}{OnHeaderSave}{\param{wxExprDatabase\&}{ database}, \param{wxExpr\&}{ expr}}
+
+Called to allow instantiation of the `diagram' header object. The default member writes no further information.
+You may wish to override this to include version information, author name, etc.
+
+\membersection{wxDiagram::OnShapeLoad}
+
+\func{bool}{OnShapeLoad}{\param{wxExprDatabase\&}{ database}, \param{wxShape\&}{ shape}, \param{wxExpr\&}{ expr}}
+
+Called to read the shape from the {\it expr}. You may override this, but call this function first.
+The default member calls ReadAttributes for the shape.
+
+\membersection{wxDiagram::OnShapeSave}
+
+\func{bool}{OnShapeSave}{\param{wxExprDatabase\&}{ database}, \param{wxShape\&}{ shape}, \param{wxExpr\&}{ expr}}
+
+Called to save the shape to the {\it expr} and {\it database}. You may override this, but call this function first.
+The default member calls WriteAttributes for the shape, appends the shape to the database, and of the shape
+is a composite, recursively calls OnShapeSave for its children.
+
+\membersection{wxDiagram::ReadContainerGeometry}
+
+\func{void}{ReadContainerGeometry}{\param{wxExprDatabase\&}{ database}}
+
+Reads container geometry from a wxExprDatabase, linking up nodes which
+are part of a composite. You probably won't need to redefine this.
+
+\membersection{wxDiagram::ReadLines}
+
+\func{void}{ReadLines}{\param{wxExprDatabase\&}{ database}}
+
+Reads lines from a wxExprDatabase. You probably won't need to redefine this.
+
+\membersection{wxDiagram::ReadNodes}
+
+\func{void}{ReadNodes}{\param{wxExprDatabase\&}{ database}}
+
+Reads nodes from a wxExprDatabase. You probably won't need to redefine this.
+
+\membersection{wxDiagram::RecentreAll}
+
+\func{void}{RecentreAll}{\param{wxDC\&}{ dc}}
+
+Make sure all text that should be centred, is centred.
+
+\membersection{wxDiagram::Redraw}
+
+\func{void}{Redraw}{\param{wxDC\&}{ dc}}
+
+Draws the shapes in the diagram on the specified device context.
+
+\membersection{wxDiagram::RemoveAllShapes}
+
+\func{void}{RemoveAllShapes}{\void}
+
+Removes all shapes from the diagram but does not delete the shapes.
+
+\membersection{wxDiagram::RemoveShape}
+
+\func{void}{RemoveShape}{\param{wxShape*}{ shape}}
+
+Removes the shape from the diagram (non-recursively) but does not delete it.
+
+\membersection{wxDiagram::SaveFile}
+
+\func{bool}{SaveFile}{\param{const wxString\& }{filename}}
+
+Saves the diagram in a file.
+
+\membersection{wxDiagram::SetCanvas}\label{wxdiagramsetcanvas}
+
+\func{void}{SetCanvas}{\param{wxShapeCanvas*}{ canvas}}
+
+Sets the canvas associated with this diagram.
+
+\membersection{wxDiagram::SetGridSpacing}
+
+\func{void}{SetGridSpacing}{\param{double}{ spacing}}
+
+Sets the grid spacing. The default is 5.
+
+\membersection{wxDiagram::SetMouseTolerance}
+
+\func{void}{SetMouseTolerance}{\param{int}{ tolerance}}
+
+Sets the tolerance within which a mouse move is ignored. The default is 3 pixels.
+
+\membersection{wxDiagram::SetQuickEditMode}
+
+\func{void}{SetQuickEditMode}{\param{bool}{ mode}}
+
+Sets quick-edit-mode on or off. In this mode, refreshes are minimized, but the
+diagram may need manual refreshing occasionally.
+
+\membersection{wxDiagram::SetSnapToGrid}
+
+\func{void}{SetSnapToGrid}{\param{bool}{ snap}}
+
+Sets snap-to-grid mode on or off. The default is on.
+
+\membersection{wxDiagram::ShowAll}
+
+\func{void}{ShowAll}{\param{bool}{ show}}
+
+Calls Show for each shape in the diagram.
+
+\membersection{wxDiagram::Snap}
+
+\func{void}{Snap}{\param{double *}{x}, \param{double *}{y}}
+
+`Snaps' the coordinate to the nearest grid position, if snap-to-grid is on.
+
+\section{\class{wxDrawnShape}}\label{wxdrawnshape}
+
+Draws a pseduo-metafile shape, which can be loaded from a simple Windows metafile.
+
+wxDrawnShape allows you to specify a different shape for each of four orientations (North, West,
+South and East). It also provides a set of drawing functions for programmatic drawing of a shape,
+so that during construction of the shape you can draw into it as if it were a device context.
+
+\wxheading{Derived from}
+
+\helpref{wxRectangleShape}{wxrectangleshape}
+
+See also \helpref{wxRectangleShape}{wxrectangleshape}.
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxDrawnShape::wxDrawnShape}
+
+\func{}{wxDrawnShape}{\void}
+
+Constructor.
+
+\membersection{wxDrawnShape::\destruct{wxDrawnShape}}
+
+\func{}{\destruct{wxDrawnShape}}{\void}
+
+Destructor.
+
+\membersection{wxDrawnShape::CalculateSize}
+
+\func{void}{CalculateSize}{\void}
+
+Calculates the wxDrawnShape size from the current metafile. Call this after you have drawn
+into the shape.
+
+\membersection{wxDrawnShape::DestroyClippingRect}\label{wxdrawnshapedestroyclippingrect}
+
+\func{void}{DestroyClippingRect}{\void}
+
+Destroys the clipping rectangle. See also \helpref{wxDrawnShape::SetClippingRect}{wxdrawnshapesetclippingrect}.
+
+\membersection{wxDrawnShape::DrawArc}\label{wxdrawnshapedrawarc}
+
+\func{void}{DrawArc}{\param{const wxPoint\&}{ centrePoint}, \param{const wxPoint\&}{ startPoint},
+ \param{const wxPoint\&}{ endPoint}}
+
+Draws an arc (see wxWindows documentation for details).
+
+\membersection{wxDrawnShape::DrawAtAngle}\label{wxdrawnshapedrawatangle}
+
+\func{void}{DrawAtAngle}{\param{int}{ angle}}
+
+Sets the metafile for the given orientation, which can be one of:
+
+\begin{itemize}\itemsep=0pt
+\item oglDRAWN\_ANGLE\_0
+\item oglDRAWN\_ANGLE\_90
+\item oglDRAWN\_ANGLE\_180
+\item oglDRAWN\_ANGLE\_270
+\end{itemize}
+
+See also \helpref{wxDrawnShape::GetAngle}{wxdrawnshapegetangle}.
+
+\membersection{wxDrawnShape::DrawEllipticArc}\label{wxdrawnshapedrawellipticarc}
+
+\func{void}{DrawEllipticArc}{\param{const wxRect\&}{ rect}, \param{double}{ startAngle}, \param{double}{ endAngle}}
+
+Draws an elliptic arc (see wxWindows documentation for details).
+
+\membersection{wxDrawnShape::DrawLine}\label{wxdrawnshapedrawline}
+
+\func{void}{DrawLine}{\param{const wxPoint\&}{ point1}, \param{const wxPoint\&}{ point2}}
+
+Draws a line from {\it point1} to {\it point2}.
+
+\membersection{wxDrawnShape::DrawLines}\label{wxdrawnshapedrawlines}
+
+\func{void}{DrawLines}{\param{int}{ n}, \param{wxPoint\&}{ points[]}}
+
+Draws {\it n} lines.
+
+\membersection{wxDrawnShape::DrawPoint}\label{wxdrawnshapedrawpoint}
+
+\func{void}{DrawPoint}{\param{const wxPoint\&}{ point}}
+
+Draws a point.
+
+\membersection{wxDrawnShape::DrawPolygon}\label{wxdrawnshapedrawpolygon}
+
+\func{void}{DrawPolygon}{\param{int}{ n}, \param{wxPoint\&}{ points[]}, \param{int}{ flags = 0}}
+
+Draws a polygon. {\it flags} can be one or more of {\bf oglMETAFLAGS\_OUTLINE} (use this polygon for the drag outline)
+and {\bf oglMETAFLAGS\_ATTACHMENTS} (use the vertices of this polygon for attachments).
+
+\membersection{wxDrawnShape::DrawRectangle}\label{wxdrawnshapedrawrectangle}
+
+\func{void}{DrawRectangle}{\param{const wxRect\&}{ rect}}
+
+Draws a rectangle.
+
+\membersection{wxDrawnShape::DrawRoundedRectangle}\label{wxdrawnshapedrawroundedrectangle}
+
+\func{void}{DrawRoundedRectangle}{\param{const wxRect\&}{ rect}, \param{double}{ radius}}
+
+Draws a rounded rectangle. {\it radius} is the corner radius. If {\it radius} is negative,
+it expresses the radius as a proportion of the smallest dimension of the rectangle.
+
+\membersection{wxDrawnShape::DrawSpline}\label{wxdrawnshapedrawspline}
+
+\func{void}{DrawSpline}{\param{int}{ n}, \param{wxPoint\&}{ points[]}}
+
+Draws a spline curve.
+
+\membersection{wxDrawnShape::DrawText}\label{wxdrawnshapedrawtext}
+
+\func{void}{DrawText}{\param{const wxString\&}{ text}, \param{const wxPoint\&}{ point}}
+
+Draws text at the given point.
+
+\membersection{wxDrawnShape::GetAngle}\label{wxdrawnshapegetangle}
+
+\constfunc{int}{GetAngle}{\void}
+
+Returns the current orientation, which can be one of:
+
+\begin{itemize}\itemsep=0pt
+\item oglDRAWN\_ANGLE\_0
+\item oglDRAWN\_ANGLE\_90
+\item oglDRAWN\_ANGLE\_180
+\item oglDRAWN\_ANGLE\_270
+\end{itemize}
+
+See also \helpref{wxDrawnShape::DrawAtAngle}{wxdrawnshapedrawatangle}.
+
+\membersection{wxDrawnShape::GetMetaFile}
+
+\constfunc{wxPseudoMetaFile\& }{GetMetaFile}{\void}
+
+Returns a reference to the internal `pseudo-metafile'.
+
+\membersection{wxDrawnShape::GetRotation}\label{wxdrawnshapegetrotation}
+
+\constfunc{double}{GetRotation}{\void}
+
+Returns the current rotation of the shape in radians.
+
+\membersection{wxDrawnShape::LoadFromMetaFile}
+
+\func{bool}{LoadFromMetaFile}{\param{const wxString\& }{filename}}
+
+Loads a (very simple) Windows metafile, created for example by Top Draw, the Windows shareware graphics package.
+
+\membersection{wxDrawnShape::Rotate}
+
+\func{void}{Rotate}{\param{double }{x}, \param{double }{y}, \param{double }{theta}}
+
+Rotate about the given axis by the given amount in radians.
+
+\membersection{wxDrawnShape::SetClippingRect}\label{wxdrawnshapesetclippingrect}
+
+\func{void}{SetClippingRect}{\param{const wxRect\&}{ rect}}
+
+Sets the clipping rectangle. See also \helpref{wxDrawnShape::DestroyClippingRect}{wxdrawnshapedestroyclippingrect}.
+
+\membersection{wxDrawnShape::SetDrawnBackgroundColour}\label{wxdrawnshapesetdrawnbackgroundcolour}
+
+\func{void}{SetDrawnBackgroundColour}{\param{const wxColour\&}{ colour}}
+
+Sets the current background colour for the current metafile.
+
+\membersection{wxDrawnShape::SetDrawnBackgroundMode}\label{wxdrawnshapesetdrawnbackgroundmode}
+
+\func{void}{SetDrawnBackgroundMode}{\param{int}{ mode}}
+
+Sets the current background mode for the current metafile.
+
+\membersection{wxDrawnShape::SetDrawnBrush}\label{wxdrawnshapesetdrawnbrush}
+
+\func{void}{SetDrawnBrush}{\param{wxPen*}{ pen}, \param{bool}{ isOutline = FALSE}}
+
+Sets the pen for this metafile. If {\it isOutline} is TRUE, this pen is taken to indicate the outline
+(and if the outline pen is changed for the whole shape, the pen will be replaced with the outline pen).
+
+\membersection{wxDrawnShape::SetDrawnFont}\label{wxdrawnshapesetdrawnfont}
+
+\func{void}{SetDrawnFont}{\param{wxFont*}{ font}}
+
+Sets the current font for the current metafile.
+
+\membersection{wxDrawnShape::SetDrawnPen}\label{wxdrawnshapesetdrawnpen}
+
+\func{void}{SetDrawnPen}{\param{wxPen*}{ pen}, \param{bool}{ isOutline = FALSE}}
+
+Sets the pen for this metafile. If {\it isOutline} is TRUE, this pen is taken to indicate the outline
+(and if the outline pen is changed for the whole shape, the pen will be replaced with the outline pen).
+
+\membersection{wxDrawnShape::SetDrawnTextColour}\label{wxdrawnshapesetdrawntextcolour}
+
+\func{void}{SetDrawnTextColour}{\param{const wxColour\&}{ colour}}
+
+Sets the current text colour for the current metafile.
+
+\membersection{wxDrawnShape::Scale}
+
+\func{void}{Scale}{\param{double }{sx}, \param{double }{sy}}
+
+Scales the shape by the given amount.
+
+\membersection{wxDrawnShape::SetSaveToFile}
+
+\func{void}{SetSaveToFile}{\param{bool }{save}}
+
+If {\it save} is TRUE, the image will be saved along with the shape's other attributes. The reason
+why this might not be desirable is that if there are many shapes with the same image, it would be
+more efficient for the application to save one copy, and not duplicate the information for every
+shape. The default is TRUE.
+
+\membersection{wxDrawnShape::Translate}
+
+\func{void}{Translate}{\param{double }{x}, \param{double }{y}}
+
+Translates the shape by the given amount.
+
+\section{\class{wxCircleShape}}\label{wxcircleshape}
+
+An wxEllipseShape whose width and height are the same.
+
+\wxheading{Derived from}
+
+\helpref{wxEllipseShape}{wxellipseshape}.
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxCircleShape::wxCircleShape}
+
+\func{}{wxCircleShape}{\param{double}{ width = 0.0}}
+
+Constructor.
+
+\membersection{wxCircleShape::\destruct{wxCircleShape}}
+
+\func{}{\destruct{wxCircleShape}}{\void}
+
+Destructor.
+
+\section{\class{wxCompositeShape}}\label{wxcompositeshape}
+
+This is an object with a list of child objects, and a list of size
+and positioning constraints between the children.
+
+\wxheading{Derived from}
+
+\helpref{wxRectangleShape}{wxrectangleshape}
+
+\wxheading{See also}
+
+\helpref{wxCompositeShape overview}{compositeshapeoverview}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxCompositeShape::wxCompositeShape}
+
+\func{}{wxCompositeShape}{\void}
+
+Constructor.
+
+\membersection{wxCompositeShape::\destruct{wxCompositeShape}}
+
+\func{}{\destruct{wxCompositeShape}}{\void}
+
+Destructor.
+
+\membersection{wxCompositeShape::AddChild}\label{wxcompositeshapeaddchild}
+
+\func{void}{AddChild}{\param{wxShape *}{child}, \param{wxShape *}{addAfter = NULL}}
+
+Adds a child shape to the composite. If {\it addAfter} is non-NULL, the shape will be added
+after this shape.
+
+\membersection{wxCompositeShape::AddConstraint}\label{wxcompositeshapeaddconstraint}
+
+\func{wxOGLConstraint *}{AddConstraint}{\param{wxOGLConstraint *}{constraint}}
+
+\func{wxOGLConstraint *}{AddConstraint}{\param{int}{ type}, \param{wxShape *}{constraining}, \param{wxList\&}{constrained}}
+
+\func{wxOGLConstraint *}{AddConstraint}{\param{int}{ type}, \param{wxShape *}{constraining}, \param{wxShape *}{constrained}}
+
+Adds a constraint to the composite.
+
+\membersection{wxCompositeShape::CalculateSize}
+
+\func{void}{CalculateSize}{\void}
+
+Calculates the size and position of the composite based on child sizes and positions.
+
+\membersection{wxCompositeShape::ContainsDivision}
+
+\func{bool}{FindContainerImage}{\param{wxDivisionShape *}{division}}
+
+Returns TRUE if {\it division} is a descendant of this container.
+
+\membersection{wxCompositeShape::DeleteConstraint}
+
+\func{void}{DeleteConstraint}{\param{wxOGLConstraint *}{constraint}}
+
+Deletes constraint from composite.
+
+\membersection{wxCompositeShape::DeleteConstraintsInvolvingChild}
+
+\func{void}{DeleteConstraintsInvolvingChild}{\param{wxShape *}{child}}
+
+This function deletes constraints which mention the given child. Used when
+deleting a child from the composite.
+
+\membersection{wxCompositeShape::FindConstraint}
+
+\func{wxOGLConstraint *}{FindConstraint}{\param{long}{ id}, \param{wxCompositeShape **}{actualComposite}}
+
+Finds the constraint with the given id, also returning the actual composite the constraint was in,
+in case that composite was a descendant of this composite.
+
+\membersection{wxCompositeShape::FindContainerImage}
+
+\func{wxShape *}{FindContainerImage}{\void}
+
+Finds the image used to visualize a container. This is any child
+of the composite that is not in the divisions list.
+
+\membersection{wxCompositeShape::GetConstraints}
+
+\constfunc{wxList\&}{GetConstraints}{\void}
+
+Returns a reference to the list of constraints.
+
+\membersection{wxCompositeShape::GetDivisions}
+
+\constfunc{wxList\&}{GetDivisions}{\void}
+
+Returns a reference to the list of divisions.
+
+\membersection{wxCompositeShape::MakeContainer}\label{wxcompositeshapemakecontainer}
+
+\func{void}{MakeContainer}{\void}
+
+Makes this composite into a container by creating one child wxDivisionShape.
+
+\membersection{wxCompositeShape::OnCreateDivision}
+
+\func{wxDivisionShape *}{OnCreateDivision}{\void}
+
+Called when a new division shape is required. Can be overriden to allow an application
+to use a different class of division.
+
+\membersection{wxCompositeShape::Recompute}\label{wxcompositeshaperecompute}
+
+\func{bool}{Recompute}{\void} 
+
+Recomputes any constraints associated with the object. If FALSE is returned,
+the constraints could not be satisfied (there was an inconsistency).
+
+\membersection{wxCompositeShape::RemoveChild}
+
+\func{void}{RemoveChild}{\param{wxShape *}{child}}
+
+Removes the child from the composite and any constraint relationships, but does not
+delete the child.
+
+\section{\class{wxDividedShape}}\label{wxdividedshape}
+
+A wxDividedShape 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.
+
+\wxheading{Derived from}
+
+\helpref{wxRectangleShape}{wxrectangleshape}
+
+\wxheading{See also}
+
+\helpref{wxDividedShape overview}{dividedshapeoverview}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxDividedShape::wxDividedShape}
+
+\func{}{wxDividedShape}{\param{double}{ width = 0.0}, \param{double}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxDividedShape::\destruct{wxDividedShape}}
+
+\func{}{\destruct{wxDividedShape}}{\void}
+
+Destructor.
+
+\membersection{wxDividedShape::EditRegions}
+
+\func{void}{EditRegions}{\void}
+
+Edit the region colours and styles.
+
+\membersection{wxDividedShape::SetRegionSizes}
+
+\func{void}{SetRegionSizes}{\void}
+
+Set all region sizes according to proportions and
+this object total size.
+
+\section{\class{wxDivisionShape}}\label{wxdivisionshape}
+
+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.
+
+\wxheading{Derived from}
+
+\helpref{wxCompositeShape}{wxcompositeshape}
+
+\wxheading{See also}
+
+\helpref{wxCompositeShape overview}{compositeshapeoverview}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxDivisionShape::wxDivisionShape}
+
+\func{}{wxDivisionShape}{\void}
+
+Constructor.
+
+\membersection{wxDivisionShape::\destruct{wxDivisionShape}}
+
+\func{}{\destruct{wxDivisionShape}}{\void}
+
+Destructor.
+
+\membersection{wxDivisionShape::AdjustBottom}
+
+\func{void}{AdjustBottom}{\param{double}{ bottom}, \param{bool}{ test}}
+
+Adjust a side, returning FALSE if it's not physically possible to adjust it to this point.
+
+\membersection{wxDivisionShape::AdjustLeft}
+
+\func{void}{AdjustLeft}{\param{double}{ left}, \param{bool}{ test}}
+
+Adjust a side, returning FALSE if it's not physically possible to adjust it to this point.
+
+\membersection{wxDivisionShape::AdjustRight}
+
+\func{void}{AdjustRight}{\param{double}{ right}, \param{bool}{ test}}
+
+Adjust a side, returning FALSE if it's not physically possible to adjust it to this point.
+
+\membersection{wxDivisionShape::AdjustTop}
+
+\func{void}{AdjustTop}{\param{double}{ top}, \param{bool}{ test}}
+
+Adjust a side, returning FALSE if it's not physically possible to adjust it to this point.
+
+\membersection{wxDivisionShape::Divide}\label{wxdivisionshapedivide}
+
+\func{void}{Divide}{\param{int}{ direction}}
+
+Divide this division into two further divisions, horizontally ({\it direction} is wxHORIZONTAL) or
+vertically ({\it direction} is wxVERTICAL).
+
+\membersection{wxDivisionShape::EditEdge}
+
+\func{void}{EditEdge}{\param{int}{ side}}
+
+Interactively edit style of left or top side.
+
+\membersection{wxDivisionShape::GetBottomSide}
+
+\func{wxDivisionShape *}{GetBottomSide}{\void}
+
+Returns a pointer to the division on the bottom side of this division.
+
+\membersection{wxDivisionShape::GetHandleSide}
+
+\func{int}{GetHandleSide}{\void}
+
+Returns the side which the handle appears on (DIVISION\_SIDE\_LEFT or DIVISION\_SIDE\_TOP).
+
+\membersection{wxDivisionShape::GetLeftSide}
+
+\func{wxDivisionShape *}{GetLeftSide}{\void}
+
+Returns a pointer to the division on the left side of this division.
+
+\membersection{wxDivisionShape::GetLeftSideColour}
+
+\func{wxString}{GetLeftSideColour}{\void}
+
+Returns a pointer to the colour used for drawing the left side of the division.
+
+\membersection{wxDivisionShape::GetLeftSidePen}
+
+\func{wxPen *}{GetLeftSidePen}{\void}
+
+Returns a pointer to the pen used for drawing the left side of the division.
+
+\membersection{wxDivisionShape::GetRightSide}
+
+\func{wxDivisionShape *}{GetRightSide}{\void}
+
+Returns a pointer to the division on the right side of this division.
+
+\membersection{wxDivisionShape::GetTopSide}
+
+\func{wxDivisionShape *}{GetTopSide}{\void}
+
+Returns a pointer to the division on the top side of this division.
+
+\membersection{wxDivisionShape::GetTopSideColour}
+
+\func{wxString}{GetTopSideColour}{\void}
+
+Returns a pointer to the colour used for drawing the top side of the division.
+
+\membersection{wxDivisionShape::GetTopSidePen}
+
+\func{wxPen *}{GetTopSidePen}{\void}
+
+Returns a pointer to the pen used for drawing the left side of the division.
+
+\membersection{wxDivisionShape::ResizeAdjoining}
+
+\func{void}{ResizeAdjoining}{\param{int}{ side}, \param{double}{ newPos}, \param{bool}{ test}}
+
+Resize adjoining divisions at the given side. If {\it test} is TRUE,
+just see whether it's possible for each adjoining region,
+returning FALSE if it's not.
+
+{\it side} can be one of:
+
+\begin{itemize}\itemsep=0pt
+\item DIVISION\_SIDE\_NONE
+\item DIVISION\_SIDE\_LEFT
+\item DIVISION\_SIDE\_TOP
+\item DIVISION\_SIDE\_RIGHT
+\item DIVISION\_SIDE\_BOTTOM
+\end{itemize}
+
+\membersection{wxDivisionShape::PopupMenu}
+
+\func{void}{PopupMenu}{\param{double}{ x}, \param{double}{ y}}
+
+Popup the division menu.
+
+\membersection{wxDivisionShape::SetBottomSide}
+
+\func{void}{SetBottomSide}{\param{wxDivisionShape *}{shape}}
+
+Set the pointer to the division on the bottom side of this division.
+
+\membersection{wxDivisionShape::SetHandleSide}
+
+\func{int}{SetHandleSide}{\void}
+
+Sets the side which the handle appears on (DIVISION\_SIDE\_LEFT or DIVISION\_SIDE\_TOP).
+
+\membersection{wxDivisionShape::SetLeftSide}
+
+\func{void}{SetLeftSide}{\param{wxDivisionShape *}{shape}}
+
+Set the pointer to the division on the left side of this division.
+
+\membersection{wxDivisionShape::SetLeftSideColour}
+
+\func{void}{SetLeftSideColour}{\param{const wxString\& }{colour}}
+
+Sets the colour for drawing the left side of the division.
+
+\membersection{wxDivisionShape::SetLeftSidePen}
+
+\func{void}{SetLeftSidePen}{\param{wxPen *}{pen}}
+
+Sets the pen for drawing the left side of the division.
+
+\membersection{wxDivisionShape::SetRightSide}
+
+\func{void}{SetRightSide}{\param{wxDivisionShape *}{shape}}
+
+Set the pointer to the division on the right side of this division.
+
+\membersection{wxDivisionShape::SetTopSide}
+
+\func{void}{SetTopSide}{\param{wxDivisionShape *}{shape}}
+
+Set the pointer to the division on the top side of this division.
+
+\membersection{wxDivisionShape::SetTopSideColour}
+
+\func{void}{SetTopSideColour}{\param{const wxString\& }{colour}}
+
+Sets the colour for drawing the top side of the division.
+
+\membersection{wxDivisionShape::SetTopSidePen}
+
+\func{void}{SetTopSidePen}{\param{wxPen *}{pen}}
+
+Sets the pen for drawing the top side of the division.
+
+\section{\class{wxEllipseShape}}\label{wxellipseshape}
+
+The wxEllipseShape behaves similarly to the wxRectangleShape but is
+elliptical.
+
+\wxheading{Derived from}
+
+\helpref{wxShape}{wxshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxEllipseShape::wxEllipseShape}
+
+\func{}{wxEllipseShape}{\param{double}{ width = 0.0}, \param{double}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxEllipseShape::\destruct{wxEllipseShape}}
+
+\func{}{\destruct{wxEllipseShape}}{\void}
+
+Destructor.
+
+\section{\class{wxLineShape}}\label{wxlineshape}
+
+A wxLineShape 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.
+
+\wxheading{Derived from}
+
+\helpref{wxShape}{wxshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxLineShape::wxLineShape}
+
+\func{}{wxLineShape}{\void}
+
+Constructor.
+
+Usually you will call \helpref{wxLineShape::MakeLineControlPoints}{wxlineshapemakelinecontrolpoints} to
+specify the number of segments in the line.
+
+\membersection{wxLineShape::\destruct{wxLineShape}}
+
+\func{}{\destruct{wxLineShape}}{\void}
+
+Destructor.
+
+\membersection{wxLineShape::AddArrow}\label{wxlineshapeaddarrow}
+
+\func{void}{AddArrow}{\param{WXTYPE}{ type}, \param{bool}{ end = ARROW\_POSITION\_END}, \param{double}{ arrowSize = 10.0},
+ \param{double}{ xOffset = 0.0}, \param{const wxString\& }{name = ""}, \param{wxPseudoMetaFile *}{mf = NULL}, \param{long}{ arrowId = -1}}
+
+Adds an arrow (or annotation) to the line.
+
+{\it type} may currently be one of:
+
+\begin{description}\itemsep=0pt
+\item[ARROW\_HOLLOW\_CIRCLE] Hollow circle.
+\item[ARROW\_FILLED\_CIRCLE] Filled circle.
+\item[ARROW\_ARROW] Conventional arrowhead.
+\item[ARROW\_SINGLE\_OBLIQUE] Single oblique stroke.
+\item[ARROW\_DOUBLE\_OBLIQUE] Double oblique stroke.
+\item[ARROW\_DOUBLE\_METAFILE] Custom arrowhead.
+\end{description}
+
+{\it end} may currently be one of:
+
+\begin{description}\itemsep=0pt
+\item[ARROW\_POSITION\_END] Arrow appears at the end.
+\item[ARROW\_POSITION\_START] Arrow appears at the start.
+\end{description}
+
+{\it arrowSize} specifies the length of the arrow.
+
+{\it xOffset} specifies the offset from the end of the line.
+
+{\it name} specifies a name for the arrow.
+
+{\it mf} can be a wxPseduoMetaFile, perhaps loaded from a simple Windows metafile.
+
+{\it arrowId} is the id for the arrow.
+
+\membersection{wxLineShape::AddArrowOrdered}
+
+\func{void}{AddArrowOrdered}{\param{wxArrowHead *}{arrow}, \param{wxList\&}{ referenceList}, \param{int}{ 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.
+
+\begin{verbatim}
+  Reference list:      a b c d e
+  Current line list:   a d
+\end{verbatim}
+
+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.
+
+\membersection{wxLineShape::ClearArrow}
+
+\func{bool}{ClearArrow}{\param{const wxString\& }{name}}
+
+Delete the arrow with the given name.
+
+\membersection{wxLineShape::ClearArrowsAtPosition}
+
+\func{void}{ClearArrowsAtPosition}{\param{int}{ position = -1}}
+
+Delete the arrows at the specified position, or at any position if {\it position} is -1.
+
+\membersection{wxLineShape::DrawArrow}
+
+\func{void}{DrawArrow}{\param{ArrowHead *}{arrow}, \param{double}{ xOffset}, \param{bool}{ proportionalOffset}}
+
+Draws the given arrowhead (or annotation).
+
+\membersection{wxLineShape::DeleteArrowHead}
+
+\func{bool}{DeleteArrowHead}{\param{long}{ arrowId}}
+
+\func{bool}{DeleteArrowHead}{\param{int}{ position}, \param{const wxString\& }{name}}
+
+Delete arrowhead by id or position and name.
+
+\membersection{wxLineShape::DeleteLineControlPoint}
+
+\func{bool}{DeleteLineControlPoint}{\void}
+
+Deletes an arbitary point on the line.
+
+\membersection{wxLineShape::DrawArrows}
+
+\func{void}{DrawArrows}{\param{wxDC\&}{ dc}}
+
+Draws all arrows.
+
+\membersection{wxLineShape::DrawRegion}
+
+\func{void}{DrawRegion}{\param{wxDC\&}{ dc}, \param{wxShapeRegion *}{region}, \param{double}{ x}, \param{double}{ y}}
+
+Format one region at this position.
+
+\membersection{wxLineShape::EraseRegion}
+
+\func{void}{EraseRegion}{\param{wxDC\&}{ dc}, \param{wxShapeRegion *}{region}, \param{double}{ x}, \param{double}{ y}}
+
+Format one region at this position.
+
+\membersection{wxLineShape::FindArrowHead}
+
+\func{wxArrowHead *}{FindArrowHead}{\param{long}{ arrowId}}
+
+\func{wxArrowHead *}{FindArrowHead}{\param{int}{ position}, \param{const wxString\& }{name}}
+
+Find arrowhead by id or position and name.
+
+\membersection{wxLineShape::FindLineEndPoints}
+
+\func{void}{FindLineEndPoints}{\param{double *}{fromX}, \param{double *}{fromY}, \param{double *}{toX}, \param{double *}{toY}}
+
+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.
+
+\membersection{wxLineShape::FindLinePosition}
+
+\func{int}{FindLinePosition}{\param{double }{x}, \param{double }{y}}
+
+Find which position we're talking about at this x, y.
+Returns ARROW\_POSITION\_START, ARROW\_POSITION\_MIDDLE, ARROW\_POSITION\_END.
+
+\membersection{wxLineShape::FindMinimumWidth}
+
+\func{double}{FindMinimumWidth}{\void}
+
+Finds the horizontal width for drawing a line with arrows in minimum
+space. Assume arrows at end only.
+
+\membersection{wxLineShape::FindNth}
+
+\func{void}{FindNth}{\param{wxShape *}{image}, \param{int *}{nth}, \param{int *}{noArcs}, \param{bool}{ incoming}}
+
+Finds the position of the line on the given object. Specify whether incoming or outgoing lines are
+being considered with {\it incoming}.
+
+\membersection{wxLineShape::GetAttachmentFrom}
+
+\constfunc{int}{GetAttachmentFrom}{\void}
+
+Returns the attachment point on the `from' node.
+
+\membersection{wxLineShape::GetAttachmentTo}
+
+\constfunc{int}{GetAttachmentTo}{\void}
+
+Returns the attachment point on the `to' node.
+
+\membersection{wxLineShape::GetEnds}
+
+\func{void}{GetEnds}{\param{double *}{x1}, \param{double *}{y1}, \param{double *}{x2}, \param{double *}{y2}}
+
+Gets the visible endpoints of the lines for drawing between two objects.
+
+\membersection{wxLineShape::GetFrom}
+
+\constfunc{wxShape *}{GetFrom}{\void}
+
+Gets the `from' object.
+
+\membersection{wxLineShape::GetLabelPosition}
+
+\func{void}{GetLabelPosition}{\param{int}{ position}, \param{double *}{x}, \param{double *}{y}}
+
+Get the reference point for a label. Region x and y are offsets from this.
+position is 0 (middle), 1 (start), 2 (end).
+
+\membersection{wxLineShape::GetNextControlPoint}
+
+\func{wxPoint *}{GetNextControlPoint}{\param{wxShape *}{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.
+
+\membersection{wxLineShape::GetTo}
+
+\func{wxShape *}{GetTo}{\void}
+
+Gets the `to' object.
+
+\membersection{wxLineShape::Initialise}
+
+\func{void}{Initialise}{\void}
+
+Initialises the line object.
+
+\membersection{wxLineShape::InsertLineControlPoint}
+
+\func{void}{InsertLineControlPoint}{\void}
+
+Inserts a control point at an arbitrary position.
+
+\membersection{wxLineShape::IsEnd}
+
+\func{bool}{IsEnd}{\param{wxShape *}{shape}}
+
+Returns TRUE if {\it shape} is at the end of the line.
+
+\membersection{wxLineShape::IsSpline}
+
+\func{bool}{IsSpline}{\void}
+
+Returns TRUE if a spline is drawn through the control points, and FALSE otherwise.
+
+\membersection{wxLineShape::MakeLineControlPoints}\label{wxlineshapemakelinecontrolpoints}
+
+\func{void}{MakeLineControlPoints}{\param{int}{ n}}
+
+Make a given number of control points (minimum of two).
+
+\membersection{wxLineShape::OnMoveLink}
+
+\func{void}{OnMoveLink}{\param{wxDC\&}{ dc}, \param{bool}{ moveControlPoints = TRUE}}
+
+Called when a connected object has moved, to move the link to
+correct position.
+
+\membersection{wxLineShape::SetAttachmentFrom}
+
+\func{void}{SetAttachmentTo}{\param{int}{ fromAttach}}
+
+Sets the `from' shape attachment.
+
+\membersection{wxLineShape::SetAttachments}
+
+\func{void}{SetAttachments}{\param{int}{ fromAttach}, \param{int}{ toAttach}}
+
+Specifies which object attachment points should be used at each end of the line.
+
+\membersection{wxLineShape::SetAttachmentTo}
+
+\func{void}{SetAttachmentTo}{\param{int}{ toAttach}}
+
+Sets the `to' shape attachment.
+
+\membersection{wxLineShape::SetEnds}
+
+\func{void}{SetEnds}{\param{double}{ x1}, \param{double}{ y1}, \param{double}{ x2}, \param{double}{ y2}}
+
+Sets the end positions of the line.
+
+\membersection{wxLineShape::SetFrom}
+
+\func{void}{SetFrom}{\param{wxShape *}{object}}
+
+Sets the `from' object for the line.
+
+\membersection{wxLineShape::SetIgnoreOffsets}
+
+\func{void}{SetIgnoreOffsets}{\param{bool}{ ignore}}
+
+Tells the shape whether to ignore offsets from the end of the line when drawing.
+
+\membersection{wxLineShape::SetSpline}
+
+\func{void}{SetSpline}{\param{bool}{ spline}}
+
+Specifies whether a spline is to be drawn through the control points (TRUE), or a line (FALSE).
+
+\membersection{wxLineShape::SetTo}
+
+\func{void}{SetTo}{\param{wxShape *}{object}}
+
+Sets the `to' object for the line.
+
+\membersection{wxLineShape::Straighten}
+
+\func{void}{Straighten}{\param{wxDC*}{ dc = NULL}}
+
+Straighten verticals and horizontals. {\it dc} is optional.
+
+\membersection{wxLineShape::Unlink}
+
+\func{void}{Unlink}{\void}
+
+Unlinks the line from the nodes at either end.
+
+\section{\class{wxPolygonShape}}\label{wxpolygonshape}
+
+A wxPolygonShape'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.
+
+\wxheading{Derived from}
+
+\helpref{wxShape}{wxshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxPolygonShape::wxPolygonShape}
+
+\func{}{wxPolygonShape}{void}
+
+Constructor. Call \helpref{wxPolygonShape::Create}{wxpolygonshapecreate} to specify the polygon's vertices.
+
+\membersection{wxPolygonShape::\destruct{wxPolygonShape}}
+
+\func{}{\destruct{wxPolygonShape}}{\void}
+
+Destructor.
+
+\membersection{wxPolygonShape::Create}\label{wxpolygonshapecreate}
+
+\func{void}{Create}{\param{wxList*}{ points}}
+
+Takes a list of wxRealPoints; each point is an {\it offset} from the centre.
+The polygon's destructor will delete these points, so do not delete them yourself.
+
+\membersection{wxPolygonShape::AddPolygonPoint}
+
+\func{void}{AddPolygonPoint}{\param{int}{ pos = 0}}
+
+Add a control point after the given point.
+
+\membersection{wxPolygonShape::CalculatePolygonCentre}
+
+\func{void}{CalculatePolygonCentre}{\void}
+
+Recalculates the centre of the polygon.
+
+\membersection{wxPolygonShape::DeletePolygonPoint}
+
+\func{void}{DeletePolygonPoint}{\param{int}{ pos = 0}}
+
+Deletes a control point.
+
+\membersection{wxPolygonShape::GetPoints}
+
+\func{wxList *}{GetPoints}{\void}
+
+Returns a pointer to the internal list of polygon vertices (wxRealPoints).
+
+\membersection{wxPolygonShape::UpdateOriginalPoints}
+
+\func{void}{UpdateOriginalPoints}{\void}
+
+If we've changed the shape, must make the original
+points match the working points with this function.
+
+\section{\class{wxRectangleShape}}\label{wxrectangleshape}
+
+The wxRectangleShape has rounded or square corners.
+
+\wxheading{Derived from}
+
+\helpref{wxShape}{wxshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxRectangleShape::wxRectangleShape}
+
+\func{}{wxRectangleShape}{\param{double}{ width = 0.0}, \param{double}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxRectangleShape::\destruct{wxRectangleShape}}
+
+\func{}{\destruct{wxRectangleShape}}{\void}
+
+Destructor.
+
+\membersection{wxRectangleShape::SetCornerRadius}
+
+\func{void}{SetCornerRadius}{\param{double}{ radius}}
+
+Sets 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.
+
+\section{\class{wxPseudoMetaFile}}\label{wxpseudometafile}
+
+A simple metafile-like class which can load data from a Windows metafile on all platforms.
+
+\wxheading{Derived from}
+
+wxObject
+
+\section{\class{wxShape}}\label{wxshape}
+
+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.
+
+\wxheading{Derived from}
+
+\helpref{wxShapeEvtHandler}{wxshapeevthandler}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxShape::wxShape}
+
+\func{}{wxShape}{\param{wxShapeCanvas*}{ canvas = NULL}}
+
+Constructs a new wxShape.
+
+\membersection{wxShape::\destruct{wxShape}}
+
+\func{}{\destruct{wxShape}}{\void}
+
+Destructor.
+
+\membersection{wxShape::AddLine}
+
+\func{void}{AddLine}{\param{wxLineShape*}{ line}, \param{wxShape*}{ other}, \param{int}{ attachFrom = 0}, \param{int}{ attachTo = 0},
+ \param{int}{ positionFrom = -1}, \param{int}{ positionTo = -1}}
+
+Adds a line between the specified canvas shapes, 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.
+
+\membersection{wxShape::AddRegion}
+
+\func{void}{AddRegion}{\param{wxShapeRegion*}{ region}}
+
+Adds a region to the shape.
+
+\membersection{wxShape::AddText}
+
+\func{void}{AddText}{\param{const wxString\& }{string}}
+
+Adds a line of text to the shape's default text region.
+
+\membersection{wxShape::AddToCanvas}
+
+\func{void}{AddToCanvas}{\param{wxShapeCanvas*}{ theCanvas}, \param{wxShape*}{ addAfter=NULL}}
+
+Adds the shape to the canvas's shape list. If {\it addAfter} is
+non-NULL, will add the shape after this one.
+
+\membersection{wxShape::AncestorSelected}
+
+\constfunc{bool}{AncestorSelected}{\void}
+
+TRUE if the shape's ancestor is currently selected.
+
+\membersection{wxShape::ApplyAttachmentOrdering}\label{wxshapeapplyattachmentordering}
+
+\func{void}{ApplyAttachmentOrdering}{\param{wxList\&}{ linesToSort}}
+
+Applies the line ordering in {\it linesToSort} to the shape, to reorder the way lines are attached.
+
+\membersection{wxShape::AssignNewIds}
+
+\func{void}{AssignNewIds}{\void}
+
+Assigns new ids to this image and its children.
+
+\membersection{wxShape::Attach}\label{wxshapeattach}
+
+\func{void}{Attach}{\param{wxShapeCanvas*}{ can}}
+
+Sets the shape's internal canvas pointer to point to the given canvas.
+
+\membersection{wxShape::AttachmentIsValid}\label{wxshapeattachmentisvalid}
+
+\constfunc{bool}{AttachmentIsValid}{\param{int}{ attachment}}
+
+Returns TRUE if {\it attachment} is a valid attachment point.
+
+\membersection{wxShape::AttachmentSortTest}\label{wxshapeattachmentsorttest}
+
+\constfunc{bool}{AttachmentSortTest}{\param{int}{ attachment}, \param{const wxRealPoint\&}{ pt1},
+ \param{const wxRealPoint\&}{ pt2}}
+
+Returns TRUE if {\it pt1} is less than or equal to {\it pt2}, in the sense
+that one point comes before another on an edge of the shape. {\it attachment} is
+the attachment point (side) in question.
+
+This function is used in \helpref{wxShape::MoveLineToNewAttachment}{wxshapemovelinetonewattachment} to
+determine the new line ordering.
+
+\membersection{wxShape::CalcSimpleAttachment}\label{wxshapecalcsimpleattachment}
+
+\func{wxRealPoint}{CalcSimpleAttachment}{\param{const wxRealPoint\&}{ pt1},
+ \param{const wxRealPoint\&}{ pt2}, \param{int}{ nth}, \param{int}{ noArcs}, \param{wxLineShape*}{ line}}
+
+Assuming the attachment lies along a vertical or horizontal line,
+calculates the position on that point.
+
+\wxheading{Parameters}
+
+\docparam{pt1}{The first point of the line repesenting the edge of the shape.}
+
+\docparam{pt2}{The second point of the line representing the edge of the shape.}
+
+\docparam{nth}{The position on the edge (for example there may be 6 lines at this attachment point,
+and this may be the 2nd line.}
+
+\docparam{noArcs}{The number of lines at this edge.}
+
+\docparam{line}{The line shape.}
+
+\wxheading{Remarks}
+
+This function expects the line to be either vertical or horizontal, and determines which.
+
+\membersection{wxShape::CalculateSize}
+
+\func{void}{CalculateSize}{\void}
+
+Called to calculate the shape's size if dependent on children sizes.
+
+\membersection{wxShape::ClearAttachments}
+
+\func{void}{ClearAttachments}{\void}
+
+Clears internal custom attachment point shapes (of class wxAttachmentPoint).
+
+\membersection{wxShape::ClearRegions}
+
+\func{void}{ClearRegions}{\void}
+
+Clears the wxShapeRegions from the shape.
+
+\membersection{wxShape::ClearText}
+
+\func{void}{ClearText}{\param{int}{ regionId = 0}}
+
+Clears the text from the specified text region.
+
+\membersection{wxShape::Constrain}
+
+\func{bool}{Constrain}{\void}
+
+Calculates the shape's constraints (if any). Applicable
+only to wxCompositeShape, does nothing if the shape is of
+a different class.
+
+\membersection{wxShape::Copy}\label{wxshapecopy}
+
+\func{void}{Copy}{\param{wxShape\&}{ copy}}
+
+Copy this shape into {\it copy}. Every derived class must have one of these, and each Copy implementation
+must call the derived class's implementation to ensure everything is copied. See also \helpref{wxShape::CreateNewCopy}{wxshapecreatenewcopy}.
+
+\membersection{wxShape::CreateNewCopy}\label{wxshapecreatenewcopy}
+
+\func{wxShape* }{CreateNewCopy}{\param{bool}{ resetMapping = TRUE}, \param{bool}{ recompute = TRUE}}
+
+Creates and returns a new copy of this shape (calling \helpref{wxShape::Copy}{wxshapecopy}). Do not override this function.
+
+This function should always be used to create a new copy, since it must do special processing
+for copying constraints associated with constraints.
+
+If {\it resetMapping} is TRUE, a mapping table used for complex shapes is reset; this may not be desirable
+if the shape being copied is a child of a composite (and so the mapping table is in use).
+
+If {\it recompute} is TRUE, \helpref{wxShape::Recompute}{wxshaperecompute} is called for the new shape.
+
+\wxheading{Remarks}
+
+This function uses the wxWindows dynamic object creation system to create a new shape of the same
+type as `this', before calling Copy.
+
+If the event handler for this shape is not the same as the shape itself, the event handler is also copied
+using \helpref{wxShapeEvtHandler::CreateNewCopy}{wxshapeevthandlercreatenewcopy}.
+
+\membersection{wxShape::DeleteControlPoints}
+
+\func{void}{DeleteControlPoints}{\void}
+
+Deletes the control points (or handles) for the shape. Does not redraw
+the shape.
+
+\membersection{wxShape::Detach}
+
+\func{void}{Detach}{\void}
+
+Disassociates the shape from its canvas by setting the internal shape
+canvas pointer to NULL.
+
+\membersection{wxShape::Draggable}
+
+\func{bool}{Draggable}{\void} 
+
+TRUE if the shape may be dragged by the user.
+
+\membersection{wxShape::Draw}
+
+\func{void}{Draw}{\param{wxDC\&}{ dc}}
+
+Draws the whole shape and any lines attached to it.
+
+Do not override this function: override OnDraw, which is called
+by this function.
+
+\membersection{wxShape::DrawContents}
+
+\func{void}{DrawContents}{\param{wxDC\&}{ dc}}
+
+Draws the internal graphic of the shape (such as
+text).
+
+Do not override this function: override OnDrawContents, which is called
+by this function.
+
+\membersection{wxShape::DrawLinks}
+
+\func{void}{DrawLinks}{\param{wxDC\&}{ dc}, \param{int}{ attachment = -1}}
+
+Draws any lines linked to this shape.
+
+\membersection{wxShape::Erase}
+
+\func{void}{Erase}{\param{wxDC\&}{ dc}}
+
+Erases the shape, but does not repair damage caused to other
+shapes.
+
+\membersection{wxShape::EraseContents}
+
+\func{void}{EraseContents}{\param{wxDC\&}{ dc}}
+
+Erases the shape contents, that is, the area within the shape's
+minimum bounding box.
+
+\membersection{wxShape::EraseLinks}
+
+\func{void}{EraseLinks}{\param{wxDC\&}{ dc}, \param{int}{ attachment = -1}}
+
+Erases links attached to this shape, but does not repair
+damage caused to other shapes.
+
+\membersection{wxShape::FindRegion}
+
+\func{wxShape *}{FindRegion}{\param{const wxString\& }{regionName}, \param{int *}{regionId}}
+
+Finds the actual image (`this' if non-composite) and region id for the given
+region name.
+
+\membersection{wxShape::FindRegionNames}
+
+\func{void}{FindRegionNames}{\param{wxStringList\&}{ list}}
+
+Finds all region names for this image (composite or simple).
+Supply an empty string list.
+
+\membersection{wxShape::Flash}
+
+\func{void}{Flash}{\void}
+
+Flashes the shape.
+
+\membersection{wxShape::FormatText}
+
+\func{void}{FormatText}{\param{const wxString\& }{s}, \param{int}{ i = 0}}
+
+Reformats the given text region; defaults to formatting the default region.
+
+\membersection{wxShape::GetAttachmentMode}
+
+\constfunc{bool}{GetAttachmentMode}{\void}
+
+Returns the attachment mode, which is TRUE if attachments are used, FALSE otherwise (in which case
+lines will be drawn as if to the centre of the shape). See \helpref{wxShape::SetAttachmentMode}{wxshapesetattachmentmode}.
+
+\membersection{wxShape::GetAttachmentPosition}\label{wxshapegetattachmentposition}
+
+\func{bool}{GetAttachmentPosition}{\param{int}{ attachment}, \param{double*}{ x}, \param{double*}{ y},
+ \param{int}{ nth = 0}, \param{int}{ noArcs = 1}, \param{wxLineShape*}{ line = NULL}}
+
+Gets the position at which the given attachment point should be drawn.
+
+If {\it attachment} isn't found among the attachment points of the shape, returns FALSE.
+
+\membersection{wxShape::GetBoundingBoxMax}
+
+\func{void}{GetBoundingBoxMax}{\param{double *}{width}, \param{double *}{height}}
+
+Gets the maximum bounding box for the shape, taking into
+account external features such as shadows.
+
+\membersection{wxShape::GetBoundingBoxMin}
+
+\func{void}{GetBoundingBoxMin}{\param{double *}{width}, \param{double *}{height}}
+
+Gets the minimum bounding box for the shape, that defines
+the area available for drawing the contents (such as text).
+
+\membersection{wxShape::GetBrush}
+
+\constfunc{wxBrush*}{GetBrush}{\void}
+
+Returns the brush used for filling the shape.
+
+\membersection{wxShape::GetCanvas}
+
+\constfunc{wxShapeCanvas*}{GetCanvas}{\void}
+
+Gets the internal canvas pointer.
+
+\membersection{wxShape::GetCentreResize}
+
+\constfunc{bool}{GetCentreResize}{\void}
+
+Returns 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).
+
+\membersection{wxShape::GetChildren}
+
+\constfunc{wxList\&}{GetChildren}{\void}
+
+Returns a reference to the list of children for this shape.
+
+\membersection{wxShape::GetClientData}
+
+\func{wxObject*}{GetClientData}{\void}
+
+Gets the client data associated with the shape (NULL if there is
+none).
+
+\membersection{wxShape::GetDisableLabel}
+
+\constfunc{bool}{GetDisableLabel}{\void}
+
+Returns TRUE if the default region will not be shown, FALSE otherwise.
+
+\membersection{wxShape::GetEventHandler}
+
+\constfunc{wxShapeEvtHandler*}{GetEventHandler}{\void}
+
+Returns the event handler for this shape.
+
+\membersection{wxShape::GetFixedHeight}
+
+\constfunc{bool}{GetFixedHeight}{\void}
+
+Returns TRUE if the shape cannot be resized in the vertical plane.
+
+\membersection{wxShape::GetFixedSize}
+
+\func{void}{GetFixedSize}{\param{bool *}{ x}, \param{bool *}{ y}}
+
+Returns flags indicating whether the shape is of fixed size in either direction.
+
+\membersection{wxShape::GetFixedWidth}
+
+\constfunc{bool}{GetFixedWidth}{\void}
+
+Returns TRUE if the shape cannot be resized in the horizontal plane.
+
+\membersection{wxShape::GetFont}
+
+\constfunc{wxFont*}{GetFont}{\param{int}{ regionId = 0}}
+
+Gets the font for the specified text region.
+
+\membersection{wxShape::GetFunctor}
+
+\constfunc{wxString}{GetFunctor}{\void}
+
+Gets a string representing the type of the shape, to be used when
+writing out shape descriptions to a file. This is overridden by
+each derived shape class to provide an appropriate type string. By default,
+"node\_image" is used for non-line shapes, and "arc\_image" for lines.
+
+\membersection{wxShape::GetId}
+
+\constfunc{long}{GetId}{\void}
+
+Returns the integer identifier for this shape.
+
+\membersection{wxShape::GetLinePosition}\label{wxshapegetlineposition}
+
+\func{int}{GetLinePosition}{\param{wxLineShape*}{ line}}
+
+Gets the zero-based position of {\it line} in the list of lines for this shape.
+
+\membersection{wxShape::GetLines}
+
+\constfunc{wxList\&}{GetLines}{\void}
+
+Returns a reference to the list of lines connected to this shape.
+
+\membersection{wxShape::GetMaintainAspectRatio}\label{wxshapegetmaintainaspectratio}
+
+\constfunc{bool}{GetMaintainAspectRatio}{\void}
+
+If returns TRUE, resizing the shape will not change the aspect ratio
+(width and height will be in the original proportion).
+
+\membersection{wxShape::GetNumberOfAttachments}\label{wxshapegetnumberofattachments}
+
+\constfunc{int}{GetNumberOfAttachments}{\void}
+
+Gets the number of attachment points for this shape.
+
+\membersection{wxShape::GetNumberOfTextRegions}
+
+\constfunc{int}{GetNumberOfTextRegions}{\void}
+
+Gets the number of text regions for this shape.
+
+\membersection{wxShape::GetParent}
+
+\constfunc{wxShape *}{GetParent}{\void}
+
+Returns the parent of this shape, if it is part of a composite.
+
+\membersection{wxShape::GetPen}
+
+\constfunc{wxPen*}{GetPen}{\void}
+
+Returns the pen used for drawing the shape's outline.
+
+\membersection{wxShape::GetPerimeterPoint}
+
+\func{bool}{GetPerimeterPoint}{\param{double}{ x1}, \param{double}{ y1}, \param{double}{ x2}, \param{double}{ y2}, \param{double *}{x3}, \param{double *}{y3}}
+
+Gets the point at which the line from (x1, y1) to (x2, y2) hits the shape. Returns TRUE if the
+line hits the perimeter.
+
+\membersection{wxShape::GetRegionId}\label{getregionid}
+
+\func{int}{GetRegionId}{\param{const wxString\& }{name}}
+
+Gets the region's identifier by name. This is {\it not} unique for within an entire composite, but
+is unique for the image.
+
+\membersection{wxShape::GetRegionName}\label{getregionname}
+
+\func{wxString}{GetRegionName}{\param{int}{ regionId = 0}}
+
+Gets the region's name. A region's name can be used to uniquely determine a region within
+an entire composite image hierarchy. See also \helpref{wxShape::SetRegionName}{wxshapesetregionname}.
+
+\membersection{wxShape::GetRegions}\label{getregions}
+
+\func{wxList\&}{GetRegions}{\void}
+
+Returns the list of wxShapeRegions.
+
+\membersection{wxShape::GetRotation}
+
+\constfunc{double}{GetRotatation}{\void}
+
+Returns the angle of rotation in radians.
+
+\membersection{wxShape::GetSensitivityFilter}
+
+\constfunc{void}{GetSensitivityFilter}{\void}
+
+Returns the sensitivity filter, a bitlist of values. See \helpref{wxShape::SetSensitivityFilter}{wxshapesetsensitivityfilter}.
+
+\membersection{wxShape::GetShadowMode}
+
+\constfunc{int}{SetShadowMode}{\void}
+
+Returns the shadow mode. See \helpref{wxShape::SetShadowMode}{wxshapesetshadowmode}.
+
+\membersection{wxShape::GetSpaceAttachments}
+
+\constfunc{bool}{GetSpaceAttachments}{\void}
+
+Indicates 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).
+
+\membersection{wxShape::GetTextColour}
+
+\constfunc{wxString}{GetTextColour}{\param{int}{ regionId = 0}}
+
+Gets the colour for the specified text region.
+
+\membersection{wxShape::GetTopAncestor}
+
+\constfunc{wxShape *}{GetTopAncestor}{\void}
+
+Returns the top-most ancestor of this shape (the root of the composite).
+
+\membersection{wxShape::GetX}
+
+\constfunc{double}{GetX}{\void}
+
+Gets the x position of the centre of the shape.
+
+\membersection{wxShape::GetY}
+
+\constfunc{double}{GetY}{\void}
+
+Gets the y position of the centre of the shape.
+
+\membersection{wxShape::HitTest}
+
+\func{bool}{HitTest}{\param{double}{ x}, \param{double}{ y}, \param{int*}{ attachment}, \param{double*}{ distance}}
+
+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.
+
+\membersection{wxShape::Insert}
+
+\func{void}{InsertInCanvas}{\param{wxShapeCanvas*}{ canvas}}
+
+Inserts the shape at the front of the shape list of {\it canvas}.
+
+\membersection{wxShape::IsHighlighted}
+
+\constfunc{bool}{IsHighlighted}{\void}
+
+Returns TRUE if the shape is highlighted. Shape highlighting is unimplemented.
+
+\membersection{wxShape::IsShown}
+
+\constfunc{bool}{IsShown}{\void}
+
+Returns 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.
+
+\membersection{wxShape::MakeControlPoints}
+
+\func{void}{MakeControlPoints}{\void}
+
+Make a list of control points (draggable handles) appropriate to the shape.
+
+\membersection{wxShape::MakeMandatoryControlPoints}
+
+\func{void}{MakeMandatoryControlPoints}{\void}
+
+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).
+
+\membersection{wxShape::Move}\label{wxshapemove}
+
+\func{void}{Move}{\param{wxDC\&}{ dc}, \param{double}{ x1}, \param{double}{ y1}, \param{bool}{ display = TRUE}}
+
+Move the shape to the given position, redrawing if {\it display} is TRUE.
+
+\membersection{wxShape::MoveLineToNewAttachment}\label{wxshapemovelinetonewattachment}
+
+\func{void}{MoveLineToNewAttachment}{\param{wxDC\&}{ dc}, \param{wxLineShape*}{ toMove}, \param{double}{ x}, \param{double}{ 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.
+
+Cals \helpref{wxShape::AttachmentSortTest}{wxshapeattachmentsorttest} and then \helpref{wxShapeEvtHandler::OnChangeAttachment}{wxshapeevthandleronchangeattachment}.
+
+\membersection{wxShape::MoveLinks}
+
+\func{void}{MoveLinks}{\param{wxDC\&}{ dc}}
+
+Redraw all the lines attached to the shape.
+
+\membersection{wxShape::NameRegions}
+
+\func{void}{NameRegions}{\param{const wxString\& }{parentName = ``"}}
+
+Make unique names for all the regions in a shape or composite shape.
+
+\membersection{wxShape::Rotate}
+
+\func{void}{Rotate}{\param{double }{x}, \param{double }{y}, \param{double }{theta}}
+
+Rotate about the given axis by the given amount in radians (does nothing
+for most shapes). But even non-rotating shapes should record their
+notional rotation in case it's important (e.g. in dog-leg code).
+
+\membersection{wxShape::ReadConstraints}
+
+\func{void}{ReadConstraints}{\param{wxExpr *}{clause}, \param{wxExprDatabase *}{database}}
+
+If the shape is a composite, it may have constraints that need to be read in in a separate pass.
+
+\membersection{wxShape::ReadAttributes}
+
+\func{void}{ReadAttributes}{\param{wxExpr*}{ clause}}
+
+Reads the attributes (data member values) from the given expression.
+
+\membersection{wxShape::ReadRegions}
+
+\func{void}{ReadRegions}{\param{wxExpr *}{clause}}
+
+Reads in the regions.
+
+\membersection{wxShape::Recentre}
+
+\func{void}{Recentre}{\void}
+
+Does recentring (or other formatting) for all the text regions for this shape.
+
+\membersection{wxShape::RemoveFromCanvas}
+
+\func{void}{RemoveFromCanvas}{\param{wxShapeCanvas*}{ canvas}}
+
+Removes the shape from the canvas.
+
+\membersection{wxShape::ResetControlPoints}
+
+\func{void}{ResetControlPoints}{\void}
+
+Resets the positions of the control points (for instance when the
+shape's shape has changed).
+
+\membersection{wxShape::ResetMandatoryControlPoints}
+
+\func{void}{ResetMandatoryControlPoints}{\void}
+
+Reset 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).
+
+\membersection{wxShape::Recompute}\label{wxshaperecompute}
+
+\func{bool}{Recompute}{\void} 
+
+Recomputes any constraints associated with the shape (normally
+applicable to wxCompositeShapes only, but harmless for other
+classes of shape).
+
+\membersection{wxShape::RemoveLine}
+
+\func{void}{RemoveLine}{\param{wxLineShape*}{ line}}
+
+Removes the given line from the shape's list of attached lines.
+
+\membersection{wxShape::Select}\label{wxshapeselect}
+
+\func{void}{Select}{\param{bool}{ select = TRUE}}
+
+Selects or deselects the given shape, drawing or erasing control points
+(handles) as necessary.
+
+\membersection{wxShape::Selected}\label{wxshapeselected}
+
+\constfunc{bool}{Selected}{\void}
+
+TRUE if the shape is currently selected.
+
+\membersection{wxShape::SetAttachmentMode}\label{wxshapesetattachmentmode}
+
+\func{void}{SetAttachmentMode}{\param{bool}{ flag}}
+
+Sets the attachment mode to TRUE or FALSE. 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.
+
+\membersection{wxShape::SetBrush}
+
+\func{void}{SetBrush}{\param{wxBrush *}{brush}}
+
+Sets the brush for filling the shape's shape.
+
+\membersection{wxShape::SetCanvas}\label{wxshapesetcanvas}
+
+\func{void}{SetCanvas}{\param{wxShapeCanvas*}{ theCanvas}}
+
+Identical to \helpref{wxShape::Attach}{wxshapesetcanvas}.
+
+\membersection{wxShape::SetCentreResize}
+
+\func{void}{SetCentreResize}{\param{bool}{ 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).
+
+\membersection{wxShape::SetClientData}
+
+\func{void}{SetClientData}{\param{wxObject *}{clientData}}
+
+Sets the client data.
+
+\membersection{wxShape::SetDefaultRegionSize}\label{setdefaultregionsize}
+
+\func{void}{SetDefaultRegionSize}{\void}
+
+Set the default region to be consistent with the shape size.
+
+\membersection{wxShape::SetDisableLabel}
+
+\func{void}{SetDisableLabel}{\param{bool}{ flag}}
+
+Set {\it flag} to TRUE to stop the default region being shown, FALSE otherwise.
+
+\membersection{wxShape::SetDraggable}
+
+\func{void}{SetDraggable}{\param{bool}{ drag}, \param{bool}{ recursive = FALSE}}
+
+Sets the shape to be draggable or not draggable.
+
+\membersection{wxShape::SetDrawHandles}
+
+\func{void}{SetDrawHandles}{\param{bool}{ drawH}}
+
+Sets the {\it drawHandles} flag for this shape and all descendants. If {\it drawH} is TRUE (the default),
+any handles (control points) will be drawn. Otherwise, the handles will not be drawn.
+
+\membersection{wxShape::SetEventHandler}
+
+\func{void}{GetEventHandler}{\param{wxShapeEvtHandler *}{handler}}
+
+Sets the event handler for this shape.
+
+\membersection{wxShape::SetFixedSize}
+
+\func{void}{SetFixedSize}{\param{bool}{ x}, \param{bool}{ y}} 
+
+Sets the shape to be of the given, fixed size.
+
+\membersection{wxShape::SetFont}
+
+\func{void}{SetFont}{\param{wxFont *}{font}, \param{int}{ regionId = 0}}
+
+Sets the font for the specified text region.
+
+\membersection{wxShape::SetFormatMode}\label{setformatmode}
+
+\func{void}{SetFormatMode}{\param{int}{ mode}, \param{int}{ regionId = 0}} 
+
+Sets the format mode of the default text region. The argument can be a bit list
+of the following:
+
+\begin{description}\itemsep=0pt
+\item[FORMAT\_NONE] No formatting.
+\item[FORMAT\_CENTRE\_HORIZ] Horizontal centring.
+\item[FORMAT\_CENTRE\_VERT] Vertical centring.
+\end{description}
+
+\membersection{wxShape::SetHighlight}
+
+\func{void}{SetHighlight}{\param{bool}{ hi}, \param{bool}{ recurse = FALSE}}
+
+Sets the highlight for a shape. Shape highlighting is unimplemented.
+
+\membersection{wxShape::SetId}
+
+\func{void}{SetId}{\param{long}{ id}}
+
+Set the integer identifier for this shape.
+
+\membersection{wxShape::SetMaintainAspectRatio}\label{wxshapesetmaintainaspectratio}
+
+\func{void}{SetMaintainAspectRatio}{\param{bool}{ flag}}
+
+If the argument is TRUE, tells the shape that resizes should not change the aspect ratio
+(width and height should be in the original proportion).
+
+\membersection{wxShape::SetPen}
+
+\func{void}{SetPen}{\param{wxPen *}{pen}}
+
+Sets the pen for drawing the shape's outline.
+
+\membersection{wxShape::SetRegionName}\label{wxshapesetregionname}
+
+\func{void}{SetRegionName}{\param{const wxString\& }{name}, \param{int}{ regionId = 0}}
+
+Sets 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.
+
+\membersection{wxShape::SetSensitivityFilter}\label{wxshapesetsensitivityfilter}
+
+\func{void}{SetSensitivityFilter}{\param{int}{ sens=OP\_ALL}, \param{bool}{ recursive = FALSE}}
+
+Sets the shape to be sensitive or insensitive to specific mouse operations.
+
+{\it sens} is a bitlist of the following:
+
+\begin{itemize}\itemsep=0pt
+\item OP\_CLICK\_LEFT
+\item OP\_CLICK\_RIGHT
+\item OP\_DRAG\_LEFT
+\item OP\_DRAG\_RIGHT
+\item OP\_ALL (equivalent to a combination of all the above).
+\end{itemize}
+
+\membersection{wxShape::SetShadowMode}\label{wxshapesetshadowmode}
+
+\func{void}{SetShadowMode}{\param{int}{ mode}, \param{bool}{ redraw = FALSE}}
+
+Sets the shadow mode (whether a shadow is drawn or not). {\it mode} can be one of
+the following:
+
+\begin{description}\itemsep=0pt
+\item[SHADOW\_NONE] No shadow (the default).
+\item[SHADOW\_LEFT] Shadow on the left side.
+\item[SHADOW\_RIGHT] Shadow on the right side.
+\end{description}
+
+\membersection{wxShape::SetSize}
+
+\func{void}{SetSize}{\param{double}{ x}, \param{double}{ y}, \param{bool}{ recursive = TRUE}}
+
+Sets the shape's size.
+
+\membersection{wxShape::SetSpaceAttachments}
+
+\func{void}{SetSpaceAttachments}{\param{bool}{ sp}}
+
+Indicate 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).
+
+\membersection{wxShape::SetTextColour}
+
+\func{void}{SetTextColour}{\param{const wxString\& }{colour}, \param{int}{ regionId = 0}}
+
+Sets the colour for the specified text region.
+
+\membersection{wxShape::SetX}
+
+\func{void}{SetX}{\param{double}{ x}}
+
+Sets the {\it x} position of the shape.
+
+\membersection{wxShape::SetX}
+
+\func{void}{SetY}{\param{double}{ y}}
+
+Sets the {\it y} position of the shape.
+
+\membersection{wxShape::SpaceAttachments}
+
+\func{void}{SpaceAttachments}{\param{bool}{ sp}} 
+
+Sets the spacing mode: if TRUE, lines at the same attachment point will be
+spaced evenly across that side of the shape. If false, all lines at the
+same attachment point will emanate from the same point.
+
+\membersection{wxShape::Show}
+
+\func{void}{Show}{\param{bool}{ show}}
+
+Sets a flag indicating whether the shape should be drawn.
+
+\membersection{wxShape::Unlink}
+
+\func{void}{Unlink}{\void}
+
+If the shape is a line, unlinks the nodes attached to the shape, removing itself from the list of
+lines for each of the `to' and `from' nodes.
+
+\membersection{wxShape::WriteAttributes}
+
+\func{void}{WriteAttributes}{\param{wxExpr *}{clause}}
+
+Writes the shape's attributes (data member values) into the given expression.
+
+\membersection{wxShape::WriteRegions}
+
+\func{void}{WriteRegions}{\param{wxExpr *}{clause}}
+
+Writes the regions.
+
+\section{\class{wxShapeCanvas}}\label{wxshapecanvas}
+
+A canvas for drawing diagrams on.
+
+\wxheading{Derived from}
+
+wxScrolledWindow
+
+\wxheading{See also}
+
+\helpref{wxDiagram}{wxdiagram}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxShapeCanvas::wxShapeCanvas}
+
+\func{}{wxShapeCanvas}{\param{wxWindow*}{ parent = NULL}, \param{wxWindowID}{ id = -1},
+ \param{const wxPoint\&}{ pos = wxDefaultPosition}, \param{const wxSize\&}{ size = wxDefaultSize},
+ \param{long}{ style = wxBORDER}}
+
+Constructor.
+
+\membersection{wxShapeCanvas::\destruct{wxShapeCanvas}}
+
+\func{}{\destruct{wxShapeCanvas}}{\void}
+
+Destructor.
+
+\membersection{wxShapeCanvas::AddShape}
+
+\func{void}{AddShape}{\param{wxShape *}{shape}, \param{wxShape *}{addAfter = NULL}}
+
+Adds a shape to the diagram. If {\it addAfter} is non-NULL, the shape will be added after this
+one.
+
+\membersection{wxShapeCanvas::FindShape}
+
+\func{wxShape *}{FindShape}{\param{double}{ x1}, \param{double}{ y}, \param{int *}{attachment}, \param{wxClassInfo *}{info = NULL},
+ \param{wxShape *}{notImage = NULL}}
+
+Find a shape under this mouse click. Returns the shape (or NULL), and the nearest attachment point.
+
+If {\it info} is non-NULL, a shape whose class which is a descendant of the desired class is found.
+
+If {\it notImage} is non-NULL, shapes which are descendants of {\it notImage} are ignored.
+
+\membersection{wxShapeCanvas::FindFirstSensitiveShape}
+
+\func{wxShape *}{FindFirstSensitiveShape}{\param{double}{ x1}, \param{double}{ y}, \param{int *}{attachment}, \param{int}{ op}}
+
+Finds the first sensitive shape whose sensitivity filter matches {\it op}, working up the hierarchy of composites until
+one (or none) is found.
+
+\membersection{wxShapeCanvas::GetDiagram}
+
+\constfunc{wxDiagram*}{GetDiagram}{\void}
+
+Returns the canvas associated with this diagram.
+
+\membersection{wxShapeCanvas::GetGridSpacing}
+
+\constfunc{double}{GetGridSpacing}{\void}
+
+Returns the grid spacing.
+
+\membersection{wxShapeCanvas::GetMouseTolerance}
+
+\constfunc{int}{GetMouseTolerance}{\void}
+
+Returns the tolerance within which a mouse move is ignored.
+
+\membersection{wxShapeCanvas::GetShapeList}
+
+\constfunc{wxList*}{GetShapeList}{\void}
+
+Returns a pointer to the internal shape list.
+
+\membersection{wxShapeCanvas::GetQuickEditMode}
+
+\constfunc{bool}{GetQuickEditMode}{\void}
+
+Returns quick edit mode for the associated diagram.
+
+\membersection{wxShapeCanvas::InsertShape}
+
+\func{void}{InsertShape}{\param{wxShape*}{ shape}}
+
+Inserts a shape at the front of the shape list.
+
+\membersection{wxShapeCanvas::OnBeginDragLeft}\label{wxshapecanvasonbegindragleft}
+
+\func{void}{OnBeginDragLeft}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when the start of a left-button drag event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+See also \helpref{wxShapeCanvas::OnDragLeft}{wxshapecanvasondragleft}, \helpref{wxShapeCanvas::OnEndDragLeft}{wxshapecanvasonenddragleft}.
+
+\membersection{wxShapeCanvas::OnBeginDragRight}\label{wxshapecanvasonbegindragright}
+
+\func{void}{OnBeginDragRight}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when the start of a right-button drag event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+See also \helpref{wxShapeCanvas::OnDragRight}{wxshapecanvasondragright}, \helpref{wxShapeCanvas::OnEndDragRight}{wxshapecanvasonenddragright}.
+
+\membersection{wxShapeCanvas::OnEndDragLeft}\label{wxshapecanvasonenddragleft}
+
+\func{void}{OnEndDragLeft}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when the end of a left-button drag event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+See also \helpref{wxShapeCanvas::OnDragLeft}{wxshapecanvasondragleft}, \helpref{wxShapeCanvas::OnBeginDragLeft}{wxshapecanvasonbegindragleft}.
+
+\membersection{wxShapeCanvas::OnEndDragRight}\label{wxshapecanvasonenddragright}
+
+\func{void}{OnEndDragRight}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when the end of a right-button drag event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+See also \helpref{wxShapeCanvas::OnDragRight}{wxshapecanvasondragright}, \helpref{wxShapeCanvas::OnBeginDragRight}{wxshapecanvasonbegindragright}.
+
+\membersection{wxShapeCanvas::OnDragLeft}\label{wxshapecanvasondragleft}
+
+\func{void}{OnDragLeft}{\param{bool}{ draw}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when a left-button drag event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it draw} is alternately TRUE and FALSE, to assist drawing and erasing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+See also \helpref{wxShapeCanvas::OnBeginDragLeft}{wxshapecanvasonbegindragleft}, \helpref{wxShapeCanvas::OnEndDragLeft}{wxshapecanvasonenddragleft}.
+
+\membersection{wxShapeCanvas::OnDragRight}\label{wxshapecanvasondragright}
+
+\func{void}{OnDragRight}{\param{bool}{ draw}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when a right-button drag event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it draw} is alternately TRUE and FALSE, to assist drawing and erasing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+See also \helpref{wxShapeCanvas::OnBeginDragRight}{wxshapecanvasonbegindragright}, \helpref{wxShapeCanvas::OnEndDragRight}{wxshapecanvasonenddragright}.
+
+\membersection{wxShapeCanvas::OnLeftClick}\label{wxshapecanvasonleftclick}
+
+\func{void}{OnLeftClick}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when a left click event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+\membersection{wxShapeCanvas::OnRightClick}\label{wxshapecanvasonrightclick}
+
+\func{void}{OnRightClick}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}}
+
+Called when a right click event on the canvas background is detected by OnEvent. You may override this member;
+by default it does nothing.
+
+{\it keys} is a bit list of the following:
+
+\begin{itemize}\itemsep=0pt
+\item KEY\_SHIFT
+\item KEY\_CTRL
+\end{itemize}
+
+\membersection{wxShapeCanvas::Redraw}
+
+\func{void}{Redraw}{\void}
+
+Calls wxDiagram::Redraw.
+
+\membersection{wxShapeCanvas::RemoveShape}
+
+\func{void}{RemoveShape}{\param{wxShape *}{shape}}
+
+Calls wxDiagram::RemoveShape.
+
+\membersection{wxShapeCanvas::SetDiagram}
+
+\func{void}{SetDiagram}{\param{wxDiagram *}{diagram}}
+
+Sets the diagram associated with this diagram.
+
+\membersection{wxShapeCanvas::Snap}
+
+\func{void}{Snap}{\param{double *}{x}, \param{double *}{y}}
+
+Calls wxDiagram::Snap.
+
+
+
+\section{\class{wxShapeEvtHandler}}\label{wxshapeevthandler}
+
+wxShapeEvtHandler is a class from which wxShape (and therefore all shape classes) are derived.
+A wxShape also contains a pointer to its current wxShapeEvtHandler. Event handlers
+can be swapped in and out, altering the behaviour of a shape. This allows, for example,
+a range of behaviours to be redefined in one class, rather than requiring
+each shape class to be subclassed.
+
+\wxheading{Derived from}
+
+wxObject
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxShapeEvtHandler::m\_handlerShape}
+
+\member{wxShape*}{m\_handlerShape}
+
+Pointer to the shape associated with this handler.
+
+\membersection{wxShapeEvtHandler::m\_previousHandler}
+
+\member{wxShapeEvtHandler*}{m\_previousHandler}
+
+Pointer to the previous handler.
+
+\membersection{wxShapeEvtHandler::wxShapeEvtHandler}
+
+\func{void}{wxShapeEvtHandler}{\param{wxShapeEvtHandler *}{previous = NULL}, \param{wxShape *}{shape = NULL}}
+
+Constructs a new event handler.
+
+\membersection{wxShapeEvtHandler::\destruct{wxShapeEvtHandler}}
+
+\func{void}{\destruct{wxShapeEvtHandler}}{\void}
+
+Destructor.
+
+\membersection{wxShapeEvtHandler::CopyData}\label{wxshapeevthandlercopydata}
+
+\func{void}{CopyData}{\param{wxShapeEvtHandler\&}{ handler}}
+
+A virtual function to copy the data from this object to {\it handler}. Override if you
+derive from wxShapeEvtHandler and have data to copy.
+
+\membersection{wxShapeEvtHandler::CreateNewCopy}\label{wxshapeevthandlercreatenewcopy}
+
+\func{wxShapeEvtHandler*}{CreateNewCopy}{\void}
+
+Creates a new event handler object of the same class as this object, and then
+calls \helpref{wxShapeEvtHandler::CopyData}{wxshapeevthandlercopydata}.
+
+\membersection{wxShapeEvtHandler::GetPreviousHandler}\label{wxshapeevthandlergetprevioushandler}
+
+\constfunc{wxShapeEvtHandler*}{GetPreviousHandler}{\void}
+
+Returns the previous handler.
+
+\membersection{wxShapeEvtHandler::GetShape}\label{wxshapeevthandlergetshape}
+
+\constfunc{wxShape*}{GetShape}{\void}
+
+Returns the shape associated with this handler.
+
+\membersection{wxShapeEvtHandler::OnBeginDragLeft}
+
+\func{void}{OnBeginDragLeft}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when the user is beginning to drag using the left mouse button.
+
+\membersection{wxShapeEvtHandler::OnBeginDragRight}
+
+\func{void}{OnBeginDragRight}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when the user is beginning to drag using the right mouse button.
+
+\membersection{wxShapeEvtHandler::OnBeginSize}
+
+\func{void}{OnBeginSize}{\param{double}{ width}, \param{double}{ height}}
+
+Called when a shape starts to be resized.
+
+\membersection{wxShapeEvtHandler::OnChangeAttachment}\label{wxshapeevthandleronchangeattachment}
+
+\func{void}{OnChangeAttachment}{\param{int}{ attachment}, \param{wxLineShape*}{ line}, \param{wxList\&}{ ordering}}
+
+Override this to prevent or intercept line reordering. wxShape's implementation of this function
+calls \helpref{wxShape::ApplyAttachmentOrdering}{wxshapeapplyattachmentordering} to apply
+the new ordering.
+
+\membersection{wxShapeEvtHandler::OnDragLeft}
+
+\func{void}{OnDragLeft}{\param{bool}{ draw}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called twice when the shape is being dragged, once to allow erasing the old
+image, and again to allow drawing at the new position.
+
+\membersection{wxShapeEvtHandler::OnDragRight}
+
+\func{void}{OnDragRight}{\param{bool}{ draw}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called twice when the shape is being dragged, once to allow erasing the old
+image, and again to allow drawing at the new position.
+
+\membersection{wxShapeEvtHandler::OnDraw}
+
+\func{void}{OnDraw}{\param{wxDC\&}{ dc}}
+
+Defined for each class to draw the main graphic, but
+not the contents.
+
+\membersection{wxShapeEvtHandler::OnDrawContents}
+
+\func{void}{OnDrawContents}{\param{wxDC\&}{ dc}}
+
+Defined for each class to draw the contents of the
+shape, such as text.
+
+\membersection{wxShapeEvtHandler::OnDrawControlPoints}
+
+\func{void}{OnDrawControlPoints}{\param{wxDC\&}{ dc}}
+
+Called when the shape's control points (handles) should
+be drawn.
+
+\membersection{wxShapeEvtHandler::OnDrawOutline}
+
+\func{void}{OnDrawOutline}{\param{wxDC\&}{ dc}}
+
+Called when the outline of the shape should be drawn.
+
+\membersection{wxShapeEvtHandler::OnEndDragLeft}
+
+\func{void}{OnEndDragLeft}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when the user is stopping dragging using the left mouse button.
+
+\membersection{wxShapeEvtHandler::OnEndDragRight}
+
+\func{void}{OnEndDragRight}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when the user is stopping dragging using the right mouse button.
+
+\membersection{wxShapeEvtHandler::OnEndSize}
+
+\func{void}{OnEndSize}{\param{double}{ width}, \param{double}{ height}}
+
+Called after a shape is resized.
+
+\membersection{wxShapeEvtHandler::OnErase}
+
+\func{void}{OnErase}{\param{wxDC\&}{ dc}}
+
+Called when the whole shape should be erased.
+
+\membersection{wxShapeEvtHandler::OnEraseContents}
+
+\func{void}{OnEraseContents}{\param{wxDC\&}{ dc}}
+
+Called when the contents should be erased.
+
+\membersection{wxShapeEvtHandler::OnEraseControlPoints}
+
+\func{void}{OnEraseControlPoints}{\param{wxDC\&}{ dc}}
+
+Called when the shape's control points (handles) should
+be erased.
+
+\membersection{wxShapeEvtHandler::OnHighlight}
+
+\func{void}{OnHighlight}{\param{wxDC\&}{ dc}}
+
+Called when the shape should be highlighted.
+
+\membersection{wxShapeEvtHandler::OnLeftClick}
+
+\func{void}{OnLeftClick}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys =0}, \param{int}{ attachment = 0}}
+
+Called when the shape receives a left mouse click event.
+
+\membersection{wxShapeEvtHandler::OnMoveLink}
+
+\func{void}{OnMoveLink}{\param{wxDC\&}{ dc}, \param{bool}{ moveControlPoints=TRUE}}
+
+Called when the line attached to an shape need to be repositioned,
+because the shape has moved.
+
+\membersection{wxShapeEvtHandler::OnMoveLinks}
+
+\func{void}{OnMoveLinks}{\param{wxDC\&}{ dc}}
+
+Called when the lines attached to an shape need to be repositioned,
+because the shape has moved.
+
+\membersection{wxShapeEvtHandler::OnMovePost}
+
+\func{bool}{OnMovePost}{\param{wxDC\&}{ dc}, \param{double}{ x}, \param{double}{ y}, \param{double}{ oldX}, \param{double}{ oldY}, \param{bool}{ display = TRUE}}
+
+Called just after the shape receives a move request.
+
+\membersection{wxShapeEvtHandler::OnMovePre}
+
+\func{bool}{OnMovePre}{\param{wxDC\&}{ dc}, \param{double}{ x}, \param{double}{ y}, \param{double}{ oldX}, \param{double}{ oldY}, \param{bool}{ display = TRUE}}
+
+Called just before the shape receives a move request. Returning TRUE
+allows the move to be processed; returning FALSE vetoes the move.
+
+\membersection{wxShapeEvtHandler::OnRightClick}
+
+\func{void}{OnRightClick}{\param{double}{ x}, \param{double}{ y}, \param{int}{ keys = 0}, \param{int}{ attachment = 0}}
+
+Called when the shape receives a mouse mouse click event.
+
+\membersection{wxShapeEvtHandler::OnSize}
+
+\func{void}{OnSize}{\param{double}{ x}, \param{double}{ y}}
+
+Called when the shape receives a resize request.
+
+\membersection{wxShapeEvtHandler::OnSizingBeginDragLeft}
+
+\func{void}{OnSizingBeginDragLeft}{\param{wxControlPoint*}{ pt}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when a sizing drag is beginning.
+
+\membersection{wxShapeEvtHandler::OnSizingDragLeft}
+
+\func{void}{OnSizingDragLeft}{\param{wxControlPoint*}{ pt}, \param{bool}{ draw}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when a sizing drag is occurring.
+
+\membersection{wxShapeEvtHandler::OnSizingEndDragLeft}
+
+\func{void}{OnSizingEndDragLeft}{\param{wxControlPoint*}{ pt}, \param{double}{ x}, \param{double}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called when a sizing drag is ending.
+
+\membersection{wxShapeEvtHandler::SetPreviousHandler}\label{wxshapeevthandlersetprevioushandler}
+
+\func{void}{SetPreviousHandler}{\param{wxShapeEvtHandler*}{ handler}}
+
+Sets the previous handler.
+
+\membersection{wxShapeEvtHandler::SetShape}\label{wxshapeevthandlersetshape}
+
+\func{void}{SetShape}{\param{wxShape*}{ shape}}
+
+Sets the shape for this handler.
+
+\section{\class{wxTextShape}}\label{wxtextshape}
+
+As wxRectangleShape, but only the text is displayed.
+
+\wxheading{Derived from}
+
+\helpref{wxRectangleShape}{wxrectangleshape}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{wxTextShape::wxTextShape}
+
+\func{void}{wxTextShape}{\param{double}{ width = 0.0}, \param{double}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxTextShape::\destruct{wxTextShape}}
+
+\func{void}{\destruct{wxTextShape}}{\void}
+
+Destructor.
+
+\section{Functions}\label{functions}
+
+These are the OGL functions.
+
+\membersection{::wxOGLInitialize}
+
+\func{void}{wxOGLInitialize}{\void}
+Initializes OGL.
+
+\membersection{::wxOGLCleanUp}
+
+\func{void}{wxOGLCleanUp}{\void}
+Cleans up OGL.
+
diff --git a/docs/latex/ogl/contents.gif b/docs/latex/ogl/contents.gif
new file mode 100644 (file)
index 0000000..3dddfa3
Binary files /dev/null and b/docs/latex/ogl/contents.gif differ
diff --git a/docs/latex/ogl/forward.gif b/docs/latex/ogl/forward.gif
new file mode 100644 (file)
index 0000000..9c81e8c
Binary files /dev/null and b/docs/latex/ogl/forward.gif differ
diff --git a/docs/latex/ogl/intro.tex b/docs/latex/ogl/intro.tex
new file mode 100644 (file)
index 0000000..6d208ff
--- /dev/null
@@ -0,0 +1,47 @@
+\chapter{Introduction}
+\pagenumbering{arabic}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+Object Graphics Library (\ogl) is a C++ library supporting the creation and
+manipulation of simple and complex graphic images on a canvas.
+
+It can be found in the directory {\tt utils/ogl/src} in the
+wxWindows distribution. The file {\tt ogl.h} must be included to make use
+of the library.
+
+Please see \helpref{OGL overview}{ogloverview} for a general description how the object library works. For details,
+please see the \helpref{class reference}{classref}.
+
+\section{File structure}
+
+These are the files that comprise the \ogl\ library.
+
+\begin{description}\itemsep=0pt
+\item[basic.h] Header for basic objects such as wxShape and wxRectangleShape.
+\item[basic.cpp] Basic objects implementation (1).
+\item[basic2.cpp] Basic objects implementation (2).
+\item[bmpshape.h] wxBitmapShape class header.
+\item[bmpshape.cpp] wxBitmapShape implementation.
+\item[canvas.h] wxShapeCanvas class header.
+\item[canvas.cpp] wxShapeCanvas class implementation.
+\item[composit.h] Composite object class header.
+\item[composit.cpp] Composite object class implementation.
+\item[constrnt.h] Constraint classes header.
+\item[constrnt.cpp] Constraint classes implementation.
+\item[divided.h] Divided object class header.
+\item[divided.cpp] Divided object class implementation.
+\item[drawn.h] Drawn (metafile) object class header.
+\item[drawn.cpp] Drawn (metafile) object class implementation.
+\item[graphics.h] Main include file.
+\item[lines.h] wxLineShape class header.
+\item[lines.cpp] wxLineShape class implementation.
+\item[misc.h] Miscellaneous graphics functions header.
+\item[misc.cpp] Miscellaneous graphics functions implementation.
+\item[ogldiag.h] wxDiagram class header.
+\item[ogldiag.cpp] wxDiagram implementation.
+\item[mfutils.h] Metafile utilities header.
+\item[mfutils.cpp] Metafile utilities implementation.
+\end{description}
+
+
diff --git a/docs/latex/ogl/ogl.hpj b/docs/latex/ogl/ogl.hpj
new file mode 100644 (file)
index 0000000..5db5ace
--- /dev/null
@@ -0,0 +1,17 @@
+[OPTIONS]
+BMROOT=d:\wx2\wxwind~1\docs\latex\ogl ; Assume that bitmaps are where the source is
+TITLE=OGL Manual
+CONTENTS=Contents
+COMPRESS=HIGH
+
+[FILES]
+ogl.rtf
+
+[CONFIG]
+CreateButton("Up", "&Up", "JumpId(`ogl.hlp', `Contents')")
+BrowseButtons()
+
+[MAP]
+
+[BITMAPS]
+
diff --git a/docs/latex/ogl/ogl.tex b/docs/latex/ogl/ogl.tex
new file mode 100644 (file)
index 0000000..c9c9c21
--- /dev/null
@@ -0,0 +1,46 @@
+\documentstyle[a4,makeidx,verbatim,texhelp,fancyhea,mysober,mytitle]{report}
+\newcommand{\ogl}[0]{{OGL}}%
+\definecolour{black}{0}{0}{0}%
+\definecolour{cyan}{0}{255}{255}%
+\definecolour{green}{0}{255}{0}%
+\definecolour{magenta}{255}{0}{255}%
+\definecolour{red}{255}{0}{0}%
+\definecolour{blue}{0}{0}{200}%
+\definecolour{yellow}{255}{255}{0}%
+\definecolour{white}{255}{255}{255}%
+\input psbox.tex
+\parindent 0pt
+\parskip 11pt
+\title{Object Graphics Library 3.0}
+\author{Julian Smart}
+\date{September 1998}
+
+\makeindex
+\begin{document}
+\maketitle
+
+\pagestyle{fancyplain}
+\bibliographystyle{plain}
+\pagenumbering{roman}
+\setheader{{\it CONTENTS}}{}{}{}{}{{\it CONTENTS}}
+\setfooter{\thepage}{}{}{}{}{\thepage}
+\tableofcontents%
+
+\input{intro.tex}
+%
+\input{sample.tex}
+%
+\input{classes.tex}
+%
+\input{topics.tex}
+%
+\input{bugs.tex}
+%
+\input{changes.tex}
+
+%
+\addcontentsline{toc}{chapter}{Index}
+\setheader{{\it INDEX}}{}{}{}{}{{\it INDEX}}
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+\printindex
+\end{document}
diff --git a/docs/latex/ogl/ogledit.bmp b/docs/latex/ogl/ogledit.bmp
new file mode 100644 (file)
index 0000000..e7cf417
Binary files /dev/null and b/docs/latex/ogl/ogledit.bmp differ
diff --git a/docs/latex/ogl/ogledit.gif b/docs/latex/ogl/ogledit.gif
new file mode 100644 (file)
index 0000000..9784681
Binary files /dev/null and b/docs/latex/ogl/ogledit.gif differ
diff --git a/docs/latex/ogl/sample.tex b/docs/latex/ogl/sample.tex
new file mode 100644 (file)
index 0000000..4e0a437
--- /dev/null
@@ -0,0 +1,87 @@
+\chapter{OGLEdit: a sample OGL application}\label{ogledit}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+OGLEdit is a sample OGL application that allows the user to draw, edit,
+save and load a few shapes. It should clarify aspects of OGL usage, and
+can act as a template for similar applications. OGLEdit can be found in\rtfsp
+{\tt samples/ogledit} in the OGL distribution.
+
+$$\image{10cm;0cm}{ogledit.eps}$$\par
+
+The wxWindows document/view model has been used in OGL, to reduce the amount of
+housekeeping logic required to get it up and running. OGLEdit also provides
+a demonstration of the Undo/Redo capability supported by the document/view classes,
+and how a typical application might implement this feature.
+
+\section{OGLEdit files}
+
+OGLEdit comprises the following source files.
+
+\begin{itemize}\itemsep=0pt
+\item doc.h, doc.cpp: MyDiagram, DiagramDocument, DiagramCommand, MyEvtHandler
+classes related to diagram functionality and documents.
+\item view.h, view.cpp: MyCanvas, DiagramView classes related to visualisation of
+the diagram.
+\item ogledit.h, ogledit.cpp: MyFrame, MyApp classes related to the overall application.
+\item palette.h, palette.cpp: EditorToolPalette implementing the shape palette.
+\end{itemize}
+
+\section{How OGLEdit works}
+
+OGLEdit defines a DiagramDocument class, each of instance of which holds a MyDiagram
+member which itself contains the shapes.
+
+In order to implement specific mouse behaviour for shapes, a class MyEvtHandler is
+defined which is `plugged into' each shape when it is created, instead of overriding each shape class
+individually. This event handler class also holds a label string.
+
+The DiagramCommand class is the key to implementing Undo/Redo. Each instance of DiagramCommand
+stores enough information about an operation (create, delete, change colour etc.) to allow
+it to carry out (or undo) its command. In DiagramView::OnMenuCommand, when the user initiates the
+command, a new DiagramCommand instance is created which is then sent to the document's
+command processor (see wxWindows manual for more information about doc/view and command
+processing).
+
+Apart from menu commands, another way commands are initiated is by the user left-clicking on
+the canvas or right-dragging on a node. MyCanvas::OnLeftClick in view.cpp shows how
+the appropriate wxClassInfo is passed to a DiagramCommand, to allow DiagramCommand::Do
+to create a new shape given the wxClassInfo.
+
+The MyEvtHandler right-drag methods in doc.cpp implement drawing a line between
+two shapes, detecting where the right mouse button was released and looking for a second
+shape. Again, a new DiagramCommand instance is created and passed to the command
+processor to carry out the command.
+
+DiagramCommand::Do and DiagramCommand::Undo embody much of the
+interesting interaction with the OGL library. A complication of note
+when implementing undo is the problem of deleting a node shape which has
+one or more arcs attached to it. If you delete the node, the arc(s)
+should be deleted too. But multiple arc deletion represents more information
+that can be incorporated in the existing DiagramCommand scheme. OGLEdit
+copes with this by treating each arc deletion as a separate command, and
+sending Cut commands recursively, providing an undo path. Undoing such a
+Cut will only undo one command at a time - not a one to one
+correspondence with the original command - but it's a reasonable
+compromise and preserves Do/Undo whilst keeping our DiagramCommand class
+simple.
+
+\section{Possible enhancements}
+
+OGLEdit is very simplistic and does not employ the more advanced features
+of OGL, such as:
+
+\begin{itemize}\itemsep=0pt
+\item attachment points (arcs are drawn to particular points on a shape)
+\item metafile and bitmaps shapes
+\item divided rectangles
+\item composite shapes, and constraints
+\item creating labels in shape regions
+\item arc labels (OGL has support for three movable labels per arc)
+\item spline and multiple-segment line arcs
+\item adding annotations to node and arc shapes
+\item line-straightening (supported by OGL) and alignment (not supported directly by OGL)
+\end{itemize}
+
+These could be added to OGLEdit, at the risk of making it a less
+useful example for beginners.
diff --git a/docs/latex/ogl/tex2rtf.ini b/docs/latex/ogl/tex2rtf.ini
new file mode 100644 (file)
index 0000000..41dd2cf
--- /dev/null
@@ -0,0 +1,35 @@
+;    Last change:  JS    8 Sep 98    2:54 pm
+runTwice = yes
+titleFontSize = 12
+authorFontSize = 10
+chapterFontSize = 12
+sectionFontSize = 12
+subsectionFontSize = 12
+headerRule = yes
+footerRule = yes
+useHeadingStyles = yes
+listItemIndent=40
+generateHPJ = no
+htmlBrowseButtons = bitmap
+winHelpVersion = 3
+winHelpContents = yes
+winHelpTitle = "OGL Manual"
+truncateFilenames = yes
+combineSubSections = yes
+\overview [2] {\rtfonly{See also }\settransparency{on}\sethotspotcolour{off}\sethotspotunderline{on}\winhelponly{\image{}{books.bmp}\settransparency{off}}
+\htmlonly{\image{}{books.gif}}\helpref{#1}{#2}
+\sethotspotcolour{on}\sethotspotunderline{on}}
+\docparam [2]{\parskip{0}{\it #1}\htmlignore{\par}\parskip{10}\indented{1cm}{#2}}
+\wxheading [1]{{\bf \htmlignore{\fcol{blue}{#1}}\htmlonly{\fcol{red}{#1}}}}
+\const [0] {{\bf const}}
+\constfunc [3] {{\bf #1} {\bf #2}(#3) {\bf const}\index{#2}}
+\windowstyle [1] {{\bf #1}\index{#1}}
+
+;;
+;; These two are for generating MS HTML Help project, contents and index files.
+;;
+htmlWorkshopFiles = true
+htmlIndex = true
+\pythonnote [1] {{\bf \fcol{blue}{wxPython note:}} #1}
+%\pythonnote [1] {}
+
diff --git a/docs/latex/ogl/texhelp.sty b/docs/latex/ogl/texhelp.sty
new file mode 100644 (file)
index 0000000..af91bd5
--- /dev/null
@@ -0,0 +1,298 @@
+% LaTeX style file
+% Name:   texhelp.sty
+% Author: Julian Smart
+%
+% Purpose
+% -------
+% Style file to enable the simultaneous preparation of printed LaTeX and on-line
+% hypertext manuals.
+% Use in conjunction with Tex2RTF (see Tex2RTF documentation).
+%
+% Note that if a non-ASCII character starts a newline and there should be a space
+% between the last word on the previous line and the first word on this line,
+% you need to use \rtfsp to generate a space in Windows Help. \rtfsp is ignored
+% in all other formats.
+%
+% Julian Smart
+% Artificial Intelligence Applications Institute
+%
+%
+% ============== C++/CLIPS Documentation Facilities ==============
+%
+% Each class definition should be typeset with e.g.
+%
+% \section{\class{Name}: Parent}
+%
+% followed by a description of the class.
+% Each member should follow:
+%
+% \membersection{wxName::Member}
+%
+% with a description of what this member does.
+% Then, one (or more if overloaded) member (function) in detail:
+%
+% \func{return type}{name}{args}
+% or
+% \member{type}{name}
+%
+% where args is a list of \param{type}{name}, ...
+
+% Function, e.g.
+% e.g. to typeset
+%
+% void DoIt(char *string);
+%
+% write:
+%
+% \func{void}{DoIt}{\param{char *}{string}}
+%
+
+\newcommand{\func}[3]{\hangafter=1\noindent\hangindent=10mm
+{{\it #1} {\bf #2}\index{#2}}(#3)}
+
+% For function/type definition where the name is a pointer,
+% e.g. to typeset
+%
+% typedef void (*wxFunction)(wxObject&)
+%
+% write:
+%
+% \pfunc{typedef void}{wxFunction}{param{wxObject&}}
+
+\newcommand{\pfunc}[3]{\hangafter=1\noindent\hangindent=10mm
+{{\it #1} ({\bf *#2})\index{#2}}(#3)}
+
+% Use an ordinary \section command for class name definitions.
+
+% This is used for a member, such as wxBitmap: GetDepth
+\newcommand{\membersection}[1]{\subsection*{#1}\index{#1}}
+
+% CLIPS function
+\newcommand{\clipsfunc}[3]{\hangafter=1\noindent\hangindent=10mm
+{{\bf #1} ({\bf #2}\index{#2}}#3)}
+
+\newcommand{\clipssection}[1]{\chapter{#1}}
+
+% This is used for a CLIPS function name
+\newcommand{\functionsection}[1]{\subsection*{#1}}
+
+% Member: a type and a name
+\newcommand{\member}[2]{{\bf #1 \it #2}}
+
+% C++ Parameter: a type and a name (no intervening space)
+\newcommand{\param}[2]{{\it #1}{\bf #2}}
+
+% CLIPS Parameter: a type and a name (one intervening space)
+\newcommand{\cparam}[2]{{\bf #1} {\it #2}}
+
+% Class: puts in index
+\newcommand{\class}[1]{#1\index{#1}}
+
+%\newcommand{\docparam}[2]{\parskip=0pt {\it #1}\par\parskip=10pt\begin{indented}{1cm}{#2}\end{indented}}
+
+% Void type
+\newcommand{\void}{{\it void}}
+
+% Typeset destructor
+\newcommand{\destruct}[1]{{$\sim$}#1}
+
+% Typeset insert/extract operators
+\newcommand{\cinsert}{$<<$}
+\newcommand{\cextract}{$>>$}
+
+
+% =================== Hypertext facilities ===================
+%
+% To insert hyperlinks (or references, in Latex), \label the sections
+% or membersections \label{ref-label} immediately after the section, on the same line,
+% and use \helpref{text-to-show}{ref-label} to make a reference.
+%
+
+% Type text with section reference
+\newcommand{\helpref}[2]{{\it #1} (p.\ \pageref{#2}) }
+
+% Type text with URL in verbatim mode
+\newcommand{\urlref}[2]{#1 (\verb$#2$)}
+
+% Don't typeset section number in LaTeX
+\newcommand{\helprefn}[2]{{\it #1}}
+
+% Like helpref, but popup text in WinHelp instead of hyperlinked
+\newcommand{\popref}[2]{{\it #1}}
+
+% Like footnote, but popup text.
+\newcommand{\footnotepopup}[2]{{\it #1}\footnote{#2}}
+
+% =================== On-line help specific macros ===================
+%
+
+% Global document font size/family, help only.
+\newcommand{\helpfontsize}[1]{}
+\newcommand{\helpfontfamily}[1]{}
+
+% Ignore in all on-line help
+\newcommand{\helpignore}[1]{#1}
+% Only print in all on-line help
+\newcommand{\helponly}[1]{}
+
+% Ignore in LaTeX
+\newcommand{\latexignore}[1]{}
+% Only print in LaTeX
+\newcommand{\latexonly}[1]{#1}
+
+% Ignore in linear RTF
+\newcommand{\rtfignore}[1]{#1}
+% Only print in linear RTF
+\newcommand{\rtfonly}[1]{}
+
+% Ignore in WinHelp RTF
+\newcommand{\winhelpignore}[1]{#1}
+% Only print in WinHelp RTF
+\newcommand{\winhelponly}[1]{}
+
+% Ignore in wxHelp
+\newcommand{\xlpignore}[1]{#1}
+% Only print in wxHelp
+\newcommand{\xlponly}[1]{}
+
+% Ignore in HTML
+\newcommand{\htmlignore}[1]{#1}
+% Only print in HTML
+\newcommand{\htmlonly}[1]{}
+
+% Input a file only for help system (binder thickness is not a limitation
+% in help systems!)
+\newcommand{\helpinput}[1]{}
+
+\newcommand{\rtfsp}{ } % Force a space in RTF, ignore in Latex
+
+% =================== Miscellaneous macros ===================
+%
+% Headings consistent with generated ones
+\newcommand{\myheading}[1]{\vspace*{25pt}
+\begin{flushleft}
+{\LARGE \bf #1}
+\end{flushleft}
+\vskip 20pt
+}
+
+% Heading with entry in contents page.
+\newcommand{\chapterheading}[1]{\myheading{#1}
+\addcontentsline{toc}{chapter}{#1}}
+
+\newcommand{\sectionheading}[1]{\myheading{#1}
+\addcontentsline{toc}{section}{#1}}
+
+% Glossary environment
+\newenvironment{helpglossary}{\newpage\chapterheading{Glossary}\begin{description}}{\end{description}}
+
+% Glossary entry
+\newcommand{\gloss}[1]{\item[#1]\index{#1}}
+
+% Image: EPS in Latex, BMP or MF (whatever's available) in RTF. Requires psbox.
+\newcommand{\image}[2]{\psboxto(#1){#2}}
+
+% Image, left aligned (HTML)
+\newcommand{\imager}[2]{\psboxto(#1){#2}}
+
+% Image, right aligned (HTML)
+\newcommand{\imagel}[2]{\psboxto(#1){#2}}
+
+% Imagemap: principally for HTML only. In Latex,
+% acts like \image.
+\newcommand{\imagemap}[3]{\psboxto(#1){#2}}
+
+% Headers and footers
+% \setheader{EvenPageLeft}{EvenPageCentre}{EvenPageRight}
+% {OddPageLeft}{OddPageCentre}{OddPageRight}
+\newcommand{\setheader}[6]{
+\lhead[\fancyplain{}{#1}]{\fancyplain{}{#4}}
+\chead[\fancyplain{}{#2}]{\fancyplain{}{#5}}
+\rhead[\fancyplain{}{#3}]{\fancyplain{}{#6}}
+}
+
+% \setfooter{EvenPageLeft}{EvenPageCentre}{EvenPageRight}
+% {OddPageLeft}{OddPageCentre}{OddPageRight}
+\newcommand{\setfooter}[6]{
+\lfoot[\fancyplain{#1}{#1}]{\fancyplain{#4}{#4}}
+\cfoot[\fancyplain{#2}{#2}]{\fancyplain{#5}{#5}}
+\rfoot[\fancyplain{#3}{#3}]{\fancyplain{#6}{#6}}
+}
+
+% Needed for telling RTF where margin paragraph should go
+% in mirrored margins mode.
+\newcommand{\marginpareven}[1]{\hspace*{0pt}\marginpar{#1}}
+\newcommand{\marginparodd}[1]{\hspace*{0pt}\marginpar{#1}}
+
+% Environment for two-column table popular in WinHelp and manuals.
+\newcommand{\twocolwidtha}[1]{\def\twocolwidthaval{#1}}
+\newcommand{\twocolwidthb}[1]{\def\twocolwidthbval{#1}}
+\newcommand{\twocolspacing}[1]{\def\twocolspacingval{#1}}
+
+\twocolwidtha{3cm}
+\twocolwidthb{8.5cm}
+\twocolspacing{2}
+
+\newcommand{\twocolitem}[2]{#1 & #2\\}
+\newcommand{\twocolitemruled}[2]{#1 & #2\\\hline}
+
+\newenvironment{twocollist}{\renewcommand{\arraystretch}{\twocolspacingval}\begin{tabular}{lp{\twocolwidthbval}}}%
+{\end{tabular}\renewcommand{\arraystretch}{1}}
+
+% Specifying table rows for RTF compatibility
+\newcommand{\row}[1]{#1\\}
+
+% Use for the last ruled row for correct RTF generation.
+\newcommand{\ruledrow}[1]{#1\\\hline}
+
+% Indentation environment. Arg1 is left margin size
+\newenvironment{indented}[1]{\begin{list}{}{\leftmargin=#1}\item[]}%
+{\end{list}}
+
+% Framed box of text, normal formatting.
+\newcommand{\normalbox}[1]{\fbox{\vbox{#1}}}
+% Double-framed box of text.
+\newcommand{\normalboxd}[1]{\fbox{\fbox{\vbox{#1}}}}
+
+% WITHDRAWN -- can't do in RTF, easily.
+% Framed box of text, horizontally centred. Ragged right within box.
+% \newcommand{\centeredbox}[2]{\begin{center}\fbox{\parbox{#1}{\raggedright#2}}\end{center}}
+% Double-framed box of text, horizontally centred. Ragged right within box.
+% \newcommand{\centeredboxd}[2]{\begin{center}\fbox{\fbox{\parbox{#1}{\raggedright#2}}}\end{center}}
+
+% toocomplex environment: simply prints the argument in LaTeX,
+% comes out verbatim in all generated formats.
+\newenvironment{toocomplex}{}{}
+
+% Colour: dummy commands since LaTeX doesn't support colour.
+% \definecolour{name}{red}{blue}{green}
+% \fcol{name}{text} ; Foreground
+% \bcol{name}{text} ; Background
+\newcommand{\definecolour}[4]{}
+\newcommand{\definecolor}[4]{}
+\newcommand{\fcol}[2]{#2}
+\newcommand{\bcol}[2]{#2}
+\newcommand{\sethotspotcolour}[1]{}
+\newcommand{\sethotspotunderline}[1]{}
+\newcommand{\settransparency}[1]{}
+\newcommand{\backslashraw}[0]{}
+\newcommand{\lbraceraw}[0]{}
+\newcommand{\rbraceraw}[0]{}
+\newcommand{\registered}[0]{(r)}
+\newcommand{\background}[1]{}
+\newcommand{\textcolour}[1]{}
+\newcommand{\overview}[2]{See \helpref{#1}{#2}.}
+\newcommand{\docparam}[2]{{\it #1}\begin{list}{}{\leftmargin=1cm}\item[]
+#2%
+\end{list}}
+\newcommand{\wxheading}[1]{{\bf #1}}
+\newcommand{\const}[0]{{\bf const}}
+\newcommand{\constfunc}[3]{{\bf #1} {\bf #2}(#3) {\bf const}\index{#2}}
+\newcommand{\windowstyle}[1]{{\bf #1}\index{#1}}
+
+\addtolength{\textwidth}{1in}
+\addtolength{\oddsidemargin}{-0.5in}
+\addtolength{\topmargin}{-0.5in}
+\addtolength{\textheight}{1in}
+\sloppy
+
diff --git a/docs/latex/ogl/topics.tex b/docs/latex/ogl/topics.tex
new file mode 100644 (file)
index 0000000..ece8016
--- /dev/null
@@ -0,0 +1,161 @@
+\chapter{Topic overviews}
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+The following sections describe particular topics.
+
+\section{OGL overview}\label{ogloverview}
+
+\helpref{wxShapeCanvas}{wxshapecanvas}, derived from {\bf wxCanvas}, is the drawing area
+for a number of \helpref{wxShape}{wxshape} instances. Everything drawn on a
+wxShapeCanvas is derived from wxShape, which provides virtual
+member functions for redrawing, creating and destroying
+resize/selection `handles', movement and erasing behaviour, mouse
+click behaviour, calculating the bounding box of the shape, linking
+nodes with arcs, and so on.
+
+The way a client application copes with `damage' to the canvas is to
+erase (white out) anything should no longer be displayed, redraw the shape,
+and then redraw everything on the canvas to repair any damage. If quick edit
+mode is on for the canvas, the complete should be omitted by OGL and the
+application.
+
+Selection handles (called control points in the code) are implemented as
+wxRectangleShapes.
+
+Events are passed to shapes by the canvas in a high-level form, for example {\bf OnLeftClick},
+{\bf OnBeginDragLeft}, {\bf OnDragLeft}, {\bf OnEndDragLeft}. The canvas decides
+what is a click and what is a drag, whether it is on a shape or the canvas itself,
+and (by interrogating the shape) which attachment point the click is associated with.
+
+In order to provide event-handling flexibility, each shapes has an `event handler' associated with it,
+which by default is the shape itself (all shapes derive from wxShapeEvtHandler).
+An application can modify the event-handling behaviour simply by plugging a new
+event handler into the shape. This can avoid the need for multiple inheritance when
+new properties and behaviour are required for a number of different shape classes: instead
+of overriding each class, one new event handler class can be defined and used for all
+existing shape classes.
+
+A range of shapes have been predefined in the library, including rectangles, ellipses,
+polygons. A client application can derive from these shapes and/or derive entirely
+new shapes from wxShape.
+
+Instances of a class called \helpref{wxDiagram}{wxdiagram} organise collections of
+shapes, providing default file input and output behaviour.
+
+\section{wxDividedShape overview}\label{dividedshapeoverview}
+
+Classes: \helpref{wxDividedShape}{wxdividedshape}
+
+A wxDividedShape 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.
+
+Once a wxDividedShape has been created, the user may move the divisions with the
+mouse. By pressing Ctrl while right-clicking, the region attributes can be edited.
+
+Here are examples of creating wxDividedShape objects:
+
+{\small
+\begin{verbatim}
+  /*
+   * Divided rectangle with 3 regions
+   *
+   */
+
+  wxDividedShape *dividedRect = new wxDividedShape(50, 60);
+
+  wxShapeRegion *region = new wxShapeRegion;
+  region->SetProportions(0.0, 0.25);
+  dividedRect->AddRegion(region);
+
+  region = new wxShapeRegion;
+  region->SetProportions(0.0, 0.5);
+  dividedRect->AddRegion(region);
+
+  region = new wxShapeRegion;
+  region->SetProportions(0.0, 0.25);
+  dividedRect->AddRegion(region);
+  
+  dividedRect->SetSize(50, 60); // Allow it to calculate region sizes
+  dividedRect->SetPen(wxBLACK_PEN);
+  dividedRect->SetBrush(wxWHITE_BRUSH);
+  dividedRect->Show(TRUE);
+  dividedRect->NameRegions();
+
+  /*
+   * Divided rectangle with 3 regions, rounded
+   *
+   */
+
+  wxDividedShape *dividedRect3 = new wxDividedShape(50, 60);
+  dividedRect3->SetCornerRadius(-0.4);
+
+  region = new wxShapeRegion;
+  region->SetProportions(0.0, 0.25);
+  dividedRect3->AddRegion(region);
+
+  region = new wxShapeRegion;
+  region->SetProportions(0.0, 0.5);
+  dividedRect3->AddRegion(region);
+
+  region = new wxShapeRegion;
+  region->SetProportions(0.0, 0.25);
+  dividedRect3->AddRegion(region);
+  
+  dividedRect3->SetSize(50, 60); // Allow it to calculate region sizes
+  dividedRect3->SetPen(wxBLACK_PEN);
+  dividedRect3->SetBrush(wxWHITE_BRUSH);
+  dividedRect3->Show(TRUE);
+  dividedRect3->NameRegions();
+\end{verbatim}
+}
+
+\section{wxCompositeShape overview}\label{compositeshapeoverview}
+
+Classes: \helpref{wxCompositeShape}{wxcompositeshape}, \helpref{wxOGLConstraint}{wxoglconstraint}
+
+The wxCompositeShape allows fairly complex shapes to be created, and maintains
+a set of constraints which specify the layout and proportions of child shapes.
+
+Add child shapes to a wxCompositeShape using \helpref{AddChild}{wxcompositeshapeaddchild}, and
+add constraints using \helpref{AddConstraint}{wxcompositeshapeaddconstraint}.
+
+After children and shapes have been added, call \helpref{Recompute}{wxcompositeshaperecompute} which
+will return TRUE is the constraints could be satisfied, FALSE otherwise. If
+constraints have been correctly and consistently specified, this call will succeed.
+
+If there is more than one child, constraints must be specified: OGL cannot calculate
+the size and position of children otherwise. Don't assume that children will simply
+move relative to the parent without the use of constraints.
+
+To specify a constraint, you need three things:
+
+\begin{enumerate}\itemsep=0pt
+\item a constraint type, such as gyCONSTRAINT\_CENTRED\_VERTICALLY;
+\item a reference shape, with respect to which other shapes are going to be positioned - the\rtfsp
+{\it constraining} shape;
+\item a list of one or more shapes to be constrained: the {\it constrained} shapes.
+\end{enumerate}
+
+The constraining shape can be either the parent of the constrained shapes, or a sibling. The
+constrained shapes must all be siblings of each other.
+
+For an exhaustive list and description of the available constraint types, see the \helpref{wxOGLConstraint constructor}{wxoglconstraintconstr}.
+Note that most constraints operate in one dimension only (vertically or horizontally), so you will
+usually need to specify constraints in pairs.
+
+You can set the spacing between constraining and constrained shapes by
+calling \helpref{wxOGLConstraint::SetSpacing}{wxoglconstraintsetspacing}.
+
+Finally, a wxCompositeShape can have {\it divisions}, which are special child shapes of class
+wxDivisionShape (not to be confused with wxDividedShape). The purpose of this is to allow
+the composite to be divided into user-adjustable regions (divisions) into which other shapes
+can be dropped dynamically, given suitable application code. Divisons allow the child
+shapes to have an identity of their own - they can be manipulated independently of their container -
+but to behave as if they are contained with the division, moving with the parent shape.
+Divisions boundaries can themselves be moved using the mouse.
+
+To create an initial division, call \helpref{wxCompositeShape::MakeContainer}{wxcompositeshapemakecontainer}.
+Make further divisions by calling \helpref{wxDivisionShape::Divide}{wxdivisionshapedivide}.
+
diff --git a/docs/latex/ogl/up.gif b/docs/latex/ogl/up.gif
new file mode 100644 (file)
index 0000000..316d0d2
Binary files /dev/null and b/docs/latex/ogl/up.gif differ
diff --git a/include/wx/ogl/basic.h b/include/wx/ogl/basic.h
new file mode 100644 (file)
index 0000000..9c52020
--- /dev/null
@@ -0,0 +1,744 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        basic.h
+// Purpose:     Basic OGL classes and definitions
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_BASIC_H_
+#define _OGL_BASIC_H_
+
+#ifdef __GNUG__
+#pragma interface "basic.h"
+#endif
+
+#define OGL_VERSION     2.0
+
+#ifndef DEFAULT_MOUSE_TOLERANCE
+#define DEFAULT_MOUSE_TOLERANCE 3
+#endif
+
+// Edit these lines if you positively don't want PROLOGIO support
+#ifndef PROLOGIO
+#define PROLOGIO
+#endif
+
+// Key identifiers
+#define KEY_SHIFT 1
+#define KEY_CTRL  2
+
+// Arrow styles
+
+#define ARROW_NONE         0
+#define ARROW_END          1
+#define ARROW_BOTH         2
+#define ARROW_MIDDLE       3
+#define ARROW_START        4
+
+// Control point types
+// Rectangle and most other shapes
+#define CONTROL_POINT_VERTICAL   1
+#define CONTROL_POINT_HORIZONTAL 2
+#define CONTROL_POINT_DIAGONAL   3
+
+// Line
+#define CONTROL_POINT_ENDPOINT_TO 4
+#define CONTROL_POINT_ENDPOINT_FROM 5
+#define CONTROL_POINT_LINE       6
+
+// Types of formatting: can be combined in a bit list
+#define FORMAT_NONE           0
+                                // Left justification
+#define FORMAT_CENTRE_HORIZ   1
+                                // Centre horizontally
+#define FORMAT_CENTRE_VERT    2
+                                // Centre vertically
+#define FORMAT_SIZE_TO_CONTENTS 4
+                                // Resize shape to contents
+
+// Shadow mode
+#define SHADOW_NONE           0
+#define SHADOW_LEFT           1
+#define SHADOW_RIGHT          2
+
+/*
+ * Declare types
+ *
+ */
+
+#define SHAPE_BASIC           wxTYPE_USER + 1
+#define SHAPE_RECTANGLE       wxTYPE_USER + 2
+#define SHAPE_ELLIPSE         wxTYPE_USER + 3
+#define SHAPE_POLYGON         wxTYPE_USER + 4
+#define SHAPE_CIRCLE          wxTYPE_USER + 5
+#define SHAPE_LINE            wxTYPE_USER + 6
+#define SHAPE_DIVIDED_RECTANGLE wxTYPE_USER + 8
+#define SHAPE_COMPOSITE       wxTYPE_USER + 9
+#define SHAPE_CONTROL_POINT   wxTYPE_USER + 10
+#define SHAPE_DRAWN           wxTYPE_USER + 11
+#define SHAPE_DIVISION        wxTYPE_USER + 12
+#define SHAPE_LABEL_OBJECT    wxTYPE_USER + 13
+#define SHAPE_BITMAP          wxTYPE_USER + 14
+#define SHAPE_DIVIDED_OBJECT_CONTROL_POINT   wxTYPE_USER + 15
+
+#define OBJECT_REGION         wxTYPE_USER + 20
+
+#define OP_CLICK_LEFT  1
+#define OP_CLICK_RIGHT 2
+#define OP_DRAG_LEFT 4
+#define OP_DRAG_RIGHT 8
+
+#define OP_ALL (OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_LEFT | OP_DRAG_RIGHT)
+
+// Attachment modes
+#define ATTACHMENT_MODE_NONE        0
+#define ATTACHMENT_MODE_EDGE        1
+#define ATTACHMENT_MODE_BRANCHING   2
+
+// Sub-modes for branching attachment mode
+#define BRANCHING_ATTACHMENT_NORMAL 1
+#define BRANCHING_ATTACHMENT_BLOB   2
+
+class wxShapeTextLine;
+class wxShapeCanvas;
+class wxLineShape;
+class wxControlPoint;
+class wxShapeRegion;
+class wxShape;
+
+#ifdef PROLOGIO
+class WXDLLEXPORT wxExpr;
+class WXDLLEXPORT wxExprDatabase;
+#endif
+
+// Round up
+#define WXROUND(x) ( (long) (x + 0.5) )
+
+
+// logical function to use when drawing rubberband boxes, etc.
+#define OGLRBLF         wxINVERT
+
+
+
+class wxShapeEvtHandler: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxShapeEvtHandler)
+
+ public:
+  wxShapeEvtHandler(wxShapeEvtHandler *prev = NULL, wxShape *shape = NULL);
+  virtual ~wxShapeEvtHandler();
+
+  inline void SetShape(wxShape *sh) { m_handlerShape = sh; }
+  inline wxShape *GetShape() const { return m_handlerShape; }
+
+  inline void SetPreviousHandler(wxShapeEvtHandler* handler) { m_previousHandler = handler; }
+  inline wxShapeEvtHandler* GetPreviousHandler() const { return m_previousHandler; }
+
+  // This is called when the _shape_ is deleted.
+  virtual void OnDelete();
+  virtual void OnDraw(wxDC& dc);
+  virtual void OnDrawContents(wxDC& dc);
+  virtual void OnDrawBranches(wxDC& dc, bool erase = FALSE);
+  virtual void OnMoveLinks(wxDC& dc);
+  virtual void OnErase(wxDC& dc);
+  virtual void OnEraseContents(wxDC& dc);
+  virtual void OnHighlight(wxDC& dc);
+  virtual void OnLeftClick(double x, double y, int keys = 0, int attachment = 0);
+  virtual void OnLeftDoubleClick(double x, double y, int keys = 0, int attachment = 0);
+  virtual void OnRightClick(double x, double y, int keys = 0, int attachment = 0);
+  virtual void OnSize(double x, double y);
+  virtual bool OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display = TRUE);
+  virtual void OnMovePost(wxDC& dc, double x, double y, double old_x, double old_y, bool display = TRUE);
+
+  virtual void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0); // Erase if draw false
+  virtual void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnDragRight(bool draw, double x, double y, int keys=0, int attachment = 0); // Erase if draw false
+  virtual void OnBeginDragRight(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnEndDragRight(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnDrawOutline(wxDC& dc, double x, double y, double w, double h);
+  virtual void OnDrawControlPoints(wxDC& dc);
+  virtual void OnEraseControlPoints(wxDC& dc);
+  virtual void OnMoveLink(wxDC& dc, bool moveControlPoints = TRUE);
+
+  // Control points ('handles') redirect control to the actual shape, to make it easier
+  // to override sizing behaviour.
+  virtual void OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys=0, int attachment = 0); // Erase if draw false
+  virtual void OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+  virtual void OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+
+  virtual void OnBeginSize(double WXUNUSED(w), double WXUNUSED(h)) { }
+  virtual void OnEndSize(double WXUNUSED(w), double WXUNUSED(h)) { }
+
+  // Can override this to prevent or intercept line reordering.
+  virtual void OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering);
+
+  // Creates a copy of this event handler.
+  wxShapeEvtHandler *CreateNewCopy();
+
+  // Does the copy - override for new event handlers which might store
+  // app-specific data.
+  virtual void CopyData(wxShapeEvtHandler& copy) {};
+
+ private:
+  wxShapeEvtHandler*    m_previousHandler;
+  wxShape*              m_handlerShape;
+};
+
+class wxShape: public wxShapeEvtHandler
+{
+ DECLARE_ABSTRACT_CLASS(wxShape)
+
+ public:
+
+  wxShape(wxShapeCanvas *can = NULL);
+  virtual ~wxShape();
+  virtual void GetBoundingBoxMax(double *width, double *height);
+  virtual void GetBoundingBoxMin(double *width, double *height) = 0;
+  virtual bool GetPerimeterPoint(double x1, double y1,
+                                 double x2, double y2,
+                                 double *x3, double *y3);
+  inline wxShapeCanvas *GetCanvas() { return m_canvas; }
+  void SetCanvas(wxShapeCanvas *the_canvas);
+  virtual void AddToCanvas(wxShapeCanvas *the_canvas, wxShape *addAfter = NULL);
+  virtual void InsertInCanvas(wxShapeCanvas *the_canvas);
+
+  virtual void RemoveFromCanvas(wxShapeCanvas *the_canvas);
+  inline double GetX() const { return m_xpos; }
+  inline double GetY() const { return m_ypos; }
+  inline void SetX(double x) { m_xpos = x; }
+  inline void SetY(double y) { m_ypos = y; }
+
+  inline wxShape *GetParent() const { return m_parent; }
+  inline void SetParent(wxShape *p) { m_parent = p; }
+  wxShape *GetTopAncestor();
+  inline wxList& GetChildren() { return m_children; }
+
+  virtual void OnDraw(wxDC& dc);
+  virtual void OnDrawContents(wxDC& dc);
+  virtual void OnMoveLinks(wxDC& dc);
+  virtual void Unlink() { };
+  void SetDrawHandles(bool drawH);
+  inline bool GetDrawHandles() { return m_drawHandles; }
+  virtual void OnErase(wxDC& dc);
+  virtual void OnEraseContents(wxDC& dc);
+  virtual void OnHighlight(wxDC& dc);
+  virtual void OnLeftClick(double x, double y, int keys = 0, int attachment = 0);
+  virtual void OnLeftDoubleClick(double x, double y, int keys = 0, int attachment = 0) {}
+  virtual void OnRightClick(double x, double y, int keys = 0, int attachment = 0);
+  virtual void OnSize(double x, double y);
+  virtual bool OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display = TRUE);
+  virtual void OnMovePost(wxDC& dc, double x, double y, double old_x, double old_y, bool display = TRUE);
+
+  virtual void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0); // Erase if draw false
+  virtual void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnDragRight(bool draw, double x, double y, int keys=0, int attachment = 0); // Erase if draw false
+  virtual void OnBeginDragRight(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnEndDragRight(double x, double y, int keys=0, int attachment = 0);
+  virtual void OnDrawOutline(wxDC& dc, double x, double y, double w, double h);
+  virtual void OnDrawControlPoints(wxDC& dc);
+  virtual void OnEraseControlPoints(wxDC& dc);
+
+  virtual void OnBeginSize(double WXUNUSED(w), double WXUNUSED(h)) { }
+  virtual void OnEndSize(double WXUNUSED(w), double WXUNUSED(h)) { }
+
+  // Control points ('handles') redirect control to the actual shape, to make it easier
+  // to override sizing behaviour.
+  virtual void OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys=0, int attachment = 0); // Erase if draw false
+  virtual void OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+  virtual void OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+
+  virtual void MakeControlPoints();
+  virtual void DeleteControlPoints(wxDC *dc = NULL);
+  virtual void ResetControlPoints();
+
+  inline wxShapeEvtHandler *GetEventHandler() { return m_eventHandler; }
+  inline void SetEventHandler(wxShapeEvtHandler *handler) { m_eventHandler = handler; }
+
+  // Mandatory control points, e.g. the divided line moving handles
+  // should appear even if a child of the 'selected' image
+  virtual void MakeMandatoryControlPoints();
+  virtual void ResetMandatoryControlPoints();
+
+  inline virtual bool Recompute() { return TRUE; };
+  // Calculate size recursively, if size changes. Size might depend on children.
+  inline virtual void CalculateSize() { };
+  virtual void Select(bool select = TRUE, wxDC* dc = NULL);
+  virtual void SetHighlight(bool hi = TRUE, bool recurse = FALSE);
+  inline virtual bool IsHighlighted() const { return m_highlighted; };
+  virtual bool Selected() const;
+  virtual bool AncestorSelected() const;
+  void SetSensitivityFilter(int sens = OP_ALL, bool recursive = FALSE);
+  int GetSensitivityFilter() const { return m_sensitivity; }
+  void SetDraggable(bool drag, bool recursive = FALSE);
+  inline  void SetFixedSize(bool x, bool y) { m_fixedWidth = x; m_fixedHeight = y; };
+  inline  void GetFixedSize(bool *x, bool *y) const { *x = m_fixedWidth; *y = m_fixedHeight; };
+  inline  bool GetFixedWidth() const { return m_fixedWidth; }
+  inline  bool GetFixedHeight() const { return m_fixedHeight; }
+  inline  void SetSpaceAttachments(bool sp) { m_spaceAttachments = sp; };
+  inline  bool GetSpaceAttachments() const { return m_spaceAttachments; };
+  void SetShadowMode(int mode, bool redraw = FALSE);
+  inline int GetShadowMode() const { return m_shadowMode; }
+  virtual bool HitTest(double x, double y, int *attachment, double *distance);
+  inline void SetCentreResize(bool cr) { m_centreResize = cr; }
+  inline bool GetCentreResize() const { return m_centreResize; }
+  inline void SetMaintainAspectRatio(bool ar) { m_maintainAspectRatio = ar; }
+  inline bool GetMaintainAspectRatio() const { return m_maintainAspectRatio; }
+  inline wxList& GetLines() const { return (wxList&) m_lines; }
+  inline void SetDisableLabel(bool flag) { m_disableLabel = flag; }
+  inline bool GetDisableLabel() const { return m_disableLabel; }
+  inline void SetAttachmentMode(int mode) { m_attachmentMode = mode; }
+  inline int GetAttachmentMode() const { return m_attachmentMode; }
+  inline void SetId(long i) { m_id = i; }
+  inline long GetId() const { return m_id; }
+
+  void SetPen(wxPen *pen);
+  void SetBrush(wxBrush *brush);
+  inline void SetClientData(wxObject *client_data) { m_clientData = client_data; };
+  inline wxObject *GetClientData() const { return m_clientData; };
+
+  virtual void Show(bool show);
+  virtual bool IsShown() const { return m_visible; }
+  virtual void Move(wxDC& dc, double x1, double y1, bool display = TRUE);
+  virtual void Erase(wxDC& dc);
+  virtual void EraseContents(wxDC& dc);
+  virtual void Draw(wxDC& dc);
+  virtual void Flash();
+  virtual void MoveLinks(wxDC& dc);
+  virtual void DrawContents(wxDC& dc);  // E.g. for drawing text label
+  virtual void SetSize(double x, double y, bool recursive = TRUE);
+  virtual void SetAttachmentSize(double x, double y);
+  void Attach(wxShapeCanvas *can);
+  void Detach();
+
+  inline virtual bool Constrain() { return FALSE; } ;
+
+  void AddLine(wxLineShape *line, wxShape *other,
+               int attachFrom = 0, int attachTo = 0,
+               // The line ordering
+               int positionFrom = -1, int positionTo = -1);
+
+  // Return the zero-based position in m_lines of line.
+  int GetLinePosition(wxLineShape* line);
+
+  void AddText(const wxString& string);
+
+  inline wxPen *GetPen() const { return m_pen; }
+  inline wxBrush *GetBrush() const { return m_brush; }
+
+  /*
+   * Region-specific functions (defaults to the default region
+   * for simple objects
+   */
+
+  // Set the default, single region size to be consistent
+  // with the object size
+  void SetDefaultRegionSize();
+  virtual void FormatText(wxDC& dc, const wxString& s, int regionId = 0);
+  virtual void SetFormatMode(int mode, int regionId = 0);
+  virtual int GetFormatMode(int regionId = 0) const;
+  virtual void SetFont(wxFont *font, int regionId = 0);
+  virtual wxFont *GetFont(int regionId = 0) const;
+  virtual void SetTextColour(const wxString& colour, int regionId = 0);
+  virtual wxString GetTextColour(int regionId = 0) const;
+  virtual inline int GetNumberOfTextRegions() const { return m_regions.Number(); }
+  virtual void SetRegionName(const wxString& name, int regionId = 0);
+
+  // Get the name representing the region for this image alone.
+  // I.e. this image's region ids go from 0 to N-1.
+  // But the names might be "0.2.0", "0.2.1" etc. depending on position in composite.
+  // So the last digit represents the region Id, the others represent positions
+  // in composites.
+  virtual wxString GetRegionName(int regionId);
+
+  // Gets the region corresponding to the name, or -1 if not found.
+  virtual int GetRegionId(const wxString& name);
+
+  // Construct names for regions, unique even for children of a composite.
+  virtual void NameRegions(const wxString& parentName = "");
+
+  // Get list of regions
+  inline wxList& GetRegions() const { return (wxList&) m_regions; }
+
+  virtual void AddRegion(wxShapeRegion *region);
+
+  virtual void ClearRegions();
+
+  // Assign new ids to this image and children (if composite)
+  void AssignNewIds();
+
+  // Returns actual image (same as 'this' if non-composite) and region id
+  // for given region name.
+  virtual wxShape *FindRegion(const wxString& regionName, int *regionId);
+
+  // Finds all region names for this image (composite or simple).
+  // Supply empty string list.
+  virtual void FindRegionNames(wxStringList& list);
+
+  virtual void ClearText(int regionId = 0);
+  void RemoveLine(wxLineShape *line);
+
+#ifdef PROLOGIO
+  // I/O
+  virtual void WriteAttributes(wxExpr *clause);
+  virtual void ReadAttributes(wxExpr *clause);
+
+  // In case the object has constraints it needs to read in in a different pass
+  inline virtual void ReadConstraints(wxExpr *WXUNUSED(clause), wxExprDatabase *WXUNUSED(database)) { };
+  virtual void WriteRegions(wxExpr *clause);
+  virtual void ReadRegions(wxExpr *clause);
+#endif
+
+  // Attachment code
+  virtual bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+  virtual int GetNumberOfAttachments() const;
+  virtual bool AttachmentIsValid(int attachment) const;
+
+  // 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.
+  virtual bool GetAttachmentPositionEdge(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+
+  // Assuming the attachment lies along a vertical or horizontal line,
+  // calculate the position on that point.
+  virtual wxRealPoint CalcSimpleAttachment(const wxRealPoint& pt1, const wxRealPoint& pt2,
+    int nth, int noArcs, wxLineShape* line);
+
+  // 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.
+  virtual bool AttachmentSortTest(int attachmentPoint, const wxRealPoint& pt1, const wxRealPoint& pt2);
+
+  virtual void EraseLinks(wxDC& dc, int attachment = -1, bool recurse = FALSE);
+  virtual void DrawLinks(wxDC& dc, int attachment = -1, bool recurse = FALSE);
+
+  virtual bool MoveLineToNewAttachment(wxDC& dc, wxLineShape *to_move,
+                                       double x, double y);
+
+  // Reorders the lines coming into the node image at this attachment
+  // position, in the order in which they appear in linesToSort.
+  virtual void SortLines(int attachment, wxList& linesToSort);
+
+  // Apply an attachment ordering change
+  void ApplyAttachmentOrdering(wxList& ordering);
+
+  // Can override this to prevent or intercept line reordering.
+  virtual void OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering);
+
+  //// New banching attachment code, 24/9/98
+
+  //
+  //             |________|
+  //                 | <- root
+  //                 | <- neck
+  // shoulder1 ->---------<- shoulder2
+  //             | | | | |<- stem
+  //                      <- branching attachment point N-1
+
+  // This function gets the root point at the given attachment.
+  virtual wxRealPoint GetBranchingAttachmentRoot(int attachment);
+
+  // This function gets information about where branching connections go (calls GetBranchingAttachmentRoot)
+  virtual bool GetBranchingAttachmentInfo(int attachment, wxRealPoint& root, wxRealPoint& neck,
+    wxRealPoint& shoulder1, wxRealPoint& shoulder2);
+
+  // n is the number of the adjoining line, from 0 to N-1 where N is the number of lines
+  // at this attachment point.
+  // attachmentPoint is where the arc meets the stem, and stemPoint is where the stem meets the
+  // shoulder.
+  virtual bool GetBranchingAttachmentPoint(int attachment, int n, wxRealPoint& attachmentPoint,
+    wxRealPoint& stemPoint);
+
+  // Get the number of lines at this attachment position.
+  virtual int GetAttachmentLineCount(int attachment) const;
+
+  // Draw the branches (not the actual arcs though)
+  virtual void OnDrawBranches(wxDC& dc, int attachment, bool erase = FALSE);
+  virtual void OnDrawBranches(wxDC& dc, bool erase = FALSE);
+
+  // Branching attachment settings
+  inline void SetBranchNeckLength(int len) { m_branchNeckLength = len; }
+  inline int GetBranchNeckLength() const { return m_branchNeckLength; }
+
+  inline void SetBranchStemLength(int len) { m_branchStemLength = len; }
+  inline int GetBranchStemLength() const { return m_branchStemLength; }
+
+  inline void SetBranchSpacing(int len) { m_branchSpacing = len; }
+  inline int GetBranchSpacing() const { return m_branchSpacing; }
+
+  // Further detail on branching style, e.g. blobs on interconnections
+  inline void SetBranchStyle(long style) { m_branchStyle = style; }
+  inline long GetBranchStyle() const { return m_branchStyle; }
+
+  // Rotate the standard attachment point from physical (0 is always North)
+  // to logical (0 -> 1 if rotated by 90 degrees)
+  virtual int PhysicalToLogicalAttachment(int physicalAttachment) const;
+
+  // Rotate the standard attachment point from logical
+  // to physical (0 is always North)
+  virtual int LogicalToPhysicalAttachment(int logicalAttachment) const;
+
+  // 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.)
+  virtual bool Draggable() const { return TRUE; }
+
+  // Returns TRUE if image is a descendant of this image
+  bool HasDescendant(wxShape *image);
+
+  // Creates a copy of this shape.
+  wxShape *CreateNewCopy(bool resetMapping = TRUE, bool recompute = TRUE);
+
+  // Does the copying for this object
+  virtual void Copy(wxShape& copy);
+
+  // Does the copying for this object, including copying event
+  // handler data if any. Calls the virtual Copy function.
+  void CopyWithHandler(wxShape& copy);
+
+  // Rotate about the given axis by the given amount in radians.
+  virtual void Rotate(double x, double y, double theta);
+  virtual inline double GetRotation() const { return m_rotation; }
+
+  void ClearAttachments();
+
+  // Recentres all the text regions for this object
+  void Recentre(wxDC& dc);
+
+  // Clears points from a list of wxRealPoints
+  void ClearPointList(wxList& list);
+
+ private:
+  wxObject*             m_clientData;
+
+ protected:
+  wxShapeEvtHandler*    m_eventHandler;
+  bool                  m_formatted;
+  double                m_xpos, m_ypos;
+  wxPen*                m_pen;
+  wxBrush*              m_brush;
+  wxFont*               m_font;
+  wxColour*             m_textColour;
+  wxString              m_textColourName;
+  wxShapeCanvas*        m_canvas;
+  wxList                m_lines;
+  wxList                m_text;
+  wxList                m_controlPoints;
+  wxList                m_regions;
+  wxList                m_attachmentPoints;
+  bool                  m_visible;
+  bool                  m_disableLabel;
+  long                  m_id;
+  bool                  m_selected;
+  bool                  m_highlighted;      // Different from selected: user-defined highlighting,
+                                            // e.g. thick border.
+  double                m_rotation;
+  int                   m_sensitivity;
+  bool                  m_draggable;
+  int                   m_attachmentMode;   // 0 for no attachments, 1 if using normal attachments,
+                                            // 2 for branching attachments
+  bool                  m_spaceAttachments; // TRUE if lines at one side should be spaced
+  bool                  m_fixedWidth;
+  bool                  m_fixedHeight;
+  bool                  m_centreResize;    // Default is to resize keeping the centre constant (TRUE)
+  bool                  m_drawHandles;     // Don't draw handles if FALSE, usually TRUE
+  wxList                m_children;      // In case it's composite
+  wxShape*              m_parent;      // In case it's a child
+  int                   m_formatMode;
+  int                   m_shadowMode;
+  wxBrush*              m_shadowBrush;
+  int                   m_shadowOffsetX;
+  int                   m_shadowOffsetY;
+  int                   m_textMarginX;    // Gap between text and border
+  int                   m_textMarginY;
+  wxString              m_regionName;
+  bool                  m_maintainAspectRatio;
+  int                   m_branchNeckLength;
+  int                   m_branchStemLength;
+  int                   m_branchSpacing;
+  long                  m_branchStyle;
+};
+
+class wxPolygonShape: public wxShape
+{
+ DECLARE_DYNAMIC_CLASS(wxPolygonShape)
+ public:
+  wxPolygonShape();
+  ~wxPolygonShape();
+
+  // Takes a list of wxRealPoints; each point is an OFFSET from the centre.
+  // Deletes user's points in destructor.
+  virtual void Create(wxList *points);
+  virtual void ClearPoints();
+
+  void GetBoundingBoxMin(double *w, double *h);
+  void CalculateBoundingBox();
+  bool GetPerimeterPoint(double x1, double y1,
+                                 double x2, double y2,
+                                 double *x3, double *y3);
+  bool HitTest(double x, double y, int *attachment, double *distance);
+  void SetSize(double x, double y, bool recursive = TRUE);
+  void OnDraw(wxDC& dc);
+  void OnDrawOutline(wxDC& dc, double x, double y, double w, double h);
+
+  // Control points ('handles') redirect control to the actual shape, to make it easier
+  // to override sizing behaviour.
+  virtual void OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys=0, int attachment = 0);
+  virtual void OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+  virtual void OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+
+  // A polygon should have a control point at each vertex,
+  // with the option of moving the control points individually
+  // to change the shape.
+  void MakeControlPoints();
+  void ResetControlPoints();
+
+  // If we've changed the shape, must make the original
+  // points match the working points
+  void UpdateOriginalPoints();
+
+  // Add a control point after the given point
+  virtual void AddPolygonPoint(int pos = 0);
+
+  // Delete a control point
+  virtual void DeletePolygonPoint(int pos = 0);
+
+  // Recalculates the centre of the polygon
+  virtual void CalculatePolygonCentre();
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+
+  int GetNumberOfAttachments() const;
+  bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+  bool AttachmentIsValid(int attachment);
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  inline wxList *GetPoints() { return m_points; }
+
+  // Rotate about the given axis by the given amount in radians
+  virtual void Rotate(double x, double y, double theta);
+
+ private:
+  wxList*       m_points;
+  wxList*       m_originalPoints;
+  double        m_boundWidth;
+  double        m_boundHeight;
+  double        m_originalWidth;
+  double        m_originalHeight;
+};
+
+class wxRectangleShape: public wxShape
+{
+ DECLARE_DYNAMIC_CLASS(wxRectangleShape)
+ public:
+  wxRectangleShape(double w = 0.0, double h = 0.0);
+  void GetBoundingBoxMin(double *w, double *h);
+  bool GetPerimeterPoint(double x1, double y1,
+                                 double x2, double y2,
+                                 double *x3, double *y3);
+  void OnDraw(wxDC& dc);
+  void SetSize(double x, double y, bool recursive = TRUE);
+  void SetCornerRadius(double rad); // If > 0, rounded corners
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+
+  int GetNumberOfAttachments() const;
+  bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  inline double GetWidth() const { return m_width; }
+  inline double GetHeight() const { return m_height; }
+  inline void SetWidth(double w) { m_width = w; }
+  inline void SetHeight(double h) { m_height = h; }
+
+protected:
+  double m_width;
+  double m_height;
+  double m_cornerRadius;
+};
+
+class wxTextShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxTextShape)
+ public:
+  wxTextShape(double width = 0.0, double height = 0.0);
+
+  void OnDraw(wxDC& dc);
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+#endif
+
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+};
+
+class wxEllipseShape: public wxShape
+{
+ DECLARE_DYNAMIC_CLASS(wxEllipseShape)
+ public:
+  wxEllipseShape(double w = 0.0, double h = 0.0);
+
+  void GetBoundingBoxMin(double *w, double *h);
+  bool GetPerimeterPoint(double x1, double y1,
+                                 double x2, double y2,
+                                 double *x3, double *y3);
+
+  void OnDraw(wxDC& dc);
+  void SetSize(double x, double y, bool recursive = TRUE);
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+
+  int GetNumberOfAttachments() const;
+  bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  inline double GetWidth() const { return m_width; }
+  inline double GetHeight() const { return m_height; }
+
+  inline void SetWidth(double w) { m_width = w; }
+  inline void SetHeight(double h) { m_height = h; }
+
+protected:
+  double m_width;
+  double m_height;
+};
+
+class wxCircleShape: public wxEllipseShape
+{
+ DECLARE_DYNAMIC_CLASS(wxCircleShape)
+ public:
+  wxCircleShape(double w = 0.0);
+
+  bool GetPerimeterPoint(double x1, double y1,
+                                 double x2, double y2,
+                                 double *x3, double *y3);
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+};
+
+#endif
+ // _OGL_BASIC_H_
diff --git a/include/wx/ogl/basicp.h b/include/wx/ogl/basicp.h
new file mode 100644 (file)
index 0000000..aa10677
--- /dev/null
@@ -0,0 +1,223 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        basicp.h
+// Purpose:     Private OGL classes and definitions
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_BASICP_H_
+#define _OGL_BASICP_H_
+
+#ifdef __GNUG__
+#pragma interface "basicp.h"
+#endif
+
+#define CONTROL_POINT_SIZE       6
+
+class wxShapeTextLine: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxShapeTextLine)
+public:
+   wxShapeTextLine(double the_x = 0.0, double the_y = 0.0, const wxString& the_line = "");
+   ~wxShapeTextLine();
+
+   inline double GetX() const { return m_x; }
+   inline double GetY() const { return m_y; }
+
+   inline void SetX(double x) { m_x = x; }
+   inline void SetY(double y) { m_y = y; }
+
+   inline void SetText(const wxString& text) { m_line = text; }
+   inline wxString GetText() const { return m_line; }
+
+protected:
+   wxString     m_line;
+   double        m_x;
+   double        m_y;
+};
+
+class wxShape;
+class wxControlPoint: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxControlPoint)
+
+ friend class wxShapeEvtHandler;
+ friend class wxShape;
+
+ public:
+  wxControlPoint(wxShapeCanvas *the_canvas = NULL, wxShape *object = NULL, double size = 0.0, double the_xoffset = 0.0,
+    double the_yoffset = 0.0, int the_type = 0);
+  ~wxControlPoint();
+
+  void OnDraw(wxDC& dc);
+  void OnErase(wxDC& dc);
+  void OnDrawContents(wxDC& dc);
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+
+  bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+  int GetNumberOfAttachments() const;
+
+  inline void SetEraseObject(bool er) { m_eraseObject = er; }
+
+public:
+  int           m_type;
+  double         m_xoffset;
+  double         m_yoffset;
+  wxShape*      m_shape;
+  wxCursor*     m_oldCursor;
+  bool          m_eraseObject; // If TRUE, erases object before dragging handle.
+
+/*
+ * Store original top-left, bottom-right coordinates
+ * in case we're doing non-vertical resizing.
+ */
+  static double sm_controlPointDragStartX;
+  static double sm_controlPointDragStartY;
+  static double sm_controlPointDragStartWidth;
+  static double sm_controlPointDragStartHeight;
+  static double sm_controlPointDragEndWidth;
+  static double sm_controlPointDragEndHeight;
+  static double sm_controlPointDragPosX;
+  static double sm_controlPointDragPosY;
+};
+
+class wxPolygonShape;
+class wxPolygonControlPoint: public wxControlPoint
+{
+ DECLARE_DYNAMIC_CLASS(wxPolygonControlPoint)
+  friend class wxPolygonShape;
+ public:
+  wxPolygonControlPoint(wxShapeCanvas *the_canvas = NULL, wxShape *object = NULL, double size = 0.0, wxRealPoint *vertex = NULL,
+    double the_xoffset = 0.0, double the_yoffset = 0.0);
+  ~wxPolygonControlPoint();
+
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+
+  // Calculate what new size would be, at end of resize
+  virtual void CalculateNewSize(double x, double y);
+
+  // Get new size
+  inline wxRealPoint GetNewSize() const { return m_newSize; };
+
+public:
+  wxRealPoint*      m_polygonVertex;
+  wxRealPoint       m_originalSize;
+  double             m_originalDistance;
+  wxRealPoint       m_newSize;
+};
+
+/*
+ * Object regions.
+ * Every shape has one or more text regions with various
+ * properties. Not all of a region's properties will be used
+ * by a shape.
+ *
+ */
+
+class wxShapeRegion: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxShapeRegion)
+
+ public:
+  // Constructor
+  wxShapeRegion();
+  // Copy constructor
+  wxShapeRegion(wxShapeRegion& region);
+  // Destructor
+  ~wxShapeRegion();
+
+  // Accessors
+  inline void SetText(const wxString& s) { m_regionText = s; }
+  void SetFont(wxFont *f);
+  void SetMinSize(double w, double h);
+  void SetSize(double w, double h);
+  void SetPosition(double x, double y);
+  void SetProportions(double x, double y);
+  void SetFormatMode(int mode);
+  inline void SetName(const wxString& s) { m_regionName = s; };
+  void SetColour(const wxString& col); // Text colour
+
+  inline wxString GetText() const { return m_regionText; }
+  inline wxFont *GetFont() const { return m_font; }
+  inline void GetMinSize(double *x, double *y) const { *x = m_minWidth; *y = m_minHeight; }
+  inline void GetProportion(double *x, double *y) const { *x = m_regionProportionX; *y = m_regionProportionY; }
+  inline void GetSize(double *x, double *y) const { *x = m_width; *y = m_height; }
+  inline void GetPosition(double *xp, double *yp) const { *xp = m_x; *yp = m_y; }
+  inline int GetFormatMode() const { return m_formatMode; }
+  inline wxString GetName() const { return m_regionName; }
+  inline wxString GetColour() const { return m_textColour; }
+  wxColour *GetActualColourObject();
+  inline wxList& GetFormattedText() { return m_formattedText; }
+  inline wxString GetPenColour() const { return m_penColour; }
+  inline int GetPenStyle() const { return m_penStyle; }
+  inline void SetPenStyle(int style) { m_penStyle = style; m_actualPenObject = NULL; }
+  void SetPenColour(const wxString& col);
+  wxPen *GetActualPen();
+  inline double GetWidth() const { return m_width; }
+  inline double GetHeight() const { return m_height; }
+
+  void ClearText();
+
+public:
+  wxString              m_regionText;
+  wxList                m_formattedText;   // List of wxShapeTextLines
+  wxFont*               m_font;
+  double                 m_minHeight;        // If zero, hide region.
+  double                 m_minWidth;        // If zero, hide region.
+  double                 m_width;
+  double                 m_height;
+  double                 m_x;
+  double                 m_y;
+
+  double                 m_regionProportionX; // Proportion of total object size;
+                                             // -1.0 indicates equal proportion
+  double                 m_regionProportionY; // Proportion of total object size;
+                                             // -1.0 indicates equal proportion
+
+  int                   m_formatMode;        // FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT | FORMAT_NONE
+  wxString              m_regionName;
+  wxString              m_textColour;
+  wxColour*             m_actualColourObject; // For speed purposes
+
+  // New members for specifying divided rectangle division colour/style 30/6/94
+  wxString              m_penColour;
+  int                   m_penStyle;
+  wxPen*                m_actualPenObject;
+
+};
+
+/*
+ * User-defined attachment point
+ */
+
+class wxAttachmentPoint: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxAttachmentPoint)
+
+public:
+  inline wxAttachmentPoint()
+  {
+    m_id = 0; m_x = 0.0; m_y = 0.0;
+  }
+  inline wxAttachmentPoint(int id, double x, double y)
+  {
+    m_id = id; m_x = x; m_y = y;
+  }
+
+public:
+  int            m_id;           // Identifier
+  double         m_x;            // x offset from centre of object
+  double         m_y;            // y offset from centre of object
+};
+
+#endif
+  // _OGL_BASICP_H_
diff --git a/include/wx/ogl/bmpshape.h b/include/wx/ogl/bmpshape.h
new file mode 100644 (file)
index 0000000..8dd7cae
--- /dev/null
@@ -0,0 +1,53 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        bmpshape.h
+// Purpose:     wxBitmapShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_BITMAP_H_
+#define _OGL_BITMAP_H_
+
+#ifdef __GNUG__
+#pragma interface "bmpshape.h"
+#endif
+
+#include <wx/ogl/basic.h>
+
+class wxBitmapShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxBitmapShape)
+ public:
+  wxBitmapShape();
+  ~wxBitmapShape();
+
+  void OnDraw(wxDC& dc);
+
+#ifdef PROLOGIO
+  // I/O
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  void SetSize(double w, double h, bool recursive = TRUE);
+  inline wxBitmap& GetBitmap() const { return (wxBitmap&) m_bitmap; }
+  void SetBitmap(const wxBitmap& bm);
+  inline void SetFilename(const wxString& f) { m_filename = f; };
+  inline wxString GetFilename() const { return m_filename; }
+
+private:
+  wxBitmap      m_bitmap;
+  wxString      m_filename;
+};
+
+#endif
+  // _OGL_BITMAP_H_
+
+
diff --git a/include/wx/ogl/canvas.h b/include/wx/ogl/canvas.h
new file mode 100644 (file)
index 0000000..faeaaa3
--- /dev/null
@@ -0,0 +1,83 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        canvas.h
+// Purpose:     wxShapeCanvas
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_CANVAS_H_
+#define _OGL_CANVAS_H_
+
+#ifdef __GNUG__
+#pragma interface "canvas.h"
+#endif
+
+// Drag states
+#define NoDragging             0
+#define StartDraggingLeft      1
+#define ContinueDraggingLeft   2
+#define StartDraggingRight     3
+#define ContinueDraggingRight  4
+
+// When drag_count reaches 0, process drag message
+
+class wxDiagram;
+
+class wxShapeCanvas: public wxScrolledWindow
+{
+ DECLARE_DYNAMIC_CLASS(wxShapeCanvas)
+ public:
+  wxShapeCanvas(wxWindow *parent = NULL, wxWindowID id = -1, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
+               long style = wxBORDER | wxRETAINED);
+  ~wxShapeCanvas();
+
+  inline void SetDiagram(wxDiagram *diag) { m_shapeDiagram = diag; }
+  inline wxDiagram *GetDiagram() const { return m_shapeDiagram; }
+
+  virtual void OnLeftClick(double x, double y, int keys = 0);
+  virtual void OnRightClick(double x, double y, int keys = 0);
+
+  virtual void OnDragLeft(bool draw, double x, double y, int keys=0); // Erase if draw false
+  virtual void OnBeginDragLeft(double x, double y, int keys=0);
+  virtual void OnEndDragLeft(double x, double y, int keys=0);
+
+  virtual void OnDragRight(bool draw, double x, double y, int keys=0); // Erase if draw false
+  virtual void OnBeginDragRight(double x, double y, int keys=0);
+  virtual void OnEndDragRight(double x, double y, int keys=0);
+
+  // Find object for mouse click, of given wxClassInfo (NULL for any type).
+  // If notImage is non-NULL, don't find an object that is equal to or a descendant of notImage
+  virtual wxShape *FindShape(double x, double y, int *attachment, wxClassInfo *info = NULL, wxShape *notImage = NULL);
+  wxShape *FindFirstSensitiveShape(double x, double y, int *new_attachment, int op);
+  wxShape *FindFirstSensitiveShape1(wxShape *image, int op);
+  
+  // Redirect to wxDiagram object
+  virtual void AddShape(wxShape *object, wxShape *addAfter = NULL);
+  virtual void InsertShape(wxShape *object);
+  virtual void RemoveShape(wxShape *object);
+  virtual bool GetQuickEditMode();
+  virtual void Redraw(wxDC& dc);
+  void Snap(double *x, double *y);
+
+  // Events
+  void OnPaint(wxPaintEvent& event);
+  void OnMouseEvent(wxMouseEvent& event);
+
+ protected:
+  wxDiagram*        m_shapeDiagram;
+  int               m_dragState;
+  double             m_oldDragX, m_oldDragY;     // Previous drag coordinates
+  double             m_firstDragX, m_firstDragY; // INITIAL drag coordinates
+  bool              m_checkTolerance;           // Whether to check drag tolerance
+  wxShape*          m_draggedShape;
+  int               m_draggedAttachment;
+
+DECLARE_EVENT_TABLE()
+};
+
+#endif
+ // _OGL_CANVAS_H_
diff --git a/include/wx/ogl/composit.h b/include/wx/ogl/composit.h
new file mode 100644 (file)
index 0000000..bbbcc83
--- /dev/null
@@ -0,0 +1,238 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        composit.h
+// Purpose:     wxCompositeShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_COMPOSIT_H_
+#define _OGL_COMPOSIT_H_
+
+#ifdef __GNUG__
+#pragma interface "composit.h"
+#endif
+
+class wxDivisionShape;
+class wxOGLConstraint;
+
+/*
+ * A composite object is an invisible rectangle surrounding all children
+ *
+ */
+
+class wxCompositeShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxCompositeShape)
+public:
+
+  wxCompositeShape();
+  ~wxCompositeShape();
+
+  void OnDraw(wxDC& dc);
+  void OnDrawContents(wxDC& dc);
+  void OnErase(wxDC& dc);
+  bool OnMovePre(wxDC& dc, double x, double y, double oldX, double oldY, bool display = TRUE);
+  void OnDragLeft(bool draw, double x, double y, int keys, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys, int attachment = 0);
+
+  void OnRightClick(double x, double y, int keys, int attachment = 0);
+
+  void SetSize(double w, double h, bool recursive = TRUE);
+
+  // Returns TRUE if it settled down
+  bool Recompute();
+
+  // New members
+  void AddChild(wxShape *child, wxShape *addAfter = NULL);
+  void RemoveChild(wxShape *child);
+
+  wxOGLConstraint *AddConstraint(wxOGLConstraint *constraint);
+  wxOGLConstraint *AddConstraint(int type, wxShape *constraining, wxList& constrained);
+  wxOGLConstraint *AddConstraint(int type, wxShape *constraining, wxShape *constrained);
+
+  void DeleteConstraint(wxOGLConstraint *constraint);
+
+  // Delete constraints that involve this child.
+  void DeleteConstraintsInvolvingChild(wxShape *child);
+
+  // Remove the image from any constraints involving it, but DON'T
+  // remove any constraints.
+  void RemoveChildFromConstraints(wxShape *child);
+
+  // Find constraint, also returning actual composite the constraint was in,
+  // in case it had to find it recursively.
+  wxOGLConstraint *FindConstraint(long id, wxCompositeShape **actualComposite = NULL);
+
+  // Returns TRUE if something changed
+  bool Constrain();
+
+  // Make this composite into a container by creating one wxDivisionShape
+  void MakeContainer();
+
+  // Calculates size and position of composite object based on children
+  void CalculateSize();
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+  // In case the object has constraints it needs to read in in a different pass
+  void ReadConstraints(wxExpr *clause, wxExprDatabase *database);
+#endif
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  virtual wxDivisionShape *OnCreateDivision();
+
+  // Finds the image used to visualize a container. This is any child
+  // of the composite that is not in the divisions list.
+  wxShape *FindContainerImage();
+
+  // Returns TRUE if division is a descendant of this container
+  bool ContainsDivision(wxDivisionShape *division);
+
+  inline wxList& GetDivisions() const { return (wxList&) m_divisions; }
+  inline wxList& GetConstraints() const { return (wxList&) m_constraints; }
+
+protected:
+  double             m_oldX;
+  double             m_oldY;
+  wxList            m_constraints;
+  wxList            m_divisions; // In case it's a container
+};
+
+/*
+ * 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.
+ */
+#define DIVISION_SIDE_NONE      0
+#define DIVISION_SIDE_LEFT      1
+#define DIVISION_SIDE_TOP       2
+#define DIVISION_SIDE_RIGHT     3
+#define DIVISION_SIDE_BOTTOM    4
+
+class wxDivisionShape: public wxCompositeShape
+{
+ DECLARE_DYNAMIC_CLASS(wxDivisionShape)
+ public:
+
+  wxDivisionShape();
+  ~wxDivisionShape();
+
+  void OnDraw(wxDC& dc);
+  void OnDrawContents(wxDC& dc);
+  bool OnMovePre(wxDC& dc, double x, double y, double oldX, double oldY, bool display = TRUE);
+  void OnDragLeft(bool draw, double x, double y, int keys, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys, int attachment = 0);
+
+  void OnRightClick(double x, double y, int keys = 0, int attachment = 0);
+
+  // Don't want this kind of composite to resize its subdiagrams, so
+  // override composite's SetSize.
+  void SetSize(double w, double h, bool recursive = TRUE);
+
+  // Similarly for calculating size: it's fixed at whatever SetSize
+  // set it to, not in terms of children.
+  void CalculateSize();
+
+  void MakeControlPoints();
+  void ResetControlPoints();
+  void MakeMandatoryControlPoints();
+  void ResetMandatoryControlPoints();
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  // Divide horizontally (wxHORIZONTAL) or vertically (wxVERTICAL)
+  bool Divide(int direction);
+
+  // 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.
+  bool ResizeAdjoining(int side, double newPos, bool test);
+
+  // Adjust a side, returning FALSE if it's not physically possible.
+  bool AdjustLeft(double left, bool test);
+  bool AdjustTop(double top, bool test);
+  bool AdjustRight(double right, bool test);
+  bool AdjustBottom(double bottom, bool test);
+
+  // Edit style of left or top side
+  void EditEdge(int side);
+
+  // Popup menu
+  void PopupMenu(double x, double y);
+
+  inline void SetLeftSide(wxDivisionShape *shape) { m_leftSide = shape; }
+  inline void SetTopSide(wxDivisionShape *shape) { m_topSide = shape; }
+  inline void SetRightSide(wxDivisionShape *shape) { m_rightSide = shape; }
+  inline void SetBottomSide(wxDivisionShape *shape) { m_bottomSide = shape; }
+  inline wxDivisionShape *GetLeftSide() const { return m_leftSide; }
+  inline wxDivisionShape *GetTopSide() const { return m_topSide; }
+  inline wxDivisionShape *GetRightSide() const { return m_rightSide; }
+  inline wxDivisionShape *GetBottomSide() const { return m_bottomSide; }
+
+  inline void SetHandleSide(int side) { m_handleSide = side; }
+  inline int GetHandleSide() const { return m_handleSide; }
+
+  inline void SetLeftSidePen(wxPen *pen) { m_leftSidePen = pen; }
+  inline wxPen *GetLeftSidePen() const { return m_leftSidePen; }
+  inline void SetTopSidePen(wxPen *pen) { m_topSidePen = pen; }
+  inline wxPen *GetTopSidePen() const { return m_topSidePen; }
+
+  void SetLeftSideColour(const wxString& colour);
+  void SetTopSideColour(const wxString& colour);
+  void SetLeftSideStyle(const wxString& style);
+  void SetTopSideStyle(const wxString& style);
+
+  inline wxString GetLeftSideColour() const { return m_leftSideColour; }
+  inline wxString GetTopSideColour() const { return m_topSideColour; }
+  inline wxString GetLeftSideStyle() const { return m_leftSideStyle; }
+  inline wxString GetTopSideStyle() const { return m_topSideStyle; }
+
+ protected:
+  // Adjoining divisions. NULL indicates edge
+  // of container, and that side shouldn't be
+  // drawn.
+  wxDivisionShape*      m_leftSide;
+  wxDivisionShape*      m_rightSide;
+  wxDivisionShape*      m_topSide;
+  wxDivisionShape*      m_bottomSide;
+
+  int                   m_handleSide;       // Side at which handle is legal
+
+  wxPen*                m_leftSidePen;
+  wxPen*                m_topSidePen;
+  wxString              m_leftSideColour;
+  wxString              m_topSideColour;
+  wxString              m_leftSideStyle;
+  wxString              m_topSideStyle;
+};
+
+
+#define DIVISION_MENU_SPLIT_HORIZONTALLY    1
+#define DIVISION_MENU_SPLIT_VERTICALLY      2
+#define DIVISION_MENU_EDIT_LEFT_EDGE        3
+#define DIVISION_MENU_EDIT_TOP_EDGE         4
+#define DIVISION_MENU_EDIT_RIGHT_EDGE       5
+#define DIVISION_MENU_EDIT_BOTTOM_EDGE      6
+#define DIVISION_MENU_DELETE_ALL            7
+
+#endif
+ // _OGL_COMPOSIT_H_
diff --git a/include/wx/ogl/constrnt.h b/include/wx/ogl/constrnt.h
new file mode 100644 (file)
index 0000000..812aca1
--- /dev/null
@@ -0,0 +1,87 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        constrnt.h
+// Purpose:     OGL constraint definitions
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_CONSTRNT_H_
+#define _OGL_CONSTRNT_H_
+
+#ifdef __GNUG__
+#pragma interface "constrnt.h"
+#endif
+
+/*
+ * OGL Constraints
+ *
+ */
+
+class wxOGLConstraintType: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxOGLConstraintType)
+public:
+  wxOGLConstraintType(int type = 0, const wxString& name = "", const wxString& phrase = "");
+  ~wxOGLConstraintType();
+
+public:
+  int           m_type;     // E.g. gyCONSTRAINT_CENTRED_VERTICALLY
+  wxString      m_name;     // E.g. "Centre vertically"
+  wxString      m_phrase;   // E.g. "centred vertically with respect to", "left of"
+
+};
+
+extern wxList* wxOGLConstraintTypes;
+
+#define gyCONSTRAINT_CENTRED_VERTICALLY   1
+#define gyCONSTRAINT_CENTRED_HORIZONTALLY 2
+#define gyCONSTRAINT_CENTRED_BOTH         3
+#define gyCONSTRAINT_LEFT_OF              4
+#define gyCONSTRAINT_RIGHT_OF             5
+#define gyCONSTRAINT_ABOVE                6
+#define gyCONSTRAINT_BELOW                7
+#define gyCONSTRAINT_ALIGNED_TOP          8
+#define gyCONSTRAINT_ALIGNED_BOTTOM       9
+#define gyCONSTRAINT_ALIGNED_LEFT         10
+#define gyCONSTRAINT_ALIGNED_RIGHT        11
+
+// Like aligned, but with the objects centred on the respective edge
+// of the reference object.
+#define gyCONSTRAINT_MIDALIGNED_TOP       12
+#define gyCONSTRAINT_MIDALIGNED_BOTTOM    13
+#define gyCONSTRAINT_MIDALIGNED_LEFT      14
+#define gyCONSTRAINT_MIDALIGNED_RIGHT     15
+
+class wxOGLConstraint: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxOGLConstraint)
+ public:
+  wxOGLConstraint() { m_xSpacing = 0.0; m_ySpacing = 0.0; m_constraintType = 0; m_constraintName = ""; m_constraintId = 0;
+                        m_constrainingObject = NULL; }
+  wxOGLConstraint(int type, wxShape *constraining, wxList& constrained);
+  ~wxOGLConstraint();
+
+  // Returns TRUE if anything changed
+  bool Evaluate();
+  inline void SetSpacing(double x, double y) { m_xSpacing = x; m_ySpacing = y; };
+  bool Equals(double a, double b);
+
+  double         m_xSpacing;
+  double         m_ySpacing;
+  int           m_constraintType;
+  wxString      m_constraintName;
+  long          m_constraintId;
+  wxShape*      m_constrainingObject;
+  wxList        m_constrainedObjects;
+
+};
+
+void OGLInitializeConstraintTypes();
+void OGLCleanUpConstraintTypes();
+
+#endif
+ // _OGL_CONSTRNT_H_
diff --git a/include/wx/ogl/divided.h b/include/wx/ogl/divided.h
new file mode 100644 (file)
index 0000000..f840463
--- /dev/null
@@ -0,0 +1,75 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        divided.h
+// Purpose:     wxDividedShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_DIVIDED_H_
+#define _OGL_DIVIDED_H_
+
+#ifdef __GNUG__
+#pragma interface "basic.h"
+#endif
+
+/*
+ * Definition of a region
+ *
+ */
+
+/*
+ * Box divided into horizontal regions
+ *
+ */
+
+extern wxFont *g_oglNormalFont;
+class wxDividedShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxDividedShape)
+
+ public:
+  wxDividedShape(double w = 0.0, double h = 0.0);
+  ~wxDividedShape();
+
+  void OnDraw(wxDC& dc);
+  void OnDrawContents(wxDC& dc);
+
+  void SetSize(double w, double h, bool recursive = TRUE);
+
+  void MakeControlPoints();
+  void ResetControlPoints();
+
+  void MakeMandatoryControlPoints();
+  void ResetMandatoryControlPoints();
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+
+  void Copy(wxShape &copy);
+
+  // Set all region sizes according to proportions and
+  // this object total size
+  void SetRegionSizes();
+
+  // Edit region colours/styles
+  void EditRegions();
+
+  // Attachment points correspond to regions in the divided box
+  bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+  bool AttachmentIsValid(int attachment);
+  int GetNumberOfAttachments() const;
+
+  // Invoke editor on CTRL-right click
+  void OnRightClick(double x, double y, int keys = 0, int attachment = 0);
+};
+
+#endif
+    // _OGL_DIVIDED_H_
+
diff --git a/include/wx/ogl/drawn.h b/include/wx/ogl/drawn.h
new file mode 100644 (file)
index 0000000..913275a
--- /dev/null
@@ -0,0 +1,227 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        drawn.h
+// Purpose:     wxDrawnShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_DRAWN_H_
+#define _OGL_DRAWN_H_
+
+#ifdef __GNUG__
+#pragma interface "drawn.h"
+#endif
+
+#include <wx/ogl/basic.h>
+
+#define oglMETAFLAGS_OUTLINE         1
+#define oglMETAFLAGS_ATTACHMENTS     2
+
+class wxDrawnShape;
+class wxPseudoMetaFile: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxPseudoMetaFile)
+ public:
+  wxPseudoMetaFile();
+  wxPseudoMetaFile(wxPseudoMetaFile& mf);
+  ~wxPseudoMetaFile();
+
+  void Draw(wxDC& dc, double xoffset, double yoffset);
+
+#ifdef PROLOGIO
+  void WriteAttributes(wxExpr *clause, int whichAngle);
+  void ReadAttributes(wxExpr *clause, int whichAngle);
+#endif
+
+  void Clear();
+
+  void Copy(wxPseudoMetaFile& copy);
+
+  void Scale(double sx, double sy);
+  void ScaleTo(double w, double h); // Scale to fit size
+  void Translate(double x, double y);
+
+  // Rotate about the given axis by theta radians from the x axis.
+  void Rotate(double x, double y, double theta);
+
+  bool LoadFromMetaFile(char *filename, double *width, double *height);
+
+  void GetBounds(double *minX, double *minY, double *maxX, double *maxY);
+
+  // Calculate size from current operations
+  void CalculateSize(wxDrawnShape* shape);
+
+  inline wxList& GetOutlineColours() const { return (wxList&) m_outlineColours; }
+  inline wxList& GetFillColours() const { return (wxList&) m_fillColours; }
+  inline void SetRotateable(bool rot) { m_rotateable = rot; }
+  inline bool GetRotateable() const { return m_rotateable; }
+
+  inline void SetSize(double w, double h) { m_width = w; m_height = h; }
+
+  inline void SetFillBrush(wxBrush* brush) { m_fillBrush = brush; }
+  inline wxBrush* GetFillBrush() const { return m_fillBrush; }
+
+  inline void SetOutlinePen(wxPen* pen) { m_outlinePen = pen; }
+  inline wxPen* GetOutlinePen() const { return m_outlinePen; }
+
+  inline void SetOutlineOp(int op) { m_outlineOp = op; }
+  inline int GetOutlineOp() const { return m_outlineOp; }
+
+  inline wxList& GetOps() const { return (wxList&) m_ops; }
+
+  // Is this a valid (non-empty) metafile?
+  inline bool IsValid() const { return (m_ops.Number() > 0); }
+
+public:
+  /// Set of functions for drawing into a pseudo metafile.
+  /// They use integers, but doubles are used internally for accuracy
+  /// when scaling.
+
+  virtual void DrawLine(const wxPoint& pt1, const wxPoint& pt2);
+  virtual void DrawRectangle(const wxRect& rect);
+  virtual void DrawRoundedRectangle(const wxRect& rect, double radius);
+  virtual void DrawArc(const wxPoint& centrePt, const wxPoint& startPt, const wxPoint& endPt);
+  virtual void DrawEllipticArc(const wxRect& rect, double startAngle, double endAngle);
+  virtual void DrawEllipse(const wxRect& rect);
+  virtual void DrawPoint(const wxPoint& pt);
+  virtual void DrawText(const wxString& text, const wxPoint& pt);
+  virtual void DrawLines(int n, wxPoint pts[]);
+  // flags:
+  // oglMETAFLAGS_OUTLINE: will be used for drawing the outline and
+  //                       also drawing lines/arrows at the circumference.
+  // oglMETAFLAGS_ATTACHMENTS: will be used for initialising attachment points at
+  //                       the vertices (perhaps a rare case...)
+  virtual void DrawPolygon(int n, wxPoint pts[], int flags = 0);
+  virtual void DrawSpline(int n, wxPoint pts[]);
+
+  virtual void SetClippingRect(const wxRect& rect);
+  virtual void DestroyClippingRect();
+
+  virtual void SetPen(wxPen* pen, bool isOutline = FALSE);     // TODO: eventually, just store GDI object attributes, not actual
+  virtual void SetBrush(wxBrush* brush, bool isFill = FALSE);  // pens/brushes etc.
+  virtual void SetFont(wxFont* font);
+  virtual void SetTextColour(const wxColour& colour);
+  virtual void SetBackgroundColour(const wxColour& colour);
+  virtual void SetBackgroundMode(int mode);
+
+public:
+  bool              m_rotateable;
+  double            m_width;
+  double            m_height;
+  wxList            m_ops; // List of drawing operations (see drawnp.h)
+  wxList            m_gdiObjects; // List of pens, brushes and fonts for this object.
+  int               m_outlineOp;  // The op representing the outline, if any
+
+  // Pen/brush specifying outline/fill colours
+  // to override operations.
+  wxPen*            m_outlinePen;
+  wxBrush*          m_fillBrush;
+  wxList            m_outlineColours; // List of the GDI operations that comprise the outline
+  wxList            m_fillColours; // List of the GDI operations that fill the shape
+  double             m_currentRotation;
+};
+
+#define oglDRAWN_ANGLE_0        0
+#define oglDRAWN_ANGLE_90       1
+#define oglDRAWN_ANGLE_180      2
+#define oglDRAWN_ANGLE_270      3
+
+class wxDrawnShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxDrawnShape)
+ public:
+  wxDrawnShape();
+  ~wxDrawnShape();
+
+  void OnDraw(wxDC& dc);
+
+#ifdef PROLOGIO
+  // I/O
+  void WriteAttributes(wxExpr *clause);
+  void ReadAttributes(wxExpr *clause);
+#endif
+
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  void Scale(double sx, double sy);
+  void Translate(double x, double y);
+  // Rotate about the given axis by theta radians from the x axis.
+  void Rotate(double x, double y, double theta);
+
+  // Get current rotation
+  inline double GetRotation() const { return m_rotation; }
+
+  void SetSize(double w, double h, bool recursive = TRUE);
+  bool LoadFromMetaFile(char *filename);
+
+  inline void SetSaveToFile(bool save) { m_saveToFile = save; }
+  inline wxPseudoMetaFile& GetMetaFile(int which = 0) const { return (wxPseudoMetaFile&) m_metafiles[which]; }
+
+  void OnDrawOutline(wxDC& dc, double x, double y, double w, double h);
+
+  // Get the perimeter point using the special outline op, if there is one,
+  // otherwise use default wxRectangleShape scheme
+  bool GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3);
+
+  /// Set of functions for drawing into a pseudo metafile.
+  /// They use integers, but doubles are used internally for accuracy
+  /// when scaling.
+
+  virtual void DrawLine(const wxPoint& pt1, const wxPoint& pt2);
+  virtual void DrawRectangle(const wxRect& rect);
+  virtual void DrawRoundedRectangle(const wxRect& rect, double radius);
+  virtual void DrawArc(const wxPoint& centrePt, const wxPoint& startPt, const wxPoint& endPt);
+  virtual void DrawEllipticArc(const wxRect& rect, double startAngle, double endAngle);
+  virtual void DrawEllipse(const wxRect& rect);
+  virtual void DrawPoint(const wxPoint& pt);
+  virtual void DrawText(const wxString& text, const wxPoint& pt);
+  virtual void DrawLines(int n, wxPoint pts[]);
+  virtual void DrawPolygon(int n, wxPoint pts[], int flags = 0);
+  virtual void DrawSpline(int n, wxPoint pts[]);
+
+  virtual void SetClippingRect(const wxRect& rect);
+  virtual void DestroyClippingRect();
+
+  virtual void SetDrawnPen(wxPen* pen, bool isOutline = FALSE);     // TODO: eventually, just store GDI object attributes, not actual
+  virtual void SetDrawnBrush(wxBrush* brush, bool isFill = FALSE);  // pens/brushes etc.
+  virtual void SetDrawnFont(wxFont* font);
+  virtual void SetDrawnTextColour(const wxColour& colour);
+  virtual void SetDrawnBackgroundColour(const wxColour& colour);
+  virtual void SetDrawnBackgroundMode(int mode);
+
+  // Set the width/height according to the shapes in the metafile.
+  // Call this after drawing into the shape.
+  inline void CalculateSize() { m_metafiles[m_currentAngle].CalculateSize(this); }
+
+  inline void DrawAtAngle(int angle) { m_currentAngle = angle; };
+
+  inline int GetAngle() const { return m_currentAngle; }
+
+// Implementation
+protected:
+  // Which metafile do we use now? Based on current rotation and validity
+  // of metafiles.
+  int DetermineMetaFile(double rotation);
+
+private:
+  // One metafile for each 90 degree rotation (or just a single one).
+  wxPseudoMetaFile      m_metafiles[4];
+
+  // Don't save all wxDrawnShape metafiles to file: sometimes
+  // we take the metafile data from a symbol library.
+  bool                  m_saveToFile;
+
+  // Which angle are we using/drawing into?
+  int                   m_currentAngle;
+};
+
+#endif
+    // _DRAWN_H_
+
diff --git a/include/wx/ogl/drawnp.h b/include/wx/ogl/drawnp.h
new file mode 100644 (file)
index 0000000..e819600
--- /dev/null
@@ -0,0 +1,205 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        drawnp.h
+// Purpose:     Private header for wxDrawnShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_DRAWNP_H_
+#define _OGL_DRAWNP_H_
+
+#ifdef __GNUG__
+#pragma interface "drawnp.h"
+#endif
+
+#include <wx/ogl/drawn.h>
+
+/*
+ * Drawing operations
+ *
+ */
+#define  DRAWOP_SET_PEN             1
+#define  DRAWOP_SET_BRUSH           2
+#define  DRAWOP_SET_FONT            3
+#define  DRAWOP_SET_TEXT_COLOUR     4
+#define  DRAWOP_SET_BK_COLOUR       5
+#define  DRAWOP_SET_BK_MODE         6
+#define  DRAWOP_SET_CLIPPING_RECT   7
+#define  DRAWOP_DESTROY_CLIPPING_RECT 8
+
+/*
+#define  DRAWOP_CREATE_PEN          10
+#define  DRAWOP_CREATE_BRUSH        11
+#define  DRAWOP_CREATE_FONT         12
+*/
+
+#define  DRAWOP_DRAW_LINE           20
+#define  DRAWOP_DRAW_POLYLINE       21
+#define  DRAWOP_DRAW_POLYGON        22
+#define  DRAWOP_DRAW_RECT           23
+#define  DRAWOP_DRAW_ROUNDED_RECT   24
+#define  DRAWOP_DRAW_ELLIPSE        25
+#define  DRAWOP_DRAW_POINT          26
+#define  DRAWOP_DRAW_ARC            27
+#define  DRAWOP_DRAW_TEXT           28
+#define  DRAWOP_DRAW_SPLINE         29
+#define  DRAWOP_DRAW_ELLIPTIC_ARC   30
+
+/*
+ * Base, virtual class
+ *
+ */
+class wxDrawOp: public wxObject
+{
+public:
+  inline wxDrawOp(int theOp) { m_op = theOp; }
+  inline ~wxDrawOp() {}
+  inline virtual void Scale(double xScale, double yScale) {};
+  inline virtual void Translate(double x, double y) {};
+  inline virtual void Rotate(double x, double y, double theta, double sinTheta, double cosTheta) {};
+  virtual void Do(wxDC& dc, double xoffset, double yoffset) = 0;
+  virtual wxDrawOp *Copy(wxPseudoMetaFile *newImage) = 0;
+  virtual wxExpr *WriteExpr(wxPseudoMetaFile *image) = 0;
+  virtual void ReadExpr(wxPseudoMetaFile *image, wxExpr *expr) = 0;
+
+  inline int GetOp() const { return m_op; }
+
+  // Draw an outline using the current operation. By default, return FALSE (not drawn)
+  virtual bool OnDrawOutline(wxDC& dc, double x, double y, double w, double h,
+    double oldW, double oldH) { return FALSE; }
+
+  // Get the perimeter point using this data
+  virtual bool GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3,
+                                     double xOffset, double yOffset,
+                                     int attachmentMode)
+  { return FALSE; }
+
+protected:
+  int           m_op;
+
+};
+
+/*
+ * Set font, brush, text colour
+ *
+ */
+class wxOpSetGDI: public wxDrawOp
+{
+ public:
+  wxOpSetGDI(int theOp, wxPseudoMetaFile *theImage, int theGdiIndex, int theMode = 0);
+  void Do(wxDC& dc, double xoffset, double yoffset);
+  wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+  wxExpr *WriteExpr(wxPseudoMetaFile *image);
+  void ReadExpr(wxPseudoMetaFile *image, wxExpr *expr);
+
+public:
+  int               m_mode;
+  int               m_gdiIndex;
+  wxPseudoMetaFile* m_image;
+  unsigned char     m_r;
+  unsigned char     m_g;
+  unsigned char     m_b;
+};
+
+/*
+ * Set/destroy clipping
+ *
+ */
+class wxOpSetClipping: public wxDrawOp
+{
+public:
+  wxOpSetClipping(int theOp, double theX1, double theY1, double theX2, double theY2);
+  void Do(wxDC& dc, double xoffset, double yoffset);
+  void Scale(double xScale, double yScale);
+  void Translate(double x, double y);
+  wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+  wxExpr *WriteExpr(wxPseudoMetaFile *image);
+  void ReadExpr(wxPseudoMetaFile *image, wxExpr *expr);
+
+public:
+  double     m_x1;
+  double     m_y1;
+  double     m_x2;
+  double     m_y2;
+};
+
+/*
+ * Draw line, rectangle, rounded rectangle, ellipse, point, arc, text
+ *
+ */
+class wxOpDraw: public wxDrawOp
+{
+ public:
+  wxOpDraw(int theOp, double theX1, double theY1, double theX2, double theY2,
+         double radius = 0.0, char *s = NULL);
+  ~wxOpDraw();
+  void Do(wxDC& dc, double xoffset, double yoffset);
+  void Scale(double scaleX, double scaleY);
+  void Translate(double x, double y);
+  void Rotate(double x, double y, double theta, double sinTheta, double cosTheta);
+  wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+  wxExpr *WriteExpr(wxPseudoMetaFile *image);
+  void ReadExpr(wxPseudoMetaFile *image, wxExpr *expr);
+
+public:
+  double     m_x1;
+  double     m_y1;
+  double     m_x2;
+  double     m_y2;
+  double     m_x3;
+  double     m_y3;
+  double     m_radius;
+  char*     m_textString;
+
+};
+
+/*
+ * Draw polyline, spline, polygon
+ *
+ */
+
+class wxOpPolyDraw: public wxDrawOp
+{
+public:
+  wxOpPolyDraw(int theOp, int n, wxRealPoint *thePoints);
+  ~wxOpPolyDraw();
+  void Do(wxDC& dc, double xoffset, double yoffset);
+  void Scale(double scaleX, double scaleY);
+  void Translate(double x, double y);
+  void Rotate(double x, double y, double theta, double sinTheta, double cosTheta);
+  wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+  wxExpr *WriteExpr(wxPseudoMetaFile *image);
+  void ReadExpr(wxPseudoMetaFile *image, wxExpr *expr);
+
+  // Draw an outline using the current operation.
+  virtual bool OnDrawOutline(wxDC& dc, double x, double y, double w, double h,
+    double oldW, double oldH);
+
+  // Get the perimeter point using this data
+  bool GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3,
+                                     double xOffset, double yOffset,
+                                     int attachmentMode);
+
+public:
+  wxRealPoint*  m_points;
+  int           m_noPoints;
+  
+};
+
+#endif
+ // _OGL_DRAWNP_H_
+
+
diff --git a/include/wx/ogl/lines.h b/include/wx/ogl/lines.h
new file mode 100644 (file)
index 0000000..9619bf7
--- /dev/null
@@ -0,0 +1,296 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        lines.h
+// Purpose:     wxLineShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_LINES_H_
+#define _OGL_LINES_H_
+
+#ifdef __GNUG__
+#pragma interface "lines.h"
+#endif
+
+class wxLabelShape;
+class wxPseudoMetaFile;
+class wxLineControlPoint;
+/*
+ * Arcs with multiple arrowheads
+ *
+ */
+
+// Types of arrowhead
+// (i) Built-in
+#define ARROW_HOLLOW_CIRCLE   1
+#define ARROW_FILLED_CIRCLE   2
+#define ARROW_ARROW           3
+#define ARROW_SINGLE_OBLIQUE  4
+#define ARROW_DOUBLE_OBLIQUE  5
+// (ii) Custom
+#define ARROW_METAFILE        20
+
+// Position of arrow on line
+#define ARROW_POSITION_START  0
+#define ARROW_POSITION_END    1
+#define ARROW_POSITION_MIDDLE 2
+
+// Line alignment flags
+// Vertical by default
+#define LINE_ALIGNMENT_HORIZ              1
+#define LINE_ALIGNMENT_VERT               0
+#define LINE_ALIGNMENT_TO_NEXT_HANDLE     2
+#define LINE_ALIGNMENT_NONE               0
+
+class wxArrowHead: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxArrowHead)
+
+ public:
+  wxArrowHead(WXTYPE type = 0, int end = 0, double size = 0.0, double dist = 0.0, const wxString& name = "", wxPseudoMetaFile *mf = NULL,
+            long arrowId = -1);
+  ~wxArrowHead();
+  wxArrowHead(wxArrowHead& toCopy);
+
+  inline WXTYPE _GetType() const { return m_arrowType; }
+  inline int GetPosition() const { return m_arrowEnd; }
+  inline void SetPosition(int pos) { m_arrowEnd = pos; }
+  inline double GetXOffset() const { return m_xOffset; }
+  inline double GetYOffset() const { return m_yOffset; }
+  inline double GetSpacing() const { return m_spacing; }
+  inline double GetSize() const { return m_arrowSize; }
+  inline wxString GetName() const { return m_arrowName; }
+  inline void SetXOffset(double x) { m_xOffset = x; }
+  inline void SetYOffset(double y) { m_yOffset = y; }
+  inline wxPseudoMetaFile *GetMetaFile() const { return m_metaFile; }
+  inline long GetId() const { return m_id; }
+  inline int GetArrowEnd() const { return m_arrowEnd; }
+  inline double GetArrowSize() const { return m_arrowSize; }
+  void SetSize(double size);
+  inline void SetSpacing(double sp) { m_spacing = sp; }
+
+ protected:
+  WXTYPE            m_arrowType;
+  int               m_arrowEnd;         // Position on line
+  double             m_xOffset;          // Distance from arc start or end, w.r.t. point on arrowhead
+                                        // nearest start or end. If zero, use default spacing.
+  double             m_yOffset;          // vertical offset (w.r.t. a horizontal line). Normally zero.
+  double             m_spacing;          // Spacing from the last arrowhead
+  double             m_arrowSize;        // Length of arrowhead
+  wxString          m_arrowName;        // Name of arrow
+  bool              m_saveToFile;       // TRUE if we want to save custom arrowheads to file.
+  wxPseudoMetaFile* m_metaFile;         // Pseudo metafile if this is a custom arrowhead
+  long              m_id;               // identifier
+};
+
+// Line object
+class wxLabelShape;
+class wxLineShape: public wxShape
+{
+ DECLARE_DYNAMIC_CLASS(wxLineShape)
+
+ public:
+  wxLineShape();
+  ~wxLineShape();
+
+  // Called when a connected object has moved, to move the link to
+  // correct position
+  // moveControlPoints must be disabled when a control point is being
+  // dragged.
+  void OnMoveLink(wxDC& dc, bool moveControlPoints = TRUE);
+  bool OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display = TRUE);
+  void OnDraw(wxDC& dc);
+  void OnDrawContents(wxDC& dc);
+  void OnDrawControlPoints(wxDC& dc);
+  void OnEraseControlPoints(wxDC& dc);
+  void OnErase(wxDC& dc);
+  virtual bool OnMoveControlPoint(int WXUNUSED(which), double WXUNUSED(x), double WXUNUSED(y)) { return FALSE; }
+  virtual bool OnMoveMiddleControlPoint(wxDC& dc, wxLineControlPoint* lpt, const wxRealPoint& pt);
+  virtual bool OnLabelMovePre(wxDC& dc, wxLabelShape* labelShape, double x, double y, double old_x, double old_y, bool display);
+  void OnDrawOutline(wxDC& dc, double x, double y, double w, double h);
+  void GetBoundingBoxMin(double *w, double *h);
+  void FormatText(wxDC& dc, const wxString& s, int regionId = 0);
+  virtual void SetEnds(double x1, double y1, double x2, double y2);
+  virtual void GetEnds(double *x1, double *y1, double *x2, double *y2);
+  inline virtual wxShape *GetFrom() { return m_from; }
+  inline virtual wxShape *GetTo() { return m_to; }
+  inline virtual int GetAttachmentFrom() { return m_attachmentFrom; }
+  inline virtual int GetAttachmentTo() { return m_attachmentTo; }
+
+  virtual void SetFrom(wxShape *object);
+  virtual void SetTo(wxShape *object);
+  virtual void DrawArrows(wxDC& dc);
+
+  // 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.
+  void FindLineEndPoints(double *fromX, double *fromY, double *toX, double *toY);
+
+  // Format one region at this position
+  void DrawRegion(wxDC& dc, wxShapeRegion *region, double x, double y);
+
+  // Erase one region at this position
+  void EraseRegion(wxDC& dc, wxShapeRegion *region, double x, double y);
+
+  // Get the reference point for a label. Region x and y
+  // are offsets from this.
+  // position is 0 (middle), 1 (start), 2 (end)
+  void GetLabelPosition(int position, double *x, double *y);
+
+  // Can override this to create a different class of label shape
+  virtual wxLabelShape* OnCreateLabelShape(wxLineShape *parent = NULL, wxShapeRegion *region = NULL, double w = 0.0, double h = 0.0);
+
+  // Straighten verticals and horizontals
+  virtual void Straighten(wxDC* dc = NULL);
+
+  // Not implemented
+  inline void SetMaintainStraightLines(bool flag) { m_maintainStraightLines = flag; }
+  inline bool GetMaintainStraightLines() const { return m_maintainStraightLines; }
+
+  // Make handle control points
+  void MakeControlPoints();
+  void ResetControlPoints();
+
+  // Make a given number of control points
+  virtual void MakeLineControlPoints(int n);
+  virtual wxNode *InsertLineControlPoint(wxDC* dc);
+  virtual bool DeleteLineControlPoint();
+  virtual void Initialise();
+  inline wxList *GetLineControlPoints() { return m_lineControlPoints; }
+
+  // Override dragging behaviour - don't want to be able to drag lines!
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+
+  // Control points ('handles') redirect control to the actual shape, to make it easier
+  // to override sizing behaviour.
+  virtual void OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys=0, int attachment = 0);
+  virtual void OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+  virtual void OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys=0, int attachment = 0);
+
+  // Override select, to create/delete temporary label-moving objects
+  void Select(bool select = TRUE, wxDC* dc = NULL);
+
+  // Set to spline (TRUE) or line (FALSE)
+  inline void SetSpline(bool spl) { m_isSpline = spl; }
+  inline bool IsSpline() const { return m_isSpline; }
+
+  void Unlink();
+  void SetAttachments(int from_attach, int to_attach);
+  inline void SetAttachmentFrom(int attach) { m_attachmentFrom = attach; }
+  inline void SetAttachmentTo(int attach) { m_attachmentTo = attach; }
+
+  bool HitTest(double x, double y, int *attachment, double *distance);
+
+#ifdef PROLOGIO
+  // I/O
+  virtual void WriteAttributes(wxExpr *clause);
+  virtual void ReadAttributes(wxExpr *clause);
+#endif
+
+  virtual void FindNth(wxShape *image, int *nth, int *no_arcs, bool incoming);
+
+  // Find which position we're talking about at this (x, y).
+  // Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END
+  int FindLinePosition(double x, double y);
+
+  // 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.)
+  virtual bool Draggable() const { return FALSE; }
+
+  // Does the copying for this object
+  void Copy(wxShape& copy);
+
+  // Add an arrowhead.
+  wxArrowHead *AddArrow(WXTYPE type, int end = ARROW_POSITION_END,
+                double arrowSize = 10.0, double xOffset = 0.0, const wxString& name = "",
+                wxPseudoMetaFile *mf = NULL, long arrowId = -1);
+
+  // 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.
+  bool AddArrowOrdered(wxArrowHead *arrow, wxList& referenceList, int end);
+
+  // Delete arrowhead(s)
+  void ClearArrowsAtPosition(int end = -1);
+  bool ClearArrow(const wxString& name);
+  wxArrowHead *FindArrowHead(int position, const wxString& name);
+  wxArrowHead *FindArrowHead(long arrowId);
+  bool DeleteArrowHead(int position, const wxString& name);
+  bool DeleteArrowHead(long arrowId);
+  void DrawArrow(wxDC& dc, wxArrowHead *arrow, double xOffset, bool proportionalOffset);
+  inline void SetIgnoreOffsets(bool ignore) { m_ignoreArrowOffsets = ignore; }
+  inline wxList& GetArrows() const { return (wxList&) m_arcArrows; }
+
+  // Find horizontal width for drawing a line with
+  // arrows in minimum space. Assume arrows at
+  // END only
+  double FindMinimumWidth();
+
+  // Set alignment flags. ALIGNMENT NOT IMPLEMENTED.
+  void SetAlignmentOrientation(bool isEnd, bool isHoriz);
+  void SetAlignmentType(bool isEnd, int alignType);
+  bool GetAlignmentOrientation(bool isEnd);
+  int GetAlignmentType(bool isEnd);
+
+  // Find next control point in line after the start/end point
+  // (depending on whether the node object is at start or end)
+  wxRealPoint *GetNextControlPoint(wxShape *nodeObject);
+  inline bool IsEnd(wxShape *nodeObject) const { return (m_to == nodeObject); }
+
+private:
+  bool              m_erasing;              // flag to say whether we're erasing or drawing
+                                            // this line (really so metafiles can draw a
+                                            // blank rectangle)
+  bool              m_ignoreArrowOffsets;   // Don't always want to draw arrowhead offsets
+                                            // because they may not work on tool palettes (for example)
+  bool              m_isSpline;
+  bool              m_maintainStraightLines;
+
+protected:
+  // Temporary list of line segment orientations
+  // so we know what direction the line is supposed to be dog-legging
+  // in. The values are integer: 0 for vertical, 1 for horizontal.
+  wxList            m_lineOrientations;
+
+  // Temporary pointers for start, middle and end label editing objects
+  // (active only when the line is selected)
+  wxLabelShape*     m_labelObjects[3];
+
+  // These define the segmented line - not to be confused with temporary control
+  // points which appear when object is selected (although in this case they'll
+  // probably be the same)
+  wxList*           m_lineControlPoints;
+
+  double            m_arrowSpacing; // Separation between adjacent arrows
+
+  wxShape*          m_to;
+  wxShape*          m_from;
+
+  int               m_attachmentTo;   // Attachment point at one end
+  int               m_attachmentFrom; // Attachment point at other end
+
+  // Alignment flags
+  int               m_alignmentStart;
+  int               m_alignmentEnd;
+
+  wxList            m_arcArrows;
+
+};
+
+#endif
+    // _OGL_LINES_H_
diff --git a/include/wx/ogl/linesp.h b/include/wx/ogl/linesp.h
new file mode 100644 (file)
index 0000000..383fe7f
--- /dev/null
@@ -0,0 +1,89 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        linesp.h
+// Purpose:     Lines private header file
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_LINESP_H_
+#define _OGL_LINESP_H_
+
+#ifdef __GNUG__
+#pragma interface "linesp.h"
+#endif
+
+class wxLineShape;
+class wxLineControlPoint: public wxControlPoint
+{
+  DECLARE_DYNAMIC_CLASS(wxLineControlPoint)
+  friend class wxLineShape;
+ public:
+
+  wxLineControlPoint(wxShapeCanvas *the_canvas = NULL, wxShape *object = NULL, double size = 0.0,
+     double x = 0.0, double y = 0.0, int the_type = 0);
+  ~wxLineControlPoint();
+
+  void OnDraw(wxDC& dc);
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+
+  // Obsolete (left-dragging now moves attachment point to new relative position OR new
+  // attachment id)
+#if 0
+  void OnDragRight(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragRight(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragRight(double x, double y, int keys=0, int attachment = 0);
+#endif
+
+public:
+
+  int           m_type;
+  wxRealPoint*  m_point;  // Line point
+  wxRealPoint   m_originalPos;
+
+};
+
+/*
+ * Temporary arc label object
+ */
+class wxLabelShape: public wxRectangleShape
+{
+  DECLARE_DYNAMIC_CLASS(wxLabelShape)
+
+ public:
+  wxLabelShape(wxLineShape *parent = NULL, wxShapeRegion *region = NULL, double w = 0.0, double h = 0.0);
+  ~wxLabelShape();
+
+  void OnDraw(wxDC& dc);
+  void OnDrawContents(wxDC& dc);
+  void OnLeftClick(double x, double y, int keys = 0, int attachment = 0);
+  void OnRightClick(double x, double y, int keys = 0, int attachment = 0);
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+  bool OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display = TRUE);
+
+public:
+  wxLineShape*      m_lineShape;
+  wxShapeRegion*    m_shapeRegion;
+
+};
+
+/*
+ * Get the point on the given line (x1, y1) (x2, y2)
+ * distance 'length' along from the end,
+ * returned values in x and y
+ */
+
+void GetPointOnLine(double x1, double y1, double x2, double y2,
+                    double length, double *x, double *y);
+
+#endif
+    // _OGL_LINESP_H_
+
diff --git a/include/wx/ogl/mfutils.h b/include/wx/ogl/mfutils.h
new file mode 100644 (file)
index 0000000..b070e17
--- /dev/null
@@ -0,0 +1,211 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        mfutils.h
+// Purpose:     Metafile utilities: reading a placeable metafile independently
+//              of Windows.
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MFUTILS_H_
+#define _MFUTILS_H_
+
+#ifdef __GNUG__
+#pragma interface "mfutils.h"
+#endif
+
+#include <wx/metafile.h>
+
+#ifndef GetRValue
+#define GetRValue(rgb)     ((unsigned char)(rgb))
+#define GetGValue(rgb)      ((unsigned char)(((int)(rgb)) >> 8))
+#define GetBValue(rgb)     ((unsigned char)((rgb)>>16))
+#endif
+
+/* Metafile Functions */
+/* Win32s/Borland need these macros, although META_SETBKCOLOR is defined */
+#if 1 // !defined(META_SETBKCOLOR) // || defined(WIN32)
+
+#define META_SETBKCOLOR                     0x0201
+#define META_SETBKMODE              0x0102
+#define META_SETMAPMODE                     0x0103
+#define META_SETROP2                0x0104
+#define META_SETRELABS              0x0105
+#define META_SETPOLYFILLMODE        0x0106
+#define META_SETSTRETCHBLTMODE      0x0107
+#define META_SETTEXTCHAREXTRA       0x0108
+#define META_SETTEXTCOLOR           0x0209
+#define META_SETTEXTJUSTIFICATION    0x020A
+#define META_SETWINDOWORG           0x020B
+#define META_SETWINDOWEXT           0x020C
+#define META_SETVIEWPORTORG         0x020D
+#define META_SETVIEWPORTEXT         0x020E
+#define META_OFFSETWINDOWORG        0x020F
+#define META_SCALEWINDOWEXT         0x0410
+#define META_OFFSETVIEWPORTORG      0x0211
+#define META_SCALEVIEWPORTEXT       0x0412
+#define META_LINETO                 0x0213
+#define META_MOVETO                 0x0214
+#define META_EXCLUDECLIPRECT        0x0415
+#define META_INTERSECTCLIPRECT      0x0416
+#define META_ARC                    0x0817
+#define META_ELLIPSE                0x0418
+#define META_FLOODFILL              0x0419
+#define META_PIE                    0x081A
+#define META_RECTANGLE              0x041B
+#define META_ROUNDRECT              0x061C
+#define META_PATBLT                 0x061D
+#define META_SAVEDC                 0x001E
+#define META_SETPIXEL               0x041F
+#define META_OFFSETCLIPRGN          0x0220
+#define META_TEXTOUT                0x0521
+#define META_BITBLT                 0x0922
+#define META_STRETCHBLT                     0x0B23
+#define META_POLYGON                0x0324
+#define META_POLYLINE               0x0325
+#define META_ESCAPE                 0x0626
+#define META_RESTOREDC              0x0127
+#define META_FILLREGION                     0x0228
+#define META_FRAMEREGION            0x0429
+#define META_INVERTREGION           0x012A
+#define META_PAINTREGION            0x012B
+#define META_SELECTCLIPREGION       0x012C
+#define META_SELECTOBJECT           0x012D
+#define META_SETTEXTALIGN           0x012E
+#define META_DRAWTEXT               0x062F
+
+#define        META_CHORD                   0x0830
+#define        META_SETMAPPERFLAGS          0x0231
+#define        META_EXTTEXTOUT              0x0a32
+#define        META_SETDIBTODEV             0x0d33
+#define        META_SELECTPALETTE           0x0234
+#define        META_REALIZEPALETTE          0x0035
+#define        META_ANIMATEPALETTE          0x0436
+#define        META_SETPALENTRIES           0x0037
+#define        META_POLYPOLYGON             0x0538
+#define        META_RESIZEPALETTE           0x0139
+
+#define        META_DIBBITBLT               0x0940
+#define        META_DIBSTRETCHBLT           0x0b41
+#define        META_DIBCREATEPATTERNBRUSH   0x0142
+#define        META_STRETCHDIB              0x0f43
+
+#define META_EXTFLOODFILL           0x0548
+
+#define META_RESETDC                0x014C
+#define META_STARTDOC               0x014D
+#define META_STARTPAGE              0x004F
+#define META_ENDPAGE                0x0050
+#define META_ABORTDOC               0x0052
+#define META_ENDDOC                 0x005E
+
+#define        META_DELETEOBJECT            0x01f0
+
+#define        META_CREATEPALETTE           0x00f7
+#define META_CREATEBRUSH            0x00F8
+#define META_CREATEPATTERNBRUSH             0x01F9
+#define META_CREATEPENINDIRECT      0x02FA
+#define META_CREATEFONTINDIRECT             0x02FB
+#define META_CREATEBRUSHINDIRECT     0x02FC
+#define META_CREATEBITMAPINDIRECT    0x02FD
+#define META_CREATEBITMAP           0x06FE
+#define META_CREATEREGION           0x06FF
+
+/* Background Modes */
+#define TRANSPARENT     1
+#define OPAQUE          2
+
+/* Pen Styles */
+#define PS_SOLID           0
+#define PS_DASH             1
+#define PS_DOT              2
+#define PS_DASHDOT          3
+#define PS_DASHDOTDOT       4
+#define PS_NULL            5
+#define PS_INSIDEFRAME             6
+
+/* PitchAndFamily family values (high 4 bits) */
+/* Win32s/Borland don't need this */
+#ifndef FF_DONTCARE // !defined(__BORLANDC__) && !defined(WIN32)
+#define FF_DONTCARE         0x00
+#define FF_ROMAN            0x10
+#define FF_SWISS            0x20
+#define FF_MODERN           0x30
+#define FF_SCRIPT           0x40
+#define FF_DECORATIVE       0x50
+#endif
+
+/* Brush Styles */
+#define BS_SOLID           0
+#define BS_NULL                    1
+#define BS_HOLLOW          BS_NULL
+#define BS_HATCHED         2
+#define BS_PATTERN         3
+#define BS_INDEXED         4
+#define        BS_DIBPATTERN       5
+
+/* Hatch Styles */
+#define HS_HORIZONTAL       0
+#define HS_VERTICAL         1
+#define HS_FDIAGONAL        2
+#define HS_BDIAGONAL        3
+#define HS_CROSS            4
+#define HS_DIAGCROSS        5
+
+#endif // metafile functions
+
+class wxMetaRecord: public wxObject
+{
+  public:
+  int metaFunction;
+  long param1;
+  long param2;
+  long param3;
+  long param4;
+  long param5;
+  long param6;
+  long param7;
+  long param8;
+  char *stringParam;
+  wxRealPoint *points;
+  
+  wxMetaRecord(int fun)
+  {
+    metaFunction = fun; points = NULL; stringParam = NULL;
+    param1 = 0;
+  }
+  ~wxMetaRecord(void);
+};
+
+class wxXMetaFile: public wxObject
+{
+ public:
+  double lastX;
+  double lastY;
+  bool ok;
+
+  double left;
+  double top;
+  double right;
+  double bottom;
+
+  wxList metaRecords;
+  wxList gdiObjects; // List of wxMetaRecord objects created with Create...,
+                     // referenced by position in list by SelectObject
+  wxXMetaFile(char *file = NULL);
+  ~wxXMetaFile(void);
+  
+  // After this is called, the metafile cannot be used for anything
+  // since it is now owned by the clipboard.
+  bool SetClipboard(int width = 0, int height = 0);
+
+  bool Play(wxDC *dc);
+  inline bool Ok(void) const { return ok; }
+  bool ReadFile(char *file);
+};
+
+#endif
+ // _MFUTILS_H_
diff --git a/include/wx/ogl/misc.h b/include/wx/ogl/misc.h
new file mode 100644 (file)
index 0000000..4e6abfb
--- /dev/null
@@ -0,0 +1,113 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        misc.h
+// Purpose:     Miscellaneous utilities for OGL
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_MISC_H_
+#define _OGL_MISC_H_
+
+#ifdef __GNUG__
+#pragma interface "misc.h"
+#endif
+
+// List to use when copying objects; may need to associate elements of new objects
+// with elements of old objects, e.g. when copying constraint.s
+extern wxList oglObjectCopyMapping;
+
+/*
+ * TEXT FORMATTING FUNCTIONS
+ *
+ */
+
+// Centres the given list of wxShapeTextLine strings in the given box
+// (changing the positions in situ). Doesn't actually draw into the DC.
+void oglCentreText(wxDC& dc, wxList *text, double m_xpos, double m_ypos,
+                double width, double height,
+                int formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT);
+
+// Given a string, returns a list of strings that fit within the given
+// width of box. Height is ignored.
+wxStringList *oglFormatText(wxDC& dc, const wxString& text, double width, double height, int formatMode = 0);
+
+// Centres the list of wxShapeTextLine strings, doesn't clip.
+// Doesn't actually draw into the DC.
+void oglCentreTextNoClipping(wxDC& dc, wxList *text_list,
+                              double m_xpos, double m_ypos, double width, double height);
+
+// Gets the maximum width and height of the given list of wxShapeTextLines.
+void oglGetCentredTextExtent(wxDC& dc, wxList *text_list,
+                              double m_xpos, double m_ypos, double width, double height,
+                              double *actual_width, double *actual_height);
+
+// Actually draw the preformatted list of wxShapeTextLines.
+void oglDrawFormattedText(wxDC& context, wxList *text_list,
+                       double m_xpos, double m_ypos, double width, double height,
+                       int formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT);
+
+// Give it a list of points, finds the centre.
+void oglFindPolylineCentroid(wxList *points, double *x, double *y);
+
+void oglCheckLineIntersection(double x1, double y1, double x2, double y2, 
+                             double x3, double y3, double x4, double y4,
+                             double *ratio1, double *ratio2);
+
+void oglFindEndForPolyline(double n, double xvec[], double yvec[], 
+                           double x1, double y1, double x2, double y2, double *x3, double *y3);
+
+
+void oglFindEndForBox(double width, double height, 
+                      double x1, double y1,         // Centre of box (possibly)
+                      double x2, double y2,         // other end of line
+                      double *x3, double *y3);      // End on box edge
+
+void oglFindEndForCircle(double radius, 
+                         double x1, double y1,  // Centre of circle
+                         double x2, double y2,  // Other end of line
+                         double *x3, double *y3);
+
+void oglGetArrowPoints(double x1, double y1, double x2, double y2,
+                      double length, double width,
+                      double *tip_x, double *tip_y,
+                      double *side1_x, double *side1_y,
+                      double *side2_x, double *side2_y);
+
+/*
+ * Given an ellipse and endpoints of a line, returns the point at which
+ * the line touches the ellipse in values x4, y4.
+ * This function assumes that the centre of the ellipse is at x1, y1, and the
+ * ellipse has a width of a1 and a height of b1. It also assumes you are
+ * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
+ * This function calculates the x,y coordinates of the intersection point of 
+ * the arc with the ellipse.
+ * Author: Ian Harrison
+ */
+
+void oglDrawArcToEllipse(double x1, double y1, double a1, double b1, double x2, double y2, double x3, double y3,
+  double *x4, double *y4);
+
+bool oglRoughlyEqual(double val1, double val2, double tol = 0.00001);
+
+extern wxFont*          g_oglNormalFont;
+extern wxPen*           g_oglBlackPen;
+extern wxPen*           g_oglWhiteBackgroundPen;
+extern wxPen*           g_oglTransparentPen;
+extern wxBrush*         g_oglWhiteBackgroundBrush;
+extern wxPen*           g_oglBlackForegroundPen;
+extern wxCursor*        g_oglBullseyeCursor;
+
+extern wxFont*          oglMatchFont(int point_size);
+
+extern wxString         oglColourToHex(const wxColour& colour);
+extern wxColour         oglHexToColour(const wxString& hex);
+extern void             oglDecToHex(unsigned int dec, char *buf);
+extern unsigned int     oglHexToDec(char* buf);
+
+
+#endif
+ // _OGL_MISC_H_
diff --git a/include/wx/ogl/ogl.h b/include/wx/ogl/ogl.h
new file mode 100644 (file)
index 0000000..a9eb003
--- /dev/null
@@ -0,0 +1,27 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        ogl.h
+// Purpose:     OGL main include
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_OGL_H_
+#define _OGL_OGL_H_
+
+#include <wx/ogl/basic.h>      // Basic shapes
+#include <wx/ogl/lines.h>      // Lines and splines
+#include <wx/ogl/divided.h>    // Vertically-divided rectangle
+#include <wx/ogl/composit.h>   // Composite images
+#include <wx/ogl/canvas.h>     // wxShapeCanvas for displaying objects
+#include <wx/ogl/ogldiag.h>    // wxDiagram
+
+// TODO: replace with wxModule implementation
+extern void wxOGLInitialize();
+extern void wxOGLCleanUp();
+
+#endif
+ // _OGL_OGL_H_
diff --git a/include/wx/ogl/ogldiag.h b/include/wx/ogl/ogldiag.h
new file mode 100644 (file)
index 0000000..e06e124
--- /dev/null
@@ -0,0 +1,124 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        ogldiag.h
+// Purpose:     OGL - wxDiagram class
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGL_OGLDIAG_H_
+#define _OGL_OGLDIAG_H_
+
+#ifdef __GNUG__
+#pragma interface "ogldiag.h"
+#endif
+
+#include "basic.h"
+
+class wxDiagram: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxDiagram)
+
+public:
+
+  wxDiagram();
+  virtual ~wxDiagram();
+
+  void SetCanvas(wxShapeCanvas *can);
+
+  inline wxShapeCanvas *GetCanvas() const { return m_diagramCanvas; }
+
+  virtual void Redraw(wxDC& dc);
+  virtual void Clear(wxDC& dc);
+  virtual void DrawOutline(wxDC& dc, double x1, double y1, double x2, double y2);
+
+  // Add object to end of object list (if addAfter is NULL)
+  // or just after addAfter.
+  virtual void AddShape(wxShape *object, wxShape *addAfter = NULL);
+
+  // Add object to front of object list
+  virtual void InsertShape(wxShape *object);
+
+  void SetSnapToGrid(bool snap);
+  void SetGridSpacing(double spacing);
+  inline double GetGridSpacing() const { return m_gridSpacing; }
+  inline bool GetSnapToGrid() const { return m_snapToGrid; }
+  void Snap(double *x, double *y);
+
+  inline void SetQuickEditMode(bool qem) { m_quickEditMode = qem; }
+  inline bool GetQuickEditMode() const { return m_quickEditMode; }
+
+  virtual void RemoveShape(wxShape *object);
+  virtual void RemoveAllShapes();
+  virtual void DeleteAllShapes();
+  virtual void ShowAll(bool show);
+
+  // Find a shape by its id
+  wxShape* FindShape(long id) const;
+
+  inline void SetMouseTolerance(int tol) { m_mouseTolerance = tol; }
+  inline int GetMouseTolerance() const { return m_mouseTolerance; }
+  inline wxList *GetShapeList() const { return m_shapeList; }
+  inline int GetCount() const { return m_shapeList->Number(); }
+
+  // Make sure all text that should be centred, is centred.
+  void RecentreAll(wxDC& dc);
+
+#ifdef PROLOGIO
+  virtual bool SaveFile(const wxString& filename);
+  virtual bool LoadFile(const wxString& filename);
+
+  virtual void ReadNodes(wxExprDatabase& database);
+  virtual void ReadLines(wxExprDatabase& database);
+  virtual void ReadContainerGeometry(wxExprDatabase& database);
+
+  // Allow for modifying file
+  virtual bool OnDatabaseLoad(wxExprDatabase& db);
+  virtual bool OnDatabaseSave(wxExprDatabase& db);
+  virtual bool OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr);
+  virtual bool OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr);
+  virtual bool OnHeaderSave(wxExprDatabase& db, wxExpr& expr);
+  virtual bool OnHeaderLoad(wxExprDatabase& db, wxExpr& expr);
+#endif
+
+protected:
+  wxShapeCanvas*        m_diagramCanvas;
+  bool                  m_quickEditMode;
+  bool                  m_snapToGrid;
+  double                m_gridSpacing;
+  int                   m_mouseTolerance;
+  wxList*               m_shapeList;
+};
+
+class wxLineCrossing: public wxObject
+{
+public:
+    wxLineCrossing() { m_lineShape1 = NULL; m_lineShape2 = NULL; }
+    wxRealPoint     m_pt1; // First line
+    wxRealPoint     m_pt2;
+    wxRealPoint     m_pt3; // Second line
+    wxRealPoint     m_pt4;
+    wxRealPoint     m_intersect;
+    wxLineShape*    m_lineShape1;
+    wxLineShape*    m_lineShape2;
+};
+
+class wxLineCrossings: public wxObject
+{
+public:
+    wxLineCrossings();
+    ~wxLineCrossings();
+
+    void FindCrossings(wxDiagram& diagram);
+    void DrawCrossings(wxDiagram& diagram, wxDC& dc);
+    void ClearCrossings();
+
+public:
+    wxList  m_crossings;
+};
+
+#endif
+ // _OGL_OGLDIAG_H_
diff --git a/samples/ogl/ogledit/Makefile b/samples/ogl/ogledit/Makefile
new file mode 100644 (file)
index 0000000..1ed2886
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# File:                Makefile
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:   (c) 2000 Julian Smart
+#
+# Makefile for OGL demo (GTK version)
+#
+# This makefile requires wxWindows/GTK to be 
+# installed (possibly using "make install")
+# on your system.
+#
+
+CPP = gcc
+CC = gcc
+WXCONFIG=../../../../wx-config
+WXINCLUDE=-I../../../../include
+WXLIB=-L../../../../lib
+
+OBJECTS=ogledit.o palette.o doc.o view.o
+
+ogledit: $(OBJECTS)
+       $(CPP) -o ogledit $(OBJECTS) `$(WXCONFIG) --libs` $(WXLIB) -logl
+
+ogledit.o: ogledit.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c ogledit.cpp
+
+palette.o: palette.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c palette.cpp
+
+doc.o: doc.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c doc.cpp
+
+view.o: view.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c view.cpp
+
+clean: 
+       rm -f *.o ogledit
diff --git a/samples/ogl/ogledit/bitmaps/arrow.bmp b/samples/ogl/ogledit/bitmaps/arrow.bmp
new file mode 100644 (file)
index 0000000..d406ceb
Binary files /dev/null and b/samples/ogl/ogledit/bitmaps/arrow.bmp differ
diff --git a/samples/ogl/ogledit/bitmaps/arrow.xpm b/samples/ogl/ogledit/bitmaps/arrow.xpm
new file mode 100644 (file)
index 0000000..d3807cb
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static char *arrow_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"22 22 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c #c0c0c0",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"######################",
+"######################",
+"######################",
+"######################",
+"######################",
+"####### ##############",
+"#######  #############",
+"#######   ############",
+"#######    ###########",
+"#######     ##########",
+"#######      #########",
+"#######       ########",
+"#######        #######",
+"#######     ##########",
+"#######  #  ##########",
+"####### ###  #########",
+"###########  #########",
+"############  ########",
+"############  ########",
+"######################",
+"######################",
+"######################"
+};
diff --git a/samples/ogl/ogledit/bitmaps/tool1.bmp b/samples/ogl/ogledit/bitmaps/tool1.bmp
new file mode 100644 (file)
index 0000000..cb1760d
Binary files /dev/null and b/samples/ogl/ogledit/bitmaps/tool1.bmp differ
diff --git a/samples/ogl/ogledit/bitmaps/tool1.xpm b/samples/ogl/ogledit/bitmaps/tool1.xpm
new file mode 100644 (file)
index 0000000..89a313d
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static char *tool1_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"22 22 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c #c0c0c0",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"######################",
+"######################",
+"#                   ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"#                   ##",
+"######################",
+"######################",
+"######################"
+};
diff --git a/samples/ogl/ogledit/bitmaps/tool2.bmp b/samples/ogl/ogledit/bitmaps/tool2.bmp
new file mode 100644 (file)
index 0000000..a18facc
Binary files /dev/null and b/samples/ogl/ogledit/bitmaps/tool2.bmp differ
diff --git a/samples/ogl/ogledit/bitmaps/tool2.xpm b/samples/ogl/ogledit/bitmaps/tool2.xpm
new file mode 100644 (file)
index 0000000..612dbf0
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static char *tool2_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"22 22 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c #c0c0c0",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"######################",
+"######################",
+"####             #####",
+"### ::::::::::::: ####",
+"## ::::::::::::::: ###",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"## ::::::::::::::: ###",
+"### ::::::::::::: ####",
+"####             #####",
+"######################",
+"######################",
+"######################"
+};
diff --git a/samples/ogl/ogledit/bitmaps/tool3.bmp b/samples/ogl/ogledit/bitmaps/tool3.bmp
new file mode 100644 (file)
index 0000000..6a49f45
Binary files /dev/null and b/samples/ogl/ogledit/bitmaps/tool3.bmp differ
diff --git a/samples/ogl/ogledit/bitmaps/tool3.xpm b/samples/ogl/ogledit/bitmaps/tool3.xpm
new file mode 100644 (file)
index 0000000..7ba0785
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static char *tool3_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"22 22 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c #c0c0c0",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"######################",
+"######################",
+"######################",
+"######################",
+"#######       ########",
+"####   :::::::   #####",
+"### ::::::::::::: ####",
+"## ::::::::::::::: ###",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"# ::::::::::::::::: ##",
+"## ::::::::::::::: ###",
+"### ::::::::::::: ####",
+"####   :::::::   #####",
+"######        ########",
+"######################",
+"######################",
+"######################",
+"######################",
+"######################",
+"######################"
+};
diff --git a/samples/ogl/ogledit/bitmaps/tool4.bmp b/samples/ogl/ogledit/bitmaps/tool4.bmp
new file mode 100644 (file)
index 0000000..95c2061
Binary files /dev/null and b/samples/ogl/ogledit/bitmaps/tool4.bmp differ
diff --git a/samples/ogl/ogledit/bitmaps/tool4.xpm b/samples/ogl/ogledit/bitmaps/tool4.xpm
new file mode 100644 (file)
index 0000000..154e93b
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static char *tool4_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"22 22 16 1",
+"  c Gray0",
+". c #bf0000",
+"X c #00bf00",
+"o c #bfbf00",
+"O c #0000bf",
+"+ c #bf00bf",
+"@ c #00bfbf",
+"# c #c0c0c0",
+"$ c #808080",
+"% c Red",
+"& c Green",
+"* c Yellow",
+"= c Blue",
+"- c Magenta",
+"; c Cyan",
+": c Gray100",
+/* pixels */
+"######################",
+"######################",
+"######################",
+"##########  ##########",
+"######### :: #########",
+"######## :::: ########",
+"####### :::::: #######",
+"###### :::::::: ######",
+"##### :::::::::: #####",
+"#### :::::::::::: ####",
+"### :::::::::::::: ###",
+"### :::::::::::::: ###",
+"#### :::::::::::: ####",
+"##### :::::::::: #####",
+"###### :::::::: ######",
+"####### :::::: #######",
+"######## :::: ########",
+"######### :: #########",
+"##########  ##########",
+"######################",
+"######################",
+"######################"
+};
diff --git a/samples/ogl/ogledit/doc.cpp b/samples/ogl/ogledit/doc.cpp
new file mode 100644 (file)
index 0000000..3557696
--- /dev/null
@@ -0,0 +1,611 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        doc.cpp
+// Purpose:     Implements document functionality in OGLEdit
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
+#endif
+
+#include <wx/wxexpr.h>
+#include "ogledit.h"
+#include "doc.h"
+#include "view.h"
+
+#if wxUSE_STD_IOSTREAM
+#include <iostream.h>
+#endif
+
+IMPLEMENT_DYNAMIC_CLASS(DiagramDocument, wxDocument)
+
+DiagramDocument::DiagramDocument(void)
+{
+}
+
+DiagramDocument::~DiagramDocument(void)
+{
+}
+
+bool DiagramDocument::OnCloseDocument(void)
+{
+  diagram.DeleteAllShapes();
+  return TRUE;
+}
+
+#if wxUSE_STD_IOSTREAM
+ostream& DiagramDocument::SaveObject(ostream& stream)
+{
+  wxDocument::SaveObject(stream);
+  
+  char buf[400];
+  (void) wxGetTempFileName("diag", buf);
+
+  diagram.SaveFile(buf);
+  wxTransferFileToStream(buf, stream);
+
+  wxRemoveFile(buf);
+  
+  return stream;
+}
+
+istream& DiagramDocument::LoadObject(istream& stream)
+{
+  wxDocument::LoadObject(stream);
+
+  char buf[400];
+  (void) wxGetTempFileName("diag", buf);
+
+  wxTransferStreamToFile(stream, buf);
+
+  diagram.DeleteAllShapes();
+  diagram.LoadFile(buf);
+  wxRemoveFile(buf);
+
+  return stream;
+}
+#else
+
+wxOutputStream& DiagramDocument::SaveObject(wxOutputStream& stream)
+{
+  wxDocument::SaveObject(stream);
+  char buf[400];
+  (void) wxGetTempFileName("diag", buf);
+
+  diagram.SaveFile(buf);
+
+  wxTransferFileToStream(buf, stream);
+
+  wxRemoveFile(buf);
+  
+
+  return stream;
+}
+
+wxInputStream& DiagramDocument::LoadObject(wxInputStream& stream)
+{
+  wxDocument::LoadObject(stream);
+
+
+  char buf[400];
+  (void) wxGetTempFileName("diag", buf);
+
+  wxTransferStreamToFile(stream, buf);
+
+  diagram.DeleteAllShapes();
+  diagram.LoadFile(buf);
+  wxRemoveFile(buf);
+
+  return stream;
+}
+
+#endif
+
+/*
+ * Implementation of drawing command
+ */
+
+DiagramCommand::DiagramCommand(char *name, int command, DiagramDocument *ddoc, wxClassInfo *info, double xx, double yy,
+  bool sel, wxShape *theShape, wxShape *fs, wxShape *ts):
+  wxCommand(TRUE, name)
+{
+  doc = ddoc;
+  cmd = command;
+  shape = theShape;
+  fromShape = fs;
+  toShape = ts;
+  shapeInfo = info;
+  shapeBrush = NULL;
+  shapePen = NULL;
+  x = xx;
+  y = yy;
+  selected = sel;
+  deleteShape = FALSE;
+}
+
+DiagramCommand::DiagramCommand(char *name, int command, DiagramDocument *ddoc, wxBrush *backgroundColour, wxShape *theShape):
+  wxCommand(TRUE, name)
+{
+  doc = ddoc;
+  cmd = command;
+  shape = theShape;
+  fromShape = NULL;
+  toShape = NULL;
+  shapeInfo = NULL;
+  x = 0.0;
+  y = 0.0;
+  selected = FALSE;
+  deleteShape = FALSE;
+  shapeBrush = backgroundColour;
+  shapePen = NULL;
+}
+
+DiagramCommand::DiagramCommand(char *name, int command, DiagramDocument *ddoc, const wxString& lab, wxShape *theShape):
+  wxCommand(TRUE, name)
+{
+  doc = ddoc;
+  cmd = command;
+  shape = theShape;
+  fromShape = NULL;
+  toShape = NULL;
+  shapeInfo = NULL;
+  x = 0.0;
+  y = 0.0;
+  selected = FALSE;
+  deleteShape = FALSE;
+  shapeBrush = NULL;
+  shapePen = NULL;
+  shapeLabel = lab;
+}
+
+DiagramCommand::~DiagramCommand(void)
+{
+  if (shape && deleteShape)
+  {
+    shape->SetCanvas(NULL);
+    delete shape;
+  }
+}
+
+bool DiagramCommand::Do(void)
+{
+  switch (cmd)
+  {
+    case OGLEDIT_CUT:
+    {
+      if (shape)
+      {
+        deleteShape = TRUE;
+        
+        shape->Select(FALSE);
+        
+        // Generate commands to explicitly remove each connected line.
+        RemoveLines(shape);
+        
+        doc->GetDiagram()->RemoveShape(shape);
+        if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+          wxLineShape *lineShape = (wxLineShape *)shape;
+          fromShape = lineShape->GetFrom();
+          toShape = lineShape->GetTo();
+        }
+        shape->Unlink();
+        
+        doc->Modify(TRUE);
+        doc->UpdateAllViews();
+      }
+
+      break;
+    }
+    case OGLEDIT_ADD_SHAPE:
+    {
+      wxShape *theShape = NULL;
+      if (shape)
+        theShape = shape; // Saved from undoing the shape
+      else
+      {
+        theShape = (wxShape *)shapeInfo->CreateObject();
+        theShape->AssignNewIds();
+        theShape->SetEventHandler(new MyEvtHandler(theShape, theShape, wxString("")));
+        theShape->SetCentreResize(FALSE);
+        theShape->SetPen(wxBLACK_PEN);
+        theShape->SetBrush(wxCYAN_BRUSH);
+      
+        theShape->SetSize(60, 60);
+      }
+      doc->GetDiagram()->AddShape(theShape);
+      theShape->Show(TRUE);
+
+      wxClientDC dc(theShape->GetCanvas());
+      theShape->GetCanvas()->PrepareDC(dc);
+
+      theShape->Move(dc, x, y);
+      
+      shape = theShape;
+      deleteShape = FALSE;
+
+      doc->Modify(TRUE);
+      doc->UpdateAllViews();
+      break;
+    }
+    case OGLEDIT_ADD_LINE:
+    {
+      wxShape *theShape = NULL;
+      if (shape)
+        theShape = shape; // Saved from undoing the line
+      else
+      {
+        theShape = (wxShape *)shapeInfo->CreateObject();
+        theShape->AssignNewIds();
+        theShape->SetEventHandler(new MyEvtHandler(theShape, theShape, wxString("")));
+        theShape->SetPen(wxBLACK_PEN);
+        theShape->SetBrush(wxRED_BRUSH);
+
+        wxLineShape *lineShape = (wxLineShape *)theShape;
+
+        // Yes, you can have more than 2 control points, in which case
+        // it becomes a multi-segment line.
+        lineShape->MakeLineControlPoints(2);
+        lineShape->AddArrow(ARROW_ARROW, ARROW_POSITION_END, 10.0, 0.0, "Normal arrowhead");
+      }
+      
+      doc->GetDiagram()->AddShape(theShape);
+      
+      fromShape->AddLine((wxLineShape *)theShape, toShape);
+      
+      theShape->Show(TRUE);
+
+      wxClientDC dc(theShape->GetCanvas());
+      theShape->GetCanvas()->PrepareDC(dc);
+
+      // It won't get drawn properly unless you move both
+      // connected images
+      fromShape->Move(dc, fromShape->GetX(), fromShape->GetY());
+      toShape->Move(dc, toShape->GetX(), toShape->GetY());
+      
+      shape = theShape;
+      deleteShape = FALSE;
+
+      doc->Modify(TRUE);
+      doc->UpdateAllViews();
+      break;
+    }
+    case OGLEDIT_CHANGE_BACKGROUND_COLOUR:
+    {
+      if (shape)
+      {
+        wxClientDC dc(shape->GetCanvas());
+        shape->GetCanvas()->PrepareDC(dc);
+
+        wxBrush *oldBrush = shape->GetBrush();
+        shape->SetBrush(shapeBrush);
+        shapeBrush = oldBrush;
+        shape->Draw(dc);
+        
+        doc->Modify(TRUE);
+        doc->UpdateAllViews();
+      }
+
+      break;
+    }
+    case OGLEDIT_EDIT_LABEL:
+    {
+      if (shape)
+      {
+        MyEvtHandler *myHandler = (MyEvtHandler *)shape->GetEventHandler();
+        wxString oldLabel(myHandler->label);
+        myHandler->label = shapeLabel;
+        shapeLabel = oldLabel;
+
+        wxClientDC dc(shape->GetCanvas());
+        shape->GetCanvas()->PrepareDC(dc);
+
+        shape->FormatText(dc, (char*) (const char*) myHandler->label);
+        shape->Draw(dc);
+        
+        doc->Modify(TRUE);
+        doc->UpdateAllViews();
+      }
+
+      break;
+    }
+  }
+  return TRUE;
+}
+
+bool DiagramCommand::Undo(void)
+{
+  switch (cmd)
+  {
+    case OGLEDIT_CUT:
+    {
+      if (shape)
+      {
+        doc->GetDiagram()->AddShape(shape);
+        shape->Show(TRUE);
+
+        if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+          wxLineShape *lineShape = (wxLineShape *)shape;
+
+          fromShape->AddLine(lineShape, toShape);
+        }
+        if (selected)
+          shape->Select(TRUE);
+
+        deleteShape = FALSE;
+      }
+      doc->Modify(TRUE);
+      doc->UpdateAllViews();
+      break;
+    }
+    case OGLEDIT_ADD_SHAPE:
+    case OGLEDIT_ADD_LINE:
+    {
+      if (shape)
+      {
+        wxClientDC dc(shape->GetCanvas());
+        shape->GetCanvas()->PrepareDC(dc);
+
+        shape->Select(FALSE, &dc);
+        doc->GetDiagram()->RemoveShape(shape);
+        shape->Unlink();
+        deleteShape = TRUE;
+      }
+      doc->Modify(TRUE);
+      doc->UpdateAllViews();
+      break;
+    }
+    case OGLEDIT_CHANGE_BACKGROUND_COLOUR:
+    {
+      if (shape)
+      {
+        wxClientDC dc(shape->GetCanvas());
+        shape->GetCanvas()->PrepareDC(dc);
+
+        wxBrush *oldBrush = shape->GetBrush();
+        shape->SetBrush(shapeBrush);
+        shapeBrush = oldBrush;
+        shape->Draw(dc);
+        
+        doc->Modify(TRUE);
+        doc->UpdateAllViews();
+      }
+      break;
+    }
+    case OGLEDIT_EDIT_LABEL:
+    {
+      if (shape)
+      {
+        MyEvtHandler *myHandler = (MyEvtHandler *)shape->GetEventHandler();
+        wxString oldLabel(myHandler->label);
+        myHandler->label = shapeLabel;
+        shapeLabel = oldLabel;
+
+        wxClientDC dc(shape->GetCanvas());
+        shape->GetCanvas()->PrepareDC(dc);
+
+        shape->FormatText(dc, (char*) (const char*) myHandler->label);
+        shape->Draw(dc);
+        
+        doc->Modify(TRUE);
+        doc->UpdateAllViews();
+      }
+
+      break;
+    }
+  }
+  return TRUE;
+}
+
+// Remove each individual line connected to a shape by sending a command.
+void DiagramCommand::RemoveLines(wxShape *shape)
+{
+  wxNode *node = shape->GetLines().First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    doc->GetCommandProcessor()->Submit(new DiagramCommand("Cut", OGLEDIT_CUT, doc, NULL, 0.0, 0.0, line->Selected(), line));
+    
+    node = shape->GetLines().First();
+  }
+}
+
+/*
+ * MyEvtHandler: an event handler class for all shapes
+ */
+void MyEvtHandler::OnLeftClick(double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  if (keys == 0)
+  {
+    // Selection is a concept the library knows about
+    if (GetShape()->Selected())
+    {
+      GetShape()->Select(FALSE, &dc);
+      GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be are missing
+    }
+    else
+    {
+      // Ensure no other shape is selected, to simplify Undo/Redo code
+      bool redraw = FALSE;
+      wxNode *node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First();
+      while (node)
+      {
+        wxShape *eachShape = (wxShape *)node->Data();
+        if (eachShape->GetParent() == NULL)
+        {
+          if (eachShape->Selected())
+          {
+            eachShape->Select(FALSE, &dc);
+            redraw = TRUE;
+          }
+        }
+        node = node->Next();
+      }
+      GetShape()->Select(TRUE, &dc);
+      if (redraw)
+        GetShape()->GetCanvas()->Redraw(dc);
+    }
+  }
+  else if (keys & KEY_CTRL)
+  {
+    // Do something for CONTROL
+  }
+  else
+  {
+    wxGetApp().frame->SetStatusText(label);
+  }
+}
+
+/*
+ * Implement connection of two shapes by right-dragging between them.
+ */
+
+void MyEvtHandler::OnBeginDragRight(double x, double y, int keys, int attachment)
+{
+  // Force attachment to be zero for now. Eventually we can deal with
+  // the actual attachment point, e.g. a rectangle side if attachment mode is on.
+  attachment = 0;
+  
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetLogicalFunction(OGLRBLF);
+  dc.SetPen(dottedPen);
+  double xp, yp;
+  GetShape()->GetAttachmentPosition(attachment, &xp, &yp);
+  dc.DrawLine((long) xp, (long) yp, (long) x, (long) y);
+  GetShape()->GetCanvas()->CaptureMouse();
+}
+
+void MyEvtHandler::OnDragRight(bool draw, double x, double y, int keys, int attachment)
+{
+  // Force attachment to be zero for now
+  attachment = 0;
+
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetLogicalFunction(OGLRBLF);
+  dc.SetPen(dottedPen);
+  double xp, yp;
+  GetShape()->GetAttachmentPosition(attachment, &xp, &yp);
+  dc.DrawLine((long) xp, (long) yp, (long) x, (long) y);
+}
+
+void MyEvtHandler::OnEndDragRight(double x, double y, int keys, int attachment)
+{
+  GetShape()->GetCanvas()->ReleaseMouse();
+  MyCanvas *canvas = (MyCanvas *)GetShape()->GetCanvas();
+
+  // Check if we're on an object
+  int new_attachment;
+  wxShape *otherShape = canvas->FindFirstSensitiveShape(x, y, &new_attachment, OP_DRAG_RIGHT);
+  
+  if (otherShape && !otherShape->IsKindOf(CLASSINFO(wxLineShape)))
+  {
+    canvas->view->GetDocument()->GetCommandProcessor()->Submit(
+      new DiagramCommand("wxLineShape", OGLEDIT_ADD_LINE, (DiagramDocument *)canvas->view->GetDocument(), CLASSINFO(wxLineShape),
+      0.0, 0.0, FALSE, NULL, GetShape(), otherShape));
+  }
+}
+
+void MyEvtHandler::OnEndSize(double x, double y)
+{
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  GetShape()->FormatText(dc, (char*) (const char*) label);
+}
+
+/*
+ * Diagram
+ */
+bool MyDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
+{
+  wxDiagram::OnShapeSave(db, shape, expr);
+  MyEvtHandler *handler = (MyEvtHandler *)shape.GetEventHandler();
+  expr.AddAttributeValueString("label", handler->label);
+  return TRUE;
+}
+
+bool MyDiagram::OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
+{
+  wxDiagram::OnShapeLoad(db, shape, expr);
+  char *label = NULL;
+  expr.AssignAttributeValue("label", &label);
+  MyEvtHandler *handler = new MyEvtHandler(&shape, &shape, wxString(label));
+  shape.SetEventHandler(handler);
+  
+  if (label)
+    delete[] label;
+  return TRUE;
+}
+
+/*
+ * New shapes
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRoundedRectangleShape, wxRectangleShape)
+
+wxRoundedRectangleShape::wxRoundedRectangleShape(double w, double h):
+ wxRectangleShape(w, h)
+{
+  // 0.3 of the smaller rectangle dimension
+  SetCornerRadius((double) -0.3);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(wxDiamondShape, wxPolygonShape)
+
+wxDiamondShape::wxDiamondShape(double w, double h):
+  wxPolygonShape()
+{
+  // wxPolygonShape::SetSize relies on the shape having non-zero
+  // size initially.
+  if (w == 0.0)
+    w = 60.0;
+  if (h == 0.0)
+    h = 60.0;
+    
+  wxList *thePoints = new wxList;
+  wxRealPoint *point = new wxRealPoint(0.0, (-h/2.0));
+  thePoints->Append((wxObject*) point);
+
+  point = new wxRealPoint((w/2.0), 0.0);
+  thePoints->Append((wxObject*) point);
+
+  point = new wxRealPoint(0.0, (h/2.0));
+  thePoints->Append((wxObject*) point);
+
+  point = new wxRealPoint((-w/2.0), 0.0);
+  thePoints->Append((wxObject*) point);
+
+  Create(thePoints);
+}
diff --git a/samples/ogl/ogledit/doc.h b/samples/ogl/ogledit/doc.h
new file mode 100644 (file)
index 0000000..09a841d
--- /dev/null
@@ -0,0 +1,182 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        doc.h
+// Purpose:     Document classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGLSAMPLE_DOC_H_
+#define _OGLSAMPLE_DOC_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/docview.h>
+#include <wx/string.h>
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/ogl.h>
+
+#if wxUSE_STD_IOSTREAM
+class ostream;
+class istream;
+#endif
+
+/*
+ * Override a few members for this application
+ */
+class MyDiagram: public wxDiagram
+{
+ public:
+  MyDiagram(void) {}
+  bool OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr);
+  bool OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr);
+};
+
+/*
+ * A few new shape classes so we have a 1:1 mapping
+ * between palette symbol and unique class
+ */
+class wxRoundedRectangleShape: public wxRectangleShape
+{
+  DECLARE_DYNAMIC_CLASS(wxRoundedRectangleShape)
+ private:
+ public:
+  wxRoundedRectangleShape(double w = 0.0, double h = 0.0);
+};
+
+class wxDiamondShape: public wxPolygonShape
+{
+  DECLARE_DYNAMIC_CLASS(wxDiamondShape)
+ private:
+ public:
+  wxDiamondShape(double w = 0.0, double h = 0.0);
+};
+
+/*
+ * All shape event behaviour is routed through this handler, so we don't
+ * have to derive from each shape class. We plug this in to each shape.
+ */
+class MyEvtHandler: public wxShapeEvtHandler
+{
+ public:
+  wxString label;
+  MyEvtHandler(wxShapeEvtHandler *prev = NULL, wxShape *shape = NULL, const wxString& lab = ""):wxShapeEvtHandler(prev, shape)
+  {
+    label = lab;
+  }
+  ~MyEvtHandler(void)
+  {
+  }
+  void OnLeftClick(double x, double y, int keys = 0, int attachment = 0);
+  void OnBeginDragRight(double x, double y, int keys = 0, int attachment = 0);
+  void OnDragRight(bool draw, double x, double y, int keys = 0, int attachment = 0);
+  void OnEndDragRight(double x, double y, int keys = 0, int attachment = 0);
+  void OnEndSize(double x, double y);
+};
+
+/*
+ * A diagram document, which contains a diagram.
+ */
+class DiagramDocument: public wxDocument
+{
+  DECLARE_DYNAMIC_CLASS(DiagramDocument)
+ private:
+ public:
+  MyDiagram diagram;
+  
+  DiagramDocument(void);
+  ~DiagramDocument(void);
+
+#if wxUSE_STD_IOSTREAM
+    virtual ostream& SaveObject(ostream& stream);
+    virtual istream& LoadObject(istream& stream);
+#else
+    virtual wxOutputStream& SaveObject(wxOutputStream& stream);
+    virtual wxInputStream& LoadObject(wxInputStream& stream);
+#endif
+  
+  inline wxDiagram *GetDiagram() { return &diagram; }
+  
+  bool OnCloseDocument(void);
+};
+
+/*
+ * Most user interface commands are routed through this, to give us the
+ * Undo/Redo mechanism. If you add more commands, such as changing the shape colour,
+ * you will need to add members to 'remember' what the user applied (for 'Do') and what the
+ * previous state was (for 'Undo').
+ * You can have one member for each property to be changed. Assume we also have
+ * a pointer member wxShape *shape, which is set to the shape being changed.
+ * Let's assume we're changing the shape colour. Our member for this is shapeColour.
+ *
+ * - In 'Do':
+ *   o Set a temporary variable 'temp' to the current colour for 'shape'.
+ *   o Change the colour to the new colour.
+ *   o Set shapeColour to the _old_ colour, 'temp'.
+ * - In 'Undo':
+ *   o Set a temporary variable 'temp' to the current colour for 'shape'.
+ *   o Change the colour to shapeColour (the old colour).
+ *   o Set shapeColour to 'temp'.
+ *
+ * So, as long as we have a pointer to the shape being changed,
+ * we only need one member variable for each property.
+ *
+ * PROBLEM: when an Add shape command is redone, the 'shape' pointer changes.
+ * Assume, as here, that we keep a pointer to the old shape so we reuse it
+ * when we recreate.
+ */
+class DiagramCommand: public wxCommand
+{
+ protected:
+  DiagramDocument *doc;
+  int cmd;
+  wxShape *shape; // Pointer to the shape we're acting on
+  wxShape *fromShape;
+  wxShape *toShape;
+  wxClassInfo *shapeInfo;
+  double x;
+  double y;
+  bool selected;
+  bool deleteShape;
+
+  // Storage for property commands
+  wxBrush *shapeBrush;
+  wxPen *shapePen;
+  wxString shapeLabel;
+ public:
+  // Multi-purpose constructor for creating, deleting shapes
+  DiagramCommand(char *name, int cmd, DiagramDocument *ddoc, wxClassInfo *shapeInfo = NULL,
+     double x = 0.0, double y = 0.0, bool sel = FALSE, wxShape *theShape = NULL, wxShape *fs = NULL, wxShape *ts = NULL);
+
+  // Property-changing command constructors
+  DiagramCommand(char *name, int cmd, DiagramDocument *ddoc, wxBrush *backgroundColour, wxShape *theShape);
+  DiagramCommand(char *name, int cmd, DiagramDocument *ddoc, const wxString& lab, wxShape *theShape);
+
+  ~DiagramCommand(void);
+
+  bool Do(void);
+  bool Undo(void);
+
+  inline void SetShape(wxShape *s) { shape = s; }
+  inline wxShape *GetShape(void) { return shape; }
+  inline wxShape *GetFromShape(void) { return fromShape; }
+  inline wxShape *GetToShape(void) { return toShape; }
+  inline wxClassInfo *GetShapeInfo(void) { return shapeInfo; }
+  inline bool GetSelected(void) { return selected; }
+
+  void RemoveLines(wxShape *shape);
+};
+
+#endif
+  // _OGLSAMPLE_DOC_H_
diff --git a/samples/ogl/ogledit/makefile.b32 b/samples/ogl/ogledit/makefile.b32
new file mode 100644 (file)
index 0000000..7ee4862
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# File:                makefile.b32
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:
+#
+# Makefile : Builds sample for 32-bit BC++
+
+WXDIR = $(WXWIN)
+
+TARGET=ogledit
+EXTRALIBS=$(WXDIR)\lib\ogl.lib
+EXTRACPPFLAGS=-I$(WXDIR)\utils\ogl\src
+OBJECTS = $(TARGET).obj doc.obj view.obj palette.obj
+
+!include $(WXDIR)\src\makeprog.b32
+
diff --git a/samples/ogl/ogledit/makefile.bcc b/samples/ogl/ogledit/makefile.bcc
new file mode 100644 (file)
index 0000000..171d966
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# File:                makefile.bcc
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+#
+# Builds a BC++ 16-bit sample
+
+!if "$(WXWIN)" == ""
+!error You must define the WXWIN variable in autoexec.bat, e.g. WXWIN=c:\wx
+!endif
+
+WXDIR = $(WXWIN)
+
+TARGET=ogledit
+EXTRALIBS=$(WXDIR)\lib\ogl.lib
+EXTRACPPFLAGS=-I$(WXDIR)\utils\ogl\src
+OBJECTS = $(TARGET).obj doc.obj view.obj palette.obj
+
+!include $(WXDIR)\src\makeprog.b32
+
diff --git a/samples/ogl/ogledit/makefile.dos b/samples/ogl/ogledit/makefile.dos
new file mode 100644 (file)
index 0000000..34eb326
--- /dev/null
@@ -0,0 +1,103 @@
+#
+# File:                makefile.dos
+# Author:      Julian Smart
+# Created:     1995
+# Updated:     
+# Copyright:   (c) 1995, AIAI, University of Edinburgh
+#
+# "%W% %G%"
+#
+# Makefile : Builds OGLEdit example (MSVC++ 1.5).
+# Use FINAL=1 argument to nmake to build final version with no debugging
+# info
+
+WXDIR = $(WXWIN)
+
+!include $(WXDIR)\src\makemsc.env
+
+THISDIR = $(WXDIR)\utils\ogl\samples\ogledit
+WXLIB = $(WXDIR)\lib\wx.lib
+
+OGLDIR = $(WXDIR)\utils\ogl
+OGLINC = $(OGLDIR)\src
+OGLLIB = $(OGLDIR)\lib\ogl.lib
+
+LIBS=$(WXLIB) $(OGLLIB) oldnames libw llibcew commdlg shell ddeml
+
+EXTRAFLAGS=/I$(OGLINC)
+
+OBJECTS = ogledit.obj doc.obj view.obj palette.obj
+
+all:    ogledit.exe
+
+wx:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.dos
+        cd $(THISDIR)
+
+wxclean:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.dos clean
+        cd $(THISDIR)
+
+ogl:
+        cd $(OGLDIR)\src
+        nmake -f makefile.dos FINAL=$(FINAL)
+        cd $(THISDIR)
+
+prologio:
+        cd $(PROLOGDIR)\src
+        nmake -f makefile.dos FINAL=$(FINAL)
+        cd $(THISDIR)
+
+ogledit.exe:      $(WXDIR)\src\msw\dummy.obj $(WXLIB) $(OBJECTS) $(OGLLIB) ogledit.def ogledit.res
+        link $(LINKFLAGS) @<<
+$(WXDIR)\src\msw\dummy.obj $(OBJECTS),
+ogledit,
+NUL,
+$(LIBS),
+ogledit.def
+;
+<<
+        rc -30 -K ogledit.res
+
+ogledit.obj:      ogledit.h ogledit.$(SRCSUFF)
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+view.obj:      view.h view.$(SRCSUFF)
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+doc.obj:      doc.h doc.$(SRCSUFF)
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+palette.obj:      view.h doc.h ogledit.h palette.$(SRCSUFF) $(DUMMYOBJ)
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+ogledit.res :      ogledit.rc $(WXDIR)\include\msw\wx.rc
+    rc -r /dFAFA_LIB /i$(WXDIR)\contrib\fafa /i$(WXDIR)\include\msw ogledit
+
+clean:
+        -erase *.obj
+        -erase *.exe
+        -erase *.res
+        -erase *.map
+        -erase *.sbr
+        -erase *.pdb
+
+cleanogl:
+        cd $(OGLDIR)\src
+        nmake -f makefile.dos clean
+        cd $(THISDIR)
+
+cleanall:
+        cd $(OGLDIR)\src
+        nmake -f makefile.dos clean
+        cd $(THISDIR)
\ No newline at end of file
diff --git a/samples/ogl/ogledit/makefile.g95 b/samples/ogl/ogledit/makefile.g95
new file mode 100644 (file)
index 0000000..633e5b8
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# File:         makefile.g95
+# Author:       Julian Smart
+# Created:      1999
+# Updated:
+# Copyright:    (c) Julian Smart, 1999
+#
+# Makefile for wxWindows sample (Cygwin/Mingw32).
+
+WXDIR = ../../../..
+
+TARGET=ogledit
+EXTRACPPFLAGS=-I../../src
+EXTRALIBS=-logl
+OBJECTS = $(TARGET).o doc.o view.o palette.o
+
+include $(WXDIR)/src/makeprog.g95
+
diff --git a/samples/ogl/ogledit/makefile.sc b/samples/ogl/ogledit/makefile.sc
new file mode 100644 (file)
index 0000000..03da92b
--- /dev/null
@@ -0,0 +1,33 @@
+# Symantec C++ makefile for docview example
+# NOTE that peripheral libraries are now dealt in main wxWindows makefile.
+
+WXDIR = $(WXWIN)
+include $(WXDIR)\src\makesc.env
+
+WXLIB = $(WXDIR)\lib\wx.lib
+INCDIR = $(WXDIR)\include
+MSWINC = $(INCDIR)\msw
+BASEINC = $(INCDIR)\base
+
+INCLUDE=$(BASEINC);$(MSWINC)
+
+LIBS=$(WXLIB) libw.lib commdlg.lib shell.lib
+
+OBJECTS=docview.obj view.obj doc.obj
+
+.$(SRCSUFF).obj:
+       *$(CC) -c $(CFLAGS) -I$(INCLUDE) $<
+
+.rc.res:
+       *$(RC) -r -I$(INCLUDE) $<
+
+docview.exe: $(OBJECTS) docview.def docview.res
+       *$(CC) $(LDFLAGS) -o$@ $(OBJECTS) docview.def $(LIBS)
+       *$(RC) -k docview.res
+
+clean:
+        -del *.obj
+       -del *.exe
+       -del *.res
+       -del *.map
+       -del *.rws
diff --git a/samples/ogl/ogledit/makefile.unx b/samples/ogl/ogledit/makefile.unx
new file mode 100644 (file)
index 0000000..0038ddc
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# File:                makefile.unx
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+# Copyright:   (c) 1998 Julian Smart
+#
+# "%W% %G%"
+#
+# Makefile for OGLEdit example (UNIX).
+
+PROGRAM=ogledit
+
+OBJECTS=$(PROGRAM).o doc.o view.o palette.o
+
+EXTRACPPFLAGS=-I$(WXDIR)/utils/ogl/src
+EXTRALDLIBS=-logl$(GUISUFFIX)
+
+include ../../../../src/makeprog.env
+
diff --git a/samples/ogl/ogledit/makefile.vc b/samples/ogl/ogledit/makefile.vc
new file mode 100644 (file)
index 0000000..d4c3e72
--- /dev/null
@@ -0,0 +1,95 @@
+#
+# File:                makefile.vc
+# Author:      Julian Smart
+# Created:     1993
+# Updated:
+# Copyright:   (c) 1993, AIAI, University of Edinburgh
+#
+# "%W% %G%"
+#
+# Makefile : Builds docview example (MS VC++).
+# Use FINAL=1 argument to nmake to build final version with no debugging
+# info
+
+# Set WXDIR for your system
+WXDIR = $(WXWIN)
+
+!include $(WXDIR)\src\makevc.env
+
+THISDIR = $(WXDIR)\utils\ogl\samples\ogledit
+
+OGLDIR = $(WXDIR)\utils\ogl
+OGLINC = $(OGLDIR)\src
+OGLLIB = $(WXDIR)\lib\ogl$(LIBEXT).lib
+
+PROGRAM=ogledit
+
+EXTRALIBS=$(OGLLIB)
+EXTRAINC=/I$(OGLINC)
+
+OBJECTS = $(PROGRAM).obj doc.obj view.obj palette.obj
+
+$(PROGRAM):    $(PROGRAM).exe
+
+all:    $(PROGRAM).exe
+
+wx:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.vc FINAL=$(FINAL)
+        cd $(THISDIR)
+
+wxclean:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.vc clean
+        cd $(THISDIR)
+
+ogl:
+        cd $(OGLDIR)\src
+        nmake -f makefile.vc FINAL=$(FINAL)
+        cd $(THISDIR)
+
+$(PROGRAM).exe:      $(DUMMYOBJ) $(WXLIB) $(OBJECTS) $(OGLLIB) $(PROGRAM).res
+       $(link) @<<
+-out:$(PROGRAM).exe
+$(LINKFLAGS)
+$(DUMMYOBJ) $(OBJECTS) $(PROGRAM).res
+$(LIBS)
+<<
+
+$(PROGRAM).obj:      $(PROGRAM).h doc.h view.h palette.h $(PROGRAM).$(SRCSUFF) $(DUMMYOBJ)
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+doc.obj:      view.h doc.h doc.$(SRCSUFF) ogledit.h $(DUMMYOBJ)
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+view.obj:      view.h doc.h view.$(SRCSUFF) ogledit.h $(DUMMYOBJ)
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+palette.obj:      view.h doc.h ogledit.h palette.$(SRCSUFF) $(DUMMYOBJ)
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+$(PROGRAM).res :      $(PROGRAM).rc $(WXDIR)\include\wx\msw\wx.rc ogl.ico
+    $(rc) -r /i$(WXDIR)\include -fo$@ $(PROGRAM).rc
+
+
+cleanogl:
+        cd $(OGLDIR)\src
+        nmake -f makefile.vc clean
+        cd $(THISDIR)
+
+clean:
+        -erase *.obj
+        -erase *.sbr
+        -erase *.exe
+        -erase *.res
+        -erase *.map
+        -erase *.pdb
+
diff --git a/samples/ogl/ogledit/makefile.vms b/samples/ogl/ogledit/makefile.vms
new file mode 100644 (file)
index 0000000..1a64fc7
--- /dev/null
@@ -0,0 +1,44 @@
+#************************************************************************
+# Makefile for DOCVIEW under VMS
+# by Stefan Hammes
+# (incomplete) update history:
+# 09.06.95
+#************************************************************************
+
+#************************************************************************
+# Definition section
+# (cave: definitions and includes must begin with ',')
+#************************************************************************
+
+APPOPTS = 
+APPDEFS = 
+APPINCS = 
+
+#************************************************************************
+# Module section
+#************************************************************************
+
+# Name of main module
+MAIN = docview
+
+# Object modules of the application.
+OBJS = docview.obj view.obj doc.obj
+OBJLIST =docview.obj,view.obj,doc.obj
+
+.include [--.src]makevms.env
+
+# main dependency
+$(MAIN).exe : $(OBJS)
+    $(LINK) $(LINKFLAGS) /exec=$(MAIN).exe $(OBJLIST),$(WXLIB)/lib,$(OPTSFILE)/option
+    - purge *.exe
+
+#************************************************************************
+# Header file depedencies following
+#************************************************************************
+
+docview.$(OBJSUFF) :        docview.$(SRCSUFF) docview.h doc.h view.h
+
+doc.$(OBJSUFF) :        doc.$(SRCSUFF) doc.h
+
+view.$(OBJSUFF) :        view.$(SRCSUFF) view.h
+
diff --git a/samples/ogl/ogledit/makefile.wat b/samples/ogl/ogledit/makefile.wat
new file mode 100644 (file)
index 0000000..bafa16f
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Makefile for WATCOM
+#
+# Created by D.Chubraev, chubraev@iem.ee.ethz.ch
+# 8 Nov 1994
+#
+
+WXDIR = ..\.. 
+
+!include $(WXDIR)\src\makewat.env
+
+WXLIB = $(WXDIR)\lib
+NAME = docview
+LNK = $(name).lnk
+OBJS = $(name).obj doc.obj view.obj
+
+all:    $(name).exe
+
+$(name).exe : $(OBJS) $(name).res $(LNK) $(WXLIB)\wx$(LEVEL).lib
+    wlink @$(LNK)
+    $(BINDCOMMAND) $(name).res
+
+$(name).res :      $(name).rc $(WXDIR)\include\msw\wx.rc
+     $(RC) $(RESFLAGS1) $(name).rc
+
+$(LNK) : makefile.wat
+    %create $(LNK)
+    @%append $(LNK) debug all
+    @%append $(LNK) system $(LINKOPTION)
+    @%append $(LNK) $(MINDATA)
+    @%append $(LNK) $(MAXDATA)
+    @%append $(LNK) $(STACK)
+    @%append $(LNK) name $(name)
+    @%append $(LNK) file $(WXLIB)\wx$(LEVEL).lib
+    @for %i in ($(EXTRALIBS)) do @%append $(LNK) file %i
+    @for %i in ($(OBJS)) do @%append $(LNK) file %i
+
+thing: .SYMBOLIC
+    echo $(WATLIBDIR)
+
+clean:   .SYMBOLIC
+    -erase *.obj *.bak *.err *.pch *.lib *.lnk *.res *.exe *.rex
+
diff --git a/samples/ogl/ogledit/ogl.ico b/samples/ogl/ogledit/ogl.ico
new file mode 100644 (file)
index 0000000..7cb092e
Binary files /dev/null and b/samples/ogl/ogledit/ogl.ico differ
diff --git a/samples/ogl/ogledit/ogl.xpm b/samples/ogl/ogledit/ogl.xpm
new file mode 100644 (file)
index 0000000..2a8c61e
--- /dev/null
@@ -0,0 +1,45 @@
+/* XPM */
+static char * ogl_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"32 32 7 1",
+/* colors */
+"      s None  c None",
+".     c #000000",
+"+     c #000080",
+"@     c #ff0000",
+"#     c #00ff00",
+"$     c #00ffff",
+"%     c #ffffff",
+/* pixels */
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%............%%%%%%........%%%%%",
+"%.@@@@@@@@@@.%%%%..$$$$$$$$..%%%",
+"%.@@@@@@@@@@.%%%.$$$$$$$$$$$$.%%",
+"%.@@@@@@@@@@.+++.$$$$$$$$$$$$.%%",
+"%.@@@@@@@@@@.+++.$$$$$$$$$$$$.%%",
+"%.@@@@@@@@@@.%%%.$$$$$$$$$$$$.%%",
+"%.@@@@@@@@@@.%%%%..$$$$$$$$..%%%",
+"%............%%%%%.........%%%%%",
+"%%%%%%++%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%++%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%++%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%++%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%++%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%++%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%+.%%%%%%%%%%%%%%++%%%%%%%%",
+"%%%%%%.+.%%%%%%%%%%%%.++..%%%%%%",
+"%%%%%.+++.%%%%%%%%%..#####..%%%%",
+"%%%%.+++++.%%%%%%%.#########.%%%",
+"%%%.+++++++.%%%%%%.#########.%%%",
+"%%.+++++++++.%%%%.###########.%%",
+"%.+++++++++++.++++###########.%%",
+"%%.+++++++++.+++++###########.%%",
+"%%%.+++++++.%%%%%%.#########.%%%",
+"%%%%.+++++.%%%%%%%.#########.%%%",
+"%%%%%.+++.%%%%%%%%%..#####..%%%%",
+"%%%%%%.+.%%%%%%%%%%%%.....%%%%%%",
+"%%%%%%%.%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"};
diff --git a/samples/ogl/ogledit/ogledit.cpp b/samples/ogl/ogledit/ogledit.cpp
new file mode 100644 (file)
index 0000000..87f19e6
--- /dev/null
@@ -0,0 +1,213 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        ogledit.cpp
+// Purpose:     OGLEdit sample app
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
+#endif
+
+#include "ogledit.h"
+#include "palette.h"
+#include "doc.h"
+#include "view.h"
+
+#if defined(__WXGTK__) || defined(__WXMOTIF__)
+#include "ogl.xpm"
+#endif
+
+// A macro needed for some compilers (AIX) that need 'main' to be defined
+// in the application itself.
+IMPLEMENT_APP(MyApp)
+
+MyApp::MyApp(void)
+{
+  frame = NULL;
+  myDocManager= NULL;
+}
+
+// The `main program' equivalent, creating the windows and returning the
+// main frame
+bool MyApp::OnInit(void)
+{
+  wxOGLInitialize();
+  
+  //// Create a document manager
+  myDocManager = new wxDocManager;
+
+  //// Create a template relating drawing documents to their views
+  (void) new wxDocTemplate(myDocManager, "Diagram", "*.dia", "", "dia", "Diagram Doc", "Diagram View",
+          CLASSINFO(DiagramDocument), CLASSINFO(DiagramView));
+
+  // If we've only got one window, we only get to edit
+  // one document at a time.
+  myDocManager->SetMaxDocsOpen(1);
+
+  //// Create the main frame window
+  frame = new MyFrame(myDocManager, NULL, "OGLEdit Demo", wxPoint(0, 0), wxSize(500, 400), wxDEFAULT_FRAME_STYLE);
+
+  //// Give it an icon
+  frame->SetIcon(wxICON(ogl));
+
+  //// Make a menubar
+  wxMenu *file_menu = new wxMenu;
+  wxMenu *edit_menu = NULL;
+
+  file_menu->Append(wxID_NEW, "&New...");
+  file_menu->Append(wxID_OPEN, "&Open...");
+
+  file_menu->Append(wxID_CLOSE, "&Close");
+  file_menu->Append(wxID_SAVE, "&Save");
+  file_menu->Append(wxID_SAVEAS, "Save &As...");
+  file_menu->AppendSeparator();
+  file_menu->Append(wxID_PRINT, "&Print...");
+  file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
+  file_menu->Append(wxID_PREVIEW, "Print Pre&view");
+
+  edit_menu = new wxMenu;
+  edit_menu->Append(wxID_UNDO, "&Undo");
+  edit_menu->Append(wxID_REDO, "&Redo");
+  edit_menu->AppendSeparator();
+  edit_menu->Append(OGLEDIT_CUT, "&Cut");
+  edit_menu->AppendSeparator();
+  edit_menu->Append(OGLEDIT_CHANGE_BACKGROUND_COLOUR, "Change &background colour");
+  edit_menu->Append(OGLEDIT_EDIT_LABEL, "Edit &label");
+
+  frame->editMenu = edit_menu;
+  
+  file_menu->AppendSeparator();
+  file_menu->Append(wxID_EXIT, "E&xit");
+
+  // A nice touch: a history of files visited. Use this menu.
+  myDocManager->FileHistoryUseMenu(file_menu);
+
+  wxMenu *help_menu = new wxMenu;
+  help_menu->Append(OGLEDIT_ABOUT, "&About");
+
+  wxMenuBar *menu_bar = new wxMenuBar;
+
+  menu_bar->Append(file_menu, "&File");
+  if (edit_menu)
+    menu_bar->Append(edit_menu, "&Edit");
+  menu_bar->Append(help_menu, "&Help");
+
+  frame->canvas = frame->CreateCanvas(NULL, frame);
+  frame->palette = wxGetApp().CreatePalette(frame);
+  myDocManager->CreateDocument("", wxDOC_NEW);
+
+  //// Associate the menu bar with the frame
+  frame->SetMenuBar(menu_bar);
+
+  frame->CreateStatusBar(1);
+
+  frame->Centre(wxBOTH);
+  frame->Show(TRUE);
+
+  return TRUE;
+}
+
+int MyApp::OnExit(void)
+{
+    wxOGLCleanUp();
+    delete myDocManager;
+    return 0;
+}
+
+/*
+ * This is the top-level window of the application.
+ */
+
+IMPLEMENT_CLASS(MyFrame, wxDocParentFrame)
+
+BEGIN_EVENT_TABLE(MyFrame, wxDocParentFrame)
+    EVT_MENU(OGLEDIT_ABOUT, MyFrame::OnAbout)
+    EVT_SIZE(MyFrame::OnSize)
+    EVT_CLOSE(MyFrame::OnCloseWindow)
+END_EVENT_TABLE()
+
+MyFrame::MyFrame(wxDocManager *manager, wxFrame *frame, const wxString& title,
+    const wxPoint& pos, const wxSize& size, long type):
+  wxDocParentFrame(manager, frame, -1, title, pos, size, type)
+{
+  canvas = NULL;
+  palette = NULL;
+  editMenu = NULL;
+}
+
+void MyFrame::OnSize(wxSizeEvent& event)
+{
+  if (canvas && palette)
+  {
+    int cw, ch;
+    GetClientSize(&cw, &ch);
+    int paletteX = 0;
+    int paletteY = 0;
+    int paletteW = 30;
+    int paletteH = ch;
+    int canvasX = paletteX + paletteW;
+    int canvasY = 0;
+    int canvasW = cw - paletteW;
+    int canvasH = ch;
+    
+    palette->SetSize(paletteX, paletteY, paletteW, paletteH);
+    canvas->SetSize(canvasX, canvasY, canvasW, canvasH);
+  }
+}
+
+void MyFrame::OnCloseWindow(wxCloseEvent& event)
+{
+  wxDocParentFrame::OnCloseWindow(event);
+  if (!event.GetVeto())
+  {
+    wxOGLCleanUp();
+  }
+}
+
+// Intercept menu commands
+void MyFrame::OnAbout(wxCommandEvent& event)
+{
+      (void)wxMessageBox("OGLEdit Demo\nTo draw a shape, select a shape on the toolbar and left-click on the canvas.\nTo draw a line, right-drag between shapes.\nFor further details, see the OGL manual.\n (c) Julian Smart 1996", "About OGLEdit");
+}
+
+// Creates a canvas. Called by OnInit as a child of the main window
+MyCanvas *MyFrame::CreateCanvas(wxView *view, wxFrame *parent)
+{
+  int width, height;
+  parent->GetClientSize(&width, &height);
+
+  // Non-retained canvas
+  MyCanvas *canvas = new MyCanvas(view, parent, -1, wxPoint(0, 0), wxSize(width, height), 0);
+  canvas->SetCursor(wxCursor(wxCURSOR_HAND));
+
+  // Give it scrollbars
+  canvas->SetScrollbars(20, 20, 50, 50);
+
+  return canvas;
+}
+
+MyFrame *GetMainFrame(void)
+{
+  return wxGetApp().frame;
+}
+
diff --git a/samples/ogl/ogledit/ogledit.def b/samples/ogl/ogledit/ogledit.def
new file mode 100644 (file)
index 0000000..d587ce5
--- /dev/null
@@ -0,0 +1,8 @@
+NAME         OGLEdit
+DESCRIPTION  'OGL Editor Sample'
+EXETYPE      WINDOWS
+STUB         'WINSTUB.EXE'
+CODE         PRELOAD MOVEABLE DISCARDABLE
+DATA         PRELOAD MOVEABLE MULTIPLE
+HEAPSIZE     1024
+STACKSIZE    8192
diff --git a/samples/ogl/ogledit/ogledit.h b/samples/ogl/ogledit/ogledit.h
new file mode 100644 (file)
index 0000000..62dfb9b
--- /dev/null
@@ -0,0 +1,77 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        ogledit.h
+// Purpose:     OGL sample
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#ifndef _OGLSAMPLE_OGLEDIT_H_
+#define _OGLSAMPLE_OGLEDIT_H_
+
+#include <wx/docview.h>
+
+// Define a new application
+class MyFrame;
+class EditorToolPalette;
+class MyApp: public wxApp
+{
+ public:
+  MyFrame *frame;
+  wxDocManager* myDocManager;
+
+  MyApp(void);
+  bool OnInit(void);
+  int OnExit(void);
+
+  // Palette stuff
+  EditorToolPalette *CreatePalette(wxFrame *parent);
+};
+
+DECLARE_APP(MyApp)
+
+// Define a new frame
+class MyCanvas;
+class MyFrame: public wxDocParentFrame
+{
+  DECLARE_CLASS(MyFrame)
+ public:
+  wxMenu *editMenu;
+  
+  MyCanvas *canvas;
+  EditorToolPalette *palette;
+  
+  MyFrame(wxDocManager *manager, wxFrame *parent, const wxString& title,
+    const wxPoint& pos = wxDefaultPosition,
+    const wxSize& size = wxDefaultSize,
+    long style = wxDEFAULT_FRAME_STYLE);
+
+  MyCanvas *CreateCanvas(wxView *view, wxFrame *parent);
+  void OnSize(wxSizeEvent& event);
+  void OnCloseWindow(wxCloseEvent& event);
+  void OnAbout(wxCommandEvent& event);
+
+DECLARE_EVENT_TABLE()
+};
+
+extern MyFrame *GetMainFrame(void);
+
+// Menu/undo/redo commands
+
+#define OGLEDIT_CUT                         1
+#define OGLEDIT_ADD_SHAPE                   2
+#define OGLEDIT_ADD_LINE                    3
+#define OGLEDIT_EDIT_LABEL                  4
+#define OGLEDIT_CHANGE_BACKGROUND_COLOUR    5
+
+#define OGLEDIT_ABOUT   100
+
+#endif
+    // _OGLSAMPLE_OGLEDIT_H_
diff --git a/samples/ogl/ogledit/ogledit.rc b/samples/ogl/ogledit/ogledit.rc
new file mode 100644 (file)
index 0000000..046f0c6
--- /dev/null
@@ -0,0 +1,10 @@
+ogl             ICON    ogl.ico
+
+TOOL1           BITMAP "bitmaps/tool1.bmp"
+TOOL2           BITMAP "bitmaps/tool2.bmp"
+TOOL3           BITMAP "bitmaps/tool3.bmp"
+TOOL4           BITMAP "bitmaps/tool4.bmp"
+ARROWTOOL       BITMAP "bitmaps/arrow.bmp"
+
+#include "wx/msw/wx.rc"
+
diff --git a/samples/ogl/ogledit/palette.cpp b/samples/ogl/ogledit/palette.cpp
new file mode 100644 (file)
index 0000000..84348c5
--- /dev/null
@@ -0,0 +1,121 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        palette.cpp
+// Purpose:     OGLEdit palette
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/toolbar.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "doc.h"
+#include "view.h"
+#include "ogledit.h"
+#include "palette.h"
+
+// Include pixmaps
+#if defined(__WXGTK__) || defined(__WXMOTIF__)
+#include "bitmaps/arrow.xpm"
+#include "bitmaps/tool1.xpm"
+#include "bitmaps/tool2.xpm"
+#include "bitmaps/tool3.xpm"
+#include "bitmaps/tool4.xpm"
+#endif
+
+/*
+ * Object editor tool palette
+ *
+ */
+
+EditorToolPalette::EditorToolPalette(wxWindow* parent, const wxPoint& pos, const wxSize& size,
+            long style):
+  TOOLPALETTECLASS(parent, -1, pos, size, style)
+{
+  currentlySelected = -1;
+
+#if 1 // ndef __WXGTK__
+  SetMaxRowsCols(1000, 1);
+#endif
+}
+
+bool EditorToolPalette::OnLeftClick(int toolIndex, bool toggled)
+{
+  // BEGIN mutual exclusivity code
+  if (toggled && (currentlySelected != -1) && (toolIndex != currentlySelected))
+    ToggleTool(currentlySelected, FALSE);
+
+  if (toggled)
+    currentlySelected = toolIndex;
+  else if (currentlySelected == toolIndex)
+    currentlySelected = -1;
+  //  END mutual exclusivity code
+
+  return TRUE;
+}
+
+void EditorToolPalette::OnMouseEnter(int toolIndex)
+{
+}
+
+void EditorToolPalette::SetSize(int x, int y, int width, int height, int sizeFlags)
+{
+  TOOLPALETTECLASS::SetSize(x, y, width, height, sizeFlags);
+}
+
+EditorToolPalette *MyApp::CreatePalette(wxFrame *parent)
+{
+  // Load palette bitmaps
+#ifdef __WXMSW__
+  wxBitmap PaletteTool1("TOOL1");
+  wxBitmap PaletteTool2("TOOL2");
+  wxBitmap PaletteTool3("TOOL3");
+  wxBitmap PaletteTool4("TOOL4");
+  wxBitmap PaletteArrow("ARROWTOOL");
+#elif defined(__WXGTK__) || defined(__WXMOTIF__)
+  wxBitmap PaletteTool1(tool1_xpm);
+  wxBitmap PaletteTool2(tool2_xpm);
+  wxBitmap PaletteTool3(tool3_xpm);
+  wxBitmap PaletteTool4(tool4_xpm);
+  wxBitmap PaletteArrow(arrow_xpm);
+#endif
+
+  EditorToolPalette *palette = new EditorToolPalette(parent, wxPoint(0, 0), wxSize(-1, -1), wxTB_HORIZONTAL);
+
+  palette->SetMargins(2, 2);
+  palette->SetToolBitmapSize(wxSize(22, 22));
+
+  palette->AddTool(PALETTE_ARROW, PaletteArrow, wxNullBitmap, TRUE, 0, -1, NULL, "Pointer");
+  palette->AddTool(PALETTE_TOOL1, PaletteTool1, wxNullBitmap, TRUE, 0, -1, NULL, "Tool 1");
+  palette->AddTool(PALETTE_TOOL2, PaletteTool2, wxNullBitmap, TRUE, 0, -1, NULL, "Tool 2");
+  palette->AddTool(PALETTE_TOOL3, PaletteTool3, wxNullBitmap, TRUE, 0, -1, NULL, "Tool 3");
+  palette->AddTool(PALETTE_TOOL4, PaletteTool4, wxNullBitmap, TRUE, 0, -1, NULL, "Tool 4");
+
+  palette->Realize();
+
+  palette->ToggleTool(PALETTE_ARROW, TRUE);
+  palette->currentlySelected = PALETTE_ARROW;
+  return palette;
+}
+
diff --git a/samples/ogl/ogledit/palette.h b/samples/ogl/ogledit/palette.h
new file mode 100644 (file)
index 0000000..2e9102f
--- /dev/null
@@ -0,0 +1,66 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        palette.h
+// Purpose:     OGL sample palette
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGLSAMPLE_PALETTE_H_
+#define _OGLSAMPLE_PALETTE_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/wx.h>
+#include <wx/string.h>
+#if 0 // def __WXGTK__
+#include <wx/toolbar.h>
+#else
+#include <wx/tbarsmpl.h>
+#endif
+
+/*
+ * Object editor tool palette
+ *
+ */
+
+// TODO for wxWin: wxToolBar95 cannot be moved to a non-0,0 position!
+// Needs to have a parent window...
+// So use a simple toolbar at present.
+#if 0 // def __WXGTK__
+#define TOOLPALETTECLASS    wxToolBar
+#else
+#define TOOLPALETTECLASS    wxToolBarSimple
+#endif
+
+class EditorToolPalette: public TOOLPALETTECLASS
+{
+ public:
+  int currentlySelected;
+
+  EditorToolPalette(wxWindow *parent, const wxPoint& pos = wxDefaultPosition,
+    const wxSize& size = wxDefaultSize,
+    long style = wxTB_VERTICAL);
+  bool OnLeftClick(int toolIndex, bool toggled);
+  void OnMouseEnter(int toolIndex);
+  void SetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO);
+};
+
+#define PALETTE_TOOL1           1
+#define PALETTE_TOOL2           2
+#define PALETTE_TOOL3           3
+#define PALETTE_TOOL4           4
+#define PALETTE_TOOL5           5
+#define PALETTE_TOOL6           6
+#define PALETTE_TOOL7           7
+#define PALETTE_TOOL8           8
+#define PALETTE_TOOL9           9
+#define PALETTE_ARROW           10
+
+#endif
+    // _OGLSAMPLE_PALETTE_H_
diff --git a/samples/ogl/ogledit/view.cpp b/samples/ogl/ogledit/view.cpp
new file mode 100644 (file)
index 0000000..0d8e582
--- /dev/null
@@ -0,0 +1,337 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        view.cpp
+// Purpose:     Implements view functionality in OGLEdit
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/colordlg.h>
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
+#endif
+
+#include "ogledit.h"
+#include "doc.h"
+#include "view.h"
+#include "palette.h"
+
+IMPLEMENT_DYNAMIC_CLASS(DiagramView, wxView)
+
+BEGIN_EVENT_TABLE(DiagramView, wxView)
+    EVT_MENU(OGLEDIT_CUT, DiagramView::OnCut)
+    EVT_MENU(OGLEDIT_CHANGE_BACKGROUND_COLOUR, DiagramView::OnChangeBackgroundColour)
+    EVT_MENU(OGLEDIT_EDIT_LABEL, DiagramView::OnEditLabel)
+END_EVENT_TABLE()
+
+// What to do when a view is created. Creates actual
+// windows for displaying the view.
+bool DiagramView::OnCreate(wxDocument *doc, long flags)
+{
+  frame = GetMainFrame();
+  canvas = GetMainFrame()->canvas;
+  canvas->view = this;
+
+  SetFrame(frame);
+  Activate(TRUE);
+
+  // Initialize the edit menu Undo and Redo items
+  doc->GetCommandProcessor()->SetEditMenu(((MyFrame *)frame)->editMenu);
+  doc->GetCommandProcessor()->Initialize();
+
+  wxShapeCanvas *shapeCanvas = (wxShapeCanvas *)canvas;
+  DiagramDocument *diagramDoc = (DiagramDocument *)doc;
+  shapeCanvas->SetDiagram(diagramDoc->GetDiagram());
+  diagramDoc->GetDiagram()->SetCanvas(shapeCanvas);
+
+  return TRUE;
+}
+
+#define CENTER  FALSE // Place the drawing to the center of the page
+
+
+// Sneakily gets used for default print/preview
+// as well as drawing on the screen. 
+void DiagramView::OnDraw(wxDC *dc) 
+{ 
+
+  /* You might use THIS code if you were scaling 
+   * graphics of known size to fit on the page. 
+   */ 
+  int w, h; 
+
+  // We need to adjust for the graphic size, a formula will be added 
+  float maxX = 900; 
+  float maxY = 700; 
+  // A better way of find the maxium values would be to search through 
+  // the linked list 
+
+  // Let's have at least 10 device units margin 
+  float marginX = 10; 
+  float marginY = 10; 
+
+  // Add the margin to the graphic size 
+  maxX += (2 * marginX); 
+  maxY += (2 * marginY); 
+
+  // Get the size of the DC in pixels 
+  dc->GetSize (&w, &h); 
+
+  // Calculate a suitable scaling factor 
+  float scaleX = (float) (w / maxX); 
+  float scaleY = (float) (h / maxY); 
+
+  // Use x or y scaling factor, whichever fits on the DC 
+  float actualScale = wxMin (scaleX, scaleY); 
+
+  float posX, posY; 
+  // Calculate the position on the DC for centring the graphic 
+  if (CENTER == TRUE) // center the drawing 
+    { 
+      posX = (float) ((w - (200 * actualScale)) / 2.0); 
+      posY = (float) ((h - (200 * actualScale)) / 2.0); 
+    } 
+  else    // Use defined presets 
+    { 
+      posX = 10; 
+      posY = 35; 
+    } 
+  
+
+  // Set the scale and origin 
+  dc->SetUserScale (actualScale, actualScale); 
+  dc->SetDeviceOrigin ((long) posX, (long) posY); 
+
+  // This part was added to preform the print preview and printing functions 
+
+  dc->BeginDrawing(); // Allows optimization of drawing code under MS Windows. 
+  wxDiagram *diagram_p=((DiagramDocument*)GetDocument())->GetDiagram();  // Get the current diagram
+  if (diagram_p->GetShapeList()) 
+  { 
+    wxCursor *old_cursor = NULL; 
+    wxNode *current = diagram_p->GetShapeList()->First();
+
+    while (current) // Loop through the entire list of shapes 
+    {
+        wxShape *object = (wxShape *)current->Data();
+        if (!object->GetParent())
+        {
+            object->Draw(* dc); // Draw the shape onto our printing dc
+        }
+        current = current->Next();  // Procede to the next shape in the list
+    }
+  }
+  dc->EndDrawing(); // Allows optimization of drawing code under MS Windows. 
+}
+
+void DiagramView::OnUpdate(wxView *sender, wxObject *hint)
+{
+  if (canvas)
+    canvas->Refresh();
+}
+
+// Clean up windows used for displaying the view.
+bool DiagramView::OnClose(bool deleteWindow)
+{
+  if (!GetDocument()->Close())
+    return FALSE;
+
+  DiagramDocument *diagramDoc = (DiagramDocument *)GetDocument();
+  diagramDoc->GetDiagram()->SetCanvas(NULL);
+
+  canvas->Clear();
+  canvas->SetDiagram(NULL);
+  canvas->view = NULL;
+  canvas = NULL;
+
+  wxString s = wxTheApp->GetAppName();
+  if (frame)
+    frame->SetTitle(s);
+
+  SetFrame(NULL);
+
+  Activate(FALSE);
+  
+  return TRUE;
+}
+
+wxShape *DiagramView::FindSelectedShape(void)
+{
+  DiagramDocument *doc = (DiagramDocument *)GetDocument();
+  wxShape *theShape = NULL;
+  wxNode *node = doc->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+    wxShape *eachShape = (wxShape *)node->Data();
+    if ((eachShape->GetParent() == NULL) && eachShape->Selected())
+    {
+      theShape = eachShape;
+      node = NULL;
+    }
+    else node = node->Next();
+  }
+  return theShape;
+}
+
+void DiagramView::OnCut(wxCommandEvent& event)
+{
+  DiagramDocument *doc = (DiagramDocument *)GetDocument();
+
+  wxShape *theShape = FindSelectedShape();
+  if (theShape)
+    doc->GetCommandProcessor()->Submit(new DiagramCommand("Cut", OGLEDIT_CUT, doc, NULL, 0.0, 0.0, TRUE, theShape));
+}
+
+void DiagramView::OnChangeBackgroundColour(wxCommandEvent& event)
+{
+      DiagramDocument *doc = (DiagramDocument *)GetDocument();
+
+      wxShape *theShape = FindSelectedShape();
+      if (theShape)
+      {
+        wxColourData data;
+        data.SetChooseFull(TRUE);
+        data.SetColour(theShape->GetBrush()->GetColour());
+
+        wxColourDialog *dialog = new wxColourDialog(frame, &data);
+        wxBrush *theBrush = NULL;
+        if (dialog->ShowModal() == wxID_OK)
+        {
+          wxColourData retData = dialog->GetColourData();
+          wxColour col = retData.GetColour();
+          theBrush = wxTheBrushList->FindOrCreateBrush(col, wxSOLID);
+        }
+        dialog->Close();
+
+        if (theBrush)
+          doc->GetCommandProcessor()->Submit(new DiagramCommand("Change colour", OGLEDIT_CHANGE_BACKGROUND_COLOUR, doc,
+            theBrush, theShape));
+      }
+}
+
+void DiagramView::OnEditLabel(wxCommandEvent& event)
+{
+      wxShape *theShape = FindSelectedShape();
+      if (theShape)
+      {
+        wxString newLabel = wxGetTextFromUser("Enter new label", "Shape Label", ((MyEvtHandler *)theShape->GetEventHandler())->label);
+        GetDocument()->GetCommandProcessor()->Submit(new DiagramCommand("Edit label", OGLEDIT_EDIT_LABEL, (DiagramDocument*) GetDocument(), newLabel, theShape));
+      }
+}
+
+
+/*
+ * Window implementations
+ */
+
+BEGIN_EVENT_TABLE(MyCanvas, wxShapeCanvas)
+    EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
+    EVT_PAINT(MyCanvas::OnPaint)
+END_EVENT_TABLE()
+
+// Define a constructor for my canvas
+MyCanvas::MyCanvas(wxView *v, wxWindow *parent, wxWindowID id, const wxPoint& pos,
+    const wxSize& size, long style):
+ wxShapeCanvas(parent, id, pos, size, style)
+{
+  SetBackgroundColour(*wxWHITE);
+  view = v;
+}
+
+MyCanvas::~MyCanvas(void)
+{
+}
+
+void MyCanvas::OnLeftClick(double x, double y, int keys)
+{
+  EditorToolPalette *palette = wxGetApp().frame->palette;
+  wxClassInfo *info = NULL;
+  switch (palette->currentlySelected)
+  {
+    case PALETTE_TOOL1:
+    {
+      info = CLASSINFO(wxRectangleShape);
+      break;
+    }
+    case PALETTE_TOOL2:
+    {
+      info = CLASSINFO(wxRoundedRectangleShape);
+      break;
+    }
+    case PALETTE_TOOL3:
+    {
+      info = CLASSINFO(wxEllipseShape);
+      break;
+    }
+    case PALETTE_TOOL4:
+    {
+      info = CLASSINFO(wxDiamondShape);
+      break;
+    }
+    default:
+      break;
+  }
+  if (info)
+  {
+    view->GetDocument()->GetCommandProcessor()->Submit(new DiagramCommand(info->GetClassName(), OGLEDIT_ADD_SHAPE, (DiagramDocument *)view->GetDocument(), info,
+      x, y));
+  }
+}
+
+void MyCanvas::OnRightClick(double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnDragLeft(bool draw, double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnBeginDragLeft(double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnEndDragLeft(double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnDragRight(bool draw, double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnBeginDragRight(double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnEndDragRight(double x, double y, int keys)
+{
+}
+
+void MyCanvas::OnMouseEvent(wxMouseEvent& event)
+{
+    wxShapeCanvas::OnMouseEvent(event);
+}
+
+void MyCanvas::OnPaint(wxPaintEvent& event)
+{
+//  if (GetDiagram())
+    wxShapeCanvas::OnPaint(event);
+}
diff --git a/samples/ogl/ogledit/view.h b/samples/ogl/ogledit/view.h
new file mode 100644 (file)
index 0000000..1498574
--- /dev/null
@@ -0,0 +1,79 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        view.h
+// Purpose:     View-related classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _OGLSAMPLE_VIEW_H_
+#define _OGLSAMPLE_VIEW_H_
+
+#ifdef __GNUG__
+// #pragma interface "view.h"
+#endif
+
+#include "doc.h"
+#include <wx/ogl/ogl.h>
+
+class MyCanvas: public wxShapeCanvas
+{
+// DECLARE_DYNAMIC_CLASS(wxShapeCanvas)
+ protected:
+ public:
+  wxView *view;
+
+  MyCanvas(wxView *view, wxWindow *parent = NULL, wxWindowID id = -1,
+            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
+            long style = wxRETAINED);
+  ~MyCanvas(void);
+
+  void OnMouseEvent(wxMouseEvent& event);
+  void OnPaint(wxPaintEvent& event);
+
+  virtual void OnLeftClick(double x, double y, int keys = 0);
+  virtual void OnRightClick(double x, double y, int keys = 0);
+
+  virtual void OnDragLeft(bool draw, double x, double y, int keys=0); // Erase if draw false
+  virtual void OnBeginDragLeft(double x, double y, int keys=0);
+  virtual void OnEndDragLeft(double x, double y, int keys=0);
+
+  virtual void OnDragRight(bool draw, double x, double y, int keys=0); // Erase if draw false
+  virtual void OnBeginDragRight(double x, double y, int keys=0);
+  virtual void OnEndDragRight(double x, double y, int keys=0);
+
+DECLARE_EVENT_TABLE()
+};
+
+class DiagramView: public wxView
+{
+  DECLARE_DYNAMIC_CLASS(DiagramView)
+ private:
+ public:
+  wxFrame *frame;
+  MyCanvas *canvas;
+  
+  DiagramView(void) { canvas = NULL; frame = NULL; };
+  ~DiagramView(void) {};
+
+  bool OnCreate(wxDocument *doc, long flags);
+  void OnDraw(wxDC *dc);
+  void OnUpdate(wxView *sender, wxObject *hint = NULL);
+  bool OnClose(bool deleteWindow = TRUE);
+
+  wxShape *FindSelectedShape(void);
+
+//  void OnMenuCommand(int cmd);
+
+  void OnCut(wxCommandEvent& event);
+  void OnChangeBackgroundColour(wxCommandEvent& event);
+  void OnEditLabel(wxCommandEvent& event);
+
+DECLARE_EVENT_TABLE()
+};
+
+#endif
+    // _OGLSAMPLE_VIEW_H_
diff --git a/samples/ogl/studio/Makefile b/samples/ogl/studio/Makefile
new file mode 100644 (file)
index 0000000..a576d54
--- /dev/null
@@ -0,0 +1,57 @@
+#
+# File:                Makefile
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:   (c) 2000 Julian Smart
+#
+# Makefile for OGL demo (GTK version)
+#
+# This makefile requires wxWindows/GTK to be 
+# installed (possibly using "make install")
+# on your system.
+#
+
+CPP = gcc
+CC = gcc
+WXCONFIG=../../../../wx-config
+WXINCLUDE=-I../../../../include
+WXLIB=-L../../../../lib
+
+OBJECTS=studio.o cspalette.o csprint.o dialogs.o doc.o mainfrm.o project.o shapes.o symbols.o view.o
+
+studio: $(OBJECTS)
+       $(CPP) -o studio $(OBJECTS) `$(WXCONFIG) --libs` $(WXLIB) -logl
+
+studio.o: studio.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c studio.cpp
+
+cspalette.o: cspalette.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c cspalette.cpp
+
+doc.o: doc.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c doc.cpp
+
+view.o: view.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c view.cpp
+
+dialogs.o: dialogs.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c dialogs.cpp
+
+mainfrm.o: mainfrm.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c mainfrm.cpp
+
+project.o: project.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c project.cpp
+
+shapes.o: shapes.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c shapes.cpp
+
+symbols.o: symbols.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c symbols.cpp
+
+csprint.o: csprint.cpp
+       $(CPP) `$(WXCONFIG) --cflags` -I../../src $(WXINCLUDE) -c csprint.cpp
+
+clean: 
+       rm -f *.o studio
diff --git a/samples/ogl/studio/cspalette.cpp b/samples/ogl/studio/cspalette.cpp
new file mode 100644 (file)
index 0000000..50c5aee
--- /dev/null
@@ -0,0 +1,153 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        cspalette.cpp
+// Purpose:     OGLEdit palette
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/laywin.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "doc.h"
+#include "view.h"
+#include "studio.h"
+#include "cspalette.h"
+#include "symbols.h"
+
+#if defined(__WXGTK__) || defined(__WXMOTIF__)
+#include "bitmaps/arrow.xpm"
+#include "bitmaps/texttool.xpm"
+#endif
+
+/*
+ * Object editor tool palette
+ *
+ */
+
+csEditorToolPalette::csEditorToolPalette(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
+            long style):
+  TOOLPALETTECLASS(parent, id, pos, size, style)
+{
+  m_currentlySelected = -1;
+
+  SetMaxRowsCols(1, 1000);
+}
+
+bool csEditorToolPalette::OnLeftClick(int toolIndex, bool toggled)
+{
+  // BEGIN mutual exclusivity code
+  if (toggled && (m_currentlySelected != -1) && (toolIndex != m_currentlySelected))
+    ToggleTool(m_currentlySelected, FALSE);
+
+  if (toggled)
+    m_currentlySelected = toolIndex;
+  else if (m_currentlySelected == toolIndex)
+    m_currentlySelected = -1;
+  //  END mutual exclusivity code
+
+  return TRUE;
+}
+
+void csEditorToolPalette::OnMouseEnter(int toolIndex)
+{
+    wxString msg("");
+    if (toolIndex == PALETTE_ARROW)
+        msg = "Pointer";
+    else if (toolIndex != -1)
+    {
+        csSymbol* symbol = wxGetApp().GetSymbolDatabase()->FindSymbol(toolIndex);
+        if (symbol)
+            msg = symbol->GetName();
+    }
+    ((wxFrame*) wxGetApp().GetTopWindow())->SetStatusText(msg);
+}
+
+void csEditorToolPalette::SetSize(int x, int y, int width, int height, int sizeFlags)
+{
+  TOOLPALETTECLASS::SetSize(x, y, width, height, sizeFlags);
+}
+
+void csEditorToolPalette::SetSelection(int sel)
+{
+    if ((sel != m_currentlySelected) && (m_currentlySelected != -1))
+    {
+        ToggleTool(m_currentlySelected, FALSE);
+    }
+    m_currentlySelected = sel;
+    ToggleTool(m_currentlySelected, TRUE);
+}
+
+bool csApp::CreatePalette(wxFrame *parent)
+{
+    // First create a layout window
+    wxSashLayoutWindow* win = new wxSashLayoutWindow(parent, ID_LAYOUT_WINDOW_PALETTE, wxDefaultPosition, wxSize(200, 30), wxNO_BORDER|wxSW_3D|wxCLIP_CHILDREN);
+    win->SetDefaultSize(wxSize(10000, 40));
+    win->SetOrientation(wxLAYOUT_HORIZONTAL);
+    win->SetAlignment(wxLAYOUT_TOP);
+    win->SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
+    win->SetSashVisible(wxSASH_BOTTOM, TRUE);
+
+    m_diagramPaletteSashWindow = win;
+
+    m_diagramPaletteSashWindow->Show(FALSE);
+
+  // Load palette bitmaps
+#ifdef __WXMSW__
+    wxBitmap PaletteArrow("arrowtool");
+    wxBitmap TextTool("texttool");
+#elif defined(__WXGTK__) || defined(__WXMOTIF__)
+    wxBitmap PaletteArrow(arrow_xpm);
+    wxBitmap TextTool(texttool_xpm);
+#endif
+
+  csEditorToolPalette *palette = new csEditorToolPalette(m_diagramPaletteSashWindow, ID_DIAGRAM_PALETTE, wxPoint(0, 0), wxSize(-1, -1), wxTB_HORIZONTAL|wxNO_BORDER);
+
+  palette->SetMargins(2, 2);
+
+  palette->SetToolBitmapSize(wxSize(32, 32));
+
+  palette->AddTool(PALETTE_ARROW, PaletteArrow, wxNullBitmap, TRUE, 0, -1, NULL, "Pointer");
+  palette->AddTool(PALETTE_TEXT_TOOL, TextTool, wxNullBitmap, TRUE, 0, -1, NULL, "Text");
+
+  wxNode* node = GetSymbolDatabase()->GetSymbols().First();
+  while (node)
+  {
+    csSymbol* symbol = (csSymbol*) node->Data();
+    wxBitmap* bitmap = GetSymbolDatabase()->CreateToolBitmap(symbol);
+    palette->AddTool(symbol->GetToolId(), *bitmap, wxNullBitmap, TRUE, 0, -1, NULL, symbol->GetName());
+
+    delete bitmap;
+
+    node = node->Next();
+  }
+
+  palette->Realize();
+
+  palette->SetSelection(PALETTE_ARROW);
+  m_diagramPalette = palette;
+
+  return TRUE;
+}
+
diff --git a/samples/ogl/studio/cspalette.h b/samples/ogl/studio/cspalette.h
new file mode 100644 (file)
index 0000000..67a8e63
--- /dev/null
@@ -0,0 +1,57 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        cspalette.h
+// Purpose:     OGL sample palette
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_CSPALETTE_H_
+#define _STUDIO_CSPALETTE_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/wx.h>
+#include <wx/string.h>
+#include <wx/tbarsmpl.h>
+
+/*
+ * Object editor tool palette
+ *
+ */
+
+// TODO for wxWin: wxToolBar95 cannot be moved to a non-0,0 position!
+// Needs to have a parent window...
+// So use a simple toolbar at present.
+#define TOOLPALETTECLASS    wxToolBarSimple
+
+class csEditorToolPalette: public TOOLPALETTECLASS
+{
+public:
+
+  csEditorToolPalette(wxWindow *parent, wxWindowID id = -1, const wxPoint& pos = wxDefaultPosition,
+    const wxSize& size = wxDefaultSize,
+    long style = wxTB_VERTICAL);
+
+  bool OnLeftClick(int toolIndex, bool toggled);
+  void OnMouseEnter(int toolIndex);
+
+  inline int GetSelection() const { return m_currentlySelected; }
+  void SetSelection(int sel);
+
+  void SetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO);
+
+protected:
+  int           m_currentlySelected;
+};
+
+#define PALETTE_ARROW           200
+#define PALETTE_TEXT_TOOL       201
+
+#endif
+    // _STUDIO_CSPALETTE_H_
diff --git a/samples/ogl/studio/csprint.cpp b/samples/ogl/studio/csprint.cpp
new file mode 100644 (file)
index 0000000..6bf5dad
--- /dev/null
@@ -0,0 +1,320 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        csprint.cpp
+// Purpose:     Printing and clipboard functionality
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+#include <wx/clipbrd.h>
+
+#ifdef __WXMSW__
+#include <wx/metafile.h>
+#endif
+
+#include "studio.h"
+#include "doc.h"
+#include "shapes.h"
+#include "view.h"
+
+IMPLEMENT_DYNAMIC_CLASS(wxDiagramClipboard, wxDiagram)
+
+// Copy selection
+bool wxDiagramClipboard::Copy(wxDiagram* diagram)
+{
+    DeleteAllShapes();
+
+    return DoCopy(diagram, this, FALSE, NULL);
+}
+
+// Copy contents to the diagram, with new ids.
+
+bool wxDiagramClipboard::Paste(wxDiagram* diagram, wxDC* dc, int offsetX, int offsetY)
+{
+    return DoCopy(this, diagram, TRUE, dc, offsetX, offsetY);
+}
+
+// Universal copy function (to or from clipboard).
+// TODO:
+// Note that this only works for non-composites so far (nested shapes
+// don't have their old-to-new object mappings stored).
+// Also, lines don't yet get their attachment points moved to the new offset position
+// if they have more than 2 points.
+bool wxDiagramClipboard::DoCopy(wxDiagram* diagramFrom, wxDiagram* diagramTo, bool newIds,
+    wxDC* dc, int offsetX, int offsetY)
+{
+    OnStartCopy(diagramTo);
+
+    wxHashTable mapping(wxKEY_INTEGER);
+
+    // First copy all node shapes.
+    wxList* shapeList = diagramFrom->GetShapeList();
+    wxNode* node = shapeList->First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (((diagramFrom == this) || shape->Selected()) && !shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxShape* newShape = shape->CreateNewCopy();
+            newShape->GetLines().Clear();
+            if (newIds)
+            {
+                newShape->AssignNewIds();
+            }
+            mapping.Put((long) shape, (wxObject*) newShape);
+
+            newShape->SetX(newShape->GetX() + offsetX);
+            newShape->SetY(newShape->GetY() + offsetY);
+
+            OnAddShape(diagramTo, newShape, dc);
+
+        }
+        node = node->Next();
+    }
+
+    node = shapeList->First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (((diagramFrom == this) || shape->Selected()) && shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxLineShape* lineShape = (wxLineShape*) shape;
+            // Only copy a line if its ends are selected too.
+            if ((diagramFrom == this) || (lineShape->GetTo()->Selected() && lineShape->GetFrom()->Selected()))
+            {
+                wxLineShape* newShape = (wxLineShape*) shape->CreateNewCopy();
+                mapping.Put((long) shape, (wxObject*) newShape);
+
+                if (newIds)
+                    newShape->AssignNewIds();
+
+                wxShape* fromShape = (wxShape*) mapping.Get((long) lineShape->GetFrom());
+                wxShape* toShape = (wxShape*) mapping.Get((long) lineShape->GetTo());
+
+                wxASSERT_MSG( (fromShape != NULL), "Could not find 'from' shape");
+                wxASSERT_MSG( (toShape != NULL), "Could not find 'to' shape");
+
+                fromShape->AddLine(newShape, toShape, newShape->GetAttachmentFrom(),
+                  newShape->GetAttachmentTo());
+
+                OnAddShape(diagramTo, newShape, dc);
+
+            }
+        }
+        node = node->Next();
+    }
+
+    // Now make sure line ordering is correct
+    node = shapeList->First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (((diagramFrom == this) || shape->Selected()) && !shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxShape* newShape = (wxShape*) mapping.Get((long) shape);
+
+            // Make a list of all the new lines, in the same order as the old lines.
+            // Then apply the list of new lines to the shape.
+            wxList newLines;
+            wxNode* lineNode = shape->GetLines().First();
+            while (lineNode)
+            {
+                wxLineShape* lineShape = (wxLineShape*) lineNode->Data();
+                if ((diagramFrom == this) || (lineShape->GetTo()->Selected() && lineShape->GetFrom()->Selected()))
+                {
+                    wxLineShape* newLineShape = (wxLineShape*) mapping.Get((long) lineShape);
+
+                    wxASSERT_MSG( (newLineShape != NULL), "Could not find new line shape");
+
+                    newLines.Append(newLineShape);
+                }
+
+                lineNode = lineNode->Next();
+            }
+
+            if (newLines.Number() > 0)
+                newShape->ApplyAttachmentOrdering(newLines);
+        }
+        node = node->Next();
+    }
+
+    OnEndCopy(diagramTo);
+
+    return TRUE;
+}
+
+#ifdef __WXMSW__
+// Draw contents to a Windows metafile device context and a bitmap, and copy
+// these to the Windows clipboard
+bool wxDiagramClipboard::CopyToClipboard(double scale)
+{
+  // Make a metafile DC
+  wxMetaFileDC mfDC;
+  if (mfDC.Ok())
+  {
+    mfDC.SetUserScale(scale, scale);
+
+    // Draw on metafile DC
+    Redraw(mfDC);
+
+    int printWidth = mfDC.MaxX() - mfDC.MinX();
+    int printHeight = mfDC.MaxY() - mfDC.MinY();
+    int maxX = (int)mfDC.MaxX();
+    int maxY = (int)mfDC.MaxY();
+    wxMetaFile *mf = mfDC.Close();
+
+    // Set to a bitmap memory DC
+    wxBitmap *newBitmap = new wxBitmap((int)(maxX + 10), (int)(maxY + 10));
+    if (!newBitmap->Ok())
+    {
+      delete newBitmap;
+      
+      char buf[200];
+      sprintf(buf, "Sorry, could not allocate clipboard bitmap (%dx%d)", (maxX+10), (maxY+10));
+      wxMessageBox(buf, "Clipboard copy problem");
+      return FALSE;
+    }
+
+    wxMemoryDC memDC;
+    memDC.SelectObject(*newBitmap);
+    memDC.Clear();
+
+    // Now draw on memory bitmap DC
+    Redraw(memDC);
+
+    memDC.SelectObject(wxNullBitmap);
+
+    // Open clipboard and set the data
+    if (wxOpenClipboard())
+    {
+        wxEmptyClipboard();
+
+        // Copy the bitmap to the clipboard
+        wxSetClipboardData(wxDF_BITMAP, newBitmap, 0, 0);
+
+#if 0 // TODO: replace this code (wxEnhMetaFile doesn't have SetClipboard)
+        if (mf)
+        {
+            // Copy the metafile to the clipboard
+            // Allow a small margin
+            bool success = mf->SetClipboard((int)(mfDC.MaxX() + 15), (int)(mfDC.MaxY() + 15));
+        }
+#endif
+
+        // Close clipboard
+        wxCloseClipboard();
+    }
+    
+    delete newBitmap;
+    delete mf;
+
+  }
+  return TRUE;
+}
+#endif
+    // __WXMSW__
+
+// Override this to e.g. have the shape added through a Do/Undo command system.
+// By default, we'll just add it directly to the destination diagram.
+bool wxDiagramClipboard::OnAddShape(wxDiagram* diagramTo, wxShape* newShape, wxDC* dc)
+{
+    diagramTo->AddShape(newShape);
+
+    if (dc && (diagramTo != this))
+    {
+        newShape->Select(TRUE, dc);
+    }
+
+    return TRUE;
+}
+
+/*
+ * csDiagramClipboard
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(csDiagramClipboard, wxDiagramClipboard)
+
+// Start/end copying
+bool csDiagramClipboard::OnStartCopy(wxDiagram* diagramTo)
+{
+    // Do nothing if copying to the clipboard
+    if (diagramTo == this)
+        return TRUE;
+
+    // Deselect all objects initially.
+
+    csDiagram* diagram = (csDiagram*) diagramTo;
+    csDiagramDocument* doc = diagram->GetDocument();
+    ((csDiagramView*)doc->GetFirstView())->SelectAll(FALSE);
+
+    m_currentCmd = new csDiagramCommand("Paste", doc);
+
+    return TRUE;
+}
+
+bool csDiagramClipboard::OnEndCopy(wxDiagram* diagramTo)
+{
+    // Do nothing if copying to the clipboard
+    if (diagramTo == this)
+        return TRUE;
+
+    csDiagram* diagram = (csDiagram*) diagramTo;
+    csDiagramDocument* doc = diagram->GetDocument();
+
+    if (m_currentCmd)
+    {
+        if (m_currentCmd->GetStates().Number() == 0)
+        {
+            delete m_currentCmd;
+        }
+        else
+        {
+            doc->GetCommandProcessor()->Submit(m_currentCmd);
+            m_currentCmd = NULL;
+        }
+    }
+    return TRUE;
+}
+
+// Use the command framework to add the shapes, if we're copying to a diagram and
+// not the clipboard.
+bool csDiagramClipboard::OnAddShape(wxDiagram* diagramTo, wxShape* newShape, wxDC* dc)
+{
+    if (diagramTo == this)
+    {
+        diagramTo->AddShape(newShape);
+    }
+    else
+    {
+        csDiagram* diagram = (csDiagram*) diagramTo;
+        csDiagramDocument* doc = diagram->GetDocument();
+
+        if (newShape->IsKindOf(CLASSINFO(wxLineShape)))
+            m_currentCmd->AddState(new csCommandState(ID_CS_ADD_LINE_SELECT, newShape, NULL));
+        else
+            m_currentCmd->AddState(new csCommandState(ID_CS_ADD_SHAPE_SELECT, newShape, NULL));
+    }
+
+    return TRUE;
+}
+
+
diff --git a/samples/ogl/studio/dialogs.cpp b/samples/ogl/studio/dialogs.cpp
new file mode 100644 (file)
index 0000000..9fef6d1
--- /dev/null
@@ -0,0 +1,525 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        dialogs.cpp
+// Purpose:     Implements Studio dialogs
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/resource.h>
+#include "dialogs.h"
+#include "doc.h"
+#include "view.h"
+#include "studio.h"
+#include "studio_resources.h"
+
+IMPLEMENT_CLASS(csLabelEditingDialog, wxDialog)
+
+BEGIN_EVENT_TABLE(csLabelEditingDialog, wxDialog)
+    EVT_BUTTON(wxID_OK, csLabelEditingDialog::OnOK)
+END_EVENT_TABLE()
+
+csLabelEditingDialog::csLabelEditingDialog(wxWindow* parent)
+{
+    LoadFromResource(parent, "shape_label_dialog");
+
+    // Accelerators
+    wxAcceleratorEntry entries[1];
+    entries[0].Set(wxACCEL_CTRL, WXK_RETURN, wxID_OK);
+    wxAcceleratorTable accel(1, entries);
+    SetAcceleratorTable(accel);
+
+    Centre();
+
+    wxTextCtrl* textCtrl = (wxTextCtrl*) FindWindow(ID_LABELTEXT);
+    wxASSERT( (textCtrl != NULL) );
+
+//    textCtrl->SetAcceleratorTable(accel);
+
+    textCtrl->SetFocus();
+}
+
+void csLabelEditingDialog::OnOK(wxCommandEvent& event)
+{
+    wxTextCtrl* textCtrl = (wxTextCtrl*) FindWindow(ID_LABELTEXT);
+    wxASSERT( (textCtrl != NULL) );
+
+    SetShapeLabel(textCtrl->GetValue());
+
+    wxDialog::OnOK(event);
+}
+
+void csLabelEditingDialog::SetShapeLabel(const wxString& label)
+{
+    wxTextCtrl* textCtrl = (wxTextCtrl*) FindWindow(ID_LABELTEXT);
+    wxASSERT( (textCtrl != NULL) );
+
+    m_label = label;
+
+    textCtrl->SetValue(label);
+}
+
+IMPLEMENT_CLASS(csSettingsDialog, wxDialog)
+
+BEGIN_EVENT_TABLE(csSettingsDialog, wxDialog)
+    EVT_BUTTON(wxID_OK, csSettingsDialog::OnOK)
+END_EVENT_TABLE()
+
+#define PROPERTY_DIALOG_WIDTH   400
+#define PROPERTY_DIALOG_HEIGHT  400
+
+// For 400x400 settings dialog, size your panels to about 375x325 in dialog editor
+
+csSettingsDialog::csSettingsDialog(wxWindow* parent):
+    wxDialog(parent, -1, "Settings", wxPoint(0, 0), wxSize(PROPERTY_DIALOG_WIDTH, PROPERTY_DIALOG_HEIGHT))
+{
+    m_generalSettings = NULL;
+    m_diagramSettings = NULL;
+
+    m_notebook = new wxNotebook(this, ID_PROPERTY_NOTEBOOK,
+         wxPoint(2, 2), wxSize(PROPERTY_DIALOG_WIDTH - 4, PROPERTY_DIALOG_HEIGHT - 4));
+
+    m_generalSettings = new wxPanel;
+
+    bool success = m_generalSettings->LoadFromResource(m_notebook, "general_settings_dialog");
+    wxASSERT_MSG( (success), "Could not load general settings panel.");
+    m_notebook->AddPage(m_generalSettings, "General", TRUE);
+
+    m_diagramSettings = new wxPanel;
+
+    success = m_diagramSettings->LoadFromResource(m_notebook, "diagram_settings_dialog");
+    wxASSERT_MSG( (success), "Could not load diagram settings panel.");
+    m_notebook->AddPage(m_diagramSettings, "Diagram");
+
+    int largeButtonWidth = 70;
+    int largeButtonHeight = 22;
+
+    wxButton* okButton = new wxButton(this, wxID_OK, "OK", wxPoint(0, 0), wxSize(largeButtonWidth, largeButtonHeight));
+    wxButton* cancelButton = new wxButton(this, wxID_CANCEL, "Cancel", wxPoint(0, 0), wxSize(largeButtonWidth, largeButtonHeight));
+    wxButton* helpButton = new wxButton(this, wxID_HELP, "Help", wxPoint(0, 0), wxSize(largeButtonWidth, largeButtonHeight));
+
+    // Constraints for the notebook
+    wxLayoutConstraints *c = new wxLayoutConstraints;
+    c->top.SameAs     (this, wxTop, 5);
+    c->left.SameAs    (this, wxLeft, 5);
+    c->right.SameAs   (this, wxRight, 5);
+    c->bottom.SameAs  (cancelButton, wxTop, 5);
+    m_notebook->SetConstraints(c);
+
+    // Constraints for the Help button
+    c = new wxLayoutConstraints;
+    c->width.AsIs();
+    c->height.AsIs();
+    c->right.SameAs    (this, wxRight, 5);
+    c->bottom.SameAs   (this, wxBottom, 5);
+    helpButton->SetConstraints(c);
+
+    // Constraints for the Cancel button
+    c = new wxLayoutConstraints;
+    c->width.AsIs();
+    c->height.AsIs();
+    c->right.SameAs    (helpButton, wxLeft, 5);
+    c->bottom.SameAs   (this, wxBottom, 5);
+    cancelButton->SetConstraints(c);
+
+    // Constraints for the OK button
+    c = new wxLayoutConstraints;
+    c->width.AsIs();
+    c->height.AsIs();
+    c->right.SameAs    (cancelButton, wxLeft, 5);
+    c->bottom.SameAs   (this, wxBottom, 5);
+    okButton->SetConstraints(c);
+
+    okButton->SetDefault();
+    okButton->SetFocus();
+
+    Layout();
+    Centre(wxBOTH);
+}
+
+void csSettingsDialog::OnOK(wxCommandEvent& event)
+{
+    wxDialog::OnOK(event);
+}
+
+bool csSettingsDialog::TransferDataToWindow()
+{
+    wxTextCtrl* gridSpacing = (wxTextCtrl*) m_diagramSettings->FindWindow(ID_GRID_SPACING);
+    wxASSERT_MSG( (gridSpacing != (wxTextCtrl*) NULL), "Could not find grid spacing control.");
+
+    wxChoice* gridStyle = (wxChoice*) m_diagramSettings->FindWindow(ID_GRID_STYLE);
+    wxASSERT_MSG( (gridStyle != (wxChoice*) NULL), "Could not find grid style control.");
+
+    gridStyle->SetSelection(wxGetApp().GetGridStyle());
+
+    wxString str;
+    str.Printf("%d", wxGetApp().GetGridSpacing());
+    gridSpacing->SetValue(str);
+
+    return TRUE;
+}
+
+bool csSettingsDialog::TransferDataFromWindow()
+{
+    wxTextCtrl* gridSpacing = (wxTextCtrl*) m_diagramSettings->FindWindow(ID_GRID_SPACING);
+    wxASSERT_MSG( (gridSpacing != (wxTextCtrl*) NULL), "Could not find grid spacing control.");
+
+    wxChoice* gridStyle = (wxChoice*) m_diagramSettings->FindWindow(ID_GRID_STYLE);
+    wxASSERT_MSG( (gridStyle != (wxChoice*) NULL), "Could not find grid style control.");
+
+    wxGetApp().SetGridStyle(gridStyle->GetSelection());
+    wxGetApp().SetGridSpacing(atoi(gridSpacing->GetValue()));
+
+    if (wxGetApp().GetGridStyle() == csGRID_STYLE_DOTTED)
+    {
+        wxMessageBox("Dotted grid style not yet implemented.", "Studio", wxICON_EXCLAMATION);
+        return FALSE;
+    }
+
+    // Apply settings to all open diagram documents
+    wxNode* node = wxGetApp().GetDocManager()->GetDocuments().First();
+    while (node)
+    {
+        wxDocument* doc = (wxDocument*) node->Data();
+        if (doc->IsKindOf(CLASSINFO(csDiagramDocument)))
+        {
+            csDiagramDocument* diagramDoc = (csDiagramDocument*) doc;
+            wxDiagram* diagram = (wxDiagram*) diagramDoc->GetDiagram();
+
+            diagram->SetGridSpacing((double) wxGetApp().GetGridSpacing());
+            switch (wxGetApp().GetGridStyle())
+            {
+                case csGRID_STYLE_NONE:
+                {
+                    diagram->SetSnapToGrid(FALSE);
+                    break;
+                }
+                case csGRID_STYLE_INVISIBLE:
+                {
+                    diagram->SetSnapToGrid(TRUE);
+                    break;
+                }
+                case csGRID_STYLE_DOTTED:
+                {
+                    // TODO (not implemented in OGL)
+                    break;
+                }
+            }
+        }
+        node = node->Next();
+    }
+
+    return TRUE;
+}
+
+/*
+ * Shape properties dialog (tabbed)
+ */
+
+
+IMPLEMENT_CLASS(csShapePropertiesDialog, wxDialog)
+
+BEGIN_EVENT_TABLE(csShapePropertiesDialog, wxDialog)
+    EVT_BUTTON(wxID_OK, csShapePropertiesDialog::OnOK)
+END_EVENT_TABLE()
+
+#define SHAPE_PROPERTY_DIALOG_WIDTH   400
+#define SHAPE_PROPERTY_DIALOG_HEIGHT  400
+
+// For 400x400 settings dialog, size your panels to about 375x325 in dialog editor
+
+csShapePropertiesDialog::csShapePropertiesDialog(wxWindow* parent, const wxString& title,
+  wxPanel* attributeDialog, const wxString& attributeDialogName):
+    wxDialog(parent, -1, title, wxPoint(0, 0), wxSize(SHAPE_PROPERTY_DIALOG_WIDTH, SHAPE_PROPERTY_DIALOG_HEIGHT))
+{
+    m_attributeDialog = attributeDialog;
+    m_alternativeAttributeDialog = NULL;
+    m_generalPropertiesDialog = NULL;
+
+    m_notebook = new wxNotebook(this, ID_SHAPE_PROPERTY_NOTEBOOK,
+         wxPoint(2, 2), wxSize(SHAPE_PROPERTY_DIALOG_WIDTH - 4, SHAPE_PROPERTY_DIALOG_HEIGHT - 4));
+
+    m_generalPropertiesDialog = new csGeneralShapePropertiesDialog;
+    bool success = m_generalPropertiesDialog->LoadFromResource(m_notebook, "general_shape_properties_dialog");
+    wxASSERT_MSG( (success), "Could not load general properties panel.");
+    m_notebook->AddPage(m_generalPropertiesDialog, "General");
+
+    success = m_attributeDialog->LoadFromResource(m_notebook, attributeDialogName);
+    if (!success)
+    {
+        wxMessageBox("Could not load the attribute dialog for this shape.", "Studio", wxICON_EXCLAMATION);
+        delete m_attributeDialog;
+        m_attributeDialog = NULL;
+    }
+    else
+    {
+        m_notebook->AddPage(m_attributeDialog, "Attributes");
+    }
+
+    // Try the alternative dialog (test code)
+    wxString str(attributeDialogName);
+    str += "1";
+    m_alternativeAttributeDialog = new wxPanel;
+    success = m_alternativeAttributeDialog->LoadFromResource(m_notebook, str);
+    if (success)
+    {
+        m_notebook->AddPage(m_alternativeAttributeDialog, "Attributes (alternative)");
+    }
+    else
+    {
+        delete m_alternativeAttributeDialog;
+        m_alternativeAttributeDialog = NULL;
+    }
+
+    int largeButtonWidth = 70;
+    int largeButtonHeight = 22;
+
+    wxButton* okButton = new wxButton(this, wxID_OK, "OK", wxPoint(0, 0), wxSize(largeButtonWidth, largeButtonHeight));
+    wxButton* cancelButton = new wxButton(this, wxID_CANCEL, "Cancel", wxPoint(0, 0), wxSize(largeButtonWidth, largeButtonHeight));
+    wxButton* helpButton = new wxButton(this, wxID_HELP, "Help", wxPoint(0, 0), wxSize(largeButtonWidth, largeButtonHeight));
+
+    // Constraints for the notebook
+    wxLayoutConstraints *c = new wxLayoutConstraints;
+    c->top.SameAs     (this, wxTop, 5);
+    c->left.SameAs    (this, wxLeft, 5);
+    c->right.SameAs   (this, wxRight, 5);
+    c->bottom.SameAs  (helpButton, wxTop, 5);
+    m_notebook->SetConstraints(c);
+
+    // Constraints for the Help button
+    c = new wxLayoutConstraints;
+    c->width.AsIs();
+    c->height.AsIs();
+    c->right.SameAs    (this, wxRight, 5);
+    c->bottom.SameAs   (this, wxBottom, 5);
+    helpButton->SetConstraints(c);
+
+    // Constraints for the Cancel button
+    c = new wxLayoutConstraints;
+    c->width.AsIs();
+    c->height.AsIs();
+    c->right.SameAs    (helpButton, wxLeft, 5);
+    c->bottom.SameAs   (this, wxBottom, 5);
+    cancelButton->SetConstraints(c);
+
+    // Constraints for the OK button
+    c = new wxLayoutConstraints;
+    c->width.AsIs();
+    c->height.AsIs();
+    c->right.SameAs    (cancelButton, wxLeft, 5);
+    c->bottom.SameAs   (this, wxBottom, 5);
+    okButton->SetConstraints(c);
+
+    okButton->SetDefault();
+    okButton->SetFocus();
+
+    SetDefaults();
+
+    Layout();
+    Centre(wxBOTH);
+}
+
+void csShapePropertiesDialog::OnOK(wxCommandEvent& event)
+{
+    wxTextCtrl* textCtrl = (wxTextCtrl*) m_generalPropertiesDialog->FindWindow(ID_LABELTEXT);
+    wxASSERT( (textCtrl != NULL) );
+
+    m_generalPropertiesDialog->SetShapeLabel(textCtrl->GetValue());
+
+    wxDialog::OnOK(event);
+}
+
+// Set some suitable defaults in the attribute dialogs (in the first instance,
+// just set all wxChoices to the first element)
+void csShapePropertiesDialog::SetDefaults()
+{
+    if (!m_attributeDialog)
+        return;
+
+    wxNode* node = m_attributeDialog->GetChildren().First();
+    while (node)
+    {
+        wxWindow* child = (wxWindow*) node->Data();
+        if (child->IsKindOf(CLASSINFO(wxChoice)))
+        {
+            wxChoice* choice = (wxChoice*) child;
+            choice->SetSelection(0);
+        }
+        node = node->Next();
+    }
+
+    if (!m_alternativeAttributeDialog)
+        return;
+
+    node = m_alternativeAttributeDialog->GetChildren().First();
+    while (node)
+    {
+        wxWindow* child = (wxWindow*) node->Data();
+        if (child->IsKindOf(CLASSINFO(wxChoice)))
+        {
+            wxChoice* choice = (wxChoice*) child;
+            choice->SetSelection(0);
+        }
+        node = node->Next();
+    }
+}
+
+/*
+ * csGeneralShapePropertiesDialog
+ */
+
+IMPLEMENT_CLASS(csGeneralShapePropertiesDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csGeneralShapePropertiesDialog, wxPanel)
+END_EVENT_TABLE()
+
+csGeneralShapePropertiesDialog::csGeneralShapePropertiesDialog()
+{
+}
+
+void csGeneralShapePropertiesDialog::SetShapeLabel(const wxString& label)
+{
+    wxTextCtrl* textCtrl = (wxTextCtrl*) FindWindow(ID_LABELTEXT);
+    wxASSERT( (textCtrl != NULL) );
+
+    m_label = label;
+
+    textCtrl->SetValue(label);
+}
+
+/*
+ * csThinRectangleDialog
+ */
+
+IMPLEMENT_CLASS(csThinRectangleDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csThinRectangleDialog, wxPanel)
+END_EVENT_TABLE()
+
+csThinRectangleDialog::csThinRectangleDialog()
+{
+}
+
+/*
+ * csWideRectangleDialog
+ */
+
+IMPLEMENT_CLASS(csWideRectangleDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csWideRectangleDialog, wxPanel)
+END_EVENT_TABLE()
+
+csWideRectangleDialog::csWideRectangleDialog()
+{
+}
+
+/*
+ * csTriangleDialog
+ */
+
+IMPLEMENT_CLASS(csTriangleDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csTriangleDialog, wxPanel)
+END_EVENT_TABLE()
+
+csTriangleDialog::csTriangleDialog()
+{
+}
+
+/*
+ * csSemiCircleDialog
+ */
+
+IMPLEMENT_CLASS(csSemiCircleDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csSemiCircleDialog, wxPanel)
+END_EVENT_TABLE()
+
+csSemiCircleDialog::csSemiCircleDialog()
+{
+}
+
+/*
+ * csCircleDialog
+ */
+
+IMPLEMENT_CLASS(csCircleDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csCircleDialog, wxPanel)
+END_EVENT_TABLE()
+
+csCircleDialog::csCircleDialog()
+{
+}
+
+/*
+ * csCircleShadowDialog
+ */
+
+IMPLEMENT_CLASS(csCircleShadowDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csCircleShadowDialog, wxPanel)
+END_EVENT_TABLE()
+
+csCircleShadowDialog::csCircleShadowDialog()
+{
+}
+
+/*
+ * csOctagonDialog
+ */
+
+IMPLEMENT_CLASS(csOctagonDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csOctagonDialog, wxPanel)
+END_EVENT_TABLE()
+
+csOctagonDialog::csOctagonDialog()
+{
+}
+
+/*
+ * csGroupDialog
+ */
+
+IMPLEMENT_CLASS(csGroupDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csGroupDialog, wxPanel)
+END_EVENT_TABLE()
+
+csGroupDialog::csGroupDialog()
+{
+}
+
+/*
+ * csTextBoxDialog
+ */
+
+IMPLEMENT_CLASS(csTextBoxDialog, wxPanel)
+
+BEGIN_EVENT_TABLE(csTextBoxDialog, wxPanel)
+END_EVENT_TABLE()
+
+csTextBoxDialog::csTextBoxDialog()
+{
+}
+
+
diff --git a/samples/ogl/studio/dialogs.h b/samples/ogl/studio/dialogs.h
new file mode 100644 (file)
index 0000000..4a97f8f
--- /dev/null
@@ -0,0 +1,248 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        dialogs.h
+// Purpose:     Miscellaneous dialogs
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_DIALOGS_H_
+#define _STUDIO_DIALOGS_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/wx.h>
+#include <wx/notebook.h>
+
+/*
+ * Label editing dialog (about to become obsolete)
+ */
+
+class csLabelEditingDialog: public wxDialog
+{
+DECLARE_CLASS(csLabelEditingDialog)
+public:
+    csLabelEditingDialog(wxWindow* parent);
+
+    void SetShapeLabel(const wxString& label);
+    inline wxString GetShapeLabel() const { return m_label; }
+
+    void OnOK(wxCommandEvent& event);
+
+protected:
+    wxString    m_label;
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * Settings dialog (tabbed)
+ */
+
+class csSettingsDialog: public wxDialog
+{
+DECLARE_CLASS(csSettingsDialog)
+public:
+    csSettingsDialog(wxWindow* parent);
+
+    void OnOK(wxCommandEvent& event);
+
+    virtual bool TransferDataToWindow();
+    virtual bool TransferDataFromWindow();
+
+protected:
+
+    wxPanel*    m_generalSettings;
+    wxPanel*    m_diagramSettings;
+    wxNotebook* m_notebook;
+
+DECLARE_EVENT_TABLE()
+};
+
+#define ID_PROPERTY_NOTEBOOK    1000
+#define ID_GENERAL_SETTINGS     1002
+#define ID_DIAGRAM_SETTINGS     1003
+
+/*
+ * csGeneralShapePropertiesDialog
+ * Name, description etc.
+ */
+
+class csGeneralShapePropertiesDialog: public wxPanel
+{
+DECLARE_CLASS(csGeneralShapePropertiesDialog)
+public:
+    csGeneralShapePropertiesDialog();
+
+    void SetShapeLabel(const wxString& label);
+    inline wxString GetShapeLabel() const { return m_label; }
+
+protected:
+    wxString    m_label;
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * Shape properties dialog (tabbed)
+ */
+
+class csShapePropertiesDialog: public wxDialog
+{
+DECLARE_CLASS(csShapePropertiesDialog)
+public:
+    csShapePropertiesDialog(wxWindow* parent, const wxString& title, wxPanel* attributeDialog, const wxString& attributeDialogName);
+
+    void OnOK(wxCommandEvent& event);
+
+    // Set some suitable defaults in the attribute dialogs (in the first instance,
+    // just set all wxChoices to the first element)
+    void SetDefaults();
+    
+// Accessors
+    csGeneralShapePropertiesDialog* GetGeneralPropertiesDialog() const { return m_generalPropertiesDialog; }
+
+
+protected:
+
+    // Attributes, specific to each shape
+    wxPanel*                        m_attributeDialog;
+    wxPanel*                        m_alternativeAttributeDialog;
+
+   // General properties, same for each shape, e.g. name/description
+    csGeneralShapePropertiesDialog* m_generalPropertiesDialog;
+
+    wxNotebook* m_notebook;
+
+DECLARE_EVENT_TABLE()
+};
+
+#define ID_SHAPE_PROPERTY_NOTEBOOK    1000
+
+//// Specific attribute-editing panel classes below here
+
+/*
+ * csThinRectangleDialog
+ */
+
+class csThinRectangleDialog: public wxPanel
+{
+DECLARE_CLASS(csThinRectangleDialog)
+public:
+    csThinRectangleDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csWideRectangleDialog
+ */
+
+class csWideRectangleDialog: public wxPanel
+{
+DECLARE_CLASS(csWideRectangleDialog)
+public:
+    csWideRectangleDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csTriangleDialog
+ */
+
+class csTriangleDialog: public wxPanel
+{
+DECLARE_CLASS(csTriangleDialog)
+public:
+    csTriangleDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csSemiCircleDialog
+ */
+
+class csSemiCircleDialog: public wxPanel
+{
+DECLARE_CLASS(csSemiCircleDialog)
+public:
+    csSemiCircleDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csCircleDialog
+ */
+
+class csCircleDialog: public wxPanel
+{
+DECLARE_CLASS(csCircleDialog)
+public:
+    csCircleDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csCircleShadowDialog
+ */
+
+class csCircleShadowDialog: public wxPanel
+{
+DECLARE_CLASS(csCircleShadowDialog)
+public:
+    csCircleShadowDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csOctagonDialog
+ */
+
+class csOctagonDialog: public wxPanel
+{
+DECLARE_CLASS(csOctagonDialog)
+public:
+    csOctagonDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csGroupDialog
+ */
+
+class csGroupDialog: public wxPanel
+{
+DECLARE_CLASS(csGroupDialog)
+public:
+    csGroupDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+/*
+ * csTextBoxDialog
+ */
+
+class csTextBoxDialog: public wxPanel
+{
+DECLARE_CLASS(csTextBoxDialog)
+public:
+    csTextBoxDialog();
+
+DECLARE_EVENT_TABLE()
+};
+
+
+#endif
+    // _STUDIO_DIALOGS_H_
diff --git a/samples/ogl/studio/doc.cpp b/samples/ogl/studio/doc.cpp
new file mode 100644 (file)
index 0000000..ea8cb82
--- /dev/null
@@ -0,0 +1,598 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        doc.cpp
+// Purpose:     Implements document functionality
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include "studio.h"
+#include "doc.h"
+#include "view.h"
+#include <wx/ogl/basicp.h>
+
+IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument, wxDocument)
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+
+csDiagramDocument::csDiagramDocument():m_diagram(this)
+{
+}
+
+#ifdef _MSC_VER
+#pragma warning(default:4355)
+#endif
+
+csDiagramDocument::~csDiagramDocument()
+{
+}
+
+bool csDiagramDocument::OnCloseDocument()
+{
+  m_diagram.DeleteAllShapes();
+  return TRUE;
+}
+
+bool csDiagramDocument::OnSaveDocument(const wxString& file)
+{
+  if (file == "")
+    return FALSE;
+
+  if (!m_diagram.SaveFile(file))
+  {
+    wxString msgTitle;
+    if (wxTheApp->GetAppName() != "")
+        msgTitle = wxTheApp->GetAppName();
+    else
+        msgTitle = wxString("File error");
+
+    (void)wxMessageBox("Sorry, could not open this file for saving.", msgTitle, wxOK | wxICON_EXCLAMATION,
+      GetDocumentWindow());
+    return FALSE;
+  }
+
+  Modify(FALSE);
+  SetFilename(file);
+  return TRUE;
+}
+       
+bool csDiagramDocument::OnOpenDocument(const wxString& file)
+{
+  if (!OnSaveModified())
+    return FALSE;
+
+  wxString msgTitle;
+  if (wxTheApp->GetAppName() != "")
+    msgTitle = wxTheApp->GetAppName();
+  else
+    msgTitle = wxString("File error");
+
+  m_diagram.DeleteAllShapes();
+  if (!m_diagram.LoadFile(file))
+  {
+    (void)wxMessageBox("Sorry, could not open this file.", msgTitle, wxOK|wxICON_EXCLAMATION,
+     GetDocumentWindow());
+    return FALSE;
+  }
+  SetFilename(file, TRUE);
+  Modify(FALSE);
+  UpdateAllViews();
+  
+  return TRUE;
+}
+       
+
+/*
+ * Implementation of drawing command
+ */
+
+csDiagramCommand::csDiagramCommand(const wxString& name, csDiagramDocument *doc,
+    csCommandState* onlyState):
+  wxCommand(TRUE, name)
+{
+  m_doc = doc;
+
+  if (onlyState)
+  {
+    AddState(onlyState);
+  }
+}
+
+csDiagramCommand::~csDiagramCommand()
+{
+    wxNode* node = m_states.First();
+    while (node)
+    {
+        csCommandState* state = (csCommandState*) node->Data();
+        delete state;
+        node = node->Next();
+    }
+}
+
+void csDiagramCommand::AddState(csCommandState* state)
+{
+    state->m_doc = m_doc;
+//    state->m_cmd = m_cmd;
+    m_states.Append(state);
+}
+
+// Insert a state at the beginning of the list
+void csDiagramCommand::InsertState(csCommandState* state)
+{
+    state->m_doc = m_doc;
+//    state->m_cmd = m_cmd;
+    m_states.Insert(state);
+}
+
+// Schedule all lines connected to the states to be cut.
+void csDiagramCommand::RemoveLines()
+{
+    wxNode* node = m_states.First();
+    while (node)
+    {
+        csCommandState* state = (csCommandState*) node->Data();
+        wxShape* shape = state->GetShapeOnCanvas();
+        wxASSERT( (shape != NULL) );
+
+        wxNode *node1 = shape->GetLines().First();
+        while (node1)
+        {
+            wxLineShape *line = (wxLineShape *)node1->Data();
+            if (!FindStateByShape(line))
+            {
+                csCommandState* newState = new csCommandState(ID_CS_CUT, NULL, line);
+                InsertState(newState);
+            }
+
+            node1 = node1->Next();
+        }
+        node = node->Next();
+    }
+}
+
+csCommandState* csDiagramCommand::FindStateByShape(wxShape* shape)
+{
+    wxNode* node = m_states.First();
+    while (node)
+    {
+        csCommandState* state = (csCommandState*) node->Data();
+        if (shape == state->GetShapeOnCanvas() || shape == state->GetSavedState())
+            return state;
+        node = node->Next();
+    }
+    return NULL;
+}
+
+bool csDiagramCommand::Do()
+{
+    wxNode* node = m_states.First();
+    while (node)
+    {
+        csCommandState* state = (csCommandState*) node->Data();
+        if (!state->Do())
+            return FALSE;
+        node = node->Next();
+    }
+    return TRUE;
+}
+
+bool csDiagramCommand::Undo()
+{
+    // Undo in reverse order, so e.g. shapes get added
+    // back before the lines do.
+    wxNode* node = m_states.Last();
+    while (node)
+    {
+        csCommandState* state = (csCommandState*) node->Data();
+        if (!state->Undo())
+            return FALSE;
+        node = node->Previous();
+    }
+    return TRUE;
+}
+
+csCommandState::csCommandState(int cmd, wxShape* savedState, wxShape* shapeOnCanvas)
+{
+    m_cmd = cmd;
+    m_doc = NULL;
+    m_savedState = savedState;
+    m_shapeOnCanvas = shapeOnCanvas;
+    m_linePositionFrom = 0;
+    m_linePositionTo = 0;
+}
+
+csCommandState::~csCommandState()
+{
+    if (m_savedState)
+    {
+        m_savedState->SetCanvas(NULL);
+        delete m_savedState;
+    }
+}
+
+bool csCommandState::Do()
+{
+  switch (m_cmd)
+  {
+    case ID_CS_CUT:
+    {
+        // New state is 'nothing' - maybe pass shape ID to state so we know what
+        // we're talking about.
+        // Then save old shape in m_savedState (actually swap pointers)
+
+        wxASSERT( (m_shapeOnCanvas != NULL) );
+        wxASSERT( (m_savedState == NULL) ); // new state will be 'nothing'
+        wxASSERT( (m_doc != NULL) );
+
+        wxShapeCanvas* canvas = m_shapeOnCanvas->GetCanvas();
+
+        // In case this is a line
+        wxShape* lineFrom = NULL;
+        wxShape* lineTo = NULL;
+        int attachmentFrom = 0, attachmentTo = 0;
+
+        if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            // Store the from/to info to save in the line shape
+            wxLineShape* lineShape = (wxLineShape*) m_shapeOnCanvas;
+            lineFrom = lineShape->GetFrom();
+            lineTo = lineShape->GetTo();
+            attachmentFrom = lineShape->GetAttachmentFrom();
+            attachmentTo = lineShape->GetAttachmentTo();
+
+            m_linePositionFrom = lineFrom->GetLinePosition(lineShape);
+            m_linePositionTo = lineTo->GetLinePosition(lineShape);
+        }
+
+        m_shapeOnCanvas->Select(FALSE);
+        ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, FALSE);
+
+        m_shapeOnCanvas->Unlink();
+        
+        m_doc->GetDiagram()->RemoveShape(m_shapeOnCanvas);
+
+        m_savedState = m_shapeOnCanvas;
+
+        if (m_savedState->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            // Restore the from/to info for future reference
+            wxLineShape* lineShape = (wxLineShape*) m_savedState;
+            lineShape->SetFrom(lineFrom);
+            lineShape->SetTo(lineTo);
+            lineShape->SetAttachments(attachmentFrom, attachmentTo);
+
+            wxClientDC dc(canvas);
+            canvas->PrepareDC(dc);
+
+            lineFrom->MoveLinks(dc);
+            lineTo->MoveLinks(dc);
+        }
+
+        m_doc->Modify(TRUE);
+        m_doc->UpdateAllViews();
+        break;
+    }
+    case ID_CS_ADD_SHAPE:
+    case ID_CS_ADD_SHAPE_SELECT:
+    {
+        // The app has given the command state a new m_savedState
+        // shape, which is the new shape to add to the canvas (but
+        // not actually added until this point).
+        // The new 'saved state' is therefore 'nothing' since there
+        // was nothing there before.
+
+        wxASSERT( (m_shapeOnCanvas == NULL) );
+        wxASSERT( (m_savedState != NULL) );
+        wxASSERT( (m_doc != NULL) );
+
+        m_shapeOnCanvas = m_savedState;
+        m_savedState = NULL;
+
+        m_doc->GetDiagram()->AddShape(m_shapeOnCanvas);
+        m_shapeOnCanvas->Show(TRUE);
+
+        wxClientDC dc(m_shapeOnCanvas->GetCanvas());
+        m_shapeOnCanvas->GetCanvas()->PrepareDC(dc);
+
+        csEvtHandler *handler = (csEvtHandler *)m_shapeOnCanvas->GetEventHandler();
+        m_shapeOnCanvas->FormatText(dc, handler->m_label);
+
+        m_shapeOnCanvas->Move(dc, m_shapeOnCanvas->GetX(), m_shapeOnCanvas->GetY());
+
+        if (m_cmd == ID_CS_ADD_SHAPE_SELECT)
+        {
+            m_shapeOnCanvas->Select(TRUE, &dc);
+            ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, TRUE);
+        }
+
+        m_doc->Modify(TRUE);
+        m_doc->UpdateAllViews();
+        break;
+    }
+    case ID_CS_ADD_LINE:
+    case ID_CS_ADD_LINE_SELECT:
+    {
+        wxASSERT( (m_shapeOnCanvas == NULL) );
+        wxASSERT( (m_savedState != NULL) );
+        wxASSERT( (m_doc != NULL) );
+
+        wxLineShape *lineShape = (wxLineShape *)m_savedState;
+        wxASSERT( (lineShape->GetFrom() != NULL) );
+        wxASSERT( (lineShape->GetTo() != NULL) );
+
+        m_shapeOnCanvas = m_savedState;
+        m_savedState = NULL;
+
+        m_doc->GetDiagram()->AddShape(lineShape);
+
+        lineShape->GetFrom()->AddLine(lineShape, lineShape->GetTo(),
+            lineShape->GetAttachmentFrom(), lineShape->GetAttachmentTo());
+      
+        lineShape->Show(TRUE);
+
+        wxClientDC dc(lineShape->GetCanvas());
+        lineShape->GetCanvas()->PrepareDC(dc);
+
+        // It won't get drawn properly unless you move both
+        // connected images
+        lineShape->GetFrom()->Move(dc, lineShape->GetFrom()->GetX(), lineShape->GetFrom()->GetY());
+        lineShape->GetTo()->Move(dc, lineShape->GetTo()->GetX(), lineShape->GetTo()->GetY());
+
+        if (m_cmd == ID_CS_ADD_LINE_SELECT)
+        {
+            lineShape->Select(TRUE, &dc);
+            ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, TRUE);
+        }
+
+        m_doc->Modify(TRUE);
+        m_doc->UpdateAllViews();
+        break;
+    }
+    case ID_CS_CHANGE_BACKGROUND_COLOUR:
+    case ID_CS_MOVE:
+    case ID_CS_SIZE:
+    case ID_CS_EDIT_PROPERTIES:
+    case ID_CS_FONT_CHANGE:
+    case ID_CS_ARROW_CHANGE:
+    case ID_CS_ROTATE_CLOCKWISE:
+    case ID_CS_ROTATE_ANTICLOCKWISE:
+    case ID_CS_CHANGE_LINE_ORDERING:
+    case ID_CS_CHANGE_LINE_ATTACHMENT:
+    case ID_CS_ALIGN:
+    case ID_CS_NEW_POINT:
+    case ID_CS_CUT_POINT:
+    case ID_CS_MOVE_LINE_POINT:
+    case ID_CS_STRAIGHTEN:
+    case ID_CS_MOVE_LABEL:
+    {
+        // At this point we have been given a new shape
+        // just like the old one but with a changed colour.
+        // It's now time to apply that change to the
+        // shape on the canvas, saving the old state.
+        // NOTE: this is general enough to work with MOST attribute
+        // changes!
+
+        wxASSERT( (m_shapeOnCanvas != NULL) );
+        wxASSERT( (m_savedState != NULL) ); // This is the new shape with changed colour
+        wxASSERT( (m_doc != NULL) );
+
+        wxClientDC dc(m_shapeOnCanvas->GetCanvas());
+        m_shapeOnCanvas->GetCanvas()->PrepareDC(dc);
+
+        bool isSelected = m_shapeOnCanvas->Selected();
+        if (isSelected)
+            m_shapeOnCanvas->Select(FALSE, & dc);
+
+        if (m_cmd == ID_CS_SIZE || m_cmd == ID_CS_ROTATE_CLOCKWISE || m_cmd == ID_CS_ROTATE_ANTICLOCKWISE ||
+            m_cmd == ID_CS_CHANGE_LINE_ORDERING || m_cmd == ID_CS_CHANGE_LINE_ATTACHMENT)
+        {
+            m_shapeOnCanvas->Erase(dc);
+        }
+
+        // TODO: make sure the ID is the same. Or, when applying the new state,
+        // don't change the original ID.
+        wxShape* tempShape = m_shapeOnCanvas->CreateNewCopy();
+
+        // Apply the saved state to the shape on the canvas, by copying.
+        m_savedState->CopyWithHandler(*m_shapeOnCanvas);
+
+        // Delete this state now it's been used (m_shapeOnCanvas currently holds this state)
+        delete m_savedState;
+
+        // Remember the previous state
+        m_savedState = tempShape;
+
+        // Redraw the shape
+
+        if (m_cmd == ID_CS_MOVE || m_cmd == ID_CS_ROTATE_CLOCKWISE || m_cmd == ID_CS_ROTATE_ANTICLOCKWISE ||
+            m_cmd == ID_CS_ALIGN)
+        {
+            m_shapeOnCanvas->Move(dc, m_shapeOnCanvas->GetX(), m_shapeOnCanvas->GetY());
+
+            csEvtHandler *handler = (csEvtHandler *)m_shapeOnCanvas->GetEventHandler();
+            m_shapeOnCanvas->FormatText(dc, handler->m_label);
+            m_shapeOnCanvas->Draw(dc);
+        }
+        else if (m_cmd == ID_CS_CHANGE_LINE_ORDERING)
+        {
+            m_shapeOnCanvas->MoveLinks(dc);
+        }
+        else if (m_cmd == ID_CS_CHANGE_LINE_ATTACHMENT)
+        {
+            wxLineShape *lineShape = (wxLineShape *)m_shapeOnCanvas;
+
+            // Have to move both sets of links since we don't know which links
+            // have been affected (unless we compared before and after states).
+            lineShape->GetFrom()->MoveLinks(dc);
+            lineShape->GetTo()->MoveLinks(dc);
+        }
+        else if (m_cmd == ID_CS_SIZE)
+        {
+            double width, height;
+            m_shapeOnCanvas->GetBoundingBoxMax(&width, &height);
+
+            m_shapeOnCanvas->SetSize(width, height);
+            m_shapeOnCanvas->Move(dc, m_shapeOnCanvas->GetX(), m_shapeOnCanvas->GetY());
+
+            m_shapeOnCanvas->Show(TRUE);
+
+            // Recursively redraw links if we have a composite.
+            if (m_shapeOnCanvas->GetChildren().Number() > 0)
+                m_shapeOnCanvas->DrawLinks(dc, -1, TRUE);
+
+            m_shapeOnCanvas->GetEventHandler()->OnEndSize(width, height);
+        }
+        else if (m_cmd == ID_CS_EDIT_PROPERTIES || m_cmd == ID_CS_FONT_CHANGE)
+        {
+            csEvtHandler *handler = (csEvtHandler *)m_shapeOnCanvas->GetEventHandler();
+            m_shapeOnCanvas->FormatText(dc, handler->m_label);
+            m_shapeOnCanvas->Draw(dc);
+        }
+        else
+        {
+            m_shapeOnCanvas->Draw(dc);
+        }
+
+        if (isSelected)
+            m_shapeOnCanvas->Select(TRUE, & dc);
+        
+        m_doc->Modify(TRUE);
+        m_doc->UpdateAllViews();
+
+        break;
+    }
+  }
+  return TRUE;
+}
+
+bool csCommandState::Undo()
+{
+  switch (m_cmd)
+  {
+    case ID_CS_CUT:
+    {
+        wxASSERT( (m_savedState != NULL) );
+        wxASSERT( (m_doc != NULL) );
+
+        m_doc->GetDiagram()->AddShape(m_savedState);
+        m_shapeOnCanvas = m_savedState;
+        m_savedState = NULL;
+
+        if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxLineShape* lineShape = (wxLineShape*) m_shapeOnCanvas;
+            lineShape->GetFrom()->AddLine(lineShape, lineShape->GetTo(),
+                lineShape->GetAttachmentFrom(), lineShape->GetAttachmentTo(),
+                m_linePositionFrom, m_linePositionTo);
+
+            wxShapeCanvas* canvas = lineShape->GetFrom()->GetCanvas();
+
+            wxClientDC dc(canvas);
+            canvas->PrepareDC(dc);
+
+            lineShape->GetFrom()->MoveLinks(dc);
+            lineShape->GetTo()->MoveLinks(dc);
+
+        }
+        m_shapeOnCanvas->Show(TRUE);
+
+        m_doc->Modify(TRUE);
+        m_doc->UpdateAllViews();
+        break;
+    }
+    case ID_CS_ADD_SHAPE:
+    case ID_CS_ADD_LINE:
+    case ID_CS_ADD_SHAPE_SELECT:
+    case ID_CS_ADD_LINE_SELECT:
+    {
+        wxASSERT( (m_shapeOnCanvas != NULL) );
+        wxASSERT( (m_savedState == NULL) );
+        wxASSERT( (m_doc != NULL) );
+
+        // In case this is a line
+        wxShape* lineFrom = NULL;
+        wxShape* lineTo = NULL;
+        int attachmentFrom = 0, attachmentTo = 0;
+
+        if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            // Store the from/to info to save in the line shape
+            wxLineShape* lineShape = (wxLineShape*) m_shapeOnCanvas;
+            lineFrom = lineShape->GetFrom();
+            lineTo = lineShape->GetTo();
+            attachmentFrom = lineShape->GetAttachmentFrom();
+            attachmentTo = lineShape->GetAttachmentTo();
+        }
+
+        wxClientDC dc(m_shapeOnCanvas->GetCanvas());
+        m_shapeOnCanvas->GetCanvas()->PrepareDC(dc);
+
+        m_shapeOnCanvas->Select(FALSE, &dc);
+        ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, FALSE);
+        m_doc->GetDiagram()->RemoveShape(m_shapeOnCanvas);
+        m_shapeOnCanvas->Unlink(); // Unlinks the line, if it is a line
+
+        if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            // Restore the from/to info for future reference
+            wxLineShape* lineShape = (wxLineShape*) m_shapeOnCanvas;
+            lineShape->SetFrom(lineFrom);
+            lineShape->SetTo(lineTo);
+            lineShape->SetAttachments(attachmentFrom, attachmentTo);
+        }
+
+        m_savedState = m_shapeOnCanvas;
+        m_shapeOnCanvas = NULL;
+
+        m_doc->Modify(TRUE);
+        m_doc->UpdateAllViews();
+        break;
+    }
+    case ID_CS_CHANGE_BACKGROUND_COLOUR:
+    case ID_CS_MOVE:
+    case ID_CS_SIZE:
+    case ID_CS_EDIT_PROPERTIES:
+    case ID_CS_FONT_CHANGE:
+    case ID_CS_ARROW_CHANGE:
+    case ID_CS_ROTATE_CLOCKWISE:
+    case ID_CS_ROTATE_ANTICLOCKWISE:
+    case ID_CS_CHANGE_LINE_ORDERING:
+    case ID_CS_CHANGE_LINE_ATTACHMENT:
+    case ID_CS_ALIGN:
+    case ID_CS_NEW_POINT:
+    case ID_CS_CUT_POINT:
+    case ID_CS_MOVE_LINE_POINT:
+    case ID_CS_STRAIGHTEN:
+    case ID_CS_MOVE_LABEL:
+    {
+        // Exactly like the Do case; we're just swapping states.
+        Do();
+        break;
+    }
+  }
+
+    return TRUE;
+}
+
diff --git a/samples/ogl/studio/doc.h b/samples/ogl/studio/doc.h
new file mode 100644 (file)
index 0000000..4e77abc
--- /dev/null
@@ -0,0 +1,134 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        doc.h
+// Purpose:     Document classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_DOC_H_
+#define _STUDIO_DOC_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/docview.h>
+#include <wx/string.h>
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/ogl.h>
+#include "shapes.h"
+
+/*
+ * A diagram document, which contains a diagram.
+ */
+class csDiagramDocument: public wxDocument
+{
+  DECLARE_DYNAMIC_CLASS(csDiagramDocument)
+public:
+  csDiagramDocument();
+  ~csDiagramDocument();
+
+  bool OnSaveDocument(const wxString& file);
+  bool OnOpenDocument(const wxString& file);
+  
+  inline wxDiagram *GetDiagram() { return &m_diagram; }
+  
+  bool OnCloseDocument();
+
+protected:
+  csDiagram         m_diagram;
+};
+
+/*
+ Do/Undo 30/7/98
+
+ 1) We have a csCommandState, and in csDiagramCommand you have a list of
+    these. This allows undo to work with several shapes at once.
+
+ 2) Instead of storing info about each operation, e.g. separate pens, colours,
+    etc., we simply use a copy of the shape.
+    In csCommandState, we have a pointer to the actual shape in the canvas, m_currentShape.
+    We also have wxShape* m_shapeState which stores the requested or previous state
+    (depending on whether it's before the Do or after the Do.
+
+    - In Do: save a temp copy of the old m_currentShape (i.e. the state just before it's changed).
+      Change the data pointed to by m_currentShape to the new attributes stored in m_shapeState.
+      Now assign the temp copy to m_shapeState, for use in Undo.
+
+      wxShape* temp = m_currentShape->Copy(); // Take a copy of the current state
+      m_currentShape->Set(m_shapeState);      // Apply the new state (e.g. moving, changing colour etc.)
+      delete m_shapeState;                    // Delete the previous 'old state'.
+      m_shapeState = temp;                    // Remember the new 'old state'.
+
+ */
+
+  
+class csCommandState;
+class csDiagramCommand: public wxCommand
+{
+    friend class csCommandState;
+ public:
+  // Multi-purpose constructor for creating, deleting shapes
+  csDiagramCommand(const wxString& name, csDiagramDocument *doc,
+    csCommandState* onlyState = NULL); // Allow for the common case of just one state to change
+
+  ~csDiagramCommand();
+
+  bool Do();
+  bool Undo();
+
+  // Add a state to the end of the list
+  void AddState(csCommandState* state);
+
+  // Insert a state at the beginning of the list
+  void InsertState(csCommandState* state);
+
+  // Schedule all lines connected to the states to be cut.
+  void RemoveLines();
+
+  // Find the state that refers to this shape
+  csCommandState* FindStateByShape(wxShape* shape);
+
+  wxList& GetStates() const { return (wxList&) m_states; }
+
+ protected:
+  csDiagramDocument*    m_doc;
+  wxList                m_states;
+};
+
+class csCommandState: public wxObject
+{
+    friend class csDiagramCommand;
+public:
+    csCommandState(int cmd, wxShape* savedState, wxShape* shapeOnCanvas);
+    ~csCommandState();
+
+    bool Do();
+    bool Undo();
+
+    inline void SetSavedState(wxShape *s) { m_savedState = s; }
+    inline wxShape *GetSavedState() const { return m_savedState; }
+
+    inline void SetShapeOnCanvas(wxShape *s) { m_shapeOnCanvas = s; }
+    inline wxShape *GetShapeOnCanvas() const { return m_shapeOnCanvas; }
+protected:
+    wxShape*                m_savedState;       // Previous state, for restoring on Undo
+    wxShape*                m_shapeOnCanvas;    // The actual shape on the canvas
+    csDiagramDocument*      m_doc;
+    int                     m_cmd;
+
+    // These store the line ordering for the shapes at either end,
+    // so an un-cut line can restore the ordering properly. Otherwise
+    // it just adds the line at an arbitrary position.
+    int                     m_linePositionFrom;
+    int                     m_linePositionTo;
+};
+
+#endif
+  // _STUDIO_DOC_H_
diff --git a/samples/ogl/studio/mainfrm.cpp b/samples/ogl/studio/mainfrm.cpp
new file mode 100644 (file)
index 0000000..c1d856c
--- /dev/null
@@ -0,0 +1,276 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        mainfrm.cpp
+// Purpose:     Studio main frame
+// Author:      Julian Smart
+// Modified by:
+// Created:     27/7/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#include "wx/mdi.h"
+#endif
+
+#include "wx/laywin.h"
+
+#include "studio.h"
+#include "view.h"
+#include "doc.h"
+#include "cspalette.h"
+#include "mainfrm.h"
+#include "dialogs.h"
+
+BEGIN_EVENT_TABLE(csFrame, wxDocMDIParentFrame)
+    EVT_MENU(ID_CS_ABOUT, csFrame::OnAbout)
+    EVT_MENU(wxID_EXIT, csFrame::OnQuit)
+    EVT_MENU(wxID_HELP, csFrame::OnHelp)
+    EVT_MENU(ID_CS_SETTINGS, csFrame::OnSettings)
+    EVT_SIZE(csFrame::OnSize)
+    EVT_SASH_DRAGGED(ID_LAYOUT_WINDOW_PALETTE, csFrame::OnSashDragPaletteWindow)
+    EVT_SASH_DRAGGED(ID_LAYOUT_WINDOW_PROJECT, csFrame::OnSashDragProjectWindow)
+    EVT_IDLE(csFrame::OnIdle)
+    EVT_UPDATE_UI(wxID_PRINT, csFrame::OnUpdateDisable)
+    EVT_UPDATE_UI(wxID_PREVIEW, csFrame::OnUpdateDisable)
+    EVT_UPDATE_UI(wxID_SAVE, csFrame::OnSaveUpdate)
+    EVT_UPDATE_UI(wxID_SAVEAS, csFrame::OnSaveUpdate)
+    EVT_UPDATE_UI(wxID_UNDO, csFrame::OnUpdateDisable)
+    EVT_UPDATE_UI(wxID_REDO, csFrame::OnUpdateDisable)
+    EVT_UPDATE_UI(wxID_CUT, csFrame::OnUpdateDisable)
+    EVT_UPDATE_UI(wxID_COPY, csFrame::OnUpdateDisable)
+    EVT_UPDATE_UI(wxID_PASTE, csFrame::OnUpdateDisable)
+    EVT_CLOSE(csFrame::OnCloseWindow)
+END_EVENT_TABLE()
+
+// Define my frame constructor
+csFrame::csFrame(wxDocManager* manager, wxFrame *parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size,
+       long style):
+  wxDocMDIParentFrame(manager, parent, id, title, pos, size, style, "frame")
+{
+    CreateToolBar(wxNO_BORDER|wxTB_FLAT|wxTB_HORIZONTAL);
+    wxGetApp().InitToolBar(GetToolBar());
+
+    // Accelerators
+    wxAcceleratorEntry entries[4];
+
+    entries[0].Set(wxACCEL_NORMAL,  WXK_F1,        wxID_HELP);
+    entries[1].Set(wxACCEL_CTRL,   'O',            wxID_OPEN);
+    entries[2].Set(wxACCEL_CTRL,   'N',            wxID_NEW);
+    entries[3].Set(wxACCEL_CTRL,   'P',            wxID_PRINT);
+
+    wxAcceleratorTable accel(4, entries);
+    SetAcceleratorTable(accel);
+}
+
+void csFrame::OnHelp(wxCommandEvent& event)
+{
+    wxGetApp().GetHelpController().DisplayContents();
+}
+
+void csFrame::OnSettings(wxCommandEvent& event)
+{
+    csSettingsDialog* dialog = new csSettingsDialog(this);
+    int ret = dialog->ShowModal();
+    dialog->Destroy();
+}
+
+void csFrame::OnQuit(wxCommandEvent& event)
+{
+      Close(TRUE);
+}
+
+void csFrame::OnAbout(wxCommandEvent& event)
+{
+      (void)wxMessageBox("OGL Studio\n(c) 1999, Julian Smart", "About OGL Studio", wxICON_INFORMATION);
+}
+
+void csFrame::OnSashDragPaletteWindow(wxSashEvent& event)
+{
+    if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
+        return;
+
+    switch (event.GetId())
+    {
+        case ID_LAYOUT_WINDOW_PALETTE:
+        {
+            wxGetApp().GetDiagramPaletteSashWindow()->SetDefaultSize(wxSize(10000, event.GetDragRect().height));
+            break;
+        }
+    }
+    wxLayoutAlgorithm layout;
+    layout.LayoutMDIFrame(this);
+}
+
+void csFrame::OnSashDragProjectWindow(wxSashEvent& event)
+{
+    if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
+        return;
+
+    switch (event.GetId())
+    {
+        case ID_LAYOUT_WINDOW_PROJECT:
+        {
+            wxGetApp().GetProjectSashWindow()->SetDefaultSize(wxSize(event.GetDragRect().width, 10000));
+            break;
+        }
+    }
+    wxLayoutAlgorithm layout;
+    layout.LayoutMDIFrame(this);
+}
+
+// Define the behaviour for the frame closing
+// - must delete all frames except for the main one.
+void csFrame::OnCloseWindow(wxCloseEvent& event)
+{
+    int x, y;
+    GetPosition(& x, & y);
+    wxGetApp().m_mainFramePos = wxPoint(x, y);
+
+    GetSize(& x, & y);
+    wxGetApp().m_mainFrameSize = wxSize(x, y);
+
+    wxDocMDIParentFrame::OnCloseWindow(event);
+}
+
+void csFrame::OnSize(wxSizeEvent& event)
+{
+    wxLayoutAlgorithm layout;
+    layout.LayoutMDIFrame(this);
+}
+
+// Make sure the correct toolbars are showing for the active view
+void csFrame::OnIdle(wxIdleEvent& event)
+{
+    wxDocMDIParentFrame::OnIdle(event);
+
+    wxSashLayoutWindow* paletteWin = wxGetApp().GetDiagramPaletteSashWindow();
+    wxSashLayoutWindow* diagramToolBarWin = wxGetApp().GetDiagramToolBarSashWindow();
+    if (!paletteWin || !diagramToolBarWin)
+        return;
+    bool doLayout = FALSE;
+    if (GetActiveChild())
+    {
+        if (!paletteWin->IsShown() || !diagramToolBarWin->IsShown())
+        {
+            paletteWin->Show(TRUE);
+            diagramToolBarWin->Show(TRUE);
+
+            doLayout = TRUE;
+        }
+    }
+    else
+    {
+        if (paletteWin->IsShown() || diagramToolBarWin->IsShown())
+        {
+            paletteWin->Show(FALSE);
+            diagramToolBarWin->Show(FALSE);
+            doLayout = TRUE;
+        }
+    }
+    if (doLayout)
+    {
+        wxLayoutAlgorithm layout;
+        layout.LayoutMDIFrame(this);
+
+#if defined(__WXMSW__) && defined(__WIN95__)
+        // Need to do something else to get it to refresh properly
+        // when a client frame is first displayed; moving the client
+        // window doesn't cause the proper refresh. Just refreshing the
+        // client doesn't work (presumably because it's clipping the
+        // children).
+        // FIXED in wxWindows, by intercepting wxMDIClientWindow::DoSetSize
+        // and checking if the position has changed, before redrawing the
+        // child windows.
+#if 0
+        wxMDIChildFrame* childFrame = GetActiveChild();
+        if (childFrame)
+        {
+            HWND hWnd = (HWND) childFrame->GetHWND();
+            ::RedrawWindow(hWnd, NULL, NULL, RDW_FRAME|RDW_ALLCHILDREN|RDW_INVALIDATE );
+
+        }
+#endif
+#endif
+    }
+}
+
+// General handler for disabling items
+void csFrame::OnUpdateDisable(wxUpdateUIEvent& event)
+{
+    event.Enable(FALSE);
+}
+
+void csFrame::OnSaveUpdate(wxUpdateUIEvent& event)
+{
+    event.Enable( (GetActiveChild() != NULL) );
+}
+
+/*
+ * Child frame
+ */
+
+BEGIN_EVENT_TABLE(csMDIChildFrame, wxDocMDIChildFrame)
+  EVT_ACTIVATE(csMDIChildFrame::OnActivate)
+END_EVENT_TABLE()
+
+csMDIChildFrame::csMDIChildFrame(wxDocument* doc, wxView* view, wxMDIParentFrame *parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style):
+  wxDocMDIChildFrame(doc, view, parent, id, title, pos, size, style)
+{
+    // Accelerators
+    wxAcceleratorEntry entries[18];
+
+    // Usual editing functions
+    entries[0].Set(wxACCEL_NORMAL,  WXK_DELETE,     wxID_CLEAR);
+    entries[1].Set(wxACCEL_CTRL,    'X',            wxID_CUT);
+    entries[2].Set(wxACCEL_CTRL,    'C',            wxID_COPY);
+    entries[3].Set(wxACCEL_SHIFT,   WXK_INSERT,     wxID_PASTE);
+    entries[4].Set(wxACCEL_CTRL,    'V',            wxID_PASTE);
+    entries[5].Set(wxACCEL_CTRL,    'A',            ID_CS_SELECT_ALL);
+
+    // Undo/redo
+    entries[6].Set(wxACCEL_CTRL,    'Z',            wxID_UNDO);
+    entries[7].Set(wxACCEL_CTRL,    'Y',            wxID_REDO);
+
+    // Other
+    entries[8].Set(wxACCEL_NORMAL,  WXK_RETURN,     ID_CS_EDIT_PROPERTIES);
+    entries[9].Set(wxACCEL_ALT,     WXK_RETURN,     ID_CS_EDIT_PROPERTIES);
+    entries[10].Set(wxACCEL_CTRL,   'D',            wxID_DUPLICATE);
+    entries[11].Set(wxACCEL_NORMAL,  WXK_F1,        wxID_HELP);
+
+    // File handling
+    entries[12].Set(wxACCEL_CTRL,   'S',            wxID_SAVE);
+    entries[13].Set(wxACCEL_NORMAL,  WXK_F12,       wxID_SAVEAS);
+    entries[14].Set(wxACCEL_CTRL,   'O',            wxID_OPEN);
+    entries[15].Set(wxACCEL_CTRL,   'N',            wxID_NEW);
+    entries[16].Set(wxACCEL_CTRL,   'P',            wxID_PRINT);
+    entries[17].Set(wxACCEL_CTRL,   'W',            wxID_CLOSE);
+
+
+    wxAcceleratorTable accel(18, entries);
+    SetAcceleratorTable(accel);
+}
+
+void csMDIChildFrame::OnActivate(wxActivateEvent& event)
+{
+    wxDocMDIChildFrame::OnActivate(event);
+/*
+    wxSashLayoutWindow* win = wxGetApp().GetDiagramPaletteSashWindow();
+    if (!win)
+        return;
+
+    win->Show(event.GetActive());
+
+    wxLayoutAlgorithm layout;
+    layout.LayoutMDIFrame((wxMDIParentFrame*) GetParent());
+*/
+}
+
diff --git a/samples/ogl/studio/mainfrm.h b/samples/ogl/studio/mainfrm.h
new file mode 100644 (file)
index 0000000..9200fc2
--- /dev/null
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        mainfrm.h
+// Purpose:     Studio main window class
+// Author:      Julian Smart
+// Modified by:
+// Created:     27/7/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_MAINFRM_H_
+#define _STUDIO_MAINFRM_H_
+
+#include <wx/docmdi.h>
+
+class wxSashLayoutWindow;
+class wxSashEvent;
+
+class csFrame: public wxDocMDIParentFrame
+{
+  public:
+    csFrame(wxDocManager *manager, wxFrame *parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style);
+
+    void OnCloseWindow(wxCloseEvent& event);
+    void OnSize(wxSizeEvent& event);
+    void OnAbout(wxCommandEvent& event);
+    void OnNewWindow(wxCommandEvent& event);
+    void OnQuit(wxCommandEvent& event);
+    void OnSashDragPaletteWindow(wxSashEvent& event);
+    void OnSashDragProjectWindow(wxSashEvent& event);
+    void OnIdle(wxIdleEvent& event);
+    void OnHelp(wxCommandEvent& event);
+    void OnSettings(wxCommandEvent& event);
+
+    // General handler for disabling items
+    void OnUpdateDisable(wxUpdateUIEvent& event);
+    void OnSaveUpdate(wxUpdateUIEvent& event);
+
+DECLARE_EVENT_TABLE()
+};
+
+class csMDIChildFrame: public wxDocMDIChildFrame
+{
+  public:
+    csMDIChildFrame(wxDocument* doc, wxView* view, wxMDIParentFrame *parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style);
+
+    void OnActivate(wxActivateEvent& event);
+
+DECLARE_EVENT_TABLE()
+};
+
+#endif
+  // _STUDIO_MAINFRM_H_
+
diff --git a/samples/ogl/studio/makefile.b32 b/samples/ogl/studio/makefile.b32
new file mode 100644 (file)
index 0000000..292366c
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# File:                makefile.b32
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:
+#
+# Makefile : Builds sample for 32-bit BC++
+
+WXDIR = $(WXWIN)
+
+TARGET=studio
+EXTRALIBS=$(WXDIR)\lib\ogl.lib
+EXTRACPPFLAGS=-I$(WXDIR)\utils\ogl\src
+OBJECTS = $(TARGET).obj doc.obj shapes.obj symbols.obj view.obj cspalette.obj\
+  mainfrm.obj project.obj dialogs.obj csprint.obj
+
+!include $(WXDIR)\src\makeprog.b32
+
diff --git a/samples/ogl/studio/makefile.bcc b/samples/ogl/studio/makefile.bcc
new file mode 100644 (file)
index 0000000..11f5143
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# File:                makefile.bcc
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+#
+# Builds a BC++ 16-bit sample
+
+!if "$(WXWIN)" == ""
+!error You must define the WXWIN variable in autoexec.bat, e.g. WXWIN=c:\wx
+!endif
+
+WXDIR = $(WXWIN)
+
+TARGET=studio
+EXTRALIBS=$(WXDIR)\lib\ogl.lib
+EXTRACPPFLAGS=-I$(WXDIR)\utils\ogl\src
+OBJECTS = $(TARGET).obj doc.obj shapes.obj symbols.obj view.obj cspalette.obj\
+  mainfrm.obj project.obj dialogs.obj csprint.obj
+
+!include $(WXDIR)\src\makeprog.b32
+
diff --git a/samples/ogl/studio/makefile.g95 b/samples/ogl/studio/makefile.g95
new file mode 100644 (file)
index 0000000..2da54a1
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# File:         makefile.g95
+# Author:       Julian Smart
+# Created:      1999
+# Updated:
+# Copyright:    (c) Julian Smart, 1999
+#
+# Makefile for wxWindows sample (Cygwin/Mingw32).
+
+WXDIR = ../../../..
+
+TARGET=studio
+EXTRACPPFLAGS=-I../../src
+EXTRALIBS=-logl
+OBJECTS = $(TARGET).o doc.o shapes.o symbols.o view.o cspalette.o\
+  mainfrm.o project.o dialogs.o csprint.o
+
+include $(WXDIR)/src/makeprog.g95
+
diff --git a/samples/ogl/studio/makefile.unx b/samples/ogl/studio/makefile.unx
new file mode 100644 (file)
index 0000000..5fca57a
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# File:                makefile.unx
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+# Copyright:   (c) 1998 Julian Smart
+#
+# "%W% %G%"
+#
+# Makefile for OGL Studio (UNIX).
+
+PROGRAM=studio
+
+OBJECTS=$(PROGRAM).o doc.o shapes.o symbols.o view.o cspalette.o\
+  mainfrm.o project.o dialogs.o csprint.o
+
+EXTRACPPFLAGS=-I$(WXDIR)/utils/ogl/src -I./bitmaps
+EXTRALDLIBS=-logl$(GUISUFFIX)
+
+#WXDIR=/home/jacs/wx2
+
+include $(WXDIR)/src/makeprog.env
+
+cleanogl:
+       cd $(WXDIR)/utils/ogl/src; make -f makefile.unx cleanmotif
+
+ogl:
+       cd $(WXDIR)/utils/ogl/src; make -f makefile.unx motif
+
+wx:
+       cd $(WXDIR)/src/motif; make -f makefile.unx motif
+
+cleanwx:
+       cd $(WXDIR)/src/motif; make -f makefile.unx cleanmotif
+
+cleanall: cleanmotif cleanogl cleanwx
+
+makeall: wx ogl motif
+
diff --git a/samples/ogl/studio/makefile.vc b/samples/ogl/studio/makefile.vc
new file mode 100644 (file)
index 0000000..b88314a
--- /dev/null
@@ -0,0 +1,143 @@
+#
+# File:                makefile.vc
+# Author:      Julian Smart
+# Created:     1999
+# Updated:
+# Copyright:   (c) Julian Smart
+#
+# "%W% %G%"
+#
+# Makefile : Builds OGL studio example (MS VC++).
+# Use FINAL=1 argument to nmake to build final version with no debugging
+# info
+
+# Set WXDIR for your system
+WXDIR = $(WXWIN)
+WXUSINGDLL=0
+
+STUDIODIR = $(WXDIR)\utils\ogl\samples\studio
+THISDIR = $(STUDIODIR)
+
+OGLDIR = $(WXDIR)\utils\ogl
+OGLINC = $(OGLDIR)\src
+OGLLIB = $(WXDIR)\lib\ogl$(LIBEXT).lib
+
+!include $(WXDIR)\src\makevc.env
+
+EXTRALIBS=$(OGLLIB)
+EXTRAINC = /I$(OGLINC)
+
+PROGRAM=studio
+
+OBJECTS = $(PROGRAM).obj doc.obj shapes.obj symbols.obj view.obj cspalette.obj\
+  mainfrm.obj project.obj dialogs.obj csprint.obj
+
+all:    ogl $(PROGRAM).exe
+
+$(PROGRAM):    $(PROGRAM).exe
+
+cleanall: clean cleanogl # cleanidelib
+cleanutils: cleanall
+
+wx:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.vc FINAL=$(FINAL)
+        cd $(THISDIR)
+
+cleanwx:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.vc clean
+        cd $(THISDIR)
+
+idelib:
+        cd $(CLIPDIR)\IDELib\src
+        nmake -f makefile.vc FINAL=$(FINAL)
+        cd $(THISDIR)
+
+cleanidelib:
+        cd $(CLIPDIR)\IDELib\src
+        nmake -f makefile.vc clean
+        cd $(THISDIR)
+
+ogl:
+        cd $(OGLDIR)\src
+        nmake -f makefile.vc FINAL=$(FINAL)
+        cd $(THISDIR)
+
+cleanogl:
+        cd $(OGLDIR)\src
+        nmake -f makefile.vc clean
+        cd $(THISDIR)
+
+$(PROGRAM).exe:      $(DUMMYOBJ) $(WXLIB) $(OBJECTS) $(EXTRALIBS) $(PROGRAM).res
+       $(link) @<<
+-out:$(PROGRAM).exe
+$(LINKFLAGS)
+$(DUMMYOBJ) $(OBJECTS) $(PROGRAM).res
+$(LIBS)
+<<
+
+
+$(PROGRAM).obj:      $(PROGRAM).$(SRCSUFF) cspalette.h doc.h view.h $(DUMMYOBJ)
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+mainfrm.obj:      mainfrm.$(SRCSUFF) mainfrm.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+cspalette.obj:      cspalette.$(SRCSUFF) cspalette.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+project.obj:      project.$(SRCSUFF) project.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+view.obj:      view.$(SRCSUFF) view.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+doc.obj:      doc.$(SRCSUFF) doc.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+shapes.obj:      shapes.$(SRCSUFF) shapes.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+symbols.obj:      symbols.$(SRCSUFF) symbols.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+dialogs.obj:      dialogs.$(SRCSUFF) dialogs.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+csprint.obj:      csprint.$(SRCSUFF) shapes.h
+        $(cc) @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+$(OBJECTS):     shapes.h doc.h view.h mainfrm.h studio.h cspalette.h project.h symbols.h dialogs.h
+
+$(PROGRAM).res :      $(PROGRAM).rc $(WXDIR)\include\wx\msw\wx.rc
+    $(rc) -r /i$(WXDIR)\include -fo$@ $(PROGRAM).rc
+
+
+clean:
+        -erase *.obj
+        -erase *.exe
+        -erase *.res
+        -erase *.map
+        -erase *.sbr
+        -erase *.pdb
diff --git a/samples/ogl/studio/manual/alignb.bmp b/samples/ogl/studio/manual/alignb.bmp
new file mode 100644 (file)
index 0000000..6cc2e1d
Binary files /dev/null and b/samples/ogl/studio/manual/alignb.bmp differ
diff --git a/samples/ogl/studio/manual/alignl.bmp b/samples/ogl/studio/manual/alignl.bmp
new file mode 100644 (file)
index 0000000..3047110
Binary files /dev/null and b/samples/ogl/studio/manual/alignl.bmp differ
diff --git a/samples/ogl/studio/manual/alignr.bmp b/samples/ogl/studio/manual/alignr.bmp
new file mode 100644 (file)
index 0000000..14a2f85
Binary files /dev/null and b/samples/ogl/studio/manual/alignr.bmp differ
diff --git a/samples/ogl/studio/manual/alignt.bmp b/samples/ogl/studio/manual/alignt.bmp
new file mode 100644 (file)
index 0000000..4505cba
Binary files /dev/null and b/samples/ogl/studio/manual/alignt.bmp differ
diff --git a/samples/ogl/studio/manual/arrow.bmp b/samples/ogl/studio/manual/arrow.bmp
new file mode 100644 (file)
index 0000000..d406ceb
Binary files /dev/null and b/samples/ogl/studio/manual/arrow.bmp differ
diff --git a/samples/ogl/studio/manual/back.gif b/samples/ogl/studio/manual/back.gif
new file mode 100644 (file)
index 0000000..8a61076
Binary files /dev/null and b/samples/ogl/studio/manual/back.gif differ
diff --git a/samples/ogl/studio/manual/bitmap1.bmp b/samples/ogl/studio/manual/bitmap1.bmp
new file mode 100644 (file)
index 0000000..13e2170
Binary files /dev/null and b/samples/ogl/studio/manual/bitmap1.bmp differ
diff --git a/samples/ogl/studio/manual/bitmap2.bmp b/samples/ogl/studio/manual/bitmap2.bmp
new file mode 100644 (file)
index 0000000..3d523b2
Binary files /dev/null and b/samples/ogl/studio/manual/bitmap2.bmp differ
diff --git a/samples/ogl/studio/manual/bullet.bmp b/samples/ogl/studio/manual/bullet.bmp
new file mode 100644 (file)
index 0000000..aad8fc7
Binary files /dev/null and b/samples/ogl/studio/manual/bullet.bmp differ
diff --git a/samples/ogl/studio/manual/contents.gif b/samples/ogl/studio/manual/contents.gif
new file mode 100644 (file)
index 0000000..3dddfa3
Binary files /dev/null and b/samples/ogl/studio/manual/contents.gif differ
diff --git a/samples/ogl/studio/manual/copy.bmp b/samples/ogl/studio/manual/copy.bmp
new file mode 100644 (file)
index 0000000..4551a06
Binary files /dev/null and b/samples/ogl/studio/manual/copy.bmp differ
diff --git a/samples/ogl/studio/manual/copysize.bmp b/samples/ogl/studio/manual/copysize.bmp
new file mode 100644 (file)
index 0000000..36060ad
Binary files /dev/null and b/samples/ogl/studio/manual/copysize.bmp differ
diff --git a/samples/ogl/studio/manual/cut.bmp b/samples/ogl/studio/manual/cut.bmp
new file mode 100644 (file)
index 0000000..1555433
Binary files /dev/null and b/samples/ogl/studio/manual/cut.bmp differ
diff --git a/samples/ogl/studio/manual/cutpoint.bmp b/samples/ogl/studio/manual/cutpoint.bmp
new file mode 100644 (file)
index 0000000..2f7dc78
Binary files /dev/null and b/samples/ogl/studio/manual/cutpoint.bmp differ
diff --git a/samples/ogl/studio/manual/forward.gif b/samples/ogl/studio/manual/forward.gif
new file mode 100644 (file)
index 0000000..9c81e8c
Binary files /dev/null and b/samples/ogl/studio/manual/forward.gif differ
diff --git a/samples/ogl/studio/manual/help.bmp b/samples/ogl/studio/manual/help.bmp
new file mode 100644 (file)
index 0000000..2d9e692
Binary files /dev/null and b/samples/ogl/studio/manual/help.bmp differ
diff --git a/samples/ogl/studio/manual/helpcs.bmp b/samples/ogl/studio/manual/helpcs.bmp
new file mode 100644 (file)
index 0000000..8837381
Binary files /dev/null and b/samples/ogl/studio/manual/helpcs.bmp differ
diff --git a/samples/ogl/studio/manual/horiz.bmp b/samples/ogl/studio/manual/horiz.bmp
new file mode 100644 (file)
index 0000000..58af34a
Binary files /dev/null and b/samples/ogl/studio/manual/horiz.bmp differ
diff --git a/samples/ogl/studio/manual/linearrow.bmp b/samples/ogl/studio/manual/linearrow.bmp
new file mode 100644 (file)
index 0000000..1fd21fb
Binary files /dev/null and b/samples/ogl/studio/manual/linearrow.bmp differ
diff --git a/samples/ogl/studio/manual/new.bmp b/samples/ogl/studio/manual/new.bmp
new file mode 100644 (file)
index 0000000..d66feb2
Binary files /dev/null and b/samples/ogl/studio/manual/new.bmp differ
diff --git a/samples/ogl/studio/manual/newpoint.bmp b/samples/ogl/studio/manual/newpoint.bmp
new file mode 100644 (file)
index 0000000..cb4f267
Binary files /dev/null and b/samples/ogl/studio/manual/newpoint.bmp differ
diff --git a/samples/ogl/studio/manual/open.bmp b/samples/ogl/studio/manual/open.bmp
new file mode 100644 (file)
index 0000000..bbf93fe
Binary files /dev/null and b/samples/ogl/studio/manual/open.bmp differ
diff --git a/samples/ogl/studio/manual/paste.bmp b/samples/ogl/studio/manual/paste.bmp
new file mode 100644 (file)
index 0000000..564f514
Binary files /dev/null and b/samples/ogl/studio/manual/paste.bmp differ
diff --git a/samples/ogl/studio/manual/pointsize.bmp b/samples/ogl/studio/manual/pointsize.bmp
new file mode 100644 (file)
index 0000000..8a01c8a
Binary files /dev/null and b/samples/ogl/studio/manual/pointsize.bmp differ
diff --git a/samples/ogl/studio/manual/preview.bmp b/samples/ogl/studio/manual/preview.bmp
new file mode 100644 (file)
index 0000000..da1f4db
Binary files /dev/null and b/samples/ogl/studio/manual/preview.bmp differ
diff --git a/samples/ogl/studio/manual/print.bmp b/samples/ogl/studio/manual/print.bmp
new file mode 100644 (file)
index 0000000..00319b5
Binary files /dev/null and b/samples/ogl/studio/manual/print.bmp differ
diff --git a/samples/ogl/studio/manual/redo.bmp b/samples/ogl/studio/manual/redo.bmp
new file mode 100644 (file)
index 0000000..5877e34
Binary files /dev/null and b/samples/ogl/studio/manual/redo.bmp differ
diff --git a/samples/ogl/studio/manual/save.bmp b/samples/ogl/studio/manual/save.bmp
new file mode 100644 (file)
index 0000000..56dd10b
Binary files /dev/null and b/samples/ogl/studio/manual/save.bmp differ
diff --git a/samples/ogl/studio/manual/straight.bmp b/samples/ogl/studio/manual/straight.bmp
new file mode 100644 (file)
index 0000000..5293665
Binary files /dev/null and b/samples/ogl/studio/manual/straight.bmp differ
diff --git a/samples/ogl/studio/manual/studio.hpj b/samples/ogl/studio/manual/studio.hpj
new file mode 100644 (file)
index 0000000..8e6d983
--- /dev/null
@@ -0,0 +1,23 @@
+[OPTIONS]
+BMROOT=. ; Assume that bitmaps are where the source is
+TITLE=OGL Studio
+CONTENTS=Contents
+; COMPRESS=12 Hall Zeck ; Max compression, but needs lots of memory
+COMPRESS=8 Zeck
+LCID=0x809 0x0 0x0 ;English (British)
+HLP=.\studio.hlp
+
+[WINDOWS]
+Main="",(553,102,400,600),20736,(r14876671),(r12632256),f3
+
+[FILES]
+studio.rtf
+
+[CONFIG]
+CreateButton("Up", "&Up", "JumpId(`studio.hlp', `Contents')")
+BrowseButtons()
+
+[MAP]
+
+[BITMAPS]
+
diff --git a/samples/ogl/studio/manual/studio.tex b/samples/ogl/studio/manual/studio.tex
new file mode 100644 (file)
index 0000000..ab4d897
--- /dev/null
@@ -0,0 +1,381 @@
+\documentstyle[a4,makeidx,verbatim,texhelp,fancyhea,mysober,mytitle]{report}%
+\twocolwidtha{4cm}%
+\input{psbox.tex}
+\newcommand{\commandref}[2]{\helpref{{\tt $\backslash$#1}}{#2}}%
+\newcommand{\commandrefn}[2]{\helprefn{{\tt $\backslash$#1}}{#2}\index{#1}}%
+\newcommand{\commandpageref}[2]{\latexignore{\helprefn{{\tt $\backslash$#1}}{#2}}\latexonly{{\tt $\backslash$#1} {\it page \pageref{#2}}}\index{#1}}%
+\newcommand{\indexit}[1]{#1\index{#1}}%
+\newcommand{\inioption}[1]{{\tt #1}\index{#1}}%
+\parskip=10pt%
+\parindent=0pt%
+\title{Manual for OGL Studio}%
+\author{by Julian Smart}%
+\makeindex%
+\begin{document}%
+\maketitle%
+\pagestyle{fancyplain}%
+\bibliographystyle{plain}%
+\pagenumbering{arabic}%
+\setheader{{\it CONTENTS}}{}{}{}{}{{\it CONTENTS}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+\tableofcontents%
+
+\chapter{Welcome to OGL Studio}%
+\setheader{{\it Welcome}}{}{}{}{}{{\it Welcome}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+Welcome to OGL Studio, an extended sample for the Object Graphics Library.
+
+For release information, please see the \helpref{Read Me}{readme} section.
+
+\chapter{Read Me}\label{readme}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+\section{Change log}
+
+Version 1, February 7th, 1999
+
+\begin{itemize}\itemsep=0pt
+\item First release.
+\end{itemize}
+
+\section{Bugs}
+
+There are no known bugs.
+
+\begin{comment}
+\chapter{Getting Started}\label{gettingstarted}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+\end{comment}
+
+\chapter{Working with the diagram window}\label{schedule}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+This section describes how you work in the diagram window.
+
+In addition, you may wish to refer to the following sections:
+
+\begin{itemize}\itemsep=0pt
+\item \helpref{How To}{howto}
+%\item \helpref{Getting started}{gettingstarted}
+\item \helpref{Using Menu Commands}{menucommands}
+\item \helpref{Using Toolbar Commands}{toolbarcommands}
+\end{itemize}
+
+When you first run OGL Studio, there is a menubar, a single
+toolbar with commonly-used functionality such as loading and
+saving, a project window to the left, and an MDI (Multiple Document
+Interface) area to the right, which will contain documents.
+
+\section{Creating a diagram}
+
+To create a new diagram, click on "File|New" or the New tool.
+
+A blank document and two new toolbars will appear. The first
+new toolbar is the \helpref{diagramming formatting toolbar}{diagramformattingtoolbar}, and contains
+icons and controls for:
+
+\begin{itemize}\itemsep=0pt
+\item alignment and size cloning;
+\item arrow toggling;
+\item point size;
+\item zoom level.
+\end{itemize}
+
+The second new toolbar is called the \helpref{diagram palette}{diagrampalette} and contains:
+
+\begin{itemize}\itemsep=0pt
+\item a pointer tool used for selecting, moving and sizing objects;
+\item a text tool used for editing text or creating new text boxes;
+\item a tool for each of the symbols.
+\end{itemize}
+
+\section{Basic editing}
+
+To add a symbol, left-click on the symbol in the diagram palette,
+and then left-click on the document. The currently selected
+tool will revert to the pointer tool, so to add another object,
+click on the symbol again, then on the document.
+
+To draw a line between two objects, right-drag between the two
+objects, starting at the attachment point area you wish to start the
+line with, and ending at another appropriate attachment point
+area. The initial ordering of the lines may not be correct (lines
+may overlap, for example) so to reorder lines on a particular
+side of a object, select a line, then left-drag the desired end to a new
+position (tip: keep within the object perimeter). Left-dragging the
+line end can also be used to change the attachment point of that
+end of the line, to a new side or vertex (depending on the object).
+
+To select or deselect a object, left click the object. To select
+several objects at once, keep the shift key pressed down when
+left-clicking, or left-drag a 'lassoo' around several objects.
+
+To delete a object or line, select it and press the Delete key, or use
+"Edit|Clear", or right-click on the object to show a menu and choose
+the "Cut" item.
+
+If you are deleting a object which has one ore more lines
+attached, the lines are deleted prior to the object deletion.
+
+Shapes can be rotated by right-clicking and selecting "Rotate
+clockwise" or "Rotate anticlockwise".
+
+Line arrows can be added (pointing in the direction in which
+you created the line) by selecting the line and pressing the
+"Toggle arrow" tool on the formatting toolbar.
+
+\section{Adding text}
+
+Select the text tool (on the symbol palette) and left-click on
+a object. If you click outside a object on the document, you are
+prompted to add a new free-floating text box.
+
+Alternatively, you can select a object and press Return (or
+select the "Edit|Edit label" menu item); or right-click and
+select "Edit label" from the object menu.
+
+Change the point size using the combobox on the formatting
+toolbar.
+
+\section{Aligning objects}
+
+Select several objects and click on an alignment tool on
+the formatting toolbar. The alignment will be done with
+respect to the first object you select. You can also copy
+the size of a object to other objects with the "Copy size" tool.
+
+\section{Adding segments to lines and straightening them}
+
+To make a line have more than one segment, select the line,
+then press the "New line point" tool. Create as many new control points
+(and therefore segments) as you like. Then arrange the points
+into a rough approximation of how they should be laid out
+horizontally and vertically. Click on "Straighten lines" to
+tidy up the layout.
+
+To delete a line control point, select the line and click on
+"Cut line point" tool. An arbitrary point will be deleted.
+
+\section{Undo/Redo}
+
+Every operation can be undone, and then redone, back until
+the time at which you last saved your document. Use
+"Edit|Undo" and "Edit|Redo"; or the shortcuts Ctrl-Z and Ctrl-Y.
+
+\section{Loading and saving files}
+
+Load and save files using the main toolbar, or "File|Open...",
+"File|Save", "File|Save As..." menu items.
+
+\section{Copy and paste}
+
+OGL Studio has a diagram clipboard, into which you can copy selections. You can then
+paste the contents of clipboard into the same or another diagram window.
+
+Use "Edit|Copy" (or the toolbar copy button) to copy the selection. Use "Edit|Cut" (or the toolbar cut button) to
+copy and then delete the selection. Use "Edit|Paste" (or the toolbar paste button) to copy the selection to
+the current diagram window.
+
+Under Windows, copy and cutting also copies the selection to the Windows clipboard into metafile (vector)
+format, and Windows bitmap format. Note that not all Windows applications can accept the vector format.
+If the application seems to be pasting the wrong format into the document, try using that application's
+"Edit|Paste Special..." menu item, if one exists.
+
+\section{Keyboard shortcuts}
+
+The following keyboard shortcuts are available. {\bf Note:} The OGL Studio menus indicate which shortcuts are
+available.
+
+\begin{twocollist}\itemsep=0pt
+\twocolitem{Delete}{Clear selected object(s)}
+\twocolitem{Enter}{Edit text for selected object}
+\twocolitem{Ctrl-A}{Select all}
+\twocolitem{Ctrl-C}{Copy the selection to the clipboard}
+\twocolitem{Ctrl-D}{Duplicate the selection}
+\twocolitem{Ctrl-O}{Open a diagram}
+\twocolitem{Ctrl-N}{Create a new diagram}
+\twocolitem{Ctrl-P}{Print (not implemented)}
+\twocolitem{Ctrl-S}{Save the diagram file without prompting}
+\twocolitem{Ctrl-V}{Paste the selection}
+\twocolitem{Ctrl-W}{Close the current window}
+\twocolitem{Ctrl-X}{Cut the selection}
+\twocolitem{Ctrl-Z}{Undo last action}
+\twocolitem{Ctrl-Y}{Redo current action on the undo stack}
+\twocolitem{Ctrl-Enter}{Confirm the label editing operation (dismisses the dialog)}
+\twocolitem{Esc}{Cancel the label editing dialog}
+\twocolitem{F1}{Invoke the manual}
+\twocolitem{F12}{Save the diagram file, prompting for a filename}
+\end{twocollist}
+
+\chapter{Menu commands}\label{menucommands}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+This section describes the menu commands.
+
+\section{File}
+
+\begin{twocollist}\itemsep=0pt
+\twocolitem{{\bf New...}}{Creates a new diagram window.}
+\twocolitem{{\bf Open...}}{Opens a diagram file.}
+\twocolitem{{\bf Close}}{Closes the current window.}
+\twocolitem{{\bf Save}}{Saves the current diagram without prompting.}
+\twocolitem{{\bf Save As...}}{Saves the current diagram, prompting for a filename.}
+\twocolitem{{\bf Print...}}{Prints the current diagram (not implemented).}
+\twocolitem{{\bf Print Setup...}}{Invokes the printer setup dialog.}
+\twocolitem{{\bf Print Preview}}{Invokes print preview for this diagram (not implemented).}
+\twocolitem{{\bf Exit}}{Exits the program.}
+\end{twocollist}
+
+Further menu items appended to the end of the File menu allow you
+to load previously-saved diagram files quickly.
+
+\section{Edit}
+
+\begin{twocollist}\itemsep=0pt
+\twocolitem{{\bf Undo}}{Undoes the previous action.}
+\twocolitem{{\bf Redo}}{Redoes the previously undone action.}
+\twocolitem{{\bf Cut}}{Deletes the current selection and places it on the clipboard.}
+\twocolitem{{\bf Copy}}{Copies the current selection onto the clipboard, both to the internal
+diagram clipboard and under Windows, to the Windows clipboard, in metafile and bitmap formats.}
+\twocolitem{{\bf Paste}}{Pastes from the internal diagram clipboard to the currently active window.}
+\twocolitem{{\bf Duplicate}}{Duplicates the current selection, placing the objects further down and to the right.}
+\twocolitem{{\bf Clear}}{Clears the current selection without placing it on the clipboard.}
+\twocolitem{{\bf Select All}}{Selects all objects.}
+\twocolitem{{\bf Edit Label...}}{Invokes a dialog to edit the label of the currently selected object.}
+\end{twocollist}
+
+\begin{comment}%
+\section{View}
+
+\begin{twocollist}\itemsep=0pt
+\twocolitem{{\bf Toolbar}}{Toggles the toolbar on and off.}
+\twocolitem{{\bf Status Bar}}{Toggles the status bar on and off.}
+\twocolitem{{\bf Settings}}{Invokes the \helpref{Settings dialog}{settings} to allow you to adjust a variety of
+settings.}
+\end{twocollist}
+\end{comment}%
+
+\section{Window}
+
+The Window menu is shown when one or more child window is active.
+
+\begin{twocollist}\itemsep=0pt
+\twocolitem{{\bf Cascade}}{Arranges the child windows in a cascade.}
+\twocolitem{{\bf Tile}}{Arranges the child windows in a tiled formation.}
+\twocolitem{{\bf Arrange Icons}}{Arranges the minimized icons.}
+\twocolitem{{\bf Next}}{Activates the next MDI window.}
+\end{twocollist}
+
+Further menu items appended to the end of the Window menu allow you
+to restore and activate any child window.
+
+\section{Help}
+
+\begin{twocollist}\itemsep=0pt
+\twocolitem{{\bf Help Contents}}{Invokes the on-line help, showing the contents page.}
+\twocolitem{{\bf About}}{Displays a small dialog giving copyright and version information.}
+\end{twocollist}
+
+\chapter{Toolbar commands}\label{toolbarcommands}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+This section describes the commands associated with the various toolbars and diagram palette.
+
+\section{Main toolbar}\label{maintoolbar}
+
+The main toolbar is active all the time, with buttons greyed out if not appropriate to the current context.
+
+\begin{twocollist}
+\twocolitem{\image{1cm;0cm}{new.bmp}}{{\bf New} Creates a new diagram window.}
+\twocolitem{\image{1cm;0cm}{open.bmp}}{{\bf Open} Opens a diagram file.}
+\twocolitem{\image{1cm;0cm}{save.bmp}}{{\bf Save} Saves the current diagram without prompting.}
+\twocolitem{\image{1cm;0cm}{print.bmp}}{{\bf Print} Prints the current diagram (not implemented).}
+\twocolitem{\image{1cm;0cm}{copy.bmp}}{{\bf Copy} Copies the current selection onto the internal clipboard, and under Windows, into the Windows clipboard
+in metafile and bitmap formats.}
+\twocolitem{\image{1cm;0cm}{cut.bmp}}{{\bf Cut} Deletes the current selection and puts it on the clipboard.}
+\twocolitem{\image{1cm;0cm}{paste.bmp}}{{\bf Paste} Pastes the contents of the internal diagram clipboard onto the
+current diagram window.}
+\twocolitem{\image{1cm;0cm}{undo.bmp}}{{\bf Undo} Undoes the last command.}
+\twocolitem{\image{1cm;0cm}{redo.bmp}}{{\bf Redo} Redoes the last command.}
+\twocolitem{\image{1cm;0cm}{help.bmp}}{{\bf Help button} Invokes on-line help.}
+\end{twocollist}
+
+\section{Diagram formatting toolbar}\label{diagramformattingtoolbar}
+
+The diagram toolbar is visible only when a diagram window is active.
+
+\begin{twocollist}
+\twocolitem{\image{1cm;0cm}{alignl.bmp}}{{\bf Align left} Aligns the selected objects to the left side of the last selection.}
+\twocolitem{\image{1cm;0cm}{alignr.bmp}}{{\bf Align right} Aligns the selected objects to the right side of the last selection.}
+\twocolitem{\image{1cm;0cm}{alignt.bmp}}{{\bf Align top} Aligns the selected objects to the top side of the last selection.}
+\twocolitem{\image{1cm;0cm}{alignb.bmp}}{{\bf Align bottom} Aligns the selected objects to the bottom side of the last selection.}
+\twocolitem{\image{1cm;0cm}{horiz.bmp}}{{\bf Align horizontally} Aligns the selected objects to be centered horizontally with respect to the last selection.}
+\twocolitem{\image{1cm;0cm}{vert.bmp}}{{\bf Align vertically} Aligns the selected objects to be centered vertically with respect to the last selection.}
+\twocolitem{\image{1cm;0cm}{copysize.bmp}}{{\bf Copy size} Makes the selected objects the same size as the last selection.}
+\twocolitem{\image{1cm;0cm}{linearrow.bmp}}{{\bf Line arrow} Toggles an arrow on or off for the selected objects.}
+\twocolitem{\image{1cm;0cm}{newpoint.bmp}}{{\bf New point} Inserts a control point into the selected line(s).}
+\twocolitem{\image{1cm;0cm}{cutpoint.bmp}}{{\bf Cut point} Deletes a control point from the selected line(s).}
+\twocolitem{\image{1cm;0cm}{straight.bmp}}{{\bf Straighten} Straightens line segments that are nearly horizontal
+or vertical.}
+\twocolitem{\image{1cm;0cm}{pointsize.bmp}}{{\bf Point size} Allows selection of the point size for the current
+selection.}
+\twocolitem{\image{1cm;0cm}{zoom.bmp}}{{\bf Zoom control} Allows selection of the zoom level for the current diagram.}
+\end{twocollist}
+
+\section{Diagram palette}\label{diagrampalette}
+
+The diagram palette is visible only when a diagram window is active. It contains the tools for
+adding objects and text to a diagram.
+
+\begin{twocollist}
+\twocolitem{\image{1cm;0cm}{arrow.bmp}}{{\bf Pointer tool} Click on this to allow dragging and selection of objects.}
+\twocolitem{\image{1cm;0cm}{texttool.bmp}}{{\bf Text tool} Click on this, then click on objects or the diagram background
+to edit object or free-floating text labels.}
+\end{twocollist}
+
+The other tools on this palette represent demo objects.
+
+To place an object on a diagram, click on its symbol, then left-click on the diagram. You will need to click
+on the palette symbol each time you wish to create an object, since the palette selection reverts to the pointer tool
+after each object is created.
+
+\chapter{Dialogs}\label{dialogs}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+To be written.
+
+\chapter{How To}\label{howto}%
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+\section{Create a new object}
+
+Create a new diagram window if you have not already. Then:
+
+\begin{itemize}\itemsep=0pt
+\item Left-click on the required object on the palette, then left-click on the diagram window.
+\end{itemize}
+
+
+% This section commented out
+\begin{comment}
+\bibliography{refs}
+\addcontentsline{toc}{chapter}{Bibliography}
+\setheader{{\it REFERENCES}}{}{}{}{}{{\it REFERENCES}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+\end{comment}
+
+\addcontentsline{toc}{chapter}{Index}
+\printindex%
+
+\setheader{{\it INDEX}}{}{}{}{}{{\it INDEX}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}%
+
+\end{document}
diff --git a/samples/ogl/studio/manual/tex2rtf.ini b/samples/ogl/studio/manual/tex2rtf.ini
new file mode 100644 (file)
index 0000000..f44fc89
--- /dev/null
@@ -0,0 +1,20 @@
+; Tex2RTF initialisation file
+runTwice = yes
+titleFontSize = 12
+authorFontSize = 10
+chapterFontSize = 12
+sectionFontSize = 12
+subsectionFontSize = 12
+headerRule = yes
+footerRule = yes
+useHeadingStyles = yes
+contentsDepth = 2
+listItemIndent=40
+winHelpContents = yes
+winHelpVersion = 4 ; 3 for Windows 3.x, 4 for Windows 95
+winHelpTitle = "OGL Studio"
+generateHPJ = yes
+htmlBrowseButtons = bitmap
+truncateFilenames = yes
+htmlIndex = no
+htmlFrameContents = no
diff --git a/samples/ogl/studio/manual/texttool.bmp b/samples/ogl/studio/manual/texttool.bmp
new file mode 100644 (file)
index 0000000..09c989a
Binary files /dev/null and b/samples/ogl/studio/manual/texttool.bmp differ
diff --git a/samples/ogl/studio/manual/tick.bmp b/samples/ogl/studio/manual/tick.bmp
new file mode 100644 (file)
index 0000000..c0d66c9
Binary files /dev/null and b/samples/ogl/studio/manual/tick.bmp differ
diff --git a/samples/ogl/studio/manual/toback.bmp b/samples/ogl/studio/manual/toback.bmp
new file mode 100644 (file)
index 0000000..d2e5eff
Binary files /dev/null and b/samples/ogl/studio/manual/toback.bmp differ
diff --git a/samples/ogl/studio/manual/tofront.bmp b/samples/ogl/studio/manual/tofront.bmp
new file mode 100644 (file)
index 0000000..5b17cc4
Binary files /dev/null and b/samples/ogl/studio/manual/tofront.bmp differ
diff --git a/samples/ogl/studio/manual/tool1.bmp b/samples/ogl/studio/manual/tool1.bmp
new file mode 100644 (file)
index 0000000..cb1760d
Binary files /dev/null and b/samples/ogl/studio/manual/tool1.bmp differ
diff --git a/samples/ogl/studio/manual/tool2.bmp b/samples/ogl/studio/manual/tool2.bmp
new file mode 100644 (file)
index 0000000..a18facc
Binary files /dev/null and b/samples/ogl/studio/manual/tool2.bmp differ
diff --git a/samples/ogl/studio/manual/tool3.bmp b/samples/ogl/studio/manual/tool3.bmp
new file mode 100644 (file)
index 0000000..6a49f45
Binary files /dev/null and b/samples/ogl/studio/manual/tool3.bmp differ
diff --git a/samples/ogl/studio/manual/tool4.bmp b/samples/ogl/studio/manual/tool4.bmp
new file mode 100644 (file)
index 0000000..95c2061
Binary files /dev/null and b/samples/ogl/studio/manual/tool4.bmp differ
diff --git a/samples/ogl/studio/manual/undo.bmp b/samples/ogl/studio/manual/undo.bmp
new file mode 100644 (file)
index 0000000..4ad80c7
Binary files /dev/null and b/samples/ogl/studio/manual/undo.bmp differ
diff --git a/samples/ogl/studio/manual/up.gif b/samples/ogl/studio/manual/up.gif
new file mode 100644 (file)
index 0000000..316d0d2
Binary files /dev/null and b/samples/ogl/studio/manual/up.gif differ
diff --git a/samples/ogl/studio/manual/vert.bmp b/samples/ogl/studio/manual/vert.bmp
new file mode 100644 (file)
index 0000000..dfd7b5c
Binary files /dev/null and b/samples/ogl/studio/manual/vert.bmp differ
diff --git a/samples/ogl/studio/manual/zoom.bmp b/samples/ogl/studio/manual/zoom.bmp
new file mode 100644 (file)
index 0000000..c6a137a
Binary files /dev/null and b/samples/ogl/studio/manual/zoom.bmp differ
diff --git a/samples/ogl/studio/project.cpp b/samples/ogl/studio/project.cpp
new file mode 100644 (file)
index 0000000..a6298d6
--- /dev/null
@@ -0,0 +1,89 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        project.cpp
+// Purpose:     Studio project classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     27/7/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#include "wx/mdi.h"
+#endif
+
+#include "wx/laywin.h"
+#include "studio.h"
+#include "project.h"
+
+IMPLEMENT_CLASS(csProjectTreeCtrl, wxTreeCtrl)
+
+BEGIN_EVENT_TABLE(csProjectTreeCtrl, wxTreeCtrl)
+END_EVENT_TABLE()
+
+// Define my frame constructor
+csProjectTreeCtrl::csProjectTreeCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
+       long style):
+
+  wxTreeCtrl(parent, id, pos, size, style),
+  m_imageList(16, 16)
+{
+    m_imageList.Add(wxIcon("folder1"));
+    m_imageList.Add(wxIcon("file1"));
+
+    SetImageList(& m_imageList);
+}
+
+csProjectTreeCtrl::~csProjectTreeCtrl()
+{
+    SetImageList(NULL);
+}
+
+// Create the project window
+bool csApp::CreateProjectWindow(wxFrame *parent)
+{
+#if 0
+    // Create a layout window
+    wxSashLayoutWindow* win = new wxSashLayoutWindow(parent, ID_LAYOUT_WINDOW_PROJECT, wxDefaultPosition, wxSize(200, 30), wxNO_BORDER|wxSW_3D|wxCLIP_CHILDREN);
+    win->SetDefaultSize(wxSize(150, 10000));
+    win->SetOrientation(wxLAYOUT_VERTICAL);
+    win->SetAlignment(wxLAYOUT_LEFT);
+    win->SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
+    win->SetSashVisible(wxSASH_RIGHT, TRUE);
+    win->SetExtraBorderSize(5);
+
+    m_projectSashWindow = win;
+
+    m_projectTreeCtrl = new csProjectTreeCtrl(win, ID_WINDOW_PROJECT_TREE, wxDefaultPosition,
+        wxDefaultSize, wxTR_HAS_BUTTONS|wxTR_LINES_AT_ROOT|wxDOUBLE_BORDER);
+
+    // For now, hide the window
+    m_projectSashWindow->Show(FALSE);
+#endif
+
+    return TRUE;
+}
+
+// Fill out the project tree control
+void csApp::FillProjectTreeCtrl()
+{
+#if 0
+    csProjectTreeCtrl& tree = *GetProjectTreeCtrl();
+
+    // Dummy data for now
+    long level0 = tree.InsertItem(0, "Applications", 0, 0);
+    long level1 = tree.InsertItem(level0, "Projects", 0, 0);
+    tree.InsertItem(level1, "project1", 1, 1);
+    tree.InsertItem(level1, "project2", 1, 1);
+#endif
+}
+
diff --git a/samples/ogl/studio/project.h b/samples/ogl/studio/project.h
new file mode 100644 (file)
index 0000000..b52ae25
--- /dev/null
@@ -0,0 +1,42 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        project.h
+// Purpose:     Studio project classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     27/7/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_PROJECT_H_
+#define _STUDIO_PROJECT_H_
+
+#include <wx/treectrl.h>
+#include <wx/imaglist.h>
+
+/*
+ * This is the project tree control.
+ */
+
+class csProjectTreeCtrl: public wxTreeCtrl
+{
+
+DECLARE_CLASS(csProjectTreeCtrl)
+public:
+
+    csProjectTreeCtrl(wxWindow *parent, const wxWindowID id, const wxPoint& pos, const wxSize& size,
+       long style = wxTR_HAS_BUTTONS|wxTR_LINES_AT_ROOT);
+
+    ~csProjectTreeCtrl();
+
+    wxImageList& GetImageList() const { return (wxImageList&) m_imageList; }
+protected:
+    wxImageList     m_imageList;
+
+DECLARE_EVENT_TABLE()
+};
+
+#endif
+  // _STUDIO_PROJECT_H_
+
diff --git a/samples/ogl/studio/shapes.cpp b/samples/ogl/studio/shapes.cpp
new file mode 100644 (file)
index 0000000..7bf02ed
--- /dev/null
@@ -0,0 +1,1194 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        shapes.cpp
+// Purpose:     Implements Studio shapes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
+#endif
+
+#include <wx/wxexpr.h>
+
+#include "studio.h"
+#include "doc.h"
+#include "shapes.h"
+#include "view.h"
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/linesp.h>
+#include "cspalette.h"
+#include "dialogs.h"
+
+#define csSTANDARD_SHAPE_WIDTH      100
+
+IMPLEMENT_CLASS(csDiagram, wxDiagram)
+
+csDiagram::~csDiagram()
+{
+    DeleteAllShapes();
+}
+
+void csDiagram::Redraw(wxDC& dc)
+{
+    wxDiagram::Redraw(dc);
+
+    // Draw line crossings
+    wxLineCrossings lineCrossings;
+    lineCrossings.FindCrossings(*this);
+    lineCrossings.DrawCrossings(*this, dc);
+}
+
+/*
+ * csEvtHandler: an event handler class for all shapes
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(csEvtHandler, wxShapeEvtHandler)
+
+csEvtHandler::csEvtHandler(wxShapeEvtHandler *prev, wxShape *shape, const wxString& lab):
+  wxShapeEvtHandler(prev, shape)
+{
+    m_label = lab;
+}
+
+csEvtHandler::~csEvtHandler()
+{
+}
+
+// Copy any event handler data
+void csEvtHandler::CopyData(wxShapeEvtHandler& copy)
+{
+    wxShapeEvtHandler::CopyData(copy);
+
+    csEvtHandler& csCopy = (csEvtHandler&) copy;
+    csCopy.m_label = m_label;
+}
+void csEvtHandler::OnLeftClick(double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  csDiagramView* view = ((csCanvas*)GetShape()->GetCanvas())->GetView();
+  view->ReflectPointSize(GetShape()->GetFont()->GetPointSize());
+
+  if (GetShape()->IsKindOf(CLASSINFO(wxLineShape)))
+      view->ReflectArrowState((wxLineShape*) GetShape());
+
+  csEditorToolPalette *palette = wxGetApp().GetDiagramPalette();
+  if (palette->GetSelection() == PALETTE_TEXT_TOOL)
+  {
+        view->ReflectPointSize(GetShape()->GetFont()->GetPointSize());
+
+        EditProperties();
+#if 0
+        csLabelEditingDialog* dialog = new csLabelEditingDialog(GetShape()->GetCanvas()->GetParent());
+        dialog->SetShapeLabel(m_label);
+        if (dialog->ShowModal() == wxID_CANCEL)
+        {
+            dialog->Destroy();
+            return;
+        }
+
+        wxString newLabel = dialog->GetShapeLabel();
+        dialog->Destroy();
+
+        wxShape* newShape = GetShape()->CreateNewCopy();
+
+        csEvtHandler* handler = (csEvtHandler *)newShape->GetEventHandler();
+        handler->m_label = newLabel;
+
+        view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument*) view->GetDocument(),
+            new csCommandState(ID_CS_EDIT_PROPERTIES, newShape, GetShape())));
+#endif
+        return;
+  }
+
+  if (keys == 0)
+  {
+    // If no shift key, then everything is deselected.
+    // If the shape was selected, deselect it and vice versa.
+    bool selected = GetShape()->Selected();
+
+    view->SelectAll(FALSE);
+
+    selected = !selected;
+
+    GetShape()->Select(selected, &dc);
+    GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be missing
+
+    view->SelectShape(GetShape(), selected);
+  }
+  else if (keys & KEY_SHIFT)
+  {
+    if (GetShape()->Selected())
+    {
+        GetShape()->Select(FALSE, &dc);
+        view->SelectShape(GetShape(), FALSE);
+    }
+    else
+    {
+        GetShape()->Select(TRUE, &dc);
+        view->SelectShape(GetShape(), TRUE);
+    }
+    GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be missing
+  }
+  else if (keys & KEY_CTRL)
+  {
+    // Do something for CONTROL
+  }
+  else
+  {
+    ((wxFrame*)wxGetApp().GetTopWindow())->SetStatusText(m_label);
+  }
+}
+
+void csEvtHandler::OnRightClick(double x, double y, int keys, int attachment)
+{
+    // Have to convert back to physical coordinates from logical coordinates.
+
+    int viewStartX, viewStartY;
+    int unitX, unitY;
+    GetShape()->GetCanvas()->ViewStart(& viewStartX, & viewStartY);
+    GetShape()->GetCanvas()->GetScrollPixelsPerUnit(& unitX, & unitY);
+
+    int x1 = (int)(x * GetShape()->GetCanvas()->GetScaleX());
+    int y1 = (int)(y * GetShape()->GetCanvas()->GetScaleY());
+
+    int menuX = (int) (x1 - (viewStartX * unitX)) ;
+    int menuY = (int) (y1 - (viewStartY * unitY));
+
+    wxGetApp().GetShapeEditMenu()->SetClientData((char*) GetShape());
+    wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_CLOCKWISE, !GetShape()->IsKindOf(CLASSINFO(wxLineShape)));
+    wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_ANTICLOCKWISE, !GetShape()->IsKindOf(CLASSINFO(wxLineShape)));
+
+    GetShape()->GetCanvas()->PopupMenu(wxGetApp().GetShapeEditMenu(), menuX, menuY);
+}
+
+/*
+ * Implement connection of two shapes by right-dragging between them.
+ */
+
+void csEvtHandler::OnBeginDragRight(double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetLogicalFunction(OGLRBLF);
+  dc.SetPen(dottedPen);
+  double xp, yp;
+  GetShape()->GetAttachmentPositionEdge(attachment, &xp, &yp);
+  dc.DrawLine(xp, yp, x, y);
+  GetShape()->GetCanvas()->CaptureMouse();
+}
+
+void csEvtHandler::OnDragRight(bool draw, double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetLogicalFunction(OGLRBLF);
+  dc.SetPen(dottedPen);
+  double xp, yp;
+  GetShape()->GetAttachmentPositionEdge(attachment, &xp, &yp);
+  dc.DrawLine(xp, yp, x, y);
+}
+
+void csEvtHandler::OnEndDragRight(double x, double y, int keys, int attachment)
+{
+  GetShape()->GetCanvas()->ReleaseMouse();
+  csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
+
+  // Check if we're on an object
+  int new_attachment;
+  wxShape *otherShape = canvas->FindFirstSensitiveShape(x, y, &new_attachment, OP_DRAG_RIGHT);
+  
+  if (otherShape && !otherShape->IsKindOf(CLASSINFO(wxLineShape)))
+  {
+        wxLineShape* theShape = new csLineShape;
+
+        theShape->AssignNewIds();
+        theShape->SetEventHandler(new csEvtHandler(theShape, theShape, wxString("")));
+        theShape->SetPen(wxBLACK_PEN);
+        theShape->SetBrush(wxRED_BRUSH);
+
+        wxToolBar* toolbar = wxGetApp().GetDiagramToolBar();
+        bool haveArrow = toolbar->GetToolState(DIAGRAM_TOOLBAR_LINE_ARROW);
+
+        wxLineShape *lineShape = (wxLineShape *)theShape;
+
+        // Yes, you can have more than 2 control points, in which case
+        // it becomes a multi-segment line.
+        lineShape->MakeLineControlPoints(2);
+
+        if (haveArrow)
+            lineShape->AddArrow(ARROW_ARROW, ARROW_POSITION_MIDDLE, 10.0, 0.0, "Normal arrowhead");
+
+        lineShape->SetFrom(GetShape());
+        lineShape->SetTo(otherShape);
+        lineShape->SetAttachments(attachment, new_attachment);
+
+        canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(
+            new csDiagramCommand("Line", (csDiagramDocument *)canvas->GetView()->GetDocument(),
+                    new csCommandState(ID_CS_ADD_LINE, lineShape, NULL)));
+  }
+}
+
+static double g_DragOffsetX = 0.0;
+static double g_DragOffsetY = 0.0;
+static double g_DragStartX = 0.0;
+static double g_DragStartY = 0.0;
+
+void csEvtHandler::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (GetShape()->GetParent())
+    {
+      GetShape()->GetParent()->HitTest(x, y, &attachment, &dist);
+      GetShape()->GetParent()->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+  double xx, yy;
+  xx = x + g_DragOffsetX;
+  yy = y + g_DragOffsetY;
+
+  GetShape()->GetCanvas()->Snap(&xx, &yy);
+
+  double offsetX = xx - g_DragStartX;
+  double offsetY = yy - g_DragStartY;
+
+//  m_xpos = xx; m_ypos = yy;
+  double w, h;
+  GetShape()->GetBoundingBoxMax(&w, &h);
+  GetShape()->GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
+
+  // Draw bounding box for other selected shapes
+  wxNode* node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+     wxShape* shape = (wxShape*) node->Data();
+     if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape()))
+     {
+        shape->GetBoundingBoxMax(&w, &h);
+        shape->OnDrawOutline(dc, shape->GetX() + offsetX, shape->GetY() + offsetY, w, h);
+     }
+     node = node->Next();
+  }
+}
+
+void csEvtHandler::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (GetShape()->GetParent())
+    {
+      GetShape()->GetParent()->HitTest(x, y, &attachment, &dist);
+      GetShape()->GetParent()->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  // New policy: don't erase shape until end of drag.
+//  Erase(dc);
+
+  g_DragOffsetX = GetShape()->GetX() - x;
+  g_DragOffsetY = GetShape()->GetY() - y;
+
+  double xx, yy;
+  xx = x + g_DragOffsetX;
+  yy = y + g_DragOffsetY;
+
+  GetShape()->GetCanvas()->Snap(&xx, &yy);
+
+  g_DragStartX = GetShape()->GetX();
+  g_DragStartY = GetShape()->GetY();
+
+  double offsetX = xx - g_DragStartX;
+  double offsetY = yy - g_DragStartY;
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  double w, h;
+  GetShape()->GetBoundingBoxMax(&w, &h);
+  GetShape()->GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
+
+  // Draw bounding box for other selected shapes
+  wxNode* node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+     wxShape* shape = (wxShape*) node->Data();
+     if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape()))
+     {
+        shape->GetBoundingBoxMax(&w, &h);
+        shape->OnDrawOutline(dc, shape->GetX() + offsetX, shape->GetY() + offsetY, w, h);
+     }
+     node = node->Next();
+  }
+
+  GetShape()->GetCanvas()->CaptureMouse();
+}
+
+
+void csEvtHandler::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+  csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
+
+  canvas->ReleaseMouse();
+  if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (GetShape()->GetParent())
+    {
+      GetShape()->GetParent()->HitTest(x, y, &attachment, &dist);
+      GetShape()->GetParent()->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxClientDC dc(canvas);
+  canvas->PrepareDC(dc);
+
+  dc.SetLogicalFunction(wxCOPY);
+
+  double xx = x + g_DragOffsetX;
+  double yy = y + g_DragOffsetY;
+
+  canvas->Snap(&xx, &yy);
+
+  double offsetX = xx - g_DragStartX;
+  double offsetY = yy - g_DragStartY;
+
+  wxShape* newShape = GetShape()->CreateNewCopy();
+
+  newShape->SetX(xx);
+  newShape->SetY(yy);
+
+  csDiagramCommand* cmd = new csDiagramCommand("Move", (csDiagramDocument*)canvas->GetView()->GetDocument(),
+                new csCommandState(ID_CS_MOVE, newShape, GetShape()));
+
+  // Move line points
+  wxNode* node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+     wxShape* shape = (wxShape*) node->Data();
+     // Only move the line point(s) if both ends move too
+     if (shape->IsKindOf(CLASSINFO(wxLineShape)) &&
+           ((wxLineShape*)shape)->GetTo()->Selected() && ((wxLineShape*)shape)->GetFrom()->Selected())
+     {
+        wxLineShape* lineShape = (wxLineShape*) shape;
+
+        if (lineShape->GetLineControlPoints()->Number() > 2)
+        {
+            wxLineShape* newLineShape = (wxLineShape*) lineShape->CreateNewCopy();
+
+            wxNode *node1 = newLineShape->GetLineControlPoints()->First();
+            while (node1)
+            {
+                wxRealPoint *point = (wxRealPoint *)node1->Data();
+                point->x += offsetX;
+                point->y += offsetY;
+                node1 = node1->Next();
+            }
+            cmd->AddState(new csCommandState(ID_CS_MOVE_LINE_POINT, newLineShape, lineShape));
+            lineShape->Erase(dc);
+        }
+     }
+     node = node->Next();
+  }
+
+  // Add other selected node shapes, if any
+  node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+     wxShape* shape = (wxShape*) node->Data();
+     if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape()))
+     {
+        wxShape* newShape2 = shape->CreateNewCopy();
+        newShape2->SetX(shape->GetX() + offsetX);
+        newShape2->SetY(shape->GetY() + offsetY);
+        cmd->AddState(new csCommandState(ID_CS_MOVE, newShape2, shape));
+     }
+     node = node->Next();
+  }
+
+  canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
+}
+
+void csEvtHandler::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  wxShape* shape = GetShape();
+  csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
+
+  if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+  {
+    // TODO: Do/Undo support for line operations
+    ((wxLineShape*)shape)->wxLineShape::OnSizingEndDragLeft(pt, x, y, keys, attachment);
+#if 0
+        wxLineShape* lineShape = (wxLineShape*) shape;
+
+        wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
+
+        wxClientDC dc(canvas);
+        canvas->PrepareDC(dc);
+
+        shape->SetDisableLabel(FALSE);
+
+        if (lpt->m_type == CONTROL_POINT_LINE)
+        {
+            canvas->Snap(&x, &y);
+
+            dc.SetLogicalFunction(wxCOPY);
+            lpt->SetX(x); lpt->SetY(y);
+            lpt->m_point->x = x; lpt->m_point->y = y;
+
+            this->OnMoveLink(dc);
+        }
+        if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM)
+        {
+            if (lpt->m_oldCursor)
+                canvas->SetCursor(lpt->m_oldCursor);
+            lineShape->Erase(dc);
+
+            lpt->SetX(x); lpt->SetY(y);
+
+            if (lineShape->GetFrom())
+            {
+                lineShape->GetFrom()->MoveLineToNewAttachment(dc, lineShape, x, y);
+            }
+        }
+        if (lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
+        {
+            if (lpt->m_oldCursor)
+                canvas->SetCursor(lpt->m_oldCursor);
+
+            lpt->SetX(x); lpt->SetY(y);
+
+            if (lineShape->GetTo())
+            {
+                lineShape->GetTo()->MoveLineToNewAttachment(dc, lineShape, x, y);
+            }
+        }
+#endif
+        return;
+  }
+
+  wxClientDC dc(canvas);
+  canvas->PrepareDC(dc);
+
+  canvas->ReleaseMouse();
+  dc.SetLogicalFunction(wxCOPY);
+
+//  shape->Erase(dc);
+/*
+  shape->Recompute();
+  shape->ResetControlPoints();
+  if (!pt->m_eraseObject)
+    shape->Show(FALSE);
+*/
+
+  wxShape* newShape = shape->CreateNewCopy();
+
+  if (newShape->IsKindOf(CLASSINFO(wxPolygonShape)))
+  {
+    wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
+    newShape->SetSize(ppt->GetNewSize().x, ppt->GetNewSize().y);
+
+    ((wxPolygonShape *)newShape)->CalculateBoundingBox();
+    ((wxPolygonShape *)newShape)->CalculatePolygonCentre();
+    newShape->ResetControlPoints();
+  }
+  else
+  {
+    newShape->SetSize(pt->sm_controlPointDragEndWidth, pt->sm_controlPointDragEndHeight);
+    if (shape->GetCentreResize())
+    {
+      // Old position is fine
+    }
+    else
+    {
+      newShape->SetX(pt->sm_controlPointDragPosX);
+      newShape->SetY(pt->sm_controlPointDragPosY);
+    }
+  }
+
+  csDiagramCommand* cmd = new csDiagramCommand("Size", (csDiagramDocument*)canvas->GetView()->GetDocument(),
+                new csCommandState(ID_CS_SIZE, newShape, shape));
+
+  canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
+
+}
+
+void csEvtHandler::OnEndSize(double x, double y)
+{
+  wxClientDC dc(GetShape()->GetCanvas());
+  GetShape()->GetCanvas()->PrepareDC(dc);
+
+  GetShape()->FormatText(dc, m_label);
+}
+
+void csEvtHandler::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering)
+{
+    csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
+
+    // We actually submit two different states: one to change the ordering, and another
+    // to change the attachment for the line.
+    // Problem. If we refresh after the attachment change, we'll get a flicker.
+    // We really want to do both in a oner.
+
+    csDiagramCommand* cmd = new csDiagramCommand("Change attachment", (csDiagramDocument*)canvas->GetView()->GetDocument());
+
+    wxLineShape* newLine = (wxLineShape*) line->CreateNewCopy();
+    if (line->GetTo() == GetShape())
+        newLine->SetAttachmentTo(attachment);
+    else
+        newLine->SetAttachmentFrom(attachment);
+
+    cmd->AddState(new csCommandState(ID_CS_CHANGE_LINE_ATTACHMENT, newLine, line));
+
+    // Change ordering
+    wxShape* newShape = GetShape()->CreateNewCopy();
+    newShape->ApplyAttachmentOrdering(ordering);
+
+    cmd->AddState(new csCommandState(ID_CS_CHANGE_LINE_ORDERING, newShape, GetShape()));
+
+    canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
+}
+
+void csEvtHandler::OnLeftDoubleClick(double x, double y, int keys, int attachment)
+{
+    EditProperties();
+}
+
+// Popup up a property dialog
+bool csEvtHandler::EditProperties()
+{
+    wxShape* shape = GetShape();
+
+    // For now, no line property editing
+    if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        return FALSE;
+
+    csDiagramView* view = ((csCanvas*)shape->GetCanvas())->GetView();
+
+    wxPanel* attributeDialog;
+    wxString attributeDialogName;
+    wxString title;
+
+    if (shape->IsKindOf(CLASSINFO(csThinRectangleShape)))
+    {
+        attributeDialog = new csThinRectangleDialog;
+        attributeDialogName = "thin_rectangle";
+        title = "Thin Rectangle Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csWideRectangleShape)))
+    {
+        attributeDialog = new csWideRectangleDialog;
+        attributeDialogName = "wide_rectangle";
+        title = "Wide Rectangle Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csTriangleShape)))
+    {
+        attributeDialog = new csTriangleDialog;
+        attributeDialogName = "triangle";
+        title = "Triangle Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csSemiCircleShape)))
+    {
+        attributeDialog = new csSemiCircleDialog;
+        attributeDialogName = "semi_circle";
+        title = "Semicircle Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csCircleShape)))
+    {
+        attributeDialog = new csCircleDialog;
+        attributeDialogName = "circle";
+        title = "Circle Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csCircleShadowShape)))
+    {
+        attributeDialog = new csCircleShadowDialog;
+        attributeDialogName = "circle_shadow";
+        title = "Circle Shadow Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csTextBoxShape)))
+    {
+        attributeDialog = new csTextBoxDialog;
+        attributeDialogName = "text_box";
+        title = "Text Box Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csGroupShape)))
+    {
+        attributeDialog = new csGroupDialog;
+        attributeDialogName = "group";
+        title = "Group Properties";
+    }
+    else if (shape->IsKindOf(CLASSINFO(csOctagonShape)))
+    {
+        attributeDialog = new csOctagonDialog;
+        attributeDialogName = "octagon";
+        title = "Octagon Properties";
+    }
+    else
+    {
+        wxMessageBox("Unrecognised shape.", "Studio", wxICON_EXCLAMATION);
+        return FALSE;
+    }
+
+    csShapePropertiesDialog* dialog = new csShapePropertiesDialog(shape->GetCanvas()->GetParent(), title, attributeDialog, attributeDialogName);
+    dialog->GetGeneralPropertiesDialog()->SetShapeLabel(m_label);
+    if (dialog->ShowModal() == wxID_CANCEL)
+    {
+        dialog->Destroy();
+        return FALSE;
+    }
+
+    wxString newLabel = dialog->GetGeneralPropertiesDialog()->GetShapeLabel();
+    dialog->Destroy();
+
+    wxShape* newShape = shape->CreateNewCopy();
+
+    csEvtHandler* handler2 = (csEvtHandler *)newShape->GetEventHandler();
+    handler2->m_label = newLabel;
+
+    view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit properties", (csDiagramDocument*) view->GetDocument(),
+                new csCommandState(ID_CS_EDIT_PROPERTIES, newShape, shape)));
+
+    return TRUE;
+}
+
+/*
+ * Diagram
+ */
+bool csDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
+{
+  wxDiagram::OnShapeSave(db, shape, expr);
+  csEvtHandler *handler = (csEvtHandler *)shape.GetEventHandler();
+  expr.AddAttributeValueString("label", handler->m_label);
+  return TRUE;
+}
+
+bool csDiagram::OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
+{
+  wxDiagram::OnShapeLoad(db, shape, expr);
+  wxString label("");
+  expr.GetAttributeValue("label", label);
+  csEvtHandler *handler = new csEvtHandler(&shape, &shape, label);
+  shape.SetEventHandler(handler);
+  
+  return TRUE;
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csThinRectangleShape, wxDrawnShape)
+
+csThinRectangleShape::csThinRectangleShape()
+{
+    SetDrawnPen(wxBLACK_PEN);
+    wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
+    SetDrawnBrush(brush);
+
+    double w = csSTANDARD_SHAPE_WIDTH/2;
+    double h = csSTANDARD_SHAPE_WIDTH;
+
+    DrawRectangle(wxRect(- w/2, - h/2, w, h));
+    CalculateSize();
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csWideRectangleShape, wxDrawnShape)
+
+csWideRectangleShape::csWideRectangleShape()
+{
+    SetDrawnPen(wxBLACK_PEN);
+    wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
+    SetDrawnBrush(brush);
+
+    double w = csSTANDARD_SHAPE_WIDTH;
+    double h = w/2.0;
+
+    DrawRoundedRectangle(wxRect(- w/2, - h/2, w, h), -0.3);
+    CalculateSize();
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csTriangleShape, wxDrawnShape)
+
+csTriangleShape::csTriangleShape()
+{
+    SetDrawnPen(wxBLACK_PEN);
+    wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
+    SetDrawnBrush(brush);
+
+    double w = csSTANDARD_SHAPE_WIDTH;
+    double h = (csSTANDARD_SHAPE_WIDTH*2.0)/3.0;
+
+    // Triangle, from top vertex
+    wxPoint* points = new wxPoint[3];
+
+
+    points[0] = wxPoint( 0 ,  - h / 2 );
+    points[1] = wxPoint( w / 2 ,  h / 2 );
+    points[2] = wxPoint( -w / 2,  h / 2 );
+
+    DrawPolygon(3, points, oglMETAFLAGS_OUTLINE);
+
+    delete[] points;
+
+    // Add another triangle at the top for the black bit
+    SetDrawnBrush(wxBLACK_BRUSH);
+
+    points = new wxPoint[3];
+
+    // Calculate where the new points will be, using the proportions
+    // of the triangle.
+    double h1 = 8; // Height of little triangle.
+
+    /*
+        Formula: ((w/2) / h) = w1 / h1
+        w1 = ((w/2) / h) * h1;
+    */
+    double ratio = ((w/2.0) / h) ;
+    double w1 = ratio * h1;
+
+    points[0] = wxPoint(0  ,  (int) (- h / 2 ));
+    points[1] = wxPoint( (int) w1,  (int) (- h / 2 + h1));
+    points[2] = wxPoint( (int) -w1, (int) (- h / 2 + h1));
+
+    DrawPolygon(3, points);
+
+    delete[] points;
+
+    CalculateSize();
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csSemiCircleShape, wxDrawnShape)
+
+csSemiCircleShape::csSemiCircleShape()
+{
+    // Zero degrees
+    DrawAtAngle(oglDRAWN_ANGLE_0);
+
+    double w = csSTANDARD_SHAPE_WIDTH;
+    double h = w/2.0;
+
+    SetDrawnPen(wxTRANSPARENT_PEN);
+    SetDrawnBrush(wxTRANSPARENT_BRUSH);
+
+    // Draw a dummy rectangle that will be used for calculating the
+    // bounding box, since we can't calculate the bounding box for
+    // an arbitrary arc (not implemented)
+
+    DrawRectangle(wxRect(-w/2.0, -h/2.0, w, h));
+
+    SetDrawnPen(wxBLACK_PEN);
+    wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
+    SetDrawnBrush(brush);
+
+    DrawEllipticArc(wxRect(-w/2, -h/2, w, 2*h), 0.0, 180.0);
+    DrawLine(wxPoint(-w/2, h/2), wxPoint(w/2, h/2));
+
+    CalculateSize();
+
+    /// 90 degrees
+
+    w = csSTANDARD_SHAPE_WIDTH/2;
+    h = csSTANDARD_SHAPE_WIDTH;
+
+    DrawAtAngle(oglDRAWN_ANGLE_90);
+
+    SetDrawnPen(wxTRANSPARENT_PEN);
+    SetDrawnBrush(wxTRANSPARENT_BRUSH);
+
+    DrawRectangle(wxRect(-w/2, -h/2, w, h));
+
+    SetDrawnPen(wxBLACK_PEN);
+    SetDrawnBrush(brush);
+
+    DrawEllipticArc(wxRect(-w/2 - w, -h/2, 2*w, h), 270.0, 90.0);
+    DrawLine(wxPoint(-w/2, -h/2), wxPoint(-w/2, h/2));
+
+    CalculateSize();
+
+    /// 180 degrees
+
+    DrawAtAngle(oglDRAWN_ANGLE_180);
+
+    w = csSTANDARD_SHAPE_WIDTH;
+    h = csSTANDARD_SHAPE_WIDTH/2;
+
+    SetDrawnPen(wxTRANSPARENT_PEN);
+    SetDrawnBrush(wxTRANSPARENT_BRUSH);
+
+    DrawRectangle(wxRect(-w/2, -h/2, w, h));
+
+    SetDrawnPen(wxBLACK_PEN);
+    SetDrawnBrush(brush);
+
+    DrawEllipticArc(wxRect(-w/2, -h/2 - h, w, 2*h), 180.0, 0.0);
+    DrawLine(wxPoint(-w/2, -h/2), wxPoint(w/2, -h/2));
+
+    CalculateSize();
+
+    /// 270 degrees
+
+    DrawAtAngle(oglDRAWN_ANGLE_270);
+
+    w = csSTANDARD_SHAPE_WIDTH/2;
+    h = csSTANDARD_SHAPE_WIDTH;
+
+    SetDrawnPen(wxTRANSPARENT_PEN);
+    SetDrawnBrush(wxTRANSPARENT_BRUSH);
+
+    DrawRectangle(wxRect(-w/2, -h/2, w, h));
+
+    SetDrawnPen(wxBLACK_PEN);
+    SetDrawnBrush(brush);
+
+    DrawEllipticArc(wxRect(-w/2, -h/2, 2*w, h), 90.0, 270.0);
+    DrawLine(wxPoint(w/2, -h/2), wxPoint(w/2, h/2));
+
+    CalculateSize();
+
+    // Reset to zero
+    DrawAtAngle(oglDRAWN_ANGLE_0);
+    CalculateSize();
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csCircleShape, wxCircleShape)
+
+csCircleShape::csCircleShape()
+{
+    SetPen(wxBLACK_PEN);
+    wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
+    SetBrush(brush);
+
+    SetSize(csSTANDARD_SHAPE_WIDTH*0.6, csSTANDARD_SHAPE_WIDTH*0.6);
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csCircleShadowShape, wxCircleShape)
+
+csCircleShadowShape::csCircleShadowShape()
+{
+    SetPen(wxBLACK_PEN);
+    wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
+    SetBrush(brush);
+
+    SetSize(csSTANDARD_SHAPE_WIDTH*0.6, csSTANDARD_SHAPE_WIDTH*0.6);
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+    SetShadowMode(SHADOW_RIGHT);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csOctagonShape, wxPolygonShape)
+
+csOctagonShape::csOctagonShape()
+{
+    SetPen(wxBLACK_PEN);
+    SetBrush(wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID));
+
+    double w = csSTANDARD_SHAPE_WIDTH*0.5;
+    double h = csSTANDARD_SHAPE_WIDTH*0.5;
+
+    double prop = h/3.0;
+
+    wxList* points = new wxList;
+    points->Append((wxObject*) new wxRealPoint(-w/2.0 + prop, -h/2.0));
+    points->Append((wxObject*) new wxRealPoint(w/2.0 - prop, -h/2.0));
+    points->Append((wxObject*) new wxRealPoint(w/2.0, -h/2.0 + prop));
+    points->Append((wxObject*) new wxRealPoint(w/2.0, h/2.0 - prop));
+    points->Append((wxObject*) new wxRealPoint(w/2.0 - prop, h/2.0));
+    points->Append((wxObject*) new wxRealPoint(-w/2.0 + prop, h/2.0));
+    points->Append((wxObject*) new wxRealPoint(-w/2.0, h/2.0 - prop));
+    points->Append((wxObject*) new wxRealPoint(-w/2.0, -h/2.0 + prop));
+
+    Create(points);
+
+    SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+// This is a transparent shape for drawing around other shapes.
+IMPLEMENT_DYNAMIC_CLASS(csGroupShape, wxRectangleShape)
+
+csGroupShape::csGroupShape()
+{
+    SetPen(wxThePenList->FindOrCreatePen("BLACK", 1, wxDOT));
+    SetBrush(wxTRANSPARENT_BRUSH);
+
+    SetSize(csSTANDARD_SHAPE_WIDTH, csSTANDARD_SHAPE_WIDTH);
+    SetCentreResize(FALSE);
+}
+
+void csGroupShape::OnDraw(wxDC& dc)
+{
+    wxRectangleShape::OnDraw(dc);
+}
+
+// Must modify the hit-test so it doesn't obscure shapes that are inside.
+bool csGroupShape::HitTest(double x, double y, int* attachment, double* distance)
+{
+    *attachment = 0;
+    *distance = 0.0;
+
+    double width = 0.0, height = 0.0;
+    GetBoundingBoxMin(&width, &height);
+
+    double x1 = GetX() - (width/2.0);
+    double y1 = GetY() - (height/2.0);
+    double x2 = GetX() + (width/2.0);
+    double y2 = GetY() + (height/2.0);
+
+    double edgeTolerance = 4.0;
+
+    // Test each edge in turn
+
+    // Top/bottom edges
+    if (x >= x1 && x <= x2)
+    {
+        if ((y >= y1 - edgeTolerance) && (y <= y1 + edgeTolerance))
+            return TRUE;
+        if ((y <= y2 + edgeTolerance) && (y >= y2 - edgeTolerance))
+            return TRUE;
+    }
+    // Left/right edges
+    if (y >= y1 && y <= y2)
+    {
+        if ((x >= x1 - edgeTolerance) && (x <= x1 + edgeTolerance))
+            return TRUE;
+        if ((x <= x2 + edgeTolerance) && (x >= x2 - edgeTolerance))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csTextBoxShape, wxRectangleShape)
+
+csTextBoxShape::csTextBoxShape()
+{
+    SetPen(wxTRANSPARENT_PEN);
+    SetBrush(wxTRANSPARENT_BRUSH);
+
+    SetSize(csSTANDARD_SHAPE_WIDTH, csSTANDARD_SHAPE_WIDTH/2.0);
+
+    SetAttachmentMode(ATTACHMENT_MODE_NONE);
+    SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
+    SetCentreResize(FALSE);
+}
+
+IMPLEMENT_DYNAMIC_CLASS(csLineShape, wxLineShape)
+
+csLineShape::csLineShape()
+{
+}
+
+bool csLineShape::OnMoveMiddleControlPoint(wxDC& dc, wxLineControlPoint* lpt, const wxRealPoint& pt)
+{
+    csDiagramView* view = ((csCanvas*)GetCanvas())->GetView();
+
+    // Temporarily set the new shape properties so we can copy it
+    lpt->SetX(pt.x); lpt->SetY(pt.y);
+    lpt->m_point->x = pt.x; lpt->m_point->y = pt.y;
+
+    wxLineShape* newShape = (wxLineShape*) this->CreateNewCopy();
+
+    // Now set them back again
+    lpt->SetX(lpt->m_originalPos.x); lpt->SetY(lpt->m_originalPos.y);
+    lpt->m_point->x = lpt->m_originalPos.x; lpt->m_point->y = lpt->m_originalPos.y;
+
+    view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Move line point", (csDiagramDocument*) view->GetDocument(),
+                new csCommandState(ID_CS_MOVE_LINE_POINT, newShape, this)));
+
+    return TRUE;
+}
+
+wxLabelShape* csLineShape::OnCreateLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h)
+{
+    return new csLabelShape(parent, region, w, h);
+}
+
+#if 0
+bool csLineShape::OnLabelMovePre(wxDC& dc, wxLabelShape* labelShape, double x, double y, double old_x, double old_y, bool display)
+{
+    csDiagramView* view = ((csCanvas*)GetCanvas())->GetView();
+
+    wxLineShape* newShape = (wxLineShape*) this->CreateNewCopy();
+
+    wxLineShape::OnLabelMovePre(dc, labelShape, x, y, old_x, old_y, display);
+
+    view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Move label", (csDiagramDocument*) view->GetDocument(),
+                new csCommandState(ID_CS_MOVE_LABEL, newShape, this)));
+  return TRUE;
+}
+#endif
+
+IMPLEMENT_DYNAMIC_CLASS(csLabelShape, wxLabelShape)
+
+csLabelShape::csLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h):
+  wxLabelShape(parent, region, w, h)
+{
+}
+
+// TODO: not sure how intercept normal behaviour (OnMovePre) to make
+// label movement undo-able.
+void csLabelShape::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+    wxLabelShape::OnEndDragLeft(x, y, keys, attachment);
+}
+
+
+// Menu for editing shapes
+void studioShapeEditProc(wxMenu& menu, wxCommandEvent& event)
+{
+    wxShape* shape = (wxShape*) menu.GetClientData();
+    csDiagramView* view = ((csCanvas*)shape->GetCanvas())->GetView();
+
+    switch (event.GetId())
+    {
+        case ID_CS_EDIT_PROPERTIES:
+        {
+            csEvtHandler* handler1 = (csEvtHandler *)shape->GetEventHandler();
+            handler1->EditProperties();
+#if 0
+            csEvtHandler* handler1 = (csEvtHandler *)shape->GetEventHandler();
+            csLabelEditingDialog* dialog = new csLabelEditingDialog(shape->GetCanvas()->GetParent());
+            dialog->SetShapeLabel(handler1->m_label);
+            if (dialog->ShowModal() == wxID_CANCEL)
+            {
+                dialog->Destroy();
+                return;
+            }
+
+            wxString newLabel = dialog->GetShapeLabel();
+            dialog->Destroy();
+
+            wxShape* newShape = shape->CreateNewCopy();
+
+            csEvtHandler* handler2 = (csEvtHandler *)newShape->GetEventHandler();
+            handler2->m_label = newLabel;
+
+            view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument*) view->GetDocument(),
+                new csCommandState(ID_CS_EDIT_LABEL, newShape, shape)));
+#endif
+            break;
+        }
+        case wxID_CUT:
+        {
+            wxList list;
+            list.Append(shape);
+            view->DoCut(list);
+            break;
+        }
+        case ID_CS_ROTATE_CLOCKWISE:
+        case ID_CS_ROTATE_ANTICLOCKWISE:
+        {
+            if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+                break;
+
+            double theta = shape->GetRotation();
+            const double myPi = 3.1415926535897932384626433832795 ;
+            double ninetyDegrees = myPi/2.0;
+
+            wxString opStr;
+            if (event.GetId() == ID_CS_ROTATE_CLOCKWISE)
+            {
+                theta += ninetyDegrees;
+                opStr = "Rotate clockwise";
+            }
+            else
+            {
+                theta -= ninetyDegrees;
+                opStr = "Rotate anticlockwise";
+            }
+
+            if (theta >= 2.0*myPi || theta < 0.0)
+                theta = 0.0;
+            wxShape* newShape = shape->CreateNewCopy();
+            newShape->Rotate(0.0, 0.0, theta);
+            wxList newShapes;
+            wxList oldShapes;
+            newShapes.Append(newShape);
+            oldShapes.Append(shape);
+            view->DoCmd(newShapes, oldShapes, event.GetId(), opStr);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+BEGIN_EVENT_TABLE(ShapeEditMenu, wxMenu)
+    EVT_COMMAND_RANGE(1, 65000, wxEVT_COMMAND_MENU_SELECTED, ShapeEditMenu::OnCommand)
+END_EVENT_TABLE()
+
+void ShapeEditMenu::OnCommand(wxCommandEvent& event)
+{
+    studioShapeEditProc(*this, event);
+}
+
diff --git a/samples/ogl/studio/shapes.h b/samples/ogl/studio/shapes.h
new file mode 100644 (file)
index 0000000..fa183b4
--- /dev/null
@@ -0,0 +1,268 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        shapes.h
+// Purpose:     Shape classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_SHAPES_H_
+#define _STUDIO_SHAPES_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/docview.h>
+#include <wx/string.h>
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/ogl.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/linesp.h>
+#include <wx/ogl/drawn.h>
+
+class csDiagramDocument;
+
+/*
+ * Override a few members for this application
+ */
+class csDiagram: public wxDiagram
+{
+DECLARE_CLASS(csDiagram)
+public:
+    csDiagram(csDiagramDocument* doc) { m_doc = doc; }
+    ~csDiagram();
+    bool OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr);
+    bool OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr);
+
+    inline csDiagramDocument* GetDocument() const { return m_doc; }
+    virtual void Redraw(wxDC& dc);
+
+protected:
+    csDiagramDocument* m_doc;
+};
+
+class wxDiagramClipboard: public wxDiagram
+{
+DECLARE_DYNAMIC_CLASS(wxDiagramClipboard)
+public:
+    wxDiagramClipboard() {}
+    ~wxDiagramClipboard() {}
+
+    // Copy selection to clipboard
+    bool Copy(wxDiagram* diagram);
+
+    // Copy contents to the diagram, with new ids.
+    // If dc is non-NULL, the pasted shapes will be selected.
+    // The offsets are used to place the shapes at a different position
+    // from the original (for example, for duplicating shapes).
+    bool Paste(wxDiagram* diagram, wxDC* dc = NULL,
+        int offsetX = 0, int offsetY = 0);
+
+#ifdef __WXMSW__
+    // Draw contents to a Windows metafile device context and bitmap, and then copy
+    // to the Windows clipboard.
+    bool CopyToClipboard(double scale);
+#endif
+
+// Overridables
+    // Start/end copying
+    virtual bool OnStartCopy(wxDiagram* diagramTo) { return TRUE; };
+    virtual bool OnEndCopy(wxDiagram* diagramTo) { return TRUE; };
+
+    // Override this to e.g. have the shape added through a Do/Undo command system.
+    // By default, we'll just add it directly to the destination diagram, and
+    // select the shape (if dc is non-NULL).
+    virtual bool OnAddShape(wxDiagram* diagramTo, wxShape* newShape, wxDC* dc);
+
+protected:
+    bool DoCopy(wxDiagram* diagramFrom, wxDiagram* diagramTo, bool newIds,
+                    wxDC* dc, int offsetX = 0, int offsetY = 0);
+
+};
+
+class csDiagramCommand;
+
+class csDiagramClipboard: public wxDiagramClipboard
+{
+DECLARE_DYNAMIC_CLASS(csDiagramClipboard)
+public:
+    csDiagramClipboard() { m_currentCmd = NULL; }
+    ~csDiagramClipboard() {}
+
+    // Start/end copying
+    bool OnStartCopy(wxDiagram* diagramTo);
+    bool OnEndCopy(wxDiagram* diagramTo);
+
+    bool OnAddShape(wxDiagram* diagramTo, wxShape* newShape, wxDC* dc);
+
+protected:
+    csDiagramCommand*   m_currentCmd;
+};
+
+
+/*
+ * The Studio shapes
+ * N.B. TODO: these should really all have another constructor
+ * for the ready-initialised shape, with the default one not having any
+ * data. Otherwise when copying a shape, you have to delete the old data
+ * first -> slightly less efficient. The initialised shapes are only required
+ * for the first creation of the shape in the palette, everything else is copied.
+ */
+
+class csThinRectangleShape: public wxDrawnShape
+{
+DECLARE_DYNAMIC_CLASS(csThinRectangleShape)
+public:
+    csThinRectangleShape();
+};
+
+class csWideRectangleShape: public wxDrawnShape
+{
+DECLARE_DYNAMIC_CLASS(csWideRectangleShape)
+public:
+    csWideRectangleShape();
+};
+
+class csTriangleShape: public wxDrawnShape
+{
+DECLARE_DYNAMIC_CLASS(csTriangleShape)
+public:
+    csTriangleShape();
+};
+
+class csSemiCircleShape: public wxDrawnShape
+{
+DECLARE_DYNAMIC_CLASS(csSemiCircleShape)
+public:
+    csSemiCircleShape();
+};
+
+class csCircleShape: public wxCircleShape
+{
+DECLARE_DYNAMIC_CLASS(csCircleShape)
+public:
+    csCircleShape();
+};
+
+class csCircleShadowShape: public wxCircleShape
+{
+DECLARE_DYNAMIC_CLASS(csCircleShadowShape)
+public:
+    csCircleShadowShape();
+};
+
+class csOctagonShape: public wxPolygonShape
+{
+DECLARE_DYNAMIC_CLASS(csOctagonShape)
+public:
+    csOctagonShape();
+
+    // The attachments are as if it's a rectangle
+    bool GetAttachmentPosition(int attachment, double *x, double *y,
+                                     int nth = 0, int no_arcs = 1, wxLineShape *line = NULL)
+    { return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line); }
+    int GetNumberOfAttachments() const
+    { return wxShape::GetNumberOfAttachments(); }
+    bool AttachmentIsValid(int attachment) const
+    { return wxShape::AttachmentIsValid(attachment); }
+};
+
+// This is a transparent shape for drawing around other shapes.
+class csGroupShape: public wxRectangleShape
+{
+DECLARE_DYNAMIC_CLASS(csGroupShape)
+public:
+    csGroupShape();
+
+    void OnDraw(wxDC& dc);
+    // Must modify the hit-test so it doesn't obscure shapes that are inside.
+    bool HitTest(double x, double y, int* attachment, double* distance);
+};
+
+class csTextBoxShape: public wxRectangleShape
+{
+DECLARE_DYNAMIC_CLASS(csTextBoxShape)
+public:
+    csTextBoxShape();
+};
+
+class csLineShape: public wxLineShape
+{
+DECLARE_DYNAMIC_CLASS(csLineShape)
+public:
+    csLineShape();
+
+    virtual bool OnMoveMiddleControlPoint(wxDC& dc, wxLineControlPoint* lpt, const wxRealPoint& pt);
+    wxLabelShape* OnCreateLabelShape(wxLineShape *parent = NULL, wxShapeRegion *region = NULL, double w = 0.0, double h = 0.0);
+};
+
+/*
+ * Temporary arc label object
+ */
+class csLabelShape: public wxLabelShape
+{
+  DECLARE_DYNAMIC_CLASS(csLabelShape)
+
+ public:
+  csLabelShape(wxLineShape *parent = NULL, wxShapeRegion *region = NULL, double w = 0.0, double h = 0.0);
+
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+};
+
+/*
+ * All shape event behaviour is routed through this handler, so we don't
+ * have to derive from each shape class. We plug this in to each shape.
+ */
+
+class csEvtHandler: public wxShapeEvtHandler
+{
+ DECLARE_DYNAMIC_CLASS(csEvtHandler)
+ public:
+  csEvtHandler(wxShapeEvtHandler *prev = NULL, wxShape *shape = NULL, const wxString& lab = "");
+  ~csEvtHandler();
+
+  void OnLeftClick(double x, double y, int keys = 0, int attachment = 0);
+  void OnRightClick(double x, double y, int keys = 0, int attachment = 0);
+  void OnBeginDragRight(double x, double y, int keys = 0, int attachment = 0);
+  void OnDragRight(bool draw, double x, double y, int keys = 0, int attachment = 0);
+  void OnEndDragRight(double x, double y, int keys = 0, int attachment = 0);
+  void OnEndSize(double x, double y);
+  void OnDragLeft(bool draw, double x, double y, int keys = 0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys = 0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys = 0, int attachment = 0);
+  void OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys = 0, int attachment = 0);
+  void OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering);
+
+  void OnLeftDoubleClick(double x, double y, int keys = 0, int attachment = 0);
+
+  // Copy any event handler data
+  virtual void CopyData(wxShapeEvtHandler& copy);
+
+  // Popup up a property dialog
+  virtual bool EditProperties();
+
+public:
+  wxString m_label;
+};
+
+class ShapeEditMenu: public wxMenu
+{
+public:
+    ShapeEditMenu() {}
+
+    void OnCommand(wxCommandEvent& event);
+
+DECLARE_EVENT_TABLE()
+};
+
+extern void studioShapeEditProc(wxMenu& menu, wxCommandEvent& event);
+
+#endif
+  // _STUDIO_SHAPES_H_
diff --git a/samples/ogl/studio/studio.cpp b/samples/ogl/studio/studio.cpp
new file mode 100644 (file)
index 0000000..d8ca7a8
--- /dev/null
@@ -0,0 +1,506 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        Studio.cpp
+// Purpose:     Studio application class
+// Author:      Julian Smart
+// Modified by:
+// Created:     27/7/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#include "wx/mdi.h"
+#endif
+
+#include "wx/resource.h"
+#include "wx/config.h"
+#include "wx/laywin.h"
+
+#include "studio.h"
+#include "view.h"
+#include "doc.h"
+#include "mainfrm.h"
+#include "cspalette.h"
+#include "project.h"
+#include "symbols.h"
+
+#if defined(__WXGTK__) || defined(__WXMOTIF__)
+#include "bitmaps/new.xpm"
+#include "bitmaps/open.xpm"
+#include "bitmaps/save.xpm"
+#include "bitmaps/copy.xpm"
+#include "bitmaps/cut.xpm"
+#include "bitmaps/paste.xpm"
+#include "bitmaps/print.xpm"
+#include "bitmaps/help.xpm"
+#include "bitmaps/undo.xpm"
+#include "bitmaps/redo.xpm"
+
+#include "bitmaps/alignl.xpm"
+#include "bitmaps/alignr.xpm"
+#include "bitmaps/alignt.xpm"
+#include "bitmaps/alignb.xpm"
+#include "bitmaps/horiz.xpm"
+#include "bitmaps/vert.xpm"
+#include "bitmaps/copysize.xpm"
+#include "bitmaps/linearrow.xpm"
+#include "bitmaps/newpoint.xpm"
+#include "bitmaps/cutpoint.xpm"
+#include "bitmaps/straight.xpm"
+
+#include "studio.xpm"
+#endif
+
+IMPLEMENT_APP(csApp)
+
+csApp::csApp()
+{
+    m_docManager = NULL;
+    m_diagramPalette = NULL;
+    m_diagramToolBar = NULL;
+    m_projectTreeCtrl = NULL;
+    m_diagramPaletteSashWindow = NULL;
+    m_projectSashWindow = NULL;
+    m_symbolDatabase = NULL;
+    m_pointSizeComboBox = NULL;
+    m_zoomComboBox = NULL;
+    m_shapeEditMenu = NULL;
+
+    // Configuration
+    m_mainFramePos.x = 20;
+    m_mainFramePos.y = 20;
+    m_mainFrameSize.x = 500;
+    m_mainFrameSize.y = 400;
+    m_gridStyle = csGRID_STYLE_INVISIBLE;
+    m_gridSpacing = 5;
+}
+
+csApp::~csApp()
+{
+}
+
+// Initialise this in OnInit, not statically
+bool csApp::OnInit(void)
+{
+  if (!wxResourceParseFile("studio_resources.wxr"))
+  {
+    wxMessageBox("Could not find or parse resource file: studio_resources.wxr", "Studio");
+    return FALSE;
+  }
+
+  m_helpController.Initialize("studio.hlp");
+
+  ReadOptions();
+
+  wxOGLInitialize();
+
+  InitSymbols();
+
+  //// Create a document manager
+  m_docManager = new wxDocManager;
+
+  //// Create a template relating drawing documents to their views
+  (void) new wxDocTemplate(m_docManager, "Diagram", "*.dia", "", "dia", "Diagram Doc", "Diagram View",
+          CLASSINFO(csDiagramDocument), CLASSINFO(csDiagramView));
+
+  // Create the main frame window
+
+  csFrame* frame = new csFrame(m_docManager, NULL, -1, "OGL Studio", m_mainFramePos, m_mainFrameSize,
+   wxDEFAULT_FRAME_STYLE | wxHSCROLL | wxVSCROLL);
+
+  // Give it an icon
+  frame->SetIcon(wxICON(studio));
+
+  // Make a menubar
+  wxMenu *fileMenu = new wxMenu;
+
+  fileMenu->Append(wxID_NEW, "&New...\tCtrl+N");
+  fileMenu->Append(wxID_OPEN, "&Open...\tCtrl+O");
+
+  fileMenu->AppendSeparator();
+
+  fileMenu->Append(wxID_PRINT, "&Print...\tCtrl+P");
+  fileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
+  fileMenu->Append(wxID_PREVIEW, "Print Pre&view");
+  fileMenu->AppendSeparator();
+  fileMenu->Append(wxID_EXIT, "E&xit");
+
+  // A history of files visited. Use this menu.
+  m_docManager->FileHistoryUseMenu(fileMenu);
+
+  wxMenu *viewMenu = new wxMenu;
+  viewMenu->Append(ID_CS_SETTINGS, "&Settings...");
+
+  wxMenu *helpMenu = new wxMenu;
+  helpMenu->Append(wxID_HELP, "&Help Contents\tF1");
+  helpMenu->Append(ID_CS_ABOUT, "&About");
+
+  wxMenuBar *menuBar = new wxMenuBar;
+
+  menuBar->Append(fileMenu, "&File");
+  menuBar->Append(viewMenu, "&View");
+  menuBar->Append(helpMenu, "&Help");
+
+  // Associate the menu bar with the frame
+  frame->SetMenuBar(menuBar);
+
+  // Load the file history
+  wxConfig config("OGL Studio", "wxWindows");
+  m_docManager->FileHistoryLoad(config);
+
+  frame->CreateStatusBar();
+
+  // The ordering of these is important for layout purposes
+  CreateDiagramToolBar(frame);
+  CreatePalette(frame);
+  CreateProjectWindow(frame);
+
+  FillProjectTreeCtrl();
+
+  // Create the shape editing menu
+  m_shapeEditMenu = new ShapeEditMenu;
+  m_shapeEditMenu->Append(ID_CS_EDIT_PROPERTIES, "Edit properties");
+  m_shapeEditMenu->AppendSeparator();
+  m_shapeEditMenu->Append(ID_CS_ROTATE_CLOCKWISE, "Rotate clockwise");
+  m_shapeEditMenu->Append(ID_CS_ROTATE_ANTICLOCKWISE, "Rotate anticlockwise");
+  m_shapeEditMenu->AppendSeparator();
+  m_shapeEditMenu->Append(ID_CS_CUT, "Cut");
+
+  frame->Show(TRUE);
+
+  SetTopWindow(frame);
+
+  return TRUE;
+}
+
+int csApp::OnExit(void)
+{
+    WriteOptions();
+
+    delete m_symbolDatabase;
+    m_symbolDatabase = NULL;
+
+    delete m_docManager;
+    m_docManager = NULL;
+
+    delete m_shapeEditMenu;
+    m_shapeEditMenu = NULL;
+
+    wxOGLCleanUp();
+
+    return 0;
+}
+
+/*
+ * Centralised code for creating a document frame.
+ * Called from view.cpp, when a view is created.
+ */
+wxMDIChildFrame *csApp::CreateChildFrame(wxDocument *doc, wxView *view, wxMenu** editMenuRet)
+{
+  //// Make a child frame
+  csMDIChildFrame *subframe = new csMDIChildFrame(doc, view, ((wxDocMDIParentFrame*)GetTopWindow()), -1, "Child Frame",
+        wxPoint(10, 10), wxSize(300, 300), wxDEFAULT_FRAME_STYLE);
+
+#ifdef __WXMSW__
+  subframe->SetIcon(wxString("chart"));
+#endif
+#ifdef __X__
+  subframe->SetIcon(wxIcon("doc.xbm"));
+#endif
+
+  //// Make a menubar
+  wxMenu *fileMenu = new wxMenu;
+
+  fileMenu->Append(wxID_NEW, "&New...\tCtrl+N");
+  fileMenu->Append(wxID_OPEN, "&Open...\tCtrl+O");
+  fileMenu->Append(wxID_CLOSE, "&Close\tCtrl+W");
+  fileMenu->Append(wxID_SAVE, "&Save\tCtrl+S");
+  fileMenu->Append(wxID_SAVEAS, "Save &As...\tF12");
+
+  fileMenu->AppendSeparator();
+  fileMenu->Append(wxID_PRINT, "&Print...\tCtrl+P");
+  fileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
+  fileMenu->Append(wxID_PREVIEW, "Print Pre&view");
+
+  fileMenu->AppendSeparator();
+  fileMenu->Append(wxID_EXIT, "E&xit");
+
+  wxMenu *editMenu = NULL;
+
+  editMenu = new wxMenu;
+  editMenu->Append(wxID_UNDO, "&Undo\tCtrl+Z");
+  editMenu->Append(wxID_REDO, "&Redo\tCtrl+Y");
+  editMenu->AppendSeparator();
+  editMenu->Append(wxID_CUT, "Cu&t\tCtrl+X");
+  editMenu->Append(wxID_COPY, "&Copy\tCtrl+C");
+  editMenu->Append(wxID_PASTE, "&Paste\tCtrl+V");
+  editMenu->Append(wxID_DUPLICATE, "&Duplicate\tCtrl+D");
+  editMenu->AppendSeparator();
+  editMenu->Append(wxID_CLEAR, "Cle&ar\tDelete");
+  editMenu->Append(ID_CS_SELECT_ALL, "Select A&ll\tCtrl+A");
+  editMenu->AppendSeparator();
+  editMenu->Append(ID_CS_EDIT_PROPERTIES, "Edit P&roperties...");
+
+  *editMenuRet = editMenu;
+
+  m_docManager->FileHistoryUseMenu(fileMenu);
+  m_docManager->FileHistoryAddFilesToMenu(fileMenu);
+
+  doc->GetCommandProcessor()->SetEditMenu(editMenu);
+
+  wxMenu *viewMenu = new wxMenu;
+  viewMenu->Append(ID_CS_SETTINGS, "&Settings...");
+
+  wxMenu *helpMenu = new wxMenu;
+  helpMenu->Append(wxID_HELP, "&Help Contents\tF1");
+  helpMenu->Append(ID_CS_ABOUT, "&About");
+
+  wxMenuBar *menuBar = new wxMenuBar;
+
+  menuBar->Append(fileMenu, "&File");
+  menuBar->Append(editMenu, "&Edit");
+  menuBar->Append(viewMenu, "&View");
+  menuBar->Append(helpMenu, "&Help");
+
+  //// Associate the menu bar with the frame
+  subframe->SetMenuBar(menuBar);
+
+  return subframe;
+}
+
+// Creates a canvas. Called by OnInit as a child of the main window
+csCanvas *csApp::CreateCanvas(wxView *view, wxFrame *parent)
+{
+  int width, height;
+  parent->GetClientSize(&width, &height);
+
+  // Non-retained canvas
+  csCanvas *canvas = new csCanvas((csDiagramView*) view, parent, 1000, wxPoint(0, 0), wxSize(width, height), wxSUNKEN_BORDER);
+
+  wxColour bgColour("WHITE");
+  canvas->SetBackgroundColour(bgColour);
+
+  wxCursor cursor(wxCURSOR_HAND);
+  canvas->SetCursor(cursor);
+
+  // Give it scrollbars
+  canvas->SetScrollbars(20, 20, 100, 100);
+
+  return canvas;
+}
+
+void csApp::InitToolBar(wxToolBar* toolBar)
+{
+    wxBitmap* bitmaps[10];
+
+#ifdef __WXMSW__
+    bitmaps[0] = new wxBitmap("new", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[1] = new wxBitmap("open", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[2] = new wxBitmap("save", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[3] = new wxBitmap("copy", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[4] = new wxBitmap("cut", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[5] = new wxBitmap("paste", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[6] = new wxBitmap("print", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[7] = new wxBitmap("help", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[8] = new wxBitmap("undo", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[9] = new wxBitmap("redo", wxBITMAP_TYPE_RESOURCE);
+#elif defined(__WXGTK__) || defined(__WXMOTIF__)
+    bitmaps[0] = new wxBitmap( new_xpm );
+    bitmaps[1] = new wxBitmap( open_xpm );
+    bitmaps[2] = new wxBitmap( save_xpm );
+    bitmaps[3] = new wxBitmap( copy_xpm );
+    bitmaps[4] = new wxBitmap( cut_xpm );
+    bitmaps[5] = new wxBitmap( paste_xpm );
+    bitmaps[6] = new wxBitmap( print_xpm );
+    bitmaps[7] = new wxBitmap( help_xpm );
+    bitmaps[8] = new wxBitmap( undo_xpm );
+    bitmaps[9] = new wxBitmap( redo_xpm );
+#else
+#error "Not implemented for this platform."
+#endif
+
+  toolBar->AddTool(wxID_NEW, *bitmaps[0], wxNullBitmap, FALSE, -1, -1, NULL, "New file");
+  toolBar->AddTool(wxID_OPEN, *bitmaps[1], wxNullBitmap, FALSE, -1, -1, NULL, "Open file");
+  toolBar->AddTool(wxID_SAVE, *bitmaps[2], wxNullBitmap, FALSE, -1, -1, NULL, "Save file");
+  toolBar->AddSeparator();
+  toolBar->AddTool(wxID_PRINT, *bitmaps[6], wxNullBitmap, FALSE, -1, -1, NULL, "Print");
+  toolBar->AddSeparator();
+  toolBar->AddTool(wxID_COPY, *bitmaps[3], wxNullBitmap, FALSE, -1, -1, NULL, "Copy");
+  toolBar->AddTool(wxID_CUT, *bitmaps[4], wxNullBitmap, FALSE, -1, -1, NULL, "Cut");
+  toolBar->AddTool(wxID_PASTE, *bitmaps[5], wxNullBitmap, FALSE, -1, -1, NULL, "Paste");
+  toolBar->AddSeparator();
+  toolBar->AddTool(wxID_UNDO, *bitmaps[8], wxNullBitmap, FALSE, -1, -1, NULL, "Undo");
+  toolBar->AddTool(wxID_REDO, *bitmaps[9], wxNullBitmap, FALSE, -1, -1, NULL, "Redo");
+  toolBar->AddSeparator();
+  toolBar->AddTool(wxID_HELP, *bitmaps[7], wxNullBitmap, FALSE, -1, -1, NULL, "Help");
+
+  toolBar->Realize();
+
+  toolBar->EnableTool(wxID_COPY, FALSE);
+  toolBar->EnableTool(wxID_PASTE, FALSE);
+  toolBar->EnableTool(wxID_PRINT, FALSE);
+  toolBar->EnableTool(wxID_UNDO, FALSE);
+  toolBar->EnableTool(wxID_REDO, FALSE);
+
+  int i;
+  for (i = 0; i < 10; i++)
+    delete bitmaps[i];
+}
+
+// Create and initialise the diagram toolbar
+void csApp::CreateDiagramToolBar(wxFrame* parent)
+{
+    // First create a layout window
+    wxSashLayoutWindow* win = new wxSashLayoutWindow(parent, ID_LAYOUT_WINDOW_DIAGRAM_TOOLBAR, wxDefaultPosition, wxSize(200, 30), wxNO_BORDER|wxSW_3D|wxCLIP_CHILDREN);
+    win->SetDefaultSize(wxSize(10000, 30));
+    win->SetOrientation(wxLAYOUT_HORIZONTAL);
+    win->SetAlignment(wxLAYOUT_TOP);
+    win->SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
+
+    m_diagramToolBarSashWindow = win;
+    m_diagramToolBarSashWindow->Show(FALSE);
+
+    // Create the actual toolbar
+    m_diagramToolBar = new wxToolBar(win, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL|wxNO_BORDER|wxTB_FLAT);
+
+    wxBitmap* bitmaps[11];
+
+#ifdef __WXMSW__
+    bitmaps[0] = new wxBitmap("alignl", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[1] = new wxBitmap("alignr", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[2] = new wxBitmap("alignt", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[3] = new wxBitmap("alignb", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[4] = new wxBitmap("horiz", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[5] = new wxBitmap("vert", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[6] = new wxBitmap("copysize", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[7] = new wxBitmap("linearrow", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[8] = new wxBitmap("newpoint", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[9] = new wxBitmap("cutpoint", wxBITMAP_TYPE_RESOURCE);
+    bitmaps[10] = new wxBitmap("straighten", wxBITMAP_TYPE_RESOURCE);
+#elif defined(__WXGTK__) || defined(__WXMOTIF__)
+    bitmaps[0] = new wxBitmap( alignl_xpm );
+    bitmaps[1] = new wxBitmap( alignr_xpm );
+    bitmaps[2] = new wxBitmap( alignt_xpm );
+    bitmaps[3] = new wxBitmap( alignb_xpm );
+    bitmaps[4] = new wxBitmap( horiz_xpm );
+    bitmaps[5] = new wxBitmap( vert_xpm );
+    bitmaps[6] = new wxBitmap( copysize_xpm );
+    bitmaps[7] = new wxBitmap( linearrow_xpm );
+    bitmaps[8] = new wxBitmap( newpoint_xpm );
+    bitmaps[9] = new wxBitmap( cutpoint_xpm );
+    bitmaps[10] = new wxBitmap( straight_xpm );
+#else
+#error "Not implemented for this platform."
+#endif
+
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_ALIGNL, *bitmaps[0], wxNullBitmap, FALSE, -1, -1, NULL, "Align left");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_ALIGNR, *bitmaps[1], wxNullBitmap, FALSE, -1, -1, NULL, "Align right");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_ALIGNT, *bitmaps[2], wxNullBitmap, FALSE, -1, -1, NULL, "Align top");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_ALIGNB, *bitmaps[3], wxNullBitmap, FALSE, -1, -1, NULL, "Align bottom");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_ALIGN_HORIZ, *bitmaps[4], wxNullBitmap, FALSE, -1, -1, NULL, "Align horizontally");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_ALIGN_VERT, *bitmaps[5], wxNullBitmap, FALSE, -1, -1, NULL, "Align vertically");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_COPY_SIZE, *bitmaps[6], wxNullBitmap, FALSE, -1, -1, NULL, "Copy size");
+    m_diagramToolBar->AddSeparator();
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_LINE_ARROW, *bitmaps[7], wxNullBitmap, TRUE, -1, -1, NULL, "Toggle arrow");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_NEW_POINT, *bitmaps[8], wxNullBitmap, FALSE, -1, -1, NULL, "New line point");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_CUT_POINT, *bitmaps[9], wxNullBitmap, FALSE, -1, -1, NULL, "Cut line point");
+    m_diagramToolBar->AddTool(DIAGRAM_TOOLBAR_STRAIGHTEN, *bitmaps[10], wxNullBitmap, FALSE, -1, -1, NULL, "Straighten lines");
+
+    m_diagramToolBar->Realize();
+
+    int i;
+    for (i = 0; i < 11; i++)
+        delete bitmaps[i];
+
+    // Create a combobox for point size
+    int maxPointSize = 40;
+    wxString *pointSizes = new wxString[maxPointSize];
+    for (i = 1; i <= maxPointSize; i++)
+    {
+        pointSizes[i-1].Printf("%d", i);
+    }
+
+    int controlX = 260;
+    int pointSizeW = 40;
+    int pointSizeH = 18;
+    int zoomW = 60;
+    int zoomH = 18;
+#ifdef __WXMOTIF__
+    controlX += 70;
+    pointSizeW = 60;
+    pointSizeH = 22;
+    zoomW = 60;
+    zoomH = 22;
+#endif
+
+    m_pointSizeComboBox = new wxComboBox(m_diagramToolBar, ID_WINDOW_POINT_SIZE_COMBOBOX,
+        "", wxPoint(controlX, 1), wxSize(pointSizeW, pointSizeH), maxPointSize, pointSizes);
+    delete[] pointSizes;
+
+    m_pointSizeComboBox->SetSelection(10 - 1);
+
+    // Create a combobox for zooming
+    int maxZoom = 200;
+    int minZoom = 5;
+    int increment = 5;
+    int noStrings = (maxZoom - minZoom)/5 ;
+    wxString *zoomStrings = new wxString[noStrings];
+    for (i = 0; i < noStrings; i ++)
+    {
+        zoomStrings[noStrings - i - 1].Printf("%d%%", (i*increment + minZoom));
+    }
+
+    controlX += pointSizeW + 10;
+
+    m_zoomComboBox = new wxComboBox(m_diagramToolBar, ID_WINDOW_ZOOM_COMBOBOX,
+        "", wxPoint(controlX, 1), wxSize(zoomW, zoomH), noStrings, zoomStrings);
+    delete[] zoomStrings;
+
+    // i = (zoom - minZoom)/increment
+    // index = noStrings - i - 1
+    // 100%
+    i = (100 - minZoom)/increment;
+    m_zoomComboBox->SetSelection(noStrings - i - 1);
+}
+
+// Read/write configuration information
+bool csApp::ReadOptions()
+{
+    wxConfig config("OGL Studio", "wxWindows");
+
+    config.Read("mainX", & m_mainFramePos.x);
+    config.Read("mainY", & m_mainFramePos.y);
+    config.Read("mainWidth", & m_mainFrameSize.x);
+    config.Read("mainHeight", & m_mainFrameSize.y);
+    config.Read("gridStyle", & m_gridStyle);
+    config.Read("gridSpacing", & m_gridSpacing);
+
+    return TRUE;
+}
+
+bool csApp::WriteOptions()
+{
+    wxConfig config("OGL Studio", "wxWindows");
+
+    config.Write("mainX", (long) m_mainFramePos.x);
+    config.Write("mainY", (long) m_mainFramePos.y);
+    config.Write("mainWidth", (long) m_mainFrameSize.x);
+    config.Write("mainHeight", (long) m_mainFrameSize.y);
+    config.Write("gridStyle", (long) m_gridStyle);
+    config.Write("gridSpacing", (long) m_gridSpacing);
+
+    m_docManager->FileHistorySave(config);
+
+    return TRUE;
+}
+
diff --git a/samples/ogl/studio/studio.h b/samples/ogl/studio/studio.h
new file mode 100644 (file)
index 0000000..0104a16
--- /dev/null
@@ -0,0 +1,171 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        Studio.h
+// Purpose:     Studio application class
+// Author:      Julian Smart
+// Modified by:
+// Created:     27/7/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_STUDIO_H_
+#define _STUDIO_STUDIO_H_
+
+#include <wx/docmdi.h>
+#include <wx/help.h>
+
+#include <wx/ogl/ogl.h>
+#include <wx/ogl/canvas.h>
+#include "shapes.h"
+
+class csEditorToolPalette;
+class csProjectTreeCtrl;
+class csCanvas;
+class csSymbolDatabase;
+class wxSashLayoutWindow;
+class csFrame;
+
+// Grid style
+#define csGRID_STYLE_NONE       0
+#define csGRID_STYLE_INVISIBLE  1
+#define csGRID_STYLE_DOTTED     2
+
+// Define a new application
+class csApp: public wxApp
+{
+    friend csFrame;
+public:
+    csApp();
+    ~csApp();
+
+// Operations
+    bool OnInit(void);
+    int OnExit(void);
+
+    // Read/write configuration information
+    bool ReadOptions();
+    bool WriteOptions();
+
+    // Create the diagram tool palette
+    bool CreatePalette(wxFrame *parent);
+
+    // Create the project window
+    bool CreateProjectWindow(wxFrame *parent);
+
+    // Initialise the general toolbar
+    void InitToolBar(wxToolBar* toolBar);
+
+    // Create and initialise the diagram toolbar
+    void CreateDiagramToolBar(wxFrame* parent);
+
+    wxMDIChildFrame *CreateChildFrame(wxDocument *doc, wxView *view, wxMenu** editMenu);
+    csCanvas *CreateCanvas(wxView *view, wxFrame *parent);
+
+    // Fill out the project tree control
+    void FillProjectTreeCtrl();
+
+    // Add symbols to database
+    void InitSymbols();
+
+// Accessors
+    csEditorToolPalette* GetDiagramPalette() const { return m_diagramPalette; }
+    wxToolBar* GetDiagramToolBar() const { return m_diagramToolBar; }
+    csProjectTreeCtrl* GetProjectTreeCtrl() const { return m_projectTreeCtrl; }
+    wxSashLayoutWindow* GetDiagramPaletteSashWindow() const { return m_diagramPaletteSashWindow; }
+    wxSashLayoutWindow* GetProjectSashWindow() const { return m_projectSashWindow; }
+    wxSashLayoutWindow* GetDiagramToolBarSashWindow() const { return m_diagramToolBarSashWindow; }
+    csSymbolDatabase* GetSymbolDatabase() const { return m_symbolDatabase; }
+    wxComboBox* GetPointSizeComboBox() const { return m_pointSizeComboBox; }
+    wxComboBox* GetZoomComboBox() const { return m_zoomComboBox; }
+    wxMenu* GetShapeEditMenu() const { return m_shapeEditMenu; }
+    wxDiagramClipboard& GetDiagramClipboard() const { return (wxDiagramClipboard&) m_diagramClipboard; }
+    wxDocManager* GetDocManager() const { return m_docManager; }
+    wxHelpController& GetHelpController() const { return (wxHelpController&) m_helpController; }
+
+    int GetGridStyle() const { return m_gridStyle; }
+    void SetGridStyle(int style) { m_gridStyle = style; }
+
+    int GetGridSpacing() const { return m_gridSpacing; }
+    void SetGridSpacing(int spacing) { m_gridSpacing = spacing; }
+
+protected:
+    wxDocManager*           m_docManager;
+    wxSashLayoutWindow*     m_diagramPaletteSashWindow;
+    wxSashLayoutWindow*     m_diagramToolBarSashWindow;
+    wxSashLayoutWindow*     m_projectSashWindow;
+    csEditorToolPalette*    m_diagramPalette;
+    csProjectTreeCtrl*      m_projectTreeCtrl;
+    csSymbolDatabase*       m_symbolDatabase;
+    wxToolBar*              m_diagramToolBar;
+    wxComboBox*             m_pointSizeComboBox;
+    wxComboBox*             m_zoomComboBox;
+    wxMenu*                 m_shapeEditMenu;
+
+    // Configuration
+    wxPoint                 m_mainFramePos;
+    wxSize                  m_mainFrameSize;
+    int                     m_gridStyle;
+    int                     m_gridSpacing;
+
+    // Diagram clipboard
+    csDiagramClipboard      m_diagramClipboard;
+
+    // Help instance
+    wxHelpController        m_helpController;
+};
+
+DECLARE_APP(csApp)
+
+#define ID_CS_CUT                         wxID_CUT
+#define ID_CS_ADD_SHAPE                   2
+#define ID_CS_ADD_LINE                    3
+// #define ID_CS_EDIT_LABEL                  4
+#define ID_CS_EDIT_PROPERTIES             4
+#define ID_CS_CHANGE_BACKGROUND_COLOUR    5
+#define ID_CS_MOVE                        6
+#define ID_CS_SIZE                        7
+#define ID_CS_FONT_CHANGE                 8
+#define ID_CS_ARROW_CHANGE                9
+#define ID_CS_ROTATE_CLOCKWISE            11
+#define ID_CS_ROTATE_ANTICLOCKWISE        12
+#define ID_CS_CHANGE_LINE_ORDERING        13  // Change the list of lines for a wxShape
+#define ID_CS_CHANGE_LINE_ATTACHMENT      14  // Change the attachment point for one end of a line
+#define ID_CS_ALIGN                       15
+#define ID_CS_NEW_POINT                   16
+#define ID_CS_CUT_POINT                   17
+#define ID_CS_STRAIGHTEN                  18
+#define ID_CS_MOVE_LINE_POINT             19
+#define ID_CS_MOVE_LABEL                  20
+#define ID_CS_ADD_SHAPE_SELECT            21
+#define ID_CS_ADD_LINE_SELECT             22
+
+#define ID_CS_ABOUT                       100
+#define ID_CS_SELECT_ALL                  102
+#define ID_CS_SETTINGS                    103
+
+#define ID_LAYOUT_WINDOW_PALETTE          200
+#define ID_LAYOUT_WINDOW_DIAGRAM_TOOLBAR  201
+#define ID_LAYOUT_WINDOW_PROJECT          202
+
+#define ID_DIAGRAM_PALETTE                250
+
+#define ID_WINDOW_PROJECT_TREE            300
+#define ID_WINDOW_POINT_SIZE_COMBOBOX     301
+#define ID_WINDOW_ZOOM_COMBOBOX           302
+
+#define DIAGRAM_TOOLBAR_ALIGNL            500
+#define DIAGRAM_TOOLBAR_ALIGNR            501
+#define DIAGRAM_TOOLBAR_ALIGNB            502
+#define DIAGRAM_TOOLBAR_ALIGNT            503
+#define DIAGRAM_TOOLBAR_ALIGN_HORIZ       504
+#define DIAGRAM_TOOLBAR_ALIGN_VERT        505
+#define DIAGRAM_TOOLBAR_COPY_SIZE         506
+#define DIAGRAM_TOOLBAR_LINE_ARROW        507
+#define DIAGRAM_TOOLBAR_NEW_POINT         508
+#define DIAGRAM_TOOLBAR_CUT_POINT         509
+#define DIAGRAM_TOOLBAR_STRAIGHTEN        510
+
+#endif
+  // _STUDIO_STUDIO_H_
+
diff --git a/samples/ogl/studio/studio.ico b/samples/ogl/studio/studio.ico
new file mode 100644 (file)
index 0000000..7cb092e
Binary files /dev/null and b/samples/ogl/studio/studio.ico differ
diff --git a/samples/ogl/studio/studio.rc b/samples/ogl/studio/studio.rc
new file mode 100644 (file)
index 0000000..70a7a9c
--- /dev/null
@@ -0,0 +1,41 @@
+aaaa                    ICON    "studio.ico"
+
+/* Useful if PROVIDE_DEFAULT_ICONS is set in wx_setup.h */
+#define IHaveMDIParentIcon
+#define IHaveMDIChildIcon
+
+wxSTD_MDIPARENTFRAME        ICON    "studio.ico"
+wxSTD_MDICHILDFRAME         ICON    "studio.ico"
+
+studio                  ICON    "studio.ico"
+folder1                     ICON    "bitmaps/folder1.ico"
+file1                       ICON    "bitmaps/file1.ico"
+
+new                         BITMAP  "bitmaps/new.bmp"
+open                        BITMAP  "bitmaps/open.bmp"
+save                        BITMAP  "bitmaps/save.bmp"
+copy                        BITMAP  "bitmaps/copy.bmp"
+cut                         BITMAP  "bitmaps/cut.bmp"
+paste                       BITMAP  "bitmaps/paste.bmp"
+print                       BITMAP  "bitmaps/print.bmp"
+help                        BITMAP  "bitmaps/help.bmp"
+undo                        BITMAP  "bitmaps/undo.bmp"
+redo                        BITMAP  "bitmaps/redo.bmp"
+
+arrowtool                   BITMAP  "bitmaps/arrow.bmp"
+texttool                    BITMAP  "bitmaps/texttool.bmp"
+
+alignl                      BITMAP  "bitmaps/alignl.bmp"
+alignb                      BITMAP  "bitmaps/alignb.bmp"
+alignr                      BITMAP  "bitmaps/alignr.bmp"
+alignt                      BITMAP  "bitmaps/alignt.bmp"
+copysize                    BITMAP  "bitmaps/copysize.bmp"
+vert                        BITMAP  "bitmaps/vert.bmp"
+horiz                       BITMAP  "bitmaps/horiz.bmp"
+linearrow                   BITMAP  "bitmaps/linearrow.bmp"
+newpoint                    BITMAP  "bitmaps/newpoint.bmp"
+cutpoint                    BITMAP  "bitmaps/cutpoint.bmp"
+straighten                  BITMAP  "bitmaps/straight.bmp"
+
+#include "wx/msw/wx.rc"
+
diff --git a/samples/ogl/studio/studio.xpm b/samples/ogl/studio/studio.xpm
new file mode 100644 (file)
index 0000000..cd5b1cb
--- /dev/null
@@ -0,0 +1,44 @@
+/* XPM */
+static char *studio_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 6 1",
+"  c Black",
+". c Blue",
+"X c #00bf00",
+"o c Red",
+"O c Yellow",
+"+ c Gray100",
+/* pixels */
+"                                ",
+" oooooo +++++++++++++++++++++++ ",
+" oooooo +++++++++++++++++++++++ ",
+" oooooo +++++++++++++++++++++++ ",
+" oooooo +++++++++++++++++++++++ ",
+" oooooo +++++++++++++++++++++++ ",
+" oooooo +++++++++++++++++++++++ ",
+" oooooo +++++++++++++++++++++++ ",
+"                                ",
+" ++++++ ++++++++++++++++++ .... ",
+" ++++++ ++++++++++++++++++ .... ",
+" ++++++ ++++++++++++++++++ .... ",
+" ++++++ ++++++++++++++++++ .... ",
+" ++++++ ++++++++++++++++++ .... ",
+" ++++++ ++++++++++++++++++      ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++ ++++++++++++++++++ ++++ ",
+" ++++++                    ++++ ",
+" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
+" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
+" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
+" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
+" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
+" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
+"                                "
+};
diff --git a/samples/ogl/studio/studio_resources.h b/samples/ogl/studio/studio_resources.h
new file mode 100644 (file)
index 0000000..92a49c0
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * studio_resources.h
+ * Window identifiers file written by Dialog Editor
+ */
+
+#define ID_GRID_STYLE 6004
+#define ID_TEXTCTRL5115 5115
+#define ID_TEXTCTRL5107 5107
+#define ID_LABEL_DIALOG 6008
+#define ID_GRID_SPACING 6007
+#define IDD_LABEL_ENTRY 100
+#define ID_STATIC 300
+#define ID_DIALOG100 100
+#define ID_DIALOG6001 6001
+#define ID_STATIC6005 6005
+#define ID_STATIC6006 6006
+#define ID_STATIC5116 5116
+#define ID_STATIC6009 6009
+#define ID_LABELTEXT 101
+#define ID_CONTROL101 106
+#define ID_CONTROL102 107
+#define ID_CONTROL111 111
+#define ID_CONTROL120 120
+#define ID_CONTROL103 108
+#define ID_CONTROL121 5105
+#define ID_CONTROL130 5114
+#define ID_CONTROL104 109
+#define ID_CONTROL122 5106
+#define ID_CONTROL131 121
+#define ID_CONTROL105 110
+#define ID_CONTROL114 115
+#define ID_CONTROL123 5107
+#define ID_CONTROL132 122
+#define ID_CONTROL124 5108
+#define ID_CONTROL116 116
+#define ID_CONTROL125 5109
+#define ID_CONTROL117 117
+#define ID_CONTROL126 5110
+#define ID_CONTROL118 118
+#define ID_CONTROL127 5111
+#define ID_CONTROL119 119
+#define ID_CONTROL128 5112
+#define ID_CONTROL129 5113
+#define ID_GENERAL_SETTINGS_DIALOG 2000
diff --git a/samples/ogl/studio/studio_resources.wxr b/samples/ogl/studio/studio_resources.wxr
new file mode 100644 (file)
index 0000000..51c9521
--- /dev/null
@@ -0,0 +1,191 @@
+static char *semi_circle = "dialog(name = 'semi_circle',\
+  style = 'wxNO_BORDER',\
+  title = 'SemiCircle',\
+  id = 100,\
+  x = 10, y = 40, width = 365, height = 405,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [106, wxStaticText, 'Segment Id', '0', 'statictext7', 14, 163, 64, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [107, wxTextCtrl, '', '0', 'textctrl8', 108, 162, 120, 19, ''],\
+  control = [108, wxStaticText, 'Num Channels', '0', 'statictext9', 14, 208, 79, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [109, wxTextCtrl, '1', '0', 'textctrl10', 108, 206, 30, 19, '1'],\
+  control = [110, wxStaticText, 'Attributes', '0', 'statictext11', 14, 245, 54, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [111, wxTextCtrl, '', '0', 'textctrl12', 108, 245, 120, 19, ''],\
+  control = [5106, wxRadioBox, 'Scope', 'wxRA_SPECIFY_COLS', 'radiobox1', 12, 13, 236, 44, ['Process', 'Application', 'Project'], 3],\
+  control = [5108, wxRadioBox, 'Resource', 'wxRA_SPECIFY_ROWS', 'radiobox3', 174, 68, 72, 82, ['Heap', 'Segment', 'Any'], 3],\
+  control = [5109, wxRadioBox, 'Volatility', 'wxRA_SPECIFY_ROWS', 'radiobox4', 12, 69, 81, 63, ['Permanent', 'Temporary'], 2],\
+  control = [5111, wxStaticText, 'Num Events', '0', 'statictext1', 154, 208, 68, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5112, wxTextCtrl, '1', '0', 'textctrl2', 230, 205, 40, 19, '1']).";
+
+static char *general_shape_properties_dialog = "panel(name = 'general_shape_properties_dialog',\
+  style = 'wxNO_BORDER',\
+  title = 'General',\
+  id = 100,\
+  x = 10, y = 40, width = 400, height = 400,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [101, wxTextCtrl, '', '0', 'textctrl3', 8, 31, 203, 24, ''],\
+  control = [300, wxStaticText, 'Name:', '0', 'message4', 8, 11, 41, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5115, wxTextCtrl, '', 'wxTE_MULTILINE', 'textctrl1', 8, 101, 204, 100, ''],\
+  control = [5116, wxStaticText, 'Description:', '0', 'statictext2', 9, 79, 66, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']]).";
+
+static char *diagram_settings_dialog = "dialog(name = 'diagram_settings_dialog',\
+  style = 'wxNO_BORDER',\
+  title = 'Diagram settings',\
+  id = 6001,\
+  x = 10, y = 40, width = 400, height = 300,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [6004, wxChoice, '', '0', 'choice4', 13, 31, 85, 21, ['None', 'Invisible', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted', 'Dotted']],\
+  control = [6005, wxStaticText, 'Grid style:', '0', 'statictext5', 13, 11, 56, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [6006, wxStaticText, 'Grid spacing:', '0', 'statictext6', 137, 11, 72, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [6007, wxTextCtrl, '', '0', 'textctrl7', 136, 31, 66, 21, '']).";
+
+static char *general_settings_dialog = "dialog(name = 'general_settings_dialog',\
+  style = 'wxNO_BORDER | wxCAPTION | wxSYSTEM_MENU',\
+  title = '',\
+  id = 2000,\
+  x = 10, y = 40, width = 375, height = 325,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [300, wxStaticText, 'Project path:', '0', 'statictext3', 12, 15, 70, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5107, wxTextCtrl, '', '0', 'textctrl4', 12, 34, 183, 24, '']).";
+
+static char *wide_rectangle = "dialog(name = 'wide_rectangle',\
+  style = 'wxNO_BORDER',\
+  title = 'Wide Rectangle',\
+  id = 100,\
+  x = 10, y = 40, width = 355, height = 405,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [106, wxStaticText, 'Segment Id', '0', 'statictext7', 13, 184, 64, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [107, wxTextCtrl, '', '0', 'textctrl8', 13, 184, 120, 19, ''],\
+  control = [108, wxStaticText, 'Num Channels', '0', 'statictext9', 13, 184, 79, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [109, wxTextCtrl, '', '0', 'textctrl10', 13, 184, 30, 19, ''],\
+  control = [110, wxStaticText, 'Attributes', '0', 'statictext11', 13, 184, 54, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [111, wxTextCtrl, '', '0', 'textctrl12', 13, 184, 120, 19, ''],\
+  control = [5106, wxRadioBox, 'Scope', 'wxRA_SPECIFY_COLS', 'radiobox1', 13, 11, 313, 46, ['Process', 'Application', 'Project'], 3],\
+  control = [5108, wxRadioBox, 'Resource', 'wxRA_SPECIFY_ROWS', 'radiobox3', 166, 70, 72, 82, ['Heap', 'Segment', 'Any'], 3],\
+  control = [5109, wxRadioBox, 'Volatility', 'wxRA_SPECIFY_ROWS', 'radiobox4', 14, 73, 81, 63, ['Permanent', 'Temporary'], 2]).";
+
+static char *thin_rectangle = "dialog(name = 'thin_rectangle',\
+  style = 'wxNO_BORDER',\
+  title = 'Thin Rectangle',\
+  id = 100,\
+  x = 10, y = 40, width = 361, height = 405,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [106, wxStaticText, 'Segment Id', '0', 'statictext7', 12, 169, 64, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [107, wxTextCtrl, '', '0', 'textctrl8', 106, 168, 120, 19, ''],\
+  control = [108, wxStaticText, 'Num Channels', '0', 'statictext9', 12, 204, 79, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [109, wxTextCtrl, '1', '0', 'textctrl10', 106, 202, 30, 19, '1'],\
+  control = [110, wxStaticText, 'Attributes', '0', 'statictext11', 12, 267, 54, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [111, wxTextCtrl, '', '0', 'textctrl12', 106, 265, 120, 19, ''],\
+  control = [5106, wxRadioBox, 'Scope', 'wxRA_SPECIFY_COLS', 'radiobox1', 13, 16, 236, 44, ['Process', 'Application', 'Project'], 3],\
+  control = [5108, wxRadioBox, 'Resource', 'wxRA_SPECIFY_ROWS', 'radiobox3', 176, 73, 72, 82, ['Heap', 'Segment', 'Any'], 3],\
+  control = [5109, wxRadioBox, 'Volatility', 'wxRA_SPECIFY_ROWS', 'radiobox4', 14, 73, 81, 63, ['Permanent', 'Temporary'], 2],\
+  control = [5111, wxStaticText, 'Num Events', '0', 'statictext1', 156, 205, 68, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5112, wxTextCtrl, '1', '0', 'textctrl2', 228, 202, 40, 19, '1'],\
+  control = [5113, wxStaticText, 'Num Fields', '0', 'statictext3', 12, 234, 62, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5114, wxTextCtrl, '1', '0', 'textctrl4', 106, 234, 40, 19, '1']).";
+
+static char *circle = "dialog(name = 'circle',\
+  style = 'wxNO_BORDER',\
+  title = 'Circle',\
+  id = 100,\
+  x = 10, y = 40, width = 361, height = 405,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [106, wxStaticText, 'Segment Id', '0', 'statictext7', 12, 169, 64, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [107, wxTextCtrl, '', '0', 'textctrl8', 106, 168, 120, 19, ''],\
+  control = [108, wxStaticText, 'Num Channels', '0', 'statictext9', 12, 204, 79, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [109, wxTextCtrl, '1', '0', 'textctrl10', 106, 202, 30, 19, '1'],\
+  control = [110, wxStaticText, 'Attributes', '0', 'statictext11', 12, 267, 54, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [111, wxTextCtrl, '', '0', 'textctrl12', 106, 265, 120, 19, ''],\
+  control = [5106, wxRadioBox, 'Scope', 'wxRA_SPECIFY_COLS', 'radiobox1', 13, 16, 236, 44, ['Process', 'Application', 'Project'], 3],\
+  control = [5108, wxRadioBox, 'Resource', 'wxRA_SPECIFY_ROWS', 'radiobox3', 176, 73, 72, 82, ['Heap', 'Segment', 'Any'], 3],\
+  control = [5109, wxRadioBox, 'Volatility', 'wxRA_SPECIFY_ROWS', 'radiobox4', 14, 73, 81, 63, ['Permanent', 'Temporary'], 2],\
+  control = [5111, wxStaticText, 'Num Events', '0', 'statictext1', 156, 205, 68, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5112, wxTextCtrl, '1', '0', 'textctrl2', 228, 202, 40, 19, '1'],\
+  control = [5113, wxStaticText, 'Num Fields', '0', 'statictext3', 12, 234, 62, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5114, wxTextCtrl, '1', '0', 'textctrl4', 106, 234, 40, 19, '1']).";
+
+static char *triangle = "dialog(name = 'triangle',\
+  style = 'wxNO_BORDER',\
+  title = 'Triangle',\
+  id = 100,\
+  x = 10, y = 40, width = 362, height = 405,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 0,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [106, wxStaticText, 'Segment Id', '0', 'statictext7', 16, 170, 64, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [107, wxTextCtrl, '', '0', 'textctrl8', 110, 169, 120, 19, ''],\
+  control = [108, wxStaticText, 'Num Channels', '0', 'statictext9', 16, 213, 79, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [109, wxTextCtrl, '1', '0', 'textctrl10', 110, 213, 30, 19, '1'],\
+  control = [110, wxStaticText, 'Attributes', '0', 'statictext11', 16, 252, 54, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [111, wxTextCtrl, '', '0', 'textctrl12', 110, 252, 120, 19, ''],\
+  control = [5106, wxRadioBox, 'Scope', 'wxRA_SPECIFY_COLS', 'radiobox1', 14, 16, 236, 44, ['Process', 'Application', 'Project'], 3],\
+  control = [5108, wxRadioBox, 'Resource', 'wxRA_SPECIFY_ROWS', 'radiobox3', 178, 75, 72, 82, ['Heap', 'Segment', 'Any'], 3],\
+  control = [5109, wxRadioBox, 'Volatility', 'wxRA_SPECIFY_ROWS', 'radiobox4', 15, 75, 81, 63, ['Permanent', 'Temporary'], 2],\
+  control = [5111, wxStaticText, 'Num Events', '0', 'statictext1', 156, 215, 68, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5112, wxTextCtrl, '1', '0', 'textctrl2', 227, 213, 40, 19, '1']).";
+
+static char *shape_label_dialog = "dialog(name = 'shape_label_dialog',\
+  style = 'wxRAISED_BORDER | wxCAPTION | wxTHICK_FRAME | wxSYSTEM_MENU',\
+  title = 'Edit Shape Label',\
+  id = 6008,\
+  x = 10, y = 10, width = 190, height = 60,\
+  background_colour = 'C0C0C0',\
+  use_dialog_units = 1,\
+  use_system_defaults = 0,\
+  font = [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif'],\
+  control = [6009, wxStaticText, 'Please enter a label for this shape.', '0', 'statictext2', 8, 6, 100, 6, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [101, wxTextCtrl, '', '0', 'textctrl4', 8, 18, 168, 11, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5100, wxButton, 'OK', '0', 'button5', 100, 37, 36, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']],\
+  control = [5101, wxButton, 'Cancel', '0', 'button6', 140, 37, 36, 13, '',\
+      [8, 'wxSWISS', 'wxNORMAL', 'wxNORMAL', 0, 'MS Sans Serif']]).";
+
diff --git a/samples/ogl/studio/symbols.cpp b/samples/ogl/studio/symbols.cpp
new file mode 100644 (file)
index 0000000..6a422c0
--- /dev/null
@@ -0,0 +1,203 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        symbols.cpp
+// Purpose:     Implements the Studio symbol database
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include "studio.h"
+#include "doc.h"
+#include "shapes.h"
+#include "view.h"
+#include "symbols.h"
+
+/*
+ * csSymbol
+ * Represents information about a symbol.
+ */
+
+csSymbol::csSymbol(const wxString& name, wxShape* shape)
+{
+    m_name = name;
+    m_shape = shape;
+    m_toolId = 0;
+}
+
+csSymbol::~csSymbol()
+{
+    delete m_shape;
+}
+
+/*
+ * A table of all possible shapes.
+ * We can use this to construct a palette, etc.
+ */
+csSymbolDatabase::csSymbolDatabase()
+{
+    m_currentId = 800;
+}
+
+csSymbolDatabase::~csSymbolDatabase()
+{
+    ClearSymbols();
+}
+
+void csSymbolDatabase::AddSymbol(csSymbol* symbol)
+{
+    symbol->SetToolId(m_currentId);
+    m_symbols.Append(symbol);
+
+    m_currentId ++;
+}
+
+void csSymbolDatabase::ClearSymbols()
+{
+    wxNode* node = m_symbols.First();
+    while (node)
+    {
+        csSymbol* symbol = (csSymbol*) node->Data();
+        delete symbol;
+
+        node = node->Next();
+    }
+    m_symbols.Clear();
+}
+
+csSymbol* csSymbolDatabase::FindSymbol(const wxString& name) const
+{
+    wxNode* node = m_symbols.First();
+    while (node)
+    {
+        csSymbol* symbol = (csSymbol*) node->Data();
+        if (symbol->GetName() == name)
+            return symbol;
+
+        node = node->Next();
+    }
+    return NULL;
+}
+
+csSymbol* csSymbolDatabase::FindSymbol(int toolId) const
+{
+    wxNode* node = m_symbols.First();
+    while (node)
+    {
+        csSymbol* symbol = (csSymbol*) node->Data();
+        if (symbol->GetToolId() == toolId)
+            return symbol;
+
+        node = node->Next();
+    }
+    return NULL;
+}
+
+// Add symbols to database
+void csApp::InitSymbols()
+{
+    m_symbolDatabase = new csSymbolDatabase;
+
+    wxShape* shape = new csCircleShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Circle", shape));
+
+    shape = new csCircleShadowShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Circle shadow", shape));
+
+    shape = new csThinRectangleShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Thin Rectangle", shape));
+
+    shape = new csWideRectangleShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Wide Rectangle", shape));
+
+    shape = new csSemiCircleShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("SemiCircle", shape));
+
+    shape = new csTriangleShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Triangle", shape));
+
+    shape = new csOctagonShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Octagon", shape));
+
+    shape = new csGroupShape();
+    shape->AssignNewIds();
+    shape->SetEventHandler(new csEvtHandler(shape, shape, wxString("")));
+
+    m_symbolDatabase->AddSymbol(new csSymbol("Group", shape));
+}
+
+wxBitmap* csSymbolDatabase::CreateToolBitmap(csSymbol* symbol)
+{
+    int objectBitmapSize = 32;
+
+    symbol->GetShape()->Recompute();
+
+    wxBitmap *newBitmap = new wxBitmap(objectBitmapSize, objectBitmapSize);
+
+    wxMemoryDC memDC;
+        
+    double height, width, maxSize;
+    symbol->GetShape()->GetBoundingBoxMax(&width, &height);
+
+    if (height > width)
+        maxSize = height;
+    else
+        maxSize = width;
+
+    double borderMargin = 4.0;
+    double scaleFactor = (double)(objectBitmapSize / (maxSize + 2*borderMargin));
+    double centreX = (double)((objectBitmapSize/scaleFactor)/2.0)-1;
+    double centreY = centreX;
+
+    memDC.SelectObject(*newBitmap);
+    memDC.SetUserScale(scaleFactor, scaleFactor);
+
+    memDC.SetBackground(wxBrush(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE), wxSOLID));
+    memDC.Clear();
+    symbol->GetShape()->Show(TRUE);
+    symbol->GetShape()->Move(memDC, centreX, centreY);
+    memDC.SelectObject(wxNullBitmap);
+
+    return newBitmap;
+}
+
diff --git a/samples/ogl/studio/symbols.h b/samples/ogl/studio/symbols.h
new file mode 100644 (file)
index 0000000..390fea9
--- /dev/null
@@ -0,0 +1,76 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        symbols.h
+// Purpose:     Symbol classes (symbol database)
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_SYMBOLS_H_
+#define _STUDIO_SYMBOLS_H_
+
+#ifdef __GNUG__
+// #pragma interface
+#endif
+
+#include <wx/docview.h>
+#include <wx/string.h>
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/ogl.h>
+
+/*
+ * csSymbol
+ * Represents information about a symbol.
+ */
+
+class csSymbol: public wxObject
+{
+public:
+    csSymbol(const wxString& name, wxShape* shape);
+    ~csSymbol();
+
+    inline void SetName(const wxString& name) { m_name = name; }
+    inline wxString GetName() const { return m_name; }
+
+    inline void SetShape(wxShape* shape) { m_shape = shape; }
+    inline wxShape* GetShape() const { return m_shape; }
+
+    inline void SetToolId(int id) { m_toolId = id; }
+    inline int GetToolId() const { return m_toolId; }
+protected:
+    wxString    m_name;
+    wxShape*    m_shape;
+    int         m_toolId;
+};
+
+/*
+ * A table of all possible shapes.
+ * We can use this to construct a palette, etc.
+ */
+class csSymbolDatabase: public wxObject
+{
+public:
+    csSymbolDatabase();
+    ~csSymbolDatabase();
+
+// Accessors
+    inline wxList& GetSymbols() const { return (wxList&) m_symbols; }
+
+// Operations
+    void AddSymbol(csSymbol* symbol);
+    void ClearSymbols();
+    csSymbol* FindSymbol(const wxString& name) const;
+    csSymbol* FindSymbol(int toolId) const;
+    wxBitmap* CreateToolBitmap(csSymbol* symbol);
+
+protected:
+    wxList          m_symbols;
+    int             m_currentId;
+};
+
+#endif
+  // _STUDIO_SYMBOLS_H_
diff --git a/samples/ogl/studio/view.cpp b/samples/ogl/studio/view.cpp
new file mode 100644 (file)
index 0000000..6a66902
--- /dev/null
@@ -0,0 +1,1039 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        view.cpp
+// Purpose:     Implements view functionality
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+// #pragma implementation
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/colordlg.h>
+
+#if !wxUSE_DOC_VIEW_ARCHITECTURE
+#error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
+#endif
+
+#include "studio.h"
+#include "doc.h"
+#include "view.h"
+#include "cspalette.h"
+#include "symbols.h"
+#include "dialogs.h"
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/linesp.h>
+
+IMPLEMENT_DYNAMIC_CLASS(csDiagramView, wxView)
+
+BEGIN_EVENT_TABLE(csDiagramView, wxView)
+    EVT_MENU(wxID_CUT, csDiagramView::OnCut)
+    EVT_MENU(wxID_COPY, csDiagramView::OnCopy)
+    EVT_MENU(wxID_CLEAR, csDiagramView::OnClear)
+    EVT_MENU(wxID_PASTE, csDiagramView::OnPaste)
+    EVT_MENU(wxID_DUPLICATE, csDiagramView::OnDuplicate)
+    EVT_MENU(ID_CS_CHANGE_BACKGROUND_COLOUR, csDiagramView::OnChangeBackgroundColour)
+    EVT_MENU(ID_CS_EDIT_PROPERTIES, csDiagramView::OnEditProperties)
+    EVT_MENU(ID_CS_SELECT_ALL, csDiagramView::OnSelectAll)
+    EVT_TOOL(DIAGRAM_TOOLBAR_LINE_ARROW, csDiagramView::OnToggleArrowTool)
+    EVT_COMBOBOX(ID_WINDOW_POINT_SIZE_COMBOBOX, csDiagramView::OnPointSizeComboSel)
+    EVT_COMBOBOX(ID_WINDOW_ZOOM_COMBOBOX, csDiagramView::OnZoomSel)
+    EVT_TEXT(ID_WINDOW_POINT_SIZE_COMBOBOX, csDiagramView::OnPointSizeComboText)
+    EVT_TOOL(DIAGRAM_TOOLBAR_ALIGNL, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_ALIGNR, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_ALIGNB, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_ALIGNT, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_ALIGN_HORIZ, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_ALIGN_VERT, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_COPY_SIZE, csDiagramView::OnAlign)
+    EVT_TOOL(DIAGRAM_TOOLBAR_NEW_POINT, csDiagramView::OnNewLinePoint)
+    EVT_TOOL(DIAGRAM_TOOLBAR_CUT_POINT, csDiagramView::OnCutLinePoint)
+    EVT_TOOL(DIAGRAM_TOOLBAR_STRAIGHTEN, csDiagramView::OnStraightenLines)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_ALIGNL, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_ALIGNR, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_ALIGNB, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_ALIGNT, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_ALIGN_HORIZ, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_ALIGN_VERT, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_COPY_SIZE, csDiagramView::OnAlignUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_NEW_POINT, csDiagramView::OnNewLinePointUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_CUT_POINT, csDiagramView::OnCutLinePointUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_STRAIGHTEN, csDiagramView::OnStraightenLinesUpdate)
+    EVT_UPDATE_UI(DIAGRAM_TOOLBAR_LINE_ARROW, csDiagramView::OnToggleArrowToolUpdate)
+    EVT_UPDATE_UI(wxID_CUT, csDiagramView::OnCutUpdate)
+    EVT_UPDATE_UI(wxID_COPY, csDiagramView::OnCopyUpdate)
+    EVT_UPDATE_UI(wxID_CLEAR, csDiagramView::OnClearUpdate)
+    EVT_UPDATE_UI(wxID_PASTE, csDiagramView::OnPasteUpdate)
+    EVT_UPDATE_UI(wxID_DUPLICATE, csDiagramView::OnDuplicateUpdate)
+    EVT_UPDATE_UI(ID_CS_EDIT_PROPERTIES, csDiagramView::OnEditPropertiesUpdate)
+    EVT_UPDATE_UI(wxID_UNDO, csDiagramView::OnUndoUpdate)
+    EVT_UPDATE_UI(wxID_REDO, csDiagramView::OnRedoUpdate)
+END_EVENT_TABLE()
+
+// What to do when a view is created. Creates actual
+// windows for displaying the view.
+bool csDiagramView::OnCreate(wxDocument *doc, long flags)
+{
+  wxMenu* editMenu;
+  frame = wxGetApp().CreateChildFrame(doc, this, &editMenu);
+  canvas = wxGetApp().CreateCanvas(this, frame);
+  canvas->SetView(this);
+
+  SetFrame(frame);
+  Activate(TRUE);
+
+  // Initialize the edit menu Undo and Redo items
+  doc->GetCommandProcessor()->SetEditMenu(editMenu);
+  doc->GetCommandProcessor()->Initialize();
+
+  wxShapeCanvas *shapeCanvas = (wxShapeCanvas *)canvas;
+  csDiagramDocument *diagramDoc = (csDiagramDocument *)doc;
+  shapeCanvas->SetDiagram(diagramDoc->GetDiagram());
+  diagramDoc->GetDiagram()->SetCanvas(shapeCanvas);
+
+  diagramDoc->GetDiagram()->SetGridSpacing((double) wxGetApp().GetGridSpacing());
+
+    switch (wxGetApp().GetGridStyle())
+    {
+        case csGRID_STYLE_NONE:
+        {
+            diagramDoc->GetDiagram()->SetSnapToGrid(FALSE);
+            break;
+        }
+        case csGRID_STYLE_INVISIBLE:
+        {
+            diagramDoc->GetDiagram()->SetSnapToGrid(TRUE);
+            break;
+        }
+        case csGRID_STYLE_DOTTED:
+        {
+            // TODO (not implemented in OGL)
+            break;
+        }
+    }
+
+
+  return TRUE;
+}
+
+csDiagramView::~csDiagramView(void)
+{
+    if (frame)
+    {
+        ((wxDocMDIChildFrame*)frame)->SetView(NULL);
+    }
+}
+
+// Sneakily gets used for default print/preview
+// as well as drawing on the screen.
+void csDiagramView::OnDraw(wxDC *dc)
+{
+}
+
+void csDiagramView::OnUpdate(wxView *sender, wxObject *hint)
+{
+  if (canvas)
+    canvas->Refresh();
+}
+
+// Clean up windows used for displaying the view.
+bool csDiagramView::OnClose(bool deleteWindow)
+{
+  if (!GetDocument()->Close())
+    return FALSE;
+
+  csDiagramDocument *diagramDoc = (csDiagramDocument *)GetDocument();
+  diagramDoc->GetDiagram()->SetCanvas(NULL);
+
+  canvas->Clear();
+  canvas->SetDiagram(NULL);
+  canvas->SetView(NULL);
+  canvas = NULL;
+
+  wxMenu* fileMenu = frame->GetMenuBar()->GetMenu(0);
+
+  // Remove file menu from those managed by the command history
+  wxGetApp().GetDocManager()->FileHistoryRemoveMenu(fileMenu);
+
+  Activate(FALSE);
+  frame->Show(FALSE);
+
+  if (deleteWindow)
+  {
+    frame->Destroy();
+  }
+  
+  return TRUE;
+}
+
+// Adds or removes shape from m_selections
+void csDiagramView::SelectShape(wxShape* shape, bool select)
+{
+    if (select && !m_selections.Member(shape))
+        m_selections.Append(shape);
+    else if (!select)
+        m_selections.DeleteObject(shape);
+}
+
+void csDiagramView::OnSelectAll(wxCommandEvent& event)
+{
+    SelectAll(TRUE);
+}
+
+wxShape *csDiagramView::FindFirstSelectedShape(void)
+{
+  csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+  wxShape *theShape = NULL;
+  wxNode *node = doc->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+    wxShape *eachShape = (wxShape *)node->Data();
+    if ((eachShape->GetParent() == NULL) && !eachShape->IsKindOf(CLASSINFO(wxLabelShape)) && eachShape->Selected())
+    {
+      theShape = eachShape;
+      node = NULL;
+    }
+    else node = node->Next();
+  }
+  return theShape;
+}
+
+void csDiagramView::FindSelectedShapes(wxList& selections, wxClassInfo* toFind)
+{
+  csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+  wxNode *node = doc->GetDiagram()->GetShapeList()->First();
+  while (node)
+  {
+    wxShape *eachShape = (wxShape *)node->Data();
+    if ((eachShape->GetParent() == NULL) && !eachShape->IsKindOf(CLASSINFO(wxLabelShape)) && eachShape->Selected() && ((toFind == NULL) || (eachShape->IsKindOf(toFind))))
+    {
+      selections.Append(eachShape);
+    }
+    node = node->Next();
+  }
+}
+
+void csDiagramView::OnUndoUpdate(wxUpdateUIEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+    event.Enable(doc->GetCommandProcessor()->CanUndo());
+}
+
+void csDiagramView::OnRedoUpdate(wxUpdateUIEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+    event.Enable(doc->GetCommandProcessor()->CanRedo());
+}
+
+void csDiagramView::OnCut(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    // Copy the shapes to the clipboard
+    wxGetApp().GetDiagramClipboard().Copy(doc->GetDiagram());
+
+    wxList selections;
+    FindSelectedShapes(selections);
+
+    DoCut(selections);
+}
+
+void csDiagramView::OnClear(wxCommandEvent& event)
+{
+    wxList selections;
+    FindSelectedShapes(selections);
+
+    DoCut(selections);
+}
+
+void csDiagramView::OnCopy(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    // Copy the shapes to the clipboard
+    if (wxGetApp().GetDiagramClipboard().Copy(doc->GetDiagram()))
+    {
+#ifdef __WXMSW__
+        // Copy to the Windows clipboard
+        wxGetApp().GetDiagramClipboard().CopyToClipboard(1.0);
+#endif
+    }
+}
+
+void csDiagramView::OnPaste(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    wxGetApp().GetDiagramClipboard().Paste(doc->GetDiagram());
+}
+
+void csDiagramView::OnDuplicate(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    // Do a copy, then a paste
+    wxGetApp().GetDiagramClipboard().Copy(doc->GetDiagram());
+
+    // Apply an offset. Really, this offset should keep being incremented,
+    // but where do we reset it again?
+    wxGetApp().GetDiagramClipboard().Paste(doc->GetDiagram(), NULL, 20, 20);
+}
+
+void csDiagramView::OnCutUpdate(wxUpdateUIEvent& event)
+{
+    event.Enable( (m_selections.Number() > 0) );
+}
+
+void csDiagramView::OnClearUpdate(wxUpdateUIEvent& event)
+{
+    event.Enable( (m_selections.Number() > 0) );
+}
+
+void csDiagramView::OnCopyUpdate(wxUpdateUIEvent& event)
+{
+    event.Enable( (m_selections.Number() > 0) );
+}
+
+void csDiagramView::OnPasteUpdate(wxUpdateUIEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    int n = wxGetApp().GetDiagramClipboard().GetCount();
+
+    event.Enable( (n > 0) );
+}
+
+void csDiagramView::OnDuplicateUpdate(wxUpdateUIEvent& event)
+{
+    event.Enable( (m_selections.Number() > 0) );
+}
+
+void csDiagramView::DoCut(wxList& shapes)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    if (shapes.Number() > 0)
+    {
+        csDiagramCommand* cmd = new csDiagramCommand("Cut", doc);
+
+        wxNode* node = shapes.First();
+        while (node)
+        {
+            wxShape *theShape = (wxShape*) node->Data();
+            csCommandState* state = new csCommandState(ID_CS_CUT, NULL, theShape);
+
+            // Insert lines at the front, so they are cut first.
+            // Otherwise we may try to remove a shape with a line still
+            // attached.
+            if (theShape->IsKindOf(CLASSINFO(wxLineShape)))
+                cmd->InsertState(state);
+            else
+                cmd->AddState(state);
+
+            node = node->Next();
+        }
+        cmd->RemoveLines(); // Schedule any connected lines, not already mentioned,
+                            // to be removed first
+
+        doc->GetCommandProcessor()->Submit(cmd);
+    }
+}
+
+// Generalised command
+void csDiagramView::DoCmd(wxList& shapes, wxList& oldShapes, int cmd, const wxString& op)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    if (shapes.Number() > 0)
+    {
+        csDiagramCommand* command = new csDiagramCommand(op, doc);
+
+        wxNode* node = shapes.First();
+        wxNode* node1 = oldShapes.First();
+        while (node && node1)
+        {
+            wxShape *theShape = (wxShape*) node->Data();
+            wxShape *oldShape = (wxShape*) node1->Data();
+            csCommandState* state = new csCommandState(cmd, theShape, oldShape);
+            command->AddState(state);
+
+            node = node->Next();
+            node1 = node1->Next();
+        }
+        doc->GetCommandProcessor()->Submit(command);
+    }
+}
+
+void csDiagramView::OnChangeBackgroundColour(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    wxList selections;
+    FindSelectedShapes(selections);
+
+    if (selections.Number() > 0)
+    {
+        wxColourData data;
+        data.SetChooseFull(TRUE);
+        if (selections.Number() == 1)
+        {
+            wxShape* firstShape = (wxShape*) selections.First()->Data();
+            data.SetColour(firstShape->GetBrush()->GetColour());
+        }
+
+        wxColourDialog *dialog = new wxColourDialog(frame, &data);
+        wxBrush *theBrush = NULL;
+        if (dialog->ShowModal() == wxID_OK)
+        {
+          wxColourData retData = dialog->GetColourData();
+          wxColour col = retData.GetColour();
+          theBrush = wxTheBrushList->FindOrCreateBrush(col, wxSOLID);
+        }
+        dialog->Close(TRUE);
+        if (!theBrush)
+            return;
+
+        csDiagramCommand* cmd = new csDiagramCommand("Change colour", doc);
+
+        wxNode* node = selections.First();
+        while (node)
+        {
+            wxShape *theShape = (wxShape*) node->Data();
+            wxShape* newShape = theShape->CreateNewCopy();
+            newShape->SetBrush(theBrush);
+
+            csCommandState* state = new csCommandState(ID_CS_CHANGE_BACKGROUND_COLOUR, newShape, theShape);
+            cmd->AddState(state);
+
+            node = node->Next();
+        }
+        doc->GetCommandProcessor()->Submit(cmd);
+    }
+}
+
+void csDiagramView::OnEditProperties(wxCommandEvent& event)
+{
+      wxShape *theShape = FindFirstSelectedShape();
+      if (theShape)
+        ((csEvtHandler *)theShape->GetEventHandler())->EditProperties();
+}
+
+void csDiagramView::OnEditPropertiesUpdate(wxUpdateUIEvent& event)
+{
+    wxList selections;
+    FindSelectedShapes(selections);
+    event.Enable( (selections.Number() > 0) );
+}
+
+void csDiagramView::OnPointSizeComboSel(wxCommandEvent& event)
+{
+    wxComboBox* combo = (wxComboBox*) event.GetEventObject();
+    wxASSERT( combo != NULL );
+
+    int newPointSize = (combo->GetSelection() + 1);
+
+    ApplyPointSize(newPointSize);
+
+}
+
+// TODO: must find out how to intercept the Return key, rather than
+// every key stroke. But for now, do every key stroke.
+void csDiagramView::OnPointSizeComboText(wxCommandEvent& event)
+{
+    wxComboBox* combo = (wxComboBox*) event.GetEventObject();
+    wxASSERT( combo != NULL );
+
+    wxString str(combo->GetValue());
+    int newPointSize = atoi((const char*) str);
+
+    if (newPointSize < 2)
+        return;
+
+    ApplyPointSize(newPointSize);
+}
+
+void csDiagramView::ApplyPointSize(int pointSize)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    wxList selections;
+    FindSelectedShapes(selections);
+
+    if (selections.Number() > 0)
+    {
+        csDiagramCommand* cmd = new csDiagramCommand("Point size", doc);
+
+        wxNode* node = selections.First();
+        while (node)
+        {
+            wxShape *theShape = (wxShape*) node->Data();
+            wxShape *newShape = theShape->CreateNewCopy();
+
+            wxFont* newFont = wxTheFontList->FindOrCreateFont(pointSize,
+                theShape->GetFont()->GetFamily(),
+                theShape->GetFont()->GetStyle(),
+                theShape->GetFont()->GetWeight(),
+                theShape->GetFont()->GetUnderlined(),
+                theShape->GetFont()->GetFaceName());
+
+            newShape->SetFont(newFont);
+
+            csCommandState* state = new csCommandState(ID_CS_FONT_CHANGE, newShape, theShape);
+
+            cmd->AddState(state);
+
+            node = node->Next();
+        }
+        doc->GetCommandProcessor()->Submit(cmd);
+    }
+}
+
+void csDiagramView::OnZoomSel(wxCommandEvent& event)
+{
+    int maxZoom = 200;
+    int minZoom = 5;
+    int inc = 5;
+    int noStrings = (maxZoom - minZoom)/inc ;
+
+    wxComboBox* combo = (wxComboBox*) event.GetEventObject();
+    wxASSERT( combo != NULL );
+
+    int scale = (int) ((noStrings - combo->GetSelection() - 1)*inc + minZoom);
+
+    canvas->SetScale((double) (scale/100.0), (double) (scale/100.0));
+    canvas->Refresh();
+}
+
+// Select or deselect all
+void csDiagramView::SelectAll(bool select)
+{
+    wxClientDC dc(canvas);
+    canvas->PrepareDC(dc);
+
+    if (!select)
+    {
+        wxList selections;
+        FindSelectedShapes(selections);
+
+        wxNode* node = selections.First();
+        while (node)
+        {
+            wxShape *theShape = (wxShape*) node->Data();
+            theShape->Select(FALSE, &dc);
+            SelectShape(theShape, FALSE);
+
+            node = node->Next();
+        }
+    }
+    else
+    {
+        csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+        wxNode *node = doc->GetDiagram()->GetShapeList()->First();
+        while (node)
+        {
+            wxShape *eachShape = (wxShape *)node->Data();
+            if (eachShape->GetParent() == NULL &&
+                !eachShape->IsKindOf(CLASSINFO(wxControlPoint)) &&
+                !eachShape->IsKindOf(CLASSINFO(wxLabelShape)))
+            {
+                eachShape->Select(TRUE, &dc);
+                SelectShape(eachShape, TRUE);
+            }
+            node = node->Next();
+        }
+    }
+}
+
+
+void csDiagramView::OnToggleArrowTool(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+
+    bool state = wxGetApp().GetDiagramToolBar()->GetToolState(DIAGRAM_TOOLBAR_LINE_ARROW);
+    wxString stateName;
+    if (state)
+        stateName = "Arrow on";
+    else
+        stateName = "Arrow off";
+
+    wxList selections;
+    FindSelectedShapes(selections, CLASSINFO(wxLineShape));
+
+    if (selections.Number() > 0)
+    {
+        csDiagramCommand* cmd = new csDiagramCommand(stateName, doc);
+
+        wxNode* node = selections.First();
+        while (node)
+        {
+            wxLineShape *theShape = (wxLineShape*) node->Data();
+            wxLineShape *newShape = NULL;
+
+            if (state)
+            {
+                // Add arrow
+                if (theShape->GetArrows().Number() == 0)
+                {
+                    newShape = (wxLineShape*) theShape->CreateNewCopy();
+                    newShape->AddArrow(ARROW_ARROW, ARROW_POSITION_MIDDLE, 10.0, 0.0, "Normal arrowhead");
+                }
+            }
+            else
+            {
+                if (theShape->GetArrows().Number() > 0)
+                {
+                    newShape = (wxLineShape*) theShape->CreateNewCopy();
+                    newShape->ClearArrowsAtPosition();
+                }
+            }
+
+            // If the new state is the same as the old, don't bother adding it to the command state.
+            if (newShape)
+            {
+                csCommandState* state = new csCommandState(ID_CS_ARROW_CHANGE, newShape, theShape);
+                cmd->AddState(state);
+            }
+
+            node = node->Next();
+        }
+        doc->GetCommandProcessor()->Submit(cmd);
+    }
+}
+
+void csDiagramView::OnToggleArrowToolUpdate(wxUpdateUIEvent& event)
+{
+    wxList selections;
+    FindSelectedShapes(selections, CLASSINFO(wxLineShape));
+    event.Enable( (selections.Number() > 0) );
+}
+
+// Make the point size combobox reflect this
+void csDiagramView::ReflectPointSize(int pointSize)
+{
+    wxComboBox* comboBox = wxGetApp().GetPointSizeComboBox();
+    comboBox->SetSelection(pointSize -1);
+}
+
+// Make the arrow toggle button reflect the state of the line
+void csDiagramView::ReflectArrowState(wxLineShape* lineShape)
+{
+    bool haveArrow = FALSE;
+    wxNode *node = lineShape->GetArrows().First();
+    while (node)
+    {
+      wxArrowHead *arrow = (wxArrowHead *)node->Data();
+      if (ARROW_POSITION_MIDDLE == arrow->GetArrowEnd())
+        haveArrow = TRUE;
+      node = node->Next();
+    }
+
+    wxGetApp().GetDiagramToolBar()->ToggleTool(DIAGRAM_TOOLBAR_LINE_ARROW, haveArrow);
+}
+
+void csDiagramView::OnAlign(wxCommandEvent& event)
+{
+    // Make a copy of the selections, keeping only those shapes
+    // that are top-level non-line shapes.
+    wxList selections;
+    wxNode* node = GetSelectionList().First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if ((shape->GetParent() == NULL) && (!shape->IsKindOf(CLASSINFO(wxLineShape))))
+        {
+            selections.Append(shape);
+        }
+        node = node->Next();
+    }
+
+    if (selections.Number() == 0)
+        return;
+
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+    csDiagramCommand* cmd = new csDiagramCommand("Align", doc);
+
+    node = selections.First();
+    wxShape* firstShape = (wxShape*) node->Data();
+
+    double x = firstShape->GetX();
+    double y = firstShape->GetY();
+    double width, height;
+    firstShape->GetBoundingBoxMax(&width, &height);
+
+    node = selections.First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (shape != firstShape)
+        {
+            double x1 = shape->GetX();
+            double y1 = shape->GetY();
+            double width1, height1;
+            shape->GetBoundingBoxMax(& width1, & height1);
+
+            wxShape* newShape = shape->CreateNewCopy();
+
+            switch (event.GetId())
+            {
+                case DIAGRAM_TOOLBAR_ALIGNL:
+                {
+                    double x2 = (double)(x - (width/2.0) + (width1/2.0));
+                    newShape->SetX(x2);
+                    break;
+                }
+                case DIAGRAM_TOOLBAR_ALIGNR:
+                {
+                    double x2 = (double)(x + (width/2.0) - (width1/2.0));
+                    newShape->SetX(x2);
+                    break;
+                }
+                case DIAGRAM_TOOLBAR_ALIGNB:
+                {
+                    double y2 = (double)(y + (height/2.0) - (height1/2.0));
+                    newShape->SetY(y2);
+                    break;
+                }
+                case DIAGRAM_TOOLBAR_ALIGNT:
+                {
+                    double y2 = (double)(y - (height/2.0) + (height1/2.0));
+                    newShape->SetY(y2);
+                    break;
+                }
+                case DIAGRAM_TOOLBAR_ALIGN_HORIZ:
+                {
+                    newShape->SetX(x);
+                    break;
+                }
+                case DIAGRAM_TOOLBAR_ALIGN_VERT:
+                {
+                    newShape->SetY(y);
+                    break;
+                }
+                case DIAGRAM_TOOLBAR_COPY_SIZE:
+                {
+                    newShape->SetSize(width, height);
+                    break;
+                }
+            }
+            csCommandState* state = new csCommandState(ID_CS_ALIGN, newShape, shape);
+            cmd->AddState(state);
+        }
+        node = node->Next();
+    }
+    doc->GetCommandProcessor()->Submit(cmd);
+}
+
+void csDiagramView::OnAlignUpdate(wxUpdateUIEvent& event)
+{
+    // This is an approximation, since there may be lines
+    // amongst the selections.
+    event.Enable( (m_selections.Number() > 1) ) ;
+}
+
+void csDiagramView::OnNewLinePoint(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+    csDiagramCommand* cmd = new csDiagramCommand("New line point", doc);
+
+    wxNode* node = m_selections.First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxShape* newShape = shape->CreateNewCopy();
+            ((wxLineShape*)newShape)->InsertLineControlPoint(NULL);
+            csCommandState* state = new csCommandState(ID_CS_NEW_POINT, newShape, shape);
+            cmd->AddState(state);
+        }
+        node = node->Next();
+    }
+    doc->GetCommandProcessor()->Submit(cmd);
+}
+
+void csDiagramView::OnCutLinePoint(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+    csDiagramCommand* cmd = new csDiagramCommand("Cut line point", doc);
+
+    wxNode* node = m_selections.First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxShape* newShape = shape->CreateNewCopy();
+            ((wxLineShape*)newShape)->DeleteLineControlPoint();
+            csCommandState* state = new csCommandState(ID_CS_CUT_POINT, newShape, shape);
+            cmd->AddState(state);
+        }
+        node = node->Next();
+    }
+    doc->GetCommandProcessor()->Submit(cmd);
+}
+
+void csDiagramView::OnStraightenLines(wxCommandEvent& event)
+{
+    csDiagramDocument *doc = (csDiagramDocument *)GetDocument();
+    csDiagramCommand* cmd = new csDiagramCommand("Straighten lines", doc);
+
+    wxNode* node = m_selections.First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxShape* newShape = shape->CreateNewCopy();
+            ((wxLineShape*)newShape)->Straighten();
+            csCommandState* state = new csCommandState(ID_CS_STRAIGHTEN, newShape, shape);
+            cmd->AddState(state);
+        }
+        node = node->Next();
+    }
+    doc->GetCommandProcessor()->Submit(cmd);
+}
+
+void csDiagramView::OnNewLinePointUpdate(wxUpdateUIEvent& event)
+{
+    wxList selections;
+    FindSelectedShapes(selections, CLASSINFO(wxLineShape));
+    event.Enable( (selections.Number() > 0) );
+}
+
+void csDiagramView::OnCutLinePointUpdate(wxUpdateUIEvent& event)
+{
+    wxList selections;
+    FindSelectedShapes(selections, CLASSINFO(wxLineShape));
+    event.Enable( (selections.Number() > 0) );
+}
+
+void csDiagramView::OnStraightenLinesUpdate(wxUpdateUIEvent& event)
+{
+    wxList selections;
+    FindSelectedShapes(selections, CLASSINFO(wxLineShape));
+    event.Enable( (selections.Number() > 0) );
+}
+
+/*
+ * Window implementations
+ */
+
+IMPLEMENT_CLASS(csCanvas, wxShapeCanvas)
+
+BEGIN_EVENT_TABLE(csCanvas, wxShapeCanvas)
+    EVT_MOUSE_EVENTS(csCanvas::OnMouseEvent)
+    EVT_PAINT(csCanvas::OnPaint)
+END_EVENT_TABLE()
+
+// Define a constructor for my canvas
+csCanvas::csCanvas(csDiagramView *v, wxWindow *parent, wxWindowID id, const wxPoint& pos,
+    const wxSize& size, long style):
+ wxShapeCanvas(parent, id, pos, size, style)
+{
+  m_view = v;
+}
+
+csCanvas::~csCanvas(void)
+{
+}
+
+void csCanvas::DrawOutline(wxDC& dc, double x1, double y1, double x2, double y2)
+{
+    wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+    dc.SetPen(dottedPen);
+    dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+    dc.DrawRectangle((long) x1, (long) y1, (long) (x2 - x1), (long) (y2 - y1));
+}
+
+void csCanvas::OnLeftClick(double x, double y, int keys)
+{
+    csEditorToolPalette *palette = wxGetApp().GetDiagramPalette();
+
+    if (palette->GetSelection() == PALETTE_ARROW)
+    {
+        GetView()->SelectAll(FALSE);
+
+        wxClientDC dc(this);
+        PrepareDC(dc);
+
+        Redraw(dc);
+        return;
+    }
+
+    if (palette->GetSelection() == PALETTE_TEXT_TOOL)
+    {
+        // Ask for a label and create a new free-floating text region
+        csLabelEditingDialog* dialog = new csLabelEditingDialog(GetParent());
+
+        dialog->SetShapeLabel("");
+        dialog->SetTitle("New text box");
+        if (dialog->ShowModal() == wxID_CANCEL)
+        {
+            dialog->Destroy();
+            return;
+        }
+
+        wxString newLabel = dialog->GetShapeLabel();
+        dialog->Destroy();
+
+        wxShape* shape = new csTextBoxShape;
+        shape->AssignNewIds();
+        shape->SetEventHandler(new csEvtHandler(shape, shape, newLabel));
+
+        wxComboBox* comboBox = wxGetApp().GetPointSizeComboBox();
+        wxString str(comboBox->GetValue());
+        int pointSize = atoi((const char*) str);
+
+        wxFont* newFont = wxTheFontList->FindOrCreateFont(pointSize,
+                shape->GetFont()->GetFamily(),
+                shape->GetFont()->GetStyle(),
+                shape->GetFont()->GetWeight(),
+                shape->GetFont()->GetUnderlined(),
+                shape->GetFont()->GetFaceName());
+
+        shape->SetFont(newFont);
+
+        shape->SetX(x);
+        shape->SetY(y);
+
+        csDiagramCommand* cmd = new csDiagramCommand("Text box",
+            (csDiagramDocument *)GetView()->GetDocument(),
+            new csCommandState(ID_CS_ADD_SHAPE, shape, NULL));
+        GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
+
+        palette->SetSelection(PALETTE_ARROW);
+
+        return;
+    }
+
+    csSymbol* symbol = wxGetApp().GetSymbolDatabase()->FindSymbol(palette->GetSelection());
+    if (symbol)
+    {
+        wxShape* theShape = symbol->GetShape()->CreateNewCopy();
+
+        wxComboBox* comboBox = wxGetApp().GetPointSizeComboBox();
+        wxString str(comboBox->GetValue());
+        int pointSize = atoi((const char*) str);
+
+        wxFont* newFont = wxTheFontList->FindOrCreateFont(pointSize,
+                symbol->GetShape()->GetFont()->GetFamily(),
+                symbol->GetShape()->GetFont()->GetStyle(),
+                symbol->GetShape()->GetFont()->GetWeight(),
+                symbol->GetShape()->GetFont()->GetUnderlined(),
+                symbol->GetShape()->GetFont()->GetFaceName());
+
+        theShape->SetFont(newFont);
+
+        theShape->AssignNewIds();
+        theShape->SetX(x);
+        theShape->SetY(y);
+
+        csDiagramCommand* cmd = new csDiagramCommand(symbol->GetName(),
+            (csDiagramDocument *)GetView()->GetDocument(),
+            new csCommandState(ID_CS_ADD_SHAPE, theShape, NULL));
+        GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
+
+        palette->SetSelection(PALETTE_ARROW);
+    }
+}
+
+void csCanvas::OnRightClick(double x, double y, int keys)
+{
+}
+
+// Initial point
+static double sg_initialX, sg_initialY;
+
+void csCanvas::OnDragLeft(bool draw, double x, double y, int keys)
+{
+    wxClientDC dc(this);
+    PrepareDC(dc);
+
+    dc.SetLogicalFunction(OGLRBLF);
+    DrawOutline(dc, sg_initialX, sg_initialY, x, y);
+}
+
+void csCanvas::OnBeginDragLeft(double x, double y, int keys)
+{
+    sg_initialX = x;
+    sg_initialY = y;
+
+    wxClientDC dc(this);
+    PrepareDC(dc);
+
+    dc.SetLogicalFunction(OGLRBLF);
+    DrawOutline(dc, sg_initialX, sg_initialY, x, y);
+    CaptureMouse();
+}
+
+void csCanvas::OnEndDragLeft(double x, double y, int keys)
+{
+    ReleaseMouse();
+
+    wxClientDC dc(this);
+    PrepareDC(dc);
+
+    // Select all images within the rectangle
+    float min_x, max_x, min_y, max_y;
+    min_x = wxMin(x, sg_initialX);
+    max_x = wxMax(x, sg_initialX);
+    min_y = wxMin(y, sg_initialY);
+    max_y = wxMax(y, sg_initialY);
+
+    wxNode *node = GetDiagram()->GetShapeList()->First();
+    while (node)
+    {
+        wxShape *shape = (wxShape *)node->Data();
+        if (shape->GetParent() == NULL && !shape->IsKindOf(CLASSINFO(wxControlPoint)))
+        {
+            float image_x = shape->GetX();
+            float image_y = shape->GetY();
+            if (image_x >= min_x && image_x <= max_x &&
+                image_y >= min_y && image_y <= max_y)
+            {
+                shape->Select(TRUE, &dc);
+                GetView()->SelectShape(shape, TRUE);
+            }
+        }
+        node = node->Next();
+    }
+}
+
+void csCanvas::OnDragRight(bool draw, double x, double y, int keys)
+{
+}
+
+void csCanvas::OnBeginDragRight(double x, double y, int keys)
+{
+}
+
+void csCanvas::OnEndDragRight(double x, double y, int keys)
+{
+}
+
+void csCanvas::OnMouseEvent(wxMouseEvent& event)
+{
+    wxShapeCanvas::OnMouseEvent(event);
+}
+
+void csCanvas::OnPaint(wxPaintEvent& event)
+{
+//  if (GetDiagram())
+    wxShapeCanvas::OnPaint(event);
+}
diff --git a/samples/ogl/studio/view.h b/samples/ogl/studio/view.h
new file mode 100644 (file)
index 0000000..11cb3ae
--- /dev/null
@@ -0,0 +1,141 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        view.h
+// Purpose:     View-related classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _STUDIO_VIEW_H_
+#define _STUDIO_VIEW_H_
+
+#ifdef __GNUG__
+// #pragma interface "view.h"
+#endif
+
+#include "doc.h"
+#include <wx/ogl/ogl.h>
+
+class csDiagramView;
+class csCanvas: public wxShapeCanvas
+{
+DECLARE_CLASS(csCanvas)
+ public:
+
+  csCanvas(csDiagramView *view, wxWindow *parent = NULL, wxWindowID id = -1,
+            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
+            long style = wxRETAINED);
+  ~csCanvas(void);
+
+  void DrawOutline(wxDC& dc, double x1, double y1, double x2, double y2);
+
+  void OnMouseEvent(wxMouseEvent& event);
+  void OnPaint(wxPaintEvent& event);
+
+  virtual void OnLeftClick(double x, double y, int keys = 0);
+  virtual void OnRightClick(double x, double y, int keys = 0);
+
+  virtual void OnDragLeft(bool draw, double x, double y, int keys=0); // Erase if draw false
+  virtual void OnBeginDragLeft(double x, double y, int keys=0);
+  virtual void OnEndDragLeft(double x, double y, int keys=0);
+
+  virtual void OnDragRight(bool draw, double x, double y, int keys=0); // Erase if draw false
+  virtual void OnBeginDragRight(double x, double y, int keys=0);
+  virtual void OnEndDragRight(double x, double y, int keys=0);
+
+  inline csDiagramView*  GetView() const { return m_view; }
+  inline void SetView(csDiagramView* view) { m_view = view; }
+
+ protected:
+  csDiagramView*    m_view;
+
+DECLARE_EVENT_TABLE()
+};
+
+class csDiagramView: public wxView
+{
+  DECLARE_DYNAMIC_CLASS(csDiagramView)
+ public:
+  csDiagramView(void) { canvas = NULL; frame = NULL; };
+  ~csDiagramView(void);
+
+  bool OnCreate(wxDocument *doc, long flags);
+  void OnDraw(wxDC *dc);
+  void OnUpdate(wxView *sender, wxObject *hint = NULL);
+  bool OnClose(bool deleteWindow = TRUE);
+  void OnSelectAll(wxCommandEvent& event);
+
+  wxShape *FindFirstSelectedShape(void);
+
+  // Scans the canvas for selections (doesn't use m_selections)
+  void FindSelectedShapes(wxList& selections, wxClassInfo* toFind = NULL);
+
+  // The selections in the order in which they were selected
+  inline wxList& GetSelectionList() const { return (wxList&) m_selections; }
+
+  // Adds or removes shape from m_selections
+  void SelectShape(wxShape* shape, bool select);
+
+  // Apply point size to current shapes
+  void ApplyPointSize(int pointSize);
+
+  // Make the point size combobox reflect this
+  void ReflectPointSize(int pointSize);
+
+  // Make the arrow toggle button reflect the state of the line
+  void ReflectArrowState(wxLineShape* lineShape);
+
+  // Do a cut operation for the given list of shapes
+  void DoCut(wxList& shapes);
+
+  // Do a general command
+  void DoCmd(wxList& shapes, wxList& oldShapes, int cmd, const wxString& op);
+
+  // Select or deselect all
+  void SelectAll(bool select = TRUE);
+
+// Event handlers
+  void OnCut(wxCommandEvent& event);
+  void OnCopy(wxCommandEvent& event);
+  void OnPaste(wxCommandEvent& event);
+  void OnDuplicate(wxCommandEvent& event);
+  void OnClear(wxCommandEvent& event);
+  void OnChangeBackgroundColour(wxCommandEvent& event);
+  void OnEditProperties(wxCommandEvent& event);
+  void OnPointSizeComboSel(wxCommandEvent& event);
+  void OnPointSizeComboText(wxCommandEvent& event);
+  void OnToggleArrowTool(wxCommandEvent& event);
+  void OnZoomSel(wxCommandEvent& event);
+  void OnAlign(wxCommandEvent& event);
+  void OnNewLinePoint(wxCommandEvent& event);
+  void OnCutLinePoint(wxCommandEvent& event);
+  void OnStraightenLines(wxCommandEvent& event);
+
+// UI update handles
+  void OnToggleArrowToolUpdate(wxUpdateUIEvent& event);
+  void OnEditPropertiesUpdate(wxUpdateUIEvent& event);
+  void OnCutUpdate(wxUpdateUIEvent& event);
+  void OnClearUpdate(wxUpdateUIEvent& event);
+  void OnCopyUpdate(wxUpdateUIEvent& event);
+  void OnPasteUpdate(wxUpdateUIEvent& event);
+  void OnDuplicateUpdate(wxUpdateUIEvent& event);
+  void OnAlignUpdate(wxUpdateUIEvent& event);
+  void OnNewLinePointUpdate(wxUpdateUIEvent& event);
+  void OnCutLinePointUpdate(wxUpdateUIEvent& event);
+  void OnStraightenLinesUpdate(wxUpdateUIEvent& event);
+  void OnUndoUpdate(wxUpdateUIEvent& event);
+  void OnRedoUpdate(wxUpdateUIEvent& event);
+
+DECLARE_EVENT_TABLE()
+
+public:
+  wxFrame*      frame;
+  csCanvas*     canvas;
+  wxList        m_selections;
+};
+
+#endif
+    // _STUDIO_VIEW_H_
diff --git a/src/ogl/Makefile.in b/src/ogl/Makefile.in
new file mode 100644 (file)
index 0000000..1edfe19
--- /dev/null
@@ -0,0 +1,13 @@
+#
+
+top_builddir = ../..
+
+VPATH= $(top_srcdir)/ogl
+
+LIBTARGET=$(top_builddir)/lib/libogl
+
+OBJECTS=basic.o   bmpshape.o  composit.o  divided.o  lines.o    misc.o \
+        basic2.o  canvas.o    constrnt.o  drawn.o    mfutils.o  ogldiag.o
+
+
+include $(top_builddir)/src/makelib.env
diff --git a/src/ogl/basic.cpp b/src/ogl/basic.cpp
new file mode 100644 (file)
index 0000000..2d53683
--- /dev/null
@@ -0,0 +1,3274 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        basic.cpp
+// Purpose:     Basic OGL classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "basic.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/composit.h>
+#include <wx/ogl/lines.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/divided.h>
+#include <wx/ogl/misc.h>
+
+// Control point types
+// Rectangle and most other shapes
+#define CONTROL_POINT_VERTICAL   1
+#define CONTROL_POINT_HORIZONTAL 2
+#define CONTROL_POINT_DIAGONAL   3
+
+// Line
+#define CONTROL_POINT_ENDPOINT_TO 4
+#define CONTROL_POINT_ENDPOINT_FROM 5
+#define CONTROL_POINT_LINE       6
+
+IMPLEMENT_DYNAMIC_CLASS(wxShapeTextLine, wxObject)
+IMPLEMENT_DYNAMIC_CLASS(wxAttachmentPoint, wxObject)
+
+wxShapeTextLine::wxShapeTextLine(double the_x, double the_y, const wxString& the_line)
+{
+  m_x = the_x; m_y = the_y; m_line = the_line;
+}
+
+wxShapeTextLine::~wxShapeTextLine()
+{
+}
+
+IMPLEMENT_ABSTRACT_CLASS(wxShapeEvtHandler, wxObject)
+
+wxShapeEvtHandler::wxShapeEvtHandler(wxShapeEvtHandler *prev, wxShape *shape)
+{
+  m_previousHandler = prev;
+  m_handlerShape = shape;
+}
+
+wxShapeEvtHandler::~wxShapeEvtHandler()
+{
+}
+
+// Creates a copy of this event handler.
+wxShapeEvtHandler* wxShapeEvtHandler::CreateNewCopy()
+{
+  wxShapeEvtHandler* newObject = (wxShapeEvtHandler*) GetClassInfo()->CreateObject();
+
+  wxASSERT( (newObject != NULL) );
+  wxASSERT( (newObject->IsKindOf(CLASSINFO(wxShapeEvtHandler))) );
+
+  newObject->m_previousHandler = newObject;
+
+  CopyData(*newObject);
+
+  return newObject;
+}
+
+
+void wxShapeEvtHandler::OnDelete()
+{
+  if (this != GetShape())
+    delete this;
+}
+
+void wxShapeEvtHandler::OnDraw(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDraw(dc);
+}
+
+void wxShapeEvtHandler::OnMoveLinks(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnMoveLinks(dc);
+}
+
+void wxShapeEvtHandler::OnMoveLink(wxDC& dc, bool moveControlPoints)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnMoveLink(dc, moveControlPoints);
+}
+
+void wxShapeEvtHandler::OnDrawContents(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDrawContents(dc);
+}
+
+void wxShapeEvtHandler::OnDrawBranches(wxDC& dc, bool erase)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDrawBranches(dc, erase);
+}
+
+void wxShapeEvtHandler::OnSize(double x, double y)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnSize(x, y);
+}
+
+bool wxShapeEvtHandler::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
+{
+  if (m_previousHandler)
+    return m_previousHandler->OnMovePre(dc, x, y, old_x, old_y, display);
+  else
+    return TRUE;
+}
+
+void wxShapeEvtHandler::OnMovePost(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnMovePost(dc, x, y, old_x, old_y, display);
+}
+
+void wxShapeEvtHandler::OnErase(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnErase(dc);
+}
+
+void wxShapeEvtHandler::OnEraseContents(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnEraseContents(dc);
+}
+
+void wxShapeEvtHandler::OnHighlight(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnHighlight(dc);
+}
+
+void wxShapeEvtHandler::OnLeftClick(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnLeftClick(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnLeftDoubleClick(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnLeftDoubleClick(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnRightClick(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnRightClick(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnEndDragLeft(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnDragRight(bool draw, double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDragRight(draw, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnBeginDragRight(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnBeginDragRight(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnEndDragRight(double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnEndDragRight(x, y, keys, attachment);
+}
+
+// Control points ('handles') redirect control to the actual shape, to make it easier
+// to override sizing behaviour.
+void wxShapeEvtHandler::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnSizingDragLeft(pt, draw, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnSizingBeginDragLeft(pt, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnSizingEndDragLeft(pt, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDrawOutline(dc, x, y, w, h);
+}
+
+void wxShapeEvtHandler::OnDrawControlPoints(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnDrawControlPoints(dc);
+}
+
+void wxShapeEvtHandler::OnEraseControlPoints(wxDC& dc)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnEraseControlPoints(dc);
+}
+
+// Can override this to prevent or intercept line reordering.
+void wxShapeEvtHandler::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering)
+{
+  if (m_previousHandler)
+    m_previousHandler->OnChangeAttachment(attachment, line, ordering);
+}
+
+IMPLEMENT_ABSTRACT_CLASS(wxShape, wxShapeEvtHandler)
+
+wxShape::wxShape(wxShapeCanvas *can)
+{
+  m_eventHandler = this;
+  SetShape(this);
+  m_id = 0;
+  m_formatted = FALSE;
+  m_canvas = can;
+  m_xpos = 0.0; m_ypos = 0.0;
+  m_pen = g_oglBlackPen;
+  m_brush = wxWHITE_BRUSH;
+  m_font = g_oglNormalFont;
+  m_textColour = wxBLACK;
+  m_textColourName = "BLACK";
+  m_visible = FALSE;
+  m_clientData = NULL;
+  m_selected = FALSE;
+  m_attachmentMode = ATTACHMENT_MODE_NONE;
+  m_spaceAttachments = TRUE;
+  m_disableLabel = FALSE;
+  m_fixedWidth = FALSE;
+  m_fixedHeight = FALSE;
+  m_drawHandles = TRUE;
+  m_sensitivity = OP_ALL;
+  m_draggable = TRUE;
+  m_parent = NULL;
+  m_formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT;
+  m_shadowMode = SHADOW_NONE;
+  m_shadowOffsetX = 6;
+  m_shadowOffsetY = 6;
+  m_shadowBrush = wxBLACK_BRUSH;
+  m_textMarginX = 5;
+  m_textMarginY = 5;
+  m_regionName = "0";
+  m_centreResize = TRUE;
+  m_maintainAspectRatio = FALSE;
+  m_highlighted = FALSE;
+  m_rotation = 0.0;
+  m_branchNeckLength = 10;
+  m_branchStemLength = 10;
+  m_branchSpacing = 10;
+  m_branchStyle = BRANCHING_ATTACHMENT_NORMAL;
+
+  // Set up a default region. Much of the above will be put into
+  // the region eventually (the duplication is for compatibility)
+  wxShapeRegion *region = new wxShapeRegion;
+  m_regions.Append(region);
+  region->SetName("0");
+  region->SetFont(g_oglNormalFont);
+  region->SetFormatMode(FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT);
+  region->SetColour("BLACK");
+}
+
+wxShape::~wxShape()
+{
+  if (m_parent)
+    m_parent->GetChildren().DeleteObject(this);
+
+  ClearText();
+  ClearRegions();
+  ClearAttachments();
+
+  if (m_canvas)
+    m_canvas->RemoveShape(this);
+
+  GetEventHandler()->OnDelete();
+}
+
+void wxShape::SetHighlight(bool hi, bool recurse)
+{
+  m_highlighted = hi;
+  if (recurse)
+  {
+    wxNode *node = m_children.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      child->SetHighlight(hi, recurse);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::SetSensitivityFilter(int sens, bool recursive)
+{
+  if (sens & OP_DRAG_LEFT)
+    m_draggable = TRUE;
+  else
+    m_draggable = FALSE;
+
+  m_sensitivity = sens;
+  if (recursive)
+  {
+    wxNode *node = m_children.First();
+    while (node)
+    {
+      wxShape *obj = (wxShape *)node->Data();
+      obj->SetSensitivityFilter(sens, TRUE);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::SetDraggable(bool drag, bool recursive)
+{
+  m_draggable = drag;
+  if (m_draggable)
+    m_sensitivity |= OP_DRAG_LEFT;
+  else
+    if (m_sensitivity & OP_DRAG_LEFT)
+      m_sensitivity = m_sensitivity - OP_DRAG_LEFT;
+
+  if (recursive)
+  {
+    wxNode *node = m_children.First();
+    while (node)
+    {
+      wxShape *obj = (wxShape *)node->Data();
+      obj->SetDraggable(drag, TRUE);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::SetDrawHandles(bool drawH)
+{
+  m_drawHandles = drawH;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *obj = (wxShape *)node->Data();
+    obj->SetDrawHandles(drawH);
+    node = node->Next();
+  }
+}
+
+void wxShape::SetShadowMode(int mode, bool redraw)
+{
+  if (redraw && GetCanvas())
+  {
+    wxClientDC dc(GetCanvas());
+    GetCanvas()->PrepareDC(dc);
+    Erase(dc);
+
+    m_shadowMode = mode;
+
+    Draw(dc);
+  }
+  else
+  {
+    m_shadowMode = mode;
+  }
+}
+
+void wxShape::SetCanvas(wxShapeCanvas *theCanvas)
+{
+  m_canvas = theCanvas;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    child->SetCanvas(theCanvas);
+    node = node->Next();
+  }
+}
+
+void wxShape::AddToCanvas(wxShapeCanvas *theCanvas, wxShape *addAfter)
+{
+  theCanvas->AddShape(this, addAfter);
+  wxNode *node = m_children.First();
+  wxShape *lastImage = this;
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    object->AddToCanvas(theCanvas, lastImage);
+    lastImage = object;
+
+    node = node->Next();
+  }
+}
+
+// Insert at front of canvas
+void wxShape::InsertInCanvas(wxShapeCanvas *theCanvas)
+{
+  theCanvas->InsertShape(this);
+  wxNode *node = m_children.First();
+  wxShape *lastImage = this;
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    object->AddToCanvas(theCanvas, lastImage);
+    lastImage = object;
+
+    node = node->Next();
+  }
+}
+
+void wxShape::RemoveFromCanvas(wxShapeCanvas *theCanvas)
+{
+  if (Selected())
+    Select(FALSE);
+  theCanvas->RemoveShape(this);
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    object->RemoveFromCanvas(theCanvas);
+
+    node = node->Next();
+  }
+}
+
+void wxShape::ClearAttachments()
+{
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    delete point;
+    node = node->Next();
+  }
+  m_attachmentPoints.Clear();
+}
+
+void wxShape::ClearText(int regionId)
+{
+  if (regionId == 0)
+  {
+    m_text.DeleteContents(TRUE);
+    m_text.Clear();
+    m_text.DeleteContents(FALSE);
+  }
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->ClearText();
+}
+
+void wxShape::ClearRegions()
+{
+  wxNode *node = m_regions.First();
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    wxNode *next = node->Next();
+    delete region;
+    delete node;
+    node = next;
+  }
+}
+
+void wxShape::AddRegion(wxShapeRegion *region)
+{
+  m_regions.Append(region);
+}
+
+void wxShape::SetDefaultRegionSize()
+{
+  wxNode *node = m_regions.First();
+  if (!node) return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  double w, h;
+  GetBoundingBoxMin(&w, &h);
+  region->SetSize(w, h);
+}
+
+bool wxShape::HitTest(double x, double y, int *attachment, double *distance)
+{
+//  if (!sensitive)
+//    return FALSE;
+
+  double width = 0.0, height = 0.0;
+  GetBoundingBoxMin(&width, &height);
+  if (fabs(width) < 4.0) width = 4.0;
+  if (fabs(height) < 4.0) height = 4.0;
+
+  width += (double)4.0; height += (double)4.0; // Allowance for inaccurate mousing
+
+  double left = (double)(m_xpos - (width/2.0));
+  double top = (double)(m_ypos - (height/2.0));
+  double right = (double)(m_xpos + (width/2.0));
+  double bottom = (double)(m_ypos + (height/2.0));
+
+  int nearest_attachment = 0;
+
+  // If within the bounding box, check the attachment points
+  // within the object.
+
+  if (x >= left && x <= right && y >= top && y <= bottom)
+  {
+    int n = GetNumberOfAttachments();
+    double nearest = 999999.0;
+
+    // GetAttachmentPosition[Edge] takes a logical attachment position,
+    // i.e. if it's rotated through 90%, position 0 is East-facing.
+
+    for (int i = 0; i < n; i++)
+    {
+      double xp, yp;
+      if (GetAttachmentPositionEdge(i, &xp, &yp))
+      {
+        double l = (double)sqrt(((xp - x) * (xp - x)) +
+                   ((yp - y) * (yp - y)));
+
+        if (l < nearest)
+        {
+          nearest = l;
+          nearest_attachment = i;
+        }
+      }
+    }
+    *attachment = nearest_attachment;
+    *distance = nearest;
+    return TRUE;
+  }
+  else return FALSE;
+}
+
+// Format a text string according to the region size, adding
+// strings with positions to region text list
+
+static bool GraphicsInSizeToContents = FALSE; // Infinite recursion elimination
+void wxShape::FormatText(wxDC& dc, const wxString& s, int i)
+{
+  double w, h;
+  ClearText(i);
+
+  if (m_regions.Number() < 1)
+    return;
+  wxNode *node = m_regions.Nth(i);
+  if (!node)
+    return;
+
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->SetText(s);
+  dc.SetFont(* region->GetFont());
+
+  region->GetSize(&w, &h);
+
+  wxStringList *stringList = oglFormatText(dc, s, (w-5), (h-5), region->GetFormatMode());
+  node = stringList->First();
+  while (node)
+  {
+    char *s = (char *)node->Data();
+    wxShapeTextLine *line = new wxShapeTextLine(0.0, 0.0, s);
+    region->GetFormattedText().Append((wxObject *)line);
+    node = node->Next();
+  }
+  delete stringList;
+  double actualW = w;
+  double 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) &&
+      (region->GetFormattedText().Number() > 0) &&
+      (m_regions.Number() == 1) && !GraphicsInSizeToContents)
+  {
+    oglGetCentredTextExtent(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, &actualW, &actualH);
+    if ((actualW+m_textMarginX != w ) || (actualH+m_textMarginY != h))
+    {
+      // If we are a descendant of a composite, must make sure the composite gets
+      // resized properly
+      wxShape *topAncestor = GetTopAncestor();
+
+      if (topAncestor != this)
+      {
+        // Make sure we don't recurse infinitely
+        GraphicsInSizeToContents = TRUE;
+
+        wxCompositeShape *composite = (wxCompositeShape *)topAncestor;
+        composite->Erase(dc);
+        SetSize(actualW+m_textMarginX, actualH+m_textMarginY);
+        Move(dc, m_xpos, m_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);
+
+        GraphicsInSizeToContents = FALSE;
+      }
+      else
+      {
+        Erase(dc);
+        SetSize(actualW+m_textMarginX, actualH+m_textMarginY);
+        Move(dc, m_xpos, m_ypos);
+      }
+      SetSize(actualW+m_textMarginX, actualH+m_textMarginY);
+      Move(dc, m_xpos, m_ypos);
+      EraseContents(dc);
+    }
+  }
+  oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, actualW, actualH, region->GetFormatMode());
+  m_formatted = TRUE;
+}
+
+void wxShape::Recentre(wxDC& dc)
+{
+  double w, h;
+  GetBoundingBoxMin(&w, &h);
+
+  int noRegions = m_regions.Number();
+  for (int i = 0; i < noRegions; i++)
+  {
+    wxNode *node = m_regions.Nth(i);
+    if (node)
+    {
+      wxShapeRegion *region = (wxShapeRegion *)node->Data();
+      oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, region->GetFormatMode());
+    }
+  }
+}
+
+bool wxShape::GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3)
+{
+  return FALSE;
+}
+
+void wxShape::SetPen(wxPen *the_pen)
+{
+  m_pen = the_pen;
+}
+
+void wxShape::SetBrush(wxBrush *the_brush)
+{
+  m_brush = the_brush;
+}
+
+// Get the top-most (non-division) ancestor, or self
+wxShape *wxShape::GetTopAncestor()
+{
+  if (!GetParent())
+    return this;
+
+  if (GetParent()->IsKindOf(CLASSINFO(wxDivisionShape)))
+    return this;
+  else return GetParent()->GetTopAncestor();
+}
+
+/*
+ * Region functions
+ *
+ */
+void wxShape::SetFont(wxFont *the_font, int regionId)
+{
+  m_font = the_font;
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->SetFont(the_font);
+}
+
+wxFont *wxShape::GetFont(int n) const
+{
+  wxNode *node = m_regions.Nth(n);
+  if (!node)
+    return NULL;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  return region->GetFont();
+}
+
+void wxShape::SetFormatMode(int mode, int regionId)
+{
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->SetFormatMode(mode);
+}
+
+int wxShape::GetFormatMode(int regionId) const
+{
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return 0;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  return region->GetFormatMode();
+}
+
+void wxShape::SetTextColour(const wxString& the_colour, int regionId)
+{
+  wxColour *wxcolour = wxTheColourDatabase->FindColour(the_colour);
+  m_textColour = wxcolour;
+  m_textColourName = the_colour;
+
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->SetColour(the_colour);
+}
+
+wxString wxShape::GetTextColour(int regionId) const
+{
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return wxString("");
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  return region->GetColour();
+}
+
+void wxShape::SetRegionName(const wxString& name, int regionId)
+{
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->SetName(name);
+}
+
+wxString wxShape::GetRegionName(int regionId)
+{
+  wxNode *node = m_regions.Nth(regionId);
+  if (!node)
+    return wxString("");
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  return region->GetName();
+}
+
+int wxShape::GetRegionId(const wxString& name)
+{
+  wxNode *node = m_regions.First();
+  int i = 0;
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    if (region->GetName() == name)
+      return i;
+    node = node->Next();
+    i ++;
+  }
+  return -1;
+}
+
+// Name all m_regions in all subimages recursively.
+void wxShape::NameRegions(const wxString& parentName)
+{
+  int n = GetNumberOfTextRegions();
+  char buf[100];
+  for (int i = 0; i < n; i++)
+  {
+    if (parentName.Length() > 0)
+      sprintf(buf, "%s.%d", (const char*) parentName, i);
+    else
+      sprintf(buf, "%d", i);
+    SetRegionName(buf, i);
+  }
+  wxNode *node = m_children.First();
+  int j = 0;
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    if (parentName.Length() > 0)
+      sprintf(buf, "%s.%d", (const char*) parentName, j);
+    else
+      sprintf(buf, "%d", j);
+    child->NameRegions(buf);
+    node = node->Next();
+    j ++;
+  }
+}
+
+// Get a region by name, possibly looking recursively into composites.
+wxShape *wxShape::FindRegion(const wxString& name, int *regionId)
+{
+  int id = GetRegionId(name);
+  if (id > -1)
+  {
+    *regionId = id;
+    return this;
+  }
+
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    wxShape *actualImage = child->FindRegion(name, regionId);
+    if (actualImage)
+      return actualImage;
+    node = node->Next();
+  }
+  return NULL;
+}
+
+// Finds all region names for this image (composite or simple).
+// Supply empty string list.
+void wxShape::FindRegionNames(wxStringList& list)
+{
+  int n = GetNumberOfTextRegions();
+  for (int i = 0; i < n; i++)
+  {
+    wxString name(GetRegionName(i));
+    list.Add((const char*) name);
+  }
+
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    child->FindRegionNames(list);
+    node = node->Next();
+  }
+}
+
+void wxShape::AssignNewIds()
+{
+//  if (m_id == 0)
+  m_id = wxNewId();
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    child->AssignNewIds();
+    node = node->Next();
+  }
+}
+
+void wxShape::OnDraw(wxDC& dc)
+{
+}
+
+void wxShape::OnMoveLinks(wxDC& dc)
+{
+  // Want to set the ends of all attached links
+  // to point to/from this object
+
+  wxNode *current = m_lines.First();
+  while (current)
+  {
+    wxLineShape *line = (wxLineShape *)current->Data();
+    line->GetEventHandler()->OnMoveLink(dc);
+    current = current->Next();
+  }
+}
+
+
+void wxShape::OnDrawContents(wxDC& dc)
+{
+  double bound_x, bound_y;
+  GetBoundingBoxMin(&bound_x, &bound_y);
+    if (m_regions.Number() < 1) return;
+
+    if (m_pen) dc.SetPen(* m_pen);
+
+    wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
+    if (region->GetFont()) dc.SetFont(* region->GetFont());
+
+    dc.SetTextForeground(* (region->GetActualColourObject()));
+    dc.SetBackgroundMode(wxTRANSPARENT);
+    if (!m_formatted)
+    {
+      oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, bound_x, bound_y, region->GetFormatMode());
+      m_formatted = TRUE;
+    }
+    if (!GetDisableLabel())
+    {
+      oglDrawFormattedText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, bound_x, bound_y, region->GetFormatMode());
+    }
+}
+
+void wxShape::DrawContents(wxDC& dc)
+{
+  GetEventHandler()->OnDrawContents(dc);
+}
+
+void wxShape::OnSize(double x, double y)
+{
+}
+
+bool wxShape::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
+{
+  return TRUE;
+}
+
+void wxShape::OnMovePost(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
+{
+}
+
+void wxShape::OnErase(wxDC& dc)
+{
+  if (!m_visible)
+    return;
+
+  // Erase links
+  wxNode *current = m_lines.First();
+  while (current)
+  {
+    wxLineShape *line = (wxLineShape *)current->Data();
+    line->GetEventHandler()->OnErase(dc);
+    current = current->Next();
+  }
+  GetEventHandler()->OnEraseContents(dc);
+}
+
+void wxShape::OnEraseContents(wxDC& dc)
+{
+  if (!m_visible)
+    return;
+
+  double maxX, maxY, minX, minY;
+  double xp = GetX();
+  double yp = GetY();
+  GetBoundingBoxMin(&minX, &minY);
+  GetBoundingBoxMax(&maxX, &maxY);
+  double topLeftX = (double)(xp - (maxX / 2.0) - 2.0);
+  double topLeftY = (double)(yp - (maxY / 2.0) - 2.0);
+
+    int penWidth = 0;
+    if (m_pen)
+      penWidth = m_pen->GetWidth();
+
+    dc.SetPen(* g_oglWhiteBackgroundPen);
+    dc.SetBrush(* g_oglWhiteBackgroundBrush);
+    dc.DrawRectangle(WXROUND(topLeftX - penWidth), WXROUND(topLeftY - penWidth),
+                      WXROUND(maxX + penWidth*2.0 + 4.0), WXROUND(maxY + penWidth*2.0 + 4.0));
+}
+
+void wxShape::EraseLinks(wxDC& dc, int attachment, bool recurse)
+{
+  if (!m_visible)
+    return;
+
+  wxNode *current = m_lines.First();
+  while (current)
+  {
+    wxLineShape *line = (wxLineShape *)current->Data();
+    if (attachment == -1 || ((line->GetTo() == this && line->GetAttachmentTo() == attachment) ||
+                             (line->GetFrom() == this && line->GetAttachmentFrom() == attachment)))
+      line->GetEventHandler()->OnErase(dc);
+    current = current->Next();
+  }
+  if (recurse)
+  {
+    wxNode *node = m_children.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      child->EraseLinks(dc, attachment, recurse);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::DrawLinks(wxDC& dc, int attachment, bool recurse)
+{
+  if (!m_visible)
+    return;
+
+  wxNode *current = m_lines.First();
+  while (current)
+  {
+    wxLineShape *line = (wxLineShape *)current->Data();
+    if (attachment == -1 ||
+        (line->GetTo() == this && line->GetAttachmentTo() == attachment) ||
+        (line->GetFrom() == this && line->GetAttachmentFrom() == attachment))
+      line->Draw(dc);
+    current = current->Next();
+  }
+  if (recurse)
+  {
+    wxNode *node = m_children.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      child->DrawLinks(dc, attachment, recurse);
+      node = node->Next();
+    }
+  }
+}
+
+// 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.
+bool wxShape::AttachmentSortTest(int attachmentPoint, const wxRealPoint& pt1, const wxRealPoint& pt2)
+{
+    int physicalAttachment = LogicalToPhysicalAttachment(attachmentPoint);
+    switch (physicalAttachment)
+    {
+        case 0:
+        case 2:
+        {
+          return (pt1.x <= pt2.x) ;
+          break;
+        }
+        case 1:
+        case 3:
+        {
+          return (pt1.y <= pt2.y) ;
+          break;
+        }
+    }
+
+    return FALSE;
+}
+
+bool wxShape::MoveLineToNewAttachment(wxDC& dc, wxLineShape *to_move,
+                                       double x, double y)
+{
+  if (GetAttachmentMode() == ATTACHMENT_MODE_NONE)
+      return FALSE;
+
+  int newAttachment, oldAttachment;
+  double distance;
+
+  // Is (x, y) on this object? If so, find the new attachment point
+  // the user has moved the point to
+  bool hit = HitTest(x, y, &newAttachment, &distance);
+  if (!hit)
+    return FALSE;
+
+  EraseLinks(dc);
+
+  if (to_move->GetTo() == this)
+    oldAttachment = to_move->GetAttachmentTo();
+  else
+    oldAttachment = to_move->GetAttachmentFrom();
+
+  // The links in a new ordering.
+  wxList newOrdering;
+
+  // First, add all links to the new list.
+  wxNode *node = m_lines.First();
+  while (node)
+  {
+    newOrdering.Append(node->Data());
+    node = node->Next();
+  }
+
+  // Delete the line object from the list of links; we're going to move
+  // it to another position in the list
+  newOrdering.DeleteObject(to_move);
+
+  double old_x = (double) -99999.9;
+  double old_y = (double) -99999.9;
+
+  node = newOrdering.First();
+  bool found = FALSE;
+
+  while (!found && node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    if ((line->GetTo() == this && oldAttachment == line->GetAttachmentTo()) ||
+        (line->GetFrom() == this && oldAttachment == line->GetAttachmentFrom()))
+    {
+      double startX, startY, endX, endY;
+      double xp, yp;
+      line->GetEnds(&startX, &startY, &endX, &endY);
+      if (line->GetTo() == this)
+      {
+        xp = endX;
+        yp = endY;
+      } else
+      {
+        xp = startX;
+        yp = startY;
+      }
+
+      wxRealPoint thisPoint(xp, yp);
+      wxRealPoint lastPoint(old_x, old_y);
+      wxRealPoint newPoint(x, y);
+
+      if (AttachmentSortTest(newAttachment, newPoint, thisPoint) && AttachmentSortTest(newAttachment, lastPoint, newPoint))
+      {
+        found = TRUE;
+        newOrdering.Insert(node, to_move);
+      }
+
+      old_x = xp;
+      old_y = yp;
+    }
+    node = node->Next();
+  }
+
+  if (!found)
+    newOrdering.Append(to_move);
+
+  GetEventHandler()->OnChangeAttachment(newAttachment, to_move, newOrdering);
+
+  return TRUE;
+}
+
+void wxShape::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering)
+{
+    if (line->GetTo() == this)
+        line->SetAttachmentTo(attachment);
+    else
+        line->SetAttachmentFrom(attachment);
+
+    ApplyAttachmentOrdering(ordering);
+
+    wxClientDC dc(GetCanvas());
+    GetCanvas()->PrepareDC(dc);
+
+    MoveLinks(dc);
+
+    if (!GetCanvas()->GetQuickEditMode()) GetCanvas()->Redraw(dc);
+}
+
+// Reorders the lines according to the given list.
+void wxShape::ApplyAttachmentOrdering(wxList& linesToSort)
+{
+  // This is a temporary store of all the lines.
+  wxList linesStore;
+
+  wxNode *node = m_lines.First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    linesStore.Append(line);
+    node = node->Next();;
+  }
+
+  m_lines.Clear();
+
+  node = linesToSort.First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    if (linesStore.Member(line))
+    {
+      // Done this one
+      linesStore.DeleteObject(line);
+      m_lines.Append(line);
+    }
+    node = node->Next();
+  }
+
+  // Now add any lines that haven't been listed in linesToSort.
+  node = linesStore.First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    m_lines.Append(line);
+    node = node->Next();
+  }
+}
+
+// Reorders 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.
+void wxShape::SortLines(int attachment, wxList& linesToSort)
+{
+  // This is a temporary store of all the lines at this attachment
+  // point. We'll tick them off as we've processed them.
+  wxList linesAtThisAttachment;
+
+  wxNode *node = m_lines.First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    wxNode *next = node->Next();
+    if ((line->GetTo() == this && line->GetAttachmentTo() == attachment) ||
+        (line->GetFrom() == this && line->GetAttachmentFrom() == attachment))
+    {
+      linesAtThisAttachment.Append(line);
+      delete node;
+      node = next;
+    }
+    else node = node->Next();
+  }
+
+  node = linesToSort.First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    if (linesAtThisAttachment.Member(line))
+    {
+      // Done this one
+      linesAtThisAttachment.DeleteObject(line);
+      m_lines.Append(line);
+    }
+    node = node->Next();
+  }
+
+  // Now add any lines that haven't been listed in linesToSort.
+  node = linesAtThisAttachment.First();
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+    m_lines.Append(line);
+    node = node->Next();
+  }
+}
+
+void wxShape::OnHighlight(wxDC& dc)
+{
+}
+
+void wxShape::OnLeftClick(double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_CLICK_LEFT) != OP_CLICK_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnLeftClick(x, y, keys, attachment);
+    }
+    return;
+  }
+}
+
+void wxShape::OnRightClick(double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_CLICK_RIGHT) != OP_CLICK_RIGHT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
+    }
+    return;
+  }
+}
+
+double DragOffsetX = 0.0;
+double DragOffsetY = 0.0;
+
+void wxShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+  double xx, yy;
+  xx = x + DragOffsetX;
+  yy = y + DragOffsetY;
+
+  m_canvas->Snap(&xx, &yy);
+//  m_xpos = xx; m_ypos = yy;
+  double w, h;
+  GetBoundingBoxMax(&w, &h);
+  GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
+}
+
+void wxShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
+    }
+    return;
+  }
+
+  DragOffsetX = m_xpos - x;
+  DragOffsetY = m_ypos - y;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  // New policy: don't erase shape until end of drag.
+//  Erase(dc);
+
+  double xx, yy;
+  xx = x + DragOffsetX;
+  yy = y + DragOffsetY;
+  m_canvas->Snap(&xx, &yy);
+//  m_xpos = xx; m_ypos = yy;
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  double w, h;
+  GetBoundingBoxMax(&w, &h);
+  GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
+  m_canvas->CaptureMouse();
+}
+
+void wxShape::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+  m_canvas->ReleaseMouse();
+  if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(wxCOPY);
+
+  double xx = x + DragOffsetX;
+  double yy = y + DragOffsetY;
+  m_canvas->Snap(&xx, &yy);
+//  canvas->Snap(&m_xpos, &m_ypos);
+
+  // New policy: erase shape at end of drag.
+  Erase(dc);
+
+  Move(dc, xx, yy);
+  if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+
+void wxShape::OnDragRight(bool draw, double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnDragRight(draw, x, y, keys, attachment);
+    }
+    return;
+  }
+}
+
+void wxShape::OnBeginDragRight(double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnBeginDragRight(x, y, keys, attachment);
+    }
+    return;
+  }
+}
+
+void wxShape::OnEndDragRight(double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnEndDragRight(x, y, keys, attachment);
+    }
+    return;
+  }
+}
+
+void wxShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
+{
+  double top_left_x = (double)(x - w/2.0);
+  double top_left_y = (double)(y - h/2.0);
+  double top_right_x = (double)(top_left_x + w);
+  double top_right_y = (double)top_left_y;
+  double bottom_left_x = (double)top_left_x;
+  double bottom_left_y = (double)(top_left_y + h);
+  double bottom_right_x = (double)top_right_x;
+  double bottom_right_y = (double)bottom_left_y;
+
+  wxPoint points[5];
+  points[0].x = WXROUND(top_left_x); points[0].y = WXROUND(top_left_y);
+  points[1].x = WXROUND(top_right_x); points[1].y = WXROUND(top_right_y);
+  points[2].x = WXROUND(bottom_right_x); points[2].y = WXROUND(bottom_right_y);
+  points[3].x = WXROUND(bottom_left_x); points[3].y = WXROUND(bottom_left_y);
+  points[4].x = WXROUND(top_left_x); points[4].y = WXROUND(top_left_y);
+
+  dc.DrawLines(5, points);
+}
+
+void wxShape::Attach(wxShapeCanvas *can)
+{
+  m_canvas = can;
+}
+
+void wxShape::Detach()
+{
+  m_canvas = NULL;
+}
+
+void wxShape::Move(wxDC& dc, double x, double y, bool display)
+{
+  double old_x = m_xpos;
+  double old_y = m_ypos;
+
+  if (!GetEventHandler()->OnMovePre(dc, x, y, old_x, old_y, display))
+  {
+//    m_xpos = old_x;
+//    m_ypos = old_y;
+    return;
+  }
+
+  m_xpos = x; m_ypos = y;
+
+  ResetControlPoints();
+
+  if (display)
+    Draw(dc);
+
+  MoveLinks(dc);
+
+  GetEventHandler()->OnMovePost(dc, x, y, old_x, old_y, display);
+}
+
+void wxShape::MoveLinks(wxDC& dc)
+{
+  GetEventHandler()->OnMoveLinks(dc);
+}
+
+
+void wxShape::Draw(wxDC& dc)
+{
+  if (m_visible)
+  {
+    GetEventHandler()->OnDraw(dc);
+    GetEventHandler()->OnDrawContents(dc);
+    GetEventHandler()->OnDrawControlPoints(dc);
+    GetEventHandler()->OnDrawBranches(dc);
+  }
+}
+
+void wxShape::Flash()
+{
+    if (GetCanvas())
+    {
+        wxClientDC dc(GetCanvas());
+        GetCanvas()->PrepareDC(dc);
+
+        dc.SetLogicalFunction(OGLRBLF);
+        Draw(dc);
+        dc.SetLogicalFunction(wxCOPY);
+        Draw(dc);
+    }
+}
+
+void wxShape::Show(bool show)
+{
+  m_visible = show;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *image = (wxShape *)node->Data();
+    image->Show(show);
+    node = node->Next();
+  }
+}
+
+void wxShape::Erase(wxDC& dc)
+{
+  GetEventHandler()->OnErase(dc);
+  GetEventHandler()->OnEraseControlPoints(dc);
+  GetEventHandler()->OnDrawBranches(dc, TRUE);
+}
+
+void wxShape::EraseContents(wxDC& dc)
+{
+  GetEventHandler()->OnEraseContents(dc);
+}
+
+void wxShape::AddText(const wxString& string)
+{
+  wxNode *node = m_regions.First();
+  if (!node)
+    return;
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->ClearText();
+  wxShapeTextLine *new_line =
+      new wxShapeTextLine(0.0, 0.0, string);
+  region->GetFormattedText().Append(new_line);
+
+  m_formatted = FALSE;
+}
+
+void wxShape::SetSize(double x, double y, bool recursive)
+{
+  SetAttachmentSize(x, y);
+  SetDefaultRegionSize();
+}
+
+void wxShape::SetAttachmentSize(double w, double h)
+{
+  double scaleX;
+  double scaleY;
+  double width, height;
+  GetBoundingBoxMin(&width, &height);
+  if (width == 0.0)
+    scaleX = 1.0;
+  else scaleX = w/width;
+  if (height == 0.0)
+    scaleY = 1.0;
+  else scaleY = h/height;
+
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    point->m_x = (double)(point->m_x * scaleX);
+    point->m_y = (double)(point->m_y * scaleY);
+    node = node->Next();
+  }
+}
+
+// Add line FROM this object
+void wxShape::AddLine(wxLineShape *line, wxShape *other,
+                            int attachFrom, int attachTo,
+                            // The line ordering
+                            int positionFrom, int positionTo)
+{
+    if (positionFrom == -1)
+    {
+        if (!m_lines.Member(line))
+            m_lines.Append(line);
+    }
+    else
+    {
+        // Don't preserve old ordering if we have new ordering instructions
+        m_lines.DeleteObject(line);
+        if (positionFrom < m_lines.Number())
+        {
+            wxNode* node = m_lines.Nth(positionFrom);
+            m_lines.Insert(node, line);
+        }
+        else
+            m_lines.Append(line);
+    }
+
+    if (positionTo == -1)
+    {
+        if (!other->m_lines.Member(line))
+            other->m_lines.Append(line);
+    }
+    else
+    {
+        // Don't preserve old ordering if we have new ordering instructions
+        other->m_lines.DeleteObject(line);
+        if (positionTo < other->m_lines.Number())
+        {
+            wxNode* node = other->m_lines.Nth(positionTo);
+            other->m_lines.Insert(node, line);
+        }
+        else
+            other->m_lines.Append(line);
+    }
+#if 0
+    // Wrong: doesn't preserve ordering of shape already linked
+    m_lines.DeleteObject(line);
+    other->m_lines.DeleteObject(line);
+
+    if (positionFrom == -1)
+        m_lines.Append(line);
+    else
+    {
+        if (positionFrom < m_lines.Number())
+        {
+            wxNode* node = m_lines.Nth(positionFrom);
+            m_lines.Insert(node, line);
+        }
+        else
+            m_lines.Append(line);
+    }
+
+    if (positionTo == -1)
+        other->m_lines.Append(line);
+    else
+    {
+        if (positionTo < other->m_lines.Number())
+        {
+            wxNode* node = other->m_lines.Nth(positionTo);
+            other->m_lines.Insert(node, line);
+        }
+        else
+            other->m_lines.Append(line);
+    }
+#endif
+
+    line->SetFrom(this);
+    line->SetTo(other);
+    line->SetAttachments(attachFrom, attachTo);
+}
+
+void wxShape::RemoveLine(wxLineShape *line)
+{
+  if (line->GetFrom() == this)
+    line->GetTo()->m_lines.DeleteObject(line);
+  else
+   line->GetFrom()->m_lines.DeleteObject(line);
+
+  m_lines.DeleteObject(line);
+}
+
+#ifdef PROLOGIO
+void wxShape::WriteAttributes(wxExpr *clause)
+{
+  clause->AddAttributeValueString("type", GetClassInfo()->GetClassName());
+  clause->AddAttributeValue("id", m_id);
+
+  if (m_pen)
+  {
+    int penWidth = m_pen->GetWidth();
+    int penStyle = m_pen->GetStyle();
+    if (penWidth != 1)
+      clause->AddAttributeValue("pen_width", (long)penWidth);
+    if (penStyle != wxSOLID)
+      clause->AddAttributeValue("pen_style", (long)penStyle);
+
+    wxString penColour = wxTheColourDatabase->FindName(m_pen->GetColour());
+    if (penColour == "")
+    {
+      wxString hex(oglColourToHex(m_pen->GetColour()));
+      hex = wxString("#") + hex;
+      clause->AddAttributeValueString("pen_colour", hex);
+    }
+    else if (penColour != "BLACK")
+      clause->AddAttributeValueString("pen_colour", penColour);
+  }
+
+  if (m_brush)
+  {
+    wxString brushColour = wxTheColourDatabase->FindName(m_brush->GetColour());
+
+    if (brushColour == "")
+    {
+      wxString hex(oglColourToHex(m_brush->GetColour()));
+      hex = wxString("#") + hex;
+      clause->AddAttributeValueString("brush_colour", hex);
+    }
+    else if (brushColour != "WHITE")
+      clause->AddAttributeValueString("brush_colour", brushColour);
+
+    if (m_brush->GetStyle() != wxSOLID)
+      clause->AddAttributeValue("brush_style", (long)m_brush->GetStyle());
+  }
+
+  // Output line ids
+
+  int n_lines = m_lines.Number();
+  if (n_lines > 0)
+  {
+    wxExpr *list = new wxExpr(wxExprList);
+    wxNode *node = m_lines.First();
+    while (node)
+    {
+      wxShape *line = (wxShape *)node->Data();
+      wxExpr *id_expr = new wxExpr(line->GetId());
+      list->Append(id_expr);
+      node = node->Next();
+    }
+    clause->AddAttributeValue("arcs", list);
+  }
+
+  // Miscellaneous members
+  if (m_attachmentMode != 0)
+    clause->AddAttributeValue("use_attachments", (long)m_attachmentMode);
+  if (m_sensitivity != OP_ALL)
+    clause->AddAttributeValue("sensitivity", (long)m_sensitivity);
+  if (!m_spaceAttachments)
+    clause->AddAttributeValue("space_attachments", (long)m_spaceAttachments);
+  if (m_fixedWidth)
+    clause->AddAttributeValue("fixed_width", (long)m_fixedWidth);
+  if (m_fixedHeight)
+    clause->AddAttributeValue("fixed_height", (long)m_fixedHeight);
+  if (m_shadowMode != SHADOW_NONE)
+    clause->AddAttributeValue("shadow_mode", (long)m_shadowMode);
+  if (m_centreResize != TRUE)
+    clause->AddAttributeValue("centre_resize", (long)0);
+  clause->AddAttributeValue("maintain_aspect_ratio", (long) m_maintainAspectRatio);
+  if (m_highlighted != FALSE)
+    clause->AddAttributeValue("hilite", (long)m_highlighted);
+
+  if (m_parent) // For composite objects
+    clause->AddAttributeValue("parent", (long)m_parent->GetId());
+
+  if (m_rotation != 0.0)
+    clause->AddAttributeValue("rotation", m_rotation);
+
+  if (!this->IsKindOf(CLASSINFO(wxLineShape)))
+  {
+    clause->AddAttributeValue("neck_length", (long) m_branchNeckLength);
+    clause->AddAttributeValue("stem_length", (long) m_branchStemLength);
+    clause->AddAttributeValue("branch_spacing", (long) m_branchSpacing);
+    clause->AddAttributeValue("branch_style", (long) m_branchStyle);
+  }
+
+  // Write user-defined attachment points, if any
+  if (m_attachmentPoints.Number() > 0)
+  {
+    wxExpr *attachmentList = new wxExpr(wxExprList);
+    wxNode *node = m_attachmentPoints.First();
+    while (node)
+    {
+      wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+      wxExpr *pointExpr = new wxExpr(wxExprList);
+      pointExpr->Append(new wxExpr((long)point->m_id));
+      pointExpr->Append(new wxExpr(point->m_x));
+      pointExpr->Append(new wxExpr(point->m_y));
+      attachmentList->Append(pointExpr);
+      node = node->Next();
+    }
+    clause->AddAttributeValue("user_attachments", attachmentList);
+  }
+
+  // Write text regions
+  WriteRegions(clause);
+}
+
+void wxShape::WriteRegions(wxExpr *clause)
+{
+  // Output regions as region1 = (...), region2 = (...), etc
+  // and formatted text as text1 = (...), text2 = (...) etc.
+  int regionNo = 1;
+  char regionNameBuf[20];
+  char textNameBuf[20];
+  wxNode *node = m_regions.First();
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    sprintf(regionNameBuf, "region%d", regionNo);
+    sprintf(textNameBuf, "text%d", regionNo);
+
+    // Original text and region attributes:
+    // region1 = (regionName regionText x y width height minWidth minHeight proportionX proportionY
+    //            formatMode fontSize fontFamily fontStyle fontWeight textColour)
+    wxExpr *regionExpr = new wxExpr(wxExprList);
+    regionExpr->Append(new wxExpr(wxExprString, region->m_regionName));
+    regionExpr->Append(new wxExpr(wxExprString, region->m_regionText));
+
+    regionExpr->Append(new wxExpr(region->m_x));
+    regionExpr->Append(new wxExpr(region->m_y));
+    regionExpr->Append(new wxExpr(region->GetWidth()));
+    regionExpr->Append(new wxExpr(region->GetHeight()));
+
+    regionExpr->Append(new wxExpr(region->m_minWidth));
+    regionExpr->Append(new wxExpr(region->m_minHeight));
+    regionExpr->Append(new wxExpr(region->m_regionProportionX));
+    regionExpr->Append(new wxExpr(region->m_regionProportionY));
+
+    regionExpr->Append(new wxExpr((long)region->m_formatMode));
+
+    regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetPointSize() : 10)));
+    regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetFamily() : wxDEFAULT)));
+    regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetStyle() : wxDEFAULT)));
+    regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetWeight() : wxNORMAL)));
+    regionExpr->Append(new wxExpr(wxExprString, region->m_textColour));
+
+    // New members for pen colour/style
+    regionExpr->Append(new wxExpr(wxExprString, region->m_penColour));
+    regionExpr->Append(new wxExpr((long)region->m_penStyle));
+
+    // Formatted text:
+    // text1 = ((x y string) (x y string) ...)
+    wxExpr *textExpr = new wxExpr(wxExprList);
+
+    wxNode *textNode = region->m_formattedText.First();
+    while (textNode)
+    {
+      wxShapeTextLine *line = (wxShapeTextLine *)textNode->Data();
+      wxExpr *list2 = new wxExpr(wxExprList);
+      list2->Append(new wxExpr(line->GetX()));
+      list2->Append(new wxExpr(line->GetY()));
+      list2->Append(new wxExpr(wxExprString, line->GetText()));
+      textExpr->Append(list2);
+      textNode = textNode->Next();
+    }
+
+    // Now add both attributes to the clause
+    clause->AddAttributeValue(regionNameBuf, regionExpr);
+    clause->AddAttributeValue(textNameBuf, textExpr);
+
+    node = node->Next();
+    regionNo ++;
+  }
+}
+
+void wxShape::ReadAttributes(wxExpr *clause)
+{
+  clause->GetAttributeValue("id", m_id);
+  wxRegisterId(m_id);
+
+  clause->GetAttributeValue("x", m_xpos);
+  clause->GetAttributeValue("y", m_ypos);
+
+  // Input text strings (FOR COMPATIBILITY WITH OLD FILES ONLY. SEE REGION CODE BELOW.)
+  ClearText();
+  wxExpr *strings = clause->AttributeValue("text");
+  if (strings && strings->Type() == wxExprList)
+  {
+    m_formatted = TRUE;  // Assume text is formatted unless we prove otherwise
+    wxExpr *node = strings->value.first;
+    while (node)
+    {
+      wxExpr *string_expr = node;
+      double the_x = 0.0;
+      double the_y = 0.0;
+      wxString the_string("");
+
+      // string_expr can either be a string, or a list of
+      // 3 elements: x, y, and string.
+      if (string_expr->Type() == wxExprString)
+      {
+        the_string = string_expr->StringValue();
+        m_formatted = FALSE;
+      }
+      else if (string_expr->Type() == wxExprList)
+      {
+        wxExpr *first = string_expr->value.first;
+        wxExpr *second = first ? first->next : (wxExpr*) NULL;
+        wxExpr *third = second ? second->next : (wxExpr*) NULL;
+
+        if (first && second && third &&
+            (first->Type() == wxExprReal || first->Type() == wxExprInteger) &&
+            (second->Type() == wxExprReal || second->Type() == wxExprInteger) &&
+            third->Type() == wxExprString)
+          {
+            if (first->Type() == wxExprReal)
+              the_x = first->RealValue();
+            else the_x = (double)first->IntegerValue();
+
+            if (second->Type() == wxExprReal)
+              the_y = second->RealValue();
+            else the_y = (double)second->IntegerValue();
+
+            the_string = third->StringValue();
+          }
+      }
+      wxShapeTextLine *line =
+            new wxShapeTextLine(the_x, the_y, (char*) (const char*) the_string);
+      m_text.Append(line);
+
+      node = node->next;
+    }
+  }
+
+  wxString pen_string = "";
+  wxString brush_string = "";
+  int pen_width = 1;
+  int pen_style = wxSOLID;
+  int brush_style = wxSOLID;
+  m_attachmentMode = ATTACHMENT_MODE_NONE;
+
+  clause->GetAttributeValue("pen_colour", pen_string);
+  clause->GetAttributeValue("text_colour", m_textColourName);
+
+  SetTextColour(m_textColourName);
+
+  clause->GetAttributeValue("region_name", m_regionName);
+
+  clause->GetAttributeValue("brush_colour", brush_string);
+  clause->GetAttributeValue("pen_width", pen_width);
+  clause->GetAttributeValue("pen_style", pen_style);
+  clause->GetAttributeValue("brush_style", brush_style);
+
+  int iVal = (int) m_attachmentMode;
+  clause->GetAttributeValue("use_attachments", iVal);
+  m_attachmentMode = iVal;
+
+  clause->GetAttributeValue("sensitivity", m_sensitivity);
+
+  iVal = (int) m_spaceAttachments;
+  clause->GetAttributeValue("space_attachments", iVal);
+  m_spaceAttachments = (iVal != 0);
+
+  iVal = (int) m_fixedWidth;
+  clause->GetAttributeValue("fixed_width", iVal);
+  m_fixedWidth = (iVal != 0);
+
+  iVal = (int) m_fixedHeight;
+  clause->GetAttributeValue("fixed_height", iVal);
+  m_fixedHeight = (iVal != 0);
+
+  clause->GetAttributeValue("format_mode", m_formatMode);
+  clause->GetAttributeValue("shadow_mode", m_shadowMode);
+
+  iVal = m_branchNeckLength;
+  clause->GetAttributeValue("neck_length", iVal);
+  m_branchNeckLength = iVal;
+
+  iVal = m_branchStemLength;
+  clause->GetAttributeValue("stem_length", iVal);
+  m_branchStemLength = iVal;
+
+  iVal = m_branchSpacing;
+  clause->GetAttributeValue("branch_spacing", iVal);
+  m_branchSpacing = iVal;
+
+  clause->GetAttributeValue("branch_style", m_branchStyle);
+
+  iVal = (int) m_centreResize;
+  clause->GetAttributeValue("centre_resize", iVal);
+  m_centreResize = (iVal != 0);
+
+  iVal = (int) m_maintainAspectRatio;
+  clause->GetAttributeValue("maintain_aspect_ratio", iVal);
+  m_maintainAspectRatio = (iVal != 0);
+
+  iVal = (int) m_highlighted;
+  clause->GetAttributeValue("hilite", iVal);
+  m_highlighted = (iVal != 0);
+
+  clause->GetAttributeValue("rotation", m_rotation);
+
+  if (pen_string == "")
+    pen_string = "BLACK";
+  if (brush_string == "")
+    brush_string = "WHITE";
+
+  if (pen_string.GetChar(0) == '#')
+  {
+    wxColour col(oglHexToColour(pen_string.After('#')));
+    m_pen = wxThePenList->FindOrCreatePen(col, pen_width, pen_style);
+  }
+  else
+    m_pen = wxThePenList->FindOrCreatePen(pen_string, pen_width, pen_style);
+
+  if (!m_pen)
+    m_pen = wxBLACK_PEN;
+
+  if (brush_string.GetChar(0) == '#')
+  {
+    wxColour col(oglHexToColour(brush_string.After('#')));
+    m_brush = wxTheBrushList->FindOrCreateBrush(col, brush_style);
+  }
+  else
+    m_brush = wxTheBrushList->FindOrCreateBrush(brush_string, brush_style);
+
+  if (!m_brush)
+    m_brush = wxWHITE_BRUSH;
+
+  int point_size = 10;
+  clause->GetAttributeValue("point_size", point_size);
+  SetFont(oglMatchFont(point_size));
+
+  // Read user-defined attachment points, if any
+  wxExpr *attachmentList = clause->AttributeValue("user_attachments");
+  if (attachmentList)
+  {
+    wxExpr *pointExpr = attachmentList->GetFirst();
+    while (pointExpr)
+    {
+      wxExpr *idExpr = pointExpr->Nth(0);
+      wxExpr *xExpr = pointExpr->Nth(1);
+      wxExpr *yExpr = pointExpr->Nth(2);
+      if (idExpr && xExpr && yExpr)
+      {
+        wxAttachmentPoint *point = new wxAttachmentPoint;
+        point->m_id = (int)idExpr->IntegerValue();
+        point->m_x = xExpr->RealValue();
+        point->m_y = yExpr->RealValue();
+        m_attachmentPoints.Append((wxObject *)point);
+      }
+      pointExpr = pointExpr->GetNext();
+    }
+  }
+
+  // Read text regions
+  ReadRegions(clause);
+}
+
+void wxShape::ReadRegions(wxExpr *clause)
+{
+  ClearRegions();
+
+  // region1 = (regionName regionText x y width height minWidth minHeight proportionX proportionY
+  //            formatMode fontSize fontFamily fontStyle fontWeight textColour)
+  int regionNo = 1;
+  char regionNameBuf[20];
+  char textNameBuf[20];
+
+  wxExpr *regionExpr = NULL;
+  wxExpr *textExpr = NULL;
+  sprintf(regionNameBuf, "region%d", regionNo);
+  sprintf(textNameBuf, "text%d", regionNo);
+
+  m_formatted = TRUE;  // Assume text is formatted unless we prove otherwise
+
+  while ((regionExpr = clause->AttributeValue(regionNameBuf)))
+  {
+    /*
+     * Get the region information
+     *
+     */
+
+    wxString regionName("");
+    wxString regionText("");
+    double x = 0.0;
+    double y = 0.0;
+    double width = 0.0;
+    double height = 0.0;
+    double minWidth = 5.0;
+    double minHeight = 5.0;
+    double m_regionProportionX = -1.0;
+    double m_regionProportionY = -1.0;
+    int formatMode = FORMAT_NONE;
+    int fontSize = 10;
+    int fontFamily = wxSWISS;
+    int fontStyle = wxNORMAL;
+    int fontWeight = wxNORMAL;
+    wxString regionTextColour("");
+    wxString penColour("");
+    int penStyle = wxSOLID;
+
+    if (regionExpr->Type() == wxExprList)
+    {
+      wxExpr *nameExpr = regionExpr->Nth(0);
+      wxExpr *textExpr = regionExpr->Nth(1);
+      wxExpr *xExpr = regionExpr->Nth(2);
+      wxExpr *yExpr = regionExpr->Nth(3);
+      wxExpr *widthExpr = regionExpr->Nth(4);
+      wxExpr *heightExpr = regionExpr->Nth(5);
+      wxExpr *minWidthExpr = regionExpr->Nth(6);
+      wxExpr *minHeightExpr = regionExpr->Nth(7);
+      wxExpr *propXExpr = regionExpr->Nth(8);
+      wxExpr *propYExpr = regionExpr->Nth(9);
+      wxExpr *formatExpr = regionExpr->Nth(10);
+      wxExpr *sizeExpr = regionExpr->Nth(11);
+      wxExpr *familyExpr = regionExpr->Nth(12);
+      wxExpr *styleExpr = regionExpr->Nth(13);
+      wxExpr *weightExpr = regionExpr->Nth(14);
+      wxExpr *colourExpr = regionExpr->Nth(15);
+      wxExpr *penColourExpr = regionExpr->Nth(16);
+      wxExpr *penStyleExpr = regionExpr->Nth(17);
+
+      regionName = nameExpr->StringValue();
+      regionText = textExpr->StringValue();
+
+      x = xExpr->RealValue();
+      y = yExpr->RealValue();
+
+      width = widthExpr->RealValue();
+      height = heightExpr->RealValue();
+
+      minWidth = minWidthExpr->RealValue();
+      minHeight = minHeightExpr->RealValue();
+
+      m_regionProportionX = propXExpr->RealValue();
+      m_regionProportionY = propYExpr->RealValue();
+
+      formatMode = (int) formatExpr->IntegerValue();
+      fontSize = (int)sizeExpr->IntegerValue();
+      fontFamily = (int)familyExpr->IntegerValue();
+      fontStyle = (int)styleExpr->IntegerValue();
+      fontWeight = (int)weightExpr->IntegerValue();
+
+      if (colourExpr)
+      {
+        regionTextColour = colourExpr->StringValue();
+      }
+      else
+        regionTextColour = "BLACK";
+
+      if (penColourExpr)
+        penColour = penColourExpr->StringValue();
+      if (penStyleExpr)
+        penStyle = (int)penStyleExpr->IntegerValue();
+    }
+    wxFont *font = wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight);
+
+    wxShapeRegion *region = new wxShapeRegion;
+    region->SetProportions(m_regionProportionX, m_regionProportionY);
+    region->SetFont(font);
+    region->SetSize(width, height);
+    region->SetPosition(x, y);
+    region->SetMinSize(minWidth, minHeight);
+    region->SetFormatMode(formatMode);
+    region->SetPenStyle(penStyle);
+    if (penColour != "")
+      region->SetPenColour(penColour);
+
+    region->m_textColour = regionTextColour;
+    region->m_regionText = regionText;
+    region->m_regionName = regionName;
+
+    m_regions.Append(region);
+
+    /*
+     * Get the formatted text strings
+     *
+     */
+    textExpr = clause->AttributeValue(textNameBuf);
+    if (textExpr && (textExpr->Type() == wxExprList))
+    {
+      wxExpr *node = textExpr->value.first;
+      while (node)
+      {
+        wxExpr *string_expr = node;
+        double the_x = 0.0;
+        double the_y = 0.0;
+        wxString the_string("");
+
+        // string_expr can either be a string, or a list of
+        // 3 elements: x, y, and string.
+        if (string_expr->Type() == wxExprString)
+        {
+          the_string = string_expr->StringValue();
+          m_formatted = FALSE;
+        }
+        else if (string_expr->Type() == wxExprList)
+        {
+          wxExpr *first = string_expr->value.first;
+          wxExpr *second = first ? first->next : (wxExpr*) NULL;
+          wxExpr *third = second ? second->next : (wxExpr*) NULL;
+
+          if (first && second && third &&
+              (first->Type() == wxExprReal || first->Type() == wxExprInteger) &&
+              (second->Type() == wxExprReal || second->Type() == wxExprInteger) &&
+              third->Type() == wxExprString)
+          {
+            if (first->Type() == wxExprReal)
+              the_x = first->RealValue();
+            else the_x = (double)first->IntegerValue();
+
+            if (second->Type() == wxExprReal)
+              the_y = second->RealValue();
+            else the_y = (double)second->IntegerValue();
+
+            the_string = third->StringValue();
+          }
+        }
+        if (the_string)
+        {
+          wxShapeTextLine *line =
+              new wxShapeTextLine(the_x, the_y, (char*) (const char*) the_string);
+          region->m_formattedText.Append(line);
+        }
+        node = node->next;
+      }
+    }
+
+    regionNo ++;
+    sprintf(regionNameBuf, "region%d", regionNo);
+    sprintf(textNameBuf, "text%d", regionNo);
+  }
+
+  // Compatibility: check for no regions (old file).
+  // Lines and divided rectangles must deal with this compatibility
+  // theirselves. Composites _may_ not have any regions anyway.
+  if ((m_regions.Number() == 0) &&
+      !this->IsKindOf(CLASSINFO(wxLineShape)) && !this->IsKindOf(CLASSINFO(wxDividedShape)) &&
+      !this->IsKindOf(CLASSINFO(wxCompositeShape)))
+  {
+    wxShapeRegion *newRegion = new wxShapeRegion;
+    newRegion->SetName("0");
+    m_regions.Append((wxObject *)newRegion);
+    if (m_text.Number() > 0)
+    {
+      newRegion->ClearText();
+      wxNode *node = m_text.First();
+      while (node)
+      {
+        wxShapeTextLine *textLine = (wxShapeTextLine *)node->Data();
+        wxNode *next = node->Next();
+        newRegion->GetFormattedText().Append((wxObject *)textLine);
+        delete node;
+        node = next;
+      }
+    }
+  }
+}
+
+#endif
+
+void wxShape::Copy(wxShape& copy)
+{
+  copy.m_id = m_id;
+  copy.m_xpos = m_xpos;
+  copy.m_ypos = m_ypos;
+  copy.m_pen = m_pen;
+  copy.m_brush = m_brush;
+  copy.m_textColour = m_textColour;
+  copy.m_centreResize = m_centreResize;
+  copy.m_maintainAspectRatio = m_maintainAspectRatio;
+  copy.m_attachmentMode = m_attachmentMode;
+  copy.m_spaceAttachments = m_spaceAttachments;
+  copy.m_highlighted = m_highlighted;
+  copy.m_rotation = m_rotation;
+  copy.m_textColourName = m_textColourName;
+  copy.m_regionName = m_regionName;
+
+  copy.m_sensitivity = m_sensitivity;
+  copy.m_draggable = m_draggable;
+  copy.m_fixedWidth = m_fixedWidth;
+  copy.m_fixedHeight = m_fixedHeight;
+  copy.m_formatMode = m_formatMode;
+  copy.m_drawHandles = m_drawHandles;
+
+  copy.m_visible = m_visible;
+  copy.m_shadowMode = m_shadowMode;
+  copy.m_shadowOffsetX = m_shadowOffsetX;
+  copy.m_shadowOffsetY = m_shadowOffsetY;
+  copy.m_shadowBrush = m_shadowBrush;
+
+  copy.m_branchNeckLength = m_branchNeckLength;
+  copy.m_branchStemLength = m_branchStemLength;
+  copy.m_branchSpacing = m_branchSpacing;
+
+  // Copy text regions
+  copy.ClearRegions();
+  wxNode *node = m_regions.First();
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    wxShapeRegion *newRegion = new wxShapeRegion(*region);
+    copy.m_regions.Append(newRegion);
+    node = node->Next();
+  }
+
+  // Copy attachments
+  copy.ClearAttachments();
+  node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    wxAttachmentPoint *newPoint = new wxAttachmentPoint;
+    newPoint->m_id = point->m_id;
+    newPoint->m_x = point->m_x;
+    newPoint->m_y = point->m_y;
+    copy.m_attachmentPoints.Append((wxObject *)newPoint);
+    node = node->Next();
+  }
+
+  // Copy lines
+  copy.m_lines.Clear();
+  node = m_lines.First();
+  while (node)
+  {
+    wxLineShape* line = (wxLineShape*) node->Data();
+    copy.m_lines.Append(line);
+    node = node->Next();
+  }
+}
+
+// Create and return a new, fully copied object.
+wxShape *wxShape::CreateNewCopy(bool resetMapping, bool recompute)
+{
+  if (resetMapping)
+    oglObjectCopyMapping.Clear();
+
+  wxShape* newObject = (wxShape*) GetClassInfo()->CreateObject();
+
+  wxASSERT( (newObject != NULL) );
+  wxASSERT( (newObject->IsKindOf(CLASSINFO(wxShape))) );
+
+  Copy(*newObject);
+
+  if (GetEventHandler() != this)
+  {
+    wxShapeEvtHandler* newHandler = GetEventHandler()->CreateNewCopy();
+    newObject->SetEventHandler(newHandler);
+    newObject->SetPreviousHandler(NULL);
+    newHandler->SetPreviousHandler(newObject);
+    newHandler->SetShape(newObject);
+  }
+
+  if (recompute)
+    newObject->Recompute();
+  return newObject;
+}
+
+// Does the copying for this object, including copying event
+// handler data if any. Calls the virtual Copy function.
+void wxShape::CopyWithHandler(wxShape& copy)
+{
+    Copy(copy);
+
+    if (GetEventHandler() != this)
+    {
+        wxASSERT( copy.GetEventHandler() != NULL );
+        wxASSERT( copy.GetEventHandler() != (&copy) );
+        wxASSERT( GetEventHandler()->GetClassInfo() == copy.GetEventHandler()->GetClassInfo() );
+        GetEventHandler()->CopyData(* (copy.GetEventHandler()));
+    }
+}
+
+
+// Default - make 6 control points
+void wxShape::MakeControlPoints()
+{
+  double maxX, maxY, minX, minY;
+
+  GetBoundingBoxMax(&maxX, &maxY);
+  GetBoundingBoxMin(&minX, &minY);
+
+  double widthMin = (double)(minX + CONTROL_POINT_SIZE + 2);
+  double heightMin = (double)(minY + CONTROL_POINT_SIZE + 2);
+
+  // Offsets from main object
+  double top = (double)(- (heightMin / 2.0));
+  double bottom = (double)(heightMin / 2.0 + (maxY - minY));
+  double left = (double)(- (widthMin / 2.0));
+  double right = (double)(widthMin / 2.0 + (maxX - minX));
+
+  wxControlPoint *control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, left, top,
+                                           CONTROL_POINT_DIAGONAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, 0, top,
+                                           CONTROL_POINT_VERTICAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, right, top,
+                                           CONTROL_POINT_DIAGONAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, right, 0,
+                                           CONTROL_POINT_HORIZONTAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, right, bottom,
+                                           CONTROL_POINT_DIAGONAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, 0, bottom,
+                                           CONTROL_POINT_VERTICAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, left, bottom,
+                                           CONTROL_POINT_DIAGONAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+  control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, left, 0,
+                                           CONTROL_POINT_HORIZONTAL);
+  m_canvas->AddShape(control);
+  m_controlPoints.Append(control);
+
+}
+
+void wxShape::MakeMandatoryControlPoints()
+{
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    child->MakeMandatoryControlPoints();
+    node = node->Next();
+  }
+}
+
+void wxShape::ResetMandatoryControlPoints()
+{
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    child->ResetMandatoryControlPoints();
+    node = node->Next();
+  }
+}
+
+void wxShape::ResetControlPoints()
+{
+  ResetMandatoryControlPoints();
+
+  if (m_controlPoints.Number() < 1)
+    return;
+
+  double maxX, maxY, minX, minY;
+
+  GetBoundingBoxMax(&maxX, &maxY);
+  GetBoundingBoxMin(&minX, &minY);
+
+  double widthMin = (double)(minX + CONTROL_POINT_SIZE + 2);
+  double heightMin = (double)(minY + CONTROL_POINT_SIZE + 2);
+
+  // Offsets from main object
+  double top = (double)(- (heightMin / 2.0));
+  double bottom = (double)(heightMin / 2.0 + (maxY - minY));
+  double left = (double)(- (widthMin / 2.0));
+  double right = (double)(widthMin / 2.0 + (maxX - minX));
+
+  wxNode *node = m_controlPoints.First();
+  wxControlPoint *control = (wxControlPoint *)node->Data();
+  control->m_xoffset = left; control->m_yoffset = top;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = 0; control->m_yoffset = top;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = right; control->m_yoffset = top;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = right; control->m_yoffset = 0;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = right; control->m_yoffset = bottom;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = 0; control->m_yoffset = bottom;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = left; control->m_yoffset = bottom;
+
+  node = node->Next(); control = (wxControlPoint *)node->Data();
+  control->m_xoffset = left; control->m_yoffset = 0;
+}
+
+void wxShape::DeleteControlPoints(wxDC *dc)
+{
+  wxNode *node = m_controlPoints.First();
+  while (node)
+  {
+    wxControlPoint *control = (wxControlPoint *)node->Data();
+    if (dc)
+        control->GetEventHandler()->OnErase(*dc);
+    m_canvas->RemoveShape(control);
+    delete control;
+    delete node;
+    node = m_controlPoints.First();
+  }
+  // Children of divisions are contained objects,
+  // so stop here
+  if (!IsKindOf(CLASSINFO(wxDivisionShape)))
+  {
+    node = m_children.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      child->DeleteControlPoints(dc);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::OnDrawControlPoints(wxDC& dc)
+{
+  if (!m_drawHandles)
+    return;
+
+  dc.SetBrush(* wxBLACK_BRUSH);
+  dc.SetPen(* wxBLACK_PEN);
+
+  wxNode *node = m_controlPoints.First();
+  while (node)
+  {
+    wxControlPoint *control = (wxControlPoint *)node->Data();
+    control->Draw(dc);
+    node = node->Next();
+  }
+  // Children of divisions are contained objects,
+  // so stop here.
+  // This test bypasses the type facility for speed
+  // (critical when drawing)
+  if (!IsKindOf(CLASSINFO(wxDivisionShape)))
+  {
+    node = m_children.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      child->GetEventHandler()->OnDrawControlPoints(dc);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::OnEraseControlPoints(wxDC& dc)
+{
+  wxNode *node = m_controlPoints.First();
+  while (node)
+  {
+    wxControlPoint *control = (wxControlPoint *)node->Data();
+    control->Erase(dc);
+    node = node->Next();
+  }
+  if (!IsKindOf(CLASSINFO(wxDivisionShape)))
+  {
+    node = m_children.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      child->GetEventHandler()->OnEraseControlPoints(dc);
+      node = node->Next();
+    }
+  }
+}
+
+void wxShape::Select(bool select, wxDC* dc)
+{
+  m_selected = select;
+  if (select)
+  {
+    MakeControlPoints();
+    // Children of divisions are contained objects,
+    // so stop here
+    if (!IsKindOf(CLASSINFO(wxDivisionShape)))
+    {
+      wxNode *node = m_children.First();
+      while (node)
+      {
+        wxShape *child = (wxShape *)node->Data();
+        child->MakeMandatoryControlPoints();
+        node = node->Next();
+      }
+    }
+    if (dc)
+        GetEventHandler()->OnDrawControlPoints(*dc);
+  }
+  if (!select)
+  {
+    DeleteControlPoints(dc);
+    if (!IsKindOf(CLASSINFO(wxDivisionShape)))
+    {
+      wxNode *node = m_children.First();
+      while (node)
+      {
+        wxShape *child = (wxShape *)node->Data();
+        child->DeleteControlPoints(dc);
+        node = node->Next();
+      }
+    }
+  }
+}
+
+bool wxShape::Selected() const
+{
+  return m_selected;
+}
+
+bool wxShape::AncestorSelected() const
+{
+  if (m_selected) return TRUE;
+  if (!GetParent())
+    return FALSE;
+  else
+    return GetParent()->AncestorSelected();
+}
+
+int wxShape::GetNumberOfAttachments() const
+{
+  // Should return the MAXIMUM attachment point id here,
+  // so higher-level functions can iterate through all attachments,
+  // even if they're not contiguous.
+  if (m_attachmentPoints.Number() == 0)
+    return 4;
+  else
+  {
+    int maxN = 3;
+    wxNode *node = m_attachmentPoints.First();
+    while (node)
+    {
+      wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+      if (point->m_id > maxN)
+        maxN = point->m_id;
+      node = node->Next();
+    }
+    return maxN+1;;
+  }
+}
+
+bool wxShape::AttachmentIsValid(int attachment) const
+{
+  if (m_attachmentPoints.Number() == 0)
+  {
+    return ((attachment >= 0) && (attachment < 4)) ;
+  }
+
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    if (point->m_id == attachment)
+      return TRUE;
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+bool wxShape::GetAttachmentPosition(int attachment, double *x, double *y,
+                                         int nth, int no_arcs, wxLineShape *line)
+{
+    if (m_attachmentMode == ATTACHMENT_MODE_NONE)
+    {
+        *x = m_xpos; *y = m_ypos;
+        return TRUE;
+    }
+    else if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
+    {
+        wxRealPoint pt, stemPt;
+        GetBranchingAttachmentPoint(attachment, nth, pt, stemPt);
+        *x = pt.x;
+        *y = pt.y;
+        return TRUE;
+    }
+    else if (m_attachmentMode == ATTACHMENT_MODE_EDGE)
+    {
+        if (m_attachmentPoints.Number() > 0)
+        {
+            wxNode *node = m_attachmentPoints.First();
+            while (node)
+            {
+                wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+                if (point->m_id == attachment)
+                {
+                    *x = (double)(m_xpos + point->m_x);
+                    *y = (double)(m_ypos + point->m_y);
+                    return TRUE;
+                }
+                node = node->Next();
+            }
+            *x = m_xpos; *y = m_ypos;
+            return FALSE;
+        }
+        else
+        {
+            // Assume is rectangular
+            double w, h;
+            GetBoundingBoxMax(&w, &h);
+            double top = (double)(m_ypos + h/2.0);
+            double bottom = (double)(m_ypos - h/2.0);
+            double left = (double)(m_xpos - w/2.0);
+            double right = (double)(m_xpos + w/2.0);
+
+            bool isEnd = (line && line->IsEnd(this));
+
+            int physicalAttachment = LogicalToPhysicalAttachment(attachment);
+
+            // Simplified code
+            switch (physicalAttachment)
+            {
+                case 0:
+                {
+                    wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(left, bottom), wxRealPoint(right, bottom),
+                            nth, no_arcs, line);
+
+                    *x = pt.x; *y = pt.y;
+                    break;
+                }
+                case 1:
+                {
+                    wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(right, bottom), wxRealPoint(right, top),
+                            nth, no_arcs, line);
+
+                    *x = pt.x; *y = pt.y;
+                    break;
+                }
+                case 2:
+                {
+                    wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(left, top), wxRealPoint(right, top),
+                            nth, no_arcs, line);
+
+                    *x = pt.x; *y = pt.y;
+                    break;
+                }
+                case 3:
+                {
+                    wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(left, bottom), wxRealPoint(left, top),
+                            nth, no_arcs, line);
+
+                    *x = pt.x; *y = pt.y;
+                    break;
+                }
+                default:
+                {
+                    return FALSE;
+                    break;
+                }
+            }
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+void wxShape::GetBoundingBoxMax(double *w, double *h)
+{
+  double ww, hh;
+  GetBoundingBoxMin(&ww, &hh);
+  if (m_shadowMode != SHADOW_NONE)
+  {
+    ww += m_shadowOffsetX;
+    hh += m_shadowOffsetY;
+  }
+  *w = ww;
+  *h = hh;
+}
+
+// Returns TRUE if image is a descendant of this composite
+bool wxShape::HasDescendant(wxShape *image)
+{
+  if (image == this)
+    return TRUE;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    bool ans = child->HasDescendant(image);
+    if (ans)
+      return TRUE;
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+// Clears points from a list of wxRealPoints, and clears list
+void wxShape::ClearPointList(wxList& list)
+{
+    wxNode* node = list.First();
+    while (node)
+    {
+        wxRealPoint* pt = (wxRealPoint*) node->Data();
+        delete pt;
+
+        node = node->Next();
+    }
+    list.Clear();
+}
+
+// Assuming the attachment lies along a vertical or horizontal line,
+// calculate the position on that point.
+wxRealPoint wxShape::CalcSimpleAttachment(const wxRealPoint& pt1, const wxRealPoint& pt2,
+    int nth, int noArcs, wxLineShape* line)
+{
+    bool isEnd = (line && line->IsEnd(this));
+
+    // Are we horizontal or vertical?
+    bool isHorizontal = (oglRoughlyEqual(pt1.y, pt2.y) == TRUE);
+
+    double x, y;
+
+    if (isHorizontal)
+    {
+        wxRealPoint firstPoint, secondPoint;
+        if (pt1.x > pt2.x)
+        {
+            firstPoint = pt2;
+            secondPoint = pt1;
+        }
+        else
+        {
+            firstPoint = pt1;
+            secondPoint = pt2;
+        }
+
+        if (m_spaceAttachments)
+        {
+          if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
+          {
+            // Align line according to the next handle along
+            wxRealPoint *point = line->GetNextControlPoint(this);
+            if (point->x < firstPoint.x)
+              x = firstPoint.x;
+            else if (point->x > secondPoint.x)
+              x = secondPoint.x;
+            else
+              x = point->x;
+          }
+          else
+            x = firstPoint.x + (nth + 1)*(secondPoint.x - firstPoint.x)/(noArcs + 1);
+        }
+        else x = (secondPoint.x - firstPoint.x)/2.0; // Midpoint
+
+        y = pt1.y;
+    }
+    else
+    {
+        wxASSERT( oglRoughlyEqual(pt1.x, pt2.x) == TRUE );
+
+        wxRealPoint firstPoint, secondPoint;
+        if (pt1.y > pt2.y)
+        {
+            firstPoint = pt2;
+            secondPoint = pt1;
+        }
+        else
+        {
+            firstPoint = pt1;
+            secondPoint = pt2;
+        }
+
+        if (m_spaceAttachments)
+        {
+          if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
+          {
+            // Align line according to the next handle along
+            wxRealPoint *point = line->GetNextControlPoint(this);
+            if (point->y < firstPoint.y)
+              y = firstPoint.y;
+            else if (point->y > secondPoint.y)
+              y = secondPoint.y;
+            else
+              y = point->y;
+          }
+          else
+            y = firstPoint.y + (nth + 1)*(secondPoint.y - firstPoint.y)/(noArcs + 1);
+        }
+        else y = (secondPoint.y - firstPoint.y)/2.0; // Midpoint
+
+        x = pt1.x;
+    }
+
+    return wxRealPoint(x, y);
+}
+
+// Return the zero-based position in m_lines of line.
+int wxShape::GetLinePosition(wxLineShape* line)
+{
+    int i = 0;
+    for (i = 0; i < m_lines.Number(); i++)
+        if ((wxLineShape*) (m_lines.Nth(i)->Data()) == line)
+            return i;
+
+    return 0;
+}
+
+//
+//             |________|
+//                 | <- root
+//                 | <- neck
+// shoulder1 ->---------<- shoulder2
+//             | | | | |
+//                      <- branching attachment point N-1
+
+// This function gets information about where branching connections go.
+// Returns FALSE if there are no lines at this attachment.
+bool wxShape::GetBranchingAttachmentInfo(int attachment, wxRealPoint& root, wxRealPoint& neck,
+    wxRealPoint& shoulder1, wxRealPoint& shoulder2)
+{
+    int physicalAttachment = LogicalToPhysicalAttachment(attachment);
+
+    // Number of lines at this attachment.
+    int lineCount = GetAttachmentLineCount(attachment);
+
+    if (lineCount == 0)
+        return FALSE;
+
+    int totalBranchLength = m_branchSpacing * (lineCount - 1);
+
+    root = GetBranchingAttachmentRoot(attachment);
+
+    // Assume that we have attachment points 0 to 3: top, right, bottom, left.
+    switch (physicalAttachment)
+    {
+        case 0:
+        {
+            neck.x = GetX();
+            neck.y = root.y - m_branchNeckLength;
+
+            shoulder1.x = root.x - (totalBranchLength/2.0) ;
+            shoulder2.x = root.x + (totalBranchLength/2.0) ;
+
+            shoulder1.y = neck.y;
+            shoulder2.y = neck.y;
+            break;
+        }
+        case 1:
+        {
+            neck.x = root.x + m_branchNeckLength;
+            neck.y = root.y;
+
+            shoulder1.x = neck.x ;
+            shoulder2.x = neck.x ;
+
+            shoulder1.y = neck.y - (totalBranchLength/2.0) ;
+            shoulder2.y = neck.y + (totalBranchLength/2.0) ;
+            break;
+        }
+        case 2:
+        {
+            neck.x = GetX();
+            neck.y = root.y + m_branchNeckLength;
+
+            shoulder1.x = root.x - (totalBranchLength/2.0) ;
+            shoulder2.x = root.x + (totalBranchLength/2.0) ;
+
+            shoulder1.y = neck.y;
+            shoulder2.y = neck.y;
+            break;
+        }
+        case 3:
+        {
+            neck.x = root.x - m_branchNeckLength;
+            neck.y = root.y ;
+
+            shoulder1.x = neck.x ;
+            shoulder2.x = neck.x ;
+
+            shoulder1.y = neck.y - (totalBranchLength/2.0) ;
+            shoulder2.y = neck.y + (totalBranchLength/2.0) ;
+            break;
+        }
+        default:
+        {
+            wxFAIL_MSG( "Unrecognised attachment point in GetBranchingAttachmentInfo." );
+            break;
+        }
+    }
+    return TRUE;
+}
+
+// n is the number of the adjoining line, from 0 to N-1 where N is the number of lines
+// at this attachment point.
+// Get the attachment point where the arc joins the stem, and also the point where the
+// the stem meets the shoulder.
+bool wxShape::GetBranchingAttachmentPoint(int attachment, int n, wxRealPoint& pt, wxRealPoint& stemPt)
+{
+    int physicalAttachment = LogicalToPhysicalAttachment(attachment);
+
+    wxRealPoint root, neck, shoulder1, shoulder2;
+    GetBranchingAttachmentInfo(attachment, root, neck, shoulder1, shoulder2);
+
+    // Assume that we have attachment points 0 to 3: top, right, bottom, left.
+    switch (physicalAttachment)
+    {
+        case 0:
+        {
+            pt.y = neck.y - m_branchStemLength;
+            pt.x = shoulder1.x + n*m_branchSpacing;
+
+            stemPt.x = pt.x;
+            stemPt.y = neck.y;
+            break;
+        }
+        case 2:
+        {
+            pt.y = neck.y + m_branchStemLength;
+            pt.x = shoulder1.x + n*m_branchSpacing;
+
+            stemPt.x = pt.x;
+            stemPt.y = neck.y;
+            break;
+        }
+        case 1:
+        {
+            pt.x = neck.x + m_branchStemLength;
+            pt.y = shoulder1.y + n*m_branchSpacing;
+
+            stemPt.x = neck.x;
+            stemPt.y = pt.y;
+            break;
+        }
+        case 3:
+        {
+            pt.x = neck.x - m_branchStemLength;
+            pt.y = shoulder1.y + n*m_branchSpacing;
+
+            stemPt.x = neck.x;
+            stemPt.y = pt.y;
+            break;
+        }
+        default:
+        {
+            wxFAIL_MSG( "Unrecognised attachment point in GetBranchingAttachmentPoint." );
+            break;
+        }
+    }
+
+    return TRUE;
+}
+
+// Get the number of lines at this attachment position.
+int wxShape::GetAttachmentLineCount(int attachment) const
+{
+    int count = 0;
+    wxNode* node = m_lines.First();
+    while (node)
+    {
+        wxLineShape* lineShape = (wxLineShape*) node->Data();
+        if ((lineShape->GetFrom() == this) && (lineShape->GetAttachmentFrom() == attachment))
+            count ++;
+        else if ((lineShape->GetTo() == this) && (lineShape->GetAttachmentTo() == attachment))
+            count ++;
+
+        node = node->Next();
+    }
+    return count;
+}
+
+// This function gets the root point at the given attachment.
+wxRealPoint wxShape::GetBranchingAttachmentRoot(int attachment)
+{
+    int physicalAttachment = LogicalToPhysicalAttachment(attachment);
+
+    wxRealPoint root;
+
+    double width, height;
+    GetBoundingBoxMax(& width, & height);
+
+    // Assume that we have attachment points 0 to 3: top, right, bottom, left.
+    switch (physicalAttachment)
+    {
+        case 0:
+        {
+            root.x = GetX() ;
+            root.y = GetY() - height/2.0;
+            break;
+        }
+        case 1:
+        {
+            root.x = GetX() + width/2.0;
+            root.y = GetY() ;
+            break;
+        }
+        case 2:
+        {
+            root.x = GetX() ;
+            root.y = GetY() + height/2.0;
+            break;
+        }
+        case 3:
+        {
+            root.x = GetX() - width/2.0;
+            root.y = GetY() ;
+            break;
+        }
+        default:
+        {
+            wxFAIL_MSG( "Unrecognised attachment point in GetBranchingAttachmentRoot." );
+            break;
+        }
+    }
+    return root;
+}
+
+// Draw or erase the branches (not the actual arcs though)
+void wxShape::OnDrawBranches(wxDC& dc, int attachment, bool erase)
+{
+    int count = GetAttachmentLineCount(attachment);
+    if (count == 0)
+        return;
+
+    wxRealPoint root, neck, shoulder1, shoulder2;
+    GetBranchingAttachmentInfo(attachment, root, neck, shoulder1, shoulder2);
+
+    if (erase)
+    {
+        dc.SetPen(*wxWHITE_PEN);
+        dc.SetBrush(*wxWHITE_BRUSH);
+    }
+    else
+    {
+        dc.SetPen(*wxBLACK_PEN);
+        dc.SetBrush(*wxBLACK_BRUSH);
+    }
+
+    // Draw neck
+    dc.DrawLine((long) root.x, (long) root.y, (long) neck.x, (long) neck.y);
+
+    if (count > 1)
+    {
+        // Draw shoulder-to-shoulder line
+        dc.DrawLine((long) shoulder1.x, (long) shoulder1.y, (long) shoulder2.x, (long) shoulder2.y);
+    }
+    // Draw all the little branches
+    int i;
+    for (i = 0; i < count; i++)
+    {
+        wxRealPoint pt, stemPt;
+        GetBranchingAttachmentPoint(attachment, i, pt, stemPt);
+        dc.DrawLine((long) stemPt.x, (long) stemPt.y, (long) pt.x, (long) pt.y);
+
+        if ((GetBranchStyle() & BRANCHING_ATTACHMENT_BLOB) && (count > 1))
+        {
+            long blobSize=6;
+//            dc.DrawEllipse((long) (stemPt.x + 0.5 - (blobSize/2.0)), (long) (stemPt.y + 0.5 - (blobSize/2.0)), blobSize, blobSize);
+            dc.DrawEllipse((long) (stemPt.x - (blobSize/2.0)), (long) (stemPt.y - (blobSize/2.0)), blobSize, blobSize);
+        }
+    }
+}
+
+// Draw or erase the branches (not the actual arcs though)
+void wxShape::OnDrawBranches(wxDC& dc, bool erase)
+{
+    if (m_attachmentMode != ATTACHMENT_MODE_BRANCHING)
+        return;
+
+    int count = GetNumberOfAttachments();
+    int i;
+    for (i = 0; i < count; i++)
+        OnDrawBranches(dc, i, erase);
+}
+
+// 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.
+bool wxShape::GetAttachmentPositionEdge(int attachment, double *x, double *y,
+                                     int nth, int no_arcs, wxLineShape *line)
+{
+    int oldMode = m_attachmentMode;
+
+    // Calculate as if to edge, not branch
+    if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
+        m_attachmentMode = ATTACHMENT_MODE_EDGE;
+    bool success = GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
+    m_attachmentMode = oldMode;
+
+    return success;
+}
+
+// Rotate the standard attachment point from physical (0 is always North)
+// to logical (0 -> 1 if rotated by 90 degrees)
+int wxShape::PhysicalToLogicalAttachment(int physicalAttachment) const
+{
+    const double pi = 3.1415926535897932384626433832795 ;
+    int i;
+    if (oglRoughlyEqual(GetRotation(), 0.0))
+    {
+        i = physicalAttachment;
+    }
+    else if (oglRoughlyEqual(GetRotation(), (pi/2.0)))
+    {
+        i = physicalAttachment - 1;
+    }
+    else if (oglRoughlyEqual(GetRotation(), pi))
+    {
+        i = physicalAttachment - 2;
+    }
+    else if (oglRoughlyEqual(GetRotation(), (3.0*pi/2.0)))
+    {
+        i = physicalAttachment - 3;
+    }
+    else
+        // Can't handle -- assume the same.
+        return physicalAttachment;
+
+    if (i < 0)
+      i += 4;
+
+    return i;
+}
+
+// Rotate the standard attachment point from logical
+// to physical (0 is always North)
+int wxShape::LogicalToPhysicalAttachment(int logicalAttachment) const
+{
+    const double pi = 3.1415926535897932384626433832795 ;
+    int i;
+    if (oglRoughlyEqual(GetRotation(), 0.0))
+    {
+        i = logicalAttachment;
+    }
+    else if (oglRoughlyEqual(GetRotation(), (pi/2.0)))
+    {
+        i = logicalAttachment + 1;
+    }
+    else if (oglRoughlyEqual(GetRotation(), pi))
+    {
+        i = logicalAttachment + 2;
+    }
+    else if (oglRoughlyEqual(GetRotation(), (3.0*pi/2.0)))
+    {
+        i = logicalAttachment + 3;
+    }
+    else
+        // Can't handle -- assume the same.
+        return logicalAttachment;
+
+    if (i > 3)
+      i -= 4;
+
+    return i;
+}
+
+void wxShape::Rotate(double WXUNUSED(x), double WXUNUSED(y), double theta)
+{
+    const double pi = 3.1415926535897932384626433832795 ;
+    m_rotation = theta;
+    if (m_rotation < 0.0)
+    {
+        m_rotation += 2*pi;
+    }
+    else if (m_rotation > 2*pi)
+    {
+        m_rotation -= 2*pi;
+    }
+}
+
diff --git a/src/ogl/basic2.cpp b/src/ogl/basic2.cpp
new file mode 100644 (file)
index 0000000..7a65d70
--- /dev/null
@@ -0,0 +1,1902 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        basic2.cpp
+// Purpose:     Basic OGL classes (2)
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "basicp.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/composit.h>
+#include <wx/ogl/lines.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/divided.h>
+#include <wx/ogl/misc.h>
+
+// Control point types
+// Rectangle and most other shapes
+#define CONTROL_POINT_VERTICAL   1
+#define CONTROL_POINT_HORIZONTAL 2
+#define CONTROL_POINT_DIAGONAL   3
+
+// Line
+#define CONTROL_POINT_ENDPOINT_TO 4
+#define CONTROL_POINT_ENDPOINT_FROM 5
+#define CONTROL_POINT_LINE       6
+
+// Two stage construction: need to call Create
+IMPLEMENT_DYNAMIC_CLASS(wxPolygonShape, wxShape)
+
+wxPolygonShape::wxPolygonShape()
+{
+  m_points = NULL;
+  m_originalPoints = NULL;
+}
+
+void wxPolygonShape::Create(wxList *the_points)
+{
+  ClearPoints();
+
+  m_originalPoints = the_points;
+
+  // Duplicate the list of points
+  m_points = new wxList;
+
+  wxNode *node = the_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
+    m_points->Append((wxObject*) new_point);
+    node = node->Next();
+  }
+  CalculateBoundingBox();
+  m_originalWidth = m_boundWidth;
+  m_originalHeight = m_boundHeight;
+  SetDefaultRegionSize();
+}
+
+wxPolygonShape::~wxPolygonShape()
+{
+    ClearPoints();
+}
+
+void wxPolygonShape::ClearPoints()
+{
+  if (m_points)
+  {
+    wxNode *node = m_points->First();
+    while (node)
+    {
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+      delete point;
+      delete node;
+      node = m_points->First();
+    }
+    delete m_points;
+    m_points = NULL;
+  }
+  if (m_originalPoints)
+  {
+    wxNode *node = m_originalPoints->First();
+    while (node)
+    {
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+      delete point;
+      delete node;
+      node = m_originalPoints->First();
+    }
+    delete m_originalPoints;
+    m_originalPoints = NULL;
+  }
+}
+
+
+// Width and height. Centre of object is centre of box.
+void wxPolygonShape::GetBoundingBoxMin(double *width, double *height)
+{
+  *width = m_boundWidth;
+  *height = m_boundHeight;
+}
+
+void wxPolygonShape::CalculateBoundingBox()
+{
+  // Calculate bounding box at construction (and presumably resize) time
+  double left = 10000;
+  double right = -10000;
+  double top = 10000;
+  double bottom = -10000;
+
+  wxNode *node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    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;
+
+    node = node->Next();
+  }
+  m_boundWidth = right - left;
+  m_boundHeight = bottom - top;
+}
+
+// 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.
+void wxPolygonShape::CalculatePolygonCentre()
+{
+  double left = 10000;
+  double right = -10000;
+  double top = 10000;
+  double bottom = -10000;
+
+  wxNode *node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    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;
+
+    node = node->Next();
+  }
+  double bwidth = right - left;
+  double bheight = bottom - top;
+
+  double newCentreX = (double)(left + (bwidth/2.0));
+  double newCentreY = (double)(top + (bheight/2.0));
+
+  node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    point->x -= newCentreX;
+    point->y -= newCentreY;
+    node = node->Next();
+  }
+  m_xpos += newCentreX;
+  m_ypos += newCentreY;
+}
+
+bool PolylineHitTest(double n, double xvec[], double yvec[],
+                           double x1, double y1, double x2, double y2)
+{
+  bool isAHit = FALSE;
+  int i;
+  double lastx = xvec[0];
+  double lasty = yvec[0];
+
+  double min_ratio = 1.0;
+  double line_ratio;
+  double other_ratio;
+
+//  char buf[300];
+  for (i = 1; i < n; i++)
+  {
+    oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i],
+                            &line_ratio, &other_ratio);
+    if (line_ratio != 1.0)
+      isAHit = TRUE;
+//    sprintf(buf, "Line ratio = %.2f, other ratio = %.2f\n", line_ratio, other_ratio);
+//    ClipsErrorFunction(buf);
+    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 (!(xvec[0] == lastx && yvec[0] == lasty))
+  {
+    oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0],
+                            &line_ratio, &other_ratio);
+    if (line_ratio != 1.0)
+      isAHit = TRUE;
+//    sprintf(buf, "Line ratio = %.2f, other ratio = %.2f\n", line_ratio, other_ratio);
+//    ClipsErrorFunction(buf);
+
+    if (line_ratio < min_ratio)
+      min_ratio = line_ratio;
+  }
+//  ClipsErrorFunction("\n");
+  return isAHit;
+}
+
+bool wxPolygonShape::HitTest(double x, double y, int *attachment, double *distance)
+{
+  // 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.
+  double endPointsX[4];
+  double endPointsY[4];
+  // North
+  endPointsX[0] = x;
+  endPointsY[0] = (double)(y - 1000.0);
+  // East
+  endPointsX[1] = (double)(x + 1000.0);
+  endPointsY[1] = y;
+  // South
+  endPointsX[2] = x;
+  endPointsY[2] = (double)(y + 1000.0);
+  // West
+  endPointsX[3] = (double)(x - 1000.0);
+  endPointsY[3] = y;
+
+  // Store polygon points in an array
+  int np = m_points->Number();
+  double *xpoints = new double[np];
+  double *ypoints = new double[np];
+  wxNode *node = m_points->First();
+  int i = 0;
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    xpoints[i] = point->x + m_xpos;
+    ypoints[i] = point->y + m_ypos;
+    node = node->Next();
+    i ++;
+  }
+
+  // We assume it's inside the polygon UNLESS one or more
+  // lines don't hit the outline.
+  bool isContained = TRUE;
+
+  int noPoints = 4;
+  for (i = 0; i < noPoints; i++)
+  {
+    if (!PolylineHitTest(np, xpoints, ypoints, x, y, endPointsX[i], endPointsY[i]))
+      isContained = FALSE;
+  }
+/*
+  if (isContained)
+    ClipsErrorFunction("It's a hit!\n");
+  else
+    ClipsErrorFunction("No hit.\n");
+*/
+  delete[] xpoints;
+  delete[] ypoints;
+
+  if (!isContained)
+    return FALSE;
+
+  int nearest_attachment = 0;
+
+  // If a hit, check the attachment points within the object.
+  int n = GetNumberOfAttachments();
+  double nearest = 999999.0;
+
+  for (i = 0; i < n; i++)
+  {
+    double xp, yp;
+    if (GetAttachmentPositionEdge(i, &xp, &yp))
+    {
+      double l = (double)sqrt(((xp - x) * (xp - x)) +
+                 ((yp - y) * (yp - y)));
+      if (l < nearest)
+      {
+        nearest = l;
+        nearest_attachment = i;
+      }
+    }
+  }
+  *attachment = nearest_attachment;
+  *distance = nearest;
+  return TRUE;
+}
+
+// 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.
+void wxPolygonShape::SetSize(double new_width, double new_height, bool recursive)
+{
+  SetAttachmentSize(new_width, new_height);
+
+  // Multiply all points by proportion of new size to old size
+  double x_proportion = (double)(fabs(new_width/m_originalWidth));
+  double y_proportion = (double)(fabs(new_height/m_originalHeight));
+
+  wxNode *node = m_points->First();
+  wxNode *original_node = m_originalPoints->First();
+  while (node && original_node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *original_point = (wxRealPoint *)original_node->Data();
+
+    point->x = (original_point->x * x_proportion);
+    point->y = (original_point->y * y_proportion);
+
+    node = node->Next();
+    original_node = original_node->Next();
+  }
+
+//  CalculateBoundingBox();
+  m_boundWidth = (double)fabs(new_width);
+  m_boundHeight = (double)fabs(new_height);
+  SetDefaultRegionSize();
+}
+
+// Make the original points the same as the working points
+void wxPolygonShape::UpdateOriginalPoints()
+{
+  if (!m_originalPoints) m_originalPoints = new wxList;
+  wxNode *original_node = m_originalPoints->First();
+  while (original_node)
+  {
+    wxNode *next_node = original_node->Next();
+    wxRealPoint *original_point = (wxRealPoint *)original_node->Data();
+    delete original_point;
+    delete original_node;
+
+    original_node = next_node;
+  }
+
+  wxNode *node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *original_point = new wxRealPoint(point->x, point->y);
+    m_originalPoints->Append((wxObject*) original_point);
+
+    node = node->Next();
+  }
+  CalculateBoundingBox();
+  m_originalWidth = m_boundWidth;
+  m_originalHeight = m_boundHeight;
+}
+
+void wxPolygonShape::AddPolygonPoint(int pos)
+{
+  wxNode *node = m_points->Nth(pos);
+  if (!node) node = m_points->First();
+  wxRealPoint *firstPoint = (wxRealPoint *)node->Data();
+
+  wxNode *node2 = m_points->Nth(pos + 1);
+  if (!node2) node2 = m_points->First();
+  wxRealPoint *secondPoint = (wxRealPoint *)node2->Data();
+
+  double x = (double)((secondPoint->x - firstPoint->x)/2.0 + firstPoint->x);
+  double y = (double)((secondPoint->y - firstPoint->y)/2.0 + firstPoint->y);
+  wxRealPoint *point = new wxRealPoint(x, y);
+
+  if (pos >= (m_points->Number() - 1))
+    m_points->Append((wxObject*) point);
+  else
+    m_points->Insert(node2, (wxObject*) point);
+
+  UpdateOriginalPoints();
+
+  if (m_selected)
+  {
+    DeleteControlPoints();
+    MakeControlPoints();
+  }
+}
+
+void wxPolygonShape::DeletePolygonPoint(int pos)
+{
+  wxNode *node = m_points->Nth(pos);
+  if (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    delete point;
+    delete node;
+    UpdateOriginalPoints();
+    if (m_selected)
+    {
+      DeleteControlPoints();
+      MakeControlPoints();
+    }
+  }
+}
+
+// Assume (x1, y1) is centre of box (most generally, line end at box)
+bool wxPolygonShape::GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3)
+{
+  int n = m_points->Number();
+
+  // 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 ((m_attachmentMode == ATTACHMENT_MODE_NONE) && (x1 == x2))
+  {
+    // Look for the point we'd be connecting to. This is
+    // a heuristic...
+    wxNode *node = m_points->First();
+    while (node)
+    {
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+      if (point->x == 0.0)
+      {
+        if ((y2 > y1) && (point->y > 0.0))
+        {
+          *x3 = point->x + m_xpos;
+          *y3 = point->y + m_ypos;
+          return TRUE;
+        }
+        else if ((y2 < y1) && (point->y < 0.0))
+        {
+          *x3 = point->x + m_xpos;
+          *y3 = point->y + m_ypos;
+          return TRUE;
+        }
+      }
+      node = node->Next();
+    }
+  }
+
+  double *xpoints = new double[n];
+  double *ypoints = new double[n];
+
+  wxNode *node = m_points->First();
+  int i = 0;
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    xpoints[i] = point->x + m_xpos;
+    ypoints[i] = point->y + m_ypos;
+    node = node->Next();
+    i ++;
+  }
+
+  oglFindEndForPolyline(n, xpoints, ypoints,
+                        x1, y1, x2, y2, x3, y3);
+
+  delete[] xpoints;
+  delete[] ypoints;
+
+  return TRUE;
+}
+
+void wxPolygonShape::OnDraw(wxDC& dc)
+{
+    int n = m_points->Number();
+    wxPoint *intPoints = new wxPoint[n];
+    int i;
+    for (i = 0; i < n; i++)
+    {
+      wxRealPoint* point = (wxRealPoint*) m_points->Nth(i)->Data();
+      intPoints[i].x = WXROUND(point->x);
+      intPoints[i].y = WXROUND(point->y);
+    }
+
+    if (m_shadowMode != SHADOW_NONE)
+    {
+      if (m_shadowBrush)
+        dc.SetBrush(* m_shadowBrush);
+      dc.SetPen(* g_oglTransparentPen);
+
+      dc.DrawPolygon(n, intPoints, WXROUND(m_xpos + m_shadowOffsetX), WXROUND(m_ypos + m_shadowOffsetY));
+    }
+
+    if (m_pen)
+    {
+      if (m_pen->GetWidth() == 0)
+        dc.SetPen(* g_oglTransparentPen);
+      else
+        dc.SetPen(* m_pen);
+    }
+    if (m_brush)
+      dc.SetBrush(* m_brush);
+    dc.DrawPolygon(n, intPoints, WXROUND(m_xpos), WXROUND(m_ypos));
+
+    delete[] intPoints;
+}
+
+void wxPolygonShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
+{
+  dc.SetBrush(* wxTRANSPARENT_BRUSH);
+  // Multiply all points by proportion of new size to old size
+  double x_proportion = (double)(fabs(w/m_originalWidth));
+  double y_proportion = (double)(fabs(h/m_originalHeight));
+
+  int n = m_originalPoints->Number();
+  wxPoint *intPoints = new wxPoint[n];
+  int i;
+  for (i = 0; i < n; i++)
+  {
+    wxRealPoint* point = (wxRealPoint*) m_originalPoints->Nth(i)->Data();
+    intPoints[i].x = WXROUND(x_proportion * point->x);
+    intPoints[i].y = WXROUND(y_proportion * point->y);
+  }
+  dc.DrawPolygon(n, intPoints, WXROUND(x), WXROUND(y));
+  delete[] intPoints;
+}
+
+// Make as many control points as there are vertices.
+void wxPolygonShape::MakeControlPoints()
+{
+  wxNode *node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxPolygonControlPoint *control = new wxPolygonControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
+      point, point->x, point->y);
+    m_canvas->AddShape(control);
+    m_controlPoints.Append(control);
+    node = node->Next();
+  }
+}
+
+void wxPolygonShape::ResetControlPoints()
+{
+  wxNode *node = m_points->First();
+  wxNode *controlPointNode = m_controlPoints.First();
+  while (node && controlPointNode)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxPolygonControlPoint *controlPoint = (wxPolygonControlPoint *)controlPointNode->Data();
+
+    controlPoint->m_xoffset = point->x;
+    controlPoint->m_yoffset = point->y;
+    controlPoint->m_polygonVertex = point;
+
+    node = node->Next();
+    controlPointNode = controlPointNode->Next();
+  }
+}
+
+
+#ifdef PROLOGIO
+void wxPolygonShape::WriteAttributes(wxExpr *clause)
+{
+  wxShape::WriteAttributes(clause);
+
+  clause->AddAttributeValue("x", m_xpos);
+  clause->AddAttributeValue("y", m_ypos);
+
+  // Make a list of lists for the coordinates
+  wxExpr *list = new wxExpr(wxExprList);
+  wxNode *node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxExpr *point_list = new wxExpr(wxExprList);
+    wxExpr *x_expr = new wxExpr((double)point->x);
+    wxExpr *y_expr = new wxExpr((double)point->y);
+
+    point_list->Append(x_expr);
+    point_list->Append(y_expr);
+    list->Append(point_list);
+
+    node = node->Next();
+  }
+  clause->AddAttributeValue("points", list);
+
+  // Save the original (unscaled) points
+  list = new wxExpr(wxExprList);
+  node = m_originalPoints->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxExpr *point_list = new wxExpr(wxExprList);
+    wxExpr *x_expr = new wxExpr((double) point->x);
+    wxExpr *y_expr = new wxExpr((double) point->y);
+    point_list->Append(x_expr);
+    point_list->Append(y_expr);
+    list->Append(point_list);
+
+    node = node->Next();
+  }
+  clause->AddAttributeValue("m_originalPoints", list);
+}
+
+void wxPolygonShape::ReadAttributes(wxExpr *clause)
+{
+  wxShape::ReadAttributes(clause);
+
+  // Read a list of lists
+  m_points = new wxList;
+  m_originalPoints = new wxList;
+
+  wxExpr *points_list = NULL;
+  clause->AssignAttributeValue("points", &points_list);
+
+  // If no points_list, don't crash!! Assume a diamond instead.
+  double the_height = 100.0;
+  double the_width = 100.0;
+  if (!points_list)
+  {
+    wxRealPoint *point = new wxRealPoint(0.0, (-the_height/2));
+    m_points->Append((wxObject*) point);
+
+    point = new wxRealPoint((the_width/2), 0.0);
+    m_points->Append((wxObject*) point);
+
+    point = new wxRealPoint(0.0, (the_height/2));
+    m_points->Append((wxObject*) point);
+
+    point = new wxRealPoint((-the_width/2), 0.0);
+    m_points->Append((wxObject*) point);
+
+    point = new wxRealPoint(0.0, (-the_height/2));
+    m_points->Append((wxObject*) point);
+  }
+  else
+  {
+    wxExpr *node = points_list->value.first;
+
+    while (node)
+    {
+      wxExpr *xexpr = node->value.first;
+      long x = xexpr->IntegerValue();
+
+      wxExpr *yexpr = xexpr->next;
+      long y = yexpr->IntegerValue();
+
+      wxRealPoint *point = new wxRealPoint((double)x, (double)y);
+      m_points->Append((wxObject*) point);
+
+      node = node->next;
+    }
+  }
+
+  points_list = NULL;
+  clause->AssignAttributeValue("m_originalPoints", &points_list);
+
+  // If no points_list, don't crash!! Assume a diamond instead.
+  if (!points_list)
+  {
+    wxRealPoint *point = new wxRealPoint(0.0, (-the_height/2));
+    m_originalPoints->Append((wxObject*) point);
+
+    point = new wxRealPoint((the_width/2), 0.0);
+    m_originalPoints->Append((wxObject*) point);
+
+    point = new wxRealPoint(0.0, (the_height/2));
+    m_originalPoints->Append((wxObject*) point);
+
+    point = new wxRealPoint((-the_width/2), 0.0);
+    m_originalPoints->Append((wxObject*) point);
+
+    point = new wxRealPoint(0.0, (-the_height/2));
+    m_originalPoints->Append((wxObject*) point);
+
+    m_originalWidth = the_width;
+    m_originalHeight = the_height;
+  }
+  else
+  {
+    wxExpr *node = points_list->value.first;
+    double min_x = 1000;
+    double min_y = 1000;
+    double max_x = -1000;
+    double max_y = -1000;
+    while (node)
+    {
+      wxExpr *xexpr = node->value.first;
+      long x = xexpr->IntegerValue();
+
+      wxExpr *yexpr = xexpr->next;
+      long y = yexpr->IntegerValue();
+
+      wxRealPoint *point = new wxRealPoint((double)x, (double)y);
+      m_originalPoints->Append((wxObject*) point);
+
+      if (x < min_x)
+        min_x = (double)x;
+      if (y < min_y)
+        min_y = (double)y;
+      if (x > max_x)
+        max_x = (double)x;
+      if (y > max_y)
+        max_y = (double)y;
+
+      node = node->next;
+    }
+    m_originalWidth = max_x - min_x;
+    m_originalHeight = max_y - min_y;
+  }
+
+  CalculateBoundingBox();
+}
+#endif
+
+void wxPolygonShape::Copy(wxShape& copy)
+{
+  wxShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxPolygonShape)) );
+
+  wxPolygonShape& polyCopy = (wxPolygonShape&) copy;
+
+  polyCopy.ClearPoints();
+
+  polyCopy.m_points = new wxList;
+  polyCopy.m_originalPoints = new wxList;
+
+  wxNode *node = m_points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
+    polyCopy.m_points->Append((wxObject*) new_point);
+    node = node->Next();
+  }
+  node = m_originalPoints->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
+    polyCopy.m_originalPoints->Append((wxObject*) new_point);
+    node = node->Next();
+  }
+  polyCopy.m_boundWidth = m_boundWidth;
+  polyCopy.m_boundHeight = m_boundHeight;
+  polyCopy.m_originalWidth = m_originalWidth;
+  polyCopy.m_originalHeight = m_originalHeight;
+}
+
+int wxPolygonShape::GetNumberOfAttachments() const
+{
+  int maxN = (m_points ? (m_points->Number() - 1) : 0);
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    if (point->m_id > maxN)
+      maxN = point->m_id;
+    node = node->Next();
+  }
+  return maxN+1;;
+}
+
+bool wxPolygonShape::GetAttachmentPosition(int attachment, double *x, double *y,
+                                         int nth, int no_arcs, wxLineShape *line)
+{
+  if ((m_attachmentMode == ATTACHMENT_MODE_EDGE) && m_points && attachment < m_points->Number())
+  {
+    wxRealPoint *point = (wxRealPoint *)m_points->Nth(attachment)->Data();
+    *x = point->x + m_xpos;
+    *y = point->y + m_ypos;
+    return TRUE;
+  }
+  else
+  { return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line); }
+}
+
+bool wxPolygonShape::AttachmentIsValid(int attachment)
+{
+  if (!m_points)
+    return FALSE;
+
+  if ((attachment >= 0) && (attachment < m_points->Number()))
+    return TRUE;
+
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    if (point->m_id == attachment)
+      return TRUE;
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+// Rotate about the given axis by the given amount in radians
+void wxPolygonShape::Rotate(double x, double y, double theta)
+{
+    double actualTheta = theta-m_rotation;
+
+    // Rotate attachment points
+    double sinTheta = (double)sin(actualTheta);
+    double cosTheta = (double)cos(actualTheta);
+    wxNode *node = m_attachmentPoints.First();
+    while (node)
+    {
+        wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+        double x1 = point->m_x;
+        double y1 = point->m_y;
+        point->m_x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+        point->m_y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+        node = node->Next();
+    }
+
+    node = m_points->First();
+    while (node)
+    {
+        wxRealPoint *point = (wxRealPoint *)node->Data();
+        double x1 = point->x;
+        double y1 = point->y;
+        point->x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+        point->y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+        node = node->Next();
+    }
+    node = m_originalPoints->First();
+    while (node)
+    {
+        wxRealPoint *point = (wxRealPoint *)node->Data();
+        double x1 = point->x;
+        double y1 = point->y;
+        point->x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+        point->y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+        node = node->Next();
+    }
+
+    m_rotation = theta;
+
+    CalculatePolygonCentre();
+    CalculateBoundingBox();
+    ResetControlPoints();
+}
+
+// Rectangle object
+
+IMPLEMENT_DYNAMIC_CLASS(wxRectangleShape, wxShape)
+
+wxRectangleShape::wxRectangleShape(double w, double h)
+{
+  m_width = w; m_height = h; m_cornerRadius = 0.0;
+  SetDefaultRegionSize();
+}
+
+void wxRectangleShape::OnDraw(wxDC& dc)
+{
+    double x1 = (double)(m_xpos - m_width/2.0);
+    double y1 = (double)(m_ypos - m_height/2.0);
+
+    if (m_shadowMode != SHADOW_NONE)
+    {
+      if (m_shadowBrush)
+        dc.SetBrush(* m_shadowBrush);
+      dc.SetPen(* g_oglTransparentPen);
+
+      if (m_cornerRadius != 0.0)
+        dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
+                                 WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
+      else
+        dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
+    }
+
+    if (m_pen)
+    {
+      if (m_pen->GetWidth() == 0)
+        dc.SetPen(* g_oglTransparentPen);
+      else
+        dc.SetPen(* m_pen);
+    }
+    if (m_brush)
+      dc.SetBrush(* m_brush);
+
+    if (m_cornerRadius != 0.0)
+      dc.DrawRoundedRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
+    else
+      dc.DrawRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height));
+}
+
+void wxRectangleShape::GetBoundingBoxMin(double *the_width, double *the_height)
+{
+  *the_width = m_width;
+  *the_height = m_height;
+}
+
+void wxRectangleShape::SetSize(double x, double y, bool recursive)
+{
+  SetAttachmentSize(x, y);
+  m_width = (double)wxMax(x, 1.0);
+  m_height = (double)wxMax(y, 1.0);
+  SetDefaultRegionSize();
+}
+
+void wxRectangleShape::SetCornerRadius(double rad)
+{
+  m_cornerRadius = rad;
+}
+
+// Assume (x1, y1) is centre of box (most generally, line end at box)
+bool wxRectangleShape::GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3)
+{
+  double bound_x, bound_y;
+  GetBoundingBoxMax(&bound_x, &bound_y);
+  oglFindEndForBox(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
+
+  return TRUE;
+}
+
+#ifdef PROLOGIO
+void wxRectangleShape::WriteAttributes(wxExpr *clause)
+{
+  wxShape::WriteAttributes(clause);
+  clause->AddAttributeValue("x", m_xpos);
+  clause->AddAttributeValue("y", m_ypos);
+
+  clause->AddAttributeValue("width", m_width);
+  clause->AddAttributeValue("height", m_height);
+  if (m_cornerRadius != 0.0)
+    clause->AddAttributeValue("corner", m_cornerRadius);
+}
+
+void wxRectangleShape::ReadAttributes(wxExpr *clause)
+{
+  wxShape::ReadAttributes(clause);
+  clause->AssignAttributeValue("width", &m_width);
+  clause->AssignAttributeValue("height", &m_height);
+  clause->AssignAttributeValue("corner", &m_cornerRadius);
+
+  // In case we're reading an old file, set the region's size
+  if (m_regions.Number() == 1)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
+    region->SetSize(m_width, m_height);
+  }
+}
+#endif
+
+void wxRectangleShape::Copy(wxShape& copy)
+{
+  wxShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxRectangleShape)) );
+
+  wxRectangleShape& rectCopy = (wxRectangleShape&) copy;
+  rectCopy.m_width = m_width;
+  rectCopy.m_height = m_height;
+  rectCopy.m_cornerRadius = m_cornerRadius;
+}
+
+int wxRectangleShape::GetNumberOfAttachments() const
+{
+  return wxShape::GetNumberOfAttachments();
+}
+
+
+// There are 4 attachment points on a rectangle - 0 = top, 1 = right, 2 = bottom,
+// 3 = left.
+bool wxRectangleShape::GetAttachmentPosition(int attachment, double *x, double *y,
+                                         int nth, int no_arcs, wxLineShape *line)
+{
+    return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
+}
+
+// Text object (no box)
+
+IMPLEMENT_DYNAMIC_CLASS(wxTextShape, wxRectangleShape)
+
+wxTextShape::wxTextShape(double width, double height):
+  wxRectangleShape(width, height)
+{
+}
+
+void wxTextShape::OnDraw(wxDC& dc)
+{
+}
+
+void wxTextShape::Copy(wxShape& copy)
+{
+  wxRectangleShape::Copy(copy);
+}
+
+#ifdef PROLOGIO
+void wxTextShape::WriteAttributes(wxExpr *clause)
+{
+  wxRectangleShape::WriteAttributes(clause);
+}
+#endif
+
+// Ellipse object
+
+IMPLEMENT_DYNAMIC_CLASS(wxEllipseShape, wxShape)
+
+wxEllipseShape::wxEllipseShape(double w, double h)
+{
+  m_width = w; m_height = h;
+  SetDefaultRegionSize();
+}
+
+void wxEllipseShape::GetBoundingBoxMin(double *w, double *h)
+{
+  *w = m_width; *h = m_height;
+}
+
+bool wxEllipseShape::GetPerimeterPoint(double x1, double y1,
+                                      double x2, double y2,
+                                      double *x3, double *y3)
+{
+  double bound_x, bound_y;
+  GetBoundingBoxMax(&bound_x, &bound_y);
+
+//  oglFindEndForBox(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
+  oglDrawArcToEllipse(m_xpos, m_ypos, bound_x, bound_y, x2, y2, x1, y1, x3, y3);
+
+  return TRUE;
+}
+
+void wxEllipseShape::OnDraw(wxDC& dc)
+{
+    if (m_shadowMode != SHADOW_NONE)
+    {
+      if (m_shadowBrush)
+        dc.SetBrush(* m_shadowBrush);
+      dc.SetPen(* g_oglTransparentPen);
+      dc.DrawEllipse((long) ((m_xpos - GetWidth()/2) + m_shadowOffsetX),
+                      (long) ((m_ypos - GetHeight()/2) + m_shadowOffsetY),
+                      (long) GetWidth(), (long) GetHeight());
+    }
+
+    if (m_pen)
+    {
+      if (m_pen->GetWidth() == 0)
+        dc.SetPen(* g_oglTransparentPen);
+      else
+        dc.SetPen(* m_pen);
+    }
+    if (m_brush)
+      dc.SetBrush(* m_brush);
+    dc.DrawEllipse((long) (m_xpos - GetWidth()/2), (long) (m_ypos - GetHeight()/2), (long) GetWidth(), (long) GetHeight());
+}
+
+void wxEllipseShape::SetSize(double x, double y, bool recursive)
+{
+  SetAttachmentSize(x, y);
+  m_width = x;
+  m_height = y;
+  SetDefaultRegionSize();
+}
+
+#ifdef PROLOGIO
+void wxEllipseShape::WriteAttributes(wxExpr *clause)
+{
+  wxShape::WriteAttributes(clause);
+  clause->AddAttributeValue("x", m_xpos);
+  clause->AddAttributeValue("y", m_ypos);
+
+  clause->AddAttributeValue("width", m_width);
+  clause->AddAttributeValue("height", m_height);
+}
+
+void wxEllipseShape::ReadAttributes(wxExpr *clause)
+{
+  wxShape::ReadAttributes(clause);
+  clause->AssignAttributeValue("width", &m_width);
+  clause->AssignAttributeValue("height", &m_height);
+
+  // In case we're reading an old file, set the region's size
+  if (m_regions.Number() == 1)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
+    region->SetSize(m_width, m_height);
+  }
+}
+#endif
+
+void wxEllipseShape::Copy(wxShape& copy)
+{
+  wxShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxEllipseShape)) );
+
+  wxEllipseShape& ellipseCopy = (wxEllipseShape&) copy;
+
+  ellipseCopy.m_width = m_width;
+  ellipseCopy.m_height = m_height;
+}
+
+int wxEllipseShape::GetNumberOfAttachments() const
+{
+  return wxShape::GetNumberOfAttachments();
+}
+
+// There are 4 attachment points on an ellipse - 0 = top, 1 = right, 2 = bottom,
+// 3 = left.
+bool wxEllipseShape::GetAttachmentPosition(int attachment, double *x, double *y,
+                                         int nth, int no_arcs, wxLineShape *line)
+{
+  if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
+    return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
+
+  if (m_attachmentMode != ATTACHMENT_MODE_NONE)
+  {
+    double top = (double)(m_ypos + m_height/2.0);
+    double bottom = (double)(m_ypos - m_height/2.0);
+    double left = (double)(m_xpos - m_width/2.0);
+    double right = (double)(m_xpos + m_width/2.0);
+
+    int physicalAttachment = LogicalToPhysicalAttachment(attachment);
+
+    switch (physicalAttachment)
+    {
+      case 0:
+      {
+        if (m_spaceAttachments)
+          *x = left + (nth + 1)*m_width/(no_arcs + 1);
+        else *x = m_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, m_ypos - m_height- 500) to (*x, m_ypos) intersecting
+        // the ellipse.
+        oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, *x, (double)(m_ypos-m_height-500), *x, m_ypos, x, y);
+        break;
+      }
+      case 1:
+      {
+        *x = right;
+        if (m_spaceAttachments)
+          *y = bottom + (nth + 1)*m_height/(no_arcs + 1);
+        else *y = m_ypos;
+        oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, (double)(m_xpos+m_width+500), *y, m_xpos, *y, x, y);
+        break;
+      }
+      case 2:
+      {
+        if (m_spaceAttachments)
+          *x = left + (nth + 1)*m_width/(no_arcs + 1);
+        else *x = m_xpos;
+        *y = bottom;
+        oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, *x, (double)(m_ypos+m_height+500), *x, m_ypos, x, y);
+        break;
+      }
+      case 3:
+      {
+        *x = left;
+        if (m_spaceAttachments)
+          *y = bottom + (nth + 1)*m_height/(no_arcs + 1);
+        else *y = m_ypos;
+        oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, (double)(m_xpos-m_width-500), *y, m_xpos, *y, x, y);
+        break;
+      }
+      default:
+      {
+        return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
+        break;
+      }
+    }
+    return TRUE;
+  }
+  else
+  { *x = m_xpos; *y = m_ypos; return TRUE; }
+}
+
+
+// Circle object
+IMPLEMENT_DYNAMIC_CLASS(wxCircleShape, wxEllipseShape)
+
+wxCircleShape::wxCircleShape(double diameter):wxEllipseShape(diameter, diameter)
+{
+    SetMaintainAspectRatio(TRUE);
+}
+
+void wxCircleShape::Copy(wxShape& copy)
+{
+  wxEllipseShape::Copy(copy);
+}
+
+bool wxCircleShape::GetPerimeterPoint(double x1, double y1,
+                                      double x2, double y2,
+                                      double *x3, double *y3)
+{
+  oglFindEndForCircle(m_width/2,
+                      m_xpos, m_ypos,  // Centre of circle
+                      x2, y2,  // Other end of line
+                      x3, y3);
+
+  return TRUE;
+}
+
+// Control points
+
+double wxControlPoint::sm_controlPointDragStartX = 0.0;
+double wxControlPoint::sm_controlPointDragStartY = 0.0;
+double wxControlPoint::sm_controlPointDragStartWidth = 0.0;
+double wxControlPoint::sm_controlPointDragStartHeight = 0.0;
+double wxControlPoint::sm_controlPointDragEndWidth = 0.0;
+double wxControlPoint::sm_controlPointDragEndHeight = 0.0;
+double wxControlPoint::sm_controlPointDragPosX = 0.0;
+double wxControlPoint::sm_controlPointDragPosY = 0.0;
+
+IMPLEMENT_DYNAMIC_CLASS(wxControlPoint, wxRectangleShape)
+
+wxControlPoint::wxControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):wxRectangleShape(size, size)
+{
+  m_canvas = theCanvas;
+  m_shape = object;
+  m_xoffset = the_xoffset;
+  m_yoffset = the_yoffset;
+  m_type = the_type;
+  SetPen(g_oglBlackForegroundPen);
+  SetBrush(wxBLACK_BRUSH);
+  m_oldCursor = NULL;
+  m_visible = TRUE;
+  m_eraseObject = TRUE;
+}
+
+wxControlPoint::~wxControlPoint()
+{
+}
+
+// Don't even attempt to draw any text - waste of time!
+void wxControlPoint::OnDrawContents(wxDC& dc)
+{
+}
+
+void wxControlPoint::OnDraw(wxDC& dc)
+{
+  m_xpos = m_shape->GetX() + m_xoffset;
+  m_ypos = m_shape->GetY() + m_yoffset;
+  wxRectangleShape::OnDraw(dc);
+}
+
+void wxControlPoint::OnErase(wxDC& dc)
+{
+  wxRectangleShape::OnErase(dc);
+}
+
+// Implement resizing of canvas object
+void wxControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
+}
+
+void wxControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
+}
+
+void wxControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
+}
+
+int wxControlPoint::GetNumberOfAttachments() const
+{
+  return 1;
+}
+
+bool wxControlPoint::GetAttachmentPosition(int attachment, double *x, double *y,
+                                         int nth, int no_arcs, wxLineShape *line)
+{
+  *x = m_xpos; *y = m_ypos;
+  return TRUE;
+}
+
+// Control points ('handles') redirect control to the actual shape, to make it easier
+// to override sizing behaviour.
+void wxShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
+{
+  double bound_x;
+  double bound_y;
+  this->GetBoundingBoxMin(&bound_x, &bound_y);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  if (this->GetCentreResize())
+  {
+    // Maintain the same centre point.
+    double new_width = (double)(2.0*fabs(x - this->GetX()));
+    double new_height = (double)(2.0*fabs(y - this->GetY()));
+
+    // Constrain sizing according to what control point you're dragging
+    if (pt->m_type == CONTROL_POINT_HORIZONTAL)
+    {
+        if (GetMaintainAspectRatio())
+        {
+            new_height = bound_y*(new_width/bound_x);
+        }
+        else
+            new_height = bound_y;
+    }
+    else if (pt->m_type == CONTROL_POINT_VERTICAL)
+    {
+        if (GetMaintainAspectRatio())
+        {
+            new_width = bound_x*(new_height/bound_y);
+        }
+        else
+            new_width = bound_x;
+    }
+    else if (pt->m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
+      new_height = bound_y*(new_width/bound_x);
+
+    if (this->GetFixedWidth())
+      new_width = bound_x;
+
+    if (this->GetFixedHeight())
+      new_height = bound_y;
+
+    pt->sm_controlPointDragEndWidth = new_width;
+    pt->sm_controlPointDragEndHeight = new_height;
+
+    this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
+                                new_width, new_height);
+  }
+  else
+  {
+    // Don't maintain the same centre point!
+    double newX1 = wxMin(pt->sm_controlPointDragStartX, x);
+    double newY1 = wxMin(pt->sm_controlPointDragStartY, y);
+    double newX2 = wxMax(pt->sm_controlPointDragStartX, x);
+    double newY2 = wxMax(pt->sm_controlPointDragStartY, y);
+    if (pt->m_type == CONTROL_POINT_HORIZONTAL)
+    {
+      newY1 = pt->sm_controlPointDragStartY;
+      newY2 = newY1 + pt->sm_controlPointDragStartHeight;
+    }
+    else if (pt->m_type == CONTROL_POINT_VERTICAL)
+    {
+      newX1 = pt->sm_controlPointDragStartX;
+      newX2 = newX1 + pt->sm_controlPointDragStartWidth;
+    }
+    else if (pt->m_type == CONTROL_POINT_DIAGONAL && ((keys & KEY_SHIFT) || GetMaintainAspectRatio()))
+    {
+      double newH = (double)((newX2 - newX1)*(pt->sm_controlPointDragStartHeight/pt->sm_controlPointDragStartWidth));
+      if (GetY() > pt->sm_controlPointDragStartY)
+        newY2 = (double)(newY1 + newH);
+      else
+        newY1 = (double)(newY2 - newH);
+    }
+    double newWidth = (double)(newX2 - newX1);
+    double newHeight = (double)(newY2 - newY1);
+
+    if (pt->m_type == CONTROL_POINT_VERTICAL && GetMaintainAspectRatio())
+    {
+        newWidth = bound_x * (newHeight/bound_y) ;
+    }
+
+    if (pt->m_type == CONTROL_POINT_HORIZONTAL && GetMaintainAspectRatio())
+    {
+        newHeight = bound_y * (newWidth/bound_x) ;
+    }
+
+    pt->sm_controlPointDragPosX = (double)(newX1 + (newWidth/2.0));
+    pt->sm_controlPointDragPosY = (double)(newY1 + (newHeight/2.0));
+    if (this->GetFixedWidth())
+      newWidth = bound_x;
+
+    if (this->GetFixedHeight())
+      newHeight = bound_y;
+
+    pt->sm_controlPointDragEndWidth = newWidth;
+    pt->sm_controlPointDragEndHeight = newHeight;
+    this->GetEventHandler()->OnDrawOutline(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY, newWidth, newHeight);
+  }
+}
+
+void wxShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  m_canvas->CaptureMouse();
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+/*
+  if (pt->m_eraseObject)
+    this->Erase(dc);
+*/
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  double bound_x;
+  double bound_y;
+  this->GetBoundingBoxMin(&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() < this->GetX())
+    pt->sm_controlPointDragStartX = (double)(this->GetX() + (bound_x/2.0));
+  else
+    pt->sm_controlPointDragStartX = (double)(this->GetX() - (bound_x/2.0));
+
+  if (pt->GetY() < this->GetY())
+    pt->sm_controlPointDragStartY = (double)(this->GetY() + (bound_y/2.0));
+  else
+    pt->sm_controlPointDragStartY = (double)(this->GetY() - (bound_y/2.0));
+
+  if (pt->m_type == CONTROL_POINT_HORIZONTAL)
+    pt->sm_controlPointDragStartY = (double)(this->GetY() - (bound_y/2.0));
+  else if (pt->m_type == CONTROL_POINT_VERTICAL)
+    pt->sm_controlPointDragStartX = (double)(this->GetX() - (bound_x/2.0));
+
+  // We may require the old width and height.
+  pt->sm_controlPointDragStartWidth = bound_x;
+  pt->sm_controlPointDragStartHeight = bound_y;
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  if (this->GetCentreResize())
+  {
+    double new_width = (double)(2.0*fabs(x - this->GetX()));
+    double new_height = (double)(2.0*fabs(y - this->GetY()));
+
+    // Constrain sizing according to what control point you're dragging
+    if (pt->m_type == CONTROL_POINT_HORIZONTAL)
+    {
+        if (GetMaintainAspectRatio())
+        {
+            new_height = bound_y*(new_width/bound_x);
+        }
+        else
+            new_height = bound_y;
+    }
+    else if (pt->m_type == CONTROL_POINT_VERTICAL)
+    {
+        if (GetMaintainAspectRatio())
+        {
+            new_width = bound_x*(new_height/bound_y);
+        }
+        else
+            new_width = bound_x;
+    }
+    else if (pt->m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
+      new_height = bound_y*(new_width/bound_x);
+
+    if (this->GetFixedWidth())
+      new_width = bound_x;
+
+    if (this->GetFixedHeight())
+      new_height = bound_y;
+
+    pt->sm_controlPointDragEndWidth = new_width;
+    pt->sm_controlPointDragEndHeight = new_height;
+    this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
+                                new_width, new_height);
+  }
+  else
+  {
+    // Don't maintain the same centre point!
+    double newX1 = wxMin(pt->sm_controlPointDragStartX, x);
+    double newY1 = wxMin(pt->sm_controlPointDragStartY, y);
+    double newX2 = wxMax(pt->sm_controlPointDragStartX, x);
+    double newY2 = wxMax(pt->sm_controlPointDragStartY, y);
+    if (pt->m_type == CONTROL_POINT_HORIZONTAL)
+    {
+      newY1 = pt->sm_controlPointDragStartY;
+      newY2 = newY1 + pt->sm_controlPointDragStartHeight;
+    }
+    else if (pt->m_type == CONTROL_POINT_VERTICAL)
+    {
+      newX1 = pt->sm_controlPointDragStartX;
+      newX2 = newX1 + pt->sm_controlPointDragStartWidth;
+    }
+    else if (pt->m_type == CONTROL_POINT_DIAGONAL && ((keys & KEY_SHIFT) || GetMaintainAspectRatio()))
+    {
+      double newH = (double)((newX2 - newX1)*(pt->sm_controlPointDragStartHeight/pt->sm_controlPointDragStartWidth));
+      if (pt->GetY() > pt->sm_controlPointDragStartY)
+        newY2 = (double)(newY1 + newH);
+      else
+        newY1 = (double)(newY2 - newH);
+    }
+    double newWidth = (double)(newX2 - newX1);
+    double newHeight = (double)(newY2 - newY1);
+
+    if (pt->m_type == CONTROL_POINT_VERTICAL && GetMaintainAspectRatio())
+    {
+        newWidth = bound_x * (newHeight/bound_y) ;
+    }
+
+    if (pt->m_type == CONTROL_POINT_HORIZONTAL && GetMaintainAspectRatio())
+    {
+        newHeight = bound_y * (newWidth/bound_x) ;
+    }
+
+    pt->sm_controlPointDragPosX = (double)(newX1 + (newWidth/2.0));
+    pt->sm_controlPointDragPosY = (double)(newY1 + (newHeight/2.0));
+    if (this->GetFixedWidth())
+      newWidth = bound_x;
+
+    if (this->GetFixedHeight())
+      newHeight = bound_y;
+
+    pt->sm_controlPointDragEndWidth = newWidth;
+    pt->sm_controlPointDragEndHeight = newHeight;
+    this->GetEventHandler()->OnDrawOutline(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY, newWidth, newHeight);
+  }
+}
+
+void wxShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  m_canvas->ReleaseMouse();
+  dc.SetLogicalFunction(wxCOPY);
+  this->Recompute();
+  this->ResetControlPoints();
+
+  this->Erase(dc);
+/*
+  if (!pt->m_eraseObject)
+    this->Show(FALSE);
+*/
+
+  this->SetSize(pt->sm_controlPointDragEndWidth, pt->sm_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.
+  wxShape *theObject = this;
+  wxShapeCanvas *theCanvas = m_canvas;
+  bool eraseIt = pt->m_eraseObject;
+
+  if (theObject->GetCentreResize())
+    theObject->Move(dc, theObject->GetX(), theObject->GetY());
+  else
+    theObject->Move(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY);
+
+/*
+  if (!eraseIt)
+    theObject->Show(TRUE);
+*/
+
+  // Recursively redraw links if we have a composite.
+  if (theObject->GetChildren().Number() > 0)
+    theObject->DrawLinks(dc, -1, TRUE);
+
+  double width, height;
+  theObject->GetBoundingBoxMax(&width, &height);
+  theObject->GetEventHandler()->OnEndSize(width, height);
+
+  if (!theCanvas->GetQuickEditMode() && eraseIt) theCanvas->Redraw(dc);
+}
+
+
+
+// Polygon control points
+
+IMPLEMENT_DYNAMIC_CLASS(wxPolygonControlPoint, wxControlPoint)
+
+wxPolygonControlPoint::wxPolygonControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size,
+  wxRealPoint *vertex, double the_xoffset, double the_yoffset):
+  wxControlPoint(theCanvas, object, size, the_xoffset, the_yoffset, 0)
+{
+  m_polygonVertex = vertex;
+  m_originalDistance = 0.0;
+}
+
+wxPolygonControlPoint::~wxPolygonControlPoint()
+{
+}
+
+// Calculate what new size would be, at end of resize
+void wxPolygonControlPoint::CalculateNewSize(double x, double y)
+{
+  double bound_x;
+  double bound_y;
+  GetShape()->GetBoundingBoxMin(&bound_x, &bound_y);
+
+  double dist = (double)sqrt((x - m_shape->GetX())*(x - m_shape->GetX()) +
+                    (y - m_shape->GetY())*(y - m_shape->GetY()));
+
+  m_newSize.x = (double)(dist/this->m_originalDistance)*this->m_originalSize.x;
+  m_newSize.y = (double)(dist/this->m_originalDistance)*this->m_originalSize.y;
+}
+
+
+// Implement resizing polygon or moving the vertex.
+void wxPolygonControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
+}
+
+void wxPolygonControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
+}
+
+void wxPolygonControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
+}
+
+// Control points ('handles') redirect control to the actual shape, to make it easier
+// to override sizing behaviour.
+void wxPolygonShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
+{
+  wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  if (0) // keys & KEY_CTRL)
+  {
+    // TODO: mend this code. Currently we rely on altering the
+    // actual points, but we should assume we're not, as per
+    // the normal sizing case.
+    m_canvas->Snap(&x, &y);
+
+    // Move point
+    ppt->m_polygonVertex->x = x - this->GetX();
+    ppt->m_polygonVertex->y = y - this->GetY();
+    ppt->SetX(x);
+    ppt->SetY(y);
+    ((wxPolygonShape *)this)->CalculateBoundingBox();
+    ((wxPolygonShape *)this)->CalculatePolygonCentre();
+  }
+  else
+  {
+    ppt->CalculateNewSize(x, y);
+  }
+
+  this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
+       ppt->GetNewSize().x, ppt->GetNewSize().y);
+}
+
+void wxPolygonShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  this->Erase(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  double bound_x;
+  double bound_y;
+  this->GetBoundingBoxMin(&bound_x, &bound_y);
+
+  double dist = (double)sqrt((x - this->GetX())*(x - this->GetX()) +
+                    (y - this->GetY())*(y - this->GetY()));
+  ppt->m_originalDistance = dist;
+  ppt->m_originalSize.x = bound_x;
+  ppt->m_originalSize.y = bound_y;
+
+  if (ppt->m_originalDistance == 0.0) ppt->m_originalDistance = (double) 0.0001;
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  if (0) // keys & KEY_CTRL)
+  {
+    // TODO: mend this code. Currently we rely on altering the
+    // actual points, but we should assume we're not, as per
+    // the normal sizing case.
+    m_canvas->Snap(&x, &y);
+
+    // Move point
+    ppt->m_polygonVertex->x = x - this->GetX();
+    ppt->m_polygonVertex->y = y - this->GetY();
+    ppt->SetX(x);
+    ppt->SetY(y);
+    ((wxPolygonShape *)this)->CalculateBoundingBox();
+    ((wxPolygonShape *)this)->CalculatePolygonCentre();
+  }
+  else
+  {
+    ppt->CalculateNewSize(x, y);
+  }
+
+  this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
+       ppt->GetNewSize().x, ppt->GetNewSize().y);
+
+  m_canvas->CaptureMouse();
+}
+
+void wxPolygonShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  m_canvas->ReleaseMouse();
+  dc.SetLogicalFunction(wxCOPY);
+
+  // If we're changing shape, must reset the original points
+  if (keys & KEY_CTRL)
+  {
+    ((wxPolygonShape *)this)->CalculateBoundingBox();
+    ((wxPolygonShape *)this)->UpdateOriginalPoints();
+  }
+  else
+  {
+    SetSize(ppt->GetNewSize().x, ppt->GetNewSize().y);
+  }
+
+  ((wxPolygonShape *)this)->CalculateBoundingBox();
+  ((wxPolygonShape *)this)->CalculatePolygonCentre();
+
+  this->Recompute();
+  this->ResetControlPoints();
+  this->Move(dc, this->GetX(), this->GetY());
+  if (!m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+
+/*
+ * Object region
+ *
+ */
+IMPLEMENT_DYNAMIC_CLASS(wxShapeRegion, wxObject)
+
+wxShapeRegion::wxShapeRegion()
+{
+  m_regionText = "";
+  m_font = g_oglNormalFont;
+  m_minHeight = 5.0;
+  m_minWidth = 5.0;
+  m_width = 0.0;
+  m_height = 0.0;
+  m_x = 0.0;
+  m_y = 0.0;
+
+  m_regionProportionX = -1.0;
+  m_regionProportionY = -1.0;
+  m_formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT;
+  m_regionName = "";
+  m_textColour = "BLACK";
+  m_penColour = "BLACK";
+  m_penStyle = wxSOLID;
+  m_actualColourObject = NULL;
+  m_actualPenObject = NULL;
+}
+
+wxShapeRegion::wxShapeRegion(wxShapeRegion& region)
+{
+  m_regionText = region.m_regionText;
+  m_regionName = region.m_regionName;
+  m_textColour = region.m_textColour;
+
+  m_font = region.m_font;
+  m_minHeight = region.m_minHeight;
+  m_minWidth = region.m_minWidth;
+  m_width = region.m_width;
+  m_height = region.m_height;
+  m_x = region.m_x;
+  m_y = region.m_y;
+
+  m_regionProportionX = region.m_regionProportionX;
+  m_regionProportionY = region.m_regionProportionY;
+  m_formatMode = region.m_formatMode;
+  m_actualColourObject = NULL;
+  m_actualPenObject = NULL;
+  m_penStyle = region.m_penStyle;
+  m_penColour = region.m_penColour;
+
+  ClearText();
+  wxNode *node = region.m_formattedText.First();
+  while (node)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)node->Data();
+    wxShapeTextLine *new_line =
+      new wxShapeTextLine(line->GetX(), line->GetY(), line->GetText());
+    m_formattedText.Append(new_line);
+    node = node->Next();
+  }
+}
+
+wxShapeRegion::~wxShapeRegion()
+{
+  ClearText();
+}
+
+void wxShapeRegion::ClearText()
+{
+  wxNode *node = m_formattedText.First();
+  while (node)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)node->Data();
+    wxNode *next = node->Next();
+    delete line;
+    delete node;
+    node = next;
+  }
+}
+
+void wxShapeRegion::SetFont(wxFont *f)
+{
+  m_font = f;
+}
+
+void wxShapeRegion::SetMinSize(double w, double h)
+{
+  m_minWidth = w;
+  m_minHeight = h;
+}
+
+void wxShapeRegion::SetSize(double w, double h)
+{
+  m_width = w;
+  m_height = h;
+}
+
+void wxShapeRegion::SetPosition(double xp, double yp)
+{
+  m_x = xp;
+  m_y = yp;
+}
+
+void wxShapeRegion::SetProportions(double xp, double yp)
+{
+  m_regionProportionX = xp;
+  m_regionProportionY = yp;
+}
+
+void wxShapeRegion::SetFormatMode(int mode)
+{
+  m_formatMode = mode;
+}
+
+void wxShapeRegion::SetColour(const wxString& col)
+{
+  m_textColour = col;
+  m_actualColourObject = NULL;
+}
+
+wxColour *wxShapeRegion::GetActualColourObject()
+{
+  if (!m_actualColourObject)
+    m_actualColourObject = wxTheColourDatabase->FindColour(GetColour());
+  if (!m_actualColourObject)
+    m_actualColourObject = wxBLACK;
+  return m_actualColourObject;
+}
+
+void wxShapeRegion::SetPenColour(const wxString& col)
+{
+  m_penColour = col;
+  m_actualPenObject = NULL;
+}
+
+// Returns NULL if the pen is invisible
+// (different to pen being transparent; indicates that
+// region boundary should not be drawn.)
+wxPen *wxShapeRegion::GetActualPen()
+{
+  if (m_actualPenObject)
+    return m_actualPenObject;
+
+  if (!m_penColour) return NULL;
+  if (m_penColour == "Invisible")
+    return NULL;
+  m_actualPenObject = wxThePenList->FindOrCreatePen(m_penColour, 1, m_penStyle);
+  return m_actualPenObject;
+}
+
+
diff --git a/src/ogl/bmpshape.cpp b/src/ogl/bmpshape.cpp
new file mode 100644 (file)
index 0000000..fc6218e
--- /dev/null
@@ -0,0 +1,115 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        bmpshape.cpp
+// Purpose:     Bitmap shape class
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "bmpshape.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/bmpshape.h>
+#include <wx/ogl/misc.h>
+
+/*
+ * Bitmap object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxBitmapShape, wxShape)
+
+wxBitmapShape::wxBitmapShape():wxRectangleShape(100.0, 50.0)
+{
+  m_filename = "";
+}
+
+wxBitmapShape::~wxBitmapShape()
+{
+}
+
+void wxBitmapShape::OnDraw(wxDC& dc)
+{
+  if (!m_bitmap.Ok())
+    return;
+    
+  wxMemoryDC tempDC;
+  tempDC.SelectObject(m_bitmap);
+  double x, y;
+  x = WXROUND(m_xpos - m_bitmap.GetWidth() / 2.0);
+  y = WXROUND(m_ypos - m_bitmap.GetHeight() / 2.0);
+  dc.Blit((long) x, (long) y, m_bitmap.GetWidth(), m_bitmap.GetHeight(), &tempDC, 0, 0);
+}
+
+void wxBitmapShape::SetSize(double w, double h, bool recursive)
+{
+  if (m_bitmap.Ok())
+  {
+    w = m_bitmap.GetWidth();
+    h = m_bitmap.GetHeight();
+  }
+
+  SetAttachmentSize(w, h);
+
+  m_width = w;
+  m_height = h;
+  SetDefaultRegionSize();
+}
+
+#ifdef PROLOGIO
+void wxBitmapShape::WriteAttributes(wxExpr *clause)
+{
+  // Can't really save the bitmap; so instantiate the bitmap
+  // at a higher level in the application, from a symbol library.
+  wxRectangleShape::WriteAttributes(clause);
+  clause->AddAttributeValueString("filename", m_filename);
+}
+
+void wxBitmapShape::ReadAttributes(wxExpr *clause)
+{
+  wxRectangleShape::ReadAttributes(clause);
+  clause->GetAttributeValue("filename", m_filename);
+}
+#endif
+
+// Does the copying for this object
+void wxBitmapShape::Copy(wxShape& copy)
+{
+  wxRectangleShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxBitmapShape)) ) ;
+
+  wxBitmapShape& bitmapCopy = (wxBitmapShape&) copy;
+
+  bitmapCopy.m_bitmap = m_bitmap;
+  bitmapCopy.SetFilename(m_filename);
+}
+
+void wxBitmapShape::SetBitmap(const wxBitmap& bm)
+{
+  m_bitmap = bm;
+  if (m_bitmap.Ok())
+    SetSize(m_bitmap.GetWidth(), m_bitmap.GetHeight());
+}
+
+
diff --git a/src/ogl/canvas.cpp b/src/ogl/canvas.cpp
new file mode 100644 (file)
index 0000000..02bae38
--- /dev/null
@@ -0,0 +1,516 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        canvas.cpp
+// Purpose:     Shape canvas class
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "canvas.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/ogldiag.h>
+#include <wx/ogl/misc.h>
+#include <wx/ogl/lines.h>
+#include <wx/ogl/composit.h>
+
+#define CONTROL_POINT_SIZE       6
+
+// Control point types
+// Rectangle and most other shapes
+#define CONTROL_POINT_VERTICAL   1
+#define CONTROL_POINT_HORIZONTAL 2
+#define CONTROL_POINT_DIAGONAL   3
+
+// Line
+#define CONTROL_POINT_ENDPOINT_TO 4
+#define CONTROL_POINT_ENDPOINT_FROM 5
+#define CONTROL_POINT_LINE       6
+
+extern wxCursor *g_oglBullseyeCursor;
+
+IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
+
+BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
+    EVT_PAINT(wxShapeCanvas::OnPaint)
+    EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
+END_EVENT_TABLE()
+
+// Object canvas
+wxShapeCanvas::wxShapeCanvas(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style):
+  wxScrolledWindow(parent, id, pos, size, style)
+{
+  m_shapeDiagram = NULL;
+  m_dragState = NoDragging;
+  m_draggedShape = NULL;
+  m_oldDragX = 0;
+  m_oldDragY = 0;
+  m_firstDragX = 0;
+  m_firstDragY = 0;
+  m_checkTolerance = TRUE;
+}
+
+wxShapeCanvas::~wxShapeCanvas()
+{
+}
+
+void wxShapeCanvas::OnPaint(wxPaintEvent& event)
+{
+    wxPaintDC dc(this);
+
+    PrepareDC(dc);
+
+    dc.Clear();
+
+       if (GetDiagram())
+               GetDiagram()->Redraw(dc);
+}
+
+void wxShapeCanvas::OnMouseEvent(wxMouseEvent& event)
+{
+  wxClientDC dc(this);
+  PrepareDC(dc);
+
+  wxPoint logPos(event.GetLogicalPosition(dc));
+
+  double x, y;
+  x = (double) logPos.x;
+  y = (double) logPos.y;
+
+  int keys = 0;
+  if (event.ShiftDown())
+    keys = keys | KEY_SHIFT;
+  if (event.ControlDown())
+    keys = keys | KEY_CTRL;
+
+  bool dragging = event.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)
+  {
+    int dx = abs(dc.LogicalToDeviceX((long) (x - m_firstDragX)));
+    int dy = abs(dc.LogicalToDeviceY((long) (y - m_firstDragY)));
+    if (m_checkTolerance && (dx <= GetDiagram()->GetMouseTolerance()) && (dy <= GetDiagram()->GetMouseTolerance()))
+    {
+      return;
+    }
+    else
+      // If we've ignored the tolerance once, then ALWAYS ignore
+      // tolerance in this drag, even if we come back within
+      // the tolerance range.
+      m_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 && m_draggedShape && m_dragState == StartDraggingLeft)
+  {
+    m_dragState = ContinueDraggingLeft;
+
+    // If the object isn't m_draggable, transfer message to canvas
+    if (m_draggedShape->Draggable())
+      m_draggedShape->GetEventHandler()->OnBeginDragLeft((double)x, (double)y, keys, m_draggedAttachment);
+    else
+    {
+      m_draggedShape = NULL;
+      OnBeginDragLeft((double)x, (double)y, keys);
+    }
+
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (dragging && m_draggedShape && m_dragState == ContinueDraggingLeft)
+  {
+    // Continue dragging
+    m_draggedShape->GetEventHandler()->OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+    m_draggedShape->GetEventHandler()->OnDragLeft(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (event.LeftUp() && m_draggedShape && m_dragState == ContinueDraggingLeft)
+  {
+    m_dragState = NoDragging;
+    m_checkTolerance = TRUE;
+
+    m_draggedShape->GetEventHandler()->OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+
+    m_draggedShape->GetEventHandler()->OnEndDragLeft((double)x, (double)y, keys, m_draggedAttachment);
+    m_draggedShape = NULL;
+  }
+  else if (dragging && m_draggedShape && m_dragState == StartDraggingRight)
+  {
+    m_dragState = ContinueDraggingRight;
+
+    if (m_draggedShape->Draggable())
+      m_draggedShape->GetEventHandler()->OnBeginDragRight((double)x, (double)y, keys, m_draggedAttachment);
+    else
+    {
+      m_draggedShape = NULL;
+      OnBeginDragRight((double)x, (double)y, keys);
+    }
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (dragging && m_draggedShape && m_dragState == ContinueDraggingRight)
+  {
+    // Continue dragging
+    m_draggedShape->GetEventHandler()->OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+    m_draggedShape->GetEventHandler()->OnDragRight(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (event.RightUp() && m_draggedShape && m_dragState == ContinueDraggingRight)
+  {
+    m_dragState = NoDragging;
+    m_checkTolerance = TRUE;
+
+    m_draggedShape->GetEventHandler()->OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+
+    m_draggedShape->GetEventHandler()->OnEndDragRight((double)x, (double)y, keys, m_draggedAttachment);
+    m_draggedShape = NULL;
+  }
+
+  // All following events sent to canvas, not object
+  else if (dragging && !m_draggedShape && m_dragState == StartDraggingLeft)
+  {
+    m_dragState = ContinueDraggingLeft;
+    OnBeginDragLeft((double)x, (double)y, keys);
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingLeft)
+  {
+    // Continue dragging
+    OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys);
+    OnDragLeft(TRUE, (double)x, (double)y, keys);
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (event.LeftUp() && !m_draggedShape && m_dragState == ContinueDraggingLeft)
+  {
+    m_dragState = NoDragging;
+    m_checkTolerance = TRUE;
+
+    OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys);
+    OnEndDragLeft((double)x, (double)y, keys);
+    m_draggedShape = NULL;
+  }
+  else if (dragging && !m_draggedShape && m_dragState == StartDraggingRight)
+  {
+    m_dragState = ContinueDraggingRight;
+    OnBeginDragRight((double)x, (double)y, keys);
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingRight)
+  {
+    // Continue dragging
+    OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys);
+    OnDragRight(TRUE, (double)x, (double)y, keys);
+    m_oldDragX = x; m_oldDragY = y;
+  }
+  else if (event.RightUp() && !m_draggedShape && m_dragState == ContinueDraggingRight)
+  {
+    m_dragState = NoDragging;
+    m_checkTolerance = TRUE;
+
+    OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys);
+    OnEndDragRight((double)x, (double)y, keys);
+    m_draggedShape = NULL;
+  }
+
+  // Non-dragging events
+  else if (event.IsButton())
+  {
+    m_checkTolerance = TRUE;
+
+    // Find the nearest object
+    int attachment = 0;
+    wxShape *nearest_object = FindShape(x, y, &attachment);
+    if (nearest_object) // Object event
+    {
+      if (event.LeftDown())
+      {
+        m_draggedShape = nearest_object;
+        m_draggedAttachment = attachment;
+        m_dragState = StartDraggingLeft;
+        m_firstDragX = x;
+        m_firstDragY = y;
+      }
+      else if (event.LeftUp())
+      {
+        // N.B. Only register a click if the same object was
+        // identified for down *and* up.
+        if (nearest_object == m_draggedShape)
+          nearest_object->GetEventHandler()->OnLeftClick((double)x, (double)y, keys, attachment);
+
+        m_draggedShape = NULL;
+        m_dragState = NoDragging;
+      }
+      else if (event.LeftDClick())
+      {
+        nearest_object->GetEventHandler()->OnLeftDoubleClick((double)x, (double)y, keys, attachment);
+
+        m_draggedShape = NULL;
+        m_dragState = NoDragging;
+      }
+      else if (event.RightDown())
+      {
+        m_draggedShape = nearest_object;
+        m_draggedAttachment = attachment;
+        m_dragState = StartDraggingRight;
+        m_firstDragX = x;
+        m_firstDragY = y;
+      }
+      else if (event.RightUp())
+      {
+        if (nearest_object == m_draggedShape)
+          nearest_object->GetEventHandler()->OnRightClick((double)x, (double)y, keys, attachment);
+
+        m_draggedShape = NULL;
+        m_dragState = NoDragging;
+      }
+    }
+    else // Canvas event (no nearest object)
+    {
+      if (event.LeftDown())
+      {
+        m_draggedShape = NULL;
+        m_dragState = StartDraggingLeft;
+        m_firstDragX = x;
+        m_firstDragY = y;
+      }
+      else if (event.LeftUp())
+      {
+        OnLeftClick((double)x, (double)y, keys);
+
+        m_draggedShape = NULL;
+        m_dragState = NoDragging;
+      }
+      else if (event.RightDown())
+      {
+        m_draggedShape = NULL;
+        m_dragState = StartDraggingRight;
+        m_firstDragX = x;
+        m_firstDragY = y;
+      }
+      else if (event.RightUp())
+      {
+        OnRightClick((double)x, (double)y, keys);
+
+        m_draggedShape = NULL;
+        m_dragState = NoDragging;
+      }
+    }
+  }
+}
+
+/*
+ * Try to find a sensitive object, working up the hierarchy of composites.
+ *
+ */
+wxShape *wxShapeCanvas::FindFirstSensitiveShape(double x, double y, int *new_attachment, int op)
+{
+  wxShape *image = FindShape(x, y, new_attachment);
+  if (!image) return NULL;
+
+  wxShape *actualImage = FindFirstSensitiveShape1(image, op);
+  if (actualImage)
+  {
+    double dist;
+    // Find actual attachment
+    actualImage->HitTest(x, y, new_attachment, &dist);
+  }
+  return actualImage;
+}
+
+wxShape *wxShapeCanvas::FindFirstSensitiveShape1(wxShape *image, int op)
+{
+  if (image->GetSensitivityFilter() & op)
+    return image;
+  if (image->GetParent())
+    return FindFirstSensitiveShape1(image->GetParent(), op);
+  return NULL;
+}
+
+// Helper function: TRUE if 'contains' wholly contains 'contained'.
+static bool WhollyContains(wxShape *contains, wxShape *contained)
+{
+  double xp1, yp1, xp2, yp2;
+  double w1, h1, w2, h2;
+  double left1, top1, right1, bottom1, left2, top2, right2, bottom2;
+
+  xp1 = contains->GetX(); yp1 = contains->GetY(); xp2 = contained->GetX(); yp2 = contained->GetY();
+  contains->GetBoundingBoxMax(&w1, &h1);
+  contained->GetBoundingBoxMax(&w2, &h2);
+
+  left1 = (double)(xp1 - (w1 / 2.0));
+  top1 = (double)(yp1 - (h1 / 2.0));
+  right1 = (double)(xp1 + (w1 / 2.0));
+  bottom1 = (double)(yp1 + (h1 / 2.0));
+
+  left2 = (double)(xp2 - (w2 / 2.0));
+  top2 = (double)(yp2 - (h2 / 2.0));
+  right2 = (double)(xp2 + (w2 / 2.0));
+  bottom2 = (double)(yp2 + (h2 / 2.0));
+
+  return ((left1 <= left2) && (top1 <= top2) && (right1 >= right2) && (bottom1 >= bottom2));
+}
+
+wxShape *wxShapeCanvas::FindShape(double x, double y, int *attachment, wxClassInfo *info, wxShape *notObject)
+{
+  double nearest = 100000.0;
+  int nearest_attachment = 0;
+  wxShape *nearest_object = NULL;
+
+  // 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
+
+  wxNode *current = GetDiagram()->GetShapeList()->Last();
+  while (current)
+  {
+    wxShape *object = (wxShape *)current->Data();
+
+    double dist;
+    int temp_attachment;
+
+    // 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() &&
+        object->IsKindOf(CLASSINFO(wxLineShape)) &&
+        object->HitTest(x, y, &temp_attachment, &dist) &&
+        ((info == NULL) || object->IsKindOf(info)) &&
+        (!notObject || !notObject->HasDescendant(object)))
+    {
+      // 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;
+      }
+    }
+    if (current)
+      current = current->Previous();
+  }
+
+  current = GetDiagram()->GetShapeList()->Last();
+  while (current)
+  {
+    wxShape *object = (wxShape *)current->Data();
+    double dist;
+    int temp_attachment;
+
+    // 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() && (object->IsKindOf(CLASSINFO(wxDivisionShape)) || !object->IsKindOf(CLASSINFO(wxCompositeShape)))
+        && object->HitTest(x, y, &temp_attachment, &dist) && ((info == NULL) || object->IsKindOf(info)) &&
+        (!notObject || !notObject->HasDescendant(object)))
+    {
+      if (!object->IsKindOf(CLASSINFO(wxLineShape)))
+      {
+        // 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 (!nearest_object || !(object->IsKindOf(CLASSINFO(wxDivisionShape)) || WhollyContains(object, nearest_object)))
+        {
+          nearest = dist;
+          nearest_object = object;
+          nearest_attachment = temp_attachment;
+          current = NULL;
+        }
+      }
+    }
+    if (current)
+      current = current->Previous();
+  }
+
+  *attachment = nearest_attachment;
+  return nearest_object;
+}
+
+/*
+ * Higher-level events called by OnEvent
+ *
+ */
+
+void wxShapeCanvas::OnLeftClick(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnRightClick(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnDragLeft(bool draw, double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnBeginDragLeft(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnEndDragLeft(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnDragRight(bool draw, double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnBeginDragRight(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnEndDragRight(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
+ { GetDiagram()->AddShape(object, addAfter); }
+void wxShapeCanvas::InsertShape(wxShape *object)
+ { GetDiagram()->InsertShape(object); }
+void wxShapeCanvas::RemoveShape(wxShape *object)
+ { GetDiagram()->RemoveShape(object); }
+bool wxShapeCanvas::GetQuickEditMode()
+ { return GetDiagram()->GetQuickEditMode(); }
+void wxShapeCanvas::Redraw(wxDC& dc)
+ { GetDiagram()->Redraw(dc); }
+void wxShapeCanvas::Snap(double *x, double *y)
+ { GetDiagram()->Snap(x, y); }
diff --git a/src/ogl/composit.cpp b/src/ogl/composit.cpp
new file mode 100644 (file)
index 0000000..ba237db
--- /dev/null
@@ -0,0 +1,1784 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        composit.cpp
+// Purpose:     Composite OGL class
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "composit.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/constrnt.h>
+#include <wx/ogl/composit.h>
+#include <wx/ogl/misc.h>
+#include <wx/ogl/canvas.h>
+
+// Sometimes, objects need to access the whole database to
+// construct themselves.
+wxExprDatabase *GlobalwxExprDatabase = NULL;
+
+
+/*
+ * Division control point
+ */
+
+class wxDivisionControlPoint: public wxControlPoint
+{
+ DECLARE_DYNAMIC_CLASS(wxDivisionControlPoint)
+ public:
+  wxDivisionControlPoint() {}
+  wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type);
+  ~wxDivisionControlPoint();
+
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDivisionControlPoint, wxControlPoint)
+
+/*
+ * Composite object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxCompositeShape, wxRectangleShape)
+
+wxCompositeShape::wxCompositeShape(): wxRectangleShape(10.0, 10.0)
+{
+//  selectable = FALSE;
+  m_oldX = m_xpos;
+  m_oldY = m_ypos;
+}
+
+wxCompositeShape::~wxCompositeShape()
+{
+  wxNode *node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+    delete constraint;
+    node = node->Next();
+  }
+  node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    wxNode *next = node->Next();
+    object->Unlink();
+    delete object;
+    node = next;
+  }
+}
+
+void wxCompositeShape::OnDraw(wxDC& dc)
+{
+  double x1 = (double)(m_xpos - m_width/2.0);
+  double y1 = (double)(m_ypos - m_height/2.0);
+
+  if (m_shadowMode != SHADOW_NONE)
+  {
+    if (m_shadowBrush)
+      dc.SetBrush(* m_shadowBrush);
+    dc.SetPen(* g_oglTransparentPen);
+
+    if (m_cornerRadius != 0.0)
+      dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
+                               WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
+    else
+      dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
+  }
+}
+
+void wxCompositeShape::OnDrawContents(wxDC& dc)
+{
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    object->Draw(dc);
+    object->DrawLinks(dc);
+    node = node->Next();
+  }
+  wxShape::OnDrawContents(dc);
+}
+
+bool wxCompositeShape::OnMovePre(wxDC& dc, double x, double y, double oldx, double oldy, bool display)
+{
+  double diffX = x - oldx;
+  double diffY = y - oldy;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+
+    object->Erase(dc);
+    object->Move(dc, object->GetX() + diffX, object->GetY() + diffY, display);
+
+    node = node->Next();
+  }
+  return TRUE;
+}
+
+void wxCompositeShape::OnErase(wxDC& dc)
+{
+  wxRectangleShape::OnErase(dc);
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    object->Erase(dc);
+    node = node->Next();
+  }
+}
+
+static double objectStartX = 0.0;
+static double objectStartY = 0.0;
+
+void wxCompositeShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  double xx = x;
+  double yy = y;
+  m_canvas->Snap(&xx, &yy);
+  double offsetX = xx - objectStartX;
+  double offsetY = yy - objectStartY;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
+//  wxShape::OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxCompositeShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  objectStartX = x;
+  objectStartY = y;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  Erase(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+  m_canvas->CaptureMouse();
+
+  double xx = x;
+  double yy = y;
+  m_canvas->Snap(&xx, &yy);
+  double offsetX = xx - objectStartX;
+  double offsetY = yy - objectStartY;
+
+  GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
+
+//  wxShape::OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxCompositeShape::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+//  wxShape::OnEndDragLeft(x, y, keys, attachment);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  m_canvas->ReleaseMouse();
+
+  if (!m_draggable)
+  {
+    if (m_parent) m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, 0);
+    return;
+  }
+
+  dc.SetLogicalFunction(wxCOPY);
+  double xx = x;
+  double yy = y;
+  m_canvas->Snap(&xx, &yy);
+  double offsetX = xx - objectStartX;
+  double offsetY = yy - objectStartY;
+
+  Move(dc, GetX() + offsetX, GetY() + offsetY);
+
+  if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+
+void wxCompositeShape::OnRightClick(double x, double y, int keys, int attachment)
+{
+  // 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)
+  {
+    wxNode *node = m_divisions.First();
+    while (node)
+    {
+      wxDivisionShape *division = (wxDivisionShape *)node->Data();
+      wxNode *next = node->Next();
+      int attach = 0;
+      double dist = 0.0;
+      if (division->HitTest(x, y, &attach, &dist))
+      {
+        division->GetEventHandler()->OnRightClick(x, y, keys, attach);
+        node = NULL;
+      }
+      if (node)
+        node = next;
+    }
+  }
+}
+
+void wxCompositeShape::SetSize(double w, double h, bool recursive)
+{
+  SetAttachmentSize(w, h);
+
+  double xScale = (double)(w/(wxMax(1.0, GetWidth())));
+  double yScale = (double)(h/(wxMax(1.0, GetHeight())));
+
+  m_width = w;
+  m_height = h;
+
+  if (!recursive) return;
+
+  wxNode *node = m_children.First();
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  double xBound, yBound;
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+
+    // Scale the position first
+    double newX = (double)(((object->GetX() - GetX())*xScale) + GetX());
+    double newY = (double)(((object->GetY() - GetY())*yScale) + GetY());
+    object->Show(FALSE);
+    object->Move(dc, newX, newY);
+    object->Show(TRUE);
+
+    // Now set the scaled size
+    object->GetBoundingBoxMin(&xBound, &yBound);
+    object->SetSize(object->GetFixedWidth() ? xBound : xScale*xBound,
+                    object->GetFixedHeight() ? yBound : yScale*yBound);
+
+    node = node->Next();
+  }
+  SetDefaultRegionSize();
+}
+
+void wxCompositeShape::AddChild(wxShape *child, wxShape *addAfter)
+{
+  m_children.Append(child);
+  child->SetParent(this);
+  if (m_canvas)
+  {
+    // Ensure we add at the right position
+    if (addAfter)
+      child->RemoveFromCanvas(m_canvas);
+    child->AddToCanvas(m_canvas, addAfter);
+  }
+}
+
+void wxCompositeShape::RemoveChild(wxShape *child)
+{
+  m_children.DeleteObject(child);
+  m_divisions.DeleteObject(child);
+  RemoveChildFromConstraints(child);
+  child->SetParent(NULL);
+}
+
+void wxCompositeShape::DeleteConstraintsInvolvingChild(wxShape *child)
+{
+  wxNode *node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+    wxNode *nextNode = node->Next();
+
+    if ((constraint->m_constrainingObject == child) ||
+        constraint->m_constrainedObjects.Member(child))
+    {
+      delete constraint;
+      delete node;
+    }
+    node = nextNode;
+  }
+}
+
+void wxCompositeShape::RemoveChildFromConstraints(wxShape *child)
+{
+  wxNode *node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+    wxNode *nextNode = node->Next();
+
+    if (constraint->m_constrainedObjects.Member(child))
+      constraint->m_constrainedObjects.DeleteObject(child);
+    if (constraint->m_constrainingObject == child)
+      constraint->m_constrainingObject = NULL;
+
+    // Delete the constraint if no participants left
+    if (!constraint->m_constrainingObject)
+    {
+      delete constraint;
+      delete node;
+    }
+
+    node = nextNode;
+  }
+}
+
+void wxCompositeShape::Copy(wxShape& copy)
+{
+  wxRectangleShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxCompositeShape)) ) ;
+
+  wxCompositeShape& compositeCopy = (wxCompositeShape&) copy;
+
+  // Associate old and new copies for compositeCopying constraints and division geometry
+  oglObjectCopyMapping.Append((long)this, &compositeCopy);
+
+  // Copy the children
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    wxShape *newObject = object->CreateNewCopy(FALSE, FALSE);
+    if (newObject->GetId() == 0)
+      newObject->SetId(wxNewId());
+
+    newObject->SetParent(&compositeCopy);
+    compositeCopy.m_children.Append(newObject);
+
+    // Some m_children may be divisions
+    if (m_divisions.Member(object))
+      compositeCopy.m_divisions.Append(newObject);
+
+    oglObjectCopyMapping.Append((long)object, newObject);
+
+    node = node->Next();
+  }
+
+  // Copy the constraints
+  node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+
+    wxShape *newConstraining = (wxShape *)(oglObjectCopyMapping.Find((long)constraint->m_constrainingObject)->Data());
+
+    wxList newConstrainedList;
+    wxNode *node2 = constraint->m_constrainedObjects.First();
+    while (node2)
+    {
+      wxShape *constrainedObject = (wxShape *)node2->Data();
+      wxShape *newConstrained = (wxShape *)(oglObjectCopyMapping.Find((long)constrainedObject)->Data());
+      newConstrainedList.Append(newConstrained);
+      node2 = node2->Next();
+    }
+
+    wxOGLConstraint *newConstraint = new wxOGLConstraint(constraint->m_constraintType, newConstraining,
+                                            newConstrainedList);
+    newConstraint->m_constraintId = constraint->m_constraintId;
+    if (constraint->m_constraintName)
+    {
+      newConstraint->m_constraintName = constraint->m_constraintName;
+    }
+    newConstraint->SetSpacing(constraint->m_xSpacing, constraint->m_ySpacing);
+    compositeCopy.m_constraints.Append(newConstraint);
+
+    node = node->Next();
+  }
+
+  // Now compositeCopy the division geometry
+  node = m_divisions.First();
+  while (node)
+  {
+    wxDivisionShape *division = (wxDivisionShape *)node->Data();
+    wxNode *node1 = oglObjectCopyMapping.Find((long)division);
+    wxNode *leftNode = NULL;
+    wxNode *topNode = NULL;
+    wxNode *rightNode = NULL;
+    wxNode *bottomNode = NULL;
+    if (division->GetLeftSide())
+      leftNode = oglObjectCopyMapping.Find((long)division->GetLeftSide());
+    if (division->GetTopSide())
+      topNode = oglObjectCopyMapping.Find((long)division->GetTopSide());
+    if (division->GetRightSide())
+      rightNode = oglObjectCopyMapping.Find((long)division->GetRightSide());
+    if (division->GetBottomSide())
+      bottomNode = oglObjectCopyMapping.Find((long)division->GetBottomSide());
+    if (node1)
+    {
+      wxDivisionShape *newDivision = (wxDivisionShape *)node1->Data();
+      if (leftNode)
+        newDivision->SetLeftSide((wxDivisionShape *)leftNode->Data());
+      if (topNode)
+        newDivision->SetTopSide((wxDivisionShape *)topNode->Data());
+      if (rightNode)
+        newDivision->SetRightSide((wxDivisionShape *)rightNode->Data());
+      if (bottomNode)
+        newDivision->SetBottomSide((wxDivisionShape *)bottomNode->Data());
+    }
+    node = node->Next();
+  }
+}
+
+wxOGLConstraint *wxCompositeShape::AddConstraint(wxOGLConstraint *constraint)
+{
+  m_constraints.Append(constraint);
+  if (constraint->m_constraintId == 0)
+    constraint->m_constraintId = wxNewId();
+  return constraint;
+}
+
+wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxList& constrained)
+{
+  wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, constrained);
+  if (constraint->m_constraintId == 0)
+    constraint->m_constraintId = wxNewId();
+  m_constraints.Append(constraint);
+  return constraint;
+}
+
+wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxShape *constrained)
+{
+  wxList l;
+  l.Append(constrained);
+  wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, l);
+  if (constraint->m_constraintId == 0)
+    constraint->m_constraintId = wxNewId();
+  m_constraints.Append(constraint);
+  return constraint;
+}
+
+wxOGLConstraint *wxCompositeShape::FindConstraint(long cId, wxCompositeShape **actualComposite)
+{
+  wxNode *node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+    if (constraint->m_constraintId == cId)
+    {
+      if (actualComposite)
+        *actualComposite = this;
+      return constraint;
+    }
+    node = node->Next();
+  }
+  // If not found, try children.
+  node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
+    {
+      wxOGLConstraint *constraint = ((wxCompositeShape *)child)->FindConstraint(cId, actualComposite);
+      if (constraint)
+      {
+        if (actualComposite)
+          *actualComposite = (wxCompositeShape *)child;
+        return constraint;
+      }
+    }
+    node = node->Next();
+  }
+  return NULL;
+}
+
+void wxCompositeShape::DeleteConstraint(wxOGLConstraint *constraint)
+{
+  m_constraints.DeleteObject(constraint);
+  delete constraint;
+}
+
+void wxCompositeShape::CalculateSize()
+{
+  double maxX = (double) -999999.9;
+  double maxY = (double) -999999.9;
+  double minX = (double)  999999.9;
+  double minY = (double)  999999.9;
+
+  double w, h;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+
+    // Recalculate size of composite objects because may not conform
+    // to size it was set to - depends on the children.
+    object->CalculateSize();
+
+    object->GetBoundingBoxMax(&w, &h);
+    if ((object->GetX() + (w/2.0)) > maxX)
+      maxX = (double)(object->GetX() + (w/2.0));
+    if ((object->GetX() - (w/2.0)) < minX)
+      minX = (double)(object->GetX() - (w/2.0));
+    if ((object->GetY() + (h/2.0)) > maxY)
+      maxY = (double)(object->GetY() + (h/2.0));
+    if ((object->GetY() - (h/2.0)) < minY)
+      minY = (double)(object->GetY() - (h/2.0));
+
+    node = node->Next();
+  }
+  m_width = maxX - minX;
+  m_height = maxY - minY;
+  m_xpos = (double)(m_width/2.0 + minX);
+  m_ypos = (double)(m_height/2.0 + minY);
+}
+
+bool wxCompositeShape::Recompute()
+{
+  int noIterations = 0;
+  bool changed = TRUE;
+  while (changed && (noIterations < 500))
+  {
+    changed = Constrain();
+    noIterations ++;
+  }
+/*
+#ifdef wx_x
+  if (changed)
+    cerr << "Warning: constraint algorithm failed after 500 iterations.\n";
+#endif
+*/
+  return (!changed);
+}
+
+bool wxCompositeShape::Constrain()
+{
+  CalculateSize();
+
+  bool changed = FALSE;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    if (object->Constrain())
+      changed = TRUE;
+    node = node->Next();
+  }
+
+  node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+    if (constraint->Evaluate()) changed = TRUE;
+    node = node->Next();
+  }
+  return changed;
+}
+
+#ifdef PROLOGIO
+void wxCompositeShape::WriteAttributes(wxExpr *clause)
+{
+  wxRectangleShape::WriteAttributes(clause);
+
+//  clause->AddAttributeValue("selectable", (long)selectable);
+
+  // Output constraints as constraint1 = (...), constraint2 = (...), etc.
+  int constraintNo = 1;
+  char m_constraintNameBuf[20];
+  wxNode *node = m_constraints.First();
+  while (node)
+  {
+    wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
+    sprintf(m_constraintNameBuf, "constraint%d", constraintNo);
+
+    // Each constraint is stored in the form
+    // (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
+    wxExpr *constraintExpr = new wxExpr(wxExprList);
+    constraintExpr->Append(new wxExpr((long)constraint->m_constraintType));
+    constraintExpr->Append(new wxExpr(wxExprString, constraint->m_constraintName));
+    constraintExpr->Append(new wxExpr(constraint->m_constraintId));
+    constraintExpr->Append(new wxExpr(constraint->m_xSpacing));
+    constraintExpr->Append(new wxExpr(constraint->m_ySpacing));
+    constraintExpr->Append(new wxExpr(constraint->m_constrainingObject->GetId()));
+
+    wxExpr *objectList = new wxExpr(wxExprList);
+    wxNode *node1 = constraint->m_constrainedObjects.First();
+    while (node1)
+    {
+      wxShape *obj = (wxShape *)node1->Data();
+      objectList->Append(new wxExpr(obj->GetId()));
+      node1 = node1->Next();
+    }
+    constraintExpr->Append(objectList);
+
+    clause->AddAttributeValue(m_constraintNameBuf, constraintExpr);
+
+    node = node->Next();
+    constraintNo ++;
+  }
+
+  // Write the ids of all the child images
+  wxExpr *childrenExpr = new wxExpr(wxExprList);
+  node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    childrenExpr->Append(new wxExpr(child->GetId()));
+    node = node->Next();
+  }
+  clause->AddAttributeValue("children", childrenExpr);
+
+  // Write the ids of all the division images
+  if (m_divisions.Number() > 0)
+  {
+    wxExpr *divisionsExpr = new wxExpr(wxExprList);
+    node = m_divisions.First();
+    while (node)
+    {
+      wxShape *child = (wxShape *)node->Data();
+      divisionsExpr->Append(new wxExpr(child->GetId()));
+      node = node->Next();
+    }
+    clause->AddAttributeValue("divisions", divisionsExpr);
+  }
+}
+
+// Problem. Child images are always written AFTER the parent
+// so as to be able to link up to parent. So we may not be able
+// to find the constraint participants until we've read everything
+// in. Need to have another pass for composites.
+void wxCompositeShape::ReadAttributes(wxExpr *clause)
+{
+  wxRectangleShape::ReadAttributes(clause);
+
+//  clause->GetAttributeValue("selectable", selectable);
+}
+
+void wxCompositeShape::ReadConstraints(wxExpr *clause, wxExprDatabase *database)
+{
+  // Constraints are output as constraint1 = (...), constraint2 = (...), etc.
+  int constraintNo = 1;
+  char m_constraintNameBuf[20];
+  bool haveConstraints = TRUE;
+
+  while (haveConstraints)
+  {
+    sprintf(m_constraintNameBuf, "constraint%d", constraintNo);
+    wxExpr *constraintExpr = NULL;
+    clause->GetAttributeValue(m_constraintNameBuf, &constraintExpr);
+    if (!constraintExpr)
+    {
+      haveConstraints = FALSE;
+      break;
+    }
+    int cType = 0;
+    double cXSpacing = 0.0;
+    double cYSpacing = 0.0;
+    wxString cName("");
+    long cId = 0;
+    wxShape *m_constrainingObject = NULL;
+    wxList m_constrainedObjects;
+
+    // Each constraint is stored in the form
+    // (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
+
+    wxExpr *typeExpr = constraintExpr->Nth(0);
+    wxExpr *nameExpr = constraintExpr->Nth(1);
+    wxExpr *idExpr = constraintExpr->Nth(2);
+    wxExpr *xExpr = constraintExpr->Nth(3);
+    wxExpr *yExpr = constraintExpr->Nth(4);
+    wxExpr *constrainingExpr = constraintExpr->Nth(5);
+    wxExpr *constrainedExpr = constraintExpr->Nth(6);
+
+    cType = (int)typeExpr->IntegerValue();
+    cXSpacing = xExpr->RealValue();
+    cYSpacing = yExpr->RealValue();
+    cName = nameExpr->StringValue();
+    cId = idExpr->IntegerValue();
+
+    wxExpr *objExpr1 = database->HashFind("node_image", constrainingExpr->IntegerValue());
+    if (objExpr1 && objExpr1->GetClientData())
+      m_constrainingObject = (wxShape *)objExpr1->GetClientData();
+    else
+      wxFatalError("Couldn't find constraining image of composite.", "Object graphics error");
+
+    int i = 0;
+    wxExpr *currentIdExpr = constrainedExpr->Nth(i);
+    while (currentIdExpr)
+    {
+      long currentId = currentIdExpr->IntegerValue();
+      wxExpr *objExpr2 = database->HashFind("node_image", currentId);
+      if (objExpr2 && objExpr2->GetClientData())
+      {
+        m_constrainedObjects.Append((wxShape *)objExpr2->GetClientData());
+      }
+      else
+      {
+        wxFatalError("Couldn't find constrained image of composite.", "Object graphics error");
+      }
+
+      i ++;
+      currentIdExpr = constrainedExpr->Nth(i);
+    }
+    wxOGLConstraint *newConstraint = AddConstraint(cType, m_constrainingObject, m_constrainedObjects);
+    newConstraint->SetSpacing(cXSpacing, cYSpacing);
+    newConstraint->m_constraintId = cId;
+    newConstraint->m_constraintName = (const char*) cName;
+    constraintNo ++;
+  }
+}
+#endif
+
+// Make this composite into a container by creating one wxDivisionShape
+void wxCompositeShape::MakeContainer()
+{
+  wxDivisionShape *division = OnCreateDivision();
+  m_divisions.Append(division);
+  AddChild(division);
+
+  division->SetSize(m_width, m_height);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  division->Move(dc, GetX(), GetY());
+  Recompute();
+  division->Show(TRUE);
+}
+
+wxDivisionShape *wxCompositeShape::OnCreateDivision()
+{
+  return new wxDivisionShape;
+}
+
+wxShape *wxCompositeShape::FindContainerImage()
+{
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    if (!m_divisions.Member(child))
+      return child;
+    node = node->Next();
+  }
+  return NULL;
+}
+
+// Returns TRUE if division is a descendant of this container
+bool wxCompositeShape::ContainsDivision(wxDivisionShape *division)
+{
+  if (m_divisions.Member(division))
+    return TRUE;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *child = (wxShape *)node->Data();
+    if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
+    {
+      bool ans = ((wxCompositeShape *)child)->ContainsDivision(division);
+      if (ans)
+        return TRUE;
+    }
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+/*
+ * Division object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxDivisionShape, wxCompositeShape)
+
+wxDivisionShape::wxDivisionShape()
+{
+  SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT);
+  SetCentreResize(FALSE);
+  SetAttachmentMode(TRUE);
+  m_leftSide = NULL;
+  m_rightSide = NULL;
+  m_topSide = NULL;
+  m_bottomSide = NULL;
+  m_handleSide = DIVISION_SIDE_NONE;
+  m_leftSidePen = wxBLACK_PEN;
+  m_topSidePen = wxBLACK_PEN;
+  m_leftSideColour = "BLACK";
+  m_topSideColour = "BLACK";
+  m_leftSideStyle = "Solid";
+  m_topSideStyle = "Solid";
+  ClearRegions();
+}
+
+wxDivisionShape::~wxDivisionShape()
+{
+}
+
+void wxDivisionShape::OnDraw(wxDC& dc)
+{
+    dc.SetBrush(* wxTRANSPARENT_BRUSH);
+    dc.SetBackgroundMode(wxTRANSPARENT);
+
+    double x1 = (double)(GetX() - (GetWidth()/2.0));
+    double y1 = (double)(GetY() - (GetHeight()/2.0));
+    double x2 = (double)(GetX() + (GetWidth()/2.0));
+    double y2 = (double)(GetY() + (GetHeight()/2.0));
+
+    // Should subtract 1 pixel if drawing under Windows
+#ifdef __WXMSW__
+    y2 -= (double)1.0;
+#endif
+
+    if (m_leftSide)
+    {
+      dc.SetPen(* m_leftSidePen);
+      dc.DrawLine(WXROUND(x1), WXROUND(y2), WXROUND(x1), WXROUND(y1));
+    }
+    if (m_topSide)
+    {
+      dc.SetPen(* m_topSidePen);
+      dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y1));
+    }
+
+    // For testing purposes, draw a rectangle so we know
+    // how big the division is.
+//    SetBrush(* wxCYAN_BRUSH);
+//    wxRectangleShape::OnDraw(dc);
+}
+
+void wxDivisionShape::OnDrawContents(wxDC& dc)
+{
+  wxCompositeShape::OnDrawContents(dc);
+}
+
+bool wxDivisionShape::OnMovePre(wxDC& dc, double x, double y, double oldx, double oldy, bool display)
+{
+  double diffX = x - oldx;
+  double diffY = y - oldy;
+  wxNode *node = m_children.First();
+  while (node)
+  {
+    wxShape *object = (wxShape *)node->Data();
+    object->Erase(dc);
+    object->Move(dc, object->GetX() + diffX, object->GetY() + diffY, display);
+    node = node->Next();
+  }
+  return TRUE;
+}
+
+void wxDivisionShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
+    }
+    return;
+  }
+  wxShape::OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxDivisionShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxShape::OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxDivisionShape::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+  m_canvas->ReleaseMouse();
+  if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
+    }
+    return;
+  }
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(wxCOPY);
+
+  m_canvas->Snap(&m_xpos, &m_ypos);
+  GetEventHandler()->OnMovePre(dc, x, y, m_oldX, m_oldY);
+
+  ResetControlPoints();
+  Draw(dc);
+  MoveLinks(dc);
+  GetEventHandler()->OnDrawControlPoints(dc);
+
+  if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+
+void wxDivisionShape::SetSize(double w, double h, bool recursive)
+{
+  m_width = w;
+  m_height = h;
+  wxRectangleShape::SetSize(w, h, recursive);
+}
+
+void wxDivisionShape::CalculateSize()
+{
+}
+
+void wxDivisionShape::Copy(wxShape& copy)
+{
+  wxCompositeShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxDivisionShape)) ) ;
+
+  wxDivisionShape& divisionCopy = (wxDivisionShape&) copy;
+
+  divisionCopy.m_leftSideStyle = m_leftSideStyle;
+  divisionCopy.m_topSideStyle = m_topSideStyle;
+  divisionCopy.m_leftSideColour = m_leftSideColour;
+  divisionCopy.m_topSideColour = m_topSideColour;
+
+  divisionCopy.m_leftSidePen = m_leftSidePen;
+  divisionCopy.m_topSidePen = m_topSidePen;
+  divisionCopy.m_handleSide = m_handleSide;
+
+  // Division geometry copying is handled at the wxCompositeShape level.
+}
+
+#ifdef PROLOGIO
+void wxDivisionShape::WriteAttributes(wxExpr *clause)
+{
+  wxCompositeShape::WriteAttributes(clause);
+
+  if (m_leftSide)
+    clause->AddAttributeValue("left_side", (long)m_leftSide->GetId());
+  if (m_topSide)
+    clause->AddAttributeValue("top_side", (long)m_topSide->GetId());
+  if (m_rightSide)
+    clause->AddAttributeValue("right_side", (long)m_rightSide->GetId());
+  if (m_bottomSide)
+    clause->AddAttributeValue("bottom_side", (long)m_bottomSide->GetId());
+
+  clause->AddAttributeValue("handle_side", (long)m_handleSide);
+  clause->AddAttributeValueString("left_colour", m_leftSideColour);
+  clause->AddAttributeValueString("top_colour", m_topSideColour);
+  clause->AddAttributeValueString("left_style", m_leftSideStyle);
+  clause->AddAttributeValueString("top_style", m_topSideStyle);
+}
+
+void wxDivisionShape::ReadAttributes(wxExpr *clause)
+{
+  wxCompositeShape::ReadAttributes(clause);
+
+  clause->GetAttributeValue("handle_side", m_handleSide);
+  clause->GetAttributeValue("left_colour", m_leftSideColour);
+  clause->GetAttributeValue("top_colour", m_topSideColour);
+  clause->GetAttributeValue("left_style", m_leftSideStyle);
+  clause->GetAttributeValue("top_style", m_topSideStyle);
+}
+#endif
+
+// Experimental
+void wxDivisionShape::OnRightClick(double x, double y, int keys, int attachment)
+{
+  if (keys & KEY_CTRL)
+  {
+    PopupMenu(x, y);
+  }
+/*
+  else if (keys & KEY_SHIFT)
+  {
+    if (m_leftSide || m_topSide || m_rightSide || m_bottomSide)
+    {
+      if (Selected())
+      {
+        Select(FALSE);
+        GetParent()->Draw(dc);
+      }
+      else
+        Select(TRUE);
+    }
+  }
+*/
+  else
+  {
+    attachment = 0;
+    double dist;
+    if (m_parent)
+    {
+      m_parent->HitTest(x, y, &attachment, &dist);
+      m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
+    }
+    return;
+  }
+}
+
+
+// Divide wxHORIZONTALly or wxVERTICALly
+bool wxDivisionShape::Divide(int direction)
+{
+  // Calculate existing top-left, bottom-right
+  double x1 = (double)(GetX() - (GetWidth()/2.0));
+  double y1 = (double)(GetY() - (GetHeight()/2.0));
+  wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
+  double oldWidth = GetWidth();
+  double oldHeight = GetHeight();
+  if (Selected())
+    Select(FALSE);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  if (direction == wxVERTICAL)
+  {
+    // Dividing vertically means notionally putting a horizontal line through it.
+    // Break existing piece into two.
+    double newXPos1 = GetX();
+    double newYPos1 = (double)(y1 + (GetHeight()/4.0));
+    double newXPos2 = GetX();
+    double newYPos2 = (double)(y1 + (3.0*GetHeight()/4.0));
+    wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
+    newDivision->Show(TRUE);
+
+    Erase(dc);
+
+    // Anything adjoining the bottom of this division now adjoins the
+    // bottom of the new division.
+    wxNode *node = compositeParent->GetDivisions().First();
+    while (node)
+    {
+      wxDivisionShape *obj = (wxDivisionShape *)node->Data();
+      if (obj->GetTopSide() == this)
+        obj->SetTopSide(newDivision);
+      node = node->Next();
+    }
+    newDivision->SetTopSide(this);
+    newDivision->SetBottomSide(m_bottomSide);
+    newDivision->SetLeftSide(m_leftSide);
+    newDivision->SetRightSide(m_rightSide);
+    m_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());
+
+    m_handleSide = DIVISION_SIDE_BOTTOM;
+    newDivision->SetHandleSide(DIVISION_SIDE_TOP);
+
+    SetSize(oldWidth, (double)(oldHeight/2.0));
+    Move(dc, newXPos1, newYPos1);
+
+    newDivision->SetSize(oldWidth, (double)(oldHeight/2.0));
+    newDivision->Move(dc, newXPos2, newYPos2);
+  }
+  else
+  {
+    // Dividing horizontally means notionally putting a vertical line through it.
+    // Break existing piece into two.
+    double newXPos1 = (double)(x1 + (GetWidth()/4.0));
+    double newYPos1 = GetY();
+    double newXPos2 = (double)(x1 + (3.0*GetWidth()/4.0));
+    double newYPos2 = GetY();
+    wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
+    newDivision->Show(TRUE);
+
+    Erase(dc);
+
+    // Anything adjoining the left of this division now adjoins the
+    // left of the new division.
+    wxNode *node = compositeParent->GetDivisions().First();
+    while (node)
+    {
+      wxDivisionShape *obj = (wxDivisionShape *)node->Data();
+      if (obj->GetLeftSide() == this)
+        obj->SetLeftSide(newDivision);
+      node = node->Next();
+    }
+    newDivision->SetTopSide(m_topSide);
+    newDivision->SetBottomSide(m_bottomSide);
+    newDivision->SetLeftSide(this);
+    newDivision->SetRightSide(m_rightSide);
+    m_rightSide = newDivision;
+
+    compositeParent->GetDivisions().Append(newDivision);
+    compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
+
+    m_handleSide = DIVISION_SIDE_RIGHT;
+    newDivision->SetHandleSide(DIVISION_SIDE_LEFT);
+
+    SetSize((double)(oldWidth/2.0), oldHeight);
+    Move(dc, newXPos1, newYPos1);
+
+    newDivision->SetSize((double)(oldWidth/2.0), oldHeight);
+    newDivision->Move(dc, newXPos2, newYPos2);
+  }
+  if (compositeParent->Selected())
+  {
+    compositeParent->DeleteControlPoints(& dc);
+    compositeParent->MakeControlPoints();
+    compositeParent->MakeMandatoryControlPoints();
+  }
+  compositeParent->Draw(dc);
+  return TRUE;
+}
+
+// Make one control point for every visible line
+void wxDivisionShape::MakeControlPoints()
+{
+  MakeMandatoryControlPoints();
+}
+
+void wxDivisionShape::MakeMandatoryControlPoints()
+{
+  double maxX, maxY;
+
+  GetBoundingBoxMax(&maxX, &maxY);
+  double x, y;
+  int direction;
+/*
+  if (m_leftSide)
+  {
+    x = (double)(-maxX/2.0);
+    y = 0.0;
+    wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
+                                             CONTROL_POINT_HORIZONTAL);
+    m_canvas->AddShape(control);
+    m_controlPoints.Append(control);
+  }
+  if (m_topSide)
+  {
+    x = 0.0;
+    y = (double)(-maxY/2.0);
+    wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
+                                             CONTROL_POINT_VERTICAL);
+    m_canvas->AddShape(control);
+    m_controlPoints.Append(control);
+  }
+*/
+  switch (m_handleSide)
+  {
+    case DIVISION_SIDE_LEFT:
+    {
+      x = (double)(-maxX/2.0);
+      y = 0.0;
+      direction = CONTROL_POINT_HORIZONTAL;
+      break;
+    }
+    case DIVISION_SIDE_TOP:
+    {
+      x = 0.0;
+      y = (double)(-maxY/2.0);
+      direction = CONTROL_POINT_VERTICAL;
+      break;
+    }
+    case DIVISION_SIDE_RIGHT:
+    {
+      x = (double)(maxX/2.0);
+      y = 0.0;
+      direction = CONTROL_POINT_HORIZONTAL;
+      break;
+    }
+    case DIVISION_SIDE_BOTTOM:
+    {
+      x = 0.0;
+      y = (double)(maxY/2.0);
+      direction = CONTROL_POINT_VERTICAL;
+      break;
+    }
+    default:
+      break;
+  }
+  if (m_handleSide != DIVISION_SIDE_NONE)
+  {
+    wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
+                                             direction);
+    m_canvas->AddShape(control);
+    m_controlPoints.Append(control);
+  }
+}
+
+void wxDivisionShape::ResetControlPoints()
+{
+  ResetMandatoryControlPoints();
+}
+
+void wxDivisionShape::ResetMandatoryControlPoints()
+{
+  if (m_controlPoints.Number() < 1)
+    return;
+
+  double maxX, maxY;
+
+  GetBoundingBoxMax(&maxX, &maxY);
+/*
+  wxNode *node = m_controlPoints.First();
+  while (node)
+  {
+    wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
+    if (control->type == CONTROL_POINT_HORIZONTAL)
+    {
+      control->xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
+    }
+    else if (control->type == CONTROL_POINT_VERTICAL)
+    {
+      control->xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
+    }
+    node = node->Next();
+  }
+*/
+  wxNode *node = m_controlPoints.First();
+  if ((m_handleSide == DIVISION_SIDE_LEFT) && node)
+  {
+    wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
+    control->m_xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
+  }
+
+  if ((m_handleSide == DIVISION_SIDE_TOP) && node)
+  {
+    wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
+    control->m_xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
+  }
+
+  if ((m_handleSide == DIVISION_SIDE_RIGHT) && node)
+  {
+    wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
+    control->m_xoffset = (double)(maxX/2.0); control->m_yoffset = 0.0;
+  }
+
+  if ((m_handleSide == DIVISION_SIDE_BOTTOM) && node)
+  {
+    wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
+    control->m_xoffset = 0.0; control->m_yoffset = (double)(maxY/2.0);
+  }
+}
+
+// Adjust a side, returning FALSE if it's not physically possible.
+bool wxDivisionShape::AdjustLeft(double left, bool test)
+{
+  double x2 = (double)(GetX() + (GetWidth()/2.0));
+
+  if (left >= x2)
+    return FALSE;
+  if (test)
+    return TRUE;
+
+  double newW = x2 - left;
+  double newX = (double)(left + newW/2.0);
+  SetSize(newW, GetHeight());
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  Move(dc, newX, GetY());
+
+  return TRUE;
+}
+
+bool wxDivisionShape::AdjustTop(double top, bool test)
+{
+  double y2 = (double)(GetY() + (GetHeight()/2.0));
+
+  if (top >= y2)
+    return FALSE;
+  if (test)
+    return TRUE;
+
+  double newH = y2 - top;
+  double newY = (double)(top + newH/2.0);
+  SetSize(GetWidth(), newH);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  Move(dc, GetX(), newY);
+
+  return TRUE;
+}
+
+bool wxDivisionShape::AdjustRight(double right, bool test)
+{
+  double x1 = (double)(GetX() - (GetWidth()/2.0));
+
+  if (right <= x1)
+    return FALSE;
+  if (test)
+    return TRUE;
+
+  double newW = right - x1;
+  double newX = (double)(x1 + newW/2.0);
+  SetSize(newW, GetHeight());
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  Move(dc, newX, GetY());
+
+  return TRUE;
+}
+
+bool wxDivisionShape::AdjustBottom(double bottom, bool test)
+{
+  double y1 = (double)(GetY() - (GetHeight()/2.0));
+
+  if (bottom <= y1)
+    return FALSE;
+  if (test)
+    return TRUE;
+
+  double newH = bottom - y1;
+  double newY = (double)(y1 + newH/2.0);
+  SetSize(GetWidth(), newH);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  Move(dc, GetX(), newY);
+
+  return TRUE;
+}
+
+wxDivisionControlPoint::wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):
+  wxControlPoint(the_canvas, object, size, the_xoffset, the_yoffset, the_type)
+{
+  SetEraseObject(FALSE);
+}
+
+wxDivisionControlPoint::~wxDivisionControlPoint()
+{
+}
+
+static double originalX = 0.0;
+static double originalY = 0.0;
+static double originalW = 0.0;
+static double originalH = 0.0;
+
+// Implement resizing of canvas object
+void wxDivisionControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  wxControlPoint::OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxDivisionControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  wxDivisionShape *division = (wxDivisionShape *)m_shape;
+  originalX = division->GetX();
+  originalY = division->GetY();
+  originalW = division->GetWidth();
+  originalH = division->GetHeight();
+
+  wxControlPoint::OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxDivisionControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+  wxControlPoint::OnEndDragLeft(x, y, keys, attachment);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  wxDivisionShape *division = (wxDivisionShape *)m_shape;
+  wxCompositeShape *divisionParent = (wxCompositeShape *)division->GetParent();
+
+  // Need to check it's within the bounds of the parent composite.
+  double x1 = (double)(divisionParent->GetX() - (divisionParent->GetWidth()/2.0));
+  double y1 = (double)(divisionParent->GetY() - (divisionParent->GetHeight()/2.0));
+  double x2 = (double)(divisionParent->GetX() + (divisionParent->GetWidth()/2.0));
+  double y2 = (double)(divisionParent->GetY() + (divisionParent->GetHeight()/2.0));
+
+  // Need to check it has not made the division zero or negative width/height
+  double dx1 = (double)(division->GetX() - (division->GetWidth()/2.0));
+  double dy1 = (double)(division->GetY() - (division->GetHeight()/2.0));
+  double dx2 = (double)(division->GetX() + (division->GetWidth()/2.0));
+  double dy2 = (double)(division->GetY() + (division->GetHeight()/2.0));
+
+  bool success = TRUE;
+  switch (division->GetHandleSide())
+  {
+    case DIVISION_SIDE_LEFT:
+    {
+      if ((x <= x1) || (x >= x2) || (x >= dx2))
+        success = FALSE;
+      // Try it out first...
+      else if (!division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, TRUE))
+        success = FALSE;
+      else
+        division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, FALSE);
+
+      break;
+    }
+    case DIVISION_SIDE_TOP:
+    {
+      if ((y <= y1) || (y >= y2) || (y >= dy2))
+        success = FALSE;
+      else if (!division->ResizeAdjoining(DIVISION_SIDE_TOP, y, TRUE))
+        success = FALSE;
+      else
+        division->ResizeAdjoining(DIVISION_SIDE_TOP, y, FALSE);
+
+      break;
+    }
+    case DIVISION_SIDE_RIGHT:
+    {
+      if ((x <= x1) || (x >= x2) || (x <= dx1))
+        success = FALSE;
+      else if (!division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, TRUE))
+        success = FALSE;
+      else
+        division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, FALSE);
+
+      break;
+    }
+    case DIVISION_SIDE_BOTTOM:
+    {
+      if ((y <= y1) || (y >= y2) || (y <= dy1))
+        success = FALSE;
+      else if (!division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, TRUE))
+        success = FALSE;
+      else
+        division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, FALSE);
+
+      break;
+    }
+  }
+  if (!success)
+  {
+    division->SetSize(originalW, originalH);
+    division->Move(dc, originalX, originalY);
+  }
+  divisionParent->Draw(dc);
+  division->GetEventHandler()->OnDrawControlPoints(dc);
+}
+
+/* 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.
+ */
+bool wxDivisionShape::ResizeAdjoining(int side, double newPos, bool test)
+{
+  wxCompositeShape *divisionParent = (wxCompositeShape *)GetParent();
+  wxNode *node = divisionParent->GetDivisions().First();
+  while (node)
+  {
+    wxDivisionShape *division = (wxDivisionShape *)node->Data();
+    switch (side)
+    {
+      case DIVISION_SIDE_LEFT:
+      {
+        if (division->m_rightSide == this)
+        {
+          bool success = division->AdjustRight(newPos, test);
+          if (!success && test)
+            return FALSE;
+        }
+        break;
+      }
+      case DIVISION_SIDE_TOP:
+      {
+        if (division->m_bottomSide == this)
+        {
+          bool success = division->AdjustBottom(newPos, test);
+          if (!success && test)
+            return FALSE;
+        }
+        break;
+      }
+      case DIVISION_SIDE_RIGHT:
+      {
+        if (division->m_leftSide == this)
+        {
+          bool success = division->AdjustLeft(newPos, test);
+          if (!success && test)
+            return FALSE;
+        }
+        break;
+      }
+      case DIVISION_SIDE_BOTTOM:
+      {
+        if (division->m_topSide == this)
+        {
+          bool success = division->AdjustTop(newPos, test);
+          if (!success && test)
+            return FALSE;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+    node = node->Next();
+  }
+
+  return TRUE;
+}
+
+/*
+ * Popup menu for editing divisions
+ *
+ */
+class OGLPopupDivisionMenu : public wxMenu {
+public:
+    OGLPopupDivisionMenu() : wxMenu() {
+        Append(DIVISION_MENU_SPLIT_HORIZONTALLY, "Split horizontally");
+        Append(DIVISION_MENU_SPLIT_VERTICALLY, "Split vertically");
+        AppendSeparator();
+        Append(DIVISION_MENU_EDIT_LEFT_EDGE, "Edit left edge");
+        Append(DIVISION_MENU_EDIT_TOP_EDGE, "Edit top edge");
+    }
+
+    void OnMenu(wxCommandEvent& event);
+
+    DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(OGLPopupDivisionMenu, wxMenu)
+    EVT_CUSTOM_RANGE(wxEVT_COMMAND_MENU_SELECTED,
+                     DIVISION_MENU_SPLIT_HORIZONTALLY,
+                     DIVISION_MENU_EDIT_BOTTOM_EDGE,
+                     OGLPopupDivisionMenu::OnMenu)
+END_EVENT_TABLE()
+
+
+void OGLPopupDivisionMenu::OnMenu(wxCommandEvent& event)
+{
+  wxDivisionShape *division = (wxDivisionShape *)GetClientData();
+  switch (event.GetInt())
+  {
+    case DIVISION_MENU_SPLIT_HORIZONTALLY:
+    {
+      division->Divide(wxHORIZONTAL);
+      break;
+    }
+    case DIVISION_MENU_SPLIT_VERTICALLY:
+    {
+      division->Divide(wxVERTICAL);
+      break;
+    }
+    case DIVISION_MENU_EDIT_LEFT_EDGE:
+    {
+      division->EditEdge(DIVISION_SIDE_LEFT);
+      break;
+    }
+    case DIVISION_MENU_EDIT_TOP_EDGE:
+    {
+      division->EditEdge(DIVISION_SIDE_TOP);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void wxDivisionShape::EditEdge(int side)
+{
+  wxMessageBox("EditEdge() not implemented", "OGL", wxOK);
+
+#if 0
+  wxBeginBusyCursor();
+
+  wxPen *currentPen = NULL;
+  char **pColour = NULL;
+  char **pStyle = NULL;
+  if (side == DIVISION_SIDE_LEFT)
+  {
+    currentPen = m_leftSidePen;
+    pColour = &m_leftSideColour;
+    pStyle = &m_leftSideStyle;
+  }
+  else
+  {
+    currentPen = m_topSidePen;
+    pColour = &m_topSideColour;
+    pStyle = &m_topSideStyle;
+  }
+
+  GraphicsForm *form = new GraphicsForm("Containers");
+  int lineWidth = currentPen->GetWidth();
+
+  form->Add(wxMakeFormShort("Width", &lineWidth, wxFORM_DEFAULT, NULL, NULL, wxVERTICAL,
+               150));
+  form->Add(wxMakeFormString("Colour", pColour, wxFORM_CHOICE,
+            new wxList(wxMakeConstraintStrings(
+  "BLACK"            ,
+  "BLUE"             ,
+  "BROWN"            ,
+  "CORAL"            ,
+  "CYAN"             ,
+  "DARK GREY"        ,
+  "DARK GREEN"       ,
+  "DIM GREY"         ,
+  "GREY"             ,
+  "GREEN"            ,
+  "LIGHT BLUE"       ,
+  "LIGHT GREY"       ,
+  "MAGENTA"          ,
+  "MAROON"           ,
+  "NAVY"             ,
+  "ORANGE"           ,
+  "PURPLE"           ,
+  "RED"              ,
+  "TURQUOISE"        ,
+  "VIOLET"           ,
+  "WHITE"            ,
+  "YELLOW"           ,
+  NULL),
+  NULL), NULL, wxVERTICAL, 150));
+  form->Add(wxMakeFormString("Style", pStyle, wxFORM_CHOICE,
+            new wxList(wxMakeConstraintStrings(
+  "Solid"            ,
+  "Short Dash"       ,
+  "Long Dash"        ,
+  "Dot"              ,
+  "Dot Dash"         ,
+  NULL),
+  NULL), NULL, wxVERTICAL, 100));
+
+  wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Division properties", 10, 10, 500, 500);
+  if (GraphicsLabelFont)
+    dialog->SetLabelFont(GraphicsLabelFont);
+  if (GraphicsButtonFont)
+    dialog->SetButtonFont(GraphicsButtonFont);
+
+  form->AssociatePanel(dialog);
+  form->dialog = dialog;
+
+  dialog->Fit();
+  dialog->Centre(wxBOTH);
+
+  wxEndBusyCursor();
+  dialog->Show(TRUE);
+
+  int lineStyle = wxSOLID;
+  if (*pStyle)
+  {
+    if (strcmp(*pStyle, "Solid") == 0)
+      lineStyle = wxSOLID;
+    else if (strcmp(*pStyle, "Dot") == 0)
+      lineStyle = wxDOT;
+    else if (strcmp(*pStyle, "Short Dash") == 0)
+      lineStyle = wxSHORT_DASH;
+    else if (strcmp(*pStyle, "Long Dash") == 0)
+      lineStyle = wxLONG_DASH;
+    else if (strcmp(*pStyle, "Dot Dash") == 0)
+      lineStyle = wxDOT_DASH;
+  }
+
+  wxPen *newPen = wxThePenList->FindOrCreatePen(*pColour, lineWidth, lineStyle);
+  if (!pen)
+    pen = wxBLACK_PEN;
+  if (side == DIVISION_SIDE_LEFT)
+    m_leftSidePen = newPen;
+  else
+    m_topSidePen = newPen;
+
+  // Need to draw whole image again
+  wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
+  compositeParent->Draw(dc);
+#endif
+}
+
+// Popup menu
+void wxDivisionShape::PopupMenu(double x, double y)
+{
+  wxMenu* oglPopupDivisionMenu = new OGLPopupDivisionMenu;
+
+  oglPopupDivisionMenu->SetClientData((void *)this);
+  if (m_leftSide)
+    oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, TRUE);
+  else
+    oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, FALSE);
+  if (m_topSide)
+    oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, TRUE);
+  else
+    oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, FALSE);
+
+  int x1, y1;
+  m_canvas->ViewStart(&x1, &y1);
+
+  int unit_x, unit_y;
+  m_canvas->GetScrollPixelsPerUnit(&unit_x, &unit_y);
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  int mouse_x = (int)(dc.LogicalToDeviceX((long)(x - x1*unit_x)));
+  int mouse_y = (int)(dc.LogicalToDeviceY((long)(y - y1*unit_y)));
+
+  m_canvas->PopupMenu(oglPopupDivisionMenu, mouse_x, mouse_y);
+  delete oglPopupDivisionMenu;
+}
+
+void wxDivisionShape::SetLeftSideColour(const wxString& colour)
+{
+  m_leftSideColour = colour;
+}
+
+void wxDivisionShape::SetTopSideColour(const wxString& colour)
+{
+  m_topSideColour = colour;
+}
+
+void wxDivisionShape::SetLeftSideStyle(const wxString& style)
+{
+  m_leftSideStyle = style;
+}
+
+void wxDivisionShape::SetTopSideStyle(const wxString& style)
+{
+  m_topSideStyle = style;
+}
+
diff --git a/src/ogl/constrnt.cpp b/src/ogl/constrnt.cpp
new file mode 100644 (file)
index 0000000..ed2562b
--- /dev/null
@@ -0,0 +1,619 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        constrnt.cpp
+// Purpose:     OGL Constraint classes
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "constrnt.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/constrnt.h>
+#include <wx/ogl/canvas.h>
+
+wxList *wxOGLConstraintTypes = NULL;
+
+/*
+ * Constraint type
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxOGLConstraintType, wxObject)
+
+wxOGLConstraintType::wxOGLConstraintType(int theType, const wxString& theName, const wxString& thePhrase)
+{
+  m_type = theType;
+  m_name = theName;
+  m_phrase = thePhrase;
+}
+
+wxOGLConstraintType::~wxOGLConstraintType()
+{
+}
+
+void OGLInitializeConstraintTypes()
+{
+    if (!wxOGLConstraintTypes)
+        return;
+
+    wxOGLConstraintTypes = new wxList(wxKEY_INTEGER);
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_CENTRED_VERTICALLY,
+        new wxOGLConstraintType(gyCONSTRAINT_CENTRED_VERTICALLY, "Centre vertically", "centred vertically w.r.t."));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_CENTRED_HORIZONTALLY,
+        new wxOGLConstraintType(gyCONSTRAINT_CENTRED_HORIZONTALLY, "Centre horizontally", "centred horizontally w.r.t."));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_CENTRED_BOTH,
+        new wxOGLConstraintType(gyCONSTRAINT_CENTRED_BOTH, "Centre", "centred w.r.t."));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_LEFT_OF,
+        new wxOGLConstraintType(gyCONSTRAINT_LEFT_OF, "Left of", "left of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_RIGHT_OF,
+        new wxOGLConstraintType(gyCONSTRAINT_RIGHT_OF, "Right of", "right of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_ABOVE,
+        new wxOGLConstraintType(gyCONSTRAINT_ABOVE, "Above", "above"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_BELOW,
+        new wxOGLConstraintType(gyCONSTRAINT_BELOW, "Below", "below"));
+
+    // Alignment
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_TOP,
+        new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_TOP, "Top-aligned", "aligned to the top of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_BOTTOM,
+        new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_BOTTOM, "Bottom-aligned", "aligned to the bottom of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_LEFT,
+        new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_LEFT, "Left-aligned", "aligned to the left of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_ALIGNED_RIGHT,
+        new wxOGLConstraintType(gyCONSTRAINT_ALIGNED_RIGHT, "Right-aligned", "aligned to the right of"));
+
+    // Mid-alignment
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_TOP,
+        new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_TOP, "Top-midaligned", "centred on the top of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_BOTTOM,
+        new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_BOTTOM, "Bottom-midaligned", "centred on the bottom of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_LEFT,
+        new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_LEFT, "Left-midaligned", "centred on the left of"));
+
+    wxOGLConstraintTypes->Append(gyCONSTRAINT_MIDALIGNED_RIGHT,
+        new wxOGLConstraintType(gyCONSTRAINT_MIDALIGNED_RIGHT, "Right-midaligned", "centred on the right of"));
+}
+
+void OGLCleanUpConstraintTypes()
+{
+    if (!wxOGLConstraintTypes)
+        return;
+
+    wxNode* node = wxOGLConstraintTypes->First();
+    while (node)
+    {
+        wxOGLConstraintType* ct = (wxOGLConstraintType*) node->Data();
+        delete ct;
+        node = node->Next();
+    }
+    delete wxOGLConstraintTypes;
+    wxOGLConstraintTypes = NULL;
+}
+
+/*
+ * Constraint Stuff
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxOGLConstraint, wxObject)
+
+wxOGLConstraint::wxOGLConstraint(int type, wxShape *constraining, wxList& constrained)
+{
+  m_xSpacing = 0.0;
+  m_ySpacing = 0.0;
+
+  m_constraintType = type;
+  m_constrainingObject = constraining;
+
+  m_constraintId = 0;
+  m_constraintName = "noname";
+
+  wxNode *node = constrained.First();
+  while (node)
+  {
+    m_constrainedObjects.Append(node->Data());
+    node = node->Next();
+  }
+}
+
+wxOGLConstraint::~wxOGLConstraint()
+{
+}
+
+bool wxOGLConstraint::Equals(double a, double b)
+{
+  double marg = 0.5;
+
+  bool eq = ((b <= a + marg) && (b >= a - marg));
+  return eq;
+}
+
+// Return TRUE if anything changed
+bool wxOGLConstraint::Evaluate()
+{
+  double maxWidth, maxHeight, minWidth, minHeight, x, y;
+  m_constrainingObject->GetBoundingBoxMax(&maxWidth, &maxHeight);
+  m_constrainingObject->GetBoundingBoxMin(&minWidth, &minHeight);
+  x = m_constrainingObject->GetX();
+  y = m_constrainingObject->GetY();
+
+  wxClientDC dc(m_constrainingObject->GetCanvas());
+  m_constrainingObject->GetCanvas()->PrepareDC(dc);
+
+  switch (m_constraintType)
+  {
+    case gyCONSTRAINT_CENTRED_VERTICALLY:
+    {
+      int n = m_constrainedObjects.Number();
+      double totalObjectHeight = 0.0;
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+        totalObjectHeight += height2;
+        node = node->Next();
+      }
+      double startY;
+      double spacingY;
+      // Check if within the constraining object...
+      if ((totalObjectHeight + (n + 1)*m_ySpacing) <= minHeight)
+      {
+        spacingY = (double)((minHeight - totalObjectHeight)/(n + 1));
+        startY = (double)(y - (minHeight/2.0));
+      }
+      // Otherwise, use default spacing
+      else
+      {
+        spacingY = m_ySpacing;
+        startY = (double)(y - ((totalObjectHeight + (n+1)*spacingY)/2.0));
+      }
+
+      // Now position the objects
+      bool changed = FALSE;
+      node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+        startY += (double)(spacingY + (height2/2.0));
+        if (!Equals(startY, constrainedObject->GetY()))
+        {
+          constrainedObject->Move(dc, constrainedObject->GetX(), startY, FALSE);
+          changed = TRUE;
+        }
+        startY += (double)(height2/2.0);
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_CENTRED_HORIZONTALLY:
+    {
+      int n = m_constrainedObjects.Number();
+      double totalObjectWidth = 0.0;
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+        totalObjectWidth += width2;
+        node = node->Next();
+      }
+      double startX;
+      double spacingX;
+      // Check if within the constraining object...
+      if ((totalObjectWidth + (n + 1)*m_xSpacing) <= minWidth)
+      {
+        spacingX = (double)((minWidth - totalObjectWidth)/(n + 1));
+        startX = (double)(x - (minWidth/2.0));
+      }
+      // Otherwise, use default spacing
+      else
+      {
+        spacingX = m_xSpacing;
+        startX = (double)(x - ((totalObjectWidth + (n+1)*spacingX)/2.0));
+      }
+
+      // Now position the objects
+      bool changed = FALSE;
+      node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+        startX += (double)(spacingX + (width2/2.0));
+        if (!Equals(startX, constrainedObject->GetX()))
+        {
+          constrainedObject->Move(dc, startX, constrainedObject->GetY(), FALSE);
+          changed = TRUE;
+        }
+        startX += (double)(width2/2.0);
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_CENTRED_BOTH:
+    {
+      int n = m_constrainedObjects.Number();
+      double totalObjectWidth = 0.0;
+      double totalObjectHeight = 0.0;
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+        totalObjectWidth += width2;
+        totalObjectHeight += height2;
+        node = node->Next();
+      }
+      double startX;
+      double spacingX;
+      double startY;
+      double spacingY;
+
+      // Check if within the constraining object...
+      if ((totalObjectWidth + (n + 1)*m_xSpacing) <= minWidth)
+      {
+        spacingX = (double)((minWidth - totalObjectWidth)/(n + 1));
+        startX = (double)(x - (minWidth/2.0));
+      }
+      // Otherwise, use default spacing
+      else
+      {
+        spacingX = m_xSpacing;
+        startX = (double)(x - ((totalObjectWidth + (n+1)*spacingX)/2.0));
+      }
+
+      // Check if within the constraining object...
+      if ((totalObjectHeight + (n + 1)*m_ySpacing) <= minHeight)
+      {
+        spacingY = (double)((minHeight - totalObjectHeight)/(n + 1));
+        startY = (double)(y - (minHeight/2.0));
+      }
+      // Otherwise, use default spacing
+      else
+      {
+        spacingY = m_ySpacing;
+        startY = (double)(y - ((totalObjectHeight + (n+1)*spacingY)/2.0));
+      }
+
+      // Now position the objects
+      bool changed = FALSE;
+      node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+        startX += (double)(spacingX + (width2/2.0));
+        startY += (double)(spacingY + (height2/2.0));
+
+        if ((!Equals(startX, constrainedObject->GetX())) || (!Equals(startY, constrainedObject->GetY())))
+           {
+          constrainedObject->Move(dc, startX, startY, FALSE);
+          changed = TRUE;
+           }
+
+        startX += (double)(width2/2.0);
+        startY += (double)(height2/2.0);
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_LEFT_OF:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double x3 = (double)(x - (minWidth/2.0) - (width2/2.0) - m_xSpacing);
+        if (!Equals(x3, constrainedObject->GetX()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_RIGHT_OF:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double x3 = (double)(x + (minWidth/2.0) + (width2/2.0) + m_xSpacing);
+        if (!Equals(x3, constrainedObject->GetX()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+
+      return FALSE;
+    }
+    case gyCONSTRAINT_ABOVE:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double y3 = (double)(y - (minHeight/2.0) - (height2/2.0) - m_ySpacing);
+        if (!Equals(y3, constrainedObject->GetY()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_BELOW:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double y3 = (double)(y + (minHeight/2.0) + (height2/2.0) + m_ySpacing);
+        if (!Equals(y3, constrainedObject->GetY()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_ALIGNED_LEFT:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double x3 = (double)(x - (minWidth/2.0) + (width2/2.0) + m_xSpacing);
+        if (!Equals(x3, constrainedObject->GetX()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_ALIGNED_RIGHT:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double x3 = (double)(x + (minWidth/2.0) - (width2/2.0) - m_xSpacing);
+        if (!Equals(x3, constrainedObject->GetX()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+
+      return FALSE;
+    }
+    case gyCONSTRAINT_ALIGNED_TOP:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double y3 = (double)(y - (minHeight/2.0) + (height2/2.0) + m_ySpacing);
+        if (!Equals(y3, constrainedObject->GetY()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_ALIGNED_BOTTOM:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double width2, height2;
+        constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+        double y3 = (double)(y + (minHeight/2.0) - (height2/2.0) - m_ySpacing);
+        if (!Equals(y3, constrainedObject->GetY()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_MIDALIGNED_LEFT:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double x3 = (double)(x - (minWidth/2.0));
+        if (!Equals(x3, constrainedObject->GetX()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_MIDALIGNED_RIGHT:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double x3 = (double)(x + (minWidth/2.0));
+        if (!Equals(x3, constrainedObject->GetX()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, x3, constrainedObject->GetY(), FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+
+      return FALSE;
+    }
+    case gyCONSTRAINT_MIDALIGNED_TOP:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double y3 = (double)(y - (minHeight/2.0));
+        if (!Equals(y3, constrainedObject->GetY()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+    case gyCONSTRAINT_MIDALIGNED_BOTTOM:
+    {
+      bool changed = FALSE;
+
+      wxNode *node = m_constrainedObjects.First();
+      while (node)
+      {
+        wxShape *constrainedObject = (wxShape *)node->Data();
+
+        double y3 = (double)(y + (minHeight/2.0));
+        if (!Equals(y3, constrainedObject->GetY()))
+           {
+          changed = TRUE;
+          constrainedObject->Move(dc, constrainedObject->GetX(), y3, FALSE);
+           }
+
+        node = node->Next();
+      }
+      return changed;
+    }
+
+    default:
+      return FALSE;
+  }
+  return FALSE;
+}
+
diff --git a/src/ogl/divided.cpp b/src/ogl/divided.cpp
new file mode 100644 (file)
index 0000000..8f66677
--- /dev/null
@@ -0,0 +1,720 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        divided.cpp
+// Purpose:     wxDividedShape class
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "divided.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/divided.h>
+#include <wx/ogl/lines.h>
+#include <wx/ogl/misc.h>
+
+class wxDividedShapeControlPoint: public wxControlPoint
+{
+ DECLARE_DYNAMIC_CLASS(wxDividedShapeControlPoint)
+ private:
+  int regionId;
+ public:
+  wxDividedShapeControlPoint() { regionId = 0; }
+  wxDividedShapeControlPoint(wxShapeCanvas *the_canvas, wxShape *object, int region,
+                            double size, double the_xoffset, double the_yoffset, int the_type);
+  ~wxDividedShapeControlPoint();
+
+  void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
+  void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
+  void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDividedShapeControlPoint, wxControlPoint)
+
+/*
+ * Divided object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxDividedShape, wxRectangleShape)
+
+wxDividedShape::wxDividedShape(double w, double h): wxRectangleShape(w, h)
+{
+  ClearRegions();
+}
+
+wxDividedShape::~wxDividedShape()
+{
+}
+
+void wxDividedShape::OnDraw(wxDC& dc)
+{
+  wxRectangleShape::OnDraw(dc);
+}
+
+void wxDividedShape::OnDrawContents(wxDC& dc)
+{
+  double defaultProportion = (double)(GetRegions().Number() > 0 ? (1.0/((double)(GetRegions().Number()))) : 0.0);
+  double currentY = (double)(m_ypos - (m_height / 2.0));
+  double maxY = (double)(m_ypos + (m_height / 2.0));
+
+  double leftX = (double)(m_xpos - (m_width / 2.0));
+  double rightX = (double)(m_xpos + (m_width / 2.0));
+
+  if (m_pen) dc.SetPen(* m_pen);
+
+  if (m_textColour) dc.SetTextForeground(* m_textColour);
+
+#ifdef __WXMSW__
+  // For efficiency, don't do this under X - doesn't make
+  // any visible difference for our purposes.
+  if (m_brush)
+    dc.SetTextBackground(m_brush->GetColour());
+#endif
+/*
+  if (!formatted)
+  {
+    FormatRegionText();
+    formatted = TRUE;
+  }
+*/
+  if (GetDisableLabel()) return;
+
+  double xMargin = 2;
+  double yMargin = 2;
+  dc.SetBackgroundMode(wxTRANSPARENT);
+
+  wxNode *node = GetRegions().First();
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    dc.SetFont(* region->GetFont());
+    dc.SetTextForeground(* region->GetActualColourObject());
+
+    double proportion =
+      region->m_regionProportionY < 0.0 ? defaultProportion : region->m_regionProportionY;
+
+    double y = currentY + m_height*proportion;
+    double actualY = maxY < y ? maxY : y;
+
+    double centreX = m_xpos;
+    double centreY = (double)(currentY + (actualY - currentY)/2.0);
+
+    oglDrawFormattedText(dc, &region->m_formattedText,
+             (double)(centreX), (double)(centreY), (double)(m_width-2*xMargin), (double)(actualY - currentY - 2*yMargin),
+             region->m_formatMode);
+    if ((y <= maxY) && (node->Next()))
+    {
+      wxPen *regionPen = region->GetActualPen();
+      if (regionPen)
+      {
+        dc.SetPen(* regionPen);
+        dc.DrawLine(WXROUND(leftX), WXROUND(y), WXROUND(rightX), WXROUND(y));
+      }
+    }
+
+    currentY = actualY;
+
+    node = node->Next();
+  }
+}
+
+void wxDividedShape::SetSize(double w, double h, bool recursive)
+{
+  SetAttachmentSize(w, h);
+  m_width = w;
+  m_height = h;
+  SetRegionSizes();
+}
+
+void wxDividedShape::SetRegionSizes()
+{
+  if (GetRegions().Number() == 0)
+    return;
+
+  double defaultProportion = (double)(GetRegions().Number() > 0 ? (1.0/((double)(GetRegions().Number()))) : 0.0);
+  double currentY = (double)(m_ypos - (m_height / 2.0));
+  double maxY = (double)(m_ypos + (m_height / 2.0));
+
+//  double leftX = (double)(m_xpos - (m_width / 2.0));
+//  double rightX = (double)(m_xpos + (m_width / 2.0));
+
+  wxNode *node = GetRegions().First();
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    double proportion =
+      region->m_regionProportionY <= 0.0 ? defaultProportion : region->m_regionProportionY;
+
+    double sizeY = (double)proportion*m_height;
+    double y = currentY + sizeY;
+    double actualY = maxY < y ? maxY : y;
+
+    double centreY = (double)(currentY + (actualY - currentY)/2.0);
+
+    region->SetSize(m_width, sizeY);
+    region->SetPosition(0.0, (double)(centreY - m_ypos));
+    currentY = actualY;
+    node = node->Next();
+  }
+}
+
+// Attachment points correspond to regions in the divided box
+bool wxDividedShape::GetAttachmentPosition(int attachment, double *x, double *y, int nth, int no_arcs,
+  wxLineShape *line)
+{
+  int totalNumberAttachments = (GetRegions().Number() * 2) + 2;
+  if ((GetAttachmentMode() == ATTACHMENT_MODE_NONE) || (attachment >= totalNumberAttachments))
+  {
+    return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs);
+  }
+
+  int n = GetRegions().Number();
+  bool isEnd = (line && line->IsEnd(this));
+
+  double left = (double)(m_xpos - m_width/2.0);
+  double right = (double)(m_xpos + m_width/2.0);
+  double top = (double)(m_ypos - m_height/2.0);
+  double bottom = (double)(m_ypos + m_height/2.0);
+
+  // Zero is top, n+1 is bottom.
+  if (attachment == 0)
+  {
+    *y = top;
+    if (m_spaceAttachments)
+    {
+      if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
+      {
+        // Align line according to the next handle along
+        wxRealPoint *point = line->GetNextControlPoint(this);
+        if (point->x < left)
+          *x = left;
+        else if (point->x > right)
+          *x = right;
+        else
+          *x = point->x;
+      }
+      else
+        *x = left + (nth + 1)*m_width/(no_arcs + 1);
+    }
+    else
+      *x = m_xpos;
+  }
+  else if (attachment == (n+1))
+  {
+    *y = bottom;
+    if (m_spaceAttachments)
+    {
+      if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
+      {
+        // Align line according to the next handle along
+        wxRealPoint *point = line->GetNextControlPoint(this);
+        if (point->x < left)
+          *x = left;
+        else if (point->x > right)
+          *x = right;
+        else
+          *x = point->x;
+      }
+      else
+        *x = left + (nth + 1)*m_width/(no_arcs + 1);
+    }
+    else
+      *x = m_xpos;
+  }
+  // Left or right.
+  else
+  {
+    int i = 0;
+    bool isLeft = FALSE;
+    if (attachment < (n+1))
+    {
+      i = attachment-1;
+      isLeft = FALSE;
+    }
+    else
+    {
+      i = (totalNumberAttachments - attachment - 1);
+      isLeft = TRUE;
+    }
+    wxNode *node = GetRegions().Nth(i);
+    if (node)
+    {
+      wxShapeRegion *region = (wxShapeRegion *)node->Data();
+
+      if (isLeft)
+        *x = left;
+      else
+        *x = right;
+
+      // Calculate top and bottom of region
+      top = (double)((m_ypos + region->m_y) - (region->m_height/2.0));
+      bottom = (double)((m_ypos + region->m_y) + (region->m_height/2.0));
+
+      // Assuming we can trust the absolute size and
+      // position of these regions...
+      if (m_spaceAttachments)
+      {
+        if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
+        {
+          // Align line according to the next handle along
+          wxRealPoint *point = line->GetNextControlPoint(this);
+          if (point->y < bottom)
+            *y = bottom;
+          else if (point->y > top)
+            *y = top;
+          else
+            *y = point->y;
+        }
+        else
+//          *y = (double)(((m_ypos + region->m_y) - (region->m_height/2.0)) + (nth + 1)*region->m_height/(no_arcs+1));
+          *y = (double)(top + (nth + 1)*region->m_height/(no_arcs+1));
+      }
+      else
+        *y = (double)(m_ypos + region->m_y);
+    }
+    else
+    {
+      *x = m_xpos;
+      *y = m_ypos;
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+int wxDividedShape::GetNumberOfAttachments() const
+{
+  // There are two attachments for each region (left and right),
+  // plus one on the top and one on the bottom.
+  int n = (GetRegions().Number() * 2) + 2;
+
+  int maxN = n - 1;
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    if (point->m_id > maxN)
+      maxN = point->m_id;
+    node = node->Next();
+  }
+  return maxN + 1;
+}
+
+bool wxDividedShape::AttachmentIsValid(int attachment)
+{
+  int totalNumberAttachments = (GetRegions().Number() * 2) + 2;
+  if (attachment >= totalNumberAttachments)
+  {
+    return wxShape::AttachmentIsValid(attachment);
+  }
+  else if (attachment >= 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+void wxDividedShape::Copy(wxShape& copy)
+{
+  wxRectangleShape::Copy(copy);
+}
+
+// Region operations
+
+void wxDividedShape::MakeControlPoints()
+{
+  wxRectangleShape::MakeControlPoints();
+
+  MakeMandatoryControlPoints();
+}
+
+void wxDividedShape::MakeMandatoryControlPoints()
+{
+  double currentY = (double)(GetY() - (m_height / 2.0));
+  double maxY = (double)(GetY() + (m_height / 2.0));
+
+  wxNode *node = GetRegions().First();
+  int i = 0;
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+
+    double proportion = region->m_regionProportionY;
+
+    double y = currentY + m_height*proportion;
+    double actualY = (double)(maxY < y ? maxY : y);
+
+    if (node->Next())
+    {
+      wxDividedShapeControlPoint *controlPoint =
+        new wxDividedShapeControlPoint(m_canvas, this, i, CONTROL_POINT_SIZE, 0.0, (double)(actualY - GetY()), 0);
+      m_canvas->AddShape(controlPoint);
+      m_controlPoints.Append(controlPoint);
+    }
+    currentY = actualY;
+    i ++;
+    node = node->Next();
+  }
+}
+
+void wxDividedShape::ResetControlPoints()
+{
+  // May only have the region handles, (n - 1) of them.
+  if (m_controlPoints.Number() > (GetRegions().Number() - 1))
+    wxRectangleShape::ResetControlPoints();
+
+  ResetMandatoryControlPoints();
+}
+
+void wxDividedShape::ResetMandatoryControlPoints()
+{
+  double currentY = (double)(GetY() - (m_height / 2.0));
+  double maxY = (double)(GetY() + (m_height / 2.0));
+
+  wxNode *node = m_controlPoints.First();
+  int i = 0;
+  while (node)
+  {
+    wxControlPoint *controlPoint = (wxControlPoint *)node->Data();
+    if (controlPoint->IsKindOf(CLASSINFO(wxDividedShapeControlPoint)))
+    {
+      wxNode *node1 = GetRegions().Nth(i);
+      wxShapeRegion *region = (wxShapeRegion *)node1->Data();
+
+      double proportion = region->m_regionProportionY;
+
+      double y = currentY + m_height*proportion;
+      double actualY = (double)(maxY < y ? maxY : y);
+
+      controlPoint->m_xoffset = 0.0;
+      controlPoint->m_yoffset = (double)(actualY - GetY());
+      currentY = actualY;
+      i ++;
+    }
+    node = node->Next();
+  }
+}
+
+#ifdef PROLOGIO
+void wxDividedShape::WriteAttributes(wxExpr *clause)
+{
+  wxRectangleShape::WriteAttributes(clause);
+}
+
+void wxDividedShape::ReadAttributes(wxExpr *clause)
+{
+  wxRectangleShape::ReadAttributes(clause);
+}
+#endif
+
+/*
+ * Edit the division colour/style
+ *
+ */
+
+void wxDividedShape::EditRegions()
+{
+  wxMessageBox("EditRegions() is unimplemented.", "OGL", wxOK);
+
+  // TODO
+#if 0
+  if (GetRegions().Number() < 2)
+    return;
+
+  wxBeginBusyCursor();
+
+  GraphicsForm *form = new GraphicsForm("Divided nodes");
+  // Need an array to store all the style strings,
+  // since they need to be converted to integers
+  char **styleStrings = new char *[GetRegions().Number()];
+  for (int j = 0; j < GetRegions().Number(); j++)
+    styleStrings[j] = NULL;
+
+  int i = 0;
+  wxNode *node = GetRegions().First();
+  while (node && node->Next())
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+    char buf[50];
+    sprintf(buf, "Region %d", (i+1));
+    form->Add(wxMakeFormMessage(buf));
+    form->Add(wxMakeFormNewLine());
+
+    form->Add(wxMakeFormString("Colour", &region->penColour, wxFORM_CHOICE,
+              new wxList(wxMakeConstraintStrings(
+    "Invisible"        ,
+    "BLACK"            ,
+    "BLUE"             ,
+    "BROWN"            ,
+    "CORAL"            ,
+    "CYAN"             ,
+    "DARK GREY"        ,
+    "DARK GREEN"       ,
+    "DIM GREY"         ,
+    "GREY"             ,
+    "GREEN"            ,
+    "LIGHT BLUE"       ,
+    "LIGHT GREY"       ,
+    "MAGENTA"          ,
+    "MAROON"           ,
+    "NAVY"             ,
+    "ORANGE"           ,
+    "PURPLE"           ,
+    "RED"              ,
+    "TURQUOISE"        ,
+    "VIOLET"           ,
+    "WHITE"            ,
+    "YELLOW"           ,
+    NULL),
+    NULL), NULL, wxVERTICAL, 150));
+
+    char *styleString = NULL;
+    switch (region->penStyle)
+    {
+      case wxSHORT_DASH:
+        styleString = "Short Dash";
+        break;
+      case wxLONG_DASH:
+        styleString = "Long Dash";
+        break;
+      case wxDOT:
+        styleString = "Dot";
+        break;
+      case wxDOT_DASH:
+        styleString = "Dot Dash";
+        break;
+      case wxSOLID:
+      default:
+        styleString = "Solid";
+        break;
+    }
+    styleStrings[i] = copystring(styleString);
+    form->Add(wxMakeFormString("Style", &(styleStrings[i]), wxFORM_CHOICE,
+              new wxList(wxMakeConstraintStrings(
+    "Solid"            ,
+    "Short Dash"       ,
+    "Long Dash"        ,
+    "Dot"              ,
+    "Dot Dash"         ,
+    NULL),
+    NULL), NULL, wxVERTICAL, 100));
+    node = node->Next();
+    i ++;
+    if (node && node->Next())
+      form->Add(wxMakeFormNewLine());
+  }
+  wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Divided object properties", 10, 10, 500, 500);
+  if (GraphicsLabelFont)
+    dialog->SetLabelFont(GraphicsLabelFont);
+  if (GraphicsButtonFont)
+    dialog->SetButtonFont(GraphicsButtonFont);
+  form->AssociatePanel(dialog);
+  form->dialog = dialog;
+
+  dialog->Fit();
+  dialog->Centre(wxBOTH);
+
+  wxEndBusyCursor();
+
+  dialog->Show(TRUE);
+
+  node = GetRegions().First();
+  i = 0;
+  while (node)
+  {
+    wxShapeRegion *region = (wxShapeRegion *)node->Data();
+
+    if (styleStrings[i])
+    {
+      if (strcmp(styleStrings[i], "Solid") == 0)
+        region->penStyle = wxSOLID;
+      else if (strcmp(styleStrings[i], "Dot") == 0)
+        region->penStyle = wxDOT;
+      else if (strcmp(styleStrings[i], "Short Dash") == 0)
+        region->penStyle = wxSHORT_DASH;
+      else if (strcmp(styleStrings[i], "Long Dash") == 0)
+        region->penStyle = wxLONG_DASH;
+      else if (strcmp(styleStrings[i], "Dot Dash") == 0)
+        region->penStyle = wxDOT_DASH;
+      delete[] styleStrings[i];
+    }
+    region->m_actualPenObject = NULL;
+    node = node->Next();
+    i ++;
+  }
+  delete[] styleStrings;
+  Draw(dc);
+#endif
+}
+
+void wxDividedShape::OnRightClick(double x, double y, int keys, int attachment)
+{
+  if (keys & KEY_CTRL)
+  {
+    EditRegions();
+  }
+  else
+  {
+    wxRectangleShape::OnRightClick(x, y, keys, attachment);
+  }
+}
+
+wxDividedShapeControlPoint::wxDividedShapeControlPoint(wxShapeCanvas *the_canvas, wxShape *object,
+  int region, double size, double the_m_xoffset, double the_m_yoffset, int the_type):
+    wxControlPoint(the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type)
+{
+  regionId = region;
+}
+
+wxDividedShapeControlPoint::~wxDividedShapeControlPoint()
+{
+}
+
+// Implement resizing of divided object division
+void wxDividedShapeControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+    wxClientDC dc(GetCanvas());
+    GetCanvas()->PrepareDC(dc);
+
+    dc.SetLogicalFunction(OGLRBLF);
+    wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+    dc.SetPen(dottedPen);
+    dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+    wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
+    double x1 = (double)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0));
+    double y1 = y;
+    double x2 = (double)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0));
+    double y2 = y;
+    dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y2));
+}
+
+void wxDividedShapeControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+    wxClientDC dc(GetCanvas());
+    GetCanvas()->PrepareDC(dc);
+
+    wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
+    dc.SetLogicalFunction(OGLRBLF);
+    wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+    dc.SetPen(dottedPen);
+    dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+    double x1 = (double)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0));
+    double y1 = y;
+    double x2 = (double)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0));
+    double y2 = y;
+    dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y2));
+    m_canvas->CaptureMouse();
+}
+
+void wxDividedShapeControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+    wxClientDC dc(GetCanvas());
+    GetCanvas()->PrepareDC(dc);
+
+    wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
+    wxNode *node = dividedObject->GetRegions().Nth(regionId);
+    if (!node)
+    return;
+
+    wxShapeRegion *thisRegion = (wxShapeRegion *)node->Data();
+    wxShapeRegion *nextRegion = NULL; // Region below this one
+
+    dc.SetLogicalFunction(wxCOPY);
+
+    m_canvas->ReleaseMouse();
+
+    // Find the old top and bottom of this region,
+    // and calculate the new proportion for this region
+    // if legal.
+
+    double currentY = (double)(dividedObject->GetY() - (dividedObject->GetHeight() / 2.0));
+    double maxY = (double)(dividedObject->GetY() + (dividedObject->GetHeight() / 2.0));
+
+    // Save values
+    double thisRegionTop = 0.0;
+    double thisRegionBottom = 0.0;
+    double nextRegionBottom = 0.0;
+
+    node = dividedObject->GetRegions().First();
+    while (node)
+    {
+      wxShapeRegion *region = (wxShapeRegion *)node->Data();
+
+      double proportion = region->m_regionProportionY;
+      double yy = currentY + (dividedObject->GetHeight()*proportion);
+      double actualY = (double)(maxY < yy ? maxY : yy);
+
+      if (region == thisRegion)
+      {
+        thisRegionTop = currentY;
+        thisRegionBottom = actualY;
+        if (node->Next())
+          nextRegion = (wxShapeRegion *)node->Next()->Data();
+      }
+      if (region == nextRegion)
+      {
+        nextRegionBottom = actualY;
+      }
+
+      currentY = actualY;
+      node = node->Next();
+    }
+    if (!nextRegion)
+      return;
+
+    // Check that we haven't gone above this region or below
+    // next region.
+    if ((y <= thisRegionTop) || (y >= nextRegionBottom))
+    return;
+
+    dividedObject->EraseLinks(dc);
+
+    // Now calculate the new proportions of this region and the next region.
+    double thisProportion = (double)((y - thisRegionTop)/dividedObject->GetHeight());
+    double nextProportion = (double)((nextRegionBottom - y)/dividedObject->GetHeight());
+    thisRegion->SetProportions(0.0, thisProportion);
+    nextRegion->SetProportions(0.0, nextProportion);
+    m_yoffset = (double)(y - dividedObject->GetY());
+
+    // Now reformat text
+    int i = 0;
+    node = dividedObject->GetRegions().First();
+    while (node)
+    {
+        wxShapeRegion *region = (wxShapeRegion *)node->Data();
+        if (region->GetText())
+        {
+        char *s = copystring(region->GetText());
+        dividedObject->FormatText(dc, s, i);
+        delete[] s;
+        }
+        node = node->Next();
+        i++;
+    }
+    dividedObject->SetRegionSizes();
+    dividedObject->Draw(dc);
+    dividedObject->GetEventHandler()->OnMoveLinks(dc);
+}
+
diff --git a/src/ogl/drawn.cpp b/src/ogl/drawn.cpp
new file mode 100644 (file)
index 0000000..b410537
--- /dev/null
@@ -0,0 +1,2489 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        drawn.cpp
+// Purpose:     wxDrawnShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "drawn.h"
+#pragma implementation "drawnp.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/mfutils.h>
+#include <wx/ogl/drawn.h>
+#include <wx/ogl/drawnp.h>
+#include <wx/ogl/misc.h>
+
+static void IntToHex(unsigned int dec, char *buf);
+static unsigned long HexToInt(char *buf);
+extern char *oglBuffer;
+
+#define gyTYPE_PEN   40
+#define gyTYPE_BRUSH 41
+#define gyTYPE_FONT  42
+
+/*
+ * Drawn object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxDrawnShape, wxRectangleShape)
+
+wxDrawnShape::wxDrawnShape():wxRectangleShape(100.0, 50.0)
+{
+  m_saveToFile = TRUE;
+  m_currentAngle = oglDRAWN_ANGLE_0;
+}
+
+wxDrawnShape::~wxDrawnShape()
+{
+}
+
+void wxDrawnShape::OnDraw(wxDC& dc)
+{
+  // Pass pen and brush in case we have force outline
+  // and fill colours
+  if (m_shadowMode != SHADOW_NONE)
+  {
+    if (m_shadowBrush)
+      m_metafiles[m_currentAngle].m_fillBrush = m_shadowBrush;
+    m_metafiles[m_currentAngle].m_outlinePen = g_oglTransparentPen;
+    m_metafiles[m_currentAngle].Draw(dc, m_xpos + m_shadowOffsetX, m_ypos + m_shadowOffsetY);
+  }
+    
+  m_metafiles[m_currentAngle].m_outlinePen = m_pen;
+  m_metafiles[m_currentAngle].m_fillBrush = m_brush;
+  m_metafiles[m_currentAngle].Draw(dc, m_xpos, m_ypos);
+}
+
+void wxDrawnShape::SetSize(double w, double h, bool recursive)
+{
+  SetAttachmentSize(w, h);
+
+  double scaleX;
+  double scaleY;
+  if (GetWidth() == 0.0)
+    scaleX = 1.0;
+  else scaleX = w/GetWidth();
+  if (GetHeight() == 0.0)
+    scaleY = 1.0;
+  else scaleY = h/GetHeight();
+
+  int i = 0;
+  for (i = 0; i < 4; i++)
+  {
+    if (m_metafiles[i].IsValid())
+        m_metafiles[i].Scale(scaleX, scaleY);
+  }
+  m_width = w;
+  m_height = h;
+  SetDefaultRegionSize();
+}
+
+void wxDrawnShape::Scale(double sx, double sy)
+{
+    int i;
+    for (i = 0; i < 4; i++)
+    {
+        if (m_metafiles[i].IsValid())
+        {
+            m_metafiles[i].Scale(sx, sy);
+            m_metafiles[i].CalculateSize(this);
+        }
+    }
+}
+
+void wxDrawnShape::Translate(double x, double y)
+{
+    int i;
+    for (i = 0; i < 4; i++)
+    {
+        if (m_metafiles[i].IsValid())
+        {
+            m_metafiles[i].Translate(x, y);
+            m_metafiles[i].CalculateSize(this);
+        }
+    }
+}
+
+// theta is absolute rotation from the zero position
+void wxDrawnShape::Rotate(double x, double y, double theta)
+{
+  m_currentAngle = DetermineMetaFile(theta);
+
+  if (m_currentAngle == 0)
+  {
+    // Rotate metafile
+    if (!m_metafiles[0].GetRotateable())
+      return;
+    
+    m_metafiles[0].Rotate(x, y, theta);
+  }
+
+  double actualTheta = theta-m_rotation;
+
+  // Rotate attachment points
+  double sinTheta = (double)sin(actualTheta);
+  double cosTheta = (double)cos(actualTheta);
+  wxNode *node = m_attachmentPoints.First();
+  while (node)
+  {
+    wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+    double x1 = point->m_x;
+    double y1 = point->m_y;
+    point->m_x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+    point->m_y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+    node = node->Next();
+  }
+  m_rotation = theta;
+
+  m_metafiles[m_currentAngle].CalculateSize(this);
+}
+
+// Which metafile do we use now? Based on current rotation and validity
+// of metafiles.
+
+int wxDrawnShape::DetermineMetaFile(double rotation)
+{
+    double tolerance = 0.0001;
+    const double pi = 3.1415926535897932384626433832795 ;
+    double angle1 = 0.0;
+    double angle2 = pi/2.0;
+    double angle3 = pi;
+    double angle4 = 3.0*pi/2.0;
+
+    int whichMetafile = 0;
+
+    if (oglRoughlyEqual(rotation, angle1, tolerance))
+    {
+        whichMetafile = 0;
+    }
+    else if (oglRoughlyEqual(rotation, angle2, tolerance))
+    {
+        whichMetafile = 1;
+    }
+    else if (oglRoughlyEqual(rotation, angle3, tolerance))
+    {
+        whichMetafile = 2;
+    }
+    else if (oglRoughlyEqual(rotation, angle4, tolerance))
+    {
+        whichMetafile = 3;
+    }
+
+    if ((whichMetafile > 0) && !m_metafiles[whichMetafile].IsValid())
+        whichMetafile = 0;
+
+    return whichMetafile;
+}
+
+void wxDrawnShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
+{
+    if (m_metafiles[m_currentAngle].GetOutlineOp() != -1)
+    {
+        wxNode* node = m_metafiles[m_currentAngle].GetOps().Nth(m_metafiles[m_currentAngle].GetOutlineOp());
+        wxASSERT (node != NULL);
+        wxDrawOp* op = (wxDrawOp*) node->Data();
+
+        if (op->OnDrawOutline(dc, x, y, w, h, m_width, m_height))
+            return;
+    }
+
+    // Default... just use a rectangle
+    wxRectangleShape::OnDrawOutline(dc, x, y, w, h);
+}
+
+// Get the perimeter point using the special outline op, if there is one,
+// otherwise use default wxRectangleShape scheme
+bool wxDrawnShape::GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3)
+{
+    if (m_metafiles[m_currentAngle].GetOutlineOp() != -1)
+    {
+        wxNode* node = m_metafiles[m_currentAngle].GetOps().Nth(m_metafiles[m_currentAngle].GetOutlineOp());
+        wxASSERT (node != NULL);
+        wxDrawOp* op = (wxDrawOp*) node->Data();
+
+        if (op->GetPerimeterPoint(x1, y1, x2, y2, x3, y3, GetX(), GetY(), GetAttachmentMode()))
+            return TRUE;
+    }
+
+    // Default... just use a rectangle
+    return wxRectangleShape::GetPerimeterPoint(x1, y1, x2, y2, x3, y3);
+}
+
+#ifdef PROLOGIO
+void wxDrawnShape::WriteAttributes(wxExpr *clause)
+{
+  wxRectangleShape::WriteAttributes(clause);
+
+  clause->AddAttributeValue("current_angle", (long)m_currentAngle);
+  clause->AddAttributeValue("save_metafile", (long)m_saveToFile);
+  if (m_saveToFile)
+  {
+    int i = 0;
+    for (i = 0; i < 4; i++)
+    {
+        if (m_metafiles[i].IsValid())
+            m_metafiles[i].WriteAttributes(clause, i);
+    }
+  }
+}
+
+void wxDrawnShape::ReadAttributes(wxExpr *clause)
+{
+  wxRectangleShape::ReadAttributes(clause);
+
+  int iVal = (int) m_saveToFile;
+  clause->GetAttributeValue("save_metafile", iVal);
+  clause->GetAttributeValue("current_angle", m_currentAngle);
+  m_saveToFile = (iVal != 0);
+
+  if (m_saveToFile)
+  {
+    int i = 0;
+    for (i = 0; i < 4; i++)
+    {
+      m_metafiles[i].ReadAttributes(clause, i);
+    }
+  }
+}
+#endif
+
+// Does the copying for this object
+void wxDrawnShape::Copy(wxShape& copy)
+{
+  wxRectangleShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxDrawnShape)) ) ;
+
+  wxDrawnShape& drawnCopy = (wxDrawnShape&) copy;
+
+  int i = 0;
+  for (i = 0; i < 4; i++)
+  {
+    m_metafiles[i].Copy(drawnCopy.m_metafiles[i]);
+  }
+  drawnCopy.m_saveToFile = m_saveToFile;
+  drawnCopy.m_currentAngle = m_currentAngle;
+}
+
+bool wxDrawnShape::LoadFromMetaFile(char *filename)
+{
+  return m_metafiles[0].LoadFromMetaFile(filename, &m_width, &m_height);
+}
+
+// Set of functions for drawing into a pseudo metafile.
+// They use integers, but doubles are used internally for accuracy
+// when scaling.
+
+void wxDrawnShape::DrawLine(const wxPoint& pt1, const wxPoint& pt2)
+{
+    m_metafiles[m_currentAngle].DrawLine(pt1, pt2);
+}
+
+void wxDrawnShape::DrawRectangle(const wxRect& rect)
+{
+    m_metafiles[m_currentAngle].DrawRectangle(rect);
+}
+
+void wxDrawnShape::DrawRoundedRectangle(const wxRect& rect, double radius)
+{
+    m_metafiles[m_currentAngle].DrawRoundedRectangle(rect, radius);
+}
+
+void wxDrawnShape::DrawEllipse(const wxRect& rect)
+{
+    m_metafiles[m_currentAngle].DrawEllipse(rect);
+}
+
+void wxDrawnShape::DrawArc(const wxPoint& centrePt, const wxPoint& startPt, const wxPoint& endPt)
+{
+    m_metafiles[m_currentAngle].DrawArc(centrePt, startPt, endPt);
+}
+
+void wxDrawnShape::DrawEllipticArc(const wxRect& rect, double startAngle, double endAngle)
+{
+    m_metafiles[m_currentAngle].DrawEllipticArc(rect, startAngle, endAngle);
+}
+
+void wxDrawnShape::DrawPoint(const wxPoint& pt)
+{
+    m_metafiles[m_currentAngle].DrawPoint(pt);
+}
+
+void wxDrawnShape::DrawText(const wxString& text, const wxPoint& pt)
+{
+    m_metafiles[m_currentAngle].DrawText(text, pt);
+}
+
+void wxDrawnShape::DrawLines(int n, wxPoint pts[])
+{
+    m_metafiles[m_currentAngle].DrawLines(n, pts);
+}
+
+void wxDrawnShape::DrawPolygon(int n, wxPoint pts[], int flags)
+{
+    if (flags & oglMETAFLAGS_ATTACHMENTS)
+    {
+        ClearAttachments();
+        int i;
+        for (i = 0; i < n; i++)
+            m_attachmentPoints.Append(new wxAttachmentPoint(i, pts[i].x, pts[i].y));
+    }
+    m_metafiles[m_currentAngle].DrawPolygon(n, pts, flags);
+}
+
+void wxDrawnShape::DrawSpline(int n, wxPoint pts[])
+{
+    m_metafiles[m_currentAngle].DrawSpline(n, pts);
+}
+
+void wxDrawnShape::SetClippingRect(const wxRect& rect)
+{
+    m_metafiles[m_currentAngle].SetClippingRect(rect);
+}
+
+void wxDrawnShape::DestroyClippingRect()
+{
+    m_metafiles[m_currentAngle].DestroyClippingRect();
+}
+
+void wxDrawnShape::SetDrawnPen(wxPen* pen, bool isOutline)
+{
+    m_metafiles[m_currentAngle].SetPen(pen, isOutline);
+}
+
+void wxDrawnShape::SetDrawnBrush(wxBrush* brush, bool isFill)
+{
+    m_metafiles[m_currentAngle].SetBrush(brush, isFill);
+}
+
+void wxDrawnShape::SetDrawnFont(wxFont* font)
+{
+    m_metafiles[m_currentAngle].SetFont(font);
+}
+
+void wxDrawnShape::SetDrawnTextColour(const wxColour& colour)
+{
+    m_metafiles[m_currentAngle].SetTextColour(colour);
+}
+
+void wxDrawnShape::SetDrawnBackgroundColour(const wxColour& colour)
+{
+    m_metafiles[m_currentAngle].SetBackgroundColour(colour);
+}
+
+void wxDrawnShape::SetDrawnBackgroundMode(int mode)
+{
+    m_metafiles[m_currentAngle].SetBackgroundMode(mode);
+}
+
+
+/*
+ * Individual operations
+ *
+ */
+/*
+ * Set font, brush, text colour
+ *
+ */
+wxOpSetGDI::wxOpSetGDI(int theOp, wxPseudoMetaFile *theImage, int theGdiIndex, int theMode):
+  wxDrawOp(theOp)
+{
+  m_gdiIndex = theGdiIndex;
+  m_image = theImage;
+  m_mode = theMode;
+}
+
+void wxOpSetGDI::Do(wxDC& dc, double xoffset, double yoffset)
+{
+  switch (m_op)
+  {
+    case DRAWOP_SET_PEN:
+    {
+      // Check for overriding this operation for outline
+      // colour
+      if (m_image->m_outlineColours.Member((wxObject *)m_gdiIndex))
+      {
+        if (m_image->m_outlinePen)
+          dc.SetPen(* m_image->m_outlinePen);
+      }
+      else
+      {
+        wxNode *node = m_image->m_gdiObjects.Nth(m_gdiIndex);
+        if (node)
+        {
+          wxPen *pen = (wxPen *)node->Data();
+          if (pen)
+            dc.SetPen(* pen);
+        }
+      }
+      break;
+    }
+    case DRAWOP_SET_BRUSH:
+    {
+      // Check for overriding this operation for outline or fill
+      // colour
+      if (m_image->m_outlineColours.Member((wxObject *)m_gdiIndex))
+      {
+        // Need to construct a brush to match the outline pen's colour
+        if (m_image->m_outlinePen)
+        {
+          wxBrush *br = wxTheBrushList->FindOrCreateBrush(m_image->m_outlinePen->GetColour(), wxSOLID);
+          if (br)
+            dc.SetBrush(* br);
+        }
+      }
+      else if (m_image->m_fillColours.Member((wxObject *)m_gdiIndex))
+      {
+        if (m_image->m_fillBrush)
+        {
+          dc.SetBrush(* m_image->m_fillBrush);
+        }
+      }
+      else
+      {
+        wxNode *node = m_image->m_gdiObjects.Nth(m_gdiIndex);
+        if (node)
+        {
+          wxBrush *brush = (wxBrush *)node->Data();
+          if (brush)
+            dc.SetBrush(* brush);
+        }
+      }
+      break;
+    }
+    case DRAWOP_SET_FONT:
+    {
+      wxNode *node = m_image->m_gdiObjects.Nth(m_gdiIndex);
+      if (node)
+      {
+        wxFont *font = (wxFont *)node->Data();
+        if (font)
+          dc.SetFont(* font);
+      }
+      break;
+    }
+    case DRAWOP_SET_TEXT_COLOUR:
+    {
+      wxColour col(m_r,m_g,m_b);
+      dc.SetTextForeground(col);
+      break;
+    }
+    case DRAWOP_SET_BK_COLOUR:
+    {
+      wxColour col(m_r,m_g,m_b);
+      dc.SetTextBackground(col);
+      break;
+    }
+    case DRAWOP_SET_BK_MODE:
+    {
+      dc.SetBackgroundMode(m_mode);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+wxDrawOp *wxOpSetGDI::Copy(wxPseudoMetaFile *newImage)
+{
+  wxOpSetGDI *newOp = new wxOpSetGDI(m_op, newImage, m_gdiIndex, m_mode);
+  newOp->m_r = m_r;
+  newOp->m_g = m_g;
+  newOp->m_b = m_b;
+  return newOp;
+}
+
+wxExpr *wxOpSetGDI::WriteExpr(wxPseudoMetaFile *image)
+{
+  wxExpr *expr = new wxExpr(wxExprList);
+  expr->Append(new wxExpr((long)m_op));
+  switch (m_op)
+  {
+    case DRAWOP_SET_PEN:
+    case DRAWOP_SET_BRUSH:
+    case DRAWOP_SET_FONT:
+    {
+      expr->Append(new wxExpr((long)m_gdiIndex));
+      break;
+    }
+    case DRAWOP_SET_TEXT_COLOUR:
+    case DRAWOP_SET_BK_COLOUR:
+    {
+      expr->Append(new wxExpr((long)m_r));
+      expr->Append(new wxExpr((long)m_g));
+      expr->Append(new wxExpr((long)m_b));
+      break;
+    }
+    case DRAWOP_SET_BK_MODE:
+    {
+      expr->Append(new wxExpr((long)m_mode));
+      break;
+    }
+    default:
+      break;
+  }
+  return expr;
+}
+
+void wxOpSetGDI::ReadExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+  switch (m_op)
+  {
+    case DRAWOP_SET_PEN:
+    case DRAWOP_SET_BRUSH:
+    case DRAWOP_SET_FONT:
+    {
+      m_gdiIndex = (int)expr->Nth(1)->IntegerValue();
+      break;
+    }
+    case DRAWOP_SET_TEXT_COLOUR:
+    case DRAWOP_SET_BK_COLOUR:
+    {
+      m_r = (unsigned char)expr->Nth(1)->IntegerValue();
+      m_g = (unsigned char)expr->Nth(2)->IntegerValue();
+      m_b = (unsigned char)expr->Nth(3)->IntegerValue();
+      break;
+    }
+    case DRAWOP_SET_BK_MODE:
+    {
+      m_mode = (int)expr->Nth(1)->IntegerValue();
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+/*
+ * Set/destroy clipping
+ *
+ */
+wxOpSetClipping::wxOpSetClipping(int theOp, double theX1, double theY1,
+    double theX2, double theY2):wxDrawOp(theOp)
+{
+  m_x1 = theX1;
+  m_y1 = theY1;
+  m_x2 = theX2;
+  m_y2 = theY2;
+}
+
+wxDrawOp *wxOpSetClipping::Copy(wxPseudoMetaFile *newImage)
+{
+  wxOpSetClipping *newOp = new wxOpSetClipping(m_op, m_x1, m_y1, m_x2, m_y2);
+  return newOp;
+}
+    
+void wxOpSetClipping::Do(wxDC& dc, double xoffset, double yoffset)
+{
+  switch (m_op)
+  {
+    case DRAWOP_SET_CLIPPING_RECT:
+    {
+      dc.SetClippingRegion((long)(m_x1 + xoffset), (long)(m_y1 + yoffset), (long)(m_x2 + xoffset), (long)(m_y2 + yoffset));
+      break;
+    }
+    case DRAWOP_DESTROY_CLIPPING_RECT:
+    {
+      dc.DestroyClippingRegion();
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void wxOpSetClipping::Scale(double xScale, double yScale)
+{
+  m_x1 *= xScale;
+  m_y1 *= yScale;
+  m_x2 *= xScale;
+  m_y2 *= yScale;
+}
+
+void wxOpSetClipping::Translate(double x, double y)
+{
+  m_x1 += x;
+  m_y1 += y;
+}
+
+wxExpr *wxOpSetClipping::WriteExpr(wxPseudoMetaFile *image)
+{
+  wxExpr *expr = new wxExpr(wxExprList);
+  expr->Append(new wxExpr((long)m_op));
+  switch (m_op)
+  {
+    case DRAWOP_SET_CLIPPING_RECT:
+    {
+      expr->Append(new wxExpr(m_x1));
+      expr->Append(new wxExpr(m_y1));
+      expr->Append(new wxExpr(m_x2));
+      expr->Append(new wxExpr(m_y2));
+      break;
+    }
+    default:
+      break;
+  }
+  return expr;
+}
+
+void wxOpSetClipping::ReadExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+  switch (m_op)
+  {
+    case DRAWOP_SET_CLIPPING_RECT:
+    {
+      m_x1 = expr->Nth(1)->RealValue();
+      m_y1 = expr->Nth(2)->RealValue();
+      m_x2 = expr->Nth(3)->RealValue();
+      m_y2 = expr->Nth(4)->RealValue();
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+/*
+ * Draw line, rectangle, rounded rectangle, ellipse, point, arc, text
+ *
+ */
+wxOpDraw::wxOpDraw(int theOp, double theX1, double theY1, double theX2, double theY2,
+         double theRadius, char *s):wxDrawOp(theOp)
+{
+  m_x1 = theX1;
+  m_y1 = theY1;
+  m_x2 = theX2;
+  m_y2 = theY2;
+  m_x3 = 0.0;
+  m_y3 = 0.0;
+  m_radius = theRadius;
+  if (s) m_textString = copystring(s);
+  else m_textString = NULL;
+}
+
+wxOpDraw::~wxOpDraw()
+{
+  if (m_textString) delete[] m_textString;
+}
+
+wxDrawOp *wxOpDraw::Copy(wxPseudoMetaFile *newImage)
+{
+  wxOpDraw *newOp = new wxOpDraw(m_op, m_x1, m_y1, m_x2, m_y2, m_radius, m_textString);
+  newOp->m_x3 = m_x3;
+  newOp->m_y3 = m_y3;
+  return newOp;
+}
+
+void wxOpDraw::Do(wxDC& dc, double xoffset, double yoffset)
+{
+  switch (m_op)
+  {
+    case DRAWOP_DRAW_LINE:
+    {
+      dc.DrawLine(WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset), WXROUND(m_x2+xoffset), WXROUND(m_y2+yoffset));
+      break;
+    }
+    case DRAWOP_DRAW_RECT:
+    {
+      dc.DrawRectangle(WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset), WXROUND(m_x2), WXROUND(m_y2));
+      break;
+    }
+    case DRAWOP_DRAW_ROUNDED_RECT:
+    {
+      dc.DrawRoundedRectangle(WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset), WXROUND(m_x2), WXROUND(m_y2), m_radius);
+      break;
+    }
+    case DRAWOP_DRAW_ELLIPSE:
+    {
+      dc.DrawEllipse(WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset), WXROUND(m_x2), WXROUND(m_y2));
+      break;
+    }
+    case DRAWOP_DRAW_ARC:
+    {
+      dc.DrawArc(WXROUND(m_x2+xoffset), WXROUND(m_y2+yoffset),
+                 WXROUND(m_x3+xoffset), WXROUND(m_y3+yoffset),
+                 WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset));
+      break;
+    }
+    case DRAWOP_DRAW_ELLIPTIC_ARC:
+    {
+      const double pi = 3.1415926535897932384626433832795 ;
+
+      // Convert back to degrees
+      dc.DrawEllipticArc(
+                 WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset),
+                 WXROUND(m_x2), WXROUND(m_y2),
+                 WXROUND(m_x3*(360.0/(2.0*pi))), WXROUND(m_y3*(360.0/(2.0*pi))));
+      break;
+    }
+    case DRAWOP_DRAW_POINT:
+    {
+      dc.DrawPoint(WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset));
+      break;
+    }
+    case DRAWOP_DRAW_TEXT:
+    {
+      dc.DrawText(m_textString, WXROUND(m_x1+xoffset), WXROUND(m_y1+yoffset));
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void wxOpDraw::Scale(double scaleX, double scaleY)
+{
+  m_x1 *= scaleX;
+  m_y1 *= scaleY;
+  m_x2 *= scaleX;
+  m_y2 *= scaleY;
+
+  if (m_op != DRAWOP_DRAW_ELLIPTIC_ARC)
+  {
+    m_x3 *= scaleX;
+    m_y3 *= scaleY;
+  }
+
+  m_radius *= scaleX;
+}
+
+void wxOpDraw::Translate(double x, double y)
+{
+  m_x1 += x;
+  m_y1 += y;
+
+  switch (m_op)
+  {
+    case DRAWOP_DRAW_LINE:
+    {
+      m_x2 += x;
+      m_y2 += y;
+      break;
+    }
+    case DRAWOP_DRAW_ARC:
+    {
+      m_x2 += x;
+      m_y2 += y;
+      m_x3 += x;
+      m_y3 += y;
+      break;
+    }
+    case DRAWOP_DRAW_ELLIPTIC_ARC:
+    {
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void wxOpDraw::Rotate(double x, double y, double theta, double sinTheta, double cosTheta)
+{
+  double newX1 = m_x1*cosTheta - m_y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+  double newY1 = m_x1*sinTheta + m_y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+
+  switch (m_op)
+  {
+    case DRAWOP_DRAW_LINE:
+    {
+      double newX2 = m_x2*cosTheta - m_y2*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+      double newY2 = m_x2*sinTheta + m_y2*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+
+         m_x1 = newX1;
+         m_y1 = newY1;
+         m_x2 = newX2;
+         m_y2 = newY2;
+      break;
+    }
+    case DRAWOP_DRAW_RECT:
+    case DRAWOP_DRAW_ROUNDED_RECT:
+    case DRAWOP_DRAW_ELLIPTIC_ARC:
+    {
+      // Assume only 0, 90, 180, 270 degree rotations.
+      // oldX1, oldY1 represents the top left corner. Find the
+      // bottom right, and rotate that. Then the width/height is the difference
+      // between x/y values.
+      double oldBottomRightX = m_x1 + m_x2;
+      double oldBottomRightY = m_y1 + m_y2;
+      double newBottomRightX = oldBottomRightX*cosTheta - oldBottomRightY*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+      double newBottomRightY = oldBottomRightX*sinTheta + oldBottomRightY*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+
+      // Now find the new top-left, bottom-right coordinates.
+      double minX = wxMin(newX1, newBottomRightX);
+      double minY = wxMin(newY1, newBottomRightY);
+      double maxX = wxMax(newX1, newBottomRightX);
+      double maxY = wxMax(newY1, newBottomRightY);
+
+      m_x1 = minX;
+      m_y1 = minY;
+      m_x2 = maxX - minX; // width
+      m_y2 = maxY - minY; // height
+
+      if (m_op == DRAWOP_DRAW_ELLIPTIC_ARC)
+      {
+        // Add rotation to angles
+        m_x3 += theta;
+        m_y3 += theta;
+      }
+
+      break;
+    }
+    case DRAWOP_DRAW_ARC:
+    {
+      double newX2 = m_x2*cosTheta - m_y2*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+      double newY2 = m_x2*sinTheta + m_y2*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+      double newX3 = m_x3*cosTheta - m_y3*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+      double newY3 = m_x3*sinTheta + m_y3*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+
+         m_x1 = newX1;
+         m_y1 = newY1;
+         m_x2 = newX2;
+         m_y2 = newY2;
+         m_x3 = newX3;
+         m_y3 = newY3;
+
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+wxExpr *wxOpDraw::WriteExpr(wxPseudoMetaFile *image)
+{
+  wxExpr *expr = new wxExpr(wxExprList);
+  expr->Append(new wxExpr((long)m_op));
+  switch (m_op)
+  {
+    case DRAWOP_DRAW_LINE:
+    case DRAWOP_DRAW_RECT:
+    case DRAWOP_DRAW_ELLIPSE:
+    {
+      expr->Append(new wxExpr(m_x1));
+      expr->Append(new wxExpr(m_y1));
+      expr->Append(new wxExpr(m_x2));
+      expr->Append(new wxExpr(m_y2));
+      break;
+    }
+    case DRAWOP_DRAW_ROUNDED_RECT:
+    {
+      expr->Append(new wxExpr(m_x1));
+      expr->Append(new wxExpr(m_y1));
+      expr->Append(new wxExpr(m_x2));
+      expr->Append(new wxExpr(m_y2));
+      expr->Append(new wxExpr(m_radius));
+      break;
+    }
+    case DRAWOP_DRAW_POINT:
+    {
+      expr->Append(new wxExpr(m_x1));
+      expr->Append(new wxExpr(m_y1));
+      break;
+    }
+    case DRAWOP_DRAW_TEXT:
+    {
+      expr->Append(new wxExpr(m_x1));
+      expr->Append(new wxExpr(m_y1));
+      expr->Append(new wxExpr(wxExprString, m_textString));
+      break;
+    }
+    case DRAWOP_DRAW_ARC:
+    case DRAWOP_DRAW_ELLIPTIC_ARC:
+    {
+      expr->Append(new wxExpr(m_x1));
+      expr->Append(new wxExpr(m_y1));
+      expr->Append(new wxExpr(m_x2));
+      expr->Append(new wxExpr(m_y2));
+      expr->Append(new wxExpr(m_x3));
+      expr->Append(new wxExpr(m_y3));
+      break;
+    }
+    default:
+    {
+      break;
+    }
+  }
+  return expr;
+}
+
+void wxOpDraw::ReadExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+  switch (m_op)
+  {
+    case DRAWOP_DRAW_LINE:
+    case DRAWOP_DRAW_RECT:
+    case DRAWOP_DRAW_ELLIPSE:
+    {
+      m_x1 = expr->Nth(1)->RealValue();
+      m_y1 = expr->Nth(2)->RealValue();
+      m_x2 = expr->Nth(3)->RealValue();
+      m_y2 = expr->Nth(4)->RealValue();
+      break;
+    }
+    case DRAWOP_DRAW_ROUNDED_RECT:
+    {
+      m_x1 = expr->Nth(1)->RealValue();
+      m_y1 = expr->Nth(2)->RealValue();
+      m_x2 = expr->Nth(3)->RealValue();
+      m_y2 = expr->Nth(4)->RealValue();
+      m_radius = expr->Nth(5)->RealValue();
+      break;
+    }
+    case DRAWOP_DRAW_POINT:
+    {
+      m_x1 = expr->Nth(1)->RealValue();
+      m_y1 = expr->Nth(2)->RealValue();
+      break;
+    }
+    case DRAWOP_DRAW_TEXT:
+    {
+      m_x1 = expr->Nth(1)->RealValue();
+      m_y1 = expr->Nth(2)->RealValue();
+      wxString str(expr->Nth(3)->StringValue());
+      m_textString = copystring((const char*) str);
+      break;
+    }
+    case DRAWOP_DRAW_ARC:
+    case DRAWOP_DRAW_ELLIPTIC_ARC:
+    {
+      m_x1 = expr->Nth(1)->RealValue();
+      m_y1 = expr->Nth(2)->RealValue();
+      m_x2 = expr->Nth(3)->RealValue();
+      m_y2 = expr->Nth(4)->RealValue();
+      m_x3 = expr->Nth(5)->RealValue();
+      m_y3 = expr->Nth(6)->RealValue();
+      break;
+    }
+    default:
+    {
+      break;
+    }
+  }
+}
+
+/*
+ * Draw polygon, polyline, spline
+ *
+ */
+
+wxOpPolyDraw::wxOpPolyDraw(int theOp, int n, wxRealPoint *thePoints):wxDrawOp(theOp)
+{
+  m_noPoints = n;
+  m_points = thePoints;
+}
+
+wxOpPolyDraw::~wxOpPolyDraw()
+{
+  delete[] m_points;
+}
+
+wxDrawOp *wxOpPolyDraw::Copy(wxPseudoMetaFile *newImage)
+{
+  wxRealPoint *newPoints = new wxRealPoint[m_noPoints];
+  for (int i = 0; i < m_noPoints; i++)
+  {
+    newPoints[i].x = m_points[i].x;
+    newPoints[i].y = m_points[i].y;
+  }
+  wxOpPolyDraw *newOp = new wxOpPolyDraw(m_op, m_noPoints, newPoints);
+  return newOp;
+}
+
+void wxOpPolyDraw::Do(wxDC& dc, double xoffset, double yoffset)
+{
+  switch (m_op)
+  {
+    case DRAWOP_DRAW_POLYLINE:
+    {
+        wxPoint *actualPoints = new wxPoint[m_noPoints];
+        int i;
+        for (i = 0; i < m_noPoints; i++)
+        {
+            actualPoints[i].x = WXROUND(m_points[i].x);
+            actualPoints[i].y = WXROUND(m_points[i].y);
+        }
+
+        dc.DrawLines(m_noPoints, actualPoints, WXROUND(xoffset), WXROUND(yoffset));
+
+        delete[] actualPoints;
+        break;
+    }
+    case DRAWOP_DRAW_POLYGON:
+    {
+        wxPoint *actualPoints = new wxPoint[m_noPoints];
+        int i;
+        for (i = 0; i < m_noPoints; i++)
+        {
+            actualPoints[i].x = WXROUND(m_points[i].x);
+            actualPoints[i].y = WXROUND(m_points[i].y);
+        }
+
+        dc.DrawPolygon(m_noPoints, actualPoints, WXROUND(xoffset), WXROUND(yoffset));
+
+        delete[] actualPoints;
+        break;
+    }
+    case DRAWOP_DRAW_SPLINE:
+    {
+        wxPoint *actualPoints = new wxPoint[m_noPoints];
+        int i;
+        for (i = 0; i < m_noPoints; i++)
+        {
+            actualPoints[i].x = WXROUND(m_points[i].x);
+            actualPoints[i].y = WXROUND(m_points[i].y);
+        }
+
+        dc.DrawSpline(m_noPoints, actualPoints); // no offsets in DrawSpline // , xoffset, yoffset);
+
+        delete[] actualPoints;
+        break;
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void wxOpPolyDraw::Scale(double scaleX, double scaleY)
+{
+  for (int i = 0; i < m_noPoints; i++)
+  {
+    m_points[i].x *= scaleX;
+    m_points[i].y *= scaleY;
+  }
+}
+
+void wxOpPolyDraw::Translate(double x, double y)
+{
+  for (int i = 0; i < m_noPoints; i++)
+  {
+    m_points[i].x += x;
+    m_points[i].y += y;
+  }
+}
+
+void wxOpPolyDraw::Rotate(double x, double y, double theta, double sinTheta, double cosTheta)
+{
+  for (int i = 0; i < m_noPoints; i++)
+  {
+    double x1 = m_points[i].x;
+    double y1 = m_points[i].y;
+    m_points[i].x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
+    m_points[i].y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
+  }
+}
+
+wxExpr *wxOpPolyDraw::WriteExpr(wxPseudoMetaFile *image)
+{
+  wxExpr *expr = new wxExpr(wxExprList);
+  expr->Append(new wxExpr((long)m_op));
+  expr->Append(new wxExpr((long)m_noPoints));
+
+//  char buf1[9];
+  char buf2[5];
+  char buf3[5];
+
+  oglBuffer[0] = 0;
+
+  /*
+   * Store each coordinate pair in a hex string to save space.
+   * E.g. "1B9080CD". 4 hex digits per coordinate pair.
+   *
+   */
+   
+  for (int i = 0; i < m_noPoints; i++)
+  {
+    long signedX = (long)(m_points[i].x*100.0);
+    long signedY = (long)(m_points[i].y*100.0);
+
+    // Scale to 0 -> 64K
+    long unSignedX = (long)(signedX + 32767.0);
+    long unSignedY = (long)(signedY + 32767.0);
+    
+//    IntToHex((unsigned int)signedX, buf2);
+//    IntToHex((unsigned int)signedY, buf3);
+    IntToHex((int)unSignedX, buf2);
+    IntToHex((int)unSignedY, buf3);
+
+    // Don't overrun the buffer
+    if ((i*8) < 3000)
+    {
+      strcat(oglBuffer, buf2);
+      strcat(oglBuffer, buf3);
+    }
+  }
+  expr->Append(new wxExpr(wxExprString, oglBuffer));
+  return expr;
+}
+
+void wxOpPolyDraw::ReadExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+  m_noPoints = (int)expr->Nth(1)->IntegerValue();
+
+  char buf1[5];
+  char buf2[5];
+
+  m_points = new wxRealPoint[m_noPoints];
+  int i = 0;
+  int bufPtr = 0;
+  wxString hexString = expr->Nth(2)->StringValue();
+  while (i < m_noPoints)
+  {
+    buf1[0] = hexString[(size_t)bufPtr];
+    buf1[1] = hexString[(size_t)(bufPtr + 1)];
+    buf1[2] = hexString[(size_t)(bufPtr + 2)];
+    buf1[3] = hexString[(size_t)(bufPtr + 3)];
+    buf1[4] = 0;
+    
+    buf2[0] = hexString[(size_t)(bufPtr + 4)];
+    buf2[1] = hexString[(size_t)(bufPtr + 5)];
+    buf2[2] = hexString[(size_t)(bufPtr + 6)];
+    buf2[3] = hexString[(size_t)(bufPtr + 7)];
+    buf2[4] = 0;
+
+    bufPtr += 8;
+
+//    int signedX = (signed int)HexToInt(buf1);
+//    int signedY = (signed int)HexToInt(buf2);
+    long unSignedX = HexToInt(buf1);
+    long unSignedY = HexToInt(buf2);
+    // Scale -32K -> +32K
+    long signedX = unSignedX - 32767;
+    long signedY = unSignedY - 32767;
+#ifdef __WXMSW__
+    int testX = (signed int)unSignedX;
+    int testY = (signed int)unSignedY;
+#endif
+
+    m_points[i].x = (double)(signedX / 100.0);
+    m_points[i].y = (double)(signedY / 100.0);
+
+    i ++;
+  }
+}
+
+// Draw an outline using the current operation.
+bool wxOpPolyDraw::OnDrawOutline(wxDC& dc, double x, double y, double w, double h, double oldW, double oldH)
+{
+    dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+    // Multiply all points by proportion of new size to old size
+    double x_proportion = (double)(fabs(w/oldW));
+    double y_proportion = (double)(fabs(h/oldH));
+
+    int n = m_noPoints;
+    wxPoint *intPoints = new wxPoint[n];
+    int i;
+    for (i = 0; i < n; i++)
+    {
+        intPoints[i].x = WXROUND (x_proportion * m_points[i].x);
+        intPoints[i].y = WXROUND (y_proportion * m_points[i].y);
+    }
+    dc.DrawPolygon(n, intPoints, (long) x, (long) y);
+    delete[] intPoints;
+    return TRUE;
+}
+
+// Assume (x1, y1) is centre of box (most generally, line end at box)
+bool wxOpPolyDraw::GetPerimeterPoint(double x1, double y1,
+                                     double x2, double y2,
+                                     double *x3, double *y3,
+                                     double xOffset, double yOffset,
+                                     int attachmentMode)
+{
+  int n = m_noPoints;
+
+  // 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 ((attachmentMode == ATTACHMENT_MODE_NONE) && (x1 == x2))
+  {
+    // Look for the point we'd be connecting to. This is
+    // a heuristic...
+    int i;
+    for (i = 0; i < n; i++)
+    {
+      wxRealPoint *point = & (m_points[i]);
+      if (point->x == 0.0)
+      {
+        if ((y2 > y1) && (point->y > 0.0))
+        {
+          *x3 = point->x + xOffset;
+          *y3 = point->y + yOffset;
+          return TRUE;
+        }
+        else if ((y2 < y1) && (point->y < 0.0))
+        {
+          *x3 = point->x + xOffset;
+          *y3 = point->y + yOffset;
+          return TRUE;
+        }
+      }
+    }
+  }
+  
+  double *xpoints = new double[n];
+  double *ypoints = new double[n];
+
+  int i = 0;
+  for (i = 0; i < n; i++)
+  {
+    wxRealPoint *point = & (m_points[i]);
+    xpoints[i] = point->x + xOffset;
+    ypoints[i] = point->y + yOffset;
+  }
+
+  oglFindEndForPolyline(n, xpoints, ypoints, 
+                        x1, y1, x2, y2, x3, y3);
+
+  delete[] xpoints;
+  delete[] ypoints;
+
+  return TRUE;
+}
+
+
+/*
+ * Utilities
+ *
+ */
+
+static char hexArray[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+  'C', 'D', 'E', 'F' };
+
+// Convert unsigned 16-bit integer to 4-character hex string
+static void IntToHex(unsigned int dec, char *buf)
+{
+  int digit1 = (int)(dec/4096);
+  int digit2 = (int)((dec - (digit1*4096))/256);
+  int digit3 = (int)((dec - (digit1*4096) - (digit2*256))/16);
+  int digit4 = dec - (digit1*4096 + digit2*256 + digit3*16);
+  
+  buf[0] = hexArray[digit1];
+  buf[1] = hexArray[digit2];
+  buf[2] = hexArray[digit3];
+  buf[3] = hexArray[digit4];
+  buf[4] = 0;
+}
+
+// One hex digit to decimal number
+static int HexToInt1(char hex)
+{
+  switch (hex)
+  {
+    case '0':
+      return 0;
+    case '1':
+      return 1;
+    case '2':
+      return 2;
+    case '3':
+      return 3;
+    case '4':
+      return 4;
+    case '5':
+      return 5;
+    case '6':
+      return 6;
+    case '7':
+      return 7;
+    case '8':
+      return 8;
+    case '9':
+      return 9;
+    case 'A':
+      return 10;
+    case 'B':
+      return 11;
+    case 'C':
+      return 12;
+    case 'D':
+      return 13;
+    case 'E':
+      return 14;
+    case 'F':
+      return 15;
+    default:
+      return 0;
+  }
+  return 0;
+}
+
+// 4-digit hex string to unsigned integer
+static unsigned long HexToInt(char *buf)
+{
+  long d1 = (long)(HexToInt1(buf[0])*4096.0) ;
+  long d2 = (long)(HexToInt1(buf[1])*256.0) ;
+  long d3 = (long)(HexToInt1(buf[2])*16.0) ;
+  long d4 = (long)(HexToInt1(buf[3])) ;
+  unsigned long n = (long)(d1 + d2 + d3 + d4) ;
+  return n;
+}
+
+/*
+ * wxPseudo meta-file
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxPseudoMetaFile, wxObject)
+
+wxPseudoMetaFile::wxPseudoMetaFile()
+{
+  m_currentRotation = 0;
+  m_rotateable = TRUE;
+  m_width = 0.0;
+  m_height = 0.0;
+  m_outlinePen = NULL;
+  m_fillBrush = NULL;
+  m_outlineOp = -1;
+}
+
+wxPseudoMetaFile::wxPseudoMetaFile(wxPseudoMetaFile& mf)
+{
+  mf.Copy(*this);
+}
+
+wxPseudoMetaFile::~wxPseudoMetaFile()
+{
+  Clear();
+}
+
+void wxPseudoMetaFile::Clear()
+{
+  wxNode *node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    delete op;
+    node = node->Next();
+  }
+  m_ops.Clear();
+  m_gdiObjects.Clear();
+  m_outlineColours.Clear();
+  m_fillColours.Clear();
+  m_outlineOp = -1;
+}
+
+void wxPseudoMetaFile::Draw(wxDC& dc, double xoffset, double yoffset)
+{
+  wxNode *node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    op->Do(dc, xoffset, yoffset);
+    node = node->Next();
+  }
+}
+
+void wxPseudoMetaFile::Scale(double sx, double sy)
+{
+  wxNode *node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    op->Scale(sx, sy);
+    node = node->Next();
+  }
+  m_width *= sx;
+  m_height *= sy;
+}
+
+void wxPseudoMetaFile::Translate(double x, double y)
+{
+  wxNode *node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    op->Translate(x, y);
+    node = node->Next();
+  }
+}
+
+void wxPseudoMetaFile::Rotate(double x, double y, double theta)
+{
+  double theta1 = theta-m_currentRotation;
+  if (theta1 == 0.0) return;
+  double cosTheta = (double)cos(theta1);
+  double sinTheta = (double)sin(theta1);
+
+  wxNode *node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    op->Rotate(x, y, theta, sinTheta, cosTheta);
+    node = node->Next();
+  }
+  m_currentRotation = theta;
+}
+
+#ifdef PROLOGIO
+void wxPseudoMetaFile::WriteAttributes(wxExpr *clause, int whichAngle)
+{
+  wxString widthStr;
+  widthStr.Printf("meta_width%d", whichAngle);
+
+  wxString heightStr;
+  heightStr.Printf("meta_height%d", whichAngle);
+
+  wxString outlineStr;
+  outlineStr.Printf("outline_op%d", whichAngle);
+
+  wxString rotateableStr;
+  rotateableStr.Printf("meta_rotateable%d", whichAngle);
+
+  // Write width and height
+  clause->AddAttributeValue(widthStr, m_width);
+  clause->AddAttributeValue(heightStr, m_height);
+  clause->AddAttributeValue(rotateableStr, (long)m_rotateable);
+  clause->AddAttributeValue(outlineStr, (long)m_outlineOp);
+
+  // Write GDI objects
+  char buf[50];
+  int i = 1;
+  wxNode *node = m_gdiObjects.First();
+  while (node)
+  {
+    sprintf(buf, "gdi%d_%d", whichAngle, i);
+    wxObject *obj = (wxObject *)node->Data();
+    wxExpr *expr = NULL;
+    if (obj)
+    {
+      if (obj->IsKindOf(CLASSINFO(wxPen)))
+      {
+        wxPen *thePen = (wxPen *)obj;
+        expr = new wxExpr(wxExprList);
+        expr->Append(new wxExpr((long)gyTYPE_PEN));
+        expr->Append(new wxExpr((long)thePen->GetWidth()));
+        expr->Append(new wxExpr((long)thePen->GetStyle()));
+        expr->Append(new wxExpr((long)thePen->GetColour().Red()));
+        expr->Append(new wxExpr((long)thePen->GetColour().Green()));
+        expr->Append(new wxExpr((long)thePen->GetColour().Blue()));
+      }
+      else if (obj->IsKindOf(CLASSINFO(wxBrush)))
+      {
+        wxBrush *theBrush = (wxBrush *)obj;
+        expr = new wxExpr(wxExprList);
+        expr->Append(new wxExpr((long)gyTYPE_BRUSH));
+        expr->Append(new wxExpr((long)theBrush->GetStyle()));
+        expr->Append(new wxExpr((long)theBrush->GetColour().Red()));
+        expr->Append(new wxExpr((long)theBrush->GetColour().Green()));
+        expr->Append(new wxExpr((long)theBrush->GetColour().Blue()));
+      }
+      else if (obj->IsKindOf(CLASSINFO(wxFont)))
+      {
+        wxFont *theFont = (wxFont *)obj;
+        expr = new wxExpr(wxExprList);
+        expr->Append(new wxExpr((long)gyTYPE_FONT));
+        expr->Append(new wxExpr((long)theFont->GetPointSize()));
+        expr->Append(new wxExpr((long)theFont->GetFamily()));
+        expr->Append(new wxExpr((long)theFont->GetStyle()));
+        expr->Append(new wxExpr((long)theFont->GetWeight()));
+        expr->Append(new wxExpr((long)theFont->GetUnderlined()));
+      }
+    }
+    else
+    {
+      // If no recognised GDI object, append a place holder anyway.
+      expr = new wxExpr(wxExprList);
+      expr->Append(new wxExpr((long)0));
+    }
+
+    if (expr)
+    {
+      clause->AddAttributeValue(buf, expr);
+      i ++;
+    }
+    node = node->Next();
+  }
+
+  // Write drawing operations
+  i = 1;
+  node = m_ops.First();
+  while (node)
+  {
+    sprintf(buf, "op%d_%d", whichAngle, i);
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    wxExpr *expr = op->WriteExpr(this);
+    if (expr)
+    {
+      clause->AddAttributeValue(buf, expr);
+      i ++;
+    }
+    node = node->Next();
+  }
+
+  // Write outline and fill GDI op lists (if any)
+  if (m_outlineColours.Number() > 0)
+  {
+    wxExpr *outlineExpr = new wxExpr(wxExprList);
+    node = m_outlineColours.First();
+    while (node)
+    {
+      outlineExpr->Append(new wxExpr((long)node->Data()));
+      node = node->Next();
+    }
+    wxString outlineObjectsStr;
+    outlineObjectsStr.Printf("outline_objects%d", whichAngle);
+
+    clause->AddAttributeValue(outlineObjectsStr, outlineExpr);
+  }
+  if (m_fillColours.Number() > 0)
+  {
+    wxExpr *fillExpr = new wxExpr(wxExprList);
+    node = m_fillColours.First();
+    while (node)
+    {
+      fillExpr->Append(new wxExpr((long)node->Data()));
+      node = node->Next();
+    }
+    wxString fillObjectsStr;
+    fillObjectsStr.Printf("fill_objects%d", whichAngle);
+
+    clause->AddAttributeValue(fillObjectsStr, fillExpr);
+  }
+    
+}
+
+void wxPseudoMetaFile::ReadAttributes(wxExpr *clause, int whichAngle)
+{
+  wxString widthStr;
+  widthStr.Printf("meta_width%d", whichAngle);
+
+  wxString heightStr;
+  heightStr.Printf("meta_height%d", whichAngle);
+
+  wxString outlineStr;
+  outlineStr.Printf("outline_op%d", whichAngle);
+
+  wxString rotateableStr;
+  rotateableStr.Printf("meta_rotateable%d", whichAngle);
+
+  clause->GetAttributeValue(widthStr, m_width);
+  clause->GetAttributeValue(heightStr, m_height);
+  clause->GetAttributeValue(outlineStr, m_outlineOp);
+
+  int iVal = (int) m_rotateable;
+  clause->GetAttributeValue(rotateableStr, iVal);
+  m_rotateable = (iVal != 0);
+
+  // Read GDI objects
+  char buf[50];
+  int i = 1;
+  bool keepGoing = TRUE;
+  while (keepGoing)
+  {
+    sprintf(buf, "gdi%d_%d", whichAngle, i);
+    wxExpr *expr = NULL;
+    clause->GetAttributeValue(buf, &expr);
+    if (!expr)
+    {
+      keepGoing = FALSE;
+    }
+    else
+    {
+      wxExpr *idExpr = expr->Nth(0);
+      switch (idExpr->IntegerValue())
+      {
+        case gyTYPE_PEN:
+        {
+          int penWidth = (int)expr->Nth(1)->IntegerValue();
+          int penStyle = (int)expr->Nth(2)->IntegerValue();
+          int penRed = (int)expr->Nth(3)->IntegerValue();
+          int penGreen = (int)expr->Nth(4)->IntegerValue();
+          int penBlue = (int)expr->Nth(5)->IntegerValue();
+          wxColour col(penRed, penGreen, penBlue);
+          wxPen *p = wxThePenList->FindOrCreatePen(col, penWidth, penStyle);
+          if (!p)
+            p = wxBLACK_PEN;
+          m_gdiObjects.Append(p);
+          break;
+        }
+        case gyTYPE_BRUSH:
+        {
+          int brushStyle = (int)expr->Nth(1)->IntegerValue();
+          int brushRed = (int)expr->Nth(2)->IntegerValue();
+          int brushGreen = (int)expr->Nth(3)->IntegerValue();
+          int brushBlue = (int)expr->Nth(4)->IntegerValue();
+          wxColour col(brushRed, brushGreen, brushBlue);
+          wxBrush *b = wxTheBrushList->FindOrCreateBrush(col, brushStyle);
+          if (!b)
+            b = wxWHITE_BRUSH;
+          m_gdiObjects.Append(b);
+          break;
+        }
+        case gyTYPE_FONT:
+        {
+          int fontPointSize = (int)expr->Nth(1)->IntegerValue();
+          int fontFamily = (int)expr->Nth(2)->IntegerValue();
+          int fontStyle = (int)expr->Nth(3)->IntegerValue();
+          int fontWeight = (int)expr->Nth(4)->IntegerValue();
+          int fontUnderlined = (int)expr->Nth(5)->IntegerValue();
+          m_gdiObjects.Append(wxTheFontList->FindOrCreateFont(fontPointSize,
+                           fontFamily, fontStyle, fontWeight, (fontUnderlined != 0)));
+          break;
+        }
+        default:
+        {
+          // Place holder
+          m_gdiObjects.Append(NULL);
+          break;
+        }
+      }
+      i ++;
+    }
+  }
+
+  // Now read in the operations
+  keepGoing = TRUE;
+  i = 1;
+  while (keepGoing)
+  {
+    sprintf(buf, "op%d_%d", whichAngle, i);
+    wxExpr *expr = NULL;
+    clause->GetAttributeValue(buf, &expr);
+    if (!expr)
+    {
+      keepGoing = FALSE;
+    }
+    else
+    {
+      wxExpr *idExpr = expr->Nth(0);
+      int opId = (int)idExpr->IntegerValue();
+      switch (opId)
+      {
+        case DRAWOP_SET_PEN:
+        case DRAWOP_SET_BRUSH:
+        case DRAWOP_SET_FONT:
+        case DRAWOP_SET_TEXT_COLOUR:
+        case DRAWOP_SET_BK_COLOUR:
+        case DRAWOP_SET_BK_MODE:
+        {
+          wxOpSetGDI *theOp = new wxOpSetGDI(opId, this, 0);
+          theOp->ReadExpr(this, expr);
+          m_ops.Append(theOp);
+          break;
+        }
+        
+        case DRAWOP_SET_CLIPPING_RECT:
+        case DRAWOP_DESTROY_CLIPPING_RECT:
+        {
+          wxOpSetClipping *theOp = new wxOpSetClipping(opId, 0.0, 0.0, 0.0, 0.0);
+          theOp->ReadExpr(this, expr);
+          m_ops.Append(theOp);
+          break;
+        }
+
+        case DRAWOP_DRAW_LINE:
+        case DRAWOP_DRAW_RECT:
+        case DRAWOP_DRAW_ROUNDED_RECT:
+        case DRAWOP_DRAW_ELLIPSE:
+        case DRAWOP_DRAW_POINT:
+        case DRAWOP_DRAW_ARC:
+        case DRAWOP_DRAW_TEXT:
+        {
+          wxOpDraw *theOp = new wxOpDraw(opId, 0.0, 0.0, 0.0, 0.0);
+          theOp->ReadExpr(this, expr);
+          m_ops.Append(theOp);
+          break;
+        }
+        case DRAWOP_DRAW_SPLINE:
+        case DRAWOP_DRAW_POLYLINE:
+        case DRAWOP_DRAW_POLYGON:
+        {
+          wxOpPolyDraw *theOp = new wxOpPolyDraw(opId, 0, NULL);
+          theOp->ReadExpr(this, expr);
+          m_ops.Append(theOp);
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    i ++;
+  }
+
+  wxString outlineObjectsStr;
+  outlineObjectsStr.Printf("outline_objects%d", whichAngle);
+
+  // Now read in the list of outline and fill operations, if any
+  wxExpr *expr1 = clause->AttributeValue(outlineObjectsStr);
+  if (expr1)
+  {
+    wxExpr *eachExpr = expr1->GetFirst();
+    while (eachExpr)
+    {
+      m_outlineColours.Append((wxObject *)eachExpr->IntegerValue());
+      eachExpr = eachExpr->GetNext();
+    }
+  }
+
+  wxString fillObjectsStr;
+  fillObjectsStr.Printf("fill_objects%d", whichAngle);
+
+  expr1 = clause->AttributeValue(fillObjectsStr);
+  if (expr1)
+  {
+    wxExpr *eachExpr = expr1->GetFirst();
+    while (eachExpr)
+    {
+      m_fillColours.Append((wxObject *)eachExpr->IntegerValue());
+      eachExpr = eachExpr->GetNext();
+    }
+  }
+}
+#endif
+
+// Does the copying for this object
+void wxPseudoMetaFile::Copy(wxPseudoMetaFile& copy)
+{
+  copy.Clear();
+
+  copy.m_currentRotation = m_currentRotation;
+  copy.m_width = m_width;
+  copy.m_height = m_height;
+  copy.m_rotateable = m_rotateable;
+  copy.m_fillBrush = m_fillBrush;
+  copy.m_outlinePen = m_outlinePen;
+  copy.m_outlineOp = m_outlineOp;
+
+  // Copy the GDI objects
+  wxNode *node = m_gdiObjects.First();
+  while (node)
+  {
+    wxObject *obj = (wxObject *)node->Data();
+    copy.m_gdiObjects.Append(obj);
+    node = node->Next();
+  }
+  
+  // Copy the operations
+  node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    copy.m_ops.Append(op->Copy(&copy));
+    node = node->Next();
+  }
+
+  // Copy the outline/fill operations
+  node = m_outlineColours.First();
+  while (node)
+  {
+    copy.m_outlineColours.Append((wxObject *)node->Data());
+    node = node->Next();
+  }
+  node = m_fillColours.First();
+  while (node)
+  {
+    copy.m_fillColours.Append((wxObject *)node->Data());
+    node = node->Next();
+  }
+}
+
+/*
+ * Pass size of existing image; scale height to
+ * fit width and return new width and height.
+ *
+ */
+bool wxPseudoMetaFile::LoadFromMetaFile(char *filename, double *rwidth, double *rheight)
+{
+  if (!FileExists(filename))
+    return NULL;
+    
+  wxXMetaFile *metaFile = new wxXMetaFile;
+  
+  if (!metaFile->ReadFile(filename))
+  {
+    delete metaFile;
+    return FALSE;
+  }
+
+  double lastX = 0.0;
+  double lastY = 0.0;
+
+  // Convert from metafile records to wxDrawnShape records
+  wxNode *node = metaFile->metaRecords.First();
+  while (node)
+  {
+    wxMetaRecord *record = (wxMetaRecord *)node->Data();
+    switch (record->metaFunction)
+    {
+      case META_SETBKCOLOR:
+      {
+        wxOpSetGDI *op = new wxOpSetGDI(DRAWOP_SET_BK_COLOUR, this, 0);
+        op->m_r = (unsigned char)record->param1;
+        op->m_g = (unsigned char)record->param2;
+        op->m_b = (unsigned char)record->param3;
+        m_ops.Append(op);
+        break;
+      }
+      case META_SETBKMODE:
+      {
+        wxOpSetGDI *op = new wxOpSetGDI(DRAWOP_SET_BK_MODE, this, 0, (int)record->param1);
+        m_ops.Append(op);
+        break;
+      }
+      case META_SETMAPMODE:
+      {
+        break;
+      }
+//      case META_SETROP2:
+//      case META_SETRELABS:
+//      case META_SETPOLYFILLMODE:
+//      case META_SETSTRETCHBLTMODE:
+//      case META_SETTEXTCHAREXTRA:
+      case META_SETTEXTCOLOR:
+      {
+        wxOpSetGDI *op = new wxOpSetGDI(DRAWOP_SET_TEXT_COLOUR, this, 0);
+        op->m_r = (unsigned char)record->param1;
+        op->m_g = (unsigned char)record->param2;
+        op->m_b = (unsigned char)record->param3;
+        m_ops.Append(op);
+        break;
+      }
+//      case META_SETTEXTJUSTIFICATION:
+//      case META_SETWINDOWORG:
+//      case META_SETWINDOWEXT:
+//      case META_SETVIEWPORTORG:
+//      case META_SETVIEWPORTEXT:
+//      case META_OFFSETWINDOWORG:
+//      case META_SCALEWINDOWEXT:
+//      case META_OFFSETVIEWPORTORG:
+//      case META_SCALEVIEWPORTEXT:
+      case META_LINETO:
+      {
+        wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_LINE, (double)lastX, (double)lastY,
+                               (double)record->param1, (double)record->param2);
+        m_ops.Append(op);
+        break;
+      }
+      case META_MOVETO:
+      {
+        lastX = (double)record->param1;
+        lastY = (double)record->param2;
+        break;
+      }
+      case META_EXCLUDECLIPRECT:
+      {
+/*
+        wxMetaRecord *rec = new wxMetaRecord(META_EXCLUDECLIPRECT);
+        rec->param4 = getshort(handle); // m_y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+*/
+        break;
+      }
+      case META_INTERSECTCLIPRECT:
+      {
+/*
+        rec->param4 = getshort(handle); // m_y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+*/
+        break;
+      }
+//      case META_ARC: // DO!!!
+      case META_ELLIPSE:
+      {
+        wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_ELLIPSE,
+                               (double)record->param1, (double)record->param2,
+                               (double)(record->param3 - record->param1),
+                               (double)(record->param4 - record->param2));
+        m_ops.Append(op);
+        break;
+      }
+//      case META_FLOODFILL:
+//      case META_PIE: // DO!!!
+      case META_RECTANGLE:
+      {
+        wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_RECT,
+                               (double)record->param1, (double)record->param2,
+                               (double)(record->param3 - record->param1),
+                               (double)(record->param4 - record->param2));
+        m_ops.Append(op);
+        break;
+      }
+      case META_ROUNDRECT:
+      {
+        wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_ROUNDED_RECT,
+              (double)record->param1, (double)record->param2,
+              (double)(record->param3 - record->param1),
+              (double)(record->param4 - record->param2), (double)record->param5);
+        m_ops.Append(op);
+        break;
+      }
+//      case META_PATBLT:
+//      case META_SAVEDC:
+      case META_SETPIXEL:
+      {
+        wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_POINT,
+              (double)record->param1, (double)record->param2,
+              0.0, 0.0);
+
+//        SHOULD SET THE COLOUR - SET PEN?
+//        rec->param3 = getint(handle);   // COLORREF
+        m_ops.Append(op);
+        break;
+      }
+//      case META_OFFSETCLIPRGN:
+      case META_TEXTOUT:
+      {
+        wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_TEXT,
+              (double)record->param1, (double)record->param2,
+              0.0, 0.0, 0.0, record->stringParam);
+        m_ops.Append(op);
+        break;
+      }
+//      case META_BITBLT:
+//      case META_STRETCHBLT:
+      case META_POLYGON:
+      {
+        int n = (int)record->param1;
+        wxRealPoint *newPoints = new wxRealPoint[n];
+        for (int i = 0; i < n; i++)
+        {
+          newPoints[i].x = record->points[i].x;
+          newPoints[i].y = record->points[i].y;
+        }
+        
+        wxOpPolyDraw *op = new wxOpPolyDraw(DRAWOP_DRAW_POLYGON, n, newPoints);
+        m_ops.Append(op);
+        break;
+      }
+      case META_POLYLINE:
+      {
+        int n = (int)record->param1;
+        wxRealPoint *newPoints = new wxRealPoint[n];
+        for (int i = 0; i < n; i++)
+        {
+          newPoints[i].x = record->points[i].x;
+          newPoints[i].y = record->points[i].y;
+        }
+        
+        wxOpPolyDraw *op = new wxOpPolyDraw(DRAWOP_DRAW_POLYLINE, n, newPoints);
+        m_ops.Append(op);
+        break;
+      }
+//      case META_ESCAPE:
+//      case META_RESTOREDC:
+//      case META_FILLREGION:
+//      case META_FRAMEREGION:
+//      case META_INVERTREGION:
+//      case META_PAINTREGION:
+//      case META_SELECTCLIPREGION: // DO THIS!
+      case META_SELECTOBJECT:
+      {
+        // The pen, brush etc. has already been created when the metafile
+        // was read in, so we don't create it - we set it.
+        wxNode *recNode = metaFile->gdiObjects.Nth((int)record->param2);
+        if (recNode)
+        {
+          wxMetaRecord *gdiRec = (wxMetaRecord *)recNode->Data();
+          if (gdiRec && (gdiRec->param1 != 0))
+          {
+            wxObject *obj = (wxObject *)gdiRec->param1;
+            if (obj->IsKindOf(CLASSINFO(wxPen)))
+            {
+              wxOpSetGDI *op = new wxOpSetGDI(DRAWOP_SET_PEN, this, (int)record->param2);
+              m_ops.Append(op);
+            }
+            else if (obj->IsKindOf(CLASSINFO(wxBrush)))
+            {
+              wxOpSetGDI *op = new wxOpSetGDI(DRAWOP_SET_BRUSH, this, (int)record->param2);
+              m_ops.Append(op);
+            }
+            else if (obj->IsKindOf(CLASSINFO(wxFont)))
+            {
+              wxOpSetGDI *op = new wxOpSetGDI(DRAWOP_SET_FONT, this, (int)record->param2);
+              m_ops.Append(op);
+            }
+          }
+        }
+        break;
+      }
+//      case META_SETTEXTALIGN:
+//      case META_DRAWTEXT:
+//      case META_CHORD:
+//      case META_SETMAPPERFLAGS:
+//      case META_EXTTEXTOUT:
+//      case META_SETDIBTODEV:
+//      case META_SELECTPALETTE:
+//      case META_REALIZEPALETTE:
+//      case META_ANIMATEPALETTE:
+//      case META_SETPALENTRIES:
+//      case META_POLYPOLYGON:
+//      case META_RESIZEPALETTE:
+//      case META_DIBBITBLT:
+//      case META_DIBSTRETCHBLT:
+      case META_DIBCREATEPATTERNBRUSH:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+//      case META_STRETCHDIB:
+//      case META_EXTFLOODFILL:
+//      case META_RESETDC:
+//      case META_STARTDOC:
+//      case META_STARTPAGE:
+//      case META_ENDPAGE:
+//      case META_ABORTDOC:
+//      case META_ENDDOC:
+//      case META_DELETEOBJECT: // DO!!
+      case META_CREATEPALETTE:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+      case META_CREATEBRUSH:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+      case META_CREATEPATTERNBRUSH:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+      case META_CREATEPENINDIRECT:
+      {
+        // The pen is created when the metafile is read in.
+        // We keep track of all the GDI objects needed for this
+        // image so when reading the wxDrawnShape from file,
+        // we can read in all the GDI objects, then refer
+        // to them by an index starting from zero thereafter.
+        m_gdiObjects.Append((wxObject *)record->param1);
+        break;
+      }
+      case META_CREATEFONTINDIRECT:
+      {
+        m_gdiObjects.Append((wxObject *)record->param1);
+        break;
+      }
+      case META_CREATEBRUSHINDIRECT:
+      {
+        // Don't have to do anything here: the pen is created
+        // when the metafile is read in.
+        m_gdiObjects.Append((wxObject *)record->param1);
+        break;
+      }
+      case META_CREATEBITMAPINDIRECT:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+      case META_CREATEBITMAP:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+      case META_CREATEREGION:
+      {
+        // Place holder
+        m_gdiObjects.Append(NULL);
+        break;
+      }
+      default:
+      {
+        break;
+      }
+    }
+    node = node->Next();
+  }
+  double actualWidth = (double)fabs(metaFile->right - metaFile->left);
+  double actualHeight = (double)fabs(metaFile->bottom - metaFile->top);
+
+  double initialScaleX = 1.0;
+  double initialScaleY = 1.0;
+
+  double xoffset, yoffset;
+
+  // Translate so origin is at centre of rectangle
+  if (metaFile->bottom > metaFile->top)
+    yoffset = - (double)((metaFile->bottom - metaFile->top)/2.0);
+  else
+    yoffset = - (double)((metaFile->top - metaFile->bottom)/2.0);
+
+  if (metaFile->right > metaFile->left)
+    xoffset = - (double)((metaFile->right - metaFile->left)/2.0);
+  else
+    xoffset = - (double)((metaFile->left - metaFile->right)/2.0);
+
+  Translate(xoffset, yoffset);
+
+  // Scale to a reasonable size (take the width of this wxDrawnShape
+  // as a guide)
+  if (actualWidth != 0.0)
+  {
+    initialScaleX = (double)((*rwidth) / actualWidth);
+    initialScaleY = initialScaleX;
+    (*rheight) = initialScaleY*actualHeight;
+  }
+  Scale(initialScaleX, initialScaleY);
+
+  m_width = (actualWidth*initialScaleX);
+  m_height = *rheight;
+
+  delete metaFile;
+  return TRUE;
+}
+
+// Scale to fit size
+void wxPseudoMetaFile::ScaleTo(double w, double h)
+{
+  double scaleX = (double)(w/m_width);
+  double scaleY = (double)(h/m_height);
+
+  // Do the scaling
+  Scale(scaleX, scaleY);
+}
+
+void wxPseudoMetaFile::GetBounds(double *boundMinX, double *boundMinY, double *boundMaxX, double *boundMaxY)
+{
+  double maxX = (double) -99999.9;
+  double maxY = (double) -99999.9;
+  double minX = (double) 99999.9;
+  double minY = (double) 99999.9;
+
+  wxNode *node = m_ops.First();
+  while (node)
+  {
+    wxDrawOp *op = (wxDrawOp *)node->Data();
+    switch (op->GetOp())
+    {
+      case DRAWOP_DRAW_LINE:
+      case DRAWOP_DRAW_RECT:
+      case DRAWOP_DRAW_ROUNDED_RECT:
+      case DRAWOP_DRAW_ELLIPSE:
+      case DRAWOP_DRAW_POINT:
+      case DRAWOP_DRAW_TEXT:
+      {
+        wxOpDraw *opDraw = (wxOpDraw *)op;
+        if (opDraw->m_x1 < minX) minX = opDraw->m_x1;
+        if (opDraw->m_x1 > maxX) maxX = opDraw->m_x1;
+        if (opDraw->m_y1 < minY) minY = opDraw->m_y1;
+        if (opDraw->m_y1 > maxY) maxY = opDraw->m_y1;
+        if (op->GetOp() == DRAWOP_DRAW_LINE)
+        {
+          if (opDraw->m_x2 < minX) minX = opDraw->m_x2;
+          if (opDraw->m_x2 > maxX) maxX = opDraw->m_x2;
+          if (opDraw->m_y2 < minY) minY = opDraw->m_y2;
+          if (opDraw->m_y2 > maxY) maxY = opDraw->m_y2;
+        }
+        else if (op->GetOp() == DRAWOP_DRAW_RECT ||
+                 op->GetOp() == DRAWOP_DRAW_ROUNDED_RECT ||
+                 op->GetOp() == DRAWOP_DRAW_ELLIPSE)
+        {
+          if ((opDraw->m_x1 + opDraw->m_x2) < minX) minX = (opDraw->m_x1 + opDraw->m_x2);
+          if ((opDraw->m_x1 + opDraw->m_x2) > maxX) maxX = (opDraw->m_x1 + opDraw->m_x2);
+          if ((opDraw->m_y1 + opDraw->m_y2) < minY) minY = (opDraw->m_y1 + opDraw->m_y2);
+          if ((opDraw->m_y1 + opDraw->m_y2) > maxY) maxY = (opDraw->m_y1 + opDraw->m_y2);
+        }
+        break;
+      }
+      case DRAWOP_DRAW_ARC:
+      {
+        // TODO: don't yet know how to calculate the bounding box
+        // for an arc. So pretend it's a line; to get a correct
+        // bounding box, draw a blank rectangle first, of the correct
+        // size.
+        wxOpDraw *opDraw = (wxOpDraw *)op;
+        if (opDraw->m_x1 < minX) minX = opDraw->m_x1;
+        if (opDraw->m_x1 > maxX) maxX = opDraw->m_x1;
+        if (opDraw->m_y1 < minY) minY = opDraw->m_y1;
+        if (opDraw->m_y1 > maxY) maxY = opDraw->m_y1;
+        if (opDraw->m_x2 < minX) minX = opDraw->m_x2;
+        if (opDraw->m_x2 > maxX) maxX = opDraw->m_x2;
+        if (opDraw->m_y2 < minY) minY = opDraw->m_y2;
+        if (opDraw->m_y2 > maxY) maxY = opDraw->m_y2;
+        break;
+      }
+      case DRAWOP_DRAW_POLYLINE:
+      case DRAWOP_DRAW_POLYGON:
+      case DRAWOP_DRAW_SPLINE:
+      {
+        wxOpPolyDraw *poly = (wxOpPolyDraw *)op;
+        for (int i = 0; i < poly->m_noPoints; i++)
+        {
+          if (poly->m_points[i].x < minX) minX = poly->m_points[i].x;
+          if (poly->m_points[i].x > maxX) maxX = poly->m_points[i].x;
+          if (poly->m_points[i].y < minY) minY = poly->m_points[i].y;
+          if (poly->m_points[i].y > maxY) maxY = poly->m_points[i].y;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+    node = node->Next();
+  }
+
+  *boundMinX = minX;
+  *boundMinY = minY;
+  *boundMaxX = maxX;
+  *boundMaxY = maxY;
+/*
+  *w = (double)fabs(maxX - minX);
+  *h = (double)fabs(maxY - minY);
+*/
+}
+
+// Calculate size from current operations
+void wxPseudoMetaFile::CalculateSize(wxDrawnShape* shape)
+{
+  double boundMinX, boundMinY, boundMaxX, boundMaxY;
+
+  GetBounds(& boundMinX, & boundMinY, & boundMaxX, & boundMaxY);
+
+  SetSize(boundMaxX - boundMinX, boundMaxY - boundMinY);
+
+  if (shape)
+  {
+    shape->SetWidth(m_width);
+    shape->SetHeight(m_height);
+  }
+}
+
+// Set of functions for drawing into a pseudo metafile.
+// They use integers, but doubles are used internally for accuracy
+// when scaling.
+
+void wxPseudoMetaFile::DrawLine(const wxPoint& pt1, const wxPoint& pt2)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_LINE,
+          (double) pt1.x, (double) pt1.y, (double) pt2.x, (double) pt2.y);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawRectangle(const wxRect& rect)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_RECT,
+          (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawRoundedRectangle(const wxRect& rect, double radius)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_ROUNDED_RECT,
+          (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
+
+    theOp->m_radius = radius;
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawEllipse(const wxRect& rect)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_ELLIPSE,
+          (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawArc(const wxPoint& centrePt, const wxPoint& startPt, const wxPoint& endPt)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_ARC,
+          (double) centrePt.x, (double) centrePt.y, (double) startPt.x, (double) startPt.y);
+
+    theOp->m_x3 = (double) endPt.x;
+    theOp->m_y3 = (double) endPt.y;
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawEllipticArc(const wxRect& rect, double startAngle, double endAngle)
+{
+    const double pi = 3.1415926535897932384626433832795 ;
+
+    double startAngleRadians = startAngle* (pi*2.0/360.0);
+    double endAngleRadians = endAngle* (pi*2.0/360.0);
+
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_ELLIPTIC_ARC,
+          (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
+
+    theOp->m_x3 = startAngleRadians;
+    theOp->m_y3 = endAngleRadians;
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawPoint(const wxPoint& pt)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_POINT,
+          (double) pt.x, (double) pt.y, 0.0, 0.0);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawText(const wxString& text, const wxPoint& pt)
+{
+    wxOpDraw *theOp = new wxOpDraw(DRAWOP_DRAW_TEXT,
+          (double) pt.x, (double) pt.y, 0.0, 0.0);
+
+    theOp->m_textString = copystring(text);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawLines(int n, wxPoint pts[])
+{
+    wxRealPoint* realPoints = new wxRealPoint[n];
+    int i;
+    for (i = 0; i < n; i++)
+    {
+        realPoints[i].x = pts[i].x;
+        realPoints[i].y = pts[i].y;
+    }
+    wxOpPolyDraw* theOp = new wxOpPolyDraw(DRAWOP_DRAW_POLYLINE, n, realPoints);
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::DrawPolygon(int n, wxPoint pts[], int flags)
+{
+    wxRealPoint* realPoints = new wxRealPoint[n];
+    int i;
+    for (i = 0; i < n; i++)
+    {
+        realPoints[i].x = pts[i].x;
+        realPoints[i].y = pts[i].y;
+    }
+    wxOpPolyDraw* theOp = new wxOpPolyDraw(DRAWOP_DRAW_POLYGON, n, realPoints);
+    m_ops.Append(theOp);
+
+    if (flags & oglMETAFLAGS_OUTLINE)
+        m_outlineOp = (m_ops.Number() - 1);
+}
+
+void wxPseudoMetaFile::DrawSpline(int n, wxPoint pts[])
+{
+    wxRealPoint* realPoints = new wxRealPoint[n];
+    int i;
+    for (i = 0; i < n; i++)
+    {
+        realPoints[i].x = pts[i].x;
+        realPoints[i].y = pts[i].y;
+    }
+    wxOpPolyDraw* theOp = new wxOpPolyDraw(DRAWOP_DRAW_SPLINE, n, realPoints);
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::SetClippingRect(const wxRect& rect)
+{
+    wxOpSetClipping* theOp = new wxOpSetClipping(DRAWOP_SET_CLIPPING_RECT,
+        (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
+}
+
+void wxPseudoMetaFile::DestroyClippingRect()
+{
+    wxOpSetClipping* theOp = new wxOpSetClipping(DRAWOP_DESTROY_CLIPPING_RECT,
+        0.0, 0.0, 0.0, 0.0);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::SetPen(wxPen* pen, bool isOutline)
+{
+    m_gdiObjects.Append(pen);
+    int n = m_gdiObjects.Number();
+
+    wxOpSetGDI* theOp = new wxOpSetGDI(DRAWOP_SET_PEN, this, n - 1);
+
+    m_ops.Append(theOp);
+
+    if (isOutline)
+    {
+        m_outlineColours.Append((wxObject*) (n - 1));
+    }
+}
+
+void wxPseudoMetaFile::SetBrush(wxBrush* brush, bool isFill)
+{
+    m_gdiObjects.Append(brush);
+    int n = m_gdiObjects.Number();
+
+    wxOpSetGDI* theOp = new wxOpSetGDI(DRAWOP_SET_BRUSH, this, n - 1);
+
+    m_ops.Append(theOp);
+
+    if (isFill)
+    {
+        m_fillColours.Append((wxObject*) (n - 1));
+    }
+}
+
+void wxPseudoMetaFile::SetFont(wxFont* font)
+{
+    m_gdiObjects.Append(font);
+    int n = m_gdiObjects.Number();
+
+    wxOpSetGDI* theOp = new wxOpSetGDI(DRAWOP_SET_FONT, this, n - 1);
+
+    m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::SetTextColour(const wxColour& colour)
+{
+   wxOpSetGDI* theOp = new wxOpSetGDI(DRAWOP_SET_TEXT_COLOUR, this, 0);
+   theOp->m_r = colour.Red();
+   theOp->m_g = colour.Green();
+   theOp->m_b = colour.Blue();
+
+   m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::SetBackgroundColour(const wxColour& colour)
+{
+   wxOpSetGDI* theOp = new wxOpSetGDI(DRAWOP_SET_BK_COLOUR, this, 0);
+   theOp->m_r = colour.Red();
+   theOp->m_g = colour.Green();
+   theOp->m_b = colour.Blue();
+
+   m_ops.Append(theOp);
+}
+
+void wxPseudoMetaFile::SetBackgroundMode(int mode)
+{
+   wxOpSetGDI* theOp = new wxOpSetGDI(DRAWOP_SET_BK_MODE, this, 0, mode);
+
+   m_ops.Append(theOp);
+}
+
diff --git a/src/ogl/lines.cpp b/src/ogl/lines.cpp
new file mode 100644 (file)
index 0000000..069a4ed
--- /dev/null
@@ -0,0 +1,2515 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        lines.cpp
+// Purpose:     wxLineShape
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "lines.h"
+#pragma implementation "linesp.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <ctype.h>
+#include <math.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/lines.h>
+#include <wx/ogl/linesp.h>
+#include <wx/ogl/drawn.h>
+#include <wx/ogl/misc.h>
+#include <wx/ogl/canvas.h>
+
+// Line shape
+IMPLEMENT_DYNAMIC_CLASS(wxLineShape, wxShape)
+
+wxLineShape::wxLineShape()
+{
+  m_sensitivity = OP_CLICK_LEFT | OP_CLICK_RIGHT;
+  m_draggable = FALSE;
+  m_attachmentTo = 0;
+  m_attachmentFrom = 0;
+/*
+  m_actualTextWidth = 0.0;
+  m_actualTextHeight = 0.0;
+*/
+  m_from = NULL;
+  m_to = NULL;
+  m_erasing = FALSE;
+  m_arrowSpacing = 5.0; // For the moment, don't bother saving this to file.
+  m_ignoreArrowOffsets = FALSE;
+  m_isSpline = FALSE;
+  m_maintainStraightLines = FALSE;
+  m_alignmentStart = 0;
+  m_alignmentEnd = 0;
+
+  m_lineControlPoints = NULL;
+
+  // Clear any existing regions (created in an earlier constructor)
+  // and make the three line regions.
+  ClearRegions();
+  wxShapeRegion *newRegion = new wxShapeRegion;
+  newRegion->SetName("Middle");
+  newRegion->SetSize(150, 50);
+  m_regions.Append((wxObject *)newRegion);
+
+  newRegion = new wxShapeRegion;
+  newRegion->SetName("Start");
+  newRegion->SetSize(150, 50);
+  m_regions.Append((wxObject *)newRegion);
+
+  newRegion = new wxShapeRegion;
+  newRegion->SetName("End");
+  newRegion->SetSize(150, 50);
+  m_regions.Append((wxObject *)newRegion);
+
+  for (int i = 0; i < 3; i++)
+    m_labelObjects[i] = NULL;
+}
+
+wxLineShape::~wxLineShape()
+{
+  if (m_lineControlPoints)
+  {
+    ClearPointList(*m_lineControlPoints);
+    delete m_lineControlPoints;
+  }
+  for (int i = 0; i < 3; i++)
+  {
+    if (m_labelObjects[i])
+    {
+      m_labelObjects[i]->Select(FALSE);
+      m_labelObjects[i]->RemoveFromCanvas(m_canvas);
+      delete m_labelObjects[i];
+      m_labelObjects[i] = NULL;
+    }
+  }
+  ClearArrowsAtPosition(-1);
+}
+
+void wxLineShape::MakeLineControlPoints(int n)
+{
+  if (m_lineControlPoints)
+  {
+    ClearPointList(*m_lineControlPoints);
+    delete m_lineControlPoints;
+  }
+  m_lineControlPoints = new wxList;
+
+  int i = 0;
+  for (i = 0; i < n; i++)
+  {
+    wxRealPoint *point = new wxRealPoint(-999, -999);
+    m_lineControlPoints->Append((wxObject*) point);
+  }
+}
+
+wxNode *wxLineShape::InsertLineControlPoint(wxDC* dc)
+{
+    if (dc)
+        Erase(*dc);
+
+  wxNode *last = m_lineControlPoints->Last();
+  wxNode *second_last = last->Previous();
+  wxRealPoint *last_point = (wxRealPoint *)last->Data();
+  wxRealPoint *second_last_point = (wxRealPoint *)second_last->Data();
+
+  // Choose a point half way between the last and penultimate points
+  double line_x = ((last_point->x + second_last_point->x)/2);
+  double line_y = ((last_point->y + second_last_point->y)/2);
+
+  wxRealPoint *point = new wxRealPoint(line_x, line_y);
+  wxNode *node = m_lineControlPoints->Insert(last, (wxObject*) point);
+  return node;
+}
+
+bool wxLineShape::DeleteLineControlPoint()
+{
+  if (m_lineControlPoints->Number() < 3)
+    return FALSE;
+
+  wxNode *last = m_lineControlPoints->Last();
+  wxNode *second_last = last->Previous();
+
+  wxRealPoint *second_last_point = (wxRealPoint *)second_last->Data();
+  delete second_last_point;
+  delete second_last;
+
+  return TRUE;
+}
+
+void wxLineShape::Initialise()
+{
+  if (m_lineControlPoints)
+  {
+    // Just move the first and last control points
+    wxNode *first = m_lineControlPoints->First();
+    wxRealPoint *first_point = (wxRealPoint *)first->Data();
+
+    wxNode *last = m_lineControlPoints->Last();
+    wxRealPoint *last_point = (wxRealPoint *)last->Data();
+
+    // If any of the line points are at -999, we must
+    // initialize them by placing them half way between the first
+    // and the last.
+    wxNode *node = first->Next();
+    while (node)
+    {
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+      if (point->x == -999)
+      {
+        double x1, y1, x2, y2;
+        if (first_point->x < last_point->x)
+          { x1 = first_point->x; x2 = last_point->x; }
+        else
+          { x2 = first_point->x; x1 = last_point->x; }
+
+        if (first_point->y < last_point->y)
+          { y1 = first_point->y; y2 = last_point->y; }
+        else
+          { y2 = first_point->y; y1 = last_point->y; }
+
+        point->x = ((x2 - x1)/2 + x1);
+        point->y = ((y2 - y1)/2 + y1);
+      }
+      node = node->Next();
+    }
+  }
+}
+
+// Format a text string according to the region size, adding
+// strings with positions to region text list
+void wxLineShape::FormatText(wxDC& dc, const wxString& s, int i)
+{
+  double w, h;
+  ClearText(i);
+
+  if (m_regions.Number() < 1)
+    return;
+  wxNode *node = m_regions.Nth(i);
+  if (!node)
+    return;
+
+  wxShapeRegion *region = (wxShapeRegion *)node->Data();
+  region->SetText(s);
+  dc.SetFont(* region->GetFont());
+
+  region->GetSize(&w, &h);
+  // Initialize the size if zero
+  if (((w == 0) || (h == 0)) && (strlen(s) > 0))
+  {
+    w = 100; h = 50;
+    region->SetSize(w, h);
+  }
+
+  wxStringList *string_list = oglFormatText(dc, s, (w-5), (h-5), region->GetFormatMode());
+  node = string_list->First();
+  while (node)
+  {
+    char *s = (char *)node->Data();
+    wxShapeTextLine *line = new wxShapeTextLine(0.0, 0.0, s);
+    region->GetFormattedText().Append((wxObject *)line);
+    node = node->Next();
+  }
+  delete string_list;
+  double actualW = w;
+  double actualH = h;
+  if (region->GetFormatMode() & FORMAT_SIZE_TO_CONTENTS)
+  {
+    oglGetCentredTextExtent(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, &actualW, &actualH);
+    if ((actualW != w ) || (actualH != h))
+    {
+      double xx, yy;
+      GetLabelPosition(i, &xx, &yy);
+      EraseRegion(dc, region, xx, yy);
+      if (m_labelObjects[i])
+      {
+        m_labelObjects[i]->Select(FALSE, &dc);
+        m_labelObjects[i]->Erase(dc);
+        m_labelObjects[i]->SetSize(actualW, actualH);
+      }
+
+      region->SetSize(actualW, actualH);
+
+      if (m_labelObjects[i])
+      {
+        m_labelObjects[i]->Select(TRUE, & dc);
+        m_labelObjects[i]->Draw(dc);
+      }
+    }
+  }
+  oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, actualW, actualH, region->GetFormatMode());
+  m_formatted = TRUE;
+}
+
+void wxLineShape::DrawRegion(wxDC& dc, wxShapeRegion *region, double x, double y)
+{
+  if (GetDisableLabel())
+    return;
+
+  double w, h;
+  double xx, yy;
+  region->GetSize(&w, &h);
+
+  // Get offset from x, y
+  region->GetPosition(&xx, &yy);
+
+  double xp = xx + x;
+  double yp = yy + y;
+
+  // First, clear a rectangle for the text IF there is any
+  if (region->GetFormattedText().Number() > 0)
+  {
+      dc.SetPen(* g_oglWhiteBackgroundPen);
+      dc.SetBrush(* g_oglWhiteBackgroundBrush);
+
+      // Now draw the text
+      if (region->GetFont()) dc.SetFont(* region->GetFont());
+
+      dc.DrawRectangle((long)(xp - w/2.0), (long)(yp - h/2.0), (long)w, (long)h);
+
+      if (m_pen) dc.SetPen(* m_pen);
+      dc.SetTextForeground(* region->GetActualColourObject());
+
+#ifdef __WXMSW__
+      dc.SetTextBackground(g_oglWhiteBackgroundBrush->GetColour());
+#endif
+
+      oglDrawFormattedText(dc, &(region->GetFormattedText()), xp, yp, w, h, region->GetFormatMode());
+  }
+}
+
+void wxLineShape::EraseRegion(wxDC& dc, wxShapeRegion *region, double x, double y)
+{
+  if (GetDisableLabel())
+    return;
+
+  double w, h;
+  double xx, yy;
+  region->GetSize(&w, &h);
+
+  // Get offset from x, y
+  region->GetPosition(&xx, &yy);
+
+  double xp = xx + x;
+  double yp = yy + y;
+
+  if (region->GetFormattedText().Number() > 0)
+  {
+      dc.SetPen(* g_oglWhiteBackgroundPen);
+      dc.SetBrush(* g_oglWhiteBackgroundBrush);
+
+      dc.DrawRectangle((long)(xp - w/2.0), (long)(yp - h/2.0), (long)w, (long)h);
+  }
+}
+
+// Get the reference point for a label. Region x and y
+// are offsets from this.
+// position is 0, 1, 2
+void wxLineShape::GetLabelPosition(int position, double *x, double *y)
+{
+  switch (position)
+  {
+    case 0:
+    {
+      // Want to take the middle section for the label
+      int n = m_lineControlPoints->Number();
+      int half_way = (int)(n/2);
+
+      // Find middle of this line
+      wxNode *node = m_lineControlPoints->Nth(half_way - 1);
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+      wxNode *next_node = node->Next();
+      wxRealPoint *next_point = (wxRealPoint *)next_node->Data();
+
+      double dx = (next_point->x - point->x);
+      double dy = (next_point->y - point->y);
+      *x = (double)(point->x + dx/2.0);
+      *y = (double)(point->y + dy/2.0);
+      break;
+    }
+    case 1:
+    {
+      wxNode *node = m_lineControlPoints->First();
+      *x = ((wxRealPoint *)node->Data())->x;
+      *y = ((wxRealPoint *)node->Data())->y;
+      break;
+    }
+    case 2:
+    {
+      wxNode *node = m_lineControlPoints->Last();
+      *x = ((wxRealPoint *)node->Data())->x;
+      *y = ((wxRealPoint *)node->Data())->y;
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+/*
+ * Find whether line is supposed to be vertical or horizontal and
+ * make it so.
+ *
+ */
+void GraphicsStraightenLine(wxRealPoint *point1, wxRealPoint *point2)
+{
+  double dx = point2->x - point1->x;
+  double dy = point2->y - point1->y;
+
+  if (dx == 0.0)
+    return;
+  else if (fabs(dy/dx) > 1.0)
+  {
+    point2->x = point1->x;
+  }
+  else point2->y = point1->y;
+}
+
+void wxLineShape::Straighten(wxDC *dc)
+{
+  if (!m_lineControlPoints || m_lineControlPoints->Number() < 3)
+    return;
+
+  if (dc)
+    Erase(* dc);
+
+  wxNode *first_point_node = m_lineControlPoints->First();
+  wxNode *last_point_node = m_lineControlPoints->Last();
+  wxNode *second_last_point_node = last_point_node->Previous();
+
+  wxRealPoint *last_point = (wxRealPoint *)last_point_node->Data();
+  wxRealPoint *second_last_point = (wxRealPoint *)second_last_point_node->Data();
+
+  GraphicsStraightenLine(last_point, second_last_point);
+
+  wxNode *node = first_point_node;
+  while (node && (node != second_last_point_node))
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *next_point = (wxRealPoint *)(node->Next()->Data());
+
+    GraphicsStraightenLine(point, next_point);
+    node = node->Next();
+  }
+
+  if (dc)
+    Draw(* dc);
+}
+
+
+void wxLineShape::Unlink()
+{
+  if (m_to)
+    m_to->GetLines().DeleteObject(this);
+  if (m_from)
+    m_from->GetLines().DeleteObject(this);
+  m_to = NULL;
+  m_from = NULL;
+}
+
+void wxLineShape::SetEnds(double x1, double y1, double x2, double y2)
+{
+  // Find centre point
+  wxNode *first_point_node = m_lineControlPoints->First();
+  wxNode *last_point_node = m_lineControlPoints->Last();
+  wxRealPoint *first_point = (wxRealPoint *)first_point_node->Data();
+  wxRealPoint *last_point = (wxRealPoint *)last_point_node->Data();
+
+  first_point->x = x1;
+  first_point->y = y1;
+  last_point->x = x2;
+  last_point->y = y2;
+
+  m_xpos = (double)((x1 + x2)/2.0);
+  m_ypos = (double)((y1 + y2)/2.0);
+}
+
+// Get absolute positions of ends
+void wxLineShape::GetEnds(double *x1, double *y1, double *x2, double *y2)
+{
+  wxNode *first_point_node = m_lineControlPoints->First();
+  wxNode *last_point_node = m_lineControlPoints->Last();
+  wxRealPoint *first_point = (wxRealPoint *)first_point_node->Data();
+  wxRealPoint *last_point = (wxRealPoint *)last_point_node->Data();
+
+  *x1 = first_point->x; *y1 = first_point->y;
+  *x2 = last_point->x; *y2 = last_point->y;
+}
+
+void wxLineShape::SetAttachments(int from_attach, int to_attach)
+{
+  m_attachmentFrom = from_attach;
+  m_attachmentTo = to_attach;
+}
+
+bool wxLineShape::HitTest(double x, double y, int *attachment, double *distance)
+{
+  if (!m_lineControlPoints)
+    return FALSE;
+
+  // Look at label regions in case mouse is over a label
+  bool inLabelRegion = FALSE;
+  for (int i = 0; i < 3; i ++)
+  {
+    wxNode *regionNode = m_regions.Nth(i);
+    if (regionNode)
+    {
+      wxShapeRegion *region = (wxShapeRegion *)regionNode->Data();
+      if (region->m_formattedText.Number() > 0)
+      {
+        double xp, yp, cx, cy, cw, ch;
+        GetLabelPosition(i, &xp, &yp);
+        // Offset region from default label position
+        region->GetPosition(&cx, &cy);
+        region->GetSize(&cw, &ch);
+        cx += xp;
+        cy += yp;
+        double rLeft = (double)(cx - (cw/2.0));
+        double rTop = (double)(cy - (ch/2.0));
+        double rRight = (double)(cx + (cw/2.0));
+        double rBottom = (double)(cy + (ch/2.0));
+        if (x > rLeft && x < rRight && y > rTop && y < rBottom)
+        {
+          inLabelRegion = TRUE;
+          i = 3;
+        }
+      }
+    }
+  }
+
+  wxNode *node = m_lineControlPoints->First();
+
+  while (node && node->Next())
+  {
+    wxRealPoint *point1 = (wxRealPoint *)node->Data();
+    wxRealPoint *point2 = (wxRealPoint *)node->Next()->Data();
+
+    // Allow for inaccurate mousing or vert/horiz lines
+    int extra = 4;
+    double left = wxMin(point1->x, point2->x) - extra;
+    double right = wxMax(point1->x, point2->x) + extra;
+
+    double bottom = wxMin(point1->y, point2->y) - extra;
+    double top = wxMax(point1->y, point2->y) + extra;
+
+    if ((x > left && x < right && y > bottom && y < top) || inLabelRegion)
+    {
+      // Work out distance from centre of line
+      double centre_x = (double)(left + (right - left)/2.0);
+      double centre_y = (double)(bottom + (top - bottom)/2.0);
+
+      *attachment = 0;
+      *distance = (double)sqrt((centre_x - x)*(centre_x - x) + (centre_y - y)*(centre_y - y));
+      return TRUE;
+    }
+
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+void wxLineShape::DrawArrows(wxDC& dc)
+{
+  // Distance along line of each arrow: space them out evenly.
+  double startArrowPos = 0.0;
+  double endArrowPos = 0.0;
+  double middleArrowPos = 0.0;
+
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    switch (arrow->GetArrowEnd())
+    {
+      case ARROW_POSITION_START:
+      {
+        if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
+          // If specified, x offset is proportional to line length
+          DrawArrow(dc, arrow, arrow->GetXOffset(), TRUE);
+        else
+        {
+          DrawArrow(dc, arrow, startArrowPos, FALSE);      // Absolute distance
+          startArrowPos += arrow->GetSize() + arrow->GetSpacing();
+        }
+        break;
+      }
+      case ARROW_POSITION_END:
+      {
+        if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
+          DrawArrow(dc, arrow, arrow->GetXOffset(), TRUE);
+        else
+        {
+          DrawArrow(dc, arrow, endArrowPos, FALSE);
+          endArrowPos += arrow->GetSize() + arrow->GetSpacing();
+        }
+        break;
+      }
+      case ARROW_POSITION_MIDDLE:
+      {
+        arrow->SetXOffset(middleArrowPos);
+        if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
+          DrawArrow(dc, arrow, arrow->GetXOffset(), TRUE);
+        else
+        {
+          DrawArrow(dc, arrow, middleArrowPos, FALSE);
+          middleArrowPos += arrow->GetSize() + arrow->GetSpacing();
+        }
+        break;
+      }
+    }
+    node = node->Next();
+  }
+}
+
+void wxLineShape::DrawArrow(wxDC& dc, wxArrowHead *arrow, double xOffset, bool proportionalOffset)
+{
+  wxNode *first_line_node = m_lineControlPoints->First();
+  wxRealPoint *first_line_point = (wxRealPoint *)first_line_node->Data();
+  wxNode *second_line_node = first_line_node->Next();
+  wxRealPoint *second_line_point = (wxRealPoint *)second_line_node->Data();
+
+  wxNode *last_line_node = m_lineControlPoints->Last();
+  wxRealPoint *last_line_point = (wxRealPoint *)last_line_node->Data();
+  wxNode *second_last_line_node = last_line_node->Previous();
+  wxRealPoint *second_last_line_point = (wxRealPoint *)second_last_line_node->Data();
+
+  // Position where we want to start drawing
+  double positionOnLineX, positionOnLineY;
+
+  // Position of start point of line, at the end of which we draw the arrow.
+  double startPositionX, startPositionY;
+
+  switch (arrow->GetPosition())
+  {
+    case ARROW_POSITION_START:
+    {
+      // If we're using a proportional offset, calculate just where this will
+      // be on the line.
+      double realOffset = xOffset;
+      if (proportionalOffset)
+      {
+        double totalLength =
+          (double)sqrt((second_line_point->x - first_line_point->x)*(second_line_point->x - first_line_point->x) +
+                      (second_line_point->y - first_line_point->y)*(second_line_point->y - first_line_point->y));
+        realOffset = (double)(xOffset * totalLength);
+      }
+      GetPointOnLine(second_line_point->x, second_line_point->y,
+                     first_line_point->x, first_line_point->y,
+                     realOffset, &positionOnLineX, &positionOnLineY);
+      startPositionX = second_line_point->x;
+      startPositionY = second_line_point->y;
+      break;
+    }
+    case ARROW_POSITION_END:
+    {
+      // If we're using a proportional offset, calculate just where this will
+      // be on the line.
+      double realOffset = xOffset;
+      if (proportionalOffset)
+      {
+        double totalLength =
+          (double)sqrt((second_last_line_point->x - last_line_point->x)*(second_last_line_point->x - last_line_point->x) +
+                      (second_last_line_point->y - last_line_point->y)*(second_last_line_point->y - last_line_point->y));
+        realOffset = (double)(xOffset * totalLength);
+      }
+      GetPointOnLine(second_last_line_point->x, second_last_line_point->y,
+                     last_line_point->x, last_line_point->y,
+                     realOffset, &positionOnLineX, &positionOnLineY);
+      startPositionX = second_last_line_point->x;
+      startPositionY = second_last_line_point->y;
+      break;
+    }
+    case ARROW_POSITION_MIDDLE:
+    {
+      // Choose a point half way between the last and penultimate points
+      double x = ((last_line_point->x + second_last_line_point->x)/2);
+      double y = ((last_line_point->y + second_last_line_point->y)/2);
+
+      // If we're using a proportional offset, calculate just where this will
+      // be on the line.
+      double realOffset = xOffset;
+      if (proportionalOffset)
+      {
+        double totalLength =
+          (double)sqrt((second_last_line_point->x - x)*(second_last_line_point->x - x) +
+                      (second_last_line_point->y - y)*(second_last_line_point->y - y));
+        realOffset = (double)(xOffset * totalLength);
+      }
+
+      GetPointOnLine(second_last_line_point->x, second_last_line_point->y,
+                     x, y, realOffset, &positionOnLineX, &positionOnLineY);
+      startPositionX = second_last_line_point->x;
+      startPositionY = second_last_line_point->y;
+      break;
+    }
+  }
+
+  /*
+   * Add yOffset to arrow, if any
+   */
+
+  const double myPi = (double) 3.14159265;
+  // The translation that the y offset may give
+  double deltaX = 0.0;
+  double deltaY = 0.0;
+  if ((arrow->GetYOffset() != 0.0) && !m_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)
+     */
+     double x1 = startPositionX;
+     double y1 = startPositionY;
+     double x3 = positionOnLineX;
+     double y3 = positionOnLineY;
+     double d = -arrow->GetYOffset(); // Negate so +offset is above line
+
+     double theta = 0.0;
+     if (x3 == x1)
+       theta = (double)(myPi/2.0);
+     else
+       theta = (double)atan((y3-y1)/(x3-x1));
+
+     double x4 = (double)(x3 - (d*sin(theta)));
+     double y4 = (double)(y3 + (d*cos(theta)));
+
+     deltaX = x4 - positionOnLineX;
+     deltaY = y4 - positionOnLineY;
+  }
+
+  switch (arrow->_GetType())
+  {
+    case ARROW_ARROW:
+    {
+      double arrowLength = arrow->GetSize();
+      double arrowWidth = (double)(arrowLength/3.0);
+
+      double tip_x, tip_y, side1_x, side1_y, side2_x, side2_y;
+      oglGetArrowPoints(startPositionX+deltaX, startPositionY+deltaY,
+                       positionOnLineX+deltaX, positionOnLineY+deltaY,
+                       arrowLength, arrowWidth, &tip_x, &tip_y,
+                       &side1_x, &side1_y, &side2_x, &side2_y);
+
+      wxPoint points[4];
+      points[0].x = (int) tip_x; points[0].y = (int) tip_y;
+      points[1].x = (int) side1_x; points[1].y = (int) side1_y;
+      points[2].x = (int) side2_x; points[2].y = (int) side2_y;
+      points[3].x = (int) tip_x; points[3].y = (int) tip_y;
+
+      dc.SetPen(* m_pen);
+      dc.SetBrush(* m_brush);
+      dc.DrawPolygon(4, points);
+      break;
+    }
+    case ARROW_HOLLOW_CIRCLE:
+    case ARROW_FILLED_CIRCLE:
+    {
+      // Find point on line of centre of circle, which is a radius away
+      // from the end position
+      double diameter = (double)(arrow->GetSize());
+      double x, y;
+      GetPointOnLine(startPositionX+deltaX, startPositionY+deltaY,
+                   positionOnLineX+deltaX, positionOnLineY+deltaY,
+                   (double)(diameter/2.0),
+                   &x, &y);
+
+      // Convert ellipse centre to top-left coordinates
+      double x1 = (double)(x - (diameter/2.0));
+      double y1 = (double)(y - (diameter/2.0));
+
+      dc.SetPen(* m_pen);
+      if (arrow->_GetType() == ARROW_HOLLOW_CIRCLE)
+        dc.SetBrush(* g_oglWhiteBackgroundBrush);
+      else
+        dc.SetBrush(* m_brush);
+
+      dc.DrawEllipse((long) x1, (long) y1, (long) diameter, (long) diameter);
+      break;
+    }
+    case ARROW_SINGLE_OBLIQUE:
+    {
+      break;
+    }
+    case 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
+         *                -----
+         */
+        double x, y;
+        GetPointOnLine(startPositionX, startPositionY,
+                   positionOnLineX, positionOnLineY,
+                   (double)(arrow->GetMetaFile()->m_width/2.0),
+                   &x, &y);
+
+        // Calculate theta for rotating the metafile.
+        /*
+          |
+          |     o(x2, y2)   'o' represents the arrowhead.
+          |    /
+          |   /
+          |  /theta
+          | /(x1, y1)
+          |______________________
+        */
+        double theta = 0.0;
+        double x1 = startPositionX;
+        double y1 = startPositionY;
+        double x2 = positionOnLineX;
+        double y2 = positionOnLineY;
+
+        if ((x1 == x2) && (y1 == y2))
+          theta = 0.0;
+
+        else if ((x1 == x2) && (y1 > y2))
+          theta = (double)(3.0*myPi/2.0);
+
+        else if ((x1 == x2) && (y2 > y1))
+          theta = (double)(myPi/2.0);
+
+        else if ((x2 > x1) && (y2 >= y1))
+          theta = (double)atan((y2 - y1)/(x2 - x1));
+
+        else if (x2 < x1)
+          theta = (double)(myPi + atan((y2 - y1)/(x2 - x1)));
+
+        else if ((x2 > x1) && (y2 < y1))
+          theta = (double)(2*myPi + atan((y2 - y1)/(x2 - x1)));
+
+        else
+        {
+          wxFatalError("Unknown arrowhead rotation case in lines.cc");
+        }
+
+        // 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 (m_erasing)
+        {
+          // If erasing, just draw a rectangle.
+          double minX, minY, maxX, maxY;
+          arrow->GetMetaFile()->GetBounds(&minX, &minY, &maxX, &maxY);
+          // Make erasing rectangle slightly bigger or you get droppings.
+          int extraPixels = 4;
+          dc.DrawRectangle((long)(deltaX + x + minX - (extraPixels/2.0)), (long)(deltaY + y + minY - (extraPixels/2.0)),
+                           (long)(maxX - minX + extraPixels), (long)(maxY - minY + extraPixels));
+        }
+        else
+          arrow->GetMetaFile()->Draw(dc, x+deltaX, y+deltaY);
+      }
+      break;
+    }
+    default:
+    {
+    }
+  }
+}
+
+void wxLineShape::OnErase(wxDC& dc)
+{
+    wxPen *old_pen = m_pen;
+    wxBrush *old_brush = m_brush;
+    SetPen(g_oglWhiteBackgroundPen);
+    SetBrush(g_oglWhiteBackgroundBrush);
+
+    double bound_x, bound_y;
+    GetBoundingBoxMax(&bound_x, &bound_y);
+    if (m_font) dc.SetFont(* m_font);
+
+    // Undraw text regions
+    for (int i = 0; i < 3; i++)
+    {
+      wxNode *node = m_regions.Nth(i);
+      if (node)
+      {
+        double x, y;
+        wxShapeRegion *region = (wxShapeRegion *)node->Data();
+        GetLabelPosition(i, &x, &y);
+        EraseRegion(dc, region, x, y);
+      }
+    }
+
+    // Undraw line
+    dc.SetPen(* g_oglWhiteBackgroundPen);
+    dc.SetBrush(* g_oglWhiteBackgroundBrush);
+
+    // Drawing over the line only seems to work if the line has a thickness
+    // of 1.
+    if (old_pen && (old_pen->GetWidth() > 1))
+    {
+      dc.DrawRectangle((long)(m_xpos - (bound_x/2.0) - 2.0), (long)(m_ypos - (bound_y/2.0) - 2.0),
+                        (long)(bound_x+4.0),  (long)(bound_y+4.0));
+    }
+    else
+    {
+      m_erasing = TRUE;
+      GetEventHandler()->OnDraw(dc);
+      GetEventHandler()->OnEraseControlPoints(dc);
+      m_erasing = FALSE;
+    }
+
+    if (old_pen) SetPen(old_pen);
+    if (old_brush) SetBrush(old_brush);
+}
+
+void wxLineShape::GetBoundingBoxMin(double *w, double *h)
+{
+  double x1 = 10000;
+  double y1 = 10000;
+  double x2 = -10000;
+  double y2 = -10000;
+
+  wxNode *node = m_lineControlPoints->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+
+    if (point->x < x1) x1 = point->x;
+    if (point->y < y1) y1 = point->y;
+    if (point->x > x2) x2 = point->x;
+    if (point->y > y2) y2 = point->y;
+
+    node = node->Next();
+  }
+  *w = (double)(x2 - x1);
+  *h = (double)(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.
+ */
+void wxLineShape::FindNth(wxShape *image, int *nth, int *no_arcs, bool incoming)
+{
+  int n = -1;
+  int num = 0;
+  wxNode *node = image->GetLines().First();
+  int this_attachment;
+  if (image == m_to)
+    this_attachment = m_attachmentTo;
+  else
+    this_attachment = m_attachmentFrom;
+
+  // Find number of lines going into/out of this particular attachment point
+  while (node)
+  {
+    wxLineShape *line = (wxLineShape *)node->Data();
+
+    if (line->m_from == image)
+    {
+      // This is the nth line attached to 'image'
+      if ((line == this) && !incoming)
+        n = num;
+
+      // Increment num count if this is the same side (attachment number)
+      if (line->m_attachmentFrom == this_attachment)
+        num ++;
+    }
+
+    if (line->m_to == image)
+    {
+      // This is the nth line attached to 'image'
+      if ((line == this) && incoming)
+        n = num;
+
+      // Increment num count if this is the same side (attachment number)
+      if (line->m_attachmentTo == this_attachment)
+        num ++;
+    }
+
+    node = node->Next();
+  }
+  *nth = n;
+  *no_arcs = num;
+}
+
+void wxLineShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
+{
+  wxPen *old_pen = m_pen;
+  wxBrush *old_brush = m_brush;
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  SetPen(& dottedPen);
+  SetBrush( wxTRANSPARENT_BRUSH );
+
+  GetEventHandler()->OnDraw(dc);
+
+  if (old_pen) SetPen(old_pen);
+  else SetPen(NULL);
+  if (old_brush) SetBrush(old_brush);
+  else SetBrush(NULL);
+}
+
+bool wxLineShape::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
+{
+  double x_offset = x - old_x;
+  double y_offset = y - old_y;
+
+  if (m_lineControlPoints && !(x_offset == 0.0 && y_offset == 0.0))
+  {
+    wxNode *node = m_lineControlPoints->First();
+    while (node)
+    {
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+      point->x += x_offset;
+      point->y += y_offset;
+      node = node->Next();
+    }
+
+  }
+
+  // Move temporary label rectangles if necessary
+  for (int i = 0; i < 3; i++)
+  {
+    if (m_labelObjects[i])
+    {
+      m_labelObjects[i]->Erase(dc);
+      double xp, yp, xr, yr;
+      GetLabelPosition(i, &xp, &yp);
+      wxNode *node = m_regions.Nth(i);
+      if (node)
+      {
+        wxShapeRegion *region = (wxShapeRegion *)node->Data();
+        region->GetPosition(&xr, &yr);
+      }
+      else
+      {
+        xr = 0.0; yr = 0.0;
+      }
+
+      m_labelObjects[i]->Move(dc, xp+xr, yp+yr);
+    }
+  }
+  return TRUE;
+}
+
+void wxLineShape::OnMoveLink(wxDC& dc, bool moveControlPoints)
+{
+  if (!m_from || !m_to)
+   return;
+
+    if (m_lineControlPoints->Number() > 2)
+      Initialise();
+
+    // Do each end - nothing in the middle. User has to move other points
+    // manually if necessary.
+    double end_x, end_y;
+    double other_end_x, other_end_y;
+
+    FindLineEndPoints(&end_x, &end_y, &other_end_x, &other_end_y);
+
+    wxNode *first = m_lineControlPoints->First();
+    wxRealPoint *first_point = (wxRealPoint *)first->Data();
+    wxNode *last = m_lineControlPoints->Last();
+    wxRealPoint *last_point = (wxRealPoint *)last->Data();
+
+/* This is redundant, surely? Done by SetEnds.
+    first_point->x = end_x; first_point->y = end_y;
+    last_point->x = other_end_x; last_point->y = other_end_y;
+*/
+
+    double oldX = m_xpos;
+    double oldY = m_ypos;
+
+    SetEnds(end_x, end_y, other_end_x, other_end_y);
+
+    // Do a second time, because one may depend on the other.
+    FindLineEndPoints(&end_x, &end_y, &other_end_x, &other_end_y);
+    SetEnds(end_x, end_y, other_end_x, other_end_y);
+
+    // Try to move control points with the arc
+    double x_offset = m_xpos - oldX;
+    double y_offset = m_ypos - oldY;
+
+//    if (moveControlPoints && m_lineControlPoints && !(x_offset == 0.0 && y_offset == 0.0))
+    // Only move control points if it's a self link. And only works if attachment mode is ON.
+    if ((m_from == m_to) && (m_from->GetAttachmentMode() != ATTACHMENT_MODE_NONE) && moveControlPoints && m_lineControlPoints && !(x_offset == 0.0 && y_offset == 0.0))
+    {
+      wxNode *node = m_lineControlPoints->First();
+      while (node)
+      {
+        if ((node != m_lineControlPoints->First()) && (node != m_lineControlPoints->Last()))
+        {
+          wxRealPoint *point = (wxRealPoint *)node->Data();
+          point->x += x_offset;
+          point->y += y_offset;
+        }
+        node = node->Next();
+      }
+    }
+
+    Move(dc, m_xpos, m_ypos);
+}
+
+// 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.
+void wxLineShape::FindLineEndPoints(double *fromX, double *fromY, double *toX, double *toY)
+{
+  if (!m_from || !m_to)
+   return;
+
+  // Do each end - nothing in the middle. User has to move other points
+  // manually if necessary.
+  double end_x, end_y;
+  double other_end_x, other_end_y;
+
+  wxNode *first = m_lineControlPoints->First();
+  wxRealPoint *first_point = (wxRealPoint *)first->Data();
+  wxNode *last = m_lineControlPoints->Last();
+  wxRealPoint *last_point = (wxRealPoint *)last->Data();
+
+  wxNode *second = first->Next();
+  wxRealPoint *second_point = (wxRealPoint *)second->Data();
+
+  wxNode *second_last = last->Previous();
+  wxRealPoint *second_last_point = (wxRealPoint *)second_last->Data();
+
+  if (m_lineControlPoints->Number() > 2)
+  {
+    if (m_from->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
+    {
+      int nth, no_arcs;
+      FindNth(m_from, &nth, &no_arcs, FALSE); // Not incoming
+      m_from->GetAttachmentPosition(m_attachmentFrom, &end_x, &end_y, nth, no_arcs, this);
+    }
+    else
+      (void) m_from->GetPerimeterPoint(m_from->GetX(), m_from->GetY(),
+                                   (double)second_point->x, (double)second_point->y,
+                                    &end_x, &end_y);
+
+    if (m_to->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
+    {
+      int nth, no_arcs;
+      FindNth(m_to, &nth, &no_arcs, TRUE); // Incoming
+      m_to->GetAttachmentPosition(m_attachmentTo, &other_end_x, &other_end_y, nth, no_arcs, this);
+    }
+    else
+      (void) m_to->GetPerimeterPoint(m_to->GetX(), m_to->GetY(),
+                                (double)second_last_point->x, (double)second_last_point->y,
+                                &other_end_x, &other_end_y);
+  }
+  else
+  {
+    double fromX = m_from->GetX();
+    double fromY = m_from->GetY();
+    double toX = m_to->GetX();
+    double toY = m_to->GetY();
+
+    if (m_from->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
+    {
+      int nth, no_arcs;
+      FindNth(m_from, &nth, &no_arcs, FALSE);
+      m_from->GetAttachmentPosition(m_attachmentFrom, &end_x, &end_y, nth, no_arcs, this);
+      fromX = end_x;
+      fromY = end_y;
+    }
+
+    if (m_to->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
+    {
+      int nth, no_arcs;
+      FindNth(m_to, &nth, &no_arcs, TRUE);
+      m_to->GetAttachmentPosition(m_attachmentTo, &other_end_x, &other_end_y, nth, no_arcs, this);
+      toX = other_end_x;
+      toY = other_end_y;
+    }
+
+    if (m_from->GetAttachmentMode() == ATTACHMENT_MODE_NONE)
+      (void) m_from->GetPerimeterPoint(m_from->GetX(), m_from->GetY(),
+                                  toX, toY,
+                                  &end_x, &end_y);
+
+    if (m_to->GetAttachmentMode() == ATTACHMENT_MODE_NONE)
+      (void) m_to->GetPerimeterPoint(m_to->GetX(), m_to->GetY(),
+                                fromX, fromY,
+                                &other_end_x, &other_end_y);
+  }
+  *fromX = end_x;
+  *fromY = end_y;
+  *toX = other_end_x;
+  *toY = other_end_y;
+}
+
+void wxLineShape::OnDraw(wxDC& dc)
+{
+  if (m_lineControlPoints)
+  {
+    if (m_pen)
+      dc.SetPen(* m_pen);
+    if (m_brush)
+      dc.SetBrush(* m_brush);
+
+    int n = m_lineControlPoints->Number();
+    wxPoint *points = new wxPoint[n];
+    int i;
+    for (i = 0; i < n; i++)
+    {
+        wxRealPoint* point = (wxRealPoint*) m_lineControlPoints->Nth(i)->Data();
+        points[i].x = WXROUND(point->x);
+        points[i].y = WXROUND(point->y);
+    }
+
+    if (m_isSpline)
+      dc.DrawSpline(n, points);
+    else
+      dc.DrawLines(n, points);
+
+#ifdef __WXMSW__
+    // For some reason, last point isn't drawn under Windows.
+    dc.DrawPoint(points[n-1]);
+#endif
+
+    delete[] points;
+
+
+    // Problem with pen - if not a solid pen, does strange things
+    // to the arrowhead. So make (get) a new pen that's solid.
+    if (m_pen && (m_pen->GetStyle() != wxSOLID))
+    {
+      wxPen *solid_pen =
+        wxThePenList->FindOrCreatePen(m_pen->GetColour(), 1, wxSOLID);
+      if (solid_pen)
+        dc.SetPen(* solid_pen);
+    }
+    DrawArrows(dc);
+  }
+}
+
+void wxLineShape::OnDrawControlPoints(wxDC& dc)
+{
+  if (!m_drawHandles)
+    return;
+
+  // Draw temporary label rectangles if necessary
+  for (int i = 0; i < 3; i++)
+  {
+    if (m_labelObjects[i])
+      m_labelObjects[i]->Draw(dc);
+  }
+  wxShape::OnDrawControlPoints(dc);
+}
+
+void wxLineShape::OnEraseControlPoints(wxDC& dc)
+{
+  // Erase temporary label rectangles if necessary
+  for (int i = 0; i < 3; i++)
+  {
+    if (m_labelObjects[i])
+      m_labelObjects[i]->Erase(dc);
+  }
+  wxShape::OnEraseControlPoints(dc);
+}
+
+void wxLineShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+}
+
+void wxLineShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+}
+
+void wxLineShape::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+}
+
+/*
+void wxLineShape::SetArrowSize(double length, double width)
+{
+  arrow_length = length;
+  arrow_width = width;
+}
+
+void wxLineShape::SetStartArrow(int style)
+{
+  start_style = style;
+}
+
+void wxLineShape::SetMiddleArrow(int style)
+{
+  middle_style = style;
+}
+
+void wxLineShape::SetEndArrow(int style)
+{
+  end_style = style;
+}
+*/
+
+void wxLineShape::OnDrawContents(wxDC& dc)
+{
+  if (GetDisableLabel())
+    return;
+
+  for (int i = 0; i < 3; i++)
+  {
+    wxNode *node = m_regions.Nth(i);
+    if (node)
+    {
+      wxShapeRegion *region = (wxShapeRegion *)node->Data();
+      double x, y;
+      GetLabelPosition(i, &x, &y);
+      DrawRegion(dc, region, x, y);
+    }
+  }
+}
+
+void wxLineShape::SetTo(wxShape *object)
+{
+  m_to = object;
+}
+
+void wxLineShape::SetFrom(wxShape *object)
+{
+  m_from = object;
+}
+
+void wxLineShape::MakeControlPoints()
+{
+  if (m_canvas && m_lineControlPoints)
+  {
+    wxNode *first = m_lineControlPoints->First();
+    wxNode *last = m_lineControlPoints->Last();
+    wxRealPoint *first_point = (wxRealPoint *)first->Data();
+    wxRealPoint *last_point = (wxRealPoint *)last->Data();
+
+    wxLineControlPoint *control = new wxLineControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
+                                               first_point->x, first_point->y,
+                                               CONTROL_POINT_ENDPOINT_FROM);
+    control->m_point = first_point;
+    m_canvas->AddShape(control);
+    m_controlPoints.Append(control);
+
+
+    wxNode *node = first->Next();
+    while (node != last)
+    {
+      wxRealPoint *point = (wxRealPoint *)node->Data();
+
+      control = new wxLineControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
+                                               point->x, point->y,
+                                               CONTROL_POINT_LINE);
+      control->m_point = point;
+
+      m_canvas->AddShape(control);
+      m_controlPoints.Append(control);
+
+      node = node->Next();
+    }
+    control = new wxLineControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
+                                               last_point->x, last_point->y,
+                                               CONTROL_POINT_ENDPOINT_TO);
+    control->m_point = last_point;
+    m_canvas->AddShape(control);
+    m_controlPoints.Append(control);
+
+  }
+
+}
+
+void wxLineShape::ResetControlPoints()
+{
+  if (m_canvas && m_lineControlPoints && m_controlPoints.Number() > 0)
+  {
+    wxNode *node = m_controlPoints.First();
+    wxNode *control_node = m_lineControlPoints->First();
+    while (node && control_node)
+    {
+      wxRealPoint *point = (wxRealPoint *)control_node->Data();
+      wxLineControlPoint *control = (wxLineControlPoint *)node->Data();
+      control->SetX(point->x);
+      control->SetY(point->y);
+
+      node = node->Next();
+      control_node = control_node->Next();
+    }
+  }
+}
+
+#ifdef PROLOGIO
+void wxLineShape::WriteAttributes(wxExpr *clause)
+{
+  wxShape::WriteAttributes(clause);
+
+  if (m_from)
+    clause->AddAttributeValue("from", m_from->GetId());
+  if (m_to)
+    clause->AddAttributeValue("to", m_to->GetId());
+
+  if (m_attachmentTo != 0)
+    clause->AddAttributeValue("attachment_to", (long)m_attachmentTo);
+  if (m_attachmentFrom != 0)
+    clause->AddAttributeValue("attachment_from", (long)m_attachmentFrom);
+
+  if (m_alignmentStart != 0)
+    clause->AddAttributeValue("align_start", (long)m_alignmentStart);
+  if (m_alignmentEnd != 0)
+    clause->AddAttributeValue("align_end", (long)m_alignmentEnd);
+
+  clause->AddAttributeValue("is_spline", (long)m_isSpline);
+  if (m_maintainStraightLines)
+    clause->AddAttributeValue("keep_lines_straight", (long)m_maintainStraightLines);
+
+  // Make a list of lists for the (sp)line controls
+  wxExpr *list = new wxExpr(wxExprList);
+  wxNode *node = m_lineControlPoints->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxExpr *point_list = new wxExpr(wxExprList);
+    wxExpr *x_expr = new wxExpr((double) point->x);
+    wxExpr *y_expr = new wxExpr((double) point->y);
+    point_list->Append(x_expr);
+    point_list->Append(y_expr);
+    list->Append(point_list);
+
+    node = node->Next();
+  }
+  clause->AddAttributeValue("controls", list);
+
+  // Write arc arrows in new OGL format, if there are any.
+  // This is a list of lists. Each sublist comprises:
+  // (arrowType arrowEnd xOffset arrowSize)
+  if (m_arcArrows.Number() > 0)
+  {
+    wxExpr *arrow_list = new wxExpr(wxExprList);
+    node = m_arcArrows.First();
+    while (node)
+    {
+      wxArrowHead *head = (wxArrowHead *)node->Data();
+      wxExpr *head_list = new wxExpr(wxExprList);
+      head_list->Append(new wxExpr((long)head->_GetType()));
+      head_list->Append(new wxExpr((long)head->GetArrowEnd()));
+      head_list->Append(new wxExpr(head->GetXOffset()));
+      head_list->Append(new wxExpr(head->GetArrowSize()));
+      head_list->Append(new wxExpr(wxExprString, head->GetName()));
+      head_list->Append(new wxExpr(head->GetId()));
+
+      // New members of wxArrowHead
+      head_list->Append(new wxExpr(head->GetYOffset()));
+      head_list->Append(new wxExpr(head->GetSpacing()));
+
+      arrow_list->Append(head_list);
+
+      node = node->Next();
+    }
+    clause->AddAttributeValue("arrows", arrow_list);
+  }
+}
+
+void wxLineShape::ReadAttributes(wxExpr *clause)
+{
+  wxShape::ReadAttributes(clause);
+
+  int iVal = (int) m_isSpline;
+  clause->AssignAttributeValue("is_spline", &iVal);
+  m_isSpline = (iVal != 0);
+
+  iVal = (int) m_maintainStraightLines;
+  clause->AssignAttributeValue("keep_lines_straight", &iVal);
+  m_maintainStraightLines = (iVal != 0);
+
+  clause->AssignAttributeValue("align_start", &m_alignmentStart);
+  clause->AssignAttributeValue("align_end", &m_alignmentEnd);
+
+  // Compatibility: check for no regions.
+  if (m_regions.Number() == 0)
+  {
+    wxShapeRegion *newRegion = new wxShapeRegion;
+    newRegion->SetName("Middle");
+    newRegion->SetSize(150, 50);
+    m_regions.Append((wxObject *)newRegion);
+    if (m_text.Number() > 0)
+    {
+      newRegion->ClearText();
+      wxNode *node = m_text.First();
+      while (node)
+      {
+        wxShapeTextLine *textLine = (wxShapeTextLine *)node->Data();
+        wxNode *next = node->Next();
+        newRegion->GetFormattedText().Append((wxObject *)textLine);
+        delete node;
+        node = next;
+      }
+    }
+
+    newRegion = new wxShapeRegion;
+    newRegion->SetName("Start");
+    newRegion->SetSize(150, 50);
+    m_regions.Append((wxObject *)newRegion);
+
+    newRegion = new wxShapeRegion;
+    newRegion->SetName("End");
+    newRegion->SetSize(150, 50);
+    m_regions.Append((wxObject *)newRegion);
+  }
+
+  m_attachmentTo = 0;
+  m_attachmentFrom = 0;
+
+  clause->AssignAttributeValue("attachment_to", &m_attachmentTo);
+  clause->AssignAttributeValue("attachment_from", &m_attachmentFrom);
+
+  wxExpr *line_list = NULL;
+
+  // When image is created, there are default control points. Override
+  // them if there are some in the file.
+  clause->AssignAttributeValue("controls", &line_list);
+
+  if (line_list)
+  {
+    // Read a list of lists for the spline controls
+    if (m_lineControlPoints)
+    {
+      ClearPointList(*m_lineControlPoints);
+    }
+    else
+      m_lineControlPoints = new wxList;
+
+    wxExpr *node = line_list->value.first;
+
+    while (node)
+    {
+      wxExpr *xexpr = node->value.first;
+      double x = xexpr->RealValue();
+
+      wxExpr *yexpr = xexpr->next;
+      double y = yexpr->RealValue();
+
+      wxRealPoint *point = new wxRealPoint(x, y);
+      m_lineControlPoints->Append((wxObject*) point);
+
+      node = node->next;
+    }
+  }
+
+  // Read arrow list, for new OGL code
+  wxExpr *arrow_list = NULL;
+
+  clause->AssignAttributeValue("arrows", &arrow_list);
+  if (arrow_list)
+  {
+    wxExpr *node = arrow_list->value.first;
+
+    while (node)
+    {
+      WXTYPE arrowType = ARROW_ARROW;
+      int arrowEnd = 0;
+      double xOffset = 0.0;
+      double arrowSize = 0.0;
+      wxString arrowName("");
+      long arrowId = -1;
+
+      wxExpr *type_expr = node->Nth(0);
+      wxExpr *end_expr = node->Nth(1);
+      wxExpr *dist_expr = node->Nth(2);
+      wxExpr *size_expr = node->Nth(3);
+      wxExpr *name_expr = node->Nth(4);
+      wxExpr *id_expr = node->Nth(5);
+
+      // New members of wxArrowHead
+      wxExpr *yOffsetExpr = node->Nth(6);
+      wxExpr *spacingExpr = node->Nth(7);
+
+      if (type_expr)
+        arrowType = (int)type_expr->IntegerValue();
+      if (end_expr)
+        arrowEnd = (int)end_expr->IntegerValue();
+      if (dist_expr)
+        xOffset = dist_expr->RealValue();
+      if (size_expr)
+        arrowSize = size_expr->RealValue();
+      if (name_expr)
+        arrowName = name_expr->StringValue();
+      if (id_expr)
+        arrowId = id_expr->IntegerValue();
+
+      if (arrowId == -1)
+        arrowId = wxNewId();
+      else
+        wxRegisterId(arrowId);
+
+      wxArrowHead *arrowHead = AddArrow(arrowType, arrowEnd, arrowSize, xOffset, (char*) (const char*) arrowName, NULL, arrowId);
+      if (yOffsetExpr)
+        arrowHead->SetYOffset(yOffsetExpr->RealValue());
+      if (spacingExpr)
+        arrowHead->SetSpacing(spacingExpr->RealValue());
+
+      node = node->next;
+    }
+  }
+}
+#endif
+
+void wxLineShape::Copy(wxShape& copy)
+{
+  wxShape::Copy(copy);
+
+  wxASSERT( copy.IsKindOf(CLASSINFO(wxLineShape)) );
+
+  wxLineShape& lineCopy = (wxLineShape&) copy;
+
+  lineCopy.m_to = m_to;
+  lineCopy.m_from = m_from;
+  lineCopy.m_attachmentTo = m_attachmentTo;
+  lineCopy.m_attachmentFrom = m_attachmentFrom;
+  lineCopy.m_isSpline = m_isSpline;
+  lineCopy.m_alignmentStart = m_alignmentStart;
+  lineCopy.m_alignmentEnd = m_alignmentEnd;
+  lineCopy.m_maintainStraightLines = m_maintainStraightLines;
+  lineCopy.m_lineOrientations.Clear();
+
+  wxNode *node = m_lineOrientations.First();
+  while (node)
+  {
+    lineCopy.m_lineOrientations.Append(node->Data());
+    node = node->Next();
+  }
+
+  if (lineCopy.m_lineControlPoints)
+  {
+    ClearPointList(*lineCopy.m_lineControlPoints);
+    delete lineCopy.m_lineControlPoints;
+  }
+
+  lineCopy.m_lineControlPoints = new wxList;
+
+  node = m_lineControlPoints->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
+    lineCopy.m_lineControlPoints->Append((wxObject*) new_point);
+    node = node->Next();
+  }
+
+  // Copy arrows
+  lineCopy.ClearArrowsAtPosition(-1);
+  node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    lineCopy.m_arcArrows.Append(new wxArrowHead(*arrow));
+    node = node->Next();
+  }
+}
+
+// Override select, to create/delete temporary label-moving objects
+void wxLineShape::Select(bool select, wxDC* dc)
+{
+  wxShape::Select(select, dc);
+  if (select)
+  {
+    for (int i = 0; i < 3; i++)
+    {
+      wxNode *node = m_regions.Nth(i);
+      if (node)
+      {
+        wxShapeRegion *region = (wxShapeRegion *)node->Data();
+        if (region->m_formattedText.Number() > 0)
+        {
+          double w, h, x, y, xx, yy;
+          region->GetSize(&w, &h);
+          region->GetPosition(&x, &y);
+          GetLabelPosition(i, &xx, &yy);
+          if (m_labelObjects[i])
+          {
+            m_labelObjects[i]->Select(FALSE);
+            m_labelObjects[i]->RemoveFromCanvas(m_canvas);
+            delete m_labelObjects[i];
+          }
+          m_labelObjects[i] = OnCreateLabelShape(this, region, w, h);
+          m_labelObjects[i]->AddToCanvas(m_canvas);
+          m_labelObjects[i]->Show(TRUE);
+          if (dc)
+            m_labelObjects[i]->Move(*dc, (double)(x + xx), (double)(y + yy));
+          m_labelObjects[i]->Select(TRUE, dc);
+        }
+      }
+    }
+  }
+  else
+  {
+    for (int i = 0; i < 3; i++)
+    {
+      if (m_labelObjects[i])
+      {
+        m_labelObjects[i]->Select(FALSE, dc);
+        m_labelObjects[i]->Erase(*dc);
+        m_labelObjects[i]->RemoveFromCanvas(m_canvas);
+        delete m_labelObjects[i];
+        m_labelObjects[i] = NULL;
+      }
+    }
+  }
+}
+
+/*
+ * Line control point
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxLineControlPoint, wxControlPoint)
+
+wxLineControlPoint::wxLineControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size, double x, double y, int the_type):
+  wxControlPoint(theCanvas, object, size, x, y, the_type)
+{
+  m_xpos = x;
+  m_ypos = y;
+  m_type = the_type;
+  m_point = NULL;
+}
+
+wxLineControlPoint::~wxLineControlPoint()
+{
+}
+
+void wxLineControlPoint::OnDraw(wxDC& dc)
+{
+  wxRectangleShape::OnDraw(dc);
+}
+
+// Implement movement of Line point
+void wxLineControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
+}
+
+void wxLineControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
+}
+
+void wxLineControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+    m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
+}
+
+// Control points ('handles') redirect control to the actual shape, to make it easier
+// to override sizing behaviour.
+void wxLineShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
+{
+  wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  dc.SetLogicalFunction(OGLRBLF);
+
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  if (lpt->m_type == CONTROL_POINT_LINE)
+  {
+    m_canvas->Snap(&x, &y);
+
+    lpt->SetX(x); lpt->SetY(y);
+    lpt->m_point->x = x; lpt->m_point->y = y;
+
+    wxLineShape *lineShape = (wxLineShape *)this;
+
+    wxPen *old_pen = lineShape->GetPen();
+    wxBrush *old_brush = lineShape->GetBrush();
+
+    wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+    lineShape->SetPen(& dottedPen);
+    lineShape->SetBrush(wxTRANSPARENT_BRUSH);
+
+    lineShape->GetEventHandler()->OnMoveLink(dc, FALSE);
+
+    lineShape->SetPen(old_pen);
+    lineShape->SetBrush(old_brush);
+  }
+
+  if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM || lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
+  {
+//    lpt->SetX(x); lpt->SetY(y);
+  }
+
+}
+
+void wxLineShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  wxLineShape *lineShape = (wxLineShape *)this;
+  if (lpt->m_type == CONTROL_POINT_LINE)
+  {
+    lpt->m_originalPos = * (lpt->m_point);
+    m_canvas->Snap(&x, &y);
+
+    this->Erase(dc);
+
+    // Redraw start and end objects because we've left holes
+    // when erasing the line
+    lineShape->GetFrom()->OnDraw(dc);
+    lineShape->GetFrom()->OnDrawContents(dc);
+    lineShape->GetTo()->OnDraw(dc);
+    lineShape->GetTo()->OnDrawContents(dc);
+
+    this->SetDisableLabel(TRUE);
+    dc.SetLogicalFunction(OGLRBLF);
+
+    lpt->m_xpos = x; lpt->m_ypos = y;
+    lpt->m_point->x = x; lpt->m_point->y = y;
+
+    wxPen *old_pen = lineShape->GetPen();
+    wxBrush *old_brush = lineShape->GetBrush();
+
+    wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+    lineShape->SetPen(& dottedPen);
+    lineShape->SetBrush(wxTRANSPARENT_BRUSH);
+
+    lineShape->GetEventHandler()->OnMoveLink(dc, FALSE);
+
+    lineShape->SetPen(old_pen);
+    lineShape->SetBrush(old_brush);
+  }
+
+  if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM || lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
+  {
+    m_canvas->SetCursor(* g_oglBullseyeCursor);
+    lpt->m_oldCursor = wxSTANDARD_CURSOR;
+  }
+}
+
+void wxLineShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
+{
+  wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
+
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  this->SetDisableLabel(FALSE);
+  wxLineShape *lineShape = (wxLineShape *)this;
+
+  if (lpt->m_type == CONTROL_POINT_LINE)
+  {
+    m_canvas->Snap(&x, &y);
+
+    wxRealPoint pt = wxRealPoint(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.
+    lpt->m_xpos = lpt->m_originalPos.x; lpt->m_ypos = lpt->m_originalPos.y;
+    lpt->m_point->x = lpt->m_originalPos.x; lpt->m_point->y = lpt->m_originalPos.y;
+
+    OnMoveMiddleControlPoint(dc, lpt, pt);
+  }
+  if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM)
+  {
+    if (lpt->m_oldCursor)
+      m_canvas->SetCursor(* lpt->m_oldCursor);
+
+//    this->Erase(dc);
+
+//    lpt->m_xpos = x; lpt->m_ypos = y;
+
+    if (lineShape->GetFrom())
+    {
+      lineShape->GetFrom()->MoveLineToNewAttachment(dc, lineShape, x, y);
+    }
+  }
+  if (lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
+  {
+    if (lpt->m_oldCursor)
+      m_canvas->SetCursor(* lpt->m_oldCursor);
+
+//    lpt->m_xpos = x; lpt->m_ypos = y;
+
+    if (lineShape->GetTo())
+    {
+      lineShape->GetTo()->MoveLineToNewAttachment(dc, lineShape, x, y);
+    }
+  }
+
+  // Needed?
+#if 0
+  int i = 0;
+  for (i = 0; i < lineShape->GetLineControlPoints()->Number(); i++)
+    if (((wxRealPoint *)(lineShape->GetLineControlPoints()->Nth(i)->Data())) == lpt->m_point)
+      break;
+
+  // N.B. in OnMoveControlPoint, an event handler in Hardy could have deselected
+  // the line and therefore deleted 'this'. -> GPF, intermittently.
+  // So assume at this point that we've been blown away.
+
+  lineShape->OnMoveControlPoint(i+1, x, y);
+#endif
+}
+
+// This is called only when a non-end control point is moved.
+bool wxLineShape::OnMoveMiddleControlPoint(wxDC& dc, wxLineControlPoint* lpt, const wxRealPoint& pt)
+{
+    lpt->m_xpos = pt.x; lpt->m_ypos = pt.y;
+    lpt->m_point->x = pt.x; lpt->m_point->y = pt.y;
+
+    GetEventHandler()->OnMoveLink(dc);
+
+    return TRUE;
+}
+
+// Implement movement of endpoint to a new attachment
+// OBSOLETE: done by dragging with the left button.
+
+#if 0
+void wxLineControlPoint::OnDragRight(bool draw, double x, double y, int keys, int attachment)
+{
+  if (m_type == CONTROL_POINT_ENDPOINT_FROM || m_type == CONTROL_POINT_ENDPOINT_TO)
+  {
+    m_xpos = x; m_ypos = y;
+  }
+}
+
+void wxLineControlPoint::OnBeginDragRight(double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  wxLineShape *lineShape = (wxLineShape *)m_shape;
+  if (m_type == CONTROL_POINT_ENDPOINT_FROM || m_type == CONTROL_POINT_ENDPOINT_TO)
+  {
+    Erase(dc);
+    lineShape->GetEventHandler()->OnDraw(dc);
+    if (m_type == CONTROL_POINT_ENDPOINT_FROM)
+    {
+      lineShape->GetFrom()->GetEventHandler()->OnDraw(dc);
+      lineShape->GetFrom()->GetEventHandler()->OnDrawContents(dc);
+    }
+    else
+    {
+      lineShape->GetTo()->GetEventHandler()->OnDraw(dc);
+      lineShape->GetTo()->GetEventHandler()->OnDrawContents(dc);
+    }
+    m_canvas->SetCursor(g_oglBullseyeCursor);
+    m_oldCursor = wxSTANDARD_CURSOR;
+  }
+}
+
+void wxLineControlPoint::OnEndDragRight(double x, double y, int keys, int attachment)
+{
+  wxClientDC dc(GetCanvas());
+  GetCanvas()->PrepareDC(dc);
+
+  wxLineShape *lineShape = (wxLineShape *)m_shape;
+  if (m_type == CONTROL_POINT_ENDPOINT_FROM)
+  {
+    if (m_oldCursor)
+      m_canvas->SetCursor(m_oldCursor);
+
+    m_xpos = x; m_ypos = y;
+
+    if (lineShape->GetFrom())
+    {
+      lineShape->GetFrom()->EraseLinks(dc);
+
+      int new_attachment;
+      double distance;
+
+      if (lineShape->GetFrom()->HitTest(x, y, &new_attachment, &distance))
+        lineShape->SetAttachments(new_attachment, lineShape->GetAttachmentTo());
+
+      lineShape->GetFrom()->MoveLinks(dc);
+    }
+  }
+  if (m_type == CONTROL_POINT_ENDPOINT_TO)
+  {
+    if (m_oldCursor)
+      m_canvas->SetCursor(m_oldCursor);
+    m_shape->Erase(dc);
+
+    m_xpos = x; m_ypos = y;
+
+    if (lineShape->GetTo())
+    {
+      lineShape->GetTo()->EraseLinks(dc);
+
+      int new_attachment;
+      double distance;
+      if (lineShape->GetTo()->HitTest(x, y, &new_attachment, &distance))
+        lineShape->SetAttachments(lineShape->GetAttachmentFrom(), new_attachment);
+
+      lineShape->GetTo()->MoveLinks(dc);
+    }
+  }
+  int i = 0;
+  for (i = 0; i < lineShape->GetLineControlPoints()->Number(); i++)
+    if (((wxRealPoint *)(lineShape->GetLineControlPoints()->Nth(i)->Data())) == m_point)
+      break;
+  lineShape->OnMoveControlPoint(i+1, x, y);
+  if (!m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+#endif
+
+/*
+ * Get the point on the given line (x1, y1) (x2, y2)
+ * distance 'length' along from the end,
+ * returned values in x and y
+ */
+
+void GetPointOnLine(double x1, double y1, double x2, double y2,
+                    double length, double *x, double *y)
+{
+  double l = (double)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+
+  if (l < 0.01)
+    l = (double) 0.01;
+
+  double i_bar = (x2 - x1)/l;
+  double j_bar = (y2 - y1)/l;
+
+  *x = (- length*i_bar) + x2;
+  *y = (- length*j_bar) + y2;
+}
+
+wxArrowHead *wxLineShape::AddArrow(WXTYPE type, int end, double size, double xOffset,
+    const wxString& name, wxPseudoMetaFile *mf, long arrowId)
+{
+  wxArrowHead *arrow = new wxArrowHead(type, end, size, xOffset, name, mf, arrowId);
+  m_arcArrows.Append(arrow);
+  return arrow;
+}
+
+/*
+ * Add arrowhead at a particular position in the arrowhead list.
+ */
+bool wxLineShape::AddArrowOrdered(wxArrowHead *arrow, wxList& referenceList, int end)
+{
+  wxNode *refNode = referenceList.First();
+  wxNode *currNode = m_arcArrows.First();
+  wxString targetName(arrow->GetName());
+  if (!refNode) return FALSE;
+
+  // 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.
+  wxArrowHead *refArrow = (wxArrowHead *)refNode->Data();
+  if (refArrow->GetName() == targetName)
+  {
+    m_arcArrows.Insert(arrow);
+    return TRUE;
+  }
+
+  while (refNode && currNode)
+  {
+    wxArrowHead *currArrow = (wxArrowHead *)currNode->Data();
+    refArrow = (wxArrowHead *)refNode->Data();
+
+    // Matching: advance current arrow pointer
+    if ((currArrow->GetArrowEnd() == end) &&
+        (currArrow->GetName() == refArrow->GetName()))
+    {
+      currNode = currNode->Next(); // Could be NULL now
+      if (currNode)
+        currArrow = (wxArrowHead *)currNode->Data();
+    }
+
+    // Check if we're at the correct position in the
+    // reference list
+    if (targetName == refArrow->GetName())
+    {
+      if (currNode)
+        m_arcArrows.Insert(currNode, arrow);
+      else
+        m_arcArrows.Append(arrow);
+      return TRUE;
+    }
+    refNode = refNode->Next();
+  }
+  m_arcArrows.Append(arrow);
+  return TRUE;
+}
+
+void wxLineShape::ClearArrowsAtPosition(int end)
+{
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    wxNode *next = node->Next();
+    switch (end)
+    {
+      case -1:
+      {
+        delete arrow;
+        delete node;
+        break;
+      }
+      case ARROW_POSITION_START:
+      {
+        if (arrow->GetArrowEnd() == ARROW_POSITION_START)
+        {
+          delete arrow;
+          delete node;
+        }
+        break;
+      }
+      case ARROW_POSITION_END:
+      {
+        if (arrow->GetArrowEnd() == ARROW_POSITION_END)
+        {
+          delete arrow;
+          delete node;
+        }
+        break;
+      }
+      case ARROW_POSITION_MIDDLE:
+      {
+        if (arrow->GetArrowEnd() == ARROW_POSITION_MIDDLE)
+        {
+          delete arrow;
+          delete node;
+        }
+        break;
+      }
+    }
+    node = next;
+  }
+}
+
+bool wxLineShape::ClearArrow(const wxString& name)
+{
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    if (arrow->GetName() == name)
+    {
+      delete arrow;
+      delete node;
+      return TRUE;
+    }
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+/*
+ * Finds an arrowhead at the given position (if -1, any position)
+ *
+ */
+
+wxArrowHead *wxLineShape::FindArrowHead(int position, const wxString& name)
+{
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    if (((position == -1) || (position == arrow->GetArrowEnd())) &&
+        (arrow->GetName() == name))
+      return arrow;
+    node = node->Next();
+  }
+  return NULL;
+}
+
+wxArrowHead *wxLineShape::FindArrowHead(long arrowId)
+{
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    if (arrowId == arrow->GetId())
+      return arrow;
+    node = node->Next();
+  }
+  return NULL;
+}
+
+/*
+ * Deletes an arrowhead at the given position (if -1, any position)
+ *
+ */
+
+bool wxLineShape::DeleteArrowHead(int position, const wxString& name)
+{
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    if (((position == -1) || (position == arrow->GetArrowEnd())) &&
+        (arrow->GetName() == name))
+    {
+      delete arrow;
+      delete node;
+      return TRUE;
+    }
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+// Overloaded DeleteArrowHead: pass arrowhead id.
+bool wxLineShape::DeleteArrowHead(long id)
+{
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrow = (wxArrowHead *)node->Data();
+    if (arrow->GetId() == id)
+    {
+      delete arrow;
+      delete node;
+      return TRUE;
+    }
+    node = node->Next();
+  }
+  return FALSE;
+}
+
+/*
+ * Calculate the minimum width a line
+ * occupies, for the purposes of drawing lines in tools.
+ *
+ */
+
+double wxLineShape::FindMinimumWidth()
+{
+  double minWidth = 0.0;
+  wxNode *node = m_arcArrows.First();
+  while (node)
+  {
+    wxArrowHead *arrowHead = (wxArrowHead *)node->Data();
+    minWidth += arrowHead->GetSize();
+    if (node->Next())
+      minWidth += arrowHead->GetSpacing();
+
+    node = node->Next();
+  }
+  // We have ABSOLUTE minimum now. So
+  // scale it to give it reasonable aesthetics
+  // when drawing with line.
+  if (minWidth > 0.0)
+    minWidth = (double)(minWidth * 1.4);
+  else
+    minWidth = 20.0;
+
+  SetEnds(0.0, 0.0, minWidth, 0.0);
+  Initialise();
+
+  return minWidth;
+}
+
+// Find which position we're talking about at this (x, y).
+// Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END
+int wxLineShape::FindLinePosition(double x, double y)
+{
+  double startX, startY, endX, endY;
+  GetEnds(&startX, &startY, &endX, &endY);
+
+  // Find distances from centre, start and end. The smallest wins.
+  double centreDistance = (double)(sqrt((x - m_xpos)*(x - m_xpos) + (y - m_ypos)*(y - m_ypos)));
+  double startDistance = (double)(sqrt((x - startX)*(x - startX) + (y - startY)*(y - startY)));
+  double endDistance = (double)(sqrt((x - endX)*(x - endX) + (y - endY)*(y - endY)));
+
+  if (centreDistance < startDistance && centreDistance < endDistance)
+    return ARROW_POSITION_MIDDLE;
+  else if (startDistance < endDistance)
+    return ARROW_POSITION_START;
+  else
+    return ARROW_POSITION_END;
+}
+
+// Set alignment flags
+void wxLineShape::SetAlignmentOrientation(bool isEnd, bool isHoriz)
+{
+  if (isEnd)
+  {
+    if (isHoriz && ((m_alignmentEnd & LINE_ALIGNMENT_HORIZ) != LINE_ALIGNMENT_HORIZ))
+      m_alignmentEnd |= LINE_ALIGNMENT_HORIZ;
+    else if (!isHoriz && ((m_alignmentEnd & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ))
+      m_alignmentEnd -= LINE_ALIGNMENT_HORIZ;
+  }
+  else
+  {
+    if (isHoriz && ((m_alignmentStart & LINE_ALIGNMENT_HORIZ) != LINE_ALIGNMENT_HORIZ))
+      m_alignmentStart |= LINE_ALIGNMENT_HORIZ;
+    else if (!isHoriz && ((m_alignmentStart & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ))
+      m_alignmentStart -= LINE_ALIGNMENT_HORIZ;
+  }
+}
+
+void wxLineShape::SetAlignmentType(bool isEnd, int alignType)
+{
+  if (isEnd)
+  {
+    if (alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE)
+    {
+      if ((m_alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE) != LINE_ALIGNMENT_TO_NEXT_HANDLE)
+        m_alignmentEnd |= LINE_ALIGNMENT_TO_NEXT_HANDLE;
+    }
+    else if ((m_alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE) == LINE_ALIGNMENT_TO_NEXT_HANDLE)
+      m_alignmentEnd -= LINE_ALIGNMENT_TO_NEXT_HANDLE;
+  }
+  else
+  {
+    if (alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE)
+    {
+      if ((m_alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE) != LINE_ALIGNMENT_TO_NEXT_HANDLE)
+        m_alignmentStart |= LINE_ALIGNMENT_TO_NEXT_HANDLE;
+    }
+    else if ((m_alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE) == LINE_ALIGNMENT_TO_NEXT_HANDLE)
+      m_alignmentStart -= LINE_ALIGNMENT_TO_NEXT_HANDLE;
+  }
+}
+
+bool wxLineShape::GetAlignmentOrientation(bool isEnd)
+{
+  if (isEnd)
+    return ((m_alignmentEnd & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ);
+  else
+    return ((m_alignmentStart & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ);
+}
+
+int wxLineShape::GetAlignmentType(bool isEnd)
+{
+  if (isEnd)
+    return (m_alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE);
+  else
+    return (m_alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE);
+}
+
+wxRealPoint *wxLineShape::GetNextControlPoint(wxShape *nodeObject)
+{
+  int n = m_lineControlPoints->Number();
+  int nn = 0;
+  if (m_to == nodeObject)
+  {
+    // 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;
+  wxNode *node = m_lineControlPoints->Nth(nn);
+  if (node)
+  {
+    return (wxRealPoint *)node->Data();
+  }
+  else
+    return FALSE;
+}
+
+/*
+ * Arrowhead
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxArrowHead, wxObject)
+
+wxArrowHead::wxArrowHead(WXTYPE type, int end, double size, double dist, const wxString& name,
+                     wxPseudoMetaFile *mf, long arrowId)
+{
+  m_arrowType = type; m_arrowEnd = end; m_arrowSize = size;
+  m_xOffset = dist;
+  m_yOffset = 0.0;
+  m_spacing = 5.0;
+
+  m_arrowName = name;
+  m_metaFile = mf;
+  m_id = arrowId;
+  if (m_id == -1)
+    m_id = wxNewId();
+}
+
+wxArrowHead::wxArrowHead(wxArrowHead& toCopy)
+{
+  m_arrowType = toCopy.m_arrowType; m_arrowEnd = toCopy.GetArrowEnd();
+  m_arrowSize = toCopy.m_arrowSize;
+  m_xOffset = toCopy.m_xOffset;
+  m_yOffset = toCopy.m_yOffset;
+  m_spacing = toCopy.m_spacing;
+  m_arrowName = toCopy.m_arrowName ;
+  if (toCopy.m_metaFile)
+    m_metaFile = new wxPseudoMetaFile(*(toCopy.m_metaFile));
+  else
+    m_metaFile = NULL;
+  m_id = wxNewId();
+}
+
+wxArrowHead::~wxArrowHead()
+{
+  if (m_metaFile) delete m_metaFile;
+}
+
+void wxArrowHead::SetSize(double size)
+{
+  m_arrowSize = size;
+  if ((m_arrowType == ARROW_METAFILE) && m_metaFile)
+  {
+    double oldWidth = m_metaFile->m_width;
+    if (oldWidth == 0.0)
+      return;
+
+    double scale = (double)(size/oldWidth);
+    if (scale != 1.0)
+      m_metaFile->Scale(scale, scale);
+  }
+}
+
+// Can override this to create a different class of label shape
+wxLabelShape* wxLineShape::OnCreateLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h)
+{
+    return new wxLabelShape(parent, region, w, h);
+}
+
+/*
+ * Label object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxLabelShape, wxRectangleShape)
+
+wxLabelShape::wxLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h):wxRectangleShape(w, h)
+{
+  m_lineShape = parent;
+  m_shapeRegion = region;
+  SetPen(wxThePenList->FindOrCreatePen(wxColour(0, 0, 0), 1, wxDOT));
+}
+
+wxLabelShape::~wxLabelShape()
+{
+}
+
+void wxLabelShape::OnDraw(wxDC& dc)
+{
+  if (m_lineShape && !m_lineShape->GetDrawHandles())
+    return;
+
+    double x1 = (double)(m_xpos - m_width/2.0);
+    double y1 = (double)(m_ypos - m_height/2.0);
+
+    if (m_pen)
+    {
+      if (m_pen->GetWidth() == 0)
+        dc.SetPen(* g_oglTransparentPen);
+      else
+        dc.SetPen(* m_pen);
+    }
+    dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+    if (m_cornerRadius > 0.0)
+      dc.DrawRoundedRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
+    else
+      dc.DrawRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height));
+}
+
+void wxLabelShape::OnDrawContents(wxDC& dc)
+{
+}
+
+void wxLabelShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
+{
+  wxRectangleShape::OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxLabelShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
+{
+  wxRectangleShape::OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxLabelShape::OnEndDragLeft(double x, double y, int keys, int attachment)
+{
+  wxRectangleShape::OnEndDragLeft(x, y, keys, attachment);
+}
+
+bool wxLabelShape::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
+{
+    return m_lineShape->OnLabelMovePre(dc, this, x, y, old_x, old_y, display);
+}
+
+bool wxLineShape::OnLabelMovePre(wxDC& dc, wxLabelShape* labelShape, double x, double y, double old_x, double old_y, bool display)
+{
+  labelShape->m_shapeRegion->SetSize(labelShape->GetWidth(), labelShape->GetHeight());
+
+  // Find position in line's region list
+  int i = 0;
+  wxNode *node = GetRegions().First();
+  while (node)
+  {
+    if (labelShape->m_shapeRegion == (wxShapeRegion *)node->Data())
+      node = NULL;
+    else
+    {
+      node = node->Next();
+      i ++;
+    }
+  }
+  double xx, yy;
+  GetLabelPosition(i, &xx, &yy);
+  // Set the region's offset, relative to the default position for
+  // each region.
+  labelShape->m_shapeRegion->SetPosition((double)(x - xx), (double)(y - yy));
+
+  labelShape->SetX(x);
+  labelShape->SetY(y);
+
+  // Need to reformat to fit region.
+  if (labelShape->m_shapeRegion->GetText())
+  {
+
+    wxString s(labelShape->m_shapeRegion->GetText());
+    labelShape->FormatText(dc, s, i);
+    DrawRegion(dc, labelShape->m_shapeRegion, xx, yy);
+  }
+  return TRUE;
+}
+
+// Divert left and right clicks to line object
+void wxLabelShape::OnLeftClick(double x, double y, int keys, int attachment)
+{
+  m_lineShape->GetEventHandler()->OnLeftClick(x, y, keys, attachment);
+}
+
+void wxLabelShape::OnRightClick(double x, double y, int keys, int attachment)
+{
+  m_lineShape->GetEventHandler()->OnRightClick(x, y, keys, attachment);
+}
+
diff --git a/src/ogl/makefile.b32 b/src/ogl/makefile.b32
new file mode 100644 (file)
index 0000000..0b5356a
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# File:                makefile.b32
+# Author:      Julian Smart
+# Created:     1999
+# Updated:     
+# Copyright:
+#
+# Makefile : Builds OGL library for 32-bit BC++
+
+WXDIR = $(WXWIN)
+
+LIBTARGET=$(WXDIR)\lib\ogl.lib
+
+OBJECTS = basic.obj basic2.obj canvas.obj ogldiag.obj lines.obj misc.obj divided.obj constrnt.obj\
+ composit.obj drawn.obj bmpshape.obj mfutils.obj
+
+!include $(WXDIR)\src\makelib.b32
+
diff --git a/src/ogl/makefile.bcc b/src/ogl/makefile.bcc
new file mode 100644 (file)
index 0000000..94177e6
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# File:                makefile.bcc
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+#
+# Builds OGL library for BC++, 16-bit
+
+!if "$(WXWIN)" == ""
+!error You must define the WXWIN variable in autoexec.bat, e.g. WXWIN=c:\wx
+!endif
+
+WXDIR = $(WXWIN)
+
+LIBTARGET=$(WXDIR)\lib\ogl.lib
+
+OBJECTS = basic.obj basic2.obj canvas.obj ogldiag.obj lines.obj misc.obj divided.obj constrnt.obj\
+ composit.obj drawn.obj bmpshape.obj mfutils.obj
+
+!include $(WXDIR)\src\makelib.bcc
+
diff --git a/src/ogl/makefile.dos b/src/ogl/makefile.dos
new file mode 100644 (file)
index 0000000..f5cfb71
--- /dev/null
@@ -0,0 +1,159 @@
+#
+# File:                makefile.dos
+# Author:      Julian Smart
+# Created:     1993
+# Updated:     
+# Copyright:   (c) 1993, AIAI, University of Edinburgh
+#
+# "%W% %G%"
+#
+# Makefile: Builds object graphics library (DOS).
+# Use FINAL=1 argument to nmake to build final version with no debugging
+# info
+
+# Set WXDIR for your system
+WXDIR = $(WXWIN)
+
+!include $(WXDIR)\src\makemsc.env
+
+OGLDIR = $(WXDIR)\src\ogl
+THISDIR = $(OGLDIR)\src
+DOCDIR = $(WXDIR)\docs\latex\ogl
+
+GRAPHICSLIB = $(WXDIR)\lib\ogl.lib
+INC = /I$(WXDIR)\include
+
+# Normally set OPTIONS =
+# to disable PROLOGIO-dependent code
+OPTIONS = -DPROLOGIO
+
+OBJECTS = basic.obj basic2.obj canvas.obj ogldiag.obj lines.obj misc.obj divided.obj constrnt.obj\
+ composit.obj drawn.obj bitmap.obj mfutils.obj
+
+all:    $(GRAPHICSLIB)
+
+wx:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.dos $(WXLIB) FINAL=$(FINAL)
+        cd $(THISDIR)
+
+$(GRAPHICSLIB):      $(OBJECTS)
+        erase $(GRAPHICSLIB)
+        lib  /PAGESIZE:128 @<<
+$(GRAPHICSLIB)
+y
+$(OBJECTS)
+nul
+;
+<<
+
+# NOTE: This causes a floating point stack error when optimized,
+# so DON'T optimize!
+
+basic.obj:      basic.$(SRCSUFF) basic.h lines.h misc.h canvas.h
+        cl @<<
+$(CPPFLAGS) /Od /c /Tp $*.$(SRCSUFF)
+<<
+
+basic2.obj:      basic2.$(SRCSUFF) basic.h lines.h misc.h canvas.h
+        cl @<<
+$(CPPFLAGS) /Od /c /Tp $*.$(SRCSUFF)
+<<
+
+canvas.obj:      canvas.$(SRCSUFF) basic.h misc.h canvas.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+ogldiag.obj:      ogldiag.$(SRCSUFF) ogldiag.h canvas.h basic.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+lines.obj:      lines.$(SRCSUFF) basic.h misc.h canvas.h lines.h basicp.h linesp.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+misc.obj:      misc.$(SRCSUFF) basic.h misc.h constrnt.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+divided.obj:      divided.$(SRCSUFF) basic.h misc.h canvas.h divided.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+constrnt.obj:     constrnt.$(SRCSUFF) basic.h constrnt.h 
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+composit.obj:      composit.$(SRCSUFF) basic.h misc.h canvas.h constrnt.h composit.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+drawn.obj:      drawn.$(SRCSUFF) basic.h misc.h canvas.h drawn.h drawnp.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+bitmap.obj:      bitmap.$(SRCSUFF) basic.h misc.h canvas.h bitmap.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+mfutils.obj:      mfutils.$(SRCSUFF) mfutils.h
+        cl @<<
+$(CPPFLAGS) /c /Tp $*.$(SRCSUFF)
+<<
+
+# Making documents
+docs:   hlp
+hlp: $(DOCDIR)/ogl.hlp
+hlp32: $(DOCDIR)/hlp32/ogl.hlp
+rtf:    $(DOCDIR)/ogl.rtf
+
+$(DOCDIR)/ogl.hlp:         $(DOCDIR)/ogl.rtf $(DOCDIR)/ogl.hpj
+        cd $(DOCDIR)
+        -erase ogl.ph
+        hc ogl
+        cd $(THISDIR)
+
+$(DOCDIR)/hlp32/ogl.hlp:         $(DOCDIR)/hlp32/ogl.rtf $(DOCDIR)/hlp32/ogl.hpj
+        cd $(DOCDIR)/hlp32
+        -erase ogl.ph
+        start /w hcw /c /e ogl.hpj
+        cd $(THISDIR)
+
+$(DOCDIR)/ogl.rtf:         $(DOCDIR)/classes.tex $(DOCDIR)/intro.tex $(DOCDIR)/ogl.tex
+        cd $(DOCDIR)
+        start /w tex2rtf $(DOCDIR)/ogl.tex $(DOCDIR)/ogl.rtf -twice -winhelp
+        cd $(THISDIR)
+
+$(DOCDIR)/hlp32/ogl.rtf:         $(DOCDIR)/classes.tex $(DOCDIR)/intro.tex $(DOCDIR)/ogl.tex
+        cd $(DOCDIR)
+        start /w tex2rtf $(DOCDIR)/ogl.tex $(DOCDIR)/hlp32/ogl.rtf -twice -winhelp -macros $(DOCDIR)/t2rtf32.ini
+        cd $(THISDIR)
+
+wordrtf: 
+        cd $(DOCDIR)
+        -wx /W tex2rtf $(DOCDIR)/ogl.tex $(DOCDIR)/ogl.rtf -twice -rtf
+        cd $(THISDIR)
+
+clean:
+        -erase *.obj
+        -erase *.sbr
+        -erase *.exe
+        -erase *.res
+        -erase *.map
+        -erase *.pdb
+        -erase *.lib
+        -erase ..\lib\*.lib
+
+wxclean:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.dos clean
+        cd $(THISDIR)
diff --git a/src/ogl/makefile.g95 b/src/ogl/makefile.g95
new file mode 100644 (file)
index 0000000..addf441
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# File:         makefile.g95
+# Author:       Julian Smart
+# Created:      1999
+# Updated:
+# Copyright:    (c) Julian Smart, 1999
+#
+# Makefile for wxWindows OGL library Cygwin/Mingw32).
+
+WXDIR = ../..
+
+LIBTARGET=$(WXDIR)/lib/libogl.a
+OBJECTS = basic.o basic2.o canvas.o ogldiag.o lines.o misc.o divided.o constrnt.o\
+ composit.o drawn.o bmpshape.o mfutils.o
+
+include $(WXDIR)/src/makelib.g95
+
diff --git a/src/ogl/makefile.unx b/src/ogl/makefile.unx
new file mode 100644 (file)
index 0000000..5141fb6
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# File:                makefile.unx
+# Author:      Julian Smart
+# Created:     1998
+# Updated:     
+# Copyright:   (c) 1998
+#
+#
+# Makefile for OGL library, Unix
+
+include ../../src/make.env
+
+OGLLIB=$(WXDIR)/lib/libogl$(GUISUFFIX).a
+
+LIB_CPP_SRC=\
+\
+  basic.o\
+  basic2.o\
+  canvas.o\
+  ogldiag.o\
+  lines.o\
+  misc.o\
+  divided.o\
+  constrnt.o\
+  composit.o\
+  drawn.o\
+  bmpshape.o\
+  mfutils.o
+
+all:    $(OGLLIB)
+
+# Define library objects
+OBJECTS=\
+ $(LIB_CPP_SRC:.cpp=.o)
+
+$(OGLLIB) : $(OBJECTS)
+       ar $(AROPTIONS) $@ $(OBJECTS)
+       $(RANLIB) $@
+
+clean:
+       rm -f $(OBJECTS) $(OGLLIB)
+
diff --git a/src/ogl/makefile.vc b/src/ogl/makefile.vc
new file mode 100644 (file)
index 0000000..e57f365
--- /dev/null
@@ -0,0 +1,195 @@
+
+# File:                makefile.vc
+# Author:      Julian Smart
+# Created:     1993
+# Updated:
+# Copyright:   (c) 1993, AIAI, University of Edinburgh
+#
+# "%W% %G%"
+#
+# Makefile : Builds OGL classes library (MS VC++).
+# Use FINAL=1 argument to nmake to build final version with no debugging
+# info
+
+
+# Set WXDIR for your system
+WXDIR = $(WXWIN)
+OGLDIR = $(WXDIR)\src\ogl
+THISDIR = $(WXDIR)\src\ogl
+EXTRAFLAGS=/DPROLOGIO=1
+DOCDIR=$(WXDIR)\docs
+LOCALDOCDIR=$(WXDIR)\docs\latex\ogl
+
+!include $(WXDIR)\src\makevc.env
+
+PROGRAM=test
+
+OBJECTS = $(D)\basic.obj $(D)\basic2.obj $(D)\canvas.obj $(D)\ogldiag.obj $(D)\lines.obj $(D)\misc.obj $(D)\divided.obj $(D)\constrnt.obj\
+ $(D)\composit.obj $(D)\drawn.obj $(D)\bmpshape.obj $(D)\mfutils.obj
+
+LIBTARGET=$(WXDIR)\lib\ogl$(LIBEXT).lib
+
+all:    $(D) $(LIBTARGET)
+
+$(PROGRAM):    $(PROGRAM).exe
+
+$(D) :
+       mkdir $(D)
+
+wx:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.vc FINAL=$(FINAL)
+        cd $(THISDIR)
+
+wxclean:
+        cd $(WXDIR)\src\msw
+        nmake -f makefile.vc clean
+        cd $(THISDIR)
+
+$(LIBTARGET): $(OBJECTS)
+       -erase $(LIBTARGET)
+       $(implib) @<<
+-out:$(LIBTARGET)
+-machine:$(CPU)
+$(OBJECTS)
+<<
+
+# NOTE: This causes a floating point stack error when optimized,
+# so DON'T optimize!
+$(D)\basic.obj:      basic.$(SRCSUFF) basic.h lines.h misc.h canvas.h
+        cl @<<
+$(CPPFLAGS) /Od /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\basic2.obj:      basic2.$(SRCSUFF) basic.h lines.h misc.h canvas.h
+        cl @<<
+$(CPPFLAGS) /Od /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\canvas.obj:      canvas.$(SRCSUFF) basic.h misc.h canvas.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\ogldiag.obj:      ogldiag.$(SRCSUFF) ogldiag.h canvas.h basic.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\lines.obj:      lines.$(SRCSUFF) basic.h misc.h canvas.h lines.h basicp.h linesp.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\misc.obj:      misc.$(SRCSUFF) basic.h misc.h constrnt.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\divided.obj:      divided.$(SRCSUFF) basic.h misc.h canvas.h divided.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\constrnt.obj:     constrnt.$(SRCSUFF) basic.h constrnt.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\composit.obj:      composit.$(SRCSUFF) basic.h misc.h canvas.h constrnt.h composit.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\drawn.obj:      drawn.$(SRCSUFF) basic.h misc.h canvas.h drawn.h drawnp.h basicp.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\bmpshape.obj:      bmpshape.$(SRCSUFF) basic.h misc.h canvas.h bmpshape.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+$(D)\mfutils.obj:      mfutils.$(SRCSUFF) mfutils.h
+        cl @<<
+$(CPPFLAGS) /c /Fo$@ /Tp $(*B).$(SRCSUFF)
+<<
+
+clean:
+        -erase $(D)\*.obj
+        -erase *.sbr
+        -erase *.exe
+        -erase *.res
+        -erase *.map
+        -erase *.pdb
+        -erase $(LIBTARGET)
+
+DOCSOURCES=$(LOCALDOCDIR)\ogl.tex \
+ $(LOCALDOCDIR)\bugs.tex $(LOCALDOCDIR)\changes.tex\
+ $(LOCALDOCDIR)\classes.tex $(LOCALDOCDIR)\intro.tex\
+ $(LOCALDOCDIR)\topics.tex $(LOCALDOCDIR)\sample.tex
+
+html: $(DOCDIR)\html\ogl\ogl.htm
+htmlhelp: $(DOCDIR)\html\ogl\ogl.chm
+hlp: $(DOCDIR)\winhelp\ogl.hlp
+pdfrtf: $(DOCDIR)\pdf\ogl.rtf
+ps: $(DOCDIR)\ps\ogl.ps
+
+touchmanual:
+       touch $(LOCALDOCDIR)\ogl.tex
+
+
+$(DOCDIR)\winhelp\ogl.hlp:         $(LOCALDOCDIR)\ogl.rtf $(LOCALDOCDIR)\ogl.hpj
+        cd $(LOCALDOCDIR)
+        -erase ogl.ph
+        hc ogl
+        move ogl.hlp $(DOCDIR)\winhelp\ogl.hlp
+        move ogl.cnt $(DOCDIR)\winhelp\ogl.cnt
+        cd $(THISDIR)
+
+$(LOCALDOCDIR)\ogl.rtf: $(DOCSOURCES)
+        cd $(LOCALDOCDIR)
+        -start $(WAITFLAG) tex2rtf $(LOCALDOCDIR)\ogl.tex $(LOCALDOCDIR)\ogl.rtf -twice -winhelp
+        cd $(THISDIR)
+
+$(DOCDIR)\pdf\ogl.rtf: $(DOCSOURCES)
+        cd $(LOCALDOCDIR)
+        -copy *.bmp $(DOCDIR)\pdf
+        -start $(WAITFLAG) tex2rtf $(LOCALDOCDIR)\ogl.tex $(DOCDIR)\pdf\ogl.rtf -twice -rtf
+        cd $(THISDIR)
+
+$(DOCDIR)\html\ogl\ogl.htm:         $(DOCSOURCES)
+        cd $(LOCALDOCDIR)
+        -mkdir $(DOCDIR)\html\ogl
+        copy *.gif $(DOCDIR)\html\ogl
+        -start $(WAITFLAG) tex2rtf $(LOCALDOCDIR)\ogl.tex $(DOCDIR)\html\ogl\ogl.htm -twice -html
+        -erase $(DOCDIR)\html\ogl\*.con
+       -erase *.con
+        -erase $(DOCDIR)\html\ogl\*.ref
+        cd $(THISDIR)
+
+
+$(DOCDIR)\html\ogl\ogl.chm: $(DOCDIR)\html\ogl\ogl.htm $(DOCDIR)\html\ogl\ogl.hhp
+       cd $(DOCDIR)\html\ogl
+       -hhc ogl.hhp
+       cd $(THISDIR)
+
+
+$(LOCALDOCDIR)\ogl.dvi:        $(DOCSOURCES)
+       cd $(LOCALDOCDIR)
+        -latex ogl
+        -latex ogl
+        -makeindx ogl
+        -bibtex ogl
+        -latex ogl
+        -latex ogl
+        cd $(THISDIR)
+
+$(WXDIR)\docs\ps\ogl.ps:       $(LOCALDOCDIR)\ogl.dvi
+       cd $(LOCALDOCDIR)
+        -dvips32 -o ogl.ps ogl
+        move ogl.ps $(WXDIR)\docs\ps\ogl.ps
+        cd $(THISDIR)
+
+
diff --git a/src/ogl/makefile.wat b/src/ogl/makefile.wat
new file mode 100644 (file)
index 0000000..a78d986
--- /dev/null
@@ -0,0 +1,26 @@
+# Objects makefile
+
+WXDIR = ..\..
+
+!include $(WXDIR)\src\makewat.env
+
+EXTRACPPFLAGS=/DPROLOGIO
+
+OGLLIB = $(WXDIR)\lib\ogl.lib
+THISDIR = $(WXDIR)\src\ogl
+
+NAME = ogl
+LNK = $(name).lnk
+
+IFLAGS      = -i=$(WXINC) -i=$(WXBASEINC)
+
+OBJECTS =  basic.obj basic2.obj canvas.obj lines.obj misc.obj divided.obj constrnt.obj composit.obj drawn.obj bitmap.obj
+
+all: $(OGLLIB)
+
+$(OGLLIB): $(OBJECTS)
+       *wlib /b /c /n /P=256 $(OGLLIB) $(OBJECTS)
+
+clean:   .SYMBOLIC
+    -erase *.obj *.bak *.err *.pch $(OGLLIB) *.lbc
+
diff --git a/src/ogl/mfutils.cpp b/src/ogl/mfutils.cpp
new file mode 100644 (file)
index 0000000..b268483
--- /dev/null
@@ -0,0 +1,1085 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        mfutils.cpp
+// Purpose:     Metafile utillities
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "mfutils.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/metafile.h>
+#include <wx/utils.h>
+
+#include <wx/ogl/mfutils.h>
+#include <stdio.h>
+
+static char hexArray[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+  'C', 'D', 'E', 'F' };
+
+static void DecToHex(int dec, char *buf)
+{
+  int firstDigit = (int)(dec/16.0);
+  int secondDigit = (int)(dec - (firstDigit*16.0));
+  buf[0] = hexArray[firstDigit];
+  buf[1] = hexArray[secondDigit];
+  buf[2] = 0;
+}
+// 16-bit unsigned integer
+static unsigned int getshort(FILE *fp)
+{
+  int c, c1;
+  c = getc(fp);  c1 = getc(fp);
+  unsigned int res = ((unsigned int) c) + (((unsigned int) c1) << 8);
+  return res;
+}
+
+// 16-bit signed integer
+static int getsignedshort(FILE *fp)
+{
+  int c, c1;
+  c = getc(fp);  c1 = getc(fp);
+  int testRes = ((unsigned int) c) + (((unsigned int) c1) << 8);
+  unsigned long res1 = ((unsigned int) c) + (((unsigned int) c1) << 8);
+  int res = 0;
+  if (res1 > 32767)
+    res = (int)(res1 - 65536);
+  else
+    res = (int)(res1);
+  return res;
+}
+
+// 32-bit integer
+static long getint(FILE *fp)
+{
+  int c, c1, c2, c3;
+  c = getc(fp);  c1 = getc(fp);  c2 = getc(fp);  c3 = getc(fp);
+  long res = (long)((long) c) +
+         (((long) c1) << 8) + 
+        (((long) c2) << 16) +
+        (((long) c3) << 24);
+  return res;
+}
+
+
+/* Placeable metafile header
+struct mfPLACEABLEHEADER {
+       DWORD   key;         // 32-bit
+       HANDLE  hmf;         // 16-bit
+       RECT    bbox;        // 4x16 bit
+       WORD    inch;        // 16-bit
+       DWORD   reserved;    // 32-bit
+       WORD    checksum;    // 16-bit
+};
+*/
+
+wxMetaRecord::~wxMetaRecord(void)
+{
+  if (points) delete[] points;
+  if (stringParam) delete[] stringParam;
+}
+
+wxXMetaFile::wxXMetaFile(char *file)
+{
+  ok = FALSE;
+  top = 0.0;
+  bottom = 0.0;
+  left = 0.0;
+  right = 0.0;
+  
+  if (file)
+    ok = ReadFile(file);
+}
+
+/*
+  Handle table       gdiObjects
+  ------------       ----------
+  [0]                  wxPen
+  [1]----param2---     wxBrush
+  [2]             |    wxFont
+  [3]             | -> wxPen
+  
+ The handle table works as follows.
+ When a GDI object is created whilst reading in the
+ metafile, the (e.g.) createpen record is added to the
+ first free entry in the handle table. The createpen
+ record's param1 is a pointer to the actual wxPen, and
+ its param2 is the index into the gdiObjects list, which only
+ grows and never shrinks (unlike the handle table.)
+ When SelectObject(index) is found, the index in the file
+ refers to the position in the handle table. BUT we then
+ set param2 to be the position of the wxPen in gdiObjects,
+ i.e. to param2 of the CreatePen record, itself found in
+ the handle table.
+
+ When an object is deleted, the entry in the handletable is
+ NULLed but the gdiObjects entry is not removed (no point, and
+ allows us to create all GDI objects in advance of playing the
+ metafile).
+*/
+
+static wxMetaRecord *HandleTable[100];
+static int HandleTableSize = 0;
+
+void DeleteMetaRecordHandle(int index)
+{
+  HandleTable[index] = NULL;
+}
+
+int AddMetaRecordHandle(wxMetaRecord *record)
+{
+  for (int i = 0; i < HandleTableSize; i++)
+    if (!HandleTable[i])
+    {
+      HandleTable[i] = record;
+      return i;
+    }
+  // No free spaces in table, so append.
+  
+  HandleTable[HandleTableSize] = record;
+  HandleTableSize ++;
+  return (HandleTableSize - 1);
+}
+
+bool wxXMetaFile::ReadFile(char *file)
+{
+  HandleTableSize = 0;
+  
+  FILE *handle = fopen(file, "rb");
+  if (!handle) return FALSE;
+
+  // Read placeable metafile header, if any
+  long key = getint(handle);
+    
+  if (key == (long) 0x9AC6CDD7)
+  {
+    long hmf = getshort(handle);
+    int iLeft, iTop, iRight, iBottom;
+    iLeft = getsignedshort(handle);
+    iTop = getsignedshort(handle);
+    iRight = getsignedshort(handle);
+    iBottom = getsignedshort(handle);
+
+    left = (double)iLeft;
+    top = (double)iTop;
+    right = (double)iRight;
+    bottom = (double)iBottom;
+
+    int inch = getshort(handle);
+    long reserved = getint(handle);
+    int checksum = getshort(handle);
+/*
+      double widthInUnits = (double)right - left;
+      double heightInUnits = (double)bottom - top;
+      *width = (int)((widthInUnits*1440.0)/inch);
+      *height = (int)((heightInUnits*1440.0)/inch);
+*/
+  }
+  else rewind(handle);
+
+  // Read METAHEADER
+  int mtType = getshort(handle);
+
+  if (mtType != 1 && mtType != 2)
+  {
+    fclose(handle);
+    return FALSE;
+  }
+
+  int mtHeaderSize = getshort(handle);
+  int mtVersion = getshort(handle);
+
+  if (mtVersion != 0x0300 && mtVersion != 0x0100)
+  {
+    fclose(handle);
+    return FALSE;
+  }
+  
+  long mtSize = getint(handle);
+  int mtNoObjects = getshort(handle);
+  long mtMaxRecord = getint(handle);
+  int mtNoParameters = getshort(handle);
+
+  while (!feof(handle))
+  {
+    long rdSize = getint(handle);      // 4 bytes
+    int rdFunction = getshort(handle); // 2 bytes
+    if (feof(handle))
+      break;
+
+    switch (rdFunction)
+    {
+      case META_SETBKCOLOR:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETBKCOLOR);
+        long colorref = getint(handle); // COLORREF
+        rec->param1 = GetRValue(colorref);
+        rec->param2 = GetGValue(colorref);
+        rec->param3 = GetBValue(colorref);
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_SETBKMODE:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETBKMODE);
+        rec->param1 = getshort(handle); // Background mode
+        if (rec->param1 == OPAQUE) rec->param1 = wxSOLID;
+        else rec->param1 = wxTRANSPARENT;
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_SETMAPMODE:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETMAPMODE);
+        rec->param1 = getshort(handle);
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_SETROP2:
+//      case META_SETRELABS:
+//      case META_SETPOLYFILLMODE:
+//      case META_SETSTRETCHBLTMODE:
+//      case META_SETTEXTCHAREXTRA:
+      case META_SETTEXTCOLOR:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETTEXTCOLOR);
+        long colorref = getint(handle); // COLORREF
+        rec->param1 = GetRValue(colorref);
+        rec->param2 = GetGValue(colorref);
+        rec->param3 = GetBValue(colorref);
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_SETTEXTJUSTIFICATION:
+      case META_SETWINDOWORG:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETWINDOWORG);
+        rec->param2 = getshort(handle);
+        rec->param1 = getshort(handle);
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_SETWINDOWEXT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETWINDOWEXT);
+        rec->param2 = getshort(handle);
+        rec->param1 = getshort(handle);
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_SETVIEWPORTORG:
+//      case META_SETVIEWPORTEXT:
+//      case META_OFFSETWINDOWORG:
+//      case META_SCALEWINDOWEXT:
+//      case META_OFFSETVIEWPORTORG:
+//      case META_SCALEVIEWPORTEXT:
+      case META_LINETO:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_LINETO);
+        rec->param1 = getshort(handle); // x1
+        rec->param2 = getshort(handle); // y1
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_MOVETO:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_MOVETO);
+        rec->param1 = getshort(handle); // x1
+        rec->param2 = getshort(handle); // y1
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_EXCLUDECLIPRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_EXCLUDECLIPRECT);
+        rec->param4 = getshort(handle); // y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_INTERSECTCLIPRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_INTERSECTCLIPRECT);
+        rec->param4 = getshort(handle); // y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_ARC: // DO!!!
+      case META_ELLIPSE:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_ELLIPSE);
+        rec->param4 = getshort(handle); // y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_FLOODFILL:
+//      case META_PIE: // DO!!!
+      case META_RECTANGLE:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_RECTANGLE);
+        rec->param4 = getshort(handle); // y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_ROUNDRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_ROUNDRECT);
+        rec->param6 = getshort(handle); // width
+        rec->param5 = getshort(handle); // height
+        rec->param4 = getshort(handle); // y2
+        rec->param3 = getshort(handle); // x2
+        rec->param2 = getshort(handle); // y1
+        rec->param1 = getshort(handle); // x1
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_PATBLT:
+//      case META_SAVEDC:
+      case META_SETPIXEL:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SETPIXEL);
+        rec->param1 = getshort(handle); // x1
+        rec->param2 = getshort(handle); // y1
+        rec->param3 = getint(handle);   // COLORREF
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_OFFSETCLIPRGN:
+      case META_TEXTOUT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_TEXTOUT);
+        int count = getshort(handle);
+        rec->stringParam = new char[count+1];
+        fread((void *)rec->stringParam, sizeof(char), count, handle);
+        rec->stringParam[count] = 0;
+        rec->param2 = getshort(handle); // Y
+        rec->param1 = getshort(handle); // X
+        metaRecords.Append(rec);
+        break;
+      }
+/*
+      case META_EXTTEXTOUT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_EXTTEXTOUT);
+        int cellSpacing = getshort(handle);
+        int count = getshort(handle);
+        rec->stringParam = new char[count+1];
+        fread((void *)rec->stringParam, sizeof(char), count, handle);
+        rec->stringParam[count] = 0;
+        // Rectangle
+        int rectY2 = getshort(handle);
+        int rectX2 = getshort(handle);
+        int rectY1 = getshort(handle);
+        int rectX1 = getshort(handle);
+        int rectType = getshort(handle);
+        rec->param2 = getshort(handle); // Y
+        rec->param1 = getshort(handle); // X
+        metaRecords.Append(rec);
+        break;
+      }
+*/
+//      case META_BITBLT:
+//      case META_STRETCHBLT:
+      case META_POLYGON:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_POLYGON);
+        rec->param1 = getshort(handle);
+        rec->points = new wxRealPoint[(int)rec->param1];
+        for (int i = 0; i < rec->param1; i++)
+        {
+          rec->points[i].x = getshort(handle);
+          rec->points[i].y = getshort(handle);
+        }
+        
+        metaRecords.Append(rec);
+        break;
+      }
+      case META_POLYLINE:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_POLYLINE);
+        rec->param1 = (long)getshort(handle);
+        rec->points = new wxRealPoint[(int)rec->param1];
+        for (int i = 0; i < rec->param1; i++)
+        {
+          rec->points[i].x = getshort(handle);
+          rec->points[i].y = getshort(handle);
+        }
+        
+        metaRecords.Append(rec);
+        break;
+      }
+//      case META_ESCAPE:
+//      case META_RESTOREDC:
+//      case META_FILLREGION:
+//      case META_FRAMEREGION:
+//      case META_INVERTREGION:
+//      case META_PAINTREGION:
+//      case META_SELECTCLIPREGION: // DO THIS!
+      case META_SELECTOBJECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_SELECTOBJECT);
+        rec->param1 = (long)getshort(handle); // Position of object in gdiObjects list
+        metaRecords.Append(rec);
+        // param2 gives the index into gdiObjects, which is different from
+        // the index into the handle table.
+        rec->param2 = HandleTable[(int)rec->param1]->param2;
+        break;
+      }
+//      case META_SETTEXTALIGN:
+//      case META_DRAWTEXT:
+//      case META_CHORD:
+//      case META_SETMAPPERFLAGS:
+//      case META_EXTTEXTOUT:
+//      case META_SETDIBTODEV:
+//      case META_SELECTPALETTE:
+//      case META_REALIZEPALETTE:
+//      case META_ANIMATEPALETTE:
+//      case META_SETPALENTRIES:
+//      case META_POLYPOLYGON:
+//      case META_RESIZEPALETTE:
+//      case META_DIBBITBLT:
+//      case META_DIBSTRETCHBLT:
+      case META_DIBCREATEPATTERNBRUSH:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_DIBCREATEPATTERNBRUSH);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+//      case META_STRETCHDIB:
+//      case META_EXTFLOODFILL:
+//      case META_RESETDC:
+//      case META_STARTDOC:
+//      case META_STARTPAGE:
+//      case META_ENDPAGE:
+//      case META_ABORTDOC:
+//      case META_ENDDOC:
+      case META_DELETEOBJECT:
+      {
+        int index = getshort(handle);
+        DeleteMetaRecordHandle(index);
+        break;
+      }
+      case META_CREATEPALETTE:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEPALETTE);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEBRUSH:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEBRUSH);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEPATTERNBRUSH:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEPATTERNBRUSH);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEPENINDIRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEPENINDIRECT);
+        int msStyle = getshort(handle); // Style: 2 bytes
+        int x = getshort(handle); // X:     2 bytes
+        int y = getshort(handle); // Y:     2 bytes
+        long colorref = getint(handle); // COLORREF 4 bytes
+
+        int style;
+        if (msStyle == PS_DOT)
+          style = wxDOT;
+        else if (msStyle == PS_DASH)
+          style = wxSHORT_DASH;
+        else if (msStyle == PS_NULL)
+          style = wxTRANSPARENT;
+        else style = wxSOLID;
+
+        wxColour colour(GetRValue(colorref), GetGValue(colorref), GetBValue(colorref));
+        rec->param1 = (long)wxThePenList->FindOrCreatePen(colour, x, style);
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        
+        // For some reason, the size of this record is sometimes 9 words!!!
+        // instead of the usual 8. So read 2 characters extra.
+        if (rdSize == 9)
+        {
+          (void) getshort(handle);
+        }
+        break;
+      }
+      case META_CREATEFONTINDIRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEFONTINDIRECT);
+        int lfHeight = getshort(handle);    // 2 bytes
+        int lfWidth = getshort(handle);     // 2 bytes
+        int lfEsc = getshort(handle);       // 2 bytes
+        int lfOrient = getshort(handle);    // 2 bytes
+        int lfWeight = getshort(handle);    // 2 bytes
+        char lfItalic = getc(handle);       // 1 byte
+        char lfUnderline = getc(handle);    // 1 byte
+        char lfStrikeout = getc(handle);    // 1 byte
+        char lfCharSet = getc(handle);      // 1 byte
+        char lfOutPrecision = getc(handle); // 1 byte
+        char lfClipPrecision = getc(handle); // 1 byte
+        char lfQuality = getc(handle);      // 1 byte
+        char lfPitchAndFamily = getc(handle);   // 1 byte (18th)
+        char lfFacename[32];
+        // Read the rest of the record, which is total record size
+        // minus the number of bytes already read (18 record, 6 metarecord
+        // header)
+        fread((void *)lfFacename, sizeof(char), (int)((2*rdSize) - 18 - 6), handle);
+
+        int family;
+        if (lfPitchAndFamily & FF_MODERN)
+          family = wxMODERN;
+        else if (lfPitchAndFamily & FF_MODERN)
+          family = wxMODERN;
+        else if (lfPitchAndFamily & FF_ROMAN)
+          family = wxROMAN;
+        else if (lfPitchAndFamily & FF_SWISS)
+          family = wxSWISS;
+        else if (lfPitchAndFamily & FF_DECORATIVE)
+          family = wxDECORATIVE;
+        else
+          family = wxDEFAULT;
+
+        int weight;
+        if (lfWeight == 300)
+          weight = wxLIGHT;
+        else if (lfWeight == 400)
+          weight = wxNORMAL;
+        else if (lfWeight == 900)
+          weight = wxBOLD;
+        else weight = wxNORMAL;
+
+        int style;
+        if (lfItalic != 0)
+          style = wxITALIC;
+        else
+          style = wxNORMAL;
+
+        // About how many pixels per inch???
+        int logPixelsY = 100;
+        int pointSize = (int)(lfHeight*72.0/logPixelsY);
+
+        wxFont *theFont =
+          wxTheFontList->FindOrCreateFont(pointSize, family, style, weight, (lfUnderline != 0));
+
+        rec->param1 = (long) theFont;
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEBRUSHINDIRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEBRUSHINDIRECT);
+        int msStyle = getshort(handle); // Style: 2 bytes
+        long colorref = getint(handle);   // COLORREF: 4 bytes
+        int hatchStyle = getshort(handle); // Hatch style 2 bytes
+
+        int style;
+        switch (msStyle)
+        {
+          case BS_HATCHED:
+          {
+            switch (hatchStyle)
+            {
+              case HS_BDIAGONAL:
+                style = wxBDIAGONAL_HATCH;
+                break;
+              case HS_DIAGCROSS:
+                style = wxCROSSDIAG_HATCH;
+                break;
+              case HS_FDIAGONAL:
+                style = wxFDIAGONAL_HATCH;
+                break;
+              case HS_HORIZONTAL:
+                style = wxHORIZONTAL_HATCH;
+                break;
+              case HS_VERTICAL:
+                style = wxVERTICAL_HATCH;
+                break;
+              default:
+              case HS_CROSS:
+                style = wxCROSS_HATCH;
+                break;
+            }
+            break;
+          }
+          case BS_SOLID:
+          default:
+            style = wxSOLID;
+            break;
+        }
+        if (msStyle == PS_DOT)
+          style = wxDOT;
+        else if (msStyle == PS_DASH)
+          style = wxSHORT_DASH;
+        else if (msStyle == PS_NULL)
+          style = wxTRANSPARENT;
+        else style = wxSOLID;
+
+        wxColour colour(GetRValue(colorref), GetGValue(colorref), GetBValue(colorref));
+        rec->param1 = (long)wxTheBrushList->FindOrCreateBrush(colour, style);
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEBITMAPINDIRECT:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEBITMAPINDIRECT);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEBITMAP:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEBITMAP);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      case META_CREATEREGION:
+      {
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEREGION);
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+
+        metaRecords.Append(rec);
+        gdiObjects.Append(rec);
+        AddMetaRecordHandle(rec);
+        rec->param2 = (long)(gdiObjects.Number() - 1);
+        break;
+      }
+      default:
+      {
+        fread((void *)wxBuffer, sizeof(char), (int)((2*rdSize) - 6), handle);
+        break;
+      }
+    }
+  }
+  fclose(handle);
+  return TRUE;
+}
+
+wxXMetaFile::~wxXMetaFile(void)
+{
+  wxNode *node = metaRecords.First();
+  while (node)
+  {
+    wxMetaRecord *rec = (wxMetaRecord *)node->Data();
+    delete rec;
+    wxNode *next = node->Next();
+    delete node;
+    node = next;
+  }
+}
+
+bool wxXMetaFile::SetClipboard(int width, int height)
+{
+  return FALSE;
+}
+
+bool wxXMetaFile::Play(wxDC *dc)
+{
+  wxNode *node = metaRecords.First();
+  while (node)
+  {
+    wxMetaRecord *rec = (wxMetaRecord *)node->Data();
+    int rdFunction = rec->metaFunction;
+
+    switch (rdFunction)
+    {
+      case META_SETBKCOLOR:
+      {
+        break;
+      }
+      case META_SETBKMODE:
+      {
+        break;
+      }
+      case META_SETMAPMODE:
+      {
+        break;
+      }
+//      case META_SETROP2:
+//      case META_SETRELABS:
+//      case META_SETPOLYFILLMODE:
+//      case META_SETSTRETCHBLTMODE:
+//      case META_SETTEXTCHAREXTRA:
+      case META_SETTEXTCOLOR:
+      {
+        break;
+      }
+//      case META_SETTEXTJUSTIFICATION:
+      case META_SETWINDOWORG:
+      {
+        break;
+      }
+      case META_SETWINDOWEXT:
+      {
+        break;
+      }
+//      case META_SETVIEWPORTORG:
+//      case META_SETVIEWPORTEXT:
+//      case META_OFFSETWINDOWORG:
+//      case META_SCALEWINDOWEXT:
+//      case META_OFFSETVIEWPORTORG:
+//      case META_SCALEVIEWPORTEXT:
+      case META_LINETO:
+      {
+        long x1 = rec->param1;
+        long y1 = rec->param2;
+        dc->DrawLine((long) lastX, (long) lastY, x1, y1);
+        break;
+      }
+      case META_MOVETO:
+      {
+        lastX = (double)rec->param1;
+        lastY = (double)rec->param2;
+        break;
+      }
+      case META_EXCLUDECLIPRECT:
+      {
+        break;
+      }
+      case META_INTERSECTCLIPRECT:
+      {
+        break;
+      }
+//      case META_ARC: // DO!!!
+      case META_ELLIPSE:
+      {
+        break;
+      }
+//      case META_FLOODFILL:
+//      case META_PIE: // DO!!!
+      case META_RECTANGLE:
+      {
+        dc->DrawRectangle((long)rec->param1, (long)rec->param2,
+                          (long)rec->param3 - rec->param1,
+                          (long)rec->param4 - rec->param2);
+        break;
+      }
+      case META_ROUNDRECT:
+      {
+        dc->DrawRoundedRectangle((long)rec->param1, (long)rec->param2,
+                          (long)rec->param3 - rec->param1,
+                          (long)rec->param4 - rec->param2,
+                          (long)rec->param5);
+        break;
+      }
+//      case META_PATBLT:
+//      case META_SAVEDC:
+      case META_SETPIXEL:
+      {
+//        rec->param1 = getshort(handle); // x1
+//        rec->param2 = getshort(handle); // y1
+//        rec->param3 = getint(handle);   // COLORREF
+        break;
+      }
+//      case META_OFFSETCLIPRGN:
+      case META_TEXTOUT:
+      {
+/*
+        int count = getshort(handle);
+        rec->stringParam = new char[count+1];
+        fread((void *)rec->stringParam, sizeof(char), count, handle);
+        rec->stringParam[count] = 0;
+        rec->param2 = getshort(handle); // Y
+        rec->param1 = getshort(handle); // X
+*/
+        break;
+      }
+//      case META_BITBLT:
+//      case META_STRETCHBLT:
+      case META_POLYGON:
+      {
+/*
+        rec->param1 = getshort(handle);
+        rec->points = new wxRealPoint[(int)rec->param1];
+        for (int i = 0; i < rec->param1; i++)
+        {
+          rec->points[i].x = getshort(handle);
+          rec->points[i].y = getshort(handle);
+        }
+*/        
+        break;
+      }
+      case META_POLYLINE:
+      {
+/*
+        wxMetaRecord *rec = new wxMetaRecord(META_POLYLINE);
+        rec->param1 = (long)getshort(handle);
+        rec->points = new wxRealPoint[(int)rec->param1];
+        for (int i = 0; i < rec->param1; i++)
+        {
+          rec->points[i].x = getshort(handle);
+          rec->points[i].y = getshort(handle);
+        }
+*/        
+        break;
+      }
+//      case META_ESCAPE:
+//      case META_RESTOREDC:
+//      case META_FILLREGION:
+//      case META_FRAMEREGION:
+//      case META_INVERTREGION:
+//      case META_PAINTREGION:
+//      case META_SELECTCLIPREGION: // DO THIS!
+      case META_SELECTOBJECT:
+      {
+/*
+        wxMetaRecord *rec = new wxMetaRecord(META_SELECTOBJECT);
+        rec->param1 = (long)getshort(handle); // Position of object in gdiObjects list
+*/
+        break;
+      }
+//      case META_SETTEXTALIGN:
+//      case META_DRAWTEXT:
+//      case META_CHORD:
+//      case META_SETMAPPERFLAGS:
+//      case META_EXTTEXTOUT:
+//      case META_SETDIBTODEV:
+//      case META_SELECTPALETTE:
+//      case META_REALIZEPALETTE:
+//      case META_ANIMATEPALETTE:
+//      case META_SETPALENTRIES:
+//      case META_POLYPOLYGON:
+//      case META_RESIZEPALETTE:
+//      case META_DIBBITBLT:
+//      case META_DIBSTRETCHBLT:
+      case META_DIBCREATEPATTERNBRUSH:
+      {
+/*
+        fread((void *)wxBuffer, sizeof(char), (int)(rdSize - 3), handle);
+*/        
+        break;
+      }
+//      case META_STRETCHDIB:
+//      case META_EXTFLOODFILL:
+//      case META_RESETDC:
+//      case META_STARTDOC:
+//      case META_STARTPAGE:
+//      case META_ENDPAGE:
+//      case META_ABORTDOC:
+//      case META_ENDDOC:
+//      case META_DELETEOBJECT: // DO!!
+      case META_CREATEPALETTE:
+      {
+/*
+        wxMetaRecord *rec = new wxMetaRecord(META_CREATEPALETTE);
+        fread((void *)wxBuffer, sizeof(char), (int)(rdSize - 3), handle);
+*/
+        break;
+      }
+      case META_CREATEBRUSH:
+      {
+/*
+        fread((void *)wxBuffer, sizeof(char), (int)(rdSize - 3), handle);
+*/
+        break;
+      }
+      case META_CREATEPATTERNBRUSH:
+      {
+/*
+        fread((void *)wxBuffer, sizeof(char), (int)(rdSize - 3), handle);
+*/
+        break;
+      }
+      case META_CREATEPENINDIRECT:
+      {
+/*
+        int msStyle = getshort(handle); // Style: 2 bytes
+        int x = getshort(handle); // X:     2 bytes
+        int y = getshort(handle); // Y:     2 bytes
+        int colorref = getint(handle); // COLORREF 4 bytes
+
+        int style;
+        if (msStyle == PS_DOT)
+          style = wxDOT;
+        else if (msStyle == PS_DASH)
+          style = wxSHORT_DASH;
+        else if (msStyle == PS_NULL)
+          style = wxTRANSPARENT;
+        else style = wxSOLID;
+
+        wxColour colour(GetRValue(colorref), GetGValue(colorref), GetBValue(colorref));
+        rec->param1 = (long)wxThePenList->FindOrCreatePen(&colour, x, style);
+*/
+        break;
+      }
+      case META_CREATEFONTINDIRECT:
+      {
+/*
+        int lfHeight = getshort(handle);
+        int lfWidth = getshort(handle);
+        int lfEsc = getshort(handle);
+        int lfOrient = getshort(handle);
+        int lfWeight = getshort(handle);
+        char lfItalic = getc(handle);
+        char lfUnderline = getc(handle);
+        char lfStrikeout = getc(handle);
+        char lfCharSet = getc(handle);
+        char lfOutPrecision = getc(handle);
+        char lfClipPrecision = getc(handle);
+        char lfQuality = getc(handle);
+        char lfPitchAndFamily = getc(handle);
+        char lfFacename[32];
+        fread((void *)lfFacename, sizeof(char), 32, handle);
+
+        int family;
+        if (lfPitchAndFamily & FF_MODERN)
+          family = wxMODERN;
+        else if (lfPitchAndFamily & FF_MODERN)
+          family = wxMODERN;
+        else if (lfPitchAndFamily & FF_ROMAN)
+          family = wxROMAN;
+        else if (lfPitchAndFamily & FF_SWISS)
+          family = wxSWISS;
+        else if (lfPitchAndFamily & FF_DECORATIVE)
+          family = wxDECORATIVE;
+        else
+          family = wxDEFAULT;
+
+        int weight;
+        if (lfWeight == 300)
+          weight = wxLIGHT;
+        else if (lfWeight == 400)
+          weight = wxNORMAL;
+        else if (lfWeight == 900)
+          weight = wxBOLD;
+        else weight = wxNORMAL;
+
+        int style;
+        if ((bool)lfItalic)
+          style = wxITALIC;
+        else
+          style = wxNORMAL;
+
+        // About how many pixels per inch???
+        int logPixelsY = 100;
+        int pointSize = (int)(lfHeight*72.0/logPixelsY);
+
+        wxFont *theFont =
+          wxTheFontList->FindOrCreateFont(pointSize, family, style, weight, (bool)lfUnderline);
+
+        rec->param1 = (long)theFont;
+*/
+        break;
+      }
+      case META_CREATEBRUSHINDIRECT:
+      {
+/*
+        int msStyle = getshort(handle); // Style: 2 bytes
+        int colorref = getint(handle);   // COLORREF: 4 bytes
+        int hatchStyle = getshort(handle); // Hatch style 2 bytes
+
+        int style;
+        if (msStyle == PS_DOT)
+          style = wxDOT;
+        else if (msStyle == PS_DASH)
+          style = wxSHORT_DASH;
+        else if (msStyle == PS_NULL)
+          style = wxTRANSPARENT;
+        else style = wxSOLID;
+
+        wxColour colour(GetRValue(colorref), GetGValue(colorref), GetBValue(colorref));
+        rec->param1 = (long)wxTheBrushList->FindOrCreateBrush(&colour, wxSOLID);
+*/
+        break;
+      }
+      case META_CREATEBITMAPINDIRECT:
+      {
+/*
+        fread((void *)wxBuffer, sizeof(char), (int)(rdSize - 3), handle);
+*/        
+        break;
+      }
+      case META_CREATEBITMAP:
+      {
+/*
+        fread((void *)wxBuffer, sizeof(char), (int)(rdSize - 3), handle);
+*/        
+        break;
+      }
+      case META_CREATEREGION:
+      {
+        dc->DestroyClippingRegion();
+/*
+        rec->param1 = getshort(handle); // Style: 2 bytes
+*/
+        break;
+      }
+      default:
+      {
+        break;
+      }
+    }
+    node = node->Next();
+  }
+  return TRUE;
+}
+
diff --git a/src/ogl/misc.cpp b/src/ogl/misc.cpp
new file mode 100644 (file)
index 0000000..4a2e766
--- /dev/null
@@ -0,0 +1,894 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        misc.cpp
+// Purpose:     Miscellaneous OGL support functions
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "misc.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#include <wx/types.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/misc.h>
+#include <wx/ogl/constrnt.h>
+#include <wx/ogl/composit.h>
+
+wxFont*         g_oglNormalFont;
+wxPen*          g_oglBlackPen;
+wxPen*          g_oglWhiteBackgroundPen;
+wxPen*          g_oglTransparentPen;
+wxBrush*        g_oglWhiteBackgroundBrush;
+wxPen*          g_oglBlackForegroundPen;
+wxCursor*       g_oglBullseyeCursor = NULL;
+
+char*           oglBuffer = NULL;
+
+wxList          oglObjectCopyMapping(wxKEY_INTEGER);
+
+
+
+void wxOGLInitialize()
+{
+  g_oglBullseyeCursor = new wxCursor(wxCURSOR_BULLSEYE);
+
+  g_oglNormalFont = new wxFont(10, wxSWISS, wxNORMAL, wxNORMAL);
+
+  g_oglBlackPen = new wxPen("BLACK", 1, wxSOLID);
+
+  g_oglWhiteBackgroundPen = new wxPen("WHITE", 1, wxSOLID);
+  g_oglTransparentPen = new wxPen("WHITE", 1, wxTRANSPARENT);
+  g_oglWhiteBackgroundBrush = new wxBrush("WHITE", wxSOLID);
+  g_oglBlackForegroundPen = new wxPen("BLACK", 1, wxSOLID);
+
+  OGLInitializeConstraintTypes();
+
+  // Initialize big buffer used when writing images
+  oglBuffer = new char[3000];
+
+}
+
+void wxOGLCleanUp()
+{
+    if (oglBuffer)
+    {
+        delete[] oglBuffer;
+        oglBuffer = NULL;
+    }
+    oglBuffer = NULL;
+    if (g_oglBullseyeCursor)
+    {
+        delete g_oglBullseyeCursor;
+        g_oglBullseyeCursor = NULL;
+    }
+
+    if (g_oglNormalFont)
+    {
+        delete g_oglNormalFont;
+        g_oglNormalFont = NULL;
+    }
+    if (g_oglBlackPen)
+    {
+        delete g_oglBlackPen;
+        g_oglBlackPen = NULL;
+    }
+    if (g_oglWhiteBackgroundPen)
+    {
+        delete g_oglWhiteBackgroundPen;
+        g_oglWhiteBackgroundPen = NULL;
+    }
+    if (g_oglTransparentPen)
+    {
+        delete g_oglTransparentPen;
+        g_oglTransparentPen = NULL;
+    }
+    if (g_oglWhiteBackgroundBrush)
+    {
+        delete g_oglWhiteBackgroundBrush;
+        g_oglWhiteBackgroundBrush = NULL;
+    }
+    if (g_oglBlackForegroundPen)
+    {
+        delete g_oglBlackForegroundPen;
+        g_oglBlackForegroundPen = NULL;
+    }
+
+    OGLCleanUpConstraintTypes();
+}
+
+wxFont *oglMatchFont(int point_size)
+{
+  wxFont *font = wxTheFontList->FindOrCreateFont(point_size, wxSWISS, wxNORMAL, wxNORMAL);
+#if 0
+  switch (point_size)
+  {
+    case 4:
+      font = swiss_font_4;
+      break;
+    case 6:
+      font = swiss_font_6;
+      break;
+    case 8:
+      font = swiss_font_8;
+      break;
+    case 12:
+      font = swiss_font_12;
+      break;
+    case 14:
+      font = swiss_font_14;
+      break;
+    case 18:
+      font = swiss_font_18;
+      break;
+    case 24:
+      font = swiss_font_24;
+      break;
+    default:
+    case 10:
+      font = swiss_font_10;
+      break;
+  }
+#endif
+  return font;
+}
+
+int FontSizeDialog(wxFrame *parent, int old_size)
+{
+  if (old_size <= 0)
+    old_size = 10;
+  char buf[40];
+  sprintf(buf, "%d", old_size);
+  wxString ans = wxGetTextFromUser("Enter point size", "Font size", buf, parent);
+  if (ans == "")
+    return 0;
+
+  int new_size = atoi(ans);
+  if ((new_size <= 0) || (new_size > 40))
+  {
+    wxMessageBox("Invalid point size!", "Error", wxOK);
+    return 0;
+  }
+  return new_size;
+/*
+  char *strings[8];
+  strings[0] = "4";
+  strings[1] = "6";
+  strings[2] = "8";
+  strings[3] = "10";
+  strings[4] = "12";
+  strings[5] = "14";
+  strings[6] = "18";
+  strings[7] = "24";
+  char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
+  if (ans)
+  {
+    int size;
+    sscanf(ans, "%d", &size);
+    return oglMatchFont(size);
+  }
+  else return NULL;
+*/
+}
+
+// Centre a list of strings in the given box. xOffset and yOffset are the
+// the positions that these lines should be relative to, and this might be
+// the same as m_xpos, m_ypos, but might be zero if formatting from left-justifying.
+void oglCentreText(wxDC& dc, wxList *text_list,
+                double m_xpos, double m_ypos, double width, double height,
+                int formatMode)
+{
+  int n = text_list->Number();
+
+  if (!text_list || (n == 0))
+    return;
+
+  // First, get maximum dimensions of box enclosing text
+
+  long char_height = 0;
+  long max_width = 0;
+  long current_width = 0;
+
+  // Store text extents for speed
+  double *widths = new double[n];
+
+  wxNode *current = text_list->First();
+  int i = 0;
+  while (current)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+    dc.GetTextExtent(line->GetText(), &current_width, &char_height);
+    widths[i] = current_width;
+
+    if (current_width > max_width)
+      max_width = current_width;
+    current = current->Next();
+    i ++;
+  }
+
+  double max_height = n*char_height;
+
+  double xoffset, yoffset, xOffset, yOffset;
+
+  if (formatMode & FORMAT_CENTRE_VERT)
+  {
+    if (max_height < height)
+      yoffset = (double)(m_ypos - (height/2.0) + (height - max_height)/2.0);
+    else
+      yoffset = (double)(m_ypos - (height/2.0));
+    yOffset = m_ypos;
+  }
+  else
+  {
+    yoffset = 0.0;
+    yOffset = 0.0;
+  }
+
+  if (formatMode & FORMAT_CENTRE_HORIZ)
+  {
+    xoffset = (double)(m_xpos - width/2.0);
+    xOffset = m_xpos;
+  }
+  else
+  {
+    xoffset = 0.0;
+    xOffset = 0.0;
+  }
+
+  current = text_list->First();
+  i = 0;
+
+  while (current)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+
+    double x;
+    if ((formatMode & FORMAT_CENTRE_HORIZ) && (widths[i] < width))
+      x = (double)((width - widths[i])/2.0 + xoffset);
+    else
+      x = xoffset;
+    double y = (double)(i*char_height + yoffset);
+
+    line->SetX( x - xOffset ); line->SetY( y - yOffset );
+    current = current->Next();
+    i ++;
+  }
+
+  delete widths;
+}
+
+// Centre a list of strings in the given box
+void oglCentreTextNoClipping(wxDC& dc, wxList *text_list,
+                              double m_xpos, double m_ypos, double width, double height)
+{
+  int n = text_list->Number();
+
+  if (!text_list || (n == 0))
+    return;
+
+  // First, get maximum dimensions of box enclosing text
+
+  long char_height = 0;
+  long max_width = 0;
+  long current_width = 0;
+
+  // Store text extents for speed
+  double *widths = new double[n];
+
+  wxNode *current = text_list->First();
+  int i = 0;
+  while (current)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+    dc.GetTextExtent(line->GetText(), &current_width, &char_height);
+    widths[i] = current_width;
+
+    if (current_width > max_width)
+      max_width = current_width;
+    current = current->Next();
+    i ++;
+  }
+
+  double max_height = n*char_height;
+
+  double yoffset = (double)(m_ypos - (height/2.0) + (height - max_height)/2.0);
+
+  double xoffset = (double)(m_xpos - width/2.0);
+
+  current = text_list->First();
+  i = 0;
+
+  while (current)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+
+    double x = (double)((width - widths[i])/2.0 + xoffset);
+    double y = (double)(i*char_height + yoffset);
+
+    line->SetX( x - m_xpos ); line->SetY( y - m_ypos );
+    current = current->Next();
+    i ++;
+  }
+  delete widths;
+}
+
+void oglGetCentredTextExtent(wxDC& dc, wxList *text_list,
+                              double m_xpos, double m_ypos, double width, double height,
+                              double *actual_width, double *actual_height)
+{
+  int n = text_list->Number();
+
+  if (!text_list || (n == 0))
+  {
+    *actual_width = 0;
+    *actual_height = 0;
+    return;
+  }
+
+  // First, get maximum dimensions of box enclosing text
+
+  long char_height = 0;
+  long max_width = 0;
+  long current_width = 0;
+
+  wxNode *current = text_list->First();
+  int i = 0;
+  while (current)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+    dc.GetTextExtent(line->GetText(), &current_width, &char_height);
+
+    if (current_width > max_width)
+      max_width = current_width;
+    current = current->Next();
+    i ++;
+  }
+
+  *actual_height = n*char_height;
+  *actual_width = max_width;
+}
+
+// Format a string to a list of strings that fit in the given box.
+// Interpret %n and 10 or 13 as a new line.
+wxStringList *oglFormatText(wxDC& dc, const wxString& text, double width, double height, int formatMode)
+{
+  // First, parse the string into a list of words
+  wxStringList word_list;
+
+  // Make new lines into NULL strings at this point
+  int i = 0; int j = 0; int len = strlen(text);
+  char word[200]; word[0] = 0;
+  bool end_word = FALSE; bool new_line = FALSE;
+  while (i < len)
+  {
+    switch (text[i])
+    {
+      case '%':
+      {
+        i ++;
+        if (i == len)
+        { word[j] = '%'; j ++; }
+        else
+        {
+          if (text[i] == 'n')
+          { new_line = TRUE; end_word = TRUE; i++; }
+          else
+          { word[j] = '%'; j ++; word[j] = text[i]; j ++; i ++; }
+        }
+        break;
+      }
+      case 10:
+      {
+        new_line = TRUE; end_word = TRUE; i++;
+        break;
+      }
+      case 13:
+      {
+        new_line = TRUE; end_word = TRUE; i++;
+      }
+      case ' ':
+      {
+        end_word = TRUE;
+        i ++;
+        break;
+      }
+      default:
+      {
+        word[j] = text[i];
+        j ++; i ++;
+        break;
+      }
+    }
+    if (i == len) end_word = TRUE;
+    if (end_word)
+    {
+      word[j] = 0;
+      j = 0;
+      word_list.Add(word);
+      end_word = FALSE;
+    }
+    if (new_line)
+    {
+      word_list.Append(NULL);
+      new_line = FALSE;
+    }
+  }
+  // Now, make a list of strings which can fit in the box
+  wxStringList *string_list = new wxStringList;
+
+  char buffer[400];
+  buffer[0] = 0;
+  wxNode *node = word_list.First();
+  long x, y;
+
+  while (node)
+  {
+    wxString oldBuffer(buffer);
+
+    char *s = (char *)node->Data();
+    if (!s)
+    {
+      // FORCE NEW LINE
+      if (strlen(buffer) > 0)
+        string_list->Add(buffer);
+
+      buffer[0] = 0;
+    }
+    else
+    {
+      if (buffer[0] != 0)
+        strcat(buffer, " ");
+
+      strcat(buffer, s);
+      dc.GetTextExtent(buffer, &x, &y);
+
+      // Don't fit within the bounding box if we're fitting shape to contents
+      if ((x > width) && !(formatMode & FORMAT_SIZE_TO_CONTENTS))
+      {
+        // Deal with first word being wider than box
+        if (oldBuffer.Length() > 0)
+          string_list->Add(oldBuffer);
+
+        buffer[0] = 0;
+        strcat(buffer, s);
+      }
+    }
+
+    node = node->Next();
+  }
+  if (buffer[0] != 0)
+    string_list->Add(buffer);
+
+  return string_list;
+}
+
+void oglDrawFormattedText(wxDC& dc, wxList *text_list,
+                       double m_xpos, double m_ypos, double width, double height,
+                       int formatMode)
+{
+  double xoffset, yoffset;
+  if (formatMode & FORMAT_CENTRE_HORIZ)
+    xoffset = m_xpos;
+  else
+    xoffset = (double)(m_xpos - (width / 2.0));
+
+  if (formatMode & FORMAT_CENTRE_VERT)
+    yoffset = m_ypos;
+  else
+    yoffset = (double)(m_ypos - (height / 2.0));
+
+  dc.SetClippingRegion(
+                    (long)(m_xpos - width/2.0), (long)(m_ypos - height/2.0),
+                    (long)width, (long)height);
+
+  wxNode *current = text_list->First();
+  while (current)
+  {
+    wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+
+    dc.DrawText(line->GetText(), WXROUND(xoffset + line->GetX()), WXROUND(yoffset + line->GetY()));
+    current = current->Next();
+  }
+
+  dc.DestroyClippingRegion();
+}
+
+/*
+ * Find centroid given list of points comprising polyline
+ *
+ */
+
+void oglFindPolylineCentroid(wxList *points, double *x, double *y)
+{
+  double xcount = 0;
+  double ycount = 0;
+
+  wxNode *node = points->First();
+  while (node)
+  {
+    wxRealPoint *point = (wxRealPoint *)node->Data();
+    xcount += point->x;
+    ycount += point->y;
+    node = node->Next();
+  }
+
+  *x = (xcount/points->Number());
+  *y = (ycount/points->Number());
+}
+
+/*
+ * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
+ * If so, ratio1 gives the proportion along the first line
+ * that the intersection occurs (or something like that).
+ * Used by functions below.
+ *
+ */
+void oglCheckLineIntersection(double x1, double y1, double x2, double y2,
+                             double x3, double y3, double x4, double y4,
+                             double *ratio1, double *ratio2)
+{
+  double denominator_term = (y4 - y3)*(x2 - x1) - (y2 - y1)*(x4 - x3);
+  double numerator_term = (x3 - x1)*(y4 - y3) + (x4 - x3)*(y1 - y3);
+
+  double line_constant;
+  double length_ratio = 1.0;
+  double k_line = 1.0;
+
+  // Check for parallel lines
+  if ((denominator_term < 0.005) && (denominator_term > -0.005))
+    line_constant = -1.0;
+  else
+    line_constant = numerator_term/denominator_term;
+
+  // Check for intersection
+  if ((line_constant < 1.0) && (line_constant > 0.0))
+  {
+    // Now must check that other line hits
+    if (((y4 - y3) < 0.005) && ((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.0) && (k_line < 1.0))
+      length_ratio = line_constant;
+    else
+      k_line = 1.0;
+  }
+  *ratio1 = length_ratio;
+  *ratio2 = k_line;
+}
+
+/*
+ * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
+ * (*x3, *y3) is the point where it hits.
+ *
+ */
+void oglFindEndForPolyline(double n, double xvec[], double yvec[],
+                           double x1, double y1, double x2, double y2, double *x3, double *y3)
+{
+  int i;
+  double lastx = xvec[0];
+  double lasty = yvec[0];
+
+  double min_ratio = 1.0;
+  double line_ratio;
+  double other_ratio;
+
+  for (i = 1; i < n; i++)
+  {
+    oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i],
+                            &line_ratio, &other_ratio);
+    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 (!(xvec[0] == lastx && yvec[0] == lasty))
+  {
+    oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0],
+                            &line_ratio, &other_ratio);
+
+    if (line_ratio < min_ratio)
+      min_ratio = line_ratio;
+  }
+
+  *x3 = (x1 + (x2 - x1)*min_ratio);
+  *y3 = (y1 + (y2 - y1)*min_ratio);
+
+}
+
+/*
+ * Find where the line hits the box.
+ *
+ */
+
+void oglFindEndForBox(double width, double height,
+                      double x1, double y1,         // Centre of box (possibly)
+                      double x2, double y2,         // other end of line
+                      double *x3, double *y3)       // End on box edge
+{
+  double xvec[5];
+  double yvec[5];
+
+  xvec[0] = (double)(x1 - width/2.0);
+  yvec[0] = (double)(y1 - height/2.0);
+  xvec[1] = (double)(x1 - width/2.0);
+  yvec[1] = (double)(y1 + height/2.0);
+  xvec[2] = (double)(x1 + width/2.0);
+  yvec[2] = (double)(y1 + height/2.0);
+  xvec[3] = (double)(x1 + width/2.0);
+  yvec[3] = (double)(y1 - height/2.0);
+  xvec[4] = (double)(x1 - width/2.0);
+  yvec[4] = (double)(y1 - height/2.0);
+
+  oglFindEndForPolyline(5, xvec, yvec, x2, y2, x1, y1, x3, y3);
+}
+
+/*
+ * Find where the line hits the circle.
+ *
+ */
+
+void oglFindEndForCircle(double radius,
+                         double x1, double y1,  // Centre of circle
+                         double x2, double y2,  // Other end of line
+                         double *x3, double *y3)
+{
+  double H = (double)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+
+  if (H == 0.0)
+  {
+    *x3 = x1;
+    *y3 = y1;
+  }
+  else
+  {
+   *y3 = radius * (y2 - y1)/H + y1;
+   *x3 = radius * (x2 - x1)/H + x1;
+  }
+}
+
+/*
+ * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
+ * return the position of the tip of the arrow and the left and right vertices of the arrow.
+ *
+ */
+
+void oglGetArrowPoints(double x1, double y1, double x2, double y2,
+                      double length, double width,
+                      double *tip_x, double *tip_y,
+                      double *side1_x, double *side1_y,
+                      double *side2_x, double *side2_y)
+{
+  double l = (double)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+
+  if (l < 0.01)
+    l = (double) 0.01;
+
+  double i_bar = (x2 - x1)/l;
+  double j_bar = (y2 - y1)/l;
+
+  double x3 = (- length*i_bar) + x2;
+  double y3 = (- length*j_bar) + y2;
+
+  *side1_x = width*(-j_bar) + x3;
+  *side1_y = width*i_bar + y3;
+
+  *side2_x = -width*(-j_bar) + x3;
+  *side2_y = -width*i_bar + y3;
+
+  *tip_x = x2; *tip_y = y2;
+}
+
+/*
+ * Given an ellipse and endpoints of a line, returns the point at which
+ * the line touches the ellipse in values x4, y4.
+ * This function assumes that the centre of the ellipse is at x1, y1, and the
+ * ellipse has a width of width1 and a height of height1. It also assumes you are
+ * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
+ * This function calculates the x,y coordinates of the intersection point of
+ * the arc with the ellipse.
+ * Author: Ian Harrison
+ */
+
+void oglDrawArcToEllipse(double x1, double y1, double width1, double height1, double x2, double y2, double x3, double y3,
+  double *x4, double *y4)
+{
+  double a1 = (double)(width1/2.0);
+  double b1 = (double)(height1/2.0);
+
+  // These are required to give top left x and y coordinates for DrawEllipse
+//  double top_left_x1 = (double)(x1 - a1);
+//  double top_left_y1 = (double)(y1 - b1);
+/*
+  // Check for vertical line
+  if (fabs(x2 - x3) < 0.05)
+  {
+    *x4 = x3;
+    if (y2 < y3)
+      *y4 = (double)(y1 - b1);
+    else
+      *y4 = (double)(y1 + b1);
+    return;
+  }
+*/
+  // Check that x2 != x3
+  if (fabs(x2 - x3) < 0.05)
+  {
+    *x4 = x2;
+    if (y3 > y2)
+      *y4 = (double)(y1 - sqrt((b1*b1 - (((x2-x1)*(x2-x1))*(b1*b1)/(a1*a1)))));
+    else
+      *y4 = (double)(y1 + sqrt((b1*b1 - (((x2-x1)*(x2-x1))*(b1*b1)/(a1*a1)))));
+    return;
+  }
+
+  // Calculate the x and y coordinates of the point where arc intersects ellipse
+
+  double A, B, C, D, E, F, G, H, K;
+  double ellipse1_x, ellipse1_y;
+
+  A = (double)(1/(a1 * a1));
+  B = (double)((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1);
+  C = (double)(2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1);
+  D = (double)((y2 - y1) * (y2 - y1)) / (b1 * b1);
+  E = (double)(A + B);
+  F = (double)(C - (2 * A * x1) - (2 * B * x2));
+  G = (double)((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1);
+  H = (double)((y3 - y2) / (x3 - x2));
+  K = (double)((F * F) - (4 * E * G));
+
+  if (K >= 0)
+  // In this case the line intersects the ellipse, so calculate intersection
+  {
+    if(x2 >= x1)
+    {
+      ellipse1_x = (double)(((F * -1) + sqrt(K)) / (2 * E));
+      ellipse1_y = (double)((H * (ellipse1_x - x2)) + y2);
+    }
+    else
+    {
+      ellipse1_x = (double)(((F * -1) -  sqrt(K)) / (2 * E));
+      ellipse1_y = (double)((H * (ellipse1_x - x2)) + y2);
+    }
+  }
+  else
+  // in this case, arc does not intersect ellipse, so just draw arc
+  {
+    ellipse1_x = x3;
+    ellipse1_y = y3;
+  }
+  *x4 = ellipse1_x;
+  *y4 = ellipse1_y;
+
+/*
+  // Draw a little circle (radius = 2) at the end of the arc where it hits
+  // the ellipse .
+
+  double circle_x = ellipse1_x - 2.0;
+  double circle_y = ellipse1_y - 2.0;
+  m_canvas->DrawEllipse(circle_x, circle_y, 4.0, 4.0);
+*/
+}
+
+// Update a list item from a list of strings
+void UpdateListBox(wxListBox *item, wxList *list)
+{
+  item->Clear();
+  if (!list)
+    return;
+
+  wxNode *node = list->First();
+  while (node)
+  {
+    char *s = (char *)node->Data();
+    item->Append(s);
+    node = node->Next();
+  }
+}
+
+bool oglRoughlyEqual(double val1, double val2, double tol)
+{
+    return ( (val1 < (val2 + tol)) && (val1 > (val2 - tol)) &&
+             (val2 < (val1 + tol)) && (val2 > (val1 - tol)));
+}
+
+/*
+ * Hex<->Dec conversion
+ */
+
+// Array used in DecToHex conversion routine.
+static char sg_HexArray[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+  'C', 'D', 'E', 'F' };
+
+// Convert 2-digit hex number to decimal
+unsigned int oglHexToDec(char* buf)
+{
+  int firstDigit, secondDigit;
+
+  if (buf[0] >= 'A')
+    firstDigit = buf[0] - 'A' + 10;
+  else
+    firstDigit = buf[0] - '0';
+
+  if (buf[1] >= 'A')
+    secondDigit = buf[1] - 'A' + 10;
+  else
+    secondDigit = buf[1] - '0';
+
+  return firstDigit * 16 + secondDigit;
+}
+
+// Convert decimal integer to 2-character hex string
+void oglDecToHex(unsigned int dec, char *buf)
+{
+    int firstDigit = (int)(dec/16.0);
+    int secondDigit = (int)(dec - (firstDigit*16.0));
+    buf[0] = sg_HexArray[firstDigit];
+    buf[1] = sg_HexArray[secondDigit];
+    buf[2] = 0;
+}
+
+// 3-digit hex to wxColour
+wxColour oglHexToColour(const wxString& hex)
+{
+    if (hex.Length() == 6)
+       {
+        char buf[7];
+        strncpy(buf, hex, 7);
+        unsigned int r = oglHexToDec((char *)buf);
+        unsigned int g = oglHexToDec((char *)(buf+2));
+        unsigned int b = oglHexToDec((char *)(buf+4));
+        return wxColour(r, g, b);
+       }
+       else
+           return wxColour(0,0,0);
+}
+
+// RGB to 3-digit hex
+wxString oglColourToHex(const wxColour& colour)
+{
+    char buf[7];
+    unsigned int red = colour.Red();
+    unsigned int green = colour.Green();
+    unsigned int blue = colour.Blue();
+
+    oglDecToHex(red, buf);
+    oglDecToHex(green, buf+2);
+    oglDecToHex(blue, buf+4);
+
+    return wxString(buf);
+}
+
+
diff --git a/src/ogl/ogldiag.cpp b/src/ogl/ogldiag.cpp
new file mode 100644 (file)
index 0000000..6698e5b
--- /dev/null
@@ -0,0 +1,756 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        ogldiag.cpp
+// Purpose:     wxDiagram
+// Author:      Julian Smart
+// Modified by:
+// Created:     12/07/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "ogldiag.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#include <fstream.h>
+#else
+#include <iostream>
+#include <fstream>
+#ifdef _MSC_VER
+using namespace std;
+#endif
+#endif
+
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include <wx/ogl/basic.h>
+#include <wx/ogl/basicp.h>
+#include <wx/ogl/canvas.h>
+#include <wx/ogl/ogldiag.h>
+#include <wx/ogl/lines.h>
+#include <wx/ogl/composit.h>
+#include <wx/ogl/misc.h>
+
+IMPLEMENT_DYNAMIC_CLASS(wxDiagram, wxObject)
+
+// Object canvas
+wxDiagram::wxDiagram()
+{
+  m_diagramCanvas = NULL;
+  m_quickEditMode = FALSE;
+  m_snapToGrid = TRUE;
+  m_gridSpacing = 5.0;
+  m_shapeList = new wxList;
+  m_mouseTolerance = DEFAULT_MOUSE_TOLERANCE;
+}
+
+wxDiagram::~wxDiagram()
+{
+  if (m_shapeList)
+    delete m_shapeList;
+}
+
+void wxDiagram::SetSnapToGrid(bool snap)
+{
+  m_snapToGrid = snap;
+}
+
+void wxDiagram::SetGridSpacing(double spacing)
+{
+  m_gridSpacing = spacing;
+}
+
+void wxDiagram::Snap(double *x, double *y)
+{
+  if (m_snapToGrid)
+  {
+    *x = m_gridSpacing * ((int)(*x/m_gridSpacing + 0.5));
+    *y = m_gridSpacing * ((int)(*y/m_gridSpacing + 0.5));
+  }
+}
+
+
+void wxDiagram::Redraw(wxDC& dc)
+{
+  if (m_shapeList)
+  {
+    if (GetCanvas())
+      GetCanvas()->SetCursor(* wxHOURGLASS_CURSOR);
+    wxNode *current = m_shapeList->First();
+
+    while (current)
+    {
+      wxShape *object = (wxShape *)current->Data();
+      if (!object->GetParent())
+        object->Draw(dc);
+
+      current = current->Next();
+    }
+    if (GetCanvas())
+      GetCanvas()->SetCursor(* wxSTANDARD_CURSOR);
+  }
+}
+
+void wxDiagram::Clear(wxDC& dc)
+{
+  dc.Clear();
+}
+
+// Insert object after addAfter, or at end of list.
+void wxDiagram::AddShape(wxShape *object, wxShape *addAfter)
+{
+  wxNode *nodeAfter = NULL;
+  if (addAfter)
+    nodeAfter = m_shapeList->Member(addAfter);
+
+  if (!m_shapeList->Member(object))
+  {
+    if (nodeAfter)
+    {
+      if (nodeAfter->Next())
+        m_shapeList->Insert(nodeAfter->Next(), object);
+      else
+        m_shapeList->Append(object);
+    }
+    else
+      m_shapeList->Append(object);
+    object->SetCanvas(GetCanvas());
+  }
+}
+
+void wxDiagram::InsertShape(wxShape *object)
+{
+  m_shapeList->Insert(object);
+  object->SetCanvas(GetCanvas());
+}
+
+void wxDiagram::RemoveShape(wxShape *object)
+{
+  m_shapeList->DeleteObject(object);
+}
+
+// Should this delete the actual objects too? I think not.
+void wxDiagram::RemoveAllShapes()
+{
+  m_shapeList->Clear();
+}
+
+void wxDiagram::DeleteAllShapes()
+{
+  wxNode *node = m_shapeList->First();
+  while (node)
+  {
+    wxShape *shape = (wxShape *)node->Data();
+    if (!shape->GetParent())
+    {
+      RemoveShape(shape);
+      delete shape;
+      node = m_shapeList->First();
+    }
+    else
+      node = node->Next();
+  }
+}
+
+void wxDiagram::ShowAll(bool show)
+{
+  wxNode *current = m_shapeList->First();
+
+  while (current)
+  {
+    wxShape *object = (wxShape *)current->Data();
+    object->Show(show);
+
+    current = current->Next();
+  }
+}
+
+void wxDiagram::DrawOutline(wxDC& dc, double x1, double y1, double x2, double y2)
+{
+  wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+  dc.SetPen(dottedPen);
+  dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+  wxPoint points[5];
+
+  points[0].x = (int) x1;
+  points[0].y = (int) y1;
+
+  points[1].x = (int) x2;
+  points[1].y = (int) y1;
+
+  points[2].x = (int) x2;
+  points[2].y = (int) y2;
+
+  points[3].x = (int) x1;
+  points[3].y = (int) y2;
+
+  points[4].x = (int) x1;
+  points[4].y = (int) y1;
+  dc.DrawLines(5, points);
+}
+
+// Make sure all text that should be centred, is centred.
+void wxDiagram::RecentreAll(wxDC& dc)
+{
+  wxNode *object_node = m_shapeList->First();
+  while (object_node)
+  {
+    wxShape *obj = (wxShape *)object_node->Data();
+    obj->Recentre(dc);
+    object_node = object_node->Next();
+  }
+}
+
+// Input/output
+#ifdef PROLOGIO
+bool wxDiagram::SaveFile(const wxString& filename)
+{
+  wxBeginBusyCursor();
+
+  wxExprDatabase *database = new wxExprDatabase;
+
+  // First write the diagram type
+  wxExpr *header = new wxExpr("diagram");
+  OnHeaderSave(*database, *header);
+
+  database->Append(header);
+
+  wxNode *node = m_shapeList->First();
+  while (node)
+  {
+    wxShape *shape = (wxShape *)node->Data();
+
+    if (!shape->IsKindOf(CLASSINFO(wxControlPoint)))
+    {
+      wxExpr *expr = NULL;
+      if (shape->IsKindOf(CLASSINFO(wxLineShape)))
+        expr = new wxExpr("line");
+       else
+        expr = new wxExpr("shape");
+
+      OnShapeSave(*database, *shape, *expr);
+    }
+    node = node->Next();
+  }
+  OnDatabaseSave(*database);
+
+  char tempFile[400];
+  wxGetTempFileName("diag", tempFile);
+  FILE* file = fopen(tempFile, "w");
+  if (! file)
+  {
+    wxEndBusyCursor();
+    delete database;
+    return FALSE;
+  }
+
+  database->Write(file);
+  fclose(file);
+  delete database;
+
+/*
+  // Save backup
+  if (FileExists(filename))
+  {
+    char buf[400];
+#ifdef __X__
+    sprintf(buf, "%s.bak", filename);
+#endif
+#ifdef __WXMSW__
+    sprintf(buf, "_diagram.bak");
+#endif
+    if (FileExists(buf)) wxRemoveFile(buf);
+    if (!wxRenameFile(filename, buf))
+    {
+      wxCopyFile(filename, buf);
+      wxRemoveFile(filename);
+    }
+  }
+*/
+
+  // Copy the temporary file to the correct filename
+  if (!wxRenameFile(tempFile, filename))
+  {
+    wxCopyFile(tempFile, filename);
+    wxRemoveFile(tempFile);
+  }
+
+  wxEndBusyCursor();
+  return TRUE;
+}
+
+bool wxDiagram::LoadFile(const wxString& filename)
+{
+  wxBeginBusyCursor();
+
+  wxExprDatabase database(wxExprInteger, "id");
+  if (!database.Read(filename))
+  {
+    wxEndBusyCursor();
+    return FALSE;
+  }
+
+  DeleteAllShapes();
+
+  database.BeginFind();
+  wxExpr *header = database.FindClauseByFunctor("diagram");
+
+  if (header)
+    OnHeaderLoad(database, *header);
+
+  // Scan through all clauses and register the ids
+  wxNode *node = database.First();
+  while (node)
+  {
+    wxExpr *clause = (wxExpr *)node->Data();
+    long id = -1;
+    clause->GetAttributeValue("id", id);
+    wxRegisterId(id);
+    node = node->Next();
+  }
+
+  ReadNodes(database);
+  ReadContainerGeometry(database);
+  ReadLines(database);
+
+  OnDatabaseLoad(database);
+
+  wxEndBusyCursor();
+
+  return TRUE;
+}
+
+void wxDiagram::ReadNodes(wxExprDatabase& database)
+{
+  // Find and create the node images
+  database.BeginFind();
+  wxExpr *clause = database.FindClauseByFunctor("shape");
+  while (clause)
+  {
+    char *type = NULL;
+    long parentId = -1;
+
+    clause->AssignAttributeValue("type", &type);
+    clause->AssignAttributeValue("parent", &parentId);
+    wxClassInfo *classInfo = wxClassInfo::FindClass(type);
+    if (classInfo)
+    {
+      wxShape *shape = (wxShape *)classInfo->CreateObject();
+      OnShapeLoad(database, *shape, *clause);
+
+      shape->SetCanvas(GetCanvas());
+      shape->Show(TRUE);
+
+      m_shapeList->Append(shape);
+
+      // If child of composite, link up
+      if (parentId > -1)
+      {
+        wxExpr *parentExpr = database.HashFind("shape", parentId);
+        if (parentExpr && parentExpr->GetClientData())
+        {
+          wxShape *parent = (wxShape *)parentExpr->GetClientData();
+          shape->SetParent(parent);
+          parent->GetChildren().Append(shape);
+        }
+      }
+
+      clause->SetClientData(shape);
+    }
+    if (type)
+      delete[] type;
+
+    clause = database.FindClauseByFunctor("shape");
+  }
+  return;
+}
+
+void wxDiagram::ReadLines(wxExprDatabase& database)
+{
+  database.BeginFind();
+  wxExpr *clause = database.FindClauseByFunctor("line");
+  while (clause)
+  {
+    wxString type("");
+    long parentId = -1;
+
+    clause->GetAttributeValue("type", type);
+    clause->GetAttributeValue("parent", parentId);
+    wxClassInfo *classInfo = wxClassInfo::FindClass((char*) (const char*) type);
+    if (classInfo)
+    {
+      wxLineShape *shape = (wxLineShape *)classInfo->CreateObject();
+      shape->Show(TRUE);
+
+      OnShapeLoad(database, *shape, *clause);
+      shape->SetCanvas(GetCanvas());
+
+      long image_to = -1; long image_from = -1;
+      clause->GetAttributeValue("to", image_to);
+      clause->GetAttributeValue("from", image_from);
+
+      wxExpr *image_to_expr = database.HashFind("shape", image_to);
+
+      if (!image_to_expr)
+      {
+        // Error
+      }
+      wxExpr *image_from_expr = database.HashFind("shape", image_from);
+
+      if (!image_from_expr)
+      {
+        // Error
+      }
+
+      if (image_to_expr && image_from_expr)
+      {
+        wxShape *image_to_object = (wxShape *)image_to_expr->GetClientData();
+        wxShape *image_from_object = (wxShape *)image_from_expr->GetClientData();
+
+        if (image_to_object && image_from_object)
+        {
+          image_from_object->AddLine(shape, image_to_object, shape->GetAttachmentFrom(), shape->GetAttachmentTo());
+        }
+      }
+      clause->SetClientData(shape);
+
+      m_shapeList->Append(shape);
+    }
+
+    clause = database.FindClauseByFunctor("line");
+  }
+}
+
+// Containers have divisions that reference adjoining divisions,
+// so we need a separate pass to link everything up.
+// Also used by Symbol Library.
+void wxDiagram::ReadContainerGeometry(wxExprDatabase& database)
+{
+  database.BeginFind();
+  wxExpr *clause = database.FindClauseByFunctor("shape");
+  while (clause)
+  {
+    wxShape *image = (wxShape *)clause->GetClientData();
+    if (image && image->IsKindOf(CLASSINFO(wxCompositeShape)))
+    {
+      wxCompositeShape *composite = (wxCompositeShape *)image;
+      wxExpr *divisionExpr = NULL;
+
+      // Find the list of divisions in the composite
+      clause->GetAttributeValue("divisions", &divisionExpr);
+      if (divisionExpr)
+      {
+        int i = 0;
+        wxExpr *idExpr = divisionExpr->Nth(i);
+        while (idExpr)
+        {
+          long divisionId = idExpr->IntegerValue();
+          wxExpr *childExpr = database.HashFind("shape", divisionId);
+          if (childExpr && childExpr->GetClientData())
+          {
+            wxDivisionShape *child = (wxDivisionShape *)childExpr->GetClientData();
+            composite->GetDivisions().Append(child);
+
+            // Find the adjoining shapes
+            long leftSideId = -1;
+            long topSideId = -1;
+            long rightSideId = -1;
+            long bottomSideId = -1;
+            childExpr->GetAttributeValue("left_side", leftSideId);
+            childExpr->GetAttributeValue("top_side", topSideId);
+            childExpr->GetAttributeValue("right_side", rightSideId);
+            childExpr->GetAttributeValue("bottom_side", bottomSideId);
+            if (leftSideId > -1)
+            {
+              wxExpr *leftExpr = database.HashFind("shape", leftSideId);
+              if (leftExpr && leftExpr->GetClientData())
+              {
+                wxDivisionShape *leftSide = (wxDivisionShape *)leftExpr->GetClientData();
+                child->SetLeftSide(leftSide);
+              }
+            }
+            if (topSideId > -1)
+            {
+              wxExpr *topExpr = database.HashFind("shape", topSideId);
+              if (topExpr && topExpr->GetClientData())
+              {
+                wxDivisionShape *topSide = (wxDivisionShape *)topExpr->GetClientData();
+                child->SetTopSide(topSide);
+              }
+            }
+            if (rightSideId > -1)
+            {
+              wxExpr *rightExpr = database.HashFind("shape", rightSideId);
+              if (rightExpr && rightExpr->GetClientData())
+              {
+                wxDivisionShape *rightSide = (wxDivisionShape *)rightExpr->GetClientData();
+                child->SetRightSide(rightSide);
+              }
+            }
+            if (bottomSideId > -1)
+            {
+              wxExpr *bottomExpr = database.HashFind("shape", bottomSideId);
+              if (bottomExpr && bottomExpr->GetClientData())
+              {
+                wxDivisionShape *bottomSide = (wxDivisionShape *)bottomExpr->GetClientData();
+                child->SetBottomSide(bottomSide);
+              }
+            }
+          }
+          i ++;
+          idExpr = divisionExpr->Nth(i);
+        }
+      }
+    }
+
+    clause = database.FindClauseByFunctor("shape");
+  }
+}
+
+// Allow for modifying file
+bool wxDiagram::OnDatabaseLoad(wxExprDatabase& db)
+{
+  return TRUE;
+}
+
+bool wxDiagram::OnDatabaseSave(wxExprDatabase& db)
+{
+  return TRUE;
+}
+
+bool wxDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
+{
+  shape.WriteAttributes(&expr);
+  db.Append(&expr);
+
+  if (shape.IsKindOf(CLASSINFO(wxCompositeShape)))
+  {
+    wxNode *node = shape.GetChildren().First();
+    while (node)
+    {
+      wxShape *childShape = (wxShape *)node->Data();
+      wxExpr *childExpr = new wxExpr("shape");
+      OnShapeSave(db, *childShape, *childExpr);
+      node = node->Next();
+    }
+  }
+
+  return TRUE;
+}
+
+bool wxDiagram::OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
+{
+  shape.ReadAttributes(&expr);
+  return TRUE;
+}
+
+bool wxDiagram::OnHeaderSave(wxExprDatabase& db, wxExpr& expr)
+{
+  return TRUE;
+}
+
+bool wxDiagram::OnHeaderLoad(wxExprDatabase& db, wxExpr& expr)
+{
+  return TRUE;
+}
+
+#endif
+
+void wxDiagram::SetCanvas(wxShapeCanvas *can)
+{
+  m_diagramCanvas = can;
+}
+
+// Find a shape by its id
+wxShape* wxDiagram::FindShape(long id) const
+{
+    wxNode* node = GetShapeList()->First();
+    while (node)
+    {
+        wxShape* shape = (wxShape*) node->Data();
+        if (shape->GetId() == id)
+            return shape;
+        node = node->Next();
+    }
+    return NULL;
+}
+
+
+//// Crossings classes
+
+wxLineCrossings::wxLineCrossings()
+{
+}
+
+wxLineCrossings::~wxLineCrossings()
+{
+    ClearCrossings();
+}
+
+void wxLineCrossings::FindCrossings(wxDiagram& diagram)
+{
+    ClearCrossings();
+    wxNode* node1 = diagram.GetShapeList()->First();
+    while (node1)
+    {
+        wxShape* shape1 = (wxShape*) node1->Data();
+        if (shape1->IsKindOf(CLASSINFO(wxLineShape)))
+        {
+            wxLineShape* lineShape1 = (wxLineShape*) shape1;
+            // Iterate through the segments
+            wxList* pts1 = lineShape1->GetLineControlPoints();
+            int i;
+            for (i = 0; i < (pts1->Number() - 1); i++)
+            {
+                wxRealPoint* pt1_a = (wxRealPoint*) (pts1->Nth(i)->Data());
+                wxRealPoint* pt1_b = (wxRealPoint*) (pts1->Nth(i+1)->Data());
+
+                // Now we iterate through the segments again
+
+                wxNode* node2 = diagram.GetShapeList()->First();
+                while (node2)
+                {
+                    wxShape* shape2 = (wxShape*) node2->Data();
+
+                    // Assume that the same line doesn't cross itself
+                    if (shape2->IsKindOf(CLASSINFO(wxLineShape)) && (shape1 != shape2))
+                    {
+                        wxLineShape* lineShape2 = (wxLineShape*) shape2;
+                        // Iterate through the segments
+                        wxList* pts2 = lineShape2->GetLineControlPoints();
+                        int j;
+                        for (j = 0; j < (pts2->Number() - 1); j++)
+                        {
+                            wxRealPoint* pt2_a = (wxRealPoint*) (pts2->Nth(j)->Data());
+                            wxRealPoint* pt2_b = (wxRealPoint*) (pts2->Nth(j+1)->Data());
+
+                            // Now let's see if these two segments cross.
+                            double ratio1, ratio2;
+                            oglCheckLineIntersection(pt1_a->x, pt1_a->y, pt1_b->x, pt1_b->y,
+                               pt2_a->x, pt2_a->y, pt2_b->x, pt2_b->y,
+                             & ratio1, & ratio2);
+
+                            if ((ratio1 < 1.0) && (ratio1 > -1.0))
+                            {
+                                // Intersection!
+                                wxLineCrossing* crossing = new wxLineCrossing;
+                                crossing->m_intersect.x = (pt1_a->x + (pt1_b->x - pt1_a->x)*ratio1);
+                                crossing->m_intersect.y = (pt1_a->y + (pt1_b->y - pt1_a->y)*ratio1);
+
+                                crossing->m_pt1 = * pt1_a;
+                                crossing->m_pt2 = * pt1_b;
+                                crossing->m_pt3 = * pt2_a;
+                                crossing->m_pt4 = * pt2_b;
+
+                                crossing->m_lineShape1 = lineShape1;
+                                crossing->m_lineShape2 = lineShape2;
+
+                                m_crossings.Append(crossing);
+                            }
+                        }
+                    }
+                    node2 = node2->Next();
+                }
+            }
+        }
+
+        node1 = node1->Next();
+    }
+}
+
+void wxLineCrossings::DrawCrossings(wxDiagram& diagram, wxDC& dc)
+{
+    dc.SetBrush(*wxTRANSPARENT_BRUSH);
+
+    long arcWidth = 8;
+
+    wxNode* node = m_crossings.First();
+    while (node)
+    {
+        wxLineCrossing* crossing = (wxLineCrossing*) node->Data();
+//        dc.DrawEllipse((long) (crossing->m_intersect.x - (arcWidth/2.0) + 0.5), (long) (crossing->m_intersect.y - (arcWidth/2.0) + 0.5),
+//           arcWidth, arcWidth);
+
+
+        // Let's do some geometry to find the points on either end of the arc.
+/*
+
+(x1, y1)
+    |\
+    | \
+    |  \
+    |   \
+    |    \
+    |    |\ c    c1
+    |  a | \
+         |  \
+    |     -  x <-- centre of arc
+ a1 |     b  |\
+    |        | \ c2
+    |     a2 |  \
+    |         -  \
+    |         b2  \
+    |              \
+    |_______________\ (x2, y2)
+          b1
+
+*/
+
+        double a1 = wxMax(crossing->m_pt1.y, crossing->m_pt2.y) - wxMin(crossing->m_pt1.y, crossing->m_pt2.y) ;
+        double b1 = wxMax(crossing->m_pt1.x, crossing->m_pt2.x) - wxMin(crossing->m_pt1.x, crossing->m_pt2.x) ;
+        double c1 = sqrt( (a1*a1) + (b1*b1) );
+
+        double c = arcWidth / 2.0;
+        double a = c * a1/c1 ;
+        double b = c * b1/c1 ;
+
+        // I'm not sure this is right, since we don't know which direction we should be going in - need
+        // to know which way the line slopes and choose the sign appropriately.
+        double arcX1 = crossing->m_intersect.x - b;
+        double arcY1 = crossing->m_intersect.y - a;
+
+        double arcX2 = crossing->m_intersect.x + b;
+        double arcY2 = crossing->m_intersect.y + a;
+
+        dc.SetPen(*wxBLACK_PEN);
+        dc.DrawArc( (long) arcX1, (long) arcY1, (long) arcX2, (long) arcY2,
+            (long) crossing->m_intersect.x, (long) crossing->m_intersect.y);
+
+        dc.SetPen(*wxWHITE_PEN);
+        dc.DrawLine( (long) arcX1, (long) arcY1, (long) arcX2, (long) arcY2 );
+
+        node = node->Next();
+    }
+}
+
+void wxLineCrossings::ClearCrossings()
+{
+    wxNode* node = m_crossings.First();
+    while (node)
+    {
+        wxLineCrossing* crossing = (wxLineCrossing*) node->Data();
+        delete crossing;
+        node = node->Next();
+    }
+    m_crossings.Clear();
+}
+
index c8a377f17b33859505271c49dd2de89499faaa3e..4003324e5f34541ff83ce9172daaefb1c2de1301 100644 (file)
@@ -451,19 +451,19 @@ void MyApp::GenerateSamples(const wxString& dir)
         wxMessageBox(msg);
     }
 
-    // OGLEdit
+    // OGLEdit. We have to do it the long way because we need to add the extra ogl.lib.
 
-    project.SetIncludeDirs(wxStringList("../../../../include", "../../src", 0));
-    project.SetResourceIncludeDirs(wxStringList("../../../../include", 0));
-    project.SetLibDirs(wxStringList("../../../../lib", 0));
-    project.SetDebugLibDirs(wxStringList("../../../../src/Debug", "../../src/Debug", "../../../../src/jpeg/Debug", "../../../../src/tiff/Debug", 0));
-    project.SetReleaseLibDirs(wxStringList("../../../../src/Release", "../../src/Release", "../../../../src/jpeg/Release", "../../../../src/tiff/Release", 0));
+    project.SetIncludeDirs(wxStringList("../../../include", 0));
+    project.SetResourceIncludeDirs(wxStringList("../../../include", 0));
+    project.SetLibDirs(wxStringList("../../../lib", 0));
+    project.SetDebugLibDirs(wxStringList("../../../src/Debug", "../../../src/ogl/Debug", "../../../src/jpeg/Debug", "../../../src/tiff/Debug", 0));
+    project.SetReleaseLibDirs(wxStringList("../../../src/Release", "../../../src/ogl/Release", "../../../src/jpeg/Release", "../../../src/tiff/Release", 0));
 
     project.SetExtraLibs(wxStringList("ogl.lib", 0));
 
     project.SetProjectName("OGLEditVC");
     project.SetTargetName("ogledit");
-    project.SetProjectPath(dir + wxString("/utils/ogl/samples/ogledit"));
+    project.SetProjectPath(dir + wxString("/samples/ogl/ogledit"));
     project.SetSourceFiles(wxStringList("ogledit.cpp", "doc.cpp", "palette.cpp", "view.cpp",
         "doc.h", "ogledit.h", "palette.h", "view.h",
         0));
@@ -476,16 +476,17 @@ void MyApp::GenerateSamples(const wxString& dir)
 
     // OGL Studio
 
-    project.SetIncludeDirs(wxStringList("../../../../include", "../../src", 0));
-    project.SetResourceIncludeDirs(wxStringList("../../../../include", 0));
-    project.SetLibDirs(wxStringList("../../../../lib", 0));
-    project.SetDebugLibDirs(wxStringList("../../../../src/Debug", "../../src/Debug", "../../../../src/jpeg/Debug", "../../../../src/tiff/Debug", 0));
-    project.SetReleaseLibDirs(wxStringList("../../../../src/Release", "../../src/Release", "../../../../src/jpeg/Release", "../../../../src/tiff/Release", 0));
+    project.SetIncludeDirs(wxStringList("../../../include", 0));
+    project.SetResourceIncludeDirs(wxStringList("../../../include", 0));
+    project.SetLibDirs(wxStringList("../../../lib", 0));
+    project.SetDebugLibDirs(wxStringList("../../../src/Debug", "../../../src/ogl/Debug", "../../../src/jpeg/Debug", "../../../src/tiff/Debug", 0));
+    project.SetReleaseLibDirs(wxStringList("../../../src/Release", "../../../src/ogl/Release", "../../../src/jpeg/Release", "../../../src/tiff/Release", 0));
+
     project.SetExtraLibs(wxStringList("ogl.lib", 0));
 
     project.SetProjectName("StudioVC");
     project.SetTargetName("studio");
-    project.SetProjectPath(dir + wxString("/utils/ogl/samples/studio"));
+    project.SetProjectPath(dir + wxString("/samples/ogl/studio"));
     project.SetSourceFiles(wxStringList("studio.cpp", "cspalette.cpp", "dialogs.cpp", "view.cpp",
         "doc.cpp", "mainfrm.cpp", "project.cpp", "shapes.cpp", "symbols.cpp", "csprint.cpp",
         "studio.h", "cspalette.h", "dialogs.h", "view.h",