--- /dev/null
+src/*.cpp
+src/*.c
+src/*.h
+src/*.rc
+src/*.def
+src/*.xbm
+src/make*.*
+src/*.txt
+src/*.ico
+src/*.bmp
+
+samples/ogledit/*.cpp
+samples/ogledit/*.c
+samples/ogledit/*.h
+samples/ogledit/*.rc
+samples/ogledit/*.def
+samples/ogledit/*.xbm
+samples/ogledit/make*.*
+samples/ogledit/*.txt
+
+samples/ogledit/*.ico
+samples/ogledit/*.bmp
+samples/ogledit/bitmaps/*.bmp
+samples/ogledit/bitmaps/*.gif
+samples/ogledit/bitmaps/*.xbm
+
+lib/dummy
+
+distrib/*.rsp
+distrib/*.bat
+
+docs/*.txt
+docs/*.tex
+docs/*.ini
+docs/*.hpj
+docs/*.ps
+docs/*.eps
+docs/*.cnt
+docs/*.bmp
+docs/*.gif
+docs/*.hlp
--- /dev/null
+@echo off
+rem Tar up an external distribution of OGL
+
+if "%1" == "" goto usage
+if "%2" == "" goto usage
+echo About to archive an external OGL distribution:
+echo From %1
+echo To %2\ogl.tgz
+echo CTRL-C if this is not correct.
+inkey /W4 `Press any key to continue...` %%input
+
+erase %2\ogl.tgz
+cd %1
+
+rem First, expand the wildcards in the ogl.rsp file
+
+rem Create empty list file
+erase %1\distrib\ogl.lis
+c:\bin\touch %1\distrib\ogl.lis
+
+rem Create a .rsp file with backslashes instead
+rem of forward slashes
+rem No need if using ls2 (from UNIX95 distribution)
+rem sed -e "s/\//\\/g" %1\distrib\ogl.rsp > %1\distrib\ogl.rs2
+
+set len=%@LINES[%1\distrib\ogl.rsp]
+rem set len=%@DEC[%len]
+do i = 0 to %len by 1
+ set line=%@LINE[%1\distrib\ogl.rsp,%i]
+ if NOT "%line" == "" ls2 -1 %line >> %1\distrib\ogl.lis
+enddo
+
+tar -c -T %1\distrib\ogl.lis
+move archive.tar ogl.tar
+gzip ogl.tar
+move ogl.taz %2\ogl.tgz
+
+echo OGL archived.
+goto end
+
+:usage
+echo DOS OGL distribution.
+echo Usage: tarogl source destination
+echo e.g. tarogl c:\wx\utils\ogl c:\wx\utils\ogl\deliver
+
+:end
+
+
--- /dev/null
+@echo off
+rem Zip up an external distribution of OGL
+
+if "%1" == "" goto usage
+if "%2" == "" goto usage
+echo About to archive an external OGL distribution:
+echo From %1
+echo To %2\ogl.zip
+echo CTRL-C if this is not correct.
+inkey /W4 `Press any key to continue...` %%input
+
+erase %2\ogl.zip
+cd %1
+zip -P %3 %4 %5 %6 %7 %8 %2\ogl.zip @%1\distrib\ogl.rsp
+
+echo OGL archived.
+goto end
+
+:usage
+echo DOS OGL distribution.
+echo Usage: zipogl source destination
+echo e.g. zipogl c:\wx\utils\ogl c:\wx\utils\ogl\deliver
+
+:end
+
+
--- /dev/null
+\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}
+
--- /dev/null
+\chapter{Change log}
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+Version 2.0, June 1st 1996
+
+\begin{itemize}\itemsep=0pt
+\item First publically released version.
+\end{itemize}
--- /dev/null
+\chapter{Class reference}\label{classref}
+\setheader{{\it CHAPTER \thechapter}}{}{}{}{}{{\it CHAPTER \thechapter}}%
+\setfooter{\thepage}{}{}{}{}{\thepage}
+
+These are the main \ogl\ classes.
+
+\section{\class{OGLConstraint}: wxObject}\label{oglconstraint}
+
+\overview{wxCompositeShape overview}{compositeshapeoverview}
+
+An OGLConstraint object helps specify how child shapes are laid out with respect
+to siblings and parents.
+
+\membersection{OGLConstraint::OGLConstraint}\label{oglconstraintconstr}
+
+\func{void}{OGLConstraint}{\void}
+
+Default constructor.
+
+\func{void}{OGLConstraint}{\param{int}{ type}, \param{wxShape *}{constraining}, \param{wxList\& }{constrained}}
+
+Constructor.
+
+{\it constraining} is the shape which is used as the reference for positioning the {\it constrained} objects.
+
+{\it constrained} contains a list of wxShapes which are to be constrained (with respect
+to {\it constraining}) using {\it type}.
+
+{\it 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{OGLConstraint::\destruct{OGLConstraint}}
+
+\func{void}{\destruct{OGLConstraint}}{\void}
+
+Destructor.
+
+\membersection{OGLConstraint::Equals}
+
+\func{bool}{Equals}{\param{float}{ x}, \param{float}{ y}}
+
+Returns TRUE if {\it x} and {\it y} are approximately equal (for the purposes
+of evaluating the constraint).
+
+\membersection{OGLConstraint::Evaluate}
+
+\func{bool}{Evaluate}{\void}
+
+Evaluates this constraint, returning TRUE if anything changed.
+
+\membersection{OGLConstraint::SetSpacing}\label{oglconstraintsetspacing}
+
+\func{void}{SetSpacing}{\param{float}{ x}, \param{float}{ y}}
+
+Sets the horizontal and vertical spacing for the constraint.
+
+
+\section{\class{wxBitmapShape}: wxRectangleShape}\label{wxbitmapshape}
+
+Draws a bitmap (non-resizable).
+
+See also \helpref{wxRectangleShape}{wxrectangleshape}.
+
+\membersection{wxBitmapShape::wxBitmapShape}
+
+\func{void}{wxBitmapShape}{\void}
+
+Constructor.
+
+\membersection{wxBitmapShape::\destruct{wxBitmapShape}}
+
+\func{void}{\destruct{wxBitmapShape}}{\void}
+
+Destructor.
+
+\membersection{wxBitmapShape::GetDeleteBitmap}
+
+\func{bool}{GetDeleteBitmap}{\void}
+
+Returns TRUE if the bitmap will be deleted when the shape is deleted.
+
+\membersection{wxBitmapShape::GetBitmap}
+
+\func{wxBitmap\&}{GetBitmap}{\void}
+
+Returns a reference to the bitmap associated with this shape.
+
+\membersection{wxBitmapShape::GetFilename}
+
+\func{wxString}{GetFilename}{\void}
+
+Returns the bitmap filename.
+
+\membersection{wxBitmapShape::SetDeleteBitmap}
+
+\func{void}{SetDeleteBitmap}{\param{bool}{ deleteBitmap}}
+
+Determines whether the bitmap will be deleted when the shape is deleted.
+
+\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}: wxObject}\label{wxdiagram}
+
+Encapsulates an entire diagram, with methods for reading/writing and drawing.
+
+\membersection{wxDiagram::wxDiagram}
+
+\func{void}{wxDiagram}{\void}
+
+Constructor.
+
+\membersection{wxDiagram::\destruct{wxDiagram}}
+
+\func{void}{\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}{\void}
+
+Clears the device context associated with the diagram.
+
+\membersection{wxDiagram::DeleteAllShapes}
+
+\func{void}{DeletesAllShapes}{\void}
+
+Removes and deletes all shapes in the diagram.
+
+\membersection{wxDiagram::DrawOutline}
+
+\func{void}{DrawOutline}{\param{float}{ x1}, \param{float}{ y1}, \param{float}{ x2}, \param{float}{ y2}}
+
+Draws an outline rectangle on the current device context.
+
+\membersection{wxDiagram::GetCanvas}
+
+\func{wxCanvas *}{GetCanvas}{\void}
+
+Returns the canvas associated with this diagram.
+
+\membersection{wxDiagram::GetGridSpacing}
+
+\func{float}{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}
+
+\func{wxList *}{GetShapeList}{\void}
+
+Returns a pointer to the internal shape list.
+
+\membersection{wxDiagram::GetQuickEditMode}
+
+\func{bool}{GetQuickEditMode}{\void}
+
+Returns quick edit mode.
+
+\membersection{wxDiagram::GetSnapToGrid}
+
+\func{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 PrologIO file.
+
+\membersection{wxDiagram::OnDatabaseLoad}
+
+\func{void}{OnDatabaseLoad}{\param{PrologDatabase\&}{ database}}
+
+Called just after the nodes and lines have been read from the PrologDatabase. You may override this;
+the default member does nothing.
+
+
+\membersection{wxDiagram::OnDatabaseSave}
+
+\func{void}{OnDatabaseSave}{\param{PrologDatabase\&}{ database}}
+
+Called just after the nodes and lines have been written to the PrologDatabase. You may override this;
+the default member does nothing.
+
+\membersection{wxDiagram::OnHeaderLoad}
+
+\func{bool}{OnHeaderLoad}{\param{PrologDatabase\&}{ database}, \param{PrologExpr\&}{ 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{PrologDatabase\&}{ database}, \param{PrologExpr\&}{ 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{PrologDatabase\&}{ database}, \param{wxShape\&}{ shape}, \param{PrologExpr\&}{ expr}}
+
+Called to read the shape from the {\it expr}. You may override this, but call this function first.
+The default member calls ReadPrologAttributes for the shape.
+
+\membersection{wxDiagram::OnShapeSave}
+
+\func{bool}{OnShapeSave}{\param{PrologDatabase\&}{ database}, \param{wxShape\&}{ shape}, \param{PrologExpr\&}{ 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 WritePrologAttributes 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{PrologDatabase\&}{ database}}
+
+Reads container geometry from a PrologDatabase, linking up nodes which
+are part of a composite. You probably won't need to redefine this.
+
+\membersection{wxDiagram::ReadLines}
+
+\func{void}{ReadLines}{\param{PrologDatabase\&}{ database}}
+
+Reads lines from a PrologDatabase. You probably won't need to redefine this.
+
+\membersection{wxDiagram::ReadNodes}
+
+\func{void}{ReadNodes}{\param{PrologDatabase\&}{ database}}
+
+Reads nodes from a PrologDatabase. You probably won't need to redefine this.
+
+
+\membersection{wxDiagram::RecentreAll}
+
+\func{void}{RecentreAll}{\void}
+
+Make sure all text that should be centred, is centred.
+
+\membersection{wxDiagram::Redraw}
+
+\func{void}{Redraw}{\void}
+
+Draws the shapes in the diagram on the currently set 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 PrologIO file.
+
+\membersection{wxDiagram::SetCanvas}
+
+\func{void}{SetCanvas}{\param{wxShapeCanvas *}{canvas}}
+
+Sets the canvas associated with this diagram.
+
+\membersection{wxDiagram::SetGridSpacing}
+
+\func{void}{SetGridSpacing}{\param{float}{ 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{float *}{x}, \param{float *}{y}}
+
+`Snaps' the coordinate to the nearest grid position, if snap-to-grid is on.
+
+
+\section{\class{wxDrawnShape}: wxRectangleShape}\label{wxdrawnshape}
+
+Draws a pseduo-metafile shape, which can be loaded from a simple Windows metafile.
+
+See also \helpref{wxRectangleShape}{wxrectangleshape}.
+
+\membersection{wxDrawnShape::wxDrawnShape}
+
+\func{void}{wxDrawnShape}{\void}
+
+Constructor.
+
+\membersection{wxDrawnShape::\destruct{wxDrawnShape}}
+
+\func{void}{\destruct{wxDrawnShape}}{\void}
+
+Destructor.
+
+\membersection{wxDrawnShape::GetMetaFile}
+
+\func{wxPseudoMetaFile\& }{GetMetaFile}{\void}
+
+Returns a reference to the internal `pseudo-metafile'.
+
+\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{float }{x}, \param{float }{y}, \param{float }{theta}}
+
+Rotate about the given axis by the given amount in radians.
+
+\membersection{wxDrawnShape::Scale}
+
+\func{void}{Scale}{\param{float }{sx}, \param{float }{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{float }{x}, \param{float }{y}}
+
+Translates the shape by the given amount.
+
+\section{\class{wxCircleShape}: wxEllipseShape}\label{wxcircleshape}
+
+An wxEllipseShape whose width and height are the same.
+
+See also \helpref{wxEllipseShape}{wxellipseshape}.
+
+\membersection{wxCircleShape::wxCircleShape}
+
+\func{void}{wxCircleShape}{\param{float}{ width = 0.0}}
+
+Constructor.
+
+\membersection{wxCircleShape::\destruct{wxCircleShape}}
+
+\func{void}{\destruct{wxCircleShape}}{\void}
+
+Destructor.
+
+
+\section{\class{wxCompositeShape}: wxRectangleShape}\label{wxcompositeshape}
+
+\overview{wxCompositeShape overview}{compositeshapeoverview}
+
+This is an object with a list of child objects, and a list of size
+and positioning constraints between the children.
+
+See also \helpref{wxRectangleShape}{wxrectangleshape}.
+
+\membersection{wxCompositeShape::wxCompositeShape}
+
+\func{void}{wxCompositeShape}{\void}
+
+Constructor.
+
+\membersection{wxCompositeShape::\destruct{wxCompositeShape}}
+
+\func{void}{\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{OGLConstraint *}{AddConstraint}{\param{OGLConstraint *}{constraint}}
+
+\func{OGLConstraint *}{AddConstraint}{\param{int}{ type}, \param{wxShape *}{constraining}, \param{wxList\&}{constrained}}
+
+\func{OGLConstraint *}{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{OGLConstraint *}{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{OGLConstraint *}{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}
+
+\func{wxList\&}{GetConstraints}{\void}
+
+Returns a reference to the list of constraints.
+
+\membersection{wxCompositeShape::GetDivisions}
+
+\func{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}: wxRectangleShape}\label{wxdividedshape}
+
+\overview{wxDividedShape overview}{dividedshapeoverview}
+
+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.
+
+See also \helpref{wxRectangleShape}{wxrectangleshape}.
+
+\membersection{wxDividedShape::wxDividedShape}
+
+\func{void}{wxDividedShape}{\param{float}{ width = 0.0}, \param{float}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxDividedShape::\destruct{wxDividedShape}}
+
+\func{void}{\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}: wxCompositeShape}\label{wxdivisionshape}
+
+\overview{wxCompositeShape overview}{compositeshapeoverview}
+
+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.
+
+See also \helpref{wxCompositeShape}{wxcompositeshape}.
+
+\membersection{wxDivisionShape::wxDivisionShape}
+
+\func{void}{wxDivisionShape}{\void}
+
+Constructor.
+
+\membersection{wxDivisionShape::\destruct{wxDivisionShape}}
+
+\func{void}{\destruct{wxDivisionShape}}{\void}
+
+Destructor.
+
+\membersection{wxDivisionShape::AdjustBottom}
+
+\func{void}{AdjustBottom}{\param{float}{ 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{float}{ 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{float}{ 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{float}{ 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{float}{ 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{float}{ x}, \param{float}{ 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}: wxShape}\label{wxellipseshape}
+
+The wxEllipseShape behaves similarly to the wxRectangleShape but is
+elliptical.
+
+See also \helpref{wxShape}{wxshape}.
+
+\membersection{wxEllipseShape::wxEllipseShape}
+
+\func{void}{wxEllipseShape}{\param{float}{ width = 0.0}, \param{float}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxEllipseShape::\destruct{wxEllipseShape}}
+
+\func{void}{\destruct{wxEllipseShape}}{\void}
+
+Destructor.
+
+\section{\class{wxLineShape}: wxShape}\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.
+
+See also \helpref{wxShape}{wxshape}.
+
+\membersection{wxLineShape::wxLineShape}
+
+\func{void}{wxLineShape}{\void}
+
+\func{void}{wxLineShape}{\param{wxList *}{list}}
+
+Constructors. In the second (usual) form, supply a list of wxPoints, each to be used
+as a `control point' for the line. The minimum number of points is two.
+
+\membersection{wxLineShape::\destruct{wxLineShape}}
+
+\func{void}{\destruct{wxLineShape}}{\void}
+
+Destructor.
+
+\membersection{wxLineShape::AddArrow}
+
+\func{void}{AddArrow}{\param{WXTYPE}{ type}, \param{bool}{ end = ARROW\_POSITION\_END}, \param{float}{ arrowSize = 10.0},
+ \param{float}{ xOffset = 0.0}, \param{const wxString\& }{name = NULL}, \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{float}{ 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}{\void}
+
+Draws all arrows.
+
+\membersection{wxLineShape::DrawRegion}
+
+\func{void}{DrawRegion}{\param{wxShapeRegion *}{region}, \param{float}{ x}, \param{float}{ y}}
+
+Format one region at this position.
+
+\membersection{wxLineShape::EraseRegion}
+
+\func{void}{EraseRegion}{\param{wxShapeRegion *}{region}, \param{float}{ x}, \param{float}{ 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{float *}{fromX}, \param{float *}{fromY}, \param{float *}{toX}, \param{float *}{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{float }{x}, \param{float }{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{float}{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}
+
+\func{int}{GetAttachmentFrom}{\void}
+
+Returns the attachment point on the `from' node.
+
+\membersection{wxLineShape::GetAttachmentTo}
+
+\func{int}{GetAttachmentTo}{\void}
+
+Returns the attachment point on the `to' node.
+
+\membersection{wxLineShape::GetEnds}
+
+\func{void}{GetEnds}{\param{float *}{x1}, \param{float *}{y1}, \param{float *}{x2}, \param{float *}{y2}}
+
+Gets the visible endpoints of the lines for drawing between two objects.
+
+\membersection{wxLineShape::GetFormatMode}
+
+\func{int}{GetFormatMode}{\param{int}{ regionId = 0}}
+
+Returns the format mode for this region. See als \helpref{SetFormatMode}{setformatmode}.
+
+\membersection{wxLineShape::GetFrom}
+
+\func{wxShape *}{GetFrom}{\void}
+
+Gets the `from' object.
+
+\membersection{wxLineShape::GetLabelPosition}
+
+\func{void}{GetLabelPosition}{\param{int}{ position}, \param{float *}{x}, \param{float *}{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}
+
+\func{void}{MakeLineControlPoints}{\param{int}{ n}}
+
+Make a given number of control points.
+
+\membersection{wxLineShape::OnMoveLink}
+
+\func{void}{OnMoveLink}{\void}
+
+Called when a connected object has moved, to move the link to
+correct position.
+
+\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::SetEnds}
+
+\func{void}{SetEnds}{\param{float}{ x1}, \param{float}{ y1}, \param{float}{ x2}, \param{float}{ 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}{\void}
+
+Straighten verticals and horizontals.
+
+\membersection{wxLineShape::Unlink}
+
+\func{void}{Unlink}{\void}
+
+Unlinks the line from the nodes at either end.
+
+
+\section{\class{wxPolygonShape}: wxShape}\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.
+
+See also \helpref{wxShape}{wxshape}.
+
+\membersection{wxPolygonShape::wxPolygonShape}
+
+\func{void}{wxPolygonShape}{void}
+
+Constructor. Call Create to specify the polygon's vertices.
+
+\membersection{wxPolygonShape::\destruct{wxPolygonShape}}
+
+\func{void}{\destruct{wxPolygonShape}}{\void}
+
+Destructor.
+
+\membersection{wxPolygonShape::Create}
+
+\func{void}{Create}{\param{wxList *}{points}}
+
+Takes a list of wxPoints; 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.
+
+\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}: wxShape}\label{wxrectangleshape}
+
+The wxRectangleShape has rounded or square corners.
+
+See also \helpref{wxShape}{wxshape}.
+
+\membersection{wxRectangleShape::wxRectangleShape}
+
+\func{void}{wxRectangleShape}{\param{float}{ width = 0.0}, \param{float}{ height = 0.0}}
+
+Constructor.
+
+\membersection{wxRectangleShape::\destruct{wxRectangleShape}}
+
+\func{void}{\destruct{wxRectangleShape}}{\void}
+
+Destructor.
+
+\membersection{wxRectangleShape::SetCornerRadius}
+
+\func{void}{SetCornerRadius}{\param{float}{ 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}: wxObject}\label{wxpseudometafile}
+
+A simple metafile-like class which can load data from a Windows metafile on all platforms.
+
+
+\section{\class{wxShape}: wxShapeEvtHandler}\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.
+
+See also \helpref{wxShapeEvtHandler}{wxshapeevthandler}.
+
+\membersection{wxShape::wxShape}
+
+\func{void}{wxShape}{\param{wxShapeCanvas *}{canvas = NULL}}
+
+Constructs a new wxShape.
+
+\membersection{wxShape::\destruct{wxShape}}
+
+\func{void}{\destruct{wxShape}}{\void}
+
+Destructor.
+
+\membersection{wxShape::AddLine}
+
+\func{void}{AddLine}{\param{wxLineShape *}{line}, \param{wxShape *}{other}, \param{int}{attachFrom = 0}, \param{int}{ attachTo = 0}}
+
+Adds a line between the specified canvas objects, at the specified attachment points.
+
+\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 object's default text region.
+
+\membersection{wxShape::AddToCanvas}
+
+\func{void}{AddToCanvas}{\param{wxShapeCanvas *}{theCanvas}, \param{wxShape *}{addAfter=NULL}}
+
+Adds the object to the canvas's object list. If {\it addAfter} is
+non-NULL, will add the shape after this one.
+
+\membersection{wxShape::AncestorSelected}
+
+\func{bool}{AncestorSelected}{\void}
+
+TRUE if the object's ancestor is currently selected.
+
+\membersection{wxShape::AssignNewIds}
+
+\func{void}{AssignNewIds}{\void}
+
+Assigns new ids to this image and its children.
+
+\membersection{wxShape::Attach}
+
+\func{void}{Attach}{\param{wxShapeCanvas *}{can}}
+
+Sets the object's internal canvas pointer to point to the given canvas.
+
+\membersection{wxShape::CalculateSize}
+
+\func{void}{CalculateSize}{\void}
+
+Called to calculate the object's size if dependent on children sizes.
+
+\membersection{wxShape::ClearAttachments}
+
+\func{void}{ClearAttachments}{\void}
+
+Clears internal custom attachment point objects (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 object's constraints (if any). Applicable
+only to wxCompositeShape, does nothing if the object is of
+a different class.
+
+\membersection{wxShape::Copy}
+
+\func{void}{Copy}{\param{wxShape\&}{ copy}}
+
+Copy the object into the given object. Every derived class must have one of these.
+
+\membersection{wxShape::CreateNewCopy}
+
+\func{wxShape *}{CreateNewCopy}{\param{wxShapeCanvas *}{theCanvas = NULL}}
+
+Creates and returns a new copy of this object (calling PrivateCopy). 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.
+
+\membersection{wxShape::DeleteControlPoints}
+
+\func{void}{DeleteControlPoints}{\void}
+
+Deletes the control points (or handles) for the object. Does not redraw
+the object.
+
+\membersection{wxShape::Detach}
+
+\func{void}{Detach}{\void}
+
+Disassociates the object from its canvas by setting the internal object
+canvas pointer to NULL.
+
+\membersection{wxShape::Draggable}
+
+\func{bool}{Draggable}{\void}
+
+TRUE if the object may be dragged by the user.
+
+\membersection{wxShape::Draw}
+
+\func{void}{Draw}{\void}
+
+Draws the whole object 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}{\void}
+
+Draws the internal graphic of the object (such as
+text).
+
+Do not override this function: override OnDrawContents, which is called
+by this function.
+
+\membersection{wxShape::DrawLines}
+
+\func{void}{DrawLinks}{\param{int}{ attachment = -1}}
+
+Draws any lines linked to this object.
+
+\membersection{wxShape::Erase}
+
+\func{void}{Erase}{\void}
+
+Erases the object, but does not repair damage caused to other
+objects.
+
+\membersection{wxShape::EraseContents}
+
+\func{void}{EraseContents}{\void}
+
+Erases the object contents, that is, the area within the object's
+minimum bounding box.
+
+\membersection{wxShape::EraseLinks}
+
+\func{void}{EraseLinks}{\param{int}{ attachment = -1}}
+
+Erases links attached to this object, but does not repair
+damage caused to other objects.
+
+\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 object.
+
+\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}
+
+\func{void}{GetAttachmentMode}{\void}
+
+Returns the attachment mode. See \helpref{SetAttachmentMode}{setattachmentmode}.
+
+\membersection{wxShape::GetAttachmentPosition}
+
+\func{void}{GetAttachmentPosition}{\param{int}{ attachment}, \param{float *}{x}, \param{float *}{y}, \param{int}{ nth = 0}, \param{int}{ noArcs = 1}}
+
+Gets the position at which the given attachment point should be drawn.
+
+\membersection{wxShape::GetBoundingBoxMax}
+
+\func{void}{GetBoundingBoxMax}{\param{float *}{width}, \param{float *}{height}}
+
+Gets the maximum bounding box for the object, taking into
+account external features such as shadows.
+
+\membersection{wxShape::GetBoundingBoxMin}
+
+\func{void}{GetBoundingBoxMin}{\param{float *}{width}, \param{float *}{height}}
+
+Gets the minimum bounding box for the object, that defines
+the area available for drawing the contents (such as text).
+
+\membersection{wxShape::GetBrush}
+
+\func{wxBrush *}{GetBrush}{\void}
+
+Returns the brush used for filling the shape.
+
+\membersection{wxShape::GetCanvas}
+
+\func{wxShapeCanvas *}{GetCanvas}{\void}
+
+Gets the internal canvas pointer.
+
+\membersection{wxShape::GetCentreResize}
+
+\func{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}
+
+\func{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 object (NULL if there is
+none).
+
+\membersection{wxShape::GetDisableLabel}
+
+\func{bool}{GetDisableLabel}{\void}
+
+Returns TRUE if the default region will not be shown, FALSE otherwise.
+
+\membersection{wxShape::GetEventHandler}
+
+\func{wxShapeEvtHandler *}{GetEventHandler}{\void}
+
+Returns the event handler for this shape.
+
+\membersection{wxShape::GetFixedHeight}
+
+\func{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}
+
+\func{bool}{GetFixedWidth}{\void}
+
+Returns TRUE if the shape cannot be resized in the horizontal plane.
+
+\membersection{wxShape::GetFont}
+
+\func{int}{GetFont}{\param{int}{ regionId = 0}}
+
+Gets the font for the specified text region.
+
+\membersection{wxShape::GetFunctor}
+
+\func{char*}{GetFunctor}{\void}
+
+Gets a string representing the type of the object, to be used when
+writing out object descriptions to a file. This is overridden by
+each derived object class to provide an appropriate type string.
+
+\membersection{wxShape::GetId}
+
+\func{long}{GetId}{\void}
+
+Returns the integer identifier for this shape.
+
+\membersection{wxShape::GetLines}
+
+\func{wxList\&}{GetLines}{\void}
+
+Returns a reference to the list of lines connected to this shape.
+
+\membersection{wxShape::GetNumberOfAttachments}
+
+\func{int}{GetNumberOfAttachments}{\void}
+
+Gets the number of attachment points for this object.
+
+\membersection{wxShape::GetNumberOfTextRegions}
+
+\func{int}{GetNumberOfTextRegions}{\void}
+
+Gets the number of text regions for this object.
+
+\membersection{wxShape::GetParent}
+
+\func{wxShape *}{GetParent}{\void}
+
+Returns the parent of this shape, if it is part of a composite.
+
+\membersection{wxShape::GetPen}
+
+\func{wxPen *}{GetPen}{\void}
+
+Returns the pen used for drawing the shape's outline.
+
+\membersection{wxShape::GetPerimeterPoint}
+
+\func{bool}{GetPerimeterPoint}{\param{float}{ x1}, \param{float}{ y1}, \param{float}{ x2}, \param{float}{ y2}, \param{float *}{x3}, \param{float *}{y3}}
+
+Gets the point at which the line from (x1, y1) to (x2, y2) hits the object. 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{SetRegionName}{setregionname}.
+
+\membersection{wxShape::GetRegions}\label{getregions}
+
+\func{wxList\&}{GetRegions}{\void}
+
+Returns the list of wxShapeRegions.
+
+\membersection{wxShape::GetRotation}
+
+\func{float}{GetRotatation}{\void}
+
+Returns the angle of rotation in radians.
+
+\membersection{wxShape::GetSensitivityFilter}
+
+\func{void}{GetSensitivityFilter}{\void}
+
+Returns the sensitivity filter, a bitlist of values. See \helpref{SetSensitivityFilter}{setsensitivityfilter}.
+
+\membersection{wxShape::GetShadowMode}
+
+\func{int}{SetShadowMode}{\void}
+
+Returns the shadow mode. See \helpref{SetShadowMode}{setshadowmode}.
+
+\membersection{wxShape::GetSpaceAttachments}
+
+\func{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}
+
+\func{wxString}{GetTextColour}{\param{int}{ regionId = 0}}
+
+Gets the colour for the specified text region.
+
+\membersection{wxShape::GetTopAncestor}
+
+\func{wxShape *}{GetTopAncestor}{\void}
+
+Returns the top-most ancestor of this shape (the root of the composite).
+
+\membersection{wxShape::GetX}
+
+\func{float}{GetX}{\void}
+
+Gets the x position of the centre of the object.
+
+\membersection{wxShape::GetY}
+
+\func{float}{GetY}{\void}
+
+Gets the y position of the centre of the object.
+
+\membersection{wxShape::HitTest}
+
+\func{bool}{HitTest}{\param{float}{ x}, \param{float}{ y}, \param{int *}{attachment}, \param{float *}{distance}}
+
+Given a point on a canvas, returns TRUE if the point was on the object, 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 object list of {\it canvas}.
+
+\membersection{wxShape::IsHighlighted}
+
+\func{bool}{IsHighlighted}{\void}
+
+Returns TRUE if the shape is highlighted. Shape highlighting is unimplemented.
+
+\membersection{wxShape::IsShown}
+
+\func{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 object.
+
+\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}
+
+\func{void}{Move}{\param{float}{ x1}, \param{float}{ y1}, \param{bool}{ display = TRUE}}
+
+Move the object to the given position, redrawing if {\it display} is TRUE.
+
+\membersection{wxShape::MoveLineToNewAttachment}
+
+\func{void}{MoveLineToNewAttachment}{\param{wxLineShape *}{toMove}, \param{float}{ x}, \param{float}{ y}}
+
+Move the given line (which must already be attached to the object) to
+a different attachment point on the object.
+
+\membersection{wxShape::MoveLinks}
+
+\func{void}{MoveLinks}{\void}
+
+Redraw all the lines attached to the object.
+
+\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::NewCopy}
+
+\func{wxShape *}{NewCopy}{\void}
+
+Returns a new instance, and does the copy for this class. Should be
+defined for each object class.
+
+\membersection{wxShape::PrivateCopy}
+
+\func{wxShape *}{PrivateCopy}{\void}
+
+Returns a new instance, and does the copy for this class. This member
+should be define for each class.
+
+\membersection{wxShape::Rotate}
+
+\func{void}{Rotate}{\param{float }{x}, \param{float }{y}, \param{float }{theta}}
+
+Rotate about the given axis by the given amount in radians (does nothing
+for most objects). But even non-rotating objects should record their
+notional rotation in case it's important (e.g. in dog-leg code).
+
+\membersection{wxShape::ReadConstraints}
+
+\func{void}{ReadConstraints}{\param{PrologExpr *}{clause}, \param{PrologDatabase *}{database}}
+
+If the object is a composite, it may have constraints that need to be read in in a separate pass.
+
+\membersection{wxShape::ReadPrologAttributes}
+
+\func{void}{ReadPrologAttributes}{\param{PrologExpr *}{clause}}
+
+Reads the attributes (data member values) from the given expression.
+
+\membersection{wxShape::ReadRegions}
+
+\func{void}{ReadRegions}{\param{PrologExpr *}{clause}}
+
+Reads in the regions.
+
+\membersection{wxShape::Recentre}
+
+\func{void}{Recentre}{\void}
+
+Does recentring (or other formatting) for all the text regions for this object.
+
+\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
+object'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}
+
+\func{bool}{Recompute}{\void}
+
+Recomputes any constraints associated with the object (normally
+applicable to wxCompositeShapes only, but harmless for other
+classes of object).
+
+\membersection{wxShape::RemoveLine}
+
+\func{void}{RemoveLine}{\param{wxLineShape *}{line}}
+
+Removes the given line from the object's list of attached lines.
+
+\membersection{wxShape::Select}
+
+\func{void}{Select}{\param{bool}{ select = TRUE}}
+
+Selects or deselects the given object, drawing or erasing control points
+(handles) as necessary.
+
+\membersection{wxShape::Selected}
+
+\func{bool}{Selected}{\void}
+
+TRUE if the object is currently selected.
+
+\membersection{wxShape::SetAttachmentMode}\label{setattachmentmode}
+
+\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 object.
+If FALSE, lines will be drawn as if to the centre of the object.
+
+\membersection{wxShape::SetBrush}
+
+\func{void}{SetBrush}{\param{wxBrush *}{brush}}
+
+Sets the brush for filling the object's shape.
+
+\membersection{wxShape::SetCanvas}
+
+\func{void}{SetCanvas}{\param{wxShapeCanvas *}{theCanvas}}
+
+Identical to Attach.
+
+\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::SetDC}
+
+\func{void}{SetDC}{\param{wxDC *}{dc}}
+
+Sets the device context associated with the object. This may temporarily be set
+to (for example) a printer device context, so that the object will be printed
+instead of drawn on a canvas.
+
+\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 object 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 object 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::SetPen}
+
+\func{void}{SetPen}{\param{wxPen *}{pen}}
+
+Sets the pen for drawing the object's outline.
+
+\membersection{wxShape::SetRegionName}\label{setregionname}
+
+\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{setsensitivityfilter}
+
+\func{void}{SetSensitivityFilter}{\param{int}{ sens=OP\_ALL}, \param{bool}{ recursive = FALSE}}
+
+Sets the object 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{setshadowmode}
+
+\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{float}{ x}, \param{float}{ y}, \param{bool}{ recursive = TRUE}}
+
+Sets the object'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{float}{ x}}
+
+Sets the {\it x} position of the shape.
+
+\membersection{wxShape::SetX}
+
+\func{void}{SetY}{\param{float}{ 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 object. 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 object should be drawn.
+
+\membersection{wxShape::Unlink}
+
+\func{void}{Unlink}{\void}
+
+If the shape is a line, unlinks the nodes attached to the object, removing itself from the list of
+lines for each of the `to' and `from' nodes.
+
+\membersection{wxShape::WritePrologAttributes}
+
+\func{void}{WritePrologAttributes}{\param{PrologExpr *}{clause}}
+
+Writes the object's attributes (data member values) into the given expression.
+
+\membersection{wxShape::WriteRegions}
+
+\func{void}{WriteRegions}{\param{PrologExpr *}{clause}}
+
+Writes the regions.
+
+
+
+\section{\class{wxShapeCanvas}: wxCanvas}\label{wxshapecanvas}
+
+A canvas for drawing diagrams on.
+
+\membersection{wxShapeCanvas::wxShapeCanvas}
+
+\func{void}{wxShapeCanvas}{\void}
+
+Constructor.
+
+\membersection{wxShapeCanvas::\destruct{wxShapeCanvas}}
+
+\func{void}{\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::Clear}
+
+\func{void}{Clear}{\void}
+
+Clears the device context associated with the diagram.
+
+\membersection{wxShapeCanvas::DrawOutline}
+
+\func{void}{DrawOutline}{\param{float}{ x1}, \param{float}{ y1}, \param{float}{ x2}, \param{float}{ y2}}
+
+Draws an outline rectangle on the current device context.
+
+\membersection{wxShapeCanvas::FindShape}
+
+\func{wxShape *}{FindShape}{\param{float}{ x1}, \param{float}{ 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{float}{ x1}, \param{float}{ 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::GetCanvas}
+
+\func{wxCanvas *}{GetCanvas}{\void}
+
+Returns the canvas associated with this diagram.
+
+\membersection{wxShapeCanvas::GetDC}
+
+\func{wxDC *}{GetDC}{\void}
+
+Returns the device context associated with this diagram.
+
+\membersection{wxShapeCanvas::GetGridSpacing}
+
+\func{float}{GetGridSpacing}{\void}
+
+Returns the grid spacing.
+
+\membersection{wxShapeCanvas::GetMouseTolerance}
+
+\func{int}{GetMouseTolerance}{\void}
+
+Returns the tolerance within which a mouse move is ignored.
+
+\membersection{wxShapeCanvas::GetShapeList}
+
+\func{wxList *}{GetShapeList}{\void}
+
+Returns a pointer to the internal shape list.
+
+\membersection{wxShapeCanvas::GetQuickEditMode}
+
+\func{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{onbegindragleft}
+
+\func{void}{OnBeginDragLeft}{\param{float}{ x}, \param{float}{ 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{OnDragLeft}{ondragleft}, \helpref{OnEndDragLeft}{onenddragleft}.
+
+\membersection{wxShapeCanvas::OnBeginDragRight}\label{onbegindragright}
+
+\func{void}{OnBeginDragRight}{\param{float}{ x}, \param{float}{ 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{OnDragRight}{ondragright}, \helpref{OnEndDragRight}{onenddragright}.
+
+\membersection{wxShapeCanvas::OnEndDragLeft}\label{onenddragleft}
+
+\func{void}{OnEndDragLeft}{\param{float}{ x}, \param{float}{ 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{OnDragLeft}{ondragleft}, \helpref{OnBeginDragLeft}{onbegindragleft}.
+
+\membersection{wxShapeCanvas::OnEndDragRight}\label{onenddragright}
+
+\func{void}{OnEndDragRight}{\param{float}{ x}, \param{float}{ 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{OnDragRight}{ondragright}, \helpref{OnBeginDragRight}{onbegindragright}.
+
+\membersection{wxShapeCanvas::OnDragLeft}\label{ondragleft}
+
+\func{void}{OnDragLeft}{\param{bool}{ draw}, \param{float}{ x}, \param{float}{ 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{OnBeginDragLeft}{onbegindragleft}, \helpref{OnEndDragLeft}{onenddragleft}.
+
+\membersection{wxShapeCanvas::OnDragRight}\label{ondragright}
+
+\func{void}{OnDragRight}{\param{bool}{ draw}, \param{float}{ x}, \param{float}{ 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{OnBeginDragRight}{onbegindragright}, \helpref{OnEndDragRight}{onenddragright}.
+
+\membersection{wxShapeCanvas::OnLeftClick}\label{onleftclick}
+
+\func{void}{OnLeftClick}{\param{float}{ x}, \param{float}{ 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{onrightclick}
+
+\func{void}{OnRightClick}{\param{float}{ x}, \param{float}{ 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{float *}{x}, \param{float *}{y}}
+
+Calls wxDiagram::Snap.
+
+
+
+\section{\class{wxShapeEvtHandler}: wxObject}\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.
+
+\membersection{wxShapeEvtHandler::handlerShape}
+
+\member{wxShape *}{handlerShape}
+
+Pointer to the shape associated with this handler.
+
+\membersection{wxShapeEvtHandler::previousHandler}
+
+\member{wxShapeEvtHandler *}{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::GetShape}
+
+\func{void}{GetShape}{\void}
+
+Returns the shape associated with this handler.
+
+\membersection{wxShapeEvtHandler::OnBeginDragLeft}
+
+\func{void}{OnBeginDragLeft}{\param{float}{ x}, \param{float}{ 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{float}{ x}, \param{float}{ 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{float}{ width}, \param{float}{ height}}
+
+Called when a shape starts to be resized.
+
+\membersection{wxShapeEvtHandler::OnDragLeft}
+
+\func{void}{OnDragLeft}{\param{bool}{ draw}, \param{float}{ x}, \param{float}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called twice when the object 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{float}{ x}, \param{float}{ y}, \param{int}{ keys=0}, \param{int}{ attachment = 0}}
+
+Called twice when the object 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}{\void}
+
+Defined for each class to draw the main graphic, but
+not the contents.
+
+\membersection{wxShapeEvtHandler::OnDrawContents}
+
+\func{void}{OnDrawContents}{\void}
+
+Defined for each class to draw the contents of the
+object, such as text.
+
+\membersection{wxShapeEvtHandler::OnDrawControlPoints}
+
+\func{void}{OnDrawControlPoints}{\void}
+
+Called when the object's control points (handles) should
+be drawn.
+
+\membersection{wxShapeEvtHandler::OnDrawOutline}
+
+\func{void}{OnDrawOutline}{\void}
+
+Called when the outline of the object should be drawn.
+
+\membersection{wxShapeEvtHandler::OnEndDragLeft}
+
+\func{void}{OnEndDragLeft}{\param{float}{ x}, \param{float}{ 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{float}{ x}, \param{float}{ 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{float}{ width}, \param{float}{ height}}
+
+Called after a shape is resized.
+
+\membersection{wxShapeEvtHandler::OnErase}
+
+\func{void}{OnErase}{\void}
+
+Called when the whole object should be erased.
+
+\membersection{wxShapeEvtHandler::OnEraseContents}
+
+\func{void}{OnEraseContents}{\void}
+
+Called when the contents should be erased.
+
+\membersection{wxShapeEvtHandler::OnEraseControlPoints}
+
+\func{void}{OnEraseControlPoints}{\void}
+
+Called when the object's control points (handles) should
+be erased.
+
+\membersection{wxShapeEvtHandler::OnHighlight}
+
+\func{void}{OnHighlight}{\void}
+
+Called when the object should be highlighted.
+
+\membersection{wxShapeEvtHandler::OnLeftClick}
+
+\func{void}{OnLeftClick}{\param{float}{ x}, \param{float}{ y}, \param{int}{ keys =0}, \param{int}{ attachment = 0}}
+
+Called when the object receives a left mouse click event.
+
+\membersection{wxShapeEvtHandler::OnMove}
+
+\func{void}{OnMove}{\param{float}{ x}, \param{float}{ y}, \param{float}{ oldX}, \param{float}{ oldY}, \param{bool}{ display = TRUE}}
+
+Called when the object receives a move request.
+
+\membersection{wxShapeEvtHandler::OnMoveLink}
+
+\func{void}{OnMoveLink}{\param{bool}{ moveControlPoints=TRUE}}
+
+Called when the line attached to an object need to be repositioned,
+because the object has moved.
+
+\membersection{wxShapeEvtHandler::OnMoveLinks}
+
+\func{void}{OnMoveLinks}{\void}
+
+Called when the lines attached to an object need to be repositioned,
+because the object has moved.
+
+\membersection{wxShapeEvtHandler::OnMovePre}
+
+\func{bool}{OnMovePre}{\param{float}{ x}, \param{float}{ y}, \param{float}{ oldX}, \param{float}{ oldY}, \param{bool}{ display = TRUE}}
+
+Called just after the object receives a move request.
+
+\membersection{wxShapeEvtHandler::OnMovePre}
+
+\func{bool}{OnMovePre}{\param{float}{ x}, \param{float}{ y}, \param{float}{ oldX}, \param{float}{ oldY}, \param{bool}{ display = TRUE}}
+
+Called just before the object receives a move request. Returning TRUE
+allows the move to be processed; returning FALSE vetoes the move.
+
+\membersection{wxShapeEvtHandler::OnRightClick}
+
+\func{void}{OnRightClick}{\param{float}{ x}, \param{float}{ y}, \param{int}{ keys = 0}, \param{int}{ attachment = 0}}
+
+Called when the object receives a mouse mouse click event.
+
+\membersection{wxShapeEvtHandler::OnSize}
+
+\func{void}{OnSize}{\param{float}{ x}, \param{float}{ y}}
+
+Called when the object receives a resize request.
+
+
+
+\section{\class{wxTextShape}: wxRectangleShape}\label{wxtextshape}
+
+As wxRectangleShape, but only the text is displayed.
+
+See also \helpref{wxRectangleShape}{wxrectangleshape}.
+
+\membersection{wxTextShape::wxTextShape}
+
+\func{void}{wxTextShape}{\param{float}{ width = 0.0}, \param{float}{ 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}{\param{wxHelpInstance *}{helpInstance = NULL}, \param{const wxString\& }{helpFile = NULL},
+ \param{wxFont *}{buttonFont = NULL}, \param{wxFont *}{labelFont = NULL}}
+
+Initializes OGL. The optional parameters tell OGL what to use for on-line help and font sizes.
+
+\membersection{::wxOGLCleanUp}
+
+\func{void}{wxOGLCleanUp}{\void}
+
+Cleans up OGL.
--- /dev/null
+\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[bitmap.cpp] wxBitmapShape class header.
+\item[bitmap.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.
+\end{description}
+
+
--- /dev/null
+[OPTIONS]
+BMROOT=d:\wx2\wxwind~1\utils\ogl\docs ; 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]
+
--- /dev/null
+\documentstyle[a4,makeidx,verbatim,texhelp,fancyhea,mysober,mytitle]{report}
+\newcommand{\ogl}[0]{{OGL}}
+\input psbox.tex
+\parindent 0pt
+\parskip 11pt
+\title{Manual for Object Graphics Library 3.0}
+\author{Julian Smart}
+\date{July 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}
--- /dev/null
+\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.
+
+{\it Note:} A bug in the wxWindows document/view implementation before
+version 1.66C may cause Do/Undo to misbehave and get out of sync. If this is the case,
+please replace wxCommandProcessor::Submit with the following in wx\_doc.cpp.
+
+{\small
+\begin{verbatim}
+ Bool wxCommandProcessor::Submit(wxCommand *command, Bool storeIt)
+ {
+ Bool success = command->Do();
+ if (success && storeIt)
+ {
+ if (commands.Number() == maxNoCommands)
+ {
+ wxNode *firstNode = commands.First();
+ wxCommand *firstCommand = (wxCommand *)firstNode->Data();
+ delete firstCommand;
+ delete firstNode;
+ }
+
+ // Correct a bug: we must chop off the current 'branch'
+ // so that we're at the end of the command list.
+ if (currentCommand)
+ {
+ wxNode *node = currentCommand->Next();
+ while (node)
+ {
+ wxNode *next = node->Next();
+ delete node;
+ node = next;
+ }
+ }
+
+ commands.Append(command);
+ currentCommand = commands.Last();
+ SetMenuStrings();
+ }
+ return success;
+ }
+\end{verbatim}
+}
+
+\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.
--- /dev/null
+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 }\sethotspotcolour{off}\sethotspotunderline{on}\winhelponly{\image{}{books.bmp}}
+\htmlonly{\image{}{books.gif}}\helpref{#1}{#2}
+\sethotspotcolour{on}\sethotspotunderline{on}}
--- /dev/null
+\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{OGLConstraint}{oglconstraint}
+
+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{OGLConstraint constructor}{oglconstraintconstr}.
+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{OGLConstraint::SetSpacing}{oglconstraintsetspacing}.
+
+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}.
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#if USE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "composit.h"
+#include "lines.h"
+#include "canvas.h"
+#include "divided.h"
+#include "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(float the_x, float 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()
+{
+}
+
+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::OnSize(float x, float y)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnSize(x, y);
+}
+
+bool wxShapeEvtHandler::OnMovePre(wxDC& dc, float x, float y, float old_x, float 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, float x, float y, float old_x, float 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(float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnLeftClick(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnRightClick(float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnRightClick(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnEndDragLeft(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnDragRight(bool draw, float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnDragRight(draw, x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnBeginDragRight(float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnBeginDragRight(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnEndDragRight(float x, float y, int keys, int attachment)
+{
+ if (m_previousHandler)
+ m_previousHandler->OnEndDragRight(x, y, keys, attachment);
+}
+
+void wxShapeEvtHandler::OnDrawOutline(wxDC& dc, float x, float y, float w, float 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);
+}
+
+IMPLEMENT_ABSTRACT_CLASS(wxShape, wxShapeEvtHandler)
+
+wxShape::wxShape(wxShapeCanvas *can)
+{
+ m_eventHandler = this;
+ SetHandlerShape(this);
+ m_id = 0;
+ m_formatted = FALSE;
+ m_canvas = can;
+ m_xpos = 0.0; m_ypos = 0.0;
+ m_pen = black_pen;
+ 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 = FALSE;
+ 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.0;
+ m_shadowOffsetY = 6.0;
+ m_shadowBrush = wxBLACK_BRUSH;
+ m_textMarginX = 5.0;
+ m_textMarginY = 5.0;
+ m_regionName = "0";
+ m_centreResize = TRUE;
+ m_highlighted = FALSE;
+ m_rotation = 0.0;
+
+ // 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();
+ float w, h;
+ GetBoundingBoxMin(&w, &h);
+ region->SetSize(w, h);
+}
+
+bool wxShape::HitTest(float x, float y, int *attachment, float *distance)
+{
+// if (!sensitive)
+// return FALSE;
+
+ float 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 += (float)4.0; height += (float)4.0; // Allowance for inaccurate mousing
+
+ float left = (float)(m_xpos - (width/2.0));
+ float top = (float)(m_ypos - (height/2.0));
+ float right = (float)(m_xpos + (width/2.0));
+ float bottom = (float)(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();
+ float nearest = 999999.0;
+
+ for (int i = 0; i < n; i++)
+ {
+ float xp, yp;
+ if (GetAttachmentPosition(i, &xp, &yp))
+ {
+ float l = (float)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)
+{
+ float 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);
+
+ wxList *string_list = ::FormatText(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);
+ delete node;
+ node = string_list->First();
+ }
+ delete string_list;
+ float actualW = w;
+ float 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)
+ {
+ GetCentredTextExtent(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);
+ }
+ }
+ CentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, actualW, actualH, region->GetFormatMode());
+ m_formatted = TRUE;
+}
+
+void wxShape::Recentre(wxDC& dc)
+{
+ float 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();
+ CentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, region->GetFormatMode());
+ }
+ }
+}
+
+bool wxShape::GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *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 = NewId();
+ 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)
+{
+ float 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)
+ {
+ CentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, bound_x, bound_y, region->GetFormatMode());
+ m_formatted = TRUE;
+ }
+ if (!GetDisableLabel())
+ {
+ DrawFormattedText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, bound_x, bound_y, region->GetFormatMode());
+ }
+}
+
+void wxShape::DrawContents(wxDC& dc)
+{
+ GetEventHandler()->OnDrawContents(dc);
+}
+
+void wxShape::OnSize(float x, float y)
+{
+}
+
+bool wxShape::OnMovePre(wxDC& dc, float x, float y, float old_x, float old_y, bool display)
+{
+ return TRUE;
+}
+
+void wxShape::OnMovePost(wxDC& dc, float x, float y, float old_x, float 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;
+
+ float maxX, maxY, minX, minY;
+ float xp = GetX();
+ float yp = GetY();
+ GetBoundingBoxMin(&minX, &minY);
+ GetBoundingBoxMax(&maxX, &maxY);
+ float topLeftX = (float)(xp - (maxX / 2.0) - 2.0);
+ float topLeftY = (float)(yp - (maxY / 2.0) - 2.0);
+
+ int penWidth = 0;
+ if (m_pen)
+ penWidth = m_pen->GetWidth();
+
+ dc.SetPen(white_background_pen);
+ dc.SetBrush(white_background_brush);
+ dc.DrawRectangle((float)(topLeftX - penWidth), (float)(topLeftY - penWidth),
+ (float)(maxX + penWidth*2.0 + 4.0), (float)(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();
+ }
+ }
+}
+
+void wxShape::MoveLineToNewAttachment(wxDC& dc, wxLineShape *to_move,
+ float x, float y)
+{
+ int new_point;
+ float 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, &new_point, &distance);
+ if (!hit)
+ return;
+
+ EraseLinks(dc);
+
+ int old_attachment;
+ if (to_move->GetTo() == this)
+ old_attachment = to_move->GetAttachmentTo();
+ else
+ old_attachment = to_move->GetAttachmentFrom();
+
+ // Delete the line object from the list of links; we're going to move
+ // it to another position in the list
+ m_lines.DeleteObject(to_move);
+
+ float old_x = (float) -9999.9;
+ float old_y = (float) -9999.9;
+
+ wxNode *node = m_lines.First();
+ bool found = FALSE;
+
+ while (node && !found)
+ {
+ wxLineShape *line = (wxLineShape *)node->Data();
+ if ((line->GetTo() == this && old_attachment == line->GetAttachmentTo()) ||
+ (line->GetFrom() == this && old_attachment == line->GetAttachmentFrom()))
+ {
+ float startX, startY, endX, endY;
+ float xp, yp;
+ line->GetEnds(&startX, &startY, &endX, &endY);
+ if (line->GetTo() == this)
+ {
+// xp = line->m_xpos2 + line->m_xpos;
+// yp = line->m_ypos2 + line->m_ypos;
+ xp = startX;
+ yp = startY;
+ } else
+ {
+// xp = line->m_xpos1 + line->m_xpos;
+// yp = line->m_ypos1 + line->m_ypos;
+ xp = endX;
+ yp = endY;
+ }
+
+ switch (old_attachment)
+ {
+ case 0:
+ case 2:
+ {
+ if (x > old_x && x <= xp)
+ {
+ found = TRUE;
+ m_lines.Insert(node, to_move);
+ }
+ break;
+ }
+ case 1:
+ case 3:
+ {
+ if (y > old_y && y <= yp)
+ {
+ found = TRUE;
+ m_lines.Insert(node, to_move);
+ }
+ break;
+ }
+ }
+ old_x = xp;
+ old_y = yp;
+ }
+ node = node->Next();
+ }
+ if (!found)
+ m_lines.Append(to_move);
+}
+
+// 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(float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_CLICK_LEFT) != OP_CLICK_LEFT)
+ {
+ attachment = 0;
+ float dist;
+ if (m_parent)
+ {
+ m_parent->HitTest(x, y, &attachment, &dist);
+ m_parent->GetEventHandler()->OnLeftClick(x, y, keys, attachment);
+ }
+ return;
+ }
+}
+
+void wxShape::OnRightClick(float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_CLICK_RIGHT) != OP_CLICK_RIGHT)
+ {
+ attachment = 0;
+ float dist;
+ if (m_parent)
+ {
+ m_parent->HitTest(x, y, &attachment, &dist);
+ m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
+ }
+ return;
+ }
+}
+
+float DragOffsetX = 0.0;
+float DragOffsetY = 0.0;
+
+void wxShape::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+ {
+ attachment = 0;
+ float 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(wxXOR);
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+ float xx, yy;
+ xx = x + DragOffsetX;
+ yy = y + DragOffsetY;
+
+ m_canvas->Snap(&xx, &yy);
+// m_xpos = xx; m_ypos = yy;
+ float w, h;
+ GetBoundingBoxMax(&w, &h);
+ GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
+}
+
+void wxShape::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+ {
+ attachment = 0;
+ float 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);
+
+ Erase(dc);
+ float xx, yy;
+ xx = x + DragOffsetX;
+ yy = y + DragOffsetY;
+ m_canvas->Snap(&xx, &yy);
+// m_xpos = xx; m_ypos = yy;
+ dc.SetLogicalFunction(wxXOR);
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ float w, h;
+ GetBoundingBoxMax(&w, &h);
+ GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
+ m_canvas->CaptureMouse();
+}
+
+void wxShape::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+ m_canvas->ReleaseMouse();
+ if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+ {
+ attachment = 0;
+ float 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);
+
+ float xx = x + DragOffsetX;
+ float yy = y + DragOffsetY;
+ m_canvas->Snap(&xx, &yy);
+// canvas->Snap(&m_xpos, &m_ypos);
+ Move(dc, xx, yy);
+ if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+
+void wxShape::OnDragRight(bool draw, float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
+ {
+ attachment = 0;
+ float dist;
+ if (m_parent)
+ {
+ m_parent->HitTest(x, y, &attachment, &dist);
+ m_parent->GetEventHandler()->OnDragRight(draw, x, y, keys, attachment);
+ }
+ return;
+ }
+}
+
+void wxShape::OnBeginDragRight(float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
+ {
+ attachment = 0;
+ float dist;
+ if (m_parent)
+ {
+ m_parent->HitTest(x, y, &attachment, &dist);
+ m_parent->GetEventHandler()->OnBeginDragRight(x, y, keys, attachment);
+ }
+ return;
+ }
+}
+
+void wxShape::OnEndDragRight(float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
+ {
+ attachment = 0;
+ float 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, float x, float y, float w, float h)
+{
+ float top_left_x = (float)(x - w/2.0);
+ float top_left_y = (float)(y - h/2.0);
+ float top_right_x = (float)(top_left_x + w);
+ float top_right_y = (float)top_left_y;
+ float bottom_left_x = (float)top_left_x;
+ float bottom_left_y = (float)(top_left_y + h);
+ float bottom_right_x = (float)top_right_x;
+ float bottom_right_y = (float)bottom_left_y;
+
+ wxPoint points[5];
+ points[0].x = top_left_x; points[0].y = top_left_y;
+ points[1].x = top_right_x; points[1].y = top_right_y;
+ points[2].x = bottom_right_x; points[2].y = bottom_right_y;
+ points[3].x = bottom_left_x; points[3].y = bottom_left_y;
+ points[4].x = top_left_x; points[4].y = 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, float x, float y, bool display)
+{
+ float old_x = m_xpos;
+ float old_y = m_ypos;
+
+ m_xpos = x; m_ypos = y;
+
+ if (!GetEventHandler()->OnMovePre(dc, x, y, old_x, old_y, display))
+ {
+ m_xpos = old_x;
+ m_ypos = old_y;
+ return;
+ }
+
+ 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);
+ }
+}
+
+void wxShape::Flash()
+{
+ if (GetCanvas())
+ {
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+ 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);
+}
+
+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(float x, float y, bool recursive)
+{
+ SetAttachmentSize(x, y);
+ SetDefaultRegionSize();
+}
+
+void wxShape::SetAttachmentSize(float w, float h)
+{
+ float scaleX;
+ float scaleY;
+ float 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 = (float)(point->m_x * scaleX);
+ point->m_y = (float)(point->m_y * scaleY);
+ node = node->Next();
+ }
+}
+
+// Add line FROM this object
+void wxShape::AddLine(wxLineShape *line, wxShape *other,
+ int attachFrom, int attachTo)
+{
+ if (!m_lines.Member(line))
+ m_lines.Append(line);
+
+ if (!other->m_lines.Member(line))
+ other->m_lines.Append(line);
+
+ 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
+char *wxShape::GetFunctor()
+{
+ return "node_image";
+}
+
+void wxShape::WritePrologAttributes(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 != "") && (penColour != "BLACK"))
+ clause->AddAttributeValueString("pen_colour", penColour);
+ }
+
+ if (m_brush)
+ {
+ wxString brushColour = wxTheColourDatabase->FindName(m_brush->GetColour());
+
+ if ((brushColour != "") && (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(PrologList);
+ 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);
+ 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);
+
+ // Write user-defined attachment points, if any
+ if (m_attachmentPoints.Number() > 0)
+ {
+ wxExpr *attachmentList = new wxExpr(PrologList);
+ wxNode *node = m_attachmentPoints.First();
+ while (node)
+ {
+ wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+ wxExpr *pointExpr = new wxExpr(PrologList);
+ 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(PrologList);
+ regionExpr->Append(new wxExpr(PrologString, (region->m_regionName ? region->m_regionName : "")));
+ regionExpr->Append(new wxExpr(PrologString, (region->m_regionText ? 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(PrologString, region->m_textColour ? region->m_textColour : "BLACK"));
+
+ // New members for pen colour/style
+ regionExpr->Append(new wxExpr(PrologString, region->m_penColour ? region->m_penColour : "BLACK"));
+ regionExpr->Append(new wxExpr((long)region->m_penStyle));
+
+ // Formatted text:
+ // text1 = ((x y string) (x y string) ...)
+ wxExpr *textExpr = new wxExpr(PrologList);
+
+ wxNode *textNode = region->m_formattedText.First();
+ while (textNode)
+ {
+ wxShapeTextLine *line = (wxShapeTextLine *)textNode->Data();
+ wxExpr *list2 = new wxExpr(PrologList);
+ list2->Append(new wxExpr(line->GetX()));
+ list2->Append(new wxExpr(line->GetY()));
+ list2->Append(new wxExpr(PrologString, 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::ReadPrologAttributes(wxExpr *clause)
+{
+ clause->GetAttributeValue("id", m_id);
+ RegisterId(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() == PrologList)
+ {
+ m_formatted = TRUE; // Assume text is formatted unless we prove otherwise
+ wxExpr *node = strings->value.first;
+ while (node)
+ {
+ wxExpr *string_expr = node;
+ float the_x = 0.0;
+ float 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() == PrologString)
+ {
+ the_string = string_expr->StringValue();
+ m_formatted = FALSE;
+ }
+ else if (string_expr->Type() == PrologList)
+ {
+ wxExpr *first = string_expr->value.first;
+ wxExpr *second = first ? first->next : NULL;
+ wxExpr *third = second ? second->next : NULL;
+
+ if (first && second && third &&
+ (first->Type() == PrologReal || first->Type() == PrologInteger) &&
+ (second->Type() == PrologReal || second->Type() == PrologInteger) &&
+ third->Type() == PrologString)
+ {
+ if (first->Type() == PrologReal)
+ the_x = first->RealValue();
+ else the_x = (float)first->IntegerValue();
+
+ if (second->Type() == PrologReal)
+ the_y = second->RealValue();
+ else the_y = (float)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 = FALSE;
+
+ 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 != 0);
+
+ 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 = (int) m_centreResize;
+ clause->GetAttributeValue("centre_resize", iVal);
+ m_centreResize = (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";
+
+ m_pen = wxThePenList->FindOrCreatePen(pen_string, pen_width, pen_style);
+ if (!m_pen)
+ m_pen = wxBLACK_PEN;
+
+ 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(MatchFont(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("");
+ float x = 0.0;
+ float y = 0.0;
+ float width = 0.0;
+ float height = 0.0;
+ float minWidth = 5.0;
+ float minHeight = 5.0;
+ float m_regionProportionX = -1.0;
+ float 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() == PrologList)
+ {
+ 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 = (formatExpr->IntegerValue() != 0);
+ 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() == PrologList))
+ {
+ wxExpr *node = textExpr->value.first;
+ while (node)
+ {
+ wxExpr *string_expr = node;
+ float the_x = 0.0;
+ float 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() == PrologString)
+ {
+ the_string = string_expr->StringValue();
+ m_formatted = FALSE;
+ }
+ else if (string_expr->Type() == PrologList)
+ {
+ wxExpr *first = string_expr->value.first;
+ wxExpr *second = first ? first->next : NULL;
+ wxExpr *third = second ? second->next : NULL;
+
+ if (first && second && third &&
+ (first->Type() == PrologReal || first->Type() == PrologInteger) &&
+ (second->Type() == PrologReal || second->Type() == PrologInteger) &&
+ third->Type() == PrologString)
+ {
+ if (first->Type() == PrologReal)
+ the_x = first->RealValue();
+ else the_x = (float)first->IntegerValue();
+
+ if (second->Type() == PrologReal)
+ the_y = second->RealValue();
+ else the_y = (float)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_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_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 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();
+ }
+}
+
+// Create and return a new, fully copied object.
+wxShape *wxShape::CreateNewCopy(wxShapeCanvas *theCanvas)
+{
+ wxObjectCopyMapping.Clear();
+ wxShape *newObject = PrivateCopy();
+ if (theCanvas)
+ newObject->AddToCanvas(theCanvas);
+ newObject->Recompute();
+ return newObject;
+}
+
+// Default - make 6 control points
+void wxShape::MakeControlPoints()
+{
+ float maxX, maxY, minX, minY;
+
+ GetBoundingBoxMax(&maxX, &maxY);
+ GetBoundingBoxMin(&minX, &minY);
+
+ float widthMin = (float)(minX + CONTROL_POINT_SIZE + 2);
+ float heightMin = (float)(minY + CONTROL_POINT_SIZE + 2);
+
+ // Offsets from main object
+ float top = (float)(- (heightMin / 2.0));
+ float bottom = (float)(heightMin / 2.0 + (maxY - minY));
+ float left = (float)(- (widthMin / 2.0));
+ float right = (float)(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;
+
+ float maxX, maxY, minX, minY;
+
+ GetBoundingBoxMax(&maxX, &maxY);
+ GetBoundingBoxMin(&minX, &minY);
+
+ float widthMin = (float)(minX + CONTROL_POINT_SIZE + 2);
+ float heightMin = (float)(minY + CONTROL_POINT_SIZE + 2);
+
+ // Offsets from main object
+ float top = (float)(- (heightMin / 2.0));
+ float bottom = (float)(heightMin / 2.0 + (maxY - minY));
+ float left = (float)(- (widthMin / 2.0));
+ float right = (float)(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()
+{
+ // 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)
+{
+ if ((attachment >= 0) && (attachment < 4))
+ 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;
+}
+
+bool wxShape::GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth, int no_arcs, wxLineShape *line)
+{
+ if (!m_attachmentMode)
+ {
+ *x = m_xpos; *y = m_ypos;
+ return TRUE;
+ }
+ else
+ {
+ wxNode *node = m_attachmentPoints.First();
+ while (node)
+ {
+ wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+ if (point->m_id == attachment)
+ {
+ *x = (float)(m_xpos + point->m_x);
+ *y = (float)(m_ypos + point->m_y);
+ return TRUE;
+ }
+ node = node->Next();
+ }
+ *x = m_xpos; *y = m_ypos;
+ return FALSE;
+ }
+}
+
+void wxShape::GetBoundingBoxMax(float *w, float *h)
+{
+ float 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();
+}
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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)
+
+class wxShapeTextLine;
+class wxShapeCanvas;
+class wxLineShape;
+class wxControlPoint;
+class wxShapeRegion;
+class wxShape;
+
+#ifdef PROLOGIO
+class wxExpr;
+class wxDatabase;
+#endif
+
+class wxShapeEvtHandler: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxShapeEvtHandler)
+
+ public:
+ wxShapeEvtHandler(wxShapeEvtHandler *prev = NULL, wxShape *shape = NULL);
+ virtual ~wxShapeEvtHandler();
+
+ inline void SetHandlerShape(wxShape *sh) { m_handlerShape = sh; }
+ inline wxShape *GetShape() const { return m_handlerShape; }
+
+ // This is called when the _shape_ is deleted.
+ virtual void OnDelete();
+ virtual void OnDraw(wxDC& dc);
+ virtual void OnDrawContents(wxDC& dc);
+ virtual void OnMoveLinks(wxDC& dc);
+ virtual void OnErase(wxDC& dc);
+ virtual void OnEraseContents(wxDC& dc);
+ virtual void OnHighlight(wxDC& dc);
+ virtual void OnLeftClick(float x, float y, int keys = 0, int attachment = 0);
+ virtual void OnRightClick(float x, float y, int keys = 0, int attachment = 0);
+ virtual void OnSize(float x, float y);
+ virtual bool OnMovePre(wxDC& dc, float x, float y, float old_x, float old_y, bool display = TRUE);
+ virtual void OnMovePost(wxDC& dc, float x, float y, float old_x, float old_y, bool display = TRUE);
+
+ virtual void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0); // Erase if draw false
+ virtual void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnDragRight(bool draw, float x, float y, int keys=0, int attachment = 0); // Erase if draw false
+ virtual void OnBeginDragRight(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnEndDragRight(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnDrawOutline(wxDC& dc, float x, float y, float w, float h);
+ virtual void OnDrawControlPoints(wxDC& dc);
+ virtual void OnEraseControlPoints(wxDC& dc);
+ virtual void OnMoveLink(wxDC& dc, bool moveControlPoints = TRUE);
+
+ virtual void OnBeginSize(float WXUNUSED(w), float WXUNUSED(h)) { }
+ virtual void OnEndSize(float WXUNUSED(w), float WXUNUSED(h)) { }
+
+ 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(float *width, float *height);
+ virtual void GetBoundingBoxMin(float *width, float *height) = 0;
+ virtual bool GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *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 float GetX() const { return m_xpos; }
+ inline float GetY() const { return m_ypos; }
+ inline void SetX(float x) { m_xpos = x; }
+ inline void SetY(float 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(float x, float y, int keys = 0, int attachment = 0);
+ virtual void OnRightClick(float x, float y, int keys = 0, int attachment = 0);
+ virtual void OnSize(float x, float y);
+ virtual bool OnMovePre(wxDC& dc, float x, float y, float old_x, float old_y, bool display = TRUE);
+ virtual void OnMovePost(wxDC& dc, float x, float y, float old_x, float old_y, bool display = TRUE);
+
+ virtual void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0); // Erase if draw false
+ virtual void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnDragRight(bool draw, float x, float y, int keys=0, int attachment = 0); // Erase if draw false
+ virtual void OnBeginDragRight(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnEndDragRight(float x, float y, int keys=0, int attachment = 0);
+ virtual void OnDrawOutline(wxDC& dc, float x, float y, float w, float h);
+ virtual void OnDrawControlPoints(wxDC& dc);
+ virtual void OnEraseControlPoints(wxDC& dc);
+
+ virtual void OnBeginSize(float WXUNUSED(w), float WXUNUSED(h)) { }
+ virtual void OnEndSize(float WXUNUSED(w), float WXUNUSED(h)) { }
+
+ 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(float x, float y, int *attachment, float *distance);
+ inline void SetCentreResize(bool cr) { m_centreResize = cr; }
+ inline bool GetCentreResize() const { return m_centreResize; }
+ inline wxList& GetLines() { return m_lines; }
+ inline void SetDisableLabel(bool flag) { m_disableLabel = flag; }
+ inline bool GetDisableLabel() const { return m_disableLabel; }
+ inline void SetAttachmentMode(bool flag) { m_attachmentMode = flag; }
+ inline bool 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, float x1, float 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(float x, float y, bool recursive = TRUE);
+ virtual void SetAttachmentSize(float x, float 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);
+ 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() { return 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
+ // Prolog database stuff
+ virtual char *GetFunctor();
+ virtual void WritePrologAttributes(wxExpr *clause);
+ virtual void ReadPrologAttributes(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
+
+ // Does the WHOLE copy calling PrivateCopy - don't redefine.
+ // If canvas is non-null, set the canvas too.
+ wxShape *CreateNewCopy(wxShapeCanvas *theCanvas = NULL);
+
+ // Attachment code
+ virtual bool GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+ virtual int GetNumberOfAttachments();
+ virtual bool AttachmentIsValid(int attachment);
+
+ virtual void EraseLinks(wxDC& dc, int attachment = -1, bool recurse = FALSE);
+ virtual void DrawLinks(wxDC& dc, int attachment = -1, bool recurse = FALSE);
+
+ virtual void MoveLineToNewAttachment(wxDC& dc, wxLineShape *to_move,
+ float x, float 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);
+
+ // 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);
+
+ // Does the copying for this object
+ void Copy(wxShape& copy);
+ // Returns a new instance, and does the copy for this class. Define for each class.
+ virtual wxShape *PrivateCopy() = 0;
+
+ // Rotate about the given axis by the given amount in radians
+ // (does nothing for most objects)
+ // But even non-rotating objects should record their notional
+ // rotation in case it's important (e.g. in dog-leg code).
+ virtual inline void Rotate(float WXUNUSED(x), float WXUNUSED(y), float theta) { m_rotation = theta; }
+ virtual inline float 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;
+ float 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.
+ float m_rotation;
+ int m_sensitivity;
+ bool m_draggable;
+ bool m_attachmentMode; // TRUE if using attachments, FALSE otherwise
+ 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;
+};
+
+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);
+
+ void GetBoundingBoxMin(float *w, float *h);
+ void CalculateBoundingBox();
+ bool GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3);
+ bool HitTest(float x, float y, int *attachment, float *distance);
+ void SetSize(float x, float y, bool recursive = TRUE);
+ void OnDraw(wxDC& dc);
+ void OnDrawOutline(wxDC& dc, float x, float y, float w, float h);
+
+ // 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
+ // Prolog database stuff
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ int GetNumberOfAttachments();
+ bool GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+ bool AttachmentIsValid(int attachment);
+ // Does the copying for this object
+ void Copy(wxPolygonShape& copy);
+ wxShape *PrivateCopy();
+
+ inline wxList *GetPoints() { return m_points; }
+
+ private:
+ wxList* m_points;
+ wxList* m_originalPoints;
+ float m_boundWidth;
+ float m_boundHeight;
+ float m_originalWidth;
+ float m_originalHeight;
+};
+
+class wxRectangleShape: public wxShape
+{
+ DECLARE_DYNAMIC_CLASS(wxRectangleShape)
+ public:
+ wxRectangleShape(float w = 0.0, float h = 0.0);
+ void GetBoundingBoxMin(float *w, float *h);
+ bool GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3);
+ void OnDraw(wxDC& dc);
+ void SetSize(float x, float y, bool recursive = TRUE);
+ void SetCornerRadius(float rad); // If > 0, rounded corners
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ int GetNumberOfAttachments();
+ bool GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+ // Does the copying for this object
+ void Copy(wxRectangleShape& copy);
+ wxShape *PrivateCopy();
+
+ inline float GetWidth() const { return m_width; }
+ inline float GetHeight() const { return m_height; }
+
+protected:
+ float m_width;
+ float m_height;
+ float m_cornerRadius;
+};
+
+class wxTextShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxTextShape)
+ public:
+ wxTextShape(float width = 0.0, float height = 0.0);
+
+ void OnDraw(wxDC& dc);
+
+#ifdef PROLOGIO
+ void WritePrologAttributes(wxExpr *clause);
+#endif
+
+ // Does the copying for this object
+ void Copy(wxTextShape& copy);
+ wxShape *PrivateCopy();
+};
+
+class wxEllipseShape: public wxShape
+{
+ DECLARE_DYNAMIC_CLASS(wxEllipseShape)
+ public:
+ wxEllipseShape(float w = 0.0, float h = 0.0);
+
+ void GetBoundingBoxMin(float *w, float *h);
+ bool GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3);
+
+ void OnDraw(wxDC& dc);
+ void SetSize(float x, float y, bool recursive = TRUE);
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ int GetNumberOfAttachments();
+ bool GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+
+ // Does the copying for this object
+ void Copy(wxEllipseShape& copy);
+ wxShape *PrivateCopy();
+
+ inline float GetWidth() const { return m_width; }
+ inline float GetHeight() const { return m_height; }
+
+protected:
+ float m_width;
+ float m_height;
+};
+
+class wxCircleShape: public wxEllipseShape
+{
+ DECLARE_DYNAMIC_CLASS(wxCircleShape)
+ public:
+ wxCircleShape(float w = 0.0);
+
+ bool GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3);
+ // Does the copying for this object
+ void Copy(wxCircleShape& copy);
+ wxShape *PrivateCopy();
+};
+
+#endif
+ // _OGL_BASIC_H_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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__
+#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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#if USE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "composit.h"
+#include "lines.h"
+#include "canvas.h"
+#include "divided.h"
+#include "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)
+{
+ 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()
+{
+ 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;
+ }
+ 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;
+ }
+}
+
+
+// Width and height. Centre of object is centre of box.
+void wxPolygonShape::GetBoundingBoxMin(float *width, float *height)
+{
+ *width = m_boundWidth;
+ *height = m_boundHeight;
+}
+
+void wxPolygonShape::CalculateBoundingBox()
+{
+ // Calculate bounding box at construction (and presumably resize) time
+ float left = 10000;
+ float right = -10000;
+ float top = 10000;
+ float 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()
+{
+ float left = 10000;
+ float right = -10000;
+ float top = 10000;
+ float 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();
+ }
+ float bwidth = right - left;
+ float bheight = bottom - top;
+
+ float newCentreX = (float)(left + (bwidth/2.0));
+ float newCentreY = (float)(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(float n, float xvec[], float yvec[],
+ float x1, float y1, float x2, float y2)
+{
+ bool isAHit = FALSE;
+ int i;
+ float lastx = xvec[0];
+ float lasty = yvec[0];
+
+ float min_ratio = 1.0;
+ float line_ratio;
+ float other_ratio;
+
+// char buf[300];
+ for (i = 1; i < n; i++)
+ {
+ check_line_intersection(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 floats are not identical
+ if (!(xvec[0] == lastx && yvec[0] == lasty))
+ {
+ check_line_intersection(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(float x, float y, int *attachment, float *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.
+ float endPointsX[4];
+ float endPointsY[4];
+ // North
+ endPointsX[0] = x;
+ endPointsY[0] = (float)(y - 1000.0);
+ // East
+ endPointsX[1] = (float)(x + 1000.0);
+ endPointsY[1] = y;
+ // South
+ endPointsX[2] = x;
+ endPointsY[2] = (float)(y + 1000.0);
+ // West
+ endPointsX[3] = (float)(x - 1000.0);
+ endPointsY[3] = y;
+
+ // Store polygon points in an array
+ int np = m_points->Number();
+ float *xpoints = new float[np];
+ float *ypoints = new float[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();
+ float nearest = 999999.0;
+
+ for (i = 0; i < n; i++)
+ {
+ float xp, yp;
+ if (GetAttachmentPosition(i, &xp, &yp))
+ {
+ float l = (float)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(float new_width, float new_height, bool recursive)
+{
+ SetAttachmentSize(new_width, new_height);
+
+ // Multiply all points by proportion of new size to old size
+ float x_proportion = (float)(fabs(new_width/m_originalWidth));
+ float y_proportion = (float)(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 = (float)fabs(new_width);
+ m_boundHeight = (float)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();
+
+ float x = (float)((secondPoint->x - firstPoint->x)/2.0 + firstPoint->x);
+ float y = (float)((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(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *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 --
+ // find_end_for_polyline can't cope with this (the arrow
+ // gets drawn to the wrong place).
+ if ((!m_attachmentMode) && (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();
+ }
+ }
+
+ float *xpoints = new float[n];
+ float *ypoints = new float[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 ++;
+ }
+/*
+ wxRealPoint *point = (wxRealPoint *)m_points->First()->Data();
+ xpoints[i] = point->x + m_xpos;
+ ypoints[i] = point->y + m_ypos;
+*/
+
+ find_end_for_polyline(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 = (int) point->x;
+ intPoints[i].y = (int) point->y;
+ }
+
+ if (m_shadowMode != SHADOW_NONE)
+ {
+ if (m_shadowBrush)
+ dc.SetBrush(m_shadowBrush);
+ dc.SetPen(transparent_pen);
+
+ dc.DrawPolygon(n, intPoints, m_xpos + m_shadowOffsetX, m_ypos + m_shadowOffsetY);
+ }
+
+ if (m_pen)
+ {
+ if (m_pen->GetWidth() == 0)
+ dc.SetPen(transparent_pen);
+ else
+ dc.SetPen(m_pen);
+ }
+ if (m_brush)
+ dc.SetBrush(m_brush);
+ dc.DrawPolygon(n, intPoints, m_xpos, m_ypos);
+
+ delete[] intPoints;
+}
+
+void wxPolygonShape::OnDrawOutline(wxDC& dc, float x, float y, float w, float h)
+{
+ dc.SetBrush(wxTRANSPARENT_BRUSH);
+
+ 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 = (int) point->x;
+ intPoints[i].y = (int) point->y;
+ }
+ dc.DrawPolygon(n, intPoints, x, y);
+// wxShape::OnDrawOutline(x, y, w, h);
+}
+
+// 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();
+ }
+
+/*
+ float maxX, maxY, minX, minY;
+
+ GetBoundingBoxMax(&maxX, &maxY);
+ GetBoundingBoxMin(&minX, &minY);
+
+ float widthMin = (float)(minX + CONTROL_POINT_SIZE + 2);
+ float heightMin = (float)(minY + CONTROL_POINT_SIZE + 2);
+
+ // Offsets from main object
+ float top = (float)(- (heightMin / 2.0));
+ float bottom = (float)(heightMin / 2.0 + (maxY - minY));
+ float left = (float)(- (widthMin / 2.0));
+ float right = (float)(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 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();
+ }
+/*
+
+ if (m_controlPoints.Number() < 1)
+ return;
+
+ float maxX, maxY, minX, minY;
+
+ GetBoundingBoxMax(&maxX, &maxY);
+ GetBoundingBoxMin(&minX, &minY);
+
+ float widthMin = (float)(minX + CONTROL_POINT_SIZE + 2);
+ float heightMin = (float)(minY + CONTROL_POINT_SIZE + 2);
+
+ // Offsets from main object
+ float top = (float)(- (heightMin / 2.0));
+ float bottom = (float)(heightMin / 2.0 + (maxY - minY));
+ float left = (float)(- (widthMin / 2.0));
+ float right = (float)(widthMin / 2.0 + (maxX - minX));
+
+ wxNode *node = m_controlPoints.First();
+ wxControlPoint *control = (wxControlPoint *)node->Data();
+ control->xoffset = left; control->yoffset = top;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = 0; control->yoffset = top;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = right; control->yoffset = top;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = right; control->yoffset = 0;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = right; control->yoffset = bottom;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = 0; control->yoffset = bottom;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = left; control->yoffset = bottom;
+
+ node = node->Next(); control = (wxControlPoint *)node->Data();
+ control->xoffset = left; control->yoffset = 0;
+*/
+}
+
+
+#ifdef PROLOGIO
+void wxPolygonShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxShape::WritePrologAttributes(clause);
+
+ clause->AddAttributeValue("x", m_xpos);
+ clause->AddAttributeValue("y", m_ypos);
+
+ // Make a list of lists for the coordinates
+ wxExpr *list = new wxExpr(PrologList);
+ wxNode *node = m_points->First();
+ while (node)
+ {
+ wxRealPoint *point = (wxRealPoint *)node->Data();
+ wxExpr *point_list = new wxExpr(PrologList);
+ wxExpr *x_expr = new wxExpr((float)point->x);
+ wxExpr *y_expr = new wxExpr((float)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(PrologList);
+ node = m_originalPoints->First();
+ while (node)
+ {
+ wxRealPoint *point = (wxRealPoint *)node->Data();
+ wxExpr *point_list = new wxExpr(PrologList);
+ wxExpr *x_expr = new wxExpr((float) point->x);
+ wxExpr *y_expr = new wxExpr((float) 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::ReadPrologAttributes(wxExpr *clause)
+{
+ wxShape::ReadPrologAttributes(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.
+ float the_height = 100.0;
+ float 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((float)x, (float)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;
+ float min_x = 1000;
+ float min_y = 1000;
+ float max_x = -1000;
+ float 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((float)x, (float)y);
+ m_originalPoints->Append((wxObject*) point);
+
+ if (x < min_x)
+ min_x = (float)x;
+ if (y < min_y)
+ min_y = (float)y;
+ if (x > max_x)
+ max_x = (float)x;
+ if (y > max_y)
+ max_y = (float)y;
+
+ node = node->next;
+ }
+ m_originalWidth = max_x - min_x;
+ m_originalHeight = max_y - min_y;
+ }
+
+ CalculateBoundingBox();
+}
+#endif
+
+void wxPolygonShape::Copy(wxPolygonShape& copy)
+{
+ wxShape::Copy(copy);
+
+ if (copy.m_points)
+ delete copy.m_points;
+
+ copy.m_points = new wxList;
+
+ if (copy.m_originalPoints)
+ delete copy.m_originalPoints;
+
+ copy.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);
+ copy.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);
+ copy.m_originalPoints->Append((wxObject*) new_point);
+ node = node->Next();
+ }
+ copy.m_boundWidth = m_boundWidth;
+ copy.m_boundHeight = m_boundHeight;
+ copy.m_originalWidth = m_originalWidth;
+ copy.m_originalHeight = m_originalHeight;
+}
+
+wxShape *wxPolygonShape::PrivateCopy()
+{
+ wxPolygonShape *obj = new wxPolygonShape;
+ Copy(*obj);
+ return obj;
+}
+
+int wxPolygonShape::GetNumberOfAttachments()
+{
+ 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, float *x, float *y,
+ int nth, int no_arcs, wxLineShape *line)
+{
+ if (m_attachmentMode && 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;
+}
+
+// Rectangle object
+
+IMPLEMENT_DYNAMIC_CLASS(wxRectangleShape, wxShape)
+
+wxRectangleShape::wxRectangleShape(float w, float h)
+{
+ m_width = w; m_height = h; m_cornerRadius = 0.0;
+ SetDefaultRegionSize();
+}
+
+void wxRectangleShape::OnDraw(wxDC& dc)
+{
+ float x1 = (float)(m_xpos - m_width/2.0);
+ float y1 = (float)(m_ypos - m_height/2.0);
+
+ if (m_shadowMode != SHADOW_NONE)
+ {
+ if (m_shadowBrush)
+ dc.SetBrush(m_shadowBrush);
+ dc.SetPen(transparent_pen);
+
+ if (m_cornerRadius != 0.0)
+ dc.DrawRoundedRectangle(x1 + m_shadowOffsetX, y1 + m_shadowOffsetY,
+ m_width, m_height, m_cornerRadius);
+ else
+ dc.DrawRectangle(x1 + m_shadowOffsetX, y1 + m_shadowOffsetY, m_width, m_height);
+ }
+
+ if (m_pen)
+ {
+ if (m_pen->GetWidth() == 0)
+ dc.SetPen(transparent_pen);
+ else
+ dc.SetPen(m_pen);
+ }
+ if (m_brush)
+ dc.SetBrush(m_brush);
+
+ if (m_cornerRadius != 0.0)
+ dc.DrawRoundedRectangle(x1, y1, m_width, m_height, m_cornerRadius);
+ else
+ dc.DrawRectangle(x1, y1, m_width, m_height);
+}
+
+void wxRectangleShape::GetBoundingBoxMin(float *the_width, float *the_height)
+{
+ *the_width = m_width;
+ *the_height = m_height;
+}
+
+void wxRectangleShape::SetSize(float x, float y, bool recursive)
+{
+ SetAttachmentSize(x, y);
+ m_width = (float)wxMax(x, 1.0);
+ m_height = (float)wxMax(y, 1.0);
+ SetDefaultRegionSize();
+}
+
+void wxRectangleShape::SetCornerRadius(float rad)
+{
+ m_cornerRadius = rad;
+}
+
+// Assume (x1, y1) is centre of box (most generally, line end at box)
+bool wxRectangleShape::GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3)
+{
+ float bound_x, bound_y;
+ GetBoundingBoxMax(&bound_x, &bound_y);
+ find_end_for_box(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
+
+ return TRUE;
+}
+
+#ifdef PROLOGIO
+void wxRectangleShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxShape::WritePrologAttributes(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::ReadPrologAttributes(wxExpr *clause)
+{
+ wxShape::ReadPrologAttributes(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(wxRectangleShape& copy)
+{
+ wxShape::Copy(copy);
+ copy.m_width = m_width;
+ copy.m_height = m_height;
+ copy.m_cornerRadius = m_cornerRadius;
+}
+
+wxShape *wxRectangleShape::PrivateCopy()
+{
+ wxRectangleShape *obj = new wxRectangleShape(0.0, 0.0);
+ Copy(*obj);
+ return obj;
+}
+
+
+int wxRectangleShape::GetNumberOfAttachments()
+{
+ return wxShape::GetNumberOfAttachments();
+}
+
+// There are 4 attachment points on a rectangle - 0 = top, 1 = right, 2 = bottom,
+// 3 = left.
+bool wxRectangleShape::GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth, int no_arcs, wxLineShape *line)
+{
+ if (m_attachmentMode)
+ {
+ float top = (float)(m_ypos + m_height/2.0);
+ float bottom = (float)(m_ypos - m_height/2.0);
+ float left = (float)(m_xpos - m_width/2.0);
+ float right = (float)(m_xpos + m_width/2.0);
+
+ bool isEnd = (line && line->IsEnd(this));
+
+ switch (attachment)
+ {
+ case 0:
+ {
+ 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;
+
+ *y = bottom;
+ break;
+ }
+ case 1:
+ {
+ *x = right;
+ 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 = bottom + (nth + 1)*m_height/(no_arcs + 1);
+ }
+ else *y = m_ypos;
+ break;
+ }
+ case 2:
+ {
+ 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;
+ *y = top;
+ break;
+ }
+ case 3:
+ {
+ *x = left;
+ 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 = bottom + (nth + 1)*m_height/(no_arcs + 1);
+ }
+ else *y = m_ypos;
+ break;
+ }
+ default:
+ {
+ return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
+ break;
+ }
+ }
+ return TRUE;
+ }
+ else
+ { *x = m_xpos; *y = m_ypos; return TRUE; }
+}
+
+// Text object (no box)
+
+IMPLEMENT_DYNAMIC_CLASS(wxTextShape, wxRectangleShape)
+
+wxTextShape::wxTextShape(float width, float height):
+ wxRectangleShape(width, height)
+{
+}
+
+void wxTextShape::OnDraw(wxDC& dc)
+{
+}
+
+wxShape *wxTextShape::PrivateCopy()
+{
+ wxTextShape *obj = new wxTextShape(0.0, 0.0);
+ Copy(*obj);
+ return obj;
+}
+
+void wxTextShape::Copy(wxTextShape& copy)
+{
+ wxRectangleShape::Copy(copy);
+}
+
+#ifdef PROLOGIO
+void wxTextShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::WritePrologAttributes(clause);
+}
+#endif
+
+// Ellipse object
+
+IMPLEMENT_DYNAMIC_CLASS(wxEllipseShape, wxShape)
+
+wxEllipseShape::wxEllipseShape(float w, float h)
+{
+ m_width = w; m_height = h;
+ SetDefaultRegionSize();
+}
+
+void wxEllipseShape::GetBoundingBoxMin(float *w, float *h)
+{
+ *w = m_width; *h = m_height;
+}
+
+bool wxEllipseShape::GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3)
+{
+ float bound_x, bound_y;
+ GetBoundingBoxMax(&bound_x, &bound_y);
+
+// find_end_for_box(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
+ draw_arc_to_ellipse(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(transparent_pen);
+ dc.DrawEllipse((m_xpos - GetWidth()/2) + m_shadowOffsetX,
+ (m_ypos - GetHeight()/2) + m_shadowOffsetY,
+ GetWidth(), GetHeight());
+ }
+
+ if (m_pen)
+ {
+ if (m_pen->GetWidth() == 0)
+ dc.SetPen(transparent_pen);
+ else
+ dc.SetPen(m_pen);
+ }
+ if (m_brush)
+ dc.SetBrush(m_brush);
+ dc.DrawEllipse((m_xpos - GetWidth()/2), (m_ypos - GetHeight()/2), GetWidth(), GetHeight());
+}
+
+void wxEllipseShape::SetSize(float x, float y, bool recursive)
+{
+ SetAttachmentSize(x, y);
+ m_width = x;
+ m_height = y;
+ SetDefaultRegionSize();
+}
+
+#ifdef PROLOGIO
+void wxEllipseShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxShape::WritePrologAttributes(clause);
+ clause->AddAttributeValue("x", m_xpos);
+ clause->AddAttributeValue("y", m_ypos);
+
+ clause->AddAttributeValue("width", m_width);
+ clause->AddAttributeValue("height", m_height);
+}
+
+void wxEllipseShape::ReadPrologAttributes(wxExpr *clause)
+{
+ wxShape::ReadPrologAttributes(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(wxEllipseShape& copy)
+{
+ wxShape::Copy(copy);
+
+ copy.m_width = m_width;
+ copy.m_height = m_height;
+}
+
+wxShape *wxEllipseShape::PrivateCopy()
+{
+ wxEllipseShape *obj = new wxEllipseShape(0.0, 0.0);
+ Copy(*obj);
+ return obj;
+}
+
+int wxEllipseShape::GetNumberOfAttachments()
+{
+ return wxShape::GetNumberOfAttachments();
+}
+
+// There are 4 attachment points on an ellipse - 0 = top, 1 = right, 2 = bottom,
+// 3 = left.
+bool wxEllipseShape::GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth, int no_arcs, wxLineShape *line)
+{
+ if (m_attachmentMode)
+ {
+ float top = (float)(m_ypos + m_height/2.0);
+ float bottom = (float)(m_ypos - m_height/2.0);
+ float left = (float)(m_xpos - m_width/2.0);
+ float right = (float)(m_xpos + m_width/2.0);
+ switch (attachment)
+ {
+ 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.
+ draw_arc_to_ellipse(m_xpos, m_ypos, m_width, m_height, *x, (float)(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;
+ draw_arc_to_ellipse(m_xpos, m_ypos, m_width, m_height, (float)(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;
+ draw_arc_to_ellipse(m_xpos, m_ypos, m_width, m_height, *x, (float)(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;
+ draw_arc_to_ellipse(m_xpos, m_ypos, m_width, m_height, (float)(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(float diameter):wxEllipseShape(diameter, diameter)
+{
+}
+
+wxShape *wxCircleShape::PrivateCopy()
+{
+ wxCircleShape *obj = new wxCircleShape(0.0);
+ Copy(*obj);
+ return obj;
+}
+
+void wxCircleShape::Copy(wxCircleShape& copy)
+{
+ wxEllipseShape::Copy(copy);
+}
+
+bool wxCircleShape::GetPerimeterPoint(float x1, float y1,
+ float x2, float y2,
+ float *x3, float *y3)
+{
+ find_end_for_circle(m_width/2,
+ m_xpos, m_ypos, // Centre of circle
+ x2, y2, // Other end of line
+ x3, y3);
+
+ return TRUE;
+}
+
+// Control points
+
+IMPLEMENT_DYNAMIC_CLASS(wxControlPoint, wxRectangleShape)
+
+wxControlPoint::wxControlPoint(wxShapeCanvas *theCanvas, wxShape *object, float size, float the_xoffset, float 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(black_foreground_pen);
+ 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);
+}
+
+/*
+ * Store original top-left, bottom-right coordinates
+ * in case we're doing non-vertical resizing.
+ */
+static float controlPointDragStartX = 0.0;
+static float controlPointDragStartY = 0.0;
+static float controlPointDragStartWidth = 0.0;
+static float controlPointDragStartHeight = 0.0;
+static float controlPointDragEndWidth = 0.0;
+static float controlPointDragEndHeight = 0.0;
+static float controlPointDragPosX = 0.0;
+static float controlPointDragPosY = 0.0;
+
+// Implement resizing of canvas object
+void wxControlPoint::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ float bound_x;
+ float bound_y;
+ m_shape->GetBoundingBoxMin(&bound_x, &bound_y);
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ if (m_shape->GetCentreResize())
+ {
+ // Maintain the same centre point.
+ float new_width = (float)(2.0*fabs(x - m_shape->GetX()));
+ float new_height = (float)(2.0*fabs(y - m_shape->GetY()));
+
+ // Constrain sizing according to what control point you're dragging
+ if (m_type == CONTROL_POINT_HORIZONTAL)
+ new_height = bound_y;
+ else if (m_type == CONTROL_POINT_VERTICAL)
+ new_width = bound_x;
+ else if (m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
+ new_height = bound_y*(new_width/bound_x);
+
+// m_shape->OnBeginSize(m_shape->m_fixedWidth ? bound_x : new_width,
+// m_shape->m_fixedHeight ? bound_y : new_height);
+
+// m_shape->SetSize(m_shape->m_fixedWidth ? bound_x : new_width,
+// m_shape->m_fixedHeight ? bound_y : new_height);
+ if (m_shape->GetFixedWidth())
+ new_width = bound_x;
+
+ if (m_shape->GetFixedHeight())
+ new_height = bound_y;
+
+ controlPointDragEndWidth = new_width;
+ controlPointDragEndHeight = new_height;
+
+ m_shape->GetEventHandler()->OnDrawOutline(dc, m_shape->GetX(), m_shape->GetY(),
+ new_width, new_height);
+ }
+ else
+ {
+ // Don't maintain the same centre point!
+ float newX1 = wxMin(controlPointDragStartX, x);
+ float newY1 = wxMin(controlPointDragStartY, y);
+ float newX2 = wxMax(controlPointDragStartX, x);
+ float newY2 = wxMax(controlPointDragStartY, y);
+ if (m_type == CONTROL_POINT_HORIZONTAL)
+ {
+ newY1 = controlPointDragStartY;
+ newY2 = newY1 + controlPointDragStartHeight;
+ }
+ else if (m_type == CONTROL_POINT_VERTICAL)
+ {
+ newX1 = controlPointDragStartX;
+ newX2 = newX1 + controlPointDragStartWidth;
+ }
+ else if (m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
+ {
+ float newH = (float)((newX2 - newX1)*(controlPointDragStartHeight/controlPointDragStartWidth));
+ if (GetY() > controlPointDragStartY)
+ newY2 = (float)(newY1 + newH);
+ else
+ newY1 = (float)(newY2 - newH);
+ }
+ float newWidth = (float)(newX2 - newX1);
+ float newHeight = (float)(newY2 - newY1);
+
+// m_shape->OnBeginSize(newWidth,
+// newHeight);
+
+// m_shape->SetSize(newWidth,
+// newHeight);
+ controlPointDragPosX = (float)(newX1 + (newWidth/2.0));
+ controlPointDragPosY = (float)(newY1 + (newHeight/2.0));
+ if (m_shape->GetFixedWidth())
+ newWidth = bound_x;
+
+ if (m_shape->GetFixedHeight())
+ newHeight = bound_y;
+
+ controlPointDragEndWidth = newWidth;
+ controlPointDragEndHeight = newHeight;
+ m_shape->GetEventHandler()->OnDrawOutline(dc, controlPointDragPosX, controlPointDragPosY, newWidth, newHeight);
+ }
+}
+
+void wxControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ m_canvas->CaptureMouse();
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ if (m_eraseObject)
+ m_shape->Erase(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+
+ float bound_x;
+ float bound_y;
+ m_shape->GetBoundingBoxMin(&bound_x, &bound_y);
+
+ // Choose the 'opposite corner' of the object as the stationary
+ // point in case this is non-centring resizing.
+ if (GetX() < m_shape->GetX())
+ controlPointDragStartX = (float)(m_shape->GetX() + (bound_x/2.0));
+ else
+ controlPointDragStartX = (float)(m_shape->GetX() - (bound_x/2.0));
+
+ if (GetY() < m_shape->GetY())
+ controlPointDragStartY = (float)(m_shape->GetY() + (bound_y/2.0));
+ else
+ controlPointDragStartY = (float)(m_shape->GetY() - (bound_y/2.0));
+
+ if (m_type == CONTROL_POINT_HORIZONTAL)
+ controlPointDragStartY = (float)(m_shape->GetY() - (bound_y/2.0));
+ else if (m_type == CONTROL_POINT_VERTICAL)
+ controlPointDragStartX = (float)(m_shape->GetX() - (bound_x/2.0));
+
+ // We may require the old width and height.
+ controlPointDragStartWidth = bound_x;
+ controlPointDragStartHeight = bound_y;
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ if (m_shape->GetCentreResize())
+ {
+ float new_width = (float)(2.0*fabs(x - m_shape->GetX()));
+ float new_height = (float)(2.0*fabs(y - m_shape->GetY()));
+
+ // Constrain sizing according to what control point you're dragging
+ if (m_type == CONTROL_POINT_HORIZONTAL)
+ new_height = bound_y;
+ else if (m_type == CONTROL_POINT_VERTICAL)
+ new_width = bound_x;
+ else if (m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
+ new_height = bound_y*(new_width/bound_x);
+
+ // Non-recursive SetSize for speed
+// m_shape->SetSize(new_width, new_height, FALSE);
+
+ if (m_shape->GetFixedWidth())
+ new_width = bound_x;
+
+ if (m_shape->GetFixedHeight())
+ new_height = bound_y;
+
+ controlPointDragEndWidth = new_width;
+ controlPointDragEndHeight = new_height;
+ m_shape->GetEventHandler()->OnDrawOutline(dc, m_shape->GetX(), m_shape->GetY(),
+ new_width, new_height);
+ }
+ else
+ {
+ // Don't maintain the same centre point!
+ float newX1 = wxMin(controlPointDragStartX, x);
+ float newY1 = wxMin(controlPointDragStartY, y);
+ float newX2 = wxMax(controlPointDragStartX, x);
+ float newY2 = wxMax(controlPointDragStartY, y);
+ if (m_type == CONTROL_POINT_HORIZONTAL)
+ {
+ newY1 = controlPointDragStartY;
+ newY2 = newY1 + controlPointDragStartHeight;
+ }
+ else if (m_type == CONTROL_POINT_VERTICAL)
+ {
+ newX1 = controlPointDragStartX;
+ newX2 = newX1 + controlPointDragStartWidth;
+ }
+ else if (m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
+ {
+ float newH = (float)((newX2 - newX1)*(controlPointDragStartHeight/controlPointDragStartWidth));
+ if (GetY() > controlPointDragStartY)
+ newY2 = (float)(newY1 + newH);
+ else
+ newY1 = (float)(newY2 - newH);
+ }
+ float newWidth = (float)(newX2 - newX1);
+ float newHeight = (float)(newY2 - newY1);
+
+// m_shape->OnBeginSize(newWidth,
+// newHeight);
+
+// m_shape->SetSize(newWidth,
+// newHeight);
+ controlPointDragPosX = (float)(newX1 + (newWidth/2.0));
+ controlPointDragPosY = (float)(newY1 + (newHeight/2.0));
+ if (m_shape->GetFixedWidth())
+ newWidth = bound_x;
+
+ if (m_shape->GetFixedHeight())
+ newHeight = bound_y;
+
+ controlPointDragEndWidth = newWidth;
+ controlPointDragEndHeight = newHeight;
+ m_shape->GetEventHandler()->OnDrawOutline(dc, controlPointDragPosX, controlPointDragPosY, newWidth, newHeight);
+ }
+}
+
+void wxControlPoint::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ m_canvas->ReleaseMouse();
+ dc.SetLogicalFunction(wxCOPY);
+ m_shape->Recompute();
+ m_shape->ResetControlPoints();
+
+ if (!m_eraseObject)
+ m_shape->Show(FALSE);
+
+ m_shape->SetSize(controlPointDragEndWidth, 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 = m_shape;
+ wxShapeCanvas *theCanvas = m_canvas;
+ bool eraseIt = m_eraseObject;
+
+ if (theObject->GetCentreResize())
+ theObject->Move(dc, theObject->GetX(), theObject->GetY());
+ else
+ theObject->Move(dc, controlPointDragPosX, controlPointDragPosY);
+
+ if (!eraseIt)
+ theObject->Show(TRUE);
+
+ // Recursively redraw links if we have a composite.
+ if (theObject->GetChildren().Number() > 0)
+ theObject->DrawLinks(dc, -1, TRUE);
+
+ float width, height;
+ theObject->GetBoundingBoxMax(&width, &height);
+ theObject->GetEventHandler()->OnEndSize(width, height);
+
+ if (!theCanvas->GetQuickEditMode() && eraseIt) theCanvas->Redraw(dc);
+}
+
+int wxControlPoint::GetNumberOfAttachments()
+{
+ return 1;
+}
+
+bool wxControlPoint::GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth, int no_arcs, wxLineShape *line)
+{
+ *x = m_xpos; *y = m_ypos;
+ return TRUE;
+}
+
+
+// Polygon control points
+
+IMPLEMENT_DYNAMIC_CLASS(wxPolygonControlPoint, wxControlPoint)
+
+wxPolygonControlPoint::wxPolygonControlPoint(wxShapeCanvas *theCanvas, wxShape *object, float size,
+ wxRealPoint *vertex, float the_xoffset, float the_yoffset):
+ wxControlPoint(theCanvas, object, size, the_xoffset, the_yoffset, 0)
+{
+ m_polygonVertex = vertex;
+}
+
+wxPolygonControlPoint::~wxPolygonControlPoint()
+{
+}
+
+// Implement resizing polygon or moving the vertex.
+void wxPolygonControlPoint::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ float bound_x;
+ float bound_y;
+ m_shape->GetBoundingBoxMin(&bound_x, &bound_y);
+/*
+ float new_width = (float)(2.0*fabs(x - m_shape->GetX()));
+ float new_height = (float)(2.0*fabs(y - m_shape->GetY()));
+*/
+ float dist = (float)sqrt((x - m_shape->GetX())*(x - m_shape->GetX()) +
+ (y - m_shape->GetY())*(y - m_shape->GetY()));
+
+ if (keys & KEY_CTRL)
+ {
+ m_canvas->Snap(&x, &y);
+
+ // Move point
+ m_polygonVertex->x = x - m_shape->GetX();
+ m_polygonVertex->y = y - m_shape->GetY();
+ m_xpos = x;
+ m_xpos = y;
+ ((wxPolygonShape *)m_shape)->CalculateBoundingBox();
+ ((wxPolygonShape *)m_shape)->CalculatePolygonCentre();
+ }
+ else
+ {
+ float new_width = (float)(dist/m_originalDistance)*m_originalSize.x;
+ float new_height = (float)(dist/m_originalDistance)*m_originalSize.y;
+
+ // Non-recursive SetSize for speed
+ m_shape->SetSize(new_width, new_height, FALSE);
+ }
+ float w, h;
+ m_shape->GetBoundingBoxMax(&w, &h);
+ m_shape->GetEventHandler()->OnDrawOutline(dc, m_shape->GetX(), m_shape->GetY(), w, h);
+}
+
+void wxPolygonControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ m_shape->Erase(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+
+ float bound_x;
+ float bound_y;
+ m_shape->GetBoundingBoxMin(&bound_x, &bound_y);
+
+ float dist = (float)sqrt((x - m_shape->GetX())*(x - m_shape->GetX()) +
+ (y - m_shape->GetY())*(y - m_shape->GetY()));
+
+ m_originalDistance = dist;
+ m_originalSize.x = bound_x;
+ m_originalSize.y = bound_y;
+
+ if (m_originalDistance == 0.0) m_originalDistance = (float) 0.0001;
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ if (keys & KEY_CTRL)
+ {
+ m_canvas->Snap(&x, &y);
+
+ // Move point
+ m_polygonVertex->x = x - m_shape->GetX();
+ m_polygonVertex->y = y - m_shape->GetY();
+ m_xpos = x;
+ m_xpos = y;
+ ((wxPolygonShape *)m_shape)->CalculateBoundingBox();
+ ((wxPolygonShape *)m_shape)->CalculatePolygonCentre();
+ }
+ else
+ {
+ float new_width = (float)(dist/m_originalDistance)*m_originalSize.x;
+ float new_height = (float)(dist/m_originalDistance)*m_originalSize.y;
+
+ // Non-recursive SetSize for speed
+ m_shape->SetSize(new_width, new_height, FALSE);
+ }
+
+ float w, h;
+ m_shape->GetBoundingBoxMax(&w, &h);
+ m_shape->GetEventHandler()->OnDrawOutline(dc, m_shape->GetX(), m_shape->GetY(), w, h);
+
+ m_canvas->CaptureMouse();
+}
+
+void wxPolygonControlPoint::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+ 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 *)m_shape)->CalculateBoundingBox();
+ ((wxPolygonShape *)m_shape)->UpdateOriginalPoints();
+ }
+
+ ((wxPolygonShape *)m_shape)->CalculateBoundingBox();
+ ((wxPolygonShape *)m_shape)->CalculatePolygonCentre();
+
+ m_shape->Recompute();
+ m_shape->ResetControlPoints();
+ m_shape->Move(dc, m_shape->GetX(), m_shape->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(float w, float h)
+{
+ m_minWidth = w;
+ m_minHeight = h;
+}
+
+void wxShapeRegion::SetSize(float w, float h)
+{
+ m_width = w;
+ m_height = h;
+}
+
+void wxShapeRegion::SetPosition(float xp, float yp)
+{
+ m_x = xp;
+ m_y = yp;
+}
+
+void wxShapeRegion::SetProportions(float xp, float 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;
+}
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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(float the_x = 0.0, float the_y = 0.0, const wxString& the_line = "");
+ ~wxShapeTextLine();
+
+ inline float GetX() const { return m_x; }
+ inline float GetY() const { return m_y; }
+
+ inline void SetX(float x) { m_x = x; }
+ inline void SetY(float y) { m_y = y; }
+
+ inline void SetText(const wxString& text) { m_line = text; }
+ inline wxString GetText() const { return m_line; }
+
+protected:
+ wxString m_line;
+ float m_x;
+ float m_y;
+};
+
+class wxControlPoint: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxControlPoint)
+
+ public:
+ wxControlPoint(wxShapeCanvas *the_canvas = NULL, wxShape *object = NULL, float size = 0.0, float the_xoffset = 0.0,
+ float 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, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+
+ bool GetAttachmentPosition(int attachment, float *x, float *y,
+ int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+ int GetNumberOfAttachments();
+
+ inline void SetEraseObject(bool er) { m_eraseObject = er; }
+
+public:
+ int m_type;
+ float m_xoffset;
+ float m_yoffset;
+ wxShape* m_shape;
+ wxCursor* m_oldCursor;
+ bool m_eraseObject; // If TRUE, erases object before dragging handle.
+
+};
+
+class wxPolygonControlPoint: public wxControlPoint
+{
+ DECLARE_DYNAMIC_CLASS(wxPolygonControlPoint)
+
+ public:
+ wxPolygonControlPoint(wxShapeCanvas *the_canvas = NULL, wxShape *object = NULL, float size = 0.0, wxRealPoint *vertex = NULL,
+ float the_xoffset = 0.0, float the_yoffset = 0.0);
+ ~wxPolygonControlPoint();
+
+ void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+
+public:
+ wxRealPoint* m_polygonVertex;
+ wxRealPoint m_originalSize;
+ float m_originalDistance;
+};
+
+/*
+ * 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(float w, float h);
+ void SetSize(float w, float h);
+ void SetPosition(float x, float y);
+ void SetProportions(float x, float 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(float *x, float *y) const { *x = m_minWidth; *y = m_minHeight; }
+ inline void GetProportion(float *x, float *y) const { *x = m_regionProportionX; *y = m_regionProportionY; }
+ inline void GetSize(float *x, float *y) const { *x = m_width; *y = m_height; }
+ inline void GetPosition(float *xp, float *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 float GetWidth() const { return m_width; }
+ inline float GetHeight() const { return m_height; }
+
+ void ClearText();
+
+public:
+ wxString m_regionText;
+ wxList m_formattedText; // List of wxShapeTextLines
+ wxFont* m_font;
+ float m_minHeight; // If zero, hide region.
+ float m_minWidth; // If zero, hide region.
+ float m_width;
+ float m_height;
+ float m_x;
+ float m_y;
+
+ float m_regionProportionX; // Proportion of total object size;
+ // -1.0 indicates equal proportion
+ float 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;
+ }
+
+public:
+ int m_id; // Identifier
+ float m_x; // x offset from centre of object
+ float m_y; // y offset from centre of object
+};
+
+#endif
+ // _OGL_BASICP_H_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: bitmap.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 "bitmap.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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#include "basic.h"
+#include "basicp.h"
+#include "canvas.h"
+#include "bitmap.h"
+#include "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);
+ float x, y;
+ x = (long)(m_xpos - m_bitmap.GetWidth() / 2.0);
+ y = (long)(m_ypos - m_bitmap.GetHeight() / 2.0);
+ dc.Blit(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight(), &tempDC, 0, 0);
+}
+
+void wxBitmapShape::SetSize(float w, float 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
+// Prolog database stuff
+char *wxBitmapShape::GetFunctor()
+{
+ return "node_image";
+}
+
+void wxBitmapShape::WritePrologAttributes(wxExpr *clause)
+{
+ // Can't really save the bitmap; so instantiate the bitmap
+ // at a higher level in the application, from a symbol library.
+ wxRectangleShape::WritePrologAttributes(clause);
+ clause->AddAttributeValueString("filename", m_filename);
+}
+
+void wxBitmapShape::ReadPrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::ReadPrologAttributes(clause);
+ clause->GetAttributeValue("filename", m_filename);
+}
+#endif
+
+// Does the copying for this object
+void wxBitmapShape::Copy(wxBitmapShape& copy)
+{
+ wxRectangleShape::Copy(copy);
+ copy.m_bitmap = m_bitmap;
+ copy.SetFilename(m_filename);
+}
+
+// Returns a new instance, and does the copy for this class. Define for each class.
+wxShape *wxBitmapShape::PrivateCopy()
+{
+ wxBitmapShape *obj = new wxBitmapShape;
+ Copy(*obj);
+ return obj;
+}
+
+void wxBitmapShape::SetBitmap(const wxBitmap& bm)
+{
+ m_bitmap = bm;
+ if (m_bitmap.Ok())
+ SetSize(m_bitmap.GetWidth(), m_bitmap.GetHeight());
+}
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: bitmap.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 "bitmap.h"
+#endif
+
+#include "basic.h"
+
+class wxBitmapShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxBitmapShape)
+ public:
+ wxBitmapShape();
+ ~wxBitmapShape();
+
+ void OnDraw(wxDC& dc);
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ char *GetFunctor();
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ // Does the copying for this object
+ void Copy(wxBitmapShape& copy);
+
+ // Returns a new instance, and does the copy for this class. Define for each class.
+ wxShape *PrivateCopy();
+
+ void SetSize(float w, float 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_
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#if USE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "canvas.h"
+#include "ogldiag.h"
+#include "misc.h"
+#include "lines.h"
+#include "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 *GraphicsBullseyeCursor;
+
+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));
+
+ float x, y;
+ x = (float) logPos.x;
+ y = (float) 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(x - m_firstDragX));
+ int dy = abs(dc.LogicalToDeviceY(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((float)x, (float)y, keys, m_draggedAttachment);
+ else
+ {
+ m_draggedShape = NULL;
+ OnBeginDragLeft((float)x, (float)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, (float)x, (float)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((float)x, (float)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((float)x, (float)y, keys, m_draggedAttachment);
+ else
+ {
+ m_draggedShape = NULL;
+ OnBeginDragRight((float)x, (float)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, (float)x, (float)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((float)x, (float)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((float)x, (float)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, (float)x, (float)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((float)x, (float)y, keys);
+ m_draggedShape = NULL;
+ }
+ else if (dragging && !m_draggedShape && m_dragState == StartDraggingRight)
+ {
+ m_dragState = ContinueDraggingRight;
+ OnBeginDragRight((float)x, (float)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, (float)x, (float)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((float)x, (float)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((float)x, (float)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((float)x, (float)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((float)x, (float)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((float)x, (float)y, keys);
+
+ m_draggedShape = NULL;
+ m_dragState = NoDragging;
+ }
+ }
+ }
+}
+
+/*
+ * Try to find a sensitive object, working up the hierarchy of composites.
+ *
+ */
+wxShape *wxShapeCanvas::FindFirstSensitiveShape(float x, float y, int *new_attachment, int op)
+{
+ wxShape *image = FindShape(x, y, new_attachment);
+ if (!image) return NULL;
+
+ wxShape *actualImage = FindFirstSensitiveShape1(image, op);
+ if (actualImage)
+ {
+ float 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)
+{
+ float xp1, yp1, xp2, yp2;
+ float w1, h1, w2, h2;
+ float 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 = (float)(xp1 - (w1 / 2.0));
+ top1 = (float)(yp1 - (h1 / 2.0));
+ right1 = (float)(xp1 + (w1 / 2.0));
+ bottom1 = (float)(yp1 + (h1 / 2.0));
+
+ left2 = (float)(xp2 - (w2 / 2.0));
+ top2 = (float)(yp2 - (h2 / 2.0));
+ right2 = (float)(xp2 + (w2 / 2.0));
+ bottom2 = (float)(yp2 + (h2 / 2.0));
+
+ return ((left1 <= left2) && (top1 <= top2) && (right1 >= right2) && (bottom1 >= bottom2));
+}
+
+wxShape *wxShapeCanvas::FindShape(float x, float y, int *attachment, wxClassInfo *info, wxShape *notObject)
+{
+ float 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();
+
+ float 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();
+ float 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(float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnRightClick(float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnDragLeft(bool draw, float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnBeginDragLeft(float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnEndDragLeft(float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnDragRight(bool draw, float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnBeginDragRight(float x, float y, int keys)
+{
+}
+
+void wxShapeCanvas::OnEndDragRight(float x, float 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(float *x, float *y)
+ { GetDiagram()->Snap(x, y); }
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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(float x, float y, int keys = 0);
+ virtual void OnRightClick(float x, float y, int keys = 0);
+
+ virtual void OnDragLeft(bool draw, float x, float y, int keys=0); // Erase if draw false
+ virtual void OnBeginDragLeft(float x, float y, int keys=0);
+ virtual void OnEndDragLeft(float x, float y, int keys=0);
+
+ virtual void OnDragRight(bool draw, float x, float y, int keys=0); // Erase if draw false
+ virtual void OnBeginDragRight(float x, float y, int keys=0);
+ virtual void OnEndDragRight(float x, float 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(float x, float y, int *attachment, wxClassInfo *info = NULL, wxShape *notImage = NULL);
+ wxShape *FindFirstSensitiveShape(float x, float 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(float *x, float *y);
+
+ // Events
+ void OnPaint(wxPaintEvent& event);
+ void OnMouseEvent(wxMouseEvent& event);
+
+ protected:
+ wxDiagram* m_shapeDiagram;
+ int m_dragState;
+ float m_oldDragX, m_oldDragY; // Previous drag coordinates
+ float 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_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#include "basic.h"
+#include "basicp.h"
+#include "constrnt.h"
+#include "composit.h"
+#include "misc.h"
+#include "canvas.h"
+
+// Sometimes, objects need to access the whole database to
+// construct themselves.
+wxExprDatabase *GlobalwxExprDatabase = NULL;
+
+// Popup menu for editing divisions
+wxMenu *oglPopupDivisionMenu = NULL;
+
+/*
+ * Division control point
+ */
+
+class wxDivisionControlPoint: public wxControlPoint
+{
+ DECLARE_DYNAMIC_CLASS(wxDivisionControlPoint)
+ public:
+ wxDivisionControlPoint() {}
+ wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, float size, float the_xoffset, float the_yoffset, int the_type);
+ ~wxDivisionControlPoint();
+
+ void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float 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)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)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)
+{
+ float x1 = (float)(m_xpos - m_width/2.0);
+ float y1 = (float)(m_ypos - m_height/2.0);
+
+ if (m_shadowMode != SHADOW_NONE)
+ {
+ if (m_shadowBrush)
+ dc.SetBrush(m_shadowBrush);
+ dc.SetPen(transparent_pen);
+
+ if (m_cornerRadius != 0.0)
+ dc.DrawRoundedRectangle(x1 + m_shadowOffsetX, y1 + m_shadowOffsetY,
+ m_width, m_height, m_cornerRadius);
+ else
+ dc.DrawRectangle(x1 + m_shadowOffsetX, y1 + m_shadowOffsetY, m_width, 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, float x, float y, float oldx, float oldy, bool display)
+{
+ float diffX = x - oldx;
+ float 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 float objectStartX = 0.0;
+static float objectStartY = 0.0;
+
+void wxCompositeShape::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ float xx = x;
+ float yy = y;
+ m_canvas->Snap(&xx, &yy);
+ float offsetX = xx - objectStartX;
+ float offsetY = yy - objectStartY;
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+ 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(float x, float y, int keys, int attachment)
+{
+ objectStartX = x;
+ objectStartY = y;
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ Erase(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+ m_canvas->CaptureMouse();
+
+ float xx = x;
+ float yy = y;
+ m_canvas->Snap(&xx, &yy);
+ float offsetX = xx - objectStartX;
+ float offsetY = yy - objectStartY;
+
+ GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
+
+// wxShape::OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxCompositeShape::OnEndDragLeft(float x, float 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);
+ float xx = x;
+ float yy = y;
+ m_canvas->Snap(&xx, &yy);
+ float offsetX = xx - objectStartX;
+ float offsetY = yy - objectStartY;
+
+ Move(dc, GetX() + offsetX, GetY() + offsetY);
+
+ if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
+}
+
+void wxCompositeShape::OnRightClick(float x, float 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;
+ float 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(float w, float h, bool recursive)
+{
+ SetAttachmentSize(w, h);
+
+ float xScale = (float)(w/(wxMax(1.0, GetWidth())));
+ float yScale = (float)(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);
+
+ float xBound, yBound;
+ while (node)
+ {
+ wxShape *object = (wxShape *)node->Data();
+
+ // Scale the position first
+ float newX = (float)(((object->GetX() - GetX())*xScale) + GetX());
+ float newY = (float)(((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)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)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)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)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(wxCompositeShape& copy)
+{
+ wxRectangleShape::Copy(copy);
+
+ // Associate old and new copies for copying constraints and division geometry
+ wxObjectCopyMapping.Append((long)this, ©);
+
+ // Copy the children
+ wxNode *node = m_children.First();
+ while (node)
+ {
+ wxShape *object = (wxShape *)node->Data();
+ wxShape *newObject = object->PrivateCopy();
+ if (newObject->GetId() == 0)
+ newObject->SetId(NewId());
+
+ newObject->SetParent(©);
+ copy.m_children.Append(newObject);
+
+ // Some m_children may be divisions
+ if (m_divisions.Member(object))
+ copy.m_divisions.Append(newObject);
+
+ wxObjectCopyMapping.Append((long)object, newObject);
+
+ node = node->Next();
+ }
+
+ // Copy the constraints
+ node = m_constraints.First();
+ while (node)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)node->Data();
+
+ wxShape *newConstraining = (wxShape *)(wxObjectCopyMapping.Find((long)constraint->m_constrainingObject)->Data());
+
+ wxList newConstrainedList;
+ wxNode *node2 = constraint->m_constrainedObjects.First();
+ while (node2)
+ {
+ wxShape *constrainedObject = (wxShape *)node2->Data();
+ wxShape *newConstrained = (wxShape *)(wxObjectCopyMapping.Find((long)constrainedObject)->Data());
+ newConstrainedList.Append(newConstrained);
+ node2 = node2->Next();
+ }
+
+ OGLConstraint *newConstraint = new OGLConstraint(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);
+ copy.m_constraints.Append(newConstraint);
+
+ node = node->Next();
+ }
+
+ // Now copy the division geometry
+ node = m_divisions.First();
+ while (node)
+ {
+ wxDivisionShape *division = (wxDivisionShape *)node->Data();
+ wxNode *node1 = wxObjectCopyMapping.Find((long)division);
+ wxNode *leftNode = NULL;
+ wxNode *topNode = NULL;
+ wxNode *rightNode = NULL;
+ wxNode *bottomNode = NULL;
+ if (division->GetLeftSide())
+ leftNode = wxObjectCopyMapping.Find((long)division->GetLeftSide());
+ if (division->GetTopSide())
+ topNode = wxObjectCopyMapping.Find((long)division->GetTopSide());
+ if (division->GetRightSide())
+ rightNode = wxObjectCopyMapping.Find((long)division->GetRightSide());
+ if (division->GetBottomSide())
+ bottomNode = wxObjectCopyMapping.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();
+ }
+}
+
+wxShape *wxCompositeShape::PrivateCopy()
+{
+ wxCompositeShape *obj = new wxCompositeShape;
+ Copy(*obj);
+ return obj;
+}
+
+OGLConstraint *wxCompositeShape::AddConstraint(OGLConstraint *constraint)
+{
+ m_constraints.Append(constraint);
+ if (constraint->m_constraintId == 0)
+ constraint->m_constraintId = NewId();
+ return constraint;
+}
+
+OGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxList& constrained)
+{
+ OGLConstraint *constraint = new OGLConstraint(type, constraining, constrained);
+ if (constraint->m_constraintId == 0)
+ constraint->m_constraintId = NewId();
+ m_constraints.Append(constraint);
+ return constraint;
+}
+
+OGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxShape *constrained)
+{
+ wxList l;
+ l.Append(constrained);
+ OGLConstraint *constraint = new OGLConstraint(type, constraining, l);
+ if (constraint->m_constraintId == 0)
+ constraint->m_constraintId = NewId();
+ m_constraints.Append(constraint);
+ return constraint;
+}
+
+OGLConstraint *wxCompositeShape::FindConstraint(long cId, wxCompositeShape **actualComposite)
+{
+ wxNode *node = m_constraints.First();
+ while (node)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)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)))
+ {
+ OGLConstraint *constraint = ((wxCompositeShape *)child)->FindConstraint(cId, actualComposite);
+ if (constraint)
+ {
+ if (actualComposite)
+ *actualComposite = (wxCompositeShape *)child;
+ return constraint;
+ }
+ }
+ node = node->Next();
+ }
+ return NULL;
+}
+
+void wxCompositeShape::DeleteConstraint(OGLConstraint *constraint)
+{
+ m_constraints.DeleteObject(constraint);
+ delete constraint;
+}
+
+void wxCompositeShape::CalculateSize()
+{
+ float maxX = (float) -999999.9;
+ float maxY = (float) -999999.9;
+ float minX = (float) 999999.9;
+ float minY = (float) 999999.9;
+
+ float 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 = (float)(object->GetX() + (w/2.0));
+ if ((object->GetX() - (w/2.0)) < minX)
+ minX = (float)(object->GetX() - (w/2.0));
+ if ((object->GetY() + (h/2.0)) > maxY)
+ maxY = (float)(object->GetY() + (h/2.0));
+ if ((object->GetY() - (h/2.0)) < minY)
+ minY = (float)(object->GetY() - (h/2.0));
+
+ node = node->Next();
+ }
+ m_width = maxX - minX;
+ m_height = maxY - minY;
+ m_xpos = (float)(m_width/2.0 + minX);
+ m_ypos = (float)(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)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)node->Data();
+ if (constraint->Evaluate()) changed = TRUE;
+ node = node->Next();
+ }
+ return changed;
+}
+
+#ifdef PROLOGIO
+void wxCompositeShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::WritePrologAttributes(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)
+ {
+ OGLConstraint *constraint = (OGLConstraint *)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(PrologList);
+ constraintExpr->Append(new wxExpr((long)constraint->m_constraintType));
+ constraintExpr->Append(new wxExpr(PrologString, 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(PrologList);
+ 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(PrologList);
+ 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(PrologList);
+ 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::ReadPrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::ReadPrologAttributes(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;
+ float cXSpacing = 0.0;
+ float 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);
+ }
+ OGLConstraint *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);
+
+ float x1 = (float)(GetX() - (GetWidth()/2.0));
+ float y1 = (float)(GetY() - (GetHeight()/2.0));
+ float x2 = (float)(GetX() + (GetWidth()/2.0));
+ float y2 = (float)(GetY() + (GetHeight()/2.0));
+
+ // Should subtract 1 pixel if drawing under Windows
+#ifdef __WXMSW__
+ y2 -= (float)1.0;
+#endif
+
+ if (m_leftSide)
+ {
+ dc.SetPen(m_leftSidePen);
+ dc.DrawLine(x1, y2, x1, y1);
+ }
+ if (m_topSide)
+ {
+ dc.SetPen(m_topSidePen);
+ dc.DrawLine(x1, y1, x2, 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, float x, float y, float oldx, float oldy, bool display)
+{
+ float diffX = x - oldx;
+ float 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, float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+ {
+ attachment = 0;
+ float 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(float x, float y, int keys, int attachment)
+{
+ if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+ {
+ attachment = 0;
+ float 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(float x, float y, int keys, int attachment)
+{
+ m_canvas->ReleaseMouse();
+ if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
+ {
+ attachment = 0;
+ float 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(float w, float h, bool recursive)
+{
+ m_width = w;
+ m_height = h;
+ wxRectangleShape::SetSize(w, h, recursive);
+}
+
+void wxDivisionShape::CalculateSize()
+{
+}
+
+void wxDivisionShape::Copy(wxDivisionShape& copy)
+{
+ wxCompositeShape::Copy(copy);
+
+ copy.m_leftSideStyle = m_leftSideStyle;
+ copy.m_topSideStyle = m_topSideStyle;
+ copy.m_leftSideColour = m_leftSideColour;
+ copy.m_topSideColour = m_topSideColour;
+
+ copy.m_leftSidePen = m_leftSidePen;
+ copy.m_topSidePen = m_topSidePen;
+ copy.m_handleSide = m_handleSide;
+
+ // Division geometry copying is handled at the wxCompositeShape level.
+}
+
+wxShape *wxDivisionShape::PrivateCopy()
+{
+ wxDivisionShape *obj = new wxDivisionShape;
+ Copy(*obj);
+ return obj;
+}
+
+#ifdef PROLOGIO
+void wxDivisionShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxCompositeShape::WritePrologAttributes(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::ReadPrologAttributes(wxExpr *clause)
+{
+ wxCompositeShape::ReadPrologAttributes(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(float x, float 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;
+ float 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
+ float x1 = (float)(GetX() - (GetWidth()/2.0));
+ float y1 = (float)(GetY() - (GetHeight()/2.0));
+ wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
+ float oldWidth = GetWidth();
+ float 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.
+ float newXPos1 = GetX();
+ float newYPos1 = (float)(y1 + (GetHeight()/4.0));
+ float newXPos2 = GetX();
+ float newYPos2 = (float)(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, (float)(oldHeight/2.0));
+ Move(dc, newXPos1, newYPos1);
+
+ newDivision->SetSize(oldWidth, (float)(oldHeight/2.0));
+ newDivision->Move(dc, newXPos2, newYPos2);
+ }
+ else
+ {
+ // Dividing horizontally means notionally putting a vertical line through it.
+ // Break existing piece into two.
+ float newXPos1 = (float)(x1 + (GetWidth()/4.0));
+ float newYPos1 = GetY();
+ float newXPos2 = (float)(x1 + (3.0*GetWidth()/4.0));
+ float 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((float)(oldWidth/2.0), oldHeight);
+ Move(dc, newXPos1, newYPos1);
+
+ newDivision->SetSize((float)(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()
+{
+ float maxX, maxY;
+
+ GetBoundingBoxMax(&maxX, &maxY);
+ float x, y;
+ int direction;
+/*
+ if (m_leftSide)
+ {
+ x = (float)(-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 = (float)(-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 = (float)(-maxX/2.0);
+ y = 0.0;
+ direction = CONTROL_POINT_HORIZONTAL;
+ break;
+ }
+ case DIVISION_SIDE_TOP:
+ {
+ x = 0.0;
+ y = (float)(-maxY/2.0);
+ direction = CONTROL_POINT_VERTICAL;
+ break;
+ }
+ case DIVISION_SIDE_RIGHT:
+ {
+ x = (float)(maxX/2.0);
+ y = 0.0;
+ direction = CONTROL_POINT_HORIZONTAL;
+ break;
+ }
+ case DIVISION_SIDE_BOTTOM:
+ {
+ x = 0.0;
+ y = (float)(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;
+
+ float 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 = (float)(-maxX/2.0); control->m_yoffset = 0.0;
+ }
+ else if (control->type == CONTROL_POINT_VERTICAL)
+ {
+ control->xoffset = 0.0; control->m_yoffset = (float)(-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 = (float)(-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 = (float)(-maxY/2.0);
+ }
+
+ if ((m_handleSide == DIVISION_SIDE_RIGHT) && node)
+ {
+ wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
+ control->m_xoffset = (float)(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 = (float)(maxY/2.0);
+ }
+}
+
+// Adjust a side, returning FALSE if it's not physically possible.
+bool wxDivisionShape::AdjustLeft(float left, bool test)
+{
+ float x2 = (float)(GetX() + (GetWidth()/2.0));
+
+ if (left >= x2)
+ return FALSE;
+ if (test)
+ return TRUE;
+
+ float newW = x2 - left;
+ float newX = (float)(left + newW/2.0);
+ SetSize(newW, GetHeight());
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ Move(dc, newX, GetY());
+
+ return TRUE;
+}
+
+bool wxDivisionShape::AdjustTop(float top, bool test)
+{
+ float y2 = (float)(GetY() + (GetHeight()/2.0));
+
+ if (top >= y2)
+ return FALSE;
+ if (test)
+ return TRUE;
+
+ float newH = y2 - top;
+ float newY = (float)(top + newH/2.0);
+ SetSize(GetWidth(), newH);
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ Move(dc, GetX(), newY);
+
+ return TRUE;
+}
+
+bool wxDivisionShape::AdjustRight(float right, bool test)
+{
+ float x1 = (float)(GetX() - (GetWidth()/2.0));
+
+ if (right <= x1)
+ return FALSE;
+ if (test)
+ return TRUE;
+
+ float newW = right - x1;
+ float newX = (float)(x1 + newW/2.0);
+ SetSize(newW, GetHeight());
+
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ Move(dc, newX, GetY());
+
+ return TRUE;
+}
+
+bool wxDivisionShape::AdjustBottom(float bottom, bool test)
+{
+ float y1 = (float)(GetY() - (GetHeight()/2.0));
+
+ if (bottom <= y1)
+ return FALSE;
+ if (test)
+ return TRUE;
+
+ float newH = bottom - y1;
+ float newY = (float)(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, float size, float the_xoffset, float the_yoffset, int the_type):
+ wxControlPoint(the_canvas, object, size, the_xoffset, the_yoffset, the_type)
+{
+ SetEraseObject(FALSE);
+}
+
+wxDivisionControlPoint::~wxDivisionControlPoint()
+{
+}
+
+static float originalX = 0.0;
+static float originalY = 0.0;
+static float originalW = 0.0;
+static float originalH = 0.0;
+
+// Implement resizing of canvas object
+void wxDivisionControlPoint::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ wxControlPoint::OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxDivisionControlPoint::OnBeginDragLeft(float x, float 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(float x, float 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.
+ float x1 = (float)(divisionParent->GetX() - (divisionParent->GetWidth()/2.0));
+ float y1 = (float)(divisionParent->GetY() - (divisionParent->GetHeight()/2.0));
+ float x2 = (float)(divisionParent->GetX() + (divisionParent->GetWidth()/2.0));
+ float y2 = (float)(divisionParent->GetY() + (divisionParent->GetHeight()/2.0));
+
+ // Need to check it has not made the division zero or negative width/height
+ float dx1 = (float)(division->GetX() - (division->GetWidth()/2.0));
+ float dy1 = (float)(division->GetY() - (division->GetHeight()/2.0));
+ float dx2 = (float)(division->GetX() + (division->GetWidth()/2.0));
+ float dy2 = (float)(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, float 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
+ *
+ */
+
+void oglGraphicsDivisionMenuProc(wxMenu& menu, wxCommandEvent& event)
+{
+ wxDivisionShape *division = (wxDivisionShape *)menu.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(float x, float y)
+{
+ oglPopupDivisionMenu->SetClientData((char *)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(x - x1*unit_x));
+ int mouse_y = (int)(dc.LogicalToDeviceY(y - y1*unit_y));
+
+ m_canvas->PopupMenu(oglPopupDivisionMenu, mouse_x, mouse_y);
+}
+
+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;
+}
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 OGLConstraint;
+
+/*
+ * 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, float x, float y, float oldX, float oldY, bool display = TRUE);
+ void OnDragLeft(bool draw, float x, float y, int keys, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys, int attachment = 0);
+
+ void OnRightClick(float x, float y, int keys, int attachment = 0);
+
+ void SetSize(float w, float 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);
+
+ OGLConstraint *AddConstraint(OGLConstraint *constraint);
+ OGLConstraint *AddConstraint(int type, wxShape *constraining, wxList& constrained);
+ OGLConstraint *AddConstraint(int type, wxShape *constraining, wxShape *constrained);
+
+ void DeleteConstraint(OGLConstraint *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.
+ OGLConstraint *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
+ // Prolog database stuff
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(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(wxCompositeShape& copy);
+ wxShape *PrivateCopy();
+
+ 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:
+ float m_oldX;
+ float 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, float x, float y, float oldX, float oldY, bool display = TRUE);
+ void OnDragLeft(bool draw, float x, float y, int keys, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys, int attachment = 0);
+
+ void OnRightClick(float x, float 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(float w, float 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
+ // Prolog database stuff
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+ // Does the copying for this object
+ void Copy(wxDivisionShape& copy);
+ wxShape *PrivateCopy();
+
+ // 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, float newPos, bool test);
+
+ // Adjust a side, returning FALSE if it's not physically possible.
+ bool AdjustLeft(float left, bool test);
+ bool AdjustTop(float top, bool test);
+ bool AdjustRight(float right, bool test);
+ bool AdjustBottom(float bottom, bool test);
+
+ // Edit style of left or top side
+ void EditEdge(int side);
+
+ // Popup menu
+ void PopupMenu(float x, float 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;
+};
+
+extern wxMenu *oglPopupDivisionMenu;
+extern void oglGraphicsDivisionMenuProc(wxMenu& menu, wxCommandEvent& event);
+
+#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_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#include "basic.h"
+#include "constrnt.h"
+#include "canvas.h"
+
+wxList OGLConstraintTypes(wxKEY_INTEGER);
+
+/*
+ * Constraint type
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(OGLConstraintType, wxObject)
+
+OGLConstraintType::OGLConstraintType(int theType, const wxString& theName, const wxString& thePhrase)
+{
+ m_type = theType;
+ m_name = theName;
+ m_phrase = thePhrase;
+}
+
+OGLConstraintType::~OGLConstraintType()
+{
+}
+
+void OGLInitializeConstraintTypes()
+{
+ OGLConstraintTypes.Append(gyCONSTRAINT_CENTRED_VERTICALLY,
+ new OGLConstraintType(gyCONSTRAINT_CENTRED_VERTICALLY, "Centre vertically", "centred vertically w.r.t."));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_CENTRED_HORIZONTALLY,
+ new OGLConstraintType(gyCONSTRAINT_CENTRED_HORIZONTALLY, "Centre horizontally", "centred horizontally w.r.t."));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_CENTRED_BOTH,
+ new OGLConstraintType(gyCONSTRAINT_CENTRED_BOTH, "Centre", "centred w.r.t."));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_LEFT_OF,
+ new OGLConstraintType(gyCONSTRAINT_LEFT_OF, "Left of", "left of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_RIGHT_OF,
+ new OGLConstraintType(gyCONSTRAINT_RIGHT_OF, "Right of", "right of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_ABOVE,
+ new OGLConstraintType(gyCONSTRAINT_ABOVE, "Above", "above"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_BELOW,
+ new OGLConstraintType(gyCONSTRAINT_BELOW, "Below", "below"));
+
+ // Alignment
+ OGLConstraintTypes.Append(gyCONSTRAINT_ALIGNED_TOP,
+ new OGLConstraintType(gyCONSTRAINT_ALIGNED_TOP, "Top-aligned", "aligned to the top of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_ALIGNED_BOTTOM,
+ new OGLConstraintType(gyCONSTRAINT_ALIGNED_BOTTOM, "Bottom-aligned", "aligned to the bottom of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_ALIGNED_LEFT,
+ new OGLConstraintType(gyCONSTRAINT_ALIGNED_LEFT, "Left-aligned", "aligned to the left of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_ALIGNED_RIGHT,
+ new OGLConstraintType(gyCONSTRAINT_ALIGNED_RIGHT, "Right-aligned", "aligned to the right of"));
+
+ // Mid-alignment
+ OGLConstraintTypes.Append(gyCONSTRAINT_MIDALIGNED_TOP,
+ new OGLConstraintType(gyCONSTRAINT_MIDALIGNED_TOP, "Top-midaligned", "centred on the top of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_MIDALIGNED_BOTTOM,
+ new OGLConstraintType(gyCONSTRAINT_MIDALIGNED_BOTTOM, "Bottom-midaligned", "centred on the bottom of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_MIDALIGNED_LEFT,
+ new OGLConstraintType(gyCONSTRAINT_MIDALIGNED_LEFT, "Left-midaligned", "centred on the left of"));
+
+ OGLConstraintTypes.Append(gyCONSTRAINT_MIDALIGNED_RIGHT,
+ new OGLConstraintType(gyCONSTRAINT_MIDALIGNED_RIGHT, "Right-midaligned", "centred on the right of"));
+}
+
+/*
+ * Constraint Stuff
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(OGLConstraint, wxObject)
+
+OGLConstraint::OGLConstraint(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();
+ }
+}
+
+OGLConstraint::~OGLConstraint()
+{
+}
+
+bool OGLConstraint::Equals(float a, float b)
+{
+ float marg = 0.5;
+
+ bool eq = ((b <= a + marg) && (b >= a - marg));
+ return eq;
+}
+
+// Return TRUE if anything changed
+bool OGLConstraint::Evaluate()
+{
+ float 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();
+ float totalObjectHeight = 0.0;
+ wxNode *node = m_constrainedObjects.First();
+ while (node)
+ {
+ wxShape *constrainedObject = (wxShape *)node->Data();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+ totalObjectHeight += height2;
+ node = node->Next();
+ }
+ float startY;
+ float spacingY;
+ // Check if within the constraining object...
+ if ((totalObjectHeight + (n + 1)*m_ySpacing) <= minHeight)
+ {
+ spacingY = (float)((minHeight - totalObjectHeight)/(n + 1));
+ startY = (float)(y - (minHeight/2.0));
+ }
+ // Otherwise, use default spacing
+ else
+ {
+ spacingY = m_ySpacing;
+ startY = (float)(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();
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+ startY += (float)(spacingY + (height2/2.0));
+ if (!Equals(startY, constrainedObject->GetY()))
+ {
+ constrainedObject->Move(dc, constrainedObject->GetX(), startY, FALSE);
+ changed = TRUE;
+ }
+ startY += (float)(height2/2.0);
+ node = node->Next();
+ }
+ return changed;
+ }
+ case gyCONSTRAINT_CENTRED_HORIZONTALLY:
+ {
+ int n = m_constrainedObjects.Number();
+ float totalObjectWidth = 0.0;
+ wxNode *node = m_constrainedObjects.First();
+ while (node)
+ {
+ wxShape *constrainedObject = (wxShape *)node->Data();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+ totalObjectWidth += width2;
+ node = node->Next();
+ }
+ float startX;
+ float spacingX;
+ // Check if within the constraining object...
+ if ((totalObjectWidth + (n + 1)*m_xSpacing) <= minWidth)
+ {
+ spacingX = (float)((minWidth - totalObjectWidth)/(n + 1));
+ startX = (float)(x - (minWidth/2.0));
+ }
+ // Otherwise, use default spacing
+ else
+ {
+ spacingX = m_xSpacing;
+ startX = (float)(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();
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+ startX += (float)(spacingX + (width2/2.0));
+ if (!Equals(startX, constrainedObject->GetX()))
+ {
+ constrainedObject->Move(dc, startX, constrainedObject->GetY(), FALSE);
+ changed = TRUE;
+ }
+ startX += (float)(width2/2.0);
+ node = node->Next();
+ }
+ return changed;
+ }
+ case gyCONSTRAINT_CENTRED_BOTH:
+ {
+ int n = m_constrainedObjects.Number();
+ float totalObjectWidth = 0.0;
+ float totalObjectHeight = 0.0;
+ wxNode *node = m_constrainedObjects.First();
+ while (node)
+ {
+ wxShape *constrainedObject = (wxShape *)node->Data();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+ totalObjectWidth += width2;
+ totalObjectHeight += height2;
+ node = node->Next();
+ }
+ float startX;
+ float spacingX;
+ float startY;
+ float spacingY;
+
+ // Check if within the constraining object...
+ if ((totalObjectWidth + (n + 1)*m_xSpacing) <= minWidth)
+ {
+ spacingX = (float)((minWidth - totalObjectWidth)/(n + 1));
+ startX = (float)(x - (minWidth/2.0));
+ }
+ // Otherwise, use default spacing
+ else
+ {
+ spacingX = m_xSpacing;
+ startX = (float)(x - ((totalObjectWidth + (n+1)*spacingX)/2.0));
+ }
+
+ // Check if within the constraining object...
+ if ((totalObjectHeight + (n + 1)*m_ySpacing) <= minHeight)
+ {
+ spacingY = (float)((minHeight - totalObjectHeight)/(n + 1));
+ startY = (float)(y - (minHeight/2.0));
+ }
+ // Otherwise, use default spacing
+ else
+ {
+ spacingY = m_ySpacing;
+ startY = (float)(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();
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+ startX += (float)(spacingX + (width2/2.0));
+ startY += (float)(spacingY + (height2/2.0));
+
+ if ((!Equals(startX, constrainedObject->GetX())) || (!Equals(startY, constrainedObject->GetY())))
+ {
+ constrainedObject->Move(dc, startX, startY, FALSE);
+ changed = TRUE;
+ }
+
+ startX += (float)(width2/2.0);
+ startY += (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float x3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float x3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float y3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float y3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float x3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float x3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float y3 = (float)(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();
+
+ float width2, height2;
+ constrainedObject->GetBoundingBoxMax(&width2, &height2);
+
+ float y3 = (float)(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();
+
+ float x3 = (float)(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();
+
+ float x3 = (float)(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();
+
+ float y3 = (float)(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();
+
+ float y3 = (float)(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;
+}
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 OGLConstraintType: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(OGLConstraintType)
+public:
+ OGLConstraintType(int type = 0, const wxString& name = "", const wxString& phrase = "");
+ ~OGLConstraintType();
+
+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 OGLConstraintTypes;
+
+#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 OGLConstraint: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(OGLConstraint)
+ public:
+ OGLConstraint() { m_xSpacing = 0.0; m_ySpacing = 0.0; m_constraintType = 0; m_constraintName = ""; m_constraintId = 0;
+ m_constrainingObject = NULL; }
+ OGLConstraint(int type, wxShape *constraining, wxList& constrained);
+ ~OGLConstraint();
+
+ // Returns TRUE if anything changed
+ bool Evaluate();
+ inline void SetSpacing(float x, float y) { m_xSpacing = x; m_ySpacing = y; };
+ bool Equals(float a, float b);
+
+ float m_xSpacing;
+ float m_ySpacing;
+ int m_constraintType;
+ wxString m_constraintName;
+ long m_constraintId;
+ wxShape* m_constrainingObject;
+ wxList m_constrainedObjects;
+
+};
+
+void OGLInitializeConstraintTypes();
+
+#endif
+ // _OGL_CONSTRNT_H_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#include "basic.h"
+#include "basicp.h"
+#include "canvas.h"
+#include "divided.h"
+#include "lines.h"
+#include "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,
+ float size, float the_xoffset, float the_yoffset, int the_type);
+ ~wxDividedShapeControlPoint();
+
+ void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDividedShapeControlPoint, wxControlPoint)
+
+/*
+ * Divided object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxDividedShape, wxRectangleShape)
+
+wxDividedShape::wxDividedShape(float w, float h): wxRectangleShape(w, h)
+{
+ ClearRegions();
+}
+
+wxDividedShape::~wxDividedShape()
+{
+}
+
+void wxDividedShape::OnDraw(wxDC& dc)
+{
+ wxRectangleShape::OnDraw(dc);
+}
+
+void wxDividedShape::OnDrawContents(wxDC& dc)
+{
+ float defaultProportion = (float)(GetRegions().Number() > 0 ? (1.0/((float)(GetRegions().Number()))) : 0.0);
+ float currentY = (float)(m_ypos - (m_height / 2.0));
+ float maxY = (float)(m_ypos + (m_height / 2.0));
+
+ float leftX = (float)(m_xpos - (m_width / 2.0));
+ float rightX = (float)(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;
+
+ float xMargin = 2;
+ float yMargin = 2;
+ dc.SetBackgroundMode(wxTRANSPARENT);
+
+ wxNode *node = GetRegions().First();
+ while (node)
+ {
+ wxShapeRegion *region = (wxShapeRegion *)node->Data();
+ dc.SetFont(region->GetFont());
+ dc.SetTextForeground(* region->GetActualColourObject());
+
+ float proportion =
+ region->m_regionProportionY < 0.0 ? defaultProportion : region->m_regionProportionY;
+
+ float y = currentY + m_height*proportion;
+ float actualY = maxY < y ? maxY : y;
+
+ float centreX = m_xpos;
+ float centreY = (float)(currentY + (actualY - currentY)/2.0);
+
+ DrawFormattedText(dc, ®ion->m_formattedText,
+ (float)(centreX), (float)(centreY), (float)(m_width-2*xMargin), (float)(actualY - currentY - 2*yMargin),
+ region->m_formatMode);
+ if ((y <= maxY) && (node->Next()))
+ {
+ wxPen *regionPen = region->GetActualPen();
+ if (regionPen)
+ {
+ dc.SetPen(regionPen);
+ dc.DrawLine(leftX, y, rightX, y);
+ }
+ }
+
+ currentY = actualY;
+
+ node = node->Next();
+ }
+}
+
+void wxDividedShape::SetSize(float w, float h, bool recursive)
+{
+ SetAttachmentSize(w, h);
+ m_width = w;
+ m_height = h;
+ SetRegionSizes();
+}
+
+void wxDividedShape::SetRegionSizes()
+{
+ if (GetRegions().Number() == 0)
+ return;
+
+ float defaultProportion = (float)(GetRegions().Number() > 0 ? (1.0/((float)(GetRegions().Number()))) : 0.0);
+ float currentY = (float)(m_ypos - (m_height / 2.0));
+ float maxY = (float)(m_ypos + (m_height / 2.0));
+
+// float leftX = (float)(m_xpos - (m_width / 2.0));
+// float rightX = (float)(m_xpos + (m_width / 2.0));
+
+ wxNode *node = GetRegions().First();
+ while (node)
+ {
+ wxShapeRegion *region = (wxShapeRegion *)node->Data();
+ float proportion =
+ region->m_regionProportionY <= 0.0 ? defaultProportion : region->m_regionProportionY;
+
+ float sizeY = (float)proportion*m_height;
+ float y = currentY + sizeY;
+ float actualY = maxY < y ? maxY : y;
+
+ float centreY = (float)(currentY + (actualY - currentY)/2.0);
+
+ region->SetSize(m_width, sizeY);
+ region->SetPosition(0.0, (float)(centreY - m_ypos));
+ currentY = actualY;
+ node = node->Next();
+ }
+}
+
+// Attachment points correspond to regions in the divided box
+bool wxDividedShape::GetAttachmentPosition(int attachment, float *x, float *y, int nth, int no_arcs,
+ wxLineShape *line)
+{
+ int totalNumberAttachments = (GetRegions().Number() * 2) + 2;
+ if (!GetAttachmentMode() || (attachment >= totalNumberAttachments))
+ {
+ return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs);
+ }
+
+ int n = GetRegions().Number();
+ bool isEnd = (line && line->IsEnd(this));
+
+ float left = (float)(m_xpos - m_width/2.0);
+ float right = (float)(m_xpos + m_width/2.0);
+ float top = (float)(m_ypos - m_height/2.0);
+ float bottom = (float)(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 = (float)((m_ypos + region->m_y) - (region->m_height/2.0));
+ bottom = (float)((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 = (float)(((m_ypos + region->m_y) - (region->m_height/2.0)) + (nth + 1)*region->m_height/(no_arcs+1));
+ *y = (float)(top + (nth + 1)*region->m_height/(no_arcs+1));
+ }
+ else
+ *y = (float)(m_ypos + region->m_y);
+ }
+ else
+ {
+ *x = m_xpos;
+ *y = m_ypos;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int wxDividedShape::GetNumberOfAttachments()
+{
+ // 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(wxDividedShape& copy)
+{
+ wxRectangleShape::Copy(copy);
+}
+
+wxShape *wxDividedShape::PrivateCopy()
+{
+ wxDividedShape *obj = new wxDividedShape(m_width, m_height);
+ Copy(*obj);
+ return obj;
+}
+
+// Region operations
+
+void wxDividedShape::MakeControlPoints()
+{
+ wxRectangleShape::MakeControlPoints();
+
+ MakeMandatoryControlPoints();
+}
+
+void wxDividedShape::MakeMandatoryControlPoints()
+{
+ float currentY = (float)(GetY() - (m_height / 2.0));
+ float maxY = (float)(GetY() + (m_height / 2.0));
+
+ wxNode *node = GetRegions().First();
+ int i = 0;
+ while (node)
+ {
+ wxShapeRegion *region = (wxShapeRegion *)node->Data();
+
+ float proportion = region->m_regionProportionY;
+
+ float y = currentY + m_height*proportion;
+ float actualY = (float)(maxY < y ? maxY : y);
+
+ if (node->Next())
+ {
+ wxDividedShapeControlPoint *controlPoint =
+ new wxDividedShapeControlPoint(m_canvas, this, i, CONTROL_POINT_SIZE, 0.0, (float)(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()
+{
+ float currentY = (float)(GetY() - (m_height / 2.0));
+ float maxY = (float)(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();
+
+ float proportion = region->m_regionProportionY;
+
+ float y = currentY + m_height*proportion;
+ float actualY = (float)(maxY < y ? maxY : y);
+
+ controlPoint->m_xoffset = 0.0;
+ controlPoint->m_yoffset = (float)(actualY - GetY());
+ currentY = actualY;
+ i ++;
+ }
+ node = node->Next();
+ }
+}
+
+#ifdef PROLOGIO
+void wxDividedShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::WritePrologAttributes(clause);
+}
+
+void wxDividedShape::ReadPrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::ReadPrologAttributes(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", ®ion->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(float x, float 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, float size, float the_m_xoffset, float 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, float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
+ float x1 = (float)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0));
+ float y1 = y;
+ float x2 = (float)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0));
+ float y2 = y;
+ dc.DrawLine(x1, y1, x2, y2);
+}
+
+void wxDividedShapeControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
+ dc.SetLogicalFunction(wxXOR);
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ float x1 = (float)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0));
+ float y1 = y;
+ float x2 = (float)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0));
+ float y2 = y;
+ dc.DrawLine(x1, y1, x2, y2);
+ m_canvas->CaptureMouse();
+}
+
+void wxDividedShapeControlPoint::OnEndDragLeft(float x, float 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.
+
+ float currentY = (float)(dividedObject->GetY() - (dividedObject->GetHeight() / 2.0));
+ float maxY = (float)(dividedObject->GetY() + (dividedObject->GetHeight() / 2.0));
+
+ // Save values
+ float thisRegionTop = 0.0;
+ float thisRegionBottom = 0.0;
+ float nextRegionBottom = 0.0;
+
+ node = dividedObject->GetRegions().First();
+ while (node)
+ {
+ wxShapeRegion *region = (wxShapeRegion *)node->Data();
+
+ float proportion = region->m_regionProportionY;
+ float yy = currentY + (dividedObject->GetHeight()*proportion);
+ float actualY = (float)(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.
+ float thisProportion = (float)((y - thisRegionTop)/dividedObject->GetHeight());
+ float nextProportion = (float)((nextRegionBottom - y)/dividedObject->GetHeight());
+ thisRegion->SetProportions(0.0, thisProportion);
+ nextRegion->SetProportions(0.0, nextProportion);
+ m_yoffset = (float)(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);
+}
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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(float w = 0.0, float h = 0.0);
+ ~wxDividedShape();
+
+ void OnDraw(wxDC& dc);
+ void OnDrawContents(wxDC& dc);
+
+ void SetSize(float w, float h, bool recursive = TRUE);
+
+ void MakeControlPoints();
+ void ResetControlPoints();
+
+ void MakeMandatoryControlPoints();
+ void ResetMandatoryControlPoints();
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ void Copy(wxDividedShape ©);
+ wxShape *PrivateCopy();
+
+ // 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, float *x, float *y,
+ int nth = 0, int no_arcs = 1, wxLineShape *line = NULL);
+ bool AttachmentIsValid(int attachment);
+ int GetNumberOfAttachments();
+
+ // Invoke editor on CTRL-right click
+ void OnRightClick(float x, float y, int keys = 0, int attachment = 0);
+};
+
+#endif
+ // _OGL_DIVIDED_H_
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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"
+#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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#include "basic.h"
+#include "basicp.h"
+#include "canvas.h"
+#include "mfutils.h"
+#include "drawn.h"
+#include "drawnp.h"
+#include "misc.h"
+
+static void IntToHex(unsigned int dec, char *buf);
+static unsigned long HexToInt(char *buf);
+extern char *GraphicsBuffer;
+
+#define gyTYPE_PEN 40
+#define gyTYPE_BRUSH 41
+#define gyTYPE_FONT 42
+
+/*
+ * Drawn object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxDrawnShape, wxShape)
+
+wxDrawnShape::wxDrawnShape():wxRectangleShape(100.0, 50.0)
+{
+ m_saveToFile = TRUE;
+}
+
+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_metafile.m_fillBrush = m_shadowBrush;
+ m_metafile.m_outlinePen = transparent_pen;
+ m_metafile.Draw(dc, m_xpos + m_shadowOffsetX, m_ypos + m_shadowOffsetY);
+ }
+
+ m_metafile.m_outlinePen = m_pen;
+ m_metafile.m_fillBrush = m_brush;
+ m_metafile.Draw(dc, m_xpos, m_ypos);
+}
+
+void wxDrawnShape::SetSize(float w, float h, bool recursive)
+{
+ SetAttachmentSize(w, h);
+
+ float scaleX;
+ float scaleY;
+ if (GetWidth() == 0.0)
+ scaleX = 1.0;
+ else scaleX = w/GetWidth();
+ if (GetHeight() == 0.0)
+ scaleY = 1.0;
+ else scaleY = h/GetHeight();
+
+ m_metafile.Scale(scaleX, scaleY);
+ m_width = w;
+ m_height = h;
+ SetDefaultRegionSize();
+}
+
+void wxDrawnShape::Scale(float sx, float sy)
+{
+ m_metafile.Scale(sx, sy);
+}
+
+void wxDrawnShape::Translate(float x, float y)
+{
+ m_metafile.Translate(x, y);
+}
+
+void wxDrawnShape::Rotate(float x, float y, float theta)
+{
+ if (!m_metafile.GetRotateable())
+ return;
+
+ float actualTheta = theta-m_metafile.m_currentRotation;
+
+ // Rotate metafile
+ m_metafile.Rotate(x, y, theta);
+
+ // Rotate attachment points
+ float sinTheta = (float)sin(actualTheta);
+ float cosTheta = (float)cos(actualTheta);
+ wxNode *node = m_attachmentPoints.First();
+ while (node)
+ {
+ wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
+ float x1 = point->m_x;
+ float y1 = point->m_y;
+ point->m_x = x1*cosTheta - y1*sinTheta + x*(1 - cosTheta) + y*sinTheta;
+ point->m_y = x1*sinTheta + y1*cosTheta + y*(1 - cosTheta) + x*sinTheta;
+ node = node->Next();
+ }
+ m_rotation = theta;
+}
+
+#ifdef PROLOGIO
+// Prolog database stuff
+char *wxDrawnShape::GetFunctor()
+{
+ return "node_image";
+}
+
+void wxDrawnShape::WritePrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::WritePrologAttributes(clause);
+
+ clause->AddAttributeValue("save_metafile", (long)m_saveToFile);
+ if (m_saveToFile)
+ m_metafile.WritePrologAttributes(clause);
+}
+
+void wxDrawnShape::ReadPrologAttributes(wxExpr *clause)
+{
+ wxRectangleShape::ReadPrologAttributes(clause);
+
+ int iVal = (int) m_saveToFile;
+ clause->AssignAttributeValue("save_metafile", &iVal);
+ m_saveToFile = (iVal != 0);
+
+ if (m_saveToFile)
+ m_metafile.ReadPrologAttributes(clause);
+}
+#endif
+
+// Does the copying for this object
+void wxDrawnShape::Copy(wxDrawnShape& copy)
+{
+ wxRectangleShape::Copy(copy);
+ m_metafile.Copy(copy.m_metafile);
+ copy.m_saveToFile = m_saveToFile;
+}
+
+// Returns a new instance, and does the copy for this class. Define for each class.
+wxShape *wxDrawnShape::PrivateCopy()
+{
+ wxDrawnShape *obj = new wxDrawnShape;
+ Copy(*obj);
+ return obj;
+}
+
+bool wxDrawnShape::LoadFromMetaFile(char *filename)
+{
+ return m_metafile.LoadFromMetaFile(filename, &m_width, &m_height);
+}
+
+/*
+ * Individual operations
+ *
+ */
+
+/*
+ * Set font, brush, text colour
+ *
+ */
+
+wxOpSetGDI::wxOpSetGDI(int theOp, wxPseudoMetaFile *theImage, int theGdiIndex, int theMode):
+ wxDrawOp(theOp)
+{
+ gdiIndex = theGdiIndex;
+ image = theImage;
+ mode = theMode;
+}
+
+void wxOpSetGDI::Do(wxDC& dc, float xoffset, float yoffset)
+{
+ switch (op)
+ {
+ case DRAWOP_SET_PEN:
+ {
+ // Check for overriding this operation for outline
+ // colour
+ if (image->m_outlineColours.Member((wxObject *)gdiIndex))
+ {
+ if (image->m_outlinePen)
+ dc.SetPen(image->m_outlinePen);
+ }
+ else
+ {
+ wxNode *node = image->m_gdiObjects.Nth(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 (image->m_outlineColours.Member((wxObject *)gdiIndex))
+ {
+ // Need to construct a brush to match the outline pen's colour
+ if (image->m_outlinePen)
+ {
+ wxBrush *br = wxTheBrushList->FindOrCreateBrush(image->m_outlinePen->GetColour(), wxSOLID);
+ if (br)
+ dc.SetBrush(br);
+ }
+ }
+ else if (image->m_fillColours.Member((wxObject *)gdiIndex))
+ {
+ if (image->m_fillBrush)
+ {
+ dc.SetBrush(image->m_fillBrush);
+ }
+ }
+ else
+ {
+ wxNode *node = image->m_gdiObjects.Nth(gdiIndex);
+ if (node)
+ {
+ wxBrush *brush = (wxBrush *)node->Data();
+ if (brush)
+ dc.SetBrush(brush);
+ }
+ }
+ break;
+ }
+ case DRAWOP_SET_FONT:
+ {
+ wxNode *node = image->m_gdiObjects.Nth(gdiIndex);
+ if (node)
+ {
+ wxFont *font = (wxFont *)node->Data();
+ if (font)
+ dc.SetFont(font);
+ }
+ break;
+ }
+ case DRAWOP_SET_TEXT_COLOUR:
+ {
+ wxColour col(r,g,b);
+ dc.SetTextForeground(col);
+ break;
+ }
+ case DRAWOP_SET_BK_COLOUR:
+ {
+ wxColour col(r,g,b);
+ dc.SetTextBackground(col);
+ break;
+ }
+ case DRAWOP_SET_BK_MODE:
+ {
+ dc.SetBackgroundMode(mode);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+wxDrawOp *wxOpSetGDI::Copy(wxPseudoMetaFile *newImage)
+{
+ wxOpSetGDI *newOp = new wxOpSetGDI(op, newImage, gdiIndex, mode);
+ newOp->r = r;
+ newOp->g = g;
+ newOp->b = b;
+ return newOp;
+}
+
+wxExpr *wxOpSetGDI::WritewxExpr(wxPseudoMetaFile *image)
+{
+ wxExpr *expr = new wxExpr(PrologList);
+ expr->Append(new wxExpr((long)op));
+ switch (op)
+ {
+ case DRAWOP_SET_PEN:
+ case DRAWOP_SET_BRUSH:
+ case DRAWOP_SET_FONT:
+ {
+ expr->Append(new wxExpr((long)gdiIndex));
+ break;
+ }
+ case DRAWOP_SET_TEXT_COLOUR:
+ case DRAWOP_SET_BK_COLOUR:
+ {
+ expr->Append(new wxExpr((long)r));
+ expr->Append(new wxExpr((long)g));
+ expr->Append(new wxExpr((long)b));
+ break;
+ }
+ case DRAWOP_SET_BK_MODE:
+ {
+ expr->Append(new wxExpr((long)mode));
+ break;
+ }
+ default:
+ break;
+ }
+ return expr;
+}
+
+void wxOpSetGDI::ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+ switch (op)
+ {
+ case DRAWOP_SET_PEN:
+ case DRAWOP_SET_BRUSH:
+ case DRAWOP_SET_FONT:
+ {
+ gdiIndex = (int)expr->Nth(1)->IntegerValue();
+ break;
+ }
+ case DRAWOP_SET_TEXT_COLOUR:
+ case DRAWOP_SET_BK_COLOUR:
+ {
+ r = (unsigned char)expr->Nth(1)->IntegerValue();
+ g = (unsigned char)expr->Nth(2)->IntegerValue();
+ b = (unsigned char)expr->Nth(3)->IntegerValue();
+ break;
+ }
+ case DRAWOP_SET_BK_MODE:
+ {
+ mode = (int)expr->Nth(1)->IntegerValue();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * Set/destroy clipping
+ *
+ */
+
+wxOpSetClipping::wxOpSetClipping(int theOp, float theX1, float theY1,
+ float theX2, float theY2):wxDrawOp(theOp)
+{
+ x1 = theX1;
+ y1 = theY1;
+ x2 = theX2;
+ y2 = theY2;
+}
+
+wxDrawOp *wxOpSetClipping::Copy(wxPseudoMetaFile *newImage)
+{
+ wxOpSetClipping *newOp = new wxOpSetClipping(op, x1, y1, x2, y2);
+ return newOp;
+}
+
+void wxOpSetClipping::Do(wxDC& dc, float xoffset, float yoffset)
+{
+ switch (op)
+ {
+ case DRAWOP_SET_CLIPPING_RECT:
+ {
+ dc.SetClippingRegion(x1 + xoffset, y1 + yoffset, x2 + xoffset, y2 + yoffset);
+ break;
+ }
+ case DRAWOP_DESTROY_CLIPPING_RECT:
+ {
+ dc.DestroyClippingRegion();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void wxOpSetClipping::Scale(float xScale, float yScale)
+{
+ x1 *= xScale;
+ y1 *= yScale;
+ x2 *= xScale;
+ y2 *= yScale;
+}
+
+void wxOpSetClipping::Translate(float x, float y)
+{
+ x1 += x;
+ y1 += y;
+}
+
+wxExpr *wxOpSetClipping::WritewxExpr(wxPseudoMetaFile *image)
+{
+ wxExpr *expr = new wxExpr(PrologList);
+ expr->Append(new wxExpr((long)op));
+ switch (op)
+ {
+ case DRAWOP_SET_CLIPPING_RECT:
+ {
+ expr->Append(new wxExpr(x1));
+ expr->Append(new wxExpr(y1));
+ expr->Append(new wxExpr(x2));
+ expr->Append(new wxExpr(y2));
+ break;
+ }
+ default:
+ break;
+ }
+ return expr;
+}
+
+void wxOpSetClipping::ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+ switch (op)
+ {
+ case DRAWOP_SET_CLIPPING_RECT:
+ {
+ x1 = expr->Nth(1)->RealValue();
+ y1 = expr->Nth(2)->RealValue();
+ x2 = expr->Nth(3)->RealValue();
+ y2 = expr->Nth(4)->RealValue();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * Draw line, rectangle, rounded rectangle, ellipse, point, arc, text
+ *
+ */
+
+wxOpDraw::wxOpDraw(int theOp, float theX1, float theY1, float theX2, float theY2,
+ float theRadius, char *s):wxDrawOp(theOp)
+{
+ x1 = theX1;
+ y1 = theY1;
+ x2 = theX2;
+ y2 = theY2;
+ radius = theRadius;
+ if (s) textString = copystring(s);
+ else textString = NULL;
+}
+
+wxOpDraw::~wxOpDraw()
+{
+ if (textString) delete[] textString;
+}
+
+wxDrawOp *wxOpDraw::Copy(wxPseudoMetaFile *newImage)
+{
+ wxOpDraw *newOp = new wxOpDraw(op, x1, y1, x2, y2, radius, textString);
+ return newOp;
+}
+
+void wxOpDraw::Do(wxDC& dc, float xoffset, float yoffset)
+{
+ switch (op)
+ {
+ case DRAWOP_DRAW_LINE:
+ {
+ dc.DrawLine(x1+xoffset, y1+yoffset, x2+xoffset, y2+yoffset);
+ break;
+ }
+ case DRAWOP_DRAW_RECT:
+ {
+ dc.DrawRectangle(x1+xoffset, y1+yoffset, x2+xoffset, y2+yoffset);
+ break;
+ }
+ case DRAWOP_DRAW_ROUNDED_RECT:
+ {
+ dc.DrawRoundedRectangle(x1+xoffset, y1+yoffset, x2+xoffset, y2+yoffset, radius);
+ break;
+ }
+ case DRAWOP_DRAW_ELLIPSE:
+ {
+ dc.DrawEllipse(x1+xoffset, y1+yoffset, x2+xoffset, y2+yoffset);
+ break;
+ }
+ case DRAWOP_DRAW_POINT:
+ {
+ dc.DrawPoint(x1+xoffset, y1+yoffset);
+ break;
+ }
+ case DRAWOP_DRAW_TEXT:
+ {
+ dc.DrawText(textString, x1+xoffset, y1+yoffset);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void wxOpDraw::Scale(float scaleX, float scaleY)
+{
+ x1 *= scaleX;
+ y1 *= scaleY;
+ x2 *= scaleX;
+ y2 *= scaleY;
+ radius *= scaleX;
+}
+
+void wxOpDraw::Translate(float x, float y)
+{
+ x1 += x;
+ y1 += y;
+}
+
+void wxOpDraw::Rotate(float x, float y, float sinTheta, float cosTheta)
+{
+ x1 = x1*cosTheta - y1*sinTheta + x*(1 - cosTheta) + y*sinTheta;
+ y1 = x1*sinTheta + y1*cosTheta + y*(1 - cosTheta) + x*sinTheta;
+
+ switch (op)
+ {
+ case DRAWOP_DRAW_LINE:
+ {
+ x2 = x2*cosTheta - y2*sinTheta + x*(1 - cosTheta) + y*sinTheta;
+ y2 = x2*sinTheta + y2*cosTheta + y*(1 - cosTheta) + x*sinTheta;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+wxExpr *wxOpDraw::WritewxExpr(wxPseudoMetaFile *image)
+{
+ wxExpr *expr = new wxExpr(PrologList);
+ expr->Append(new wxExpr((long)op));
+ switch (op)
+ {
+ case DRAWOP_DRAW_LINE:
+ case DRAWOP_DRAW_RECT:
+ case DRAWOP_DRAW_ELLIPSE:
+ {
+ expr->Append(new wxExpr(x1));
+ expr->Append(new wxExpr(y1));
+ expr->Append(new wxExpr(x2));
+ expr->Append(new wxExpr(y2));
+ break;
+ }
+ case DRAWOP_DRAW_ROUNDED_RECT:
+ {
+ expr->Append(new wxExpr(x1));
+ expr->Append(new wxExpr(y1));
+ expr->Append(new wxExpr(x2));
+ expr->Append(new wxExpr(y2));
+ expr->Append(new wxExpr(radius));
+ break;
+ }
+ case DRAWOP_DRAW_POINT:
+ {
+ expr->Append(new wxExpr(x1));
+ expr->Append(new wxExpr(y1));
+ break;
+ }
+ case DRAWOP_DRAW_TEXT:
+ {
+ expr->Append(new wxExpr(x1));
+ expr->Append(new wxExpr(y1));
+ expr->Append(new wxExpr(PrologString, textString));
+ break;
+ }
+ case DRAWOP_DRAW_ARC:
+ default:
+ {
+ break;
+ }
+ }
+ return expr;
+}
+
+void wxOpDraw::ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+ switch (op)
+ {
+ case DRAWOP_DRAW_LINE:
+ case DRAWOP_DRAW_RECT:
+ case DRAWOP_DRAW_ELLIPSE:
+ {
+ x1 = expr->Nth(1)->RealValue();
+ y1 = expr->Nth(2)->RealValue();
+ x2 = expr->Nth(3)->RealValue();
+ y2 = expr->Nth(4)->RealValue();
+ break;
+ }
+ case DRAWOP_DRAW_ROUNDED_RECT:
+ {
+ x1 = expr->Nth(1)->RealValue();
+ y1 = expr->Nth(2)->RealValue();
+ x2 = expr->Nth(3)->RealValue();
+ y2 = expr->Nth(4)->RealValue();
+ radius = expr->Nth(5)->RealValue();
+ break;
+ }
+ case DRAWOP_DRAW_POINT:
+ {
+ x1 = expr->Nth(1)->RealValue();
+ y1 = expr->Nth(2)->RealValue();
+ break;
+ }
+ case DRAWOP_DRAW_TEXT:
+ {
+ x1 = expr->Nth(1)->RealValue();
+ y1 = expr->Nth(2)->RealValue();
+ textString = copystring(expr->Nth(3)->StringValue());
+ break;
+ }
+ case DRAWOP_DRAW_ARC:
+ default:
+ {
+ break;
+ }
+ }
+}
+
+/*
+ * Draw polygon, polyline, spline
+ *
+ */
+
+wxOpPolyDraw::wxOpPolyDraw(int theOp, int n, wxRealPoint *thePoints):wxDrawOp(theOp)
+{
+ noPoints = n;
+ points = thePoints;
+}
+
+wxOpPolyDraw::~wxOpPolyDraw()
+{
+ delete[] points;
+}
+
+wxDrawOp *wxOpPolyDraw::Copy(wxPseudoMetaFile *newImage)
+{
+ wxRealPoint *newPoints = new wxRealPoint[noPoints];
+ for (int i = 0; i < noPoints; i++)
+ {
+ newPoints[i].x = points[i].x;
+ newPoints[i].y = points[i].y;
+ }
+ wxOpPolyDraw *newOp = new wxOpPolyDraw(op, noPoints, newPoints);
+ return newOp;
+}
+
+void wxOpPolyDraw::Do(wxDC& dc, float xoffset, float yoffset)
+{
+ switch (op)
+ {
+ case DRAWOP_DRAW_POLYLINE:
+ {
+ wxPoint *actualPoints = new wxPoint[noPoints];
+ int i;
+ for (i = 0; i < noPoints; i++)
+ {
+ actualPoints[i].x = (long) points[i].x;
+ actualPoints[i].y = (long) points[i].y;
+ }
+
+ dc.DrawLines(noPoints, actualPoints, xoffset, yoffset);
+
+ delete[] actualPoints;
+ break;
+ }
+ case DRAWOP_DRAW_POLYGON:
+ {
+ wxPoint *actualPoints = new wxPoint[noPoints];
+ int i;
+ for (i = 0; i < noPoints; i++)
+ {
+ actualPoints[i].x = (long) points[i].x;
+ actualPoints[i].y = (long) points[i].y;
+ }
+
+ dc.DrawPolygon(noPoints, actualPoints, xoffset, yoffset);
+
+ delete[] actualPoints;
+ break;
+ }
+ case DRAWOP_DRAW_SPLINE:
+ {
+// dc.DrawSpline(noPoints, points, xoffset, yoffset);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void wxOpPolyDraw::Scale(float scaleX, float scaleY)
+{
+ for (int i = 0; i < noPoints; i++)
+ {
+ points[i].x *= scaleX;
+ points[i].y *= scaleY;
+ }
+}
+
+void wxOpPolyDraw::Translate(float x, float y)
+{
+ for (int i = 0; i < noPoints; i++)
+ {
+ points[i].x += x;
+ points[i].y += y;
+ }
+}
+
+void wxOpPolyDraw::Rotate(float x, float y, float sinTheta, float cosTheta)
+{
+ for (int i = 0; i < noPoints; i++)
+ {
+ float x1 = points[i].x;
+ float y1 = points[i].y;
+ points[i].x = x1*cosTheta - y1*sinTheta + x*(1 - cosTheta) + y*sinTheta;
+ points[i].y = x1*sinTheta + y1*cosTheta + y*(1 - cosTheta) + x*sinTheta;
+ }
+}
+
+wxExpr *wxOpPolyDraw::WritewxExpr(wxPseudoMetaFile *image)
+{
+ wxExpr *expr = new wxExpr(PrologList);
+ expr->Append(new wxExpr((long)op));
+ expr->Append(new wxExpr((long)noPoints));
+
+// char buf1[9];
+ char buf2[5];
+ char buf3[5];
+
+ GraphicsBuffer[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 < noPoints; i++)
+ {
+ long signedX = (long)(points[i].x*100.0);
+ long signedY = (long)(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(GraphicsBuffer, buf2);
+ strcat(GraphicsBuffer, buf3);
+ }
+ }
+ expr->Append(new wxExpr(PrologString, GraphicsBuffer));
+ return expr;
+}
+
+void wxOpPolyDraw::ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr)
+{
+ noPoints = (int)expr->Nth(1)->IntegerValue();
+
+ char buf1[5];
+ char buf2[5];
+
+ points = new wxRealPoint[noPoints];
+ int i = 0;
+ int bufPtr = 0;
+ wxString hexString = expr->Nth(2)->StringValue();
+ while (i < noPoints)
+ {
+ buf1[0] = hexString[bufPtr];
+ buf1[1] = hexString[bufPtr + 1];
+ buf1[2] = hexString[bufPtr + 2];
+ buf1[3] = hexString[bufPtr + 3];
+ buf1[4] = 0;
+
+ buf2[0] = hexString[bufPtr + 4];
+ buf2[1] = hexString[bufPtr + 5];
+ buf2[2] = hexString[bufPtr + 6];
+ buf2[3] = hexString[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
+
+ points[i].x = (float)(signedX / 100.0);
+ points[i].y = (float)(signedY / 100.0);
+
+ i ++;
+ }
+}
+
+
+/*
+ * 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;
+}
+
+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();
+}
+
+void wxPseudoMetaFile::Draw(wxDC& dc, float xoffset, float yoffset)
+{
+ wxNode *node = m_ops.First();
+ while (node)
+ {
+ wxDrawOp *op = (wxDrawOp *)node->Data();
+ op->Do(dc, xoffset, yoffset);
+ node = node->Next();
+ }
+}
+
+void wxPseudoMetaFile::Scale(float sx, float 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(float x, float y)
+{
+ wxNode *node = m_ops.First();
+ while (node)
+ {
+ wxDrawOp *op = (wxDrawOp *)node->Data();
+ op->Translate(x, y);
+ node = node->Next();
+ }
+}
+
+void wxPseudoMetaFile::Rotate(float x, float y, float theta)
+{
+ float theta1 = theta-m_currentRotation;
+ if (theta1 == 0.0) return;
+ float cosTheta = (float)cos(theta1);
+ float sinTheta = (float)sin(theta1);
+
+ wxNode *node = m_ops.First();
+ while (node)
+ {
+ wxDrawOp *op = (wxDrawOp *)node->Data();
+ op->Rotate(x, y, sinTheta, cosTheta);
+ node = node->Next();
+ }
+ m_currentRotation = theta;
+}
+
+#ifdef PROLOGIO
+void wxPseudoMetaFile::WritePrologAttributes(wxExpr *clause)
+{
+ // Write width and height
+ clause->AddAttributeValue("meta_width", m_width);
+ clause->AddAttributeValue("meta_height", m_height);
+ clause->AddAttributeValue("meta_rotateable", (long)m_rotateable);
+
+ // Write GDI objects
+ char buf[50];
+ int i = 1;
+ wxNode *node = m_gdiObjects.First();
+ while (node)
+ {
+ sprintf(buf, "gdi%d", i);
+ wxObject *obj = (wxObject *)node->Data();
+ wxExpr *expr = NULL;
+ if (obj)
+ {
+ if (obj->IsKindOf(CLASSINFO(wxPen)))
+ {
+ wxPen *thePen = (wxPen *)obj;
+ expr = new wxExpr(PrologList);
+ 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(PrologList);
+ 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(PrologList);
+ 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(PrologList);
+ 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", i);
+ wxDrawOp *op = (wxDrawOp *)node->Data();
+ wxExpr *expr = op->WritewxExpr(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(PrologList);
+ node = m_outlineColours.First();
+ while (node)
+ {
+ outlineExpr->Append(new wxExpr((long)node->Data()));
+ node = node->Next();
+ }
+ clause->AddAttributeValue("outline_objects", outlineExpr);
+ }
+ if (m_fillColours.Number() > 0)
+ {
+ wxExpr *fillExpr = new wxExpr(PrologList);
+ node = m_fillColours.First();
+ while (node)
+ {
+ fillExpr->Append(new wxExpr((long)node->Data()));
+ node = node->Next();
+ }
+ clause->AddAttributeValue("fill_objects", fillExpr);
+ }
+
+}
+
+void wxPseudoMetaFile::ReadPrologAttributes(wxExpr *clause)
+{
+ clause->AssignAttributeValue("meta_width", &m_width);
+ clause->AssignAttributeValue("meta_height", &m_height);
+
+ int iVal = (int) m_rotateable;
+ clause->AssignAttributeValue("meta_rotateable", &iVal);
+ m_rotateable = (iVal != 0);
+
+ // Read GDI objects
+ char buf[50];
+ int i = 1;
+ bool keepGoing = TRUE;
+ while (keepGoing)
+ {
+ sprintf(buf, "gdi%d", i);
+ wxExpr *expr = NULL;
+ clause->AssignAttributeValue(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", i);
+ wxExpr *expr = NULL;
+ clause->AssignAttributeValue(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->ReadwxExpr(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->ReadwxExpr(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->ReadwxExpr(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->ReadwxExpr(this, expr);
+ m_ops.Append(theOp);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ i ++;
+ }
+
+ // Now read in the list of outline and fill operations, if any
+ wxExpr *expr1 = clause->AttributeValue("outline_objects");
+ if (expr1)
+ {
+ wxExpr *eachExpr = expr1->GetFirst();
+ while (eachExpr)
+ {
+ m_outlineColours.Append((wxObject *)eachExpr->IntegerValue());
+ eachExpr = eachExpr->GetNext();
+ }
+ }
+ expr1 = clause->AttributeValue("fill_objects");
+ 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.m_currentRotation = m_currentRotation;
+ copy.m_width = m_width;
+ copy.m_height = m_height;
+ copy.m_rotateable = m_rotateable;
+
+ // 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(©));
+ node = node->Next();
+ }
+
+ // Copy the outline/fill operations
+ copy.m_outlineColours.Clear();
+ node = m_outlineColours.First();
+ while (node)
+ {
+ copy.m_outlineColours.Append((wxObject *)node->Data());
+ node = node->Next();
+ }
+ copy.m_fillColours.Clear();
+ 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, float *rwidth, float *rheight)
+{
+ if (!FileExists(filename))
+ return NULL;
+
+ wxXMetaFile *metaFile = new wxXMetaFile;
+
+ if (!metaFile->ReadFile(filename))
+ {
+ delete metaFile;
+ return FALSE;
+ }
+
+ float lastX = 0.0;
+ float 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->r = (unsigned char)record->param1;
+ op->g = (unsigned char)record->param2;
+ op->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->r = (unsigned char)record->param1;
+ op->g = (unsigned char)record->param2;
+ op->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, (float)lastX, (float)lastY,
+ (float)record->param1, (float)record->param2);
+ m_ops.Append(op);
+ break;
+ }
+ case META_MOVETO:
+ {
+ lastX = (float)record->param1;
+ lastY = (float)record->param2;
+ 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
+*/
+ break;
+ }
+ case META_INTERSECTCLIPRECT:
+ {
+/*
+ rec->param4 = getshort(handle); // 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,
+ (float)record->param1, (float)record->param2,
+ (float)(record->param3 - record->param1),
+ (float)(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,
+ (float)record->param1, (float)record->param2,
+ (float)(record->param3 - record->param1),
+ (float)(record->param4 - record->param2));
+ m_ops.Append(op);
+ break;
+ }
+ case META_ROUNDRECT:
+ {
+ wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_ROUNDED_RECT,
+ (float)record->param1, (float)record->param2,
+ (float)(record->param3 - record->param1),
+ (float)(record->param4 - record->param2), (float)record->param5);
+ m_ops.Append(op);
+ break;
+ }
+// case META_PATBLT:
+// case META_SAVEDC:
+ case META_SETPIXEL:
+ {
+ wxOpDraw *op = new wxOpDraw(DRAWOP_DRAW_POINT,
+ (float)record->param1, (float)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,
+ (float)record->param1, (float)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();
+ }
+ float actualWidth = (float)fabs(metaFile->right - metaFile->left);
+ float actualHeight = (float)fabs(metaFile->bottom - metaFile->top);
+
+ float initialScaleX = 1.0;
+ float initialScaleY = 1.0;
+
+ float xoffset, yoffset;
+
+ // Translate so origin is at centre of rectangle
+ if (metaFile->bottom > metaFile->top)
+ yoffset = - (float)((metaFile->bottom - metaFile->top)/2.0);
+ else
+ yoffset = - (float)((metaFile->top - metaFile->bottom)/2.0);
+
+ if (metaFile->right > metaFile->left)
+ xoffset = - (float)((metaFile->right - metaFile->left)/2.0);
+ else
+ xoffset = - (float)((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 = (float)((*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(float w, float h)
+{
+ float scaleX = (float)(w/m_width);
+ float scaleY = (float)(h/m_height);
+
+ // Do the scaling
+ Scale(scaleX, scaleY);
+}
+
+void wxPseudoMetaFile::GetBounds(float *boundMinX, float *boundMinY, float *boundMaxX, float *boundMaxY)
+{
+ float maxX = (float) -99999.9;
+ float maxY = (float) -99999.9;
+ float minX = (float) 99999.9;
+ float minY = (float) 99999.9;
+
+ wxNode *node = m_ops.First();
+ while (node)
+ {
+ wxDrawOp *op = (wxDrawOp *)node->Data();
+ switch (op->op)
+ {
+ 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 *opDraw = (wxOpDraw *)op;
+ if (opDraw->x1 < minX) minX = opDraw->x1;
+ if (opDraw->x1 > maxX) maxX = opDraw->x1;
+ if (opDraw->y1 < minY) minY = opDraw->y1;
+ if (opDraw->y1 > maxY) maxY = opDraw->y1;
+ if (op->op == DRAWOP_DRAW_LINE)
+ {
+ if (opDraw->x2 < minX) minX = opDraw->x2;
+ if (opDraw->x2 > maxX) maxX = opDraw->x2;
+ if (opDraw->y2 < minY) minY = opDraw->y2;
+ if (opDraw->y2 > maxY) maxY = opDraw->y2;
+ }
+ else if (op->op == DRAWOP_DRAW_RECT ||
+ op->op == DRAWOP_DRAW_ROUNDED_RECT ||
+ op->op == DRAWOP_DRAW_ELLIPSE)
+ {
+ if ((opDraw->x1 + opDraw->x2) < minX) minX = (opDraw->x1 + opDraw->x2);
+ if ((opDraw->x1 + opDraw->x2) > maxX) maxX = (opDraw->x1 + opDraw->x2);
+ if ((opDraw->y1 + opDraw->y2) < minY) minY = (opDraw->y1 + opDraw->y2);
+ if ((opDraw->y1 + opDraw->y2) > maxY) maxY = (opDraw->y1 + opDraw->y2);
+ }
+ break;
+ }
+ case DRAWOP_DRAW_POLYLINE:
+ case DRAWOP_DRAW_POLYGON:
+ case DRAWOP_DRAW_SPLINE:
+ {
+ wxOpPolyDraw *poly = (wxOpPolyDraw *)op;
+ for (int i = 0; i < poly->noPoints; i++)
+ {
+ if (poly->points[i].x < minX) minX = poly->points[i].x;
+ if (poly->points[i].x > maxX) maxX = poly->points[i].x;
+ if (poly->points[i].y < minY) minY = poly->points[i].y;
+ if (poly->points[i].y > maxY) maxY = poly->points[i].y;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ node = node->Next();
+ }
+
+ *boundMinX = minX;
+ *boundMinY = minY;
+ *boundMaxX = maxX;
+ *boundMaxY = maxY;
+/*
+ *w = (float)fabs(maxX - minX);
+ *h = (float)fabs(maxY - minY);
+*/
+}
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 "basic.h"
+
+class wxPseudoMetaFile: public wxObject
+{
+ DECLARE_DYNAMIC_CLASS(wxPseudoMetaFile)
+ public:
+ wxPseudoMetaFile();
+ wxPseudoMetaFile(wxPseudoMetaFile& mf);
+ ~wxPseudoMetaFile();
+
+ void Draw(wxDC& dc, float xoffset, float yoffset);
+
+#ifdef PROLOGIO
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ void Clear();
+
+ void Copy(wxPseudoMetaFile& copy);
+
+ void Scale(float sx, float sy);
+ void ScaleTo(float w, float h); // Scale to fit size
+ void Translate(float x, float y);
+
+ // Rotate about the given axis by theta radians from the x axis.
+ void Rotate(float x, float y, float theta);
+
+ bool LoadFromMetaFile(char *filename, float *width, float *height);
+
+ void GetBounds(float *minX, float *minY, float *maxX, float *maxY);
+
+ 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; }
+
+public:
+ bool m_rotateable;
+ float m_width;
+ float m_height;
+ wxList m_ops; // List of drawing operations (see drawnp.h)
+ wxList m_gdiObjects; // List of pens, brushes and fonts for this object.
+
+ // 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
+ float m_currentRotation;
+};
+
+class wxDrawnShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxDrawnShape)
+ public:
+ wxDrawnShape();
+ ~wxDrawnShape();
+
+ void OnDraw(wxDC& dc);
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ char *GetFunctor();
+ void WritePrologAttributes(wxExpr *clause);
+ void ReadPrologAttributes(wxExpr *clause);
+#endif
+
+ // Does the copying for this object
+ void Copy(wxDrawnShape& copy);
+
+ // Returns a new instance, and does the copy for this class. Define for each class.
+ wxShape *PrivateCopy();
+
+ void Scale(float sx, float sy);
+ void Translate(float x, float y);
+ // Rotate about the given axis by theta radians from the x axis.
+ void Rotate(float x, float y, float theta);
+
+ // Get current rotation
+ inline float GetRotation() const { return m_rotation; }
+
+ void SetSize(float w, float h, bool recursive = TRUE);
+ bool LoadFromMetaFile(char *filename);
+
+ inline void SetSaveToFile(bool save) { m_saveToFile = save; }
+ inline wxPseudoMetaFile& GetMetaFile() const { return (wxPseudoMetaFile&) m_metafile; }
+
+private:
+ wxPseudoMetaFile m_metafile;
+
+ // Don't save all wxDrawnShape metafiles to file: sometimes
+ // we take the metafile data from a symbol library.
+ bool m_saveToFile;
+
+};
+
+#endif
+ // _DRAWN_H_
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 "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
+
+/*
+ * Base, virtual class
+ *
+ */
+
+class wxDrawOp: public wxObject
+{
+ public:
+ int op;
+
+ inline wxDrawOp(int theOp) { op = theOp; }
+ inline ~wxDrawOp() {}
+ inline virtual void Scale(float xScale, float yScale) {};
+ inline virtual void Translate(float x, float y) {};
+ inline virtual void Rotate(float x, float y, float sinTheta, float cosTheta) {};
+ virtual void Do(wxDC& dc, float xoffset, float yoffset) = 0;
+ virtual wxDrawOp *Copy(wxPseudoMetaFile *newImage) = 0;
+ virtual wxExpr *WritewxExpr(wxPseudoMetaFile *image) = 0;
+ virtual void ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr) = 0;
+};
+
+/*
+ * Set font, brush, text colour
+ *
+ */
+
+class wxOpSetGDI: public wxDrawOp
+{
+ public:
+ int mode;
+ int gdiIndex;
+ wxPseudoMetaFile *image;
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ wxOpSetGDI(int theOp, wxPseudoMetaFile *theImage, int theGdiIndex, int theMode = 0);
+ void Do(wxDC& dc, float xoffset, float yoffset);
+ wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+ wxExpr *WritewxExpr(wxPseudoMetaFile *image);
+ void ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr);
+};
+
+/*
+ * Set/destroy clipping
+ *
+ */
+
+class wxOpSetClipping: public wxDrawOp
+{
+ public:
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+ wxOpSetClipping(int theOp, float theX1, float theY1, float theX2, float theY2);
+ void Do(wxDC& dc, float xoffset, float yoffset);
+ void Scale(float xScale, float yScale);
+ void Translate(float x, float y);
+ wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+ wxExpr *WritewxExpr(wxPseudoMetaFile *image);
+ void ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr);
+};
+
+/*
+ * Draw line, rectangle, rounded rectangle, ellipse, point, arc, text
+ *
+ */
+
+class wxOpDraw: public wxDrawOp
+{
+ public:
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+ float x3;
+ float radius;
+ char *textString;
+
+ wxOpDraw(int theOp, float theX1, float theY1, float theX2, float theY2,
+ float radius = 0.0, char *s = NULL);
+ ~wxOpDraw();
+ void Do(wxDC& dc, float xoffset, float yoffset);
+ void Scale(float scaleX, float scaleY);
+ void Translate(float x, float y);
+ void Rotate(float x, float y, float sinTheta, float cosTheta);
+ wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+ wxExpr *WritewxExpr(wxPseudoMetaFile *image);
+ void ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr);
+};
+
+/*
+ * Draw polyline, spline, polygon
+ *
+ */
+
+class wxOpPolyDraw: public wxDrawOp
+{
+ public:
+ wxRealPoint *points;
+ int noPoints;
+
+ wxOpPolyDraw(int theOp, int n, wxRealPoint *thePoints);
+ ~wxOpPolyDraw();
+ void Do(wxDC& dc, float xoffset, float yoffset);
+ void Scale(float scaleX, float scaleY);
+ void Translate(float x, float y);
+ void Rotate(float x, float y, float sinTheta, float cosTheta);
+ wxDrawOp *Copy(wxPseudoMetaFile *newImage);
+ wxExpr *WritewxExpr(wxPseudoMetaFile *image);
+ void ReadwxExpr(wxPseudoMetaFile *image, wxExpr *expr);
+};
+
+#endif
+ // _OGL_DRAWNP_H_
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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"
+#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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#if USE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <ctype.h>
+#include <math.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "lines.h"
+#include "linesp.h"
+#include "drawn.h"
+#include "misc.h"
+#include "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;
+ }
+ }
+}
+
+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
+ float line_x = ((last_point->x + second_last_point->x)/2);
+ float 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)
+ {
+ float 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)
+{
+ float 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);
+ }
+
+ wxList *string_list = ::FormatText(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);
+ delete node;
+ node = string_list->First();
+ }
+ delete string_list;
+ float actualW = w;
+ float actualH = h;
+ if (region->GetFormatMode() & FORMAT_SIZE_TO_CONTENTS)
+ {
+ GetCentredTextExtent(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, &actualW, &actualH);
+ if ((actualW != w ) || (actualH != h))
+ {
+ float xx, yy;
+ GetLabelPosition(i, &xx, &yy);
+ EraseRegion(dc, region, xx, yy);
+ if (m_labelObjects[i])
+ {
+ m_labelObjects[i]->Select(FALSE);
+ 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);
+ }
+ }
+ }
+ CentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, actualW, actualH, region->GetFormatMode());
+ m_formatted = TRUE;
+}
+
+void wxLineShape::DrawRegion(wxDC& dc, wxShapeRegion *region, float x, float y)
+{
+ if (GetDisableLabel())
+ return;
+
+ float w, h;
+ float xx, yy;
+ region->GetSize(&w, &h);
+
+ // Get offset from x, y
+ region->GetPosition(&xx, &yy);
+
+ float xp = xx + x;
+ float yp = yy + y;
+
+ // First, clear a rectangle for the text IF there is any
+ if (region->GetFormattedText().Number() > 0)
+ {
+ dc.SetPen(white_background_pen);
+ dc.SetBrush(white_background_brush);
+
+ // Now draw the text
+ if (region->GetFont()) dc.SetFont(region->GetFont());
+
+ dc.DrawRectangle((float)(xp - w/2.0), (float)(yp - h/2.0), (float)w, (float)h);
+
+ if (m_pen) dc.SetPen(m_pen);
+ dc.SetTextForeground(* region->GetActualColourObject());
+
+#ifdef __WXMSW__
+ dc.SetTextBackground(white_background_brush->GetColour());
+#endif
+
+ DrawFormattedText(dc, &(region->GetFormattedText()), xp, yp, w, h, region->GetFormatMode());
+ }
+}
+
+void wxLineShape::EraseRegion(wxDC& dc, wxShapeRegion *region, float x, float y)
+{
+ if (GetDisableLabel())
+ return;
+
+ float w, h;
+ float xx, yy;
+ region->GetSize(&w, &h);
+
+ // Get offset from x, y
+ region->GetPosition(&xx, &yy);
+
+ float xp = xx + x;
+ float yp = yy + y;
+
+ if (region->GetFormattedText().Number() > 0)
+ {
+ dc.SetPen(white_background_pen);
+ dc.SetBrush(white_background_brush);
+
+ dc.DrawRectangle((float)(xp - w/2.0), (float)(yp - h/2.0), (float)w, (float)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, float *x, float *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();
+
+ float dx = (next_point->x - point->x);
+ float dy = (next_point->y - point->y);
+ *x = (float)(point->x + dx/2.0);
+ *y = (float)(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)
+{
+ float dx = point2->x - point1->x;
+ float 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;
+
+ 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();
+ }
+
+ 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(float x1, float y1, float x2, float 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 = (float)((x1 + x2)/2.0);
+ m_ypos = (float)((y1 + y2)/2.0);
+}
+
+// Get absolute positions of ends
+void wxLineShape::GetEnds(float *x1, float *y1, float *x2, float *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(float x, float y, int *attachment, float *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)
+ {
+ float 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;
+ float rLeft = (float)(cx - (cw/2.0));
+ float rTop = (float)(cy - (ch/2.0));
+ float rRight = (float)(cx + (cw/2.0));
+ float rBottom = (float)(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;
+ float left = wxMin(point1->x, point2->x) - extra;
+ float right = wxMax(point1->x, point2->x) + extra;
+
+ float bottom = wxMin(point1->y, point2->y) - extra;
+ float top = wxMax(point1->y, point2->y) + extra;
+
+ if ((x > left && x < right && y > bottom && y < top) || inLabelRegion)
+ {
+ // Work out distance from centre of line
+ float centre_x = (float)(left + (right - left)/2.0);
+ float centre_y = (float)(bottom + (top - bottom)/2.0);
+
+ *attachment = 0;
+ *distance = (float)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.
+ float startArrowPos = 0.0;
+ float endArrowPos = 0.0;
+ float 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, float 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
+ float positionOnLineX, positionOnLineY;
+
+ // Position of start point of line, at the end of which we draw the arrow.
+ float 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.
+ float realOffset = xOffset;
+ if (proportionalOffset)
+ {
+ float totalLength =
+ (float)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 = (float)(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.
+ float realOffset = xOffset;
+ if (proportionalOffset)
+ {
+ float totalLength =
+ (float)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 = (float)(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
+ float x = ((last_line_point->x + second_last_line_point->x)/2);
+ float 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.
+ float realOffset = xOffset;
+ if (proportionalOffset)
+ {
+ float totalLength =
+ (float)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 = (float)(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 float myPi = (float) 3.14159265;
+ // The translation that the y offset may give
+ float deltaX = 0.0;
+ float 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)
+ */
+ float x1 = startPositionX;
+ float y1 = startPositionY;
+ float x3 = positionOnLineX;
+ float y3 = positionOnLineY;
+ float d = -arrow->GetYOffset(); // Negate so +offset is above line
+
+ float theta = 0.0;
+ if (x3 == x1)
+ theta = (float)(myPi/2.0);
+ else
+ theta = (float)atan((y3-y1)/(x3-x1));
+
+ float x4 = (float)(x3 - (d*sin(theta)));
+ float y4 = (float)(y3 + (d*cos(theta)));
+
+ deltaX = x4 - positionOnLineX;
+ deltaY = y4 - positionOnLineY;
+ }
+
+ switch (arrow->_GetType())
+ {
+ case ARROW_ARROW:
+ {
+ float arrowLength = arrow->GetSize();
+ float arrowWidth = (float)(arrowLength/3.0);
+
+ float tip_x, tip_y, side1_x, side1_y, side2_x, side2_y;
+ get_arrow_points(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 = tip_x; points[0].y = tip_y;
+ points[1].x = side1_x; points[1].y = side1_y;
+ points[2].x = side2_x; points[2].y = side2_y;
+ points[3].x = tip_x; points[3].y = 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
+ float diameter = (float)(arrow->GetSize());
+ float x, y;
+ GetPointOnLine(startPositionX+deltaX, startPositionY+deltaY,
+ positionOnLineX+deltaX, positionOnLineY+deltaY,
+ (float)(diameter/2.0),
+ &x, &y);
+
+ // Convert ellipse centre to top-left coordinates
+ float x1 = (float)(x - (diameter/2.0));
+ float y1 = (float)(y - (diameter/2.0));
+
+ dc.SetPen(m_pen);
+ if (arrow->_GetType() == ARROW_HOLLOW_CIRCLE)
+ dc.SetBrush(white_background_brush);
+ else
+ dc.SetBrush(m_brush);
+
+ dc.DrawEllipse(x1, y1, diameter, 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
+ * -----
+ */
+ float x, y;
+ GetPointOnLine(startPositionX, startPositionY,
+ positionOnLineX, positionOnLineY,
+ (float)(arrow->GetMetaFile()->m_width/2.0),
+ &x, &y);
+
+ // Calculate theta for rotating the metafile.
+ /*
+ |
+ | o(x2, y2) 'o' represents the arrowhead.
+ | /
+ | /
+ | /theta
+ | /(x1, y1)
+ |______________________
+ */
+ float theta = 0.0;
+ float x1 = startPositionX;
+ float y1 = startPositionY;
+ float x2 = positionOnLineX;
+ float y2 = positionOnLineY;
+
+ if ((x1 == x2) && (y1 == y2))
+ theta = 0.0;
+
+ else if ((x1 == x2) && (y1 > y2))
+ theta = (float)(3.0*myPi/2.0);
+
+ else if ((x1 == x2) && (y2 > y1))
+ theta = (float)(myPi/2.0);
+
+ else if ((x2 > x1) && (y2 >= y1))
+ theta = (float)atan((y2 - y1)/(x2 - x1));
+
+ else if (x2 < x1)
+ theta = (float)(myPi + atan((y2 - y1)/(x2 - x1)));
+
+ else if ((x2 > x1) && (y2 < y1))
+ theta = (float)(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.
+ float 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((float)(deltaX + x + minX - (extraPixels/2.0)), (float)(deltaY + y + minY - (extraPixels/2.0)),
+ (float)(maxX - minX + extraPixels), (float)(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(white_background_pen);
+ SetBrush(white_background_brush);
+
+ float 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)
+ {
+ float x, y;
+ wxShapeRegion *region = (wxShapeRegion *)node->Data();
+ GetLabelPosition(i, &x, &y);
+ EraseRegion(dc, region, x, y);
+ }
+ }
+
+ // Undraw line
+ dc.SetPen(white_background_pen);
+ dc.SetBrush(white_background_brush);
+
+ // 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((float)(m_xpos - (bound_x/2.0) - 2.0), (float)(m_ypos - (bound_y/2.0) - 2.0),
+ (float)(bound_x+4.0), (float)(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(float *w, float *h)
+{
+ float x1 = 10000;
+ float y1 = 10000;
+ float x2 = -10000;
+ float 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 = (float)(x2 - x1);
+ *h = (float)(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, float x, float y, float w, float h)
+{
+ wxPen *old_pen = m_pen;
+ wxBrush *old_brush = m_brush;
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ SetPen(& dottedPen);
+ SetBrush( wxTRANSPARENT_BRUSH );
+
+/*
+ if (m_isSpline)
+ dc.DrawSpline(m_lineControlPoints);
+ else
+ dc.DrawLines(m_lineControlPoints);
+*/
+ 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, float x, float y, float old_x, float old_y, bool display)
+{
+ float x_offset = x - old_x;
+ float 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);
+ float 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.
+ float end_x, end_y;
+ float 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;
+*/
+
+ float oldX = m_xpos;
+ float 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
+ float x_offset = m_xpos - oldX;
+ float 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() && 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(float *fromX, float *fromY, float *toX, float *toY)
+{
+ if (!m_from || !m_to)
+ return;
+
+ // Do each end - nothing in the middle. User has to move other points
+ // manually if necessary.
+ float end_x, end_y;
+ float 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())
+ {
+ 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(),
+ (float)second_point->x, (float)second_point->y,
+ &end_x, &end_y);
+
+ if (m_to->GetAttachmentMode())
+ {
+ 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(),
+ (float)second_last_point->x, (float)second_last_point->y,
+ &other_end_x, &other_end_y);
+ }
+ else
+ {
+ float fromX = m_from->GetX();
+ float fromY = m_from->GetY();
+ float toX = m_to->GetX();
+ float toY = m_to->GetY();
+
+ if (m_from->GetAttachmentMode())
+ {
+ 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())
+ {
+ 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())
+ (void) m_from->GetPerimeterPoint(m_from->GetX(), m_from->GetY(),
+ toX, toY,
+ &end_x, &end_y);
+
+ if (!m_to->GetAttachmentMode())
+ (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 = (int) point->x;
+ points[i].y = (int) point->y;
+ }
+
+ if (m_isSpline)
+ dc.DrawSpline(n, points);
+ else
+ dc.DrawLines(n, points);
+
+ 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, float x, float y, int keys, int attachment)
+{
+}
+
+void wxLineShape::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+}
+
+void wxLineShape::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+}
+
+/*
+void wxLineShape::SetArrowSize(float length, float 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();
+ float x, y;
+ GetLabelPosition(i, &x, &y);
+ DrawRegion(dc, region, x, y);
+ }
+ }
+}
+
+
+#ifdef PROLOGIO
+char *wxLineShape::GetFunctor()
+{
+ return "arc_image";
+}
+#endif
+
+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::WritePrologAttributes(wxExpr *clause)
+{
+ wxShape::WritePrologAttributes(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(PrologList);
+ wxNode *node = m_lineControlPoints->First();
+ while (node)
+ {
+ wxRealPoint *point = (wxRealPoint *)node->Data();
+ wxExpr *point_list = new wxExpr(PrologList);
+ wxExpr *x_expr = new wxExpr((float) point->x);
+ wxExpr *y_expr = new wxExpr((float) 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(PrologList);
+ node = m_arcArrows.First();
+ while (node)
+ {
+ wxArrowHead *head = (wxArrowHead *)node->Data();
+ wxExpr *head_list = new wxExpr(PrologList);
+ 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(PrologString, (head->GetName() ? 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::ReadPrologAttributes(wxExpr *clause)
+{
+ wxShape::ReadPrologAttributes(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("attachmen_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;
+ float x = xexpr->RealValue();
+
+ wxExpr *yexpr = xexpr->next;
+ float 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;
+ float xOffset = 0.0;
+ float 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 = NewId();
+ else
+ RegisterId(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(wxLineShape& copy)
+{
+ wxShape::Copy(copy);
+
+ copy.m_isSpline = m_isSpline;
+ copy.m_alignmentStart = m_alignmentStart;
+ copy.m_alignmentEnd = m_alignmentEnd;
+ copy.m_maintainStraightLines = m_maintainStraightLines;
+ copy.m_lineOrientations.Clear();
+ wxNode *node = m_lineOrientations.First();
+ while (node)
+ {
+ copy.m_lineOrientations.Append(node->Data());
+ node = node->Next();
+ }
+
+ if (copy.m_lineControlPoints)
+ {
+ ClearPointList(*copy.m_lineControlPoints);
+ delete copy.m_lineControlPoints;
+ }
+
+ copy.m_lineControlPoints = new wxList;
+
+ node = m_lineControlPoints->First();
+ while (node)
+ {
+ wxRealPoint *point = (wxRealPoint *)node->Data();
+ wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
+ copy.m_lineControlPoints->Append((wxObject*) new_point);
+ node = node->Next();
+ }
+
+/*
+ copy.start_style = start_style;
+ copy.end_style = end_style;
+ copy.middle_style = middle_style;
+
+ copy.arrow_length = arrow_length;
+ copy.arrow_width = arrow_width;
+*/
+
+ // Copy new OGL stuff
+ copy.ClearArrowsAtPosition(-1);
+ node = m_arcArrows.First();
+ while (node)
+ {
+ wxArrowHead *arrow = (wxArrowHead *)node->Data();
+ copy.m_arcArrows.Append(new wxArrowHead(*arrow));
+ node = node->Next();
+ }
+}
+
+wxShape *wxLineShape::PrivateCopy()
+{
+ wxLineShape *line = new wxLineShape;
+ Copy(*line);
+ return line;
+}
+
+// Override select, to create/delete temporary label-moving objects
+void wxLineShape::Select(bool select, wxDC* dc)
+{
+ wxShape::Select(select);
+ 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)
+ {
+ float 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] = new wxLabelShape(this, region, w, h);
+ m_labelObjects[i]->AddToCanvas(m_canvas);
+ m_labelObjects[i]->Show(TRUE);
+ if (dc)
+ m_labelObjects[i]->Move(*dc, (float)(x + xx), (float)(y + yy));
+ m_labelObjects[i]->Select(TRUE);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ if (m_labelObjects[i])
+ {
+ m_labelObjects[i]->Select(FALSE, 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, float size, float x, float y, int the_type):
+ wxControlPoint(theCanvas, object, size, x, y, the_type)
+{
+ m_xpos = x;
+ m_ypos = y;
+ m_type = the_type;
+}
+
+wxLineControlPoint::~wxLineControlPoint()
+{
+}
+
+void wxLineControlPoint::OnDraw(wxDC& dc)
+{
+ wxRectangleShape::OnDraw(dc);
+}
+
+// Implement movement of Line point
+void wxLineControlPoint::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ dc.SetLogicalFunction(wxXOR);
+
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ if (m_type == CONTROL_POINT_LINE)
+ {
+ m_canvas->Snap(&x, &y);
+
+ m_xpos = x; m_ypos = y;
+ m_point->x = x; m_point->y = y;
+
+ wxLineShape *lineShape = (wxLineShape *)m_shape;
+
+ 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 (m_type == CONTROL_POINT_ENDPOINT_FROM || m_type == CONTROL_POINT_ENDPOINT_TO)
+ {
+ m_xpos = x; m_ypos = y;
+ }
+
+}
+
+void wxLineControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ wxLineShape *lineShape = (wxLineShape *)m_shape;
+ if (m_type == CONTROL_POINT_LINE)
+ {
+ m_canvas->Snap(&x, &y);
+
+ m_shape->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);
+
+ m_shape->SetDisableLabel(TRUE);
+ dc.SetLogicalFunction(wxXOR);
+
+ m_xpos = x; m_ypos = y;
+ m_point->x = x; 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 (m_type == CONTROL_POINT_ENDPOINT_FROM || m_type == CONTROL_POINT_ENDPOINT_TO)
+ {
+ Erase(dc);
+ lineShape->OnDraw(dc);
+ if (m_type == CONTROL_POINT_ENDPOINT_FROM)
+ {
+ lineShape->GetFrom()->OnDraw(dc);
+ lineShape->GetFrom()->OnDrawContents(dc);
+ }
+ else
+ {
+ lineShape->GetTo()->OnDraw(dc);
+ lineShape->GetTo()->OnDrawContents(dc);
+ }
+ m_canvas->SetCursor(GraphicsBullseyeCursor);
+ m_oldCursor = wxSTANDARD_CURSOR;
+ }
+}
+
+void wxLineControlPoint::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+ wxClientDC dc(GetCanvas());
+ GetCanvas()->PrepareDC(dc);
+
+ m_shape->SetDisableLabel(FALSE);
+ wxLineShape *lineShape = (wxLineShape *)m_shape;
+
+ if (m_type == CONTROL_POINT_LINE)
+ {
+ m_canvas->Snap(&x, &y);
+
+ dc.SetLogicalFunction(wxCOPY);
+ m_xpos = x; m_ypos = y;
+ m_point->x = x; m_point->y = y;
+
+ lineShape->GetEventHandler()->OnMoveLink(dc);
+ }
+ if (m_type == CONTROL_POINT_ENDPOINT_FROM)
+ {
+ if (m_oldCursor)
+ m_canvas->SetCursor(m_oldCursor);
+ m_shape->Erase(dc);
+
+ m_xpos = x; m_ypos = y;
+
+ if (lineShape->GetFrom())
+ {
+ lineShape->GetFrom()->MoveLineToNewAttachment(dc, lineShape, x, y);
+ lineShape->GetFrom()->MoveLinks(dc);
+ }
+ }
+ if (m_type == CONTROL_POINT_ENDPOINT_TO)
+ {
+ if (m_oldCursor)
+ m_canvas->SetCursor(m_oldCursor);
+
+ m_xpos = x; m_ypos = y;
+
+ if (lineShape->GetTo())
+ {
+ lineShape->GetTo()->MoveLineToNewAttachment(dc, lineShape, x, y);
+ 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;
+
+ // 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.
+ wxLineShape *lineObj = lineShape;
+ wxShapeCanvas *objCanvas = m_canvas;
+
+ lineObj->OnMoveControlPoint(i+1, x, y);
+
+ if (!objCanvas->GetQuickEditMode()) objCanvas->Redraw(dc);
+}
+
+// Implement movement of endpoint to a new attachment
+void wxLineControlPoint::OnDragRight(bool draw, float x, float 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(float x, float 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(GraphicsBullseyeCursor);
+ m_oldCursor = wxSTANDARD_CURSOR;
+ }
+}
+
+void wxLineControlPoint::OnEndDragRight(float x, float 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;
+ float 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;
+ float 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);
+}
+
+/*
+ * 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(float x1, float y1, float x2, float y2,
+ float length, float *x, float *y)
+{
+ float l = (float)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+
+ if (l < 0.01)
+ l = (float) 0.01;
+
+ float i_bar = (x2 - x1)/l;
+ float j_bar = (y2 - y1)/l;
+
+ *x = (- length*i_bar) + x2;
+ *y = (- length*j_bar) + y2;
+}
+
+wxArrowHead *wxLineShape::AddArrow(WXTYPE type, int end, float size, float 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.
+ *
+ */
+
+float wxLineShape::FindMinimumWidth()
+{
+ float 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 = (float)(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(float x, float y)
+{
+ float startX, startY, endX, endY;
+ GetEnds(&startX, &startY, &endX, &endY);
+
+ // Find distances from centre, start and end. The smallest wins.
+ float centreDistance = (float)(sqrt((x - m_xpos)*(x - m_xpos) + (y - m_ypos)*(y - m_ypos)));
+ float startDistance = (float)(sqrt((x - startX)*(x - startX) + (y - startY)*(y - startY)));
+ float endDistance = (float)(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, float size, float 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 = NewId();
+}
+
+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 = NewId();
+}
+
+wxArrowHead::~wxArrowHead()
+{
+ if (m_metaFile) delete m_metaFile;
+}
+
+void wxArrowHead::SetSize(float size)
+{
+ m_arrowSize = size;
+ if ((m_arrowType == ARROW_METAFILE) && m_metaFile)
+ {
+ float oldWidth = m_metaFile->m_width;
+ if (oldWidth == 0.0)
+ return;
+
+ float scale = (float)(size/oldWidth);
+ if (scale != 1.0)
+ m_metaFile->Scale(scale, scale);
+ }
+}
+
+/*
+ * Label object
+ *
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxLabelShape, wxRectangleShape)
+
+wxLabelShape::wxLabelShape(wxLineShape *parent, wxShapeRegion *region, float w, float 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;
+
+ float x1 = (float)(m_xpos - m_width/2.0);
+ float y1 = (float)(m_ypos - m_height/2.0);
+
+ if (m_pen)
+ {
+ if (m_pen->GetWidth() == 0)
+ dc.SetPen(transparent_pen);
+ else
+ dc.SetPen(m_pen);
+ }
+ dc.SetBrush(wxTRANSPARENT_BRUSH);
+
+ if (m_cornerRadius > 0.0)
+ dc.DrawRoundedRectangle(x1, y1, m_width, m_height, m_cornerRadius);
+ else
+ dc.DrawRectangle(x1, y1, m_width, m_height);
+}
+
+void wxLabelShape::OnDrawContents(wxDC& dc)
+{
+}
+
+void wxLabelShape::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
+{
+ wxRectangleShape::OnDragLeft(draw, x, y, keys, attachment);
+}
+
+void wxLabelShape::OnBeginDragLeft(float x, float y, int keys, int attachment)
+{
+ wxRectangleShape::OnBeginDragLeft(x, y, keys, attachment);
+}
+
+void wxLabelShape::OnEndDragLeft(float x, float y, int keys, int attachment)
+{
+ wxRectangleShape::OnEndDragLeft(x, y, keys, attachment);
+}
+
+bool wxLabelShape::OnMovePre(wxDC& dc, float x, float y, float old_x, float old_y, bool display)
+{
+ m_shapeRegion->SetSize(m_width, m_height);
+
+ // Find position in line's region list
+ int i = 0;
+ wxNode *node = m_lineShape->GetRegions().First();
+ while (node)
+ {
+ if (m_shapeRegion == (wxShapeRegion *)node->Data())
+ node = NULL;
+ else
+ {
+ node = node->Next();
+ i ++;
+ }
+ }
+ float xx, yy;
+ m_lineShape->GetLabelPosition(i, &xx, &yy);
+ // Set the region's offset, relative to the default position for
+ // each region.
+ m_shapeRegion->SetPosition((float)(x - xx), (float)(y - yy));
+
+ // Need to reformat to fit region.
+ if (m_shapeRegion->GetText())
+ {
+
+ wxString s(m_shapeRegion->GetText());
+ m_lineShape->FormatText(dc, s, i);
+ m_lineShape->DrawRegion(dc, m_shapeRegion, xx, yy);
+ }
+ return TRUE;
+}
+
+// Divert left and right clicks to line object
+void wxLabelShape::OnLeftClick(float x, float y, int keys, int attachment)
+{
+ m_lineShape->GetEventHandler()->OnLeftClick(x, y, keys, attachment);
+}
+
+void wxLabelShape::OnRightClick(float x, float y, int keys, int attachment)
+{
+ m_lineShape->GetEventHandler()->OnRightClick(x, y, keys, attachment);
+}
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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, float size = 0.0, float 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 float GetXOffset() const { return m_xOffset; }
+ inline float GetYOffset() const { return m_yOffset; }
+ inline float GetSpacing() const { return m_spacing; }
+ inline float GetSize() const { return m_arrowSize; }
+ inline wxString GetName() const { return m_arrowName; }
+ inline void SetXOffset(float x) { m_xOffset = x; }
+ inline void SetYOffset(float 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 float GetArrowSize() const { return m_arrowSize; }
+ void SetSize(float size);
+ inline void SetSpacing(float sp) { m_spacing = sp; }
+
+ protected:
+ WXTYPE m_arrowType;
+ int m_arrowEnd; // Position on line
+ float m_xOffset; // Distance from arc start or end, w.r.t. point on arrowhead
+ // nearest start or end. If zero, use default spacing.
+ float m_yOffset; // vertical offset (w.r.t. a horizontal line). Normally zero.
+ float m_spacing; // Spacing from the last arrowhead
+ float 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 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, float x, float y, float old_x, float 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 inline void OnMoveControlPoint(int WXUNUSED(which), float WXUNUSED(x), float WXUNUSED(y)) {}
+ void OnDrawOutline(wxDC& dc, float x, float y, float w, float h);
+ void GetBoundingBoxMin(float *w, float *h);
+ void FormatText(wxDC& dc, const wxString& s, int regionId = 0);
+ virtual void SetEnds(float x1, float y1, float x2, float y2);
+ virtual void GetEnds(float *x1, float *y1, float *x2, float *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(float *fromX, float *fromY, float *toX, float *toY);
+
+ // Format one region at this position
+ void DrawRegion(wxDC& dc, wxShapeRegion *region, float x, float y);
+
+ // Erase one region at this position
+ void EraseRegion(wxDC& dc, wxShapeRegion *region, float x, float 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, float *x, float *y);
+
+ // Straighten verticals and horizontals
+ virtual void Straighten(wxDC& dc);
+
+ // 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, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float 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);
+
+ bool HitTest(float x, float y, int *attachment, float *distance);
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ virtual char *GetFunctor();
+ virtual void WritePrologAttributes(wxExpr *clause);
+ virtual void ReadPrologAttributes(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(float x, float 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(wxLineShape& copy);
+ wxShape *PrivateCopy();
+
+ // New OGL stuff
+ wxArrowHead *AddArrow(WXTYPE type, int end = ARROW_POSITION_END,
+ float arrowSize = 10.0, float 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, float xOffset, bool proportionalOffset);
+ inline void SetIgnoreOffsets(bool ignore) { m_ignoreArrowOffsets = ignore; }
+
+ // Find horizontal width for drawing a line with
+ // arrows in minimum space. Assume arrows at
+ // END only
+ float 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;
+
+ float m_arrowSpacing; // Separation between adjacent arrows
+
+ wxShape* m_to;
+ wxShape* m_from;
+
+/*
+ float m_actualTextWidth; // Space the text takes up
+ float m_actualTextHeight; // (depends on text content unlike nodes)
+*/
+ 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_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 wxLineControlPoint: public wxControlPoint
+{
+ DECLARE_DYNAMIC_CLASS(wxLineControlPoint)
+
+ public:
+
+ wxLineControlPoint(wxShapeCanvas *the_canvas = NULL, wxShape *object = NULL, float size = 0.0,
+ float x = 0.0, float y = 0.0, int the_type = 0);
+ ~wxLineControlPoint();
+
+ void OnDraw(wxDC& dc);
+ void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+
+ void OnDragRight(bool draw, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragRight(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragRight(float x, float y, int keys=0, int attachment = 0);
+
+public:
+
+ int m_type;
+ wxRealPoint* m_point; // Line point
+
+};
+
+/*
+ * Temporary arc label object
+ */
+
+class wxLabelShape: public wxRectangleShape
+{
+ DECLARE_DYNAMIC_CLASS(wxLabelShape)
+
+ public:
+ wxLabelShape(wxLineShape *parent = NULL, wxShapeRegion *region = NULL, float w = 0.0, float h = 0.0);
+ ~wxLabelShape();
+
+ void OnDraw(wxDC& dc);
+ void OnDrawContents(wxDC& dc);
+ void OnLeftClick(float x, float y, int keys = 0, int attachment = 0);
+ void OnRightClick(float x, float y, int keys = 0, int attachment = 0);
+ void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0);
+ void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
+ void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
+ bool OnMovePre(wxDC& dc, float x, float y, float old_x, float 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(float x1, float y1, float x2, float y2,
+ float length, float *x, float *y);
+
+#endif
+ // _OGL_LINESP_H_
+
--- /dev/null
+#
+# File: makefile.b32
+# Author: Julian Smart
+# Created: 1996
+# Updated:
+# Copyright: (c) 1993, AIAI, University of Edinburgh
+#
+# "%W% %G%"
+#
+# Makefile : Builds OGL for BC++, 32-bit.
+
+!if "$(BCCDIR)" == ""
+!error You must define the BCCDIR variable in autoexec.bat, e.g. BCCDIR=d:\bc4
+!endif
+
+!if "$(WXWIN)" == ""
+!error You must define the WXWIN variable in autoexec.bat, e.g. WXWIN=c:\wx
+!endif
+
+# Change WXDIR to wherever wxWindows is found
+WXDIR = $(WXWIN)
+!include $(WXDIR)\src\makeb32.env
+
+WXLIB = $(WXDIR)\lib\wx32.lib
+WXINC = $(WXDIR)\include
+CFG=$(WXWIN)\src\wxwin32.cfg
+
+OGLDIR = $(WXDIR)\utils\ogl
+OGLLIB = $(WXDIR)\lib\ogl.lib
+DOCDIR = $(OGLDIR)\docs
+
+INC=/DPROLOGIO=1 # /I$(WXDIR)\include\base /I$(WXDIR)\include\msw
+
+LIBS=$(WXLIB) $(OGLLIB) mathwl cwl import mathwl
+
+!ifndef FINAL
+FINAL=0
+!endif
+
+!if "$(FINAL)" == "0"
+LINKFLAGS=/v /Tpe /L$(WXLIBDIR);$(BCCDIR)\lib
+OPT = -Od
+DEBUG_FLAGS= -v -DDEBUG=$(DEBUG)
+!else
+LINKFLAGS=/Tpe /L$(WXLIBDIR);$(BCCDIR)\lib
+OPT = -O2
+DEBUG_FLAGS = -DDEBUG=$(DEBUG)
+!endif
+
+CPPFLAGS=$(DEBUG_FLAGS) $(OPT) @$(CFG)
+CFLAGS=$(DEBUG_FLAGS) $(OPT) @$(CFG)
+
+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: $(OGLLIB)
+
+.$(SRCSUFF).obj:
+ bcc32 $(CPPFLAGS) $(INC) -c {$< }
+
+$(OGLLIB): $(OBJECTS)
+ erase $(OGLLIB)
+ tlib /P128 @&&!
+$(OGLLIB) &
++$(OBJECTS:.obj =.obj +)
+!
+
+# 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 *.exe
+ -erase *.res
+ -erase ..\lib\*.lib $(OGLLIB)
+
--- /dev/null
+#
+# File: makefile.bcc
+# Author: Julian Smart
+# Created: 1996
+# Updated:
+# Copyright: (c) 1996
+#
+# "%W% %G%"
+#
+# Makefile : Builds OGL for BC++, 16-bit.
+
+!if "$(BCCDIR)" == ""
+!error You must define the BCCDIR variable in autoexec.bat, e.g. BCCDIR=d:\bc4
+!endif
+
+!if "$(WXWIN)" == ""
+!error You must define the WXWIN variable in autoexec.bat, e.g. WXWIN=c:\wx
+!endif
+
+# Change WXDIR to wherever wxWindows is found
+WXDIR = $(WXWIN)
+!include $(WXDIR)\src\makebcc.env
+
+WXLIB = $(WXDIR)\lib\wx.lib
+WXINC = $(WXDIR)\include
+CFG=$(WXWIN)\src\wxwin.cfg
+
+OGLDIR = $(WXDIR)\utils\ogl
+OGLLIB = $(OGLDIR)\lib\ogl.lib
+DOCDIR = $(OGLDIR)\docs
+
+INC=/DPROLOGIO=1 # /I$(WXDIR)\include\base /I$(WXDIR)\include\msw
+
+LIBS=$(WXLIB) $(OGLLIB) mathwl cwl import mathwl
+
+!ifndef FINAL
+FINAL=0
+!endif
+
+!if "$(FINAL)" == "0"
+LINKFLAGS=/v/Vt /Twe /L$(WXDIR)\lib;$(BCCDIR)\lib
+OPT = -Od
+DEBUG_FLAGS= -v
+!else
+LINKFLAGS=/Twe /L$(WXDIR)\lib;$(BCCDIR)\lib
+OPT = -O2
+DEBUG_FLAGS=
+!endif
+CPPFLAGS=$(DEBUG_FLAGS) $(OPT) @$(CFG)
+CFLAGS=$(DEBUG_FLAGS) $(OPT) @$(CFG)
+
+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: $(OGLLIB)
+
+.$(SRCSUFF).obj:
+ bcc $(CPPFLAGS) $(INC) -c {$< }
+
+$(OGLLIB): $(OBJECTS)
+ erase $(OGLLIB)
+ tlib /P128 @&&!
+$(OGLLIB) &
++$(OBJECTS:.obj =.obj +)
+!
+
+# 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 *.exe
+ -erase *.res
+ -erase ..\lib\*.lib
+
--- /dev/null
+#
+# 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)\utils\ogl
+THISDIR = $(OGLDIR)\src
+DOCDIR = $(OGLDIR)\docs
+WXLIB = $(WXDIR)\lib\wx.lib
+LIBS=$(WXLIB) libw llibcew commdlg shell ctl3dv2
+
+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)
--- /dev/null
+#
+# File: makefile.nt
+# 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)
+OBJECTSDIR = $(WXDIR)\utils\ogl
+THISDIR = $(WXDIR)\utils\ogl\src
+EXTRALIBS=$(WXDIR)\lib\ogl.lib $(WXDIR)\lib\mfutils.lib
+EXTRAINC=/I$(WXDIR)\utils\mfutils\src /I$(WXDIR)\utils\prologio\src
+EXTRAFLAGS=/DPROLOGIO=1
+DOCDIR=$(WXDIR)\docs
+LOCALDOCDIR=$(WXDIR)\utils\ogl\docs
+
+!include $(WXDIR)\src\ntwxwin.mak
+
+PROGRAM=test
+
+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
+
+LIBTARGET=$(WXDIR)\lib\ogl.lib
+
+all: $(LIBTARGET)
+
+$(PROGRAM): $(PROGRAM).exe
+
+wx:
+ cd $(WXDIR)\src\msw
+ nmake -f makefile.nt FINAL=$(FINAL)
+ cd $(THISDIR)
+
+wxclean:
+ cd $(WXDIR)\src\msw
+ nmake -f makefile.nt 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!
+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)
+<<
+
+clean:
+ -erase *.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
+hlp: $(DOCDIR)\winhelp\ogl.hlp
+pdfrtf: $(DOCDIR)\pdf\ogl.rtf
+ps: $(DOCDIR)\ps\ogl.ps
+
+$(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 /w tex2rtf $(LOCALDOCDIR)\ogl.tex $(LOCALDOCDIR)\ogl.rtf -twice -winhelp
+ cd $(THISDIR)
+
+$(DOCDIR)\pdf\ogl.rtf: $(DOCSOURCES)
+ cd $(LOCALDOCDIR)
+ -copy *.bmp $(DOCDIR)\pdf
+ -start /w tex2rtf $(LOCALDOCDIR)\ogl.tex $(DOCDIR)\pdf\ogl.rtf -twice -rtf
+ cd $(THISDIR)
+
+$(DOCDIR)\html\ogl\ogl.htm: $(DOCSOURCES)
+ cd $(LOCALDOCDIR)
+ -mkdir $(DOCDIR)\html\ogl
+ cp *.gif $(DOCDIR)\html\ogl
+ -start /w tex2rtf $(LOCALDOCDIR)\ogl.tex $(DOCDIR)\html\ogl\ogl.htm -twice -html
+ -erase $(DOCDIR)\html\ogl\*.con
+ -erase $(DOCDIR)\html\ogl\*.ref
+ 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)
+
+
--- /dev/null
+#
+# File: makefile.unx
+# Author: Julian Smart
+# Created: 1996
+# Updated:
+# Copyright: (c) 1996 Julian Smart
+#
+# "%W% %G%"
+#
+# Makefile for object graphics library (UNIX).
+
+WXDIR = ../../..
+
+# All common UNIX compiler flags and options are now in
+# this central makefile.
+include $(WXDIR)/src/make.env
+
+PRODIR = $(WXDIR)/utils/prologio
+PROINC = $(PRODIR)/src
+PROLIB = $(PRODIR)/lib/libproio$(GUISUFFIX).a
+
+MFDIR = $(WXDIR)/utils/mfutils
+MFINC = $(MFDIR)/src
+
+OGLDIR = $(WXDIR)/utils/ogl
+OGLLIB = $(OGLDIR)/lib/libogl$(GUISUFFIX).a
+
+OBJECTS = $(OBJDIR)/basic.o $(OBJDIR)/basic2.o $(OBJDIR)/canvas.o $(OBJDIR)/lines.o $(OBJDIR)/misc.o\
+ $(OBJDIR)/divided.o $(OBJDIR)/constrnt.o $(OBJDIR)/composit.o $(OBJDIR)/drawn.o\
+ $(OBJDIR)/bitmap.o $(OBJDIR)/ogldiag.o
+
+CPPFLAGS = -I$(PROINC) -I$(MFINC) $(XINCLUDE) $(INC) $(GUI) -DDEBUG='$(DEBUG)' $(DEBUGFLAGS) $(WARN) $(OPTIONS) -DPROLOGIO
+
+all: $(OBJDIR) $(OGLLIB)
+
+.SUFFIXES:
+
+wx:
+ cd $(WXDIR)/src/x; $(MAKE) -f makefile.unx OPT=$(OPT) GUI=$(GUI)
+
+motif:
+ $(MAKE) -f makefile.unx GUI=-Dwx_motif OPT=$(OPT) GUISUFFIX=_motif LDLIBS='$(MOTIFLDLIBS)' XVIEW_LINK=
+
+xview:
+ $(MAKE) -f makefile.unx GUI=-Dwx_xview OPT=$(OPT) GUISUFFIX=_ol
+
+hp:
+ $(MAKE) -f makefile.unx GUI=-Dwx_motif GUISUFFIX=_hp CC=CC DEBUG='$(DEBUG)' DEBUGFLAGS='-g' OPT='' WARN='-w' \
+ XINCLUDE='$(HPXINCLUDE)' XLIB='$(HPXLIB)' XVIEW_LINK='' \
+ LDLIBS='$(HPLDLIBS)'
+
+$(OBJDIR):
+ mkdir $(OBJDIR)
+
+$(OGLLIB): $(OBJECTS)
+ rm -f $@
+ ar $(AROPTIONS) $@ $(OBJECTS)
+ $(RANLIB) $@
+
+$(OBJDIR)/basic.o: basic.$(SRCSUFF) basic.h lines.h misc.h canvas.h
+ $(CC) -c $(CPPFLAGS) -o $@ basic.$(SRCSUFF)
+
+$(OBJDIR)/basic2.o: basic2.$(SRCSUFF) basic.h lines.h misc.h canvas.h
+ $(CC) -c $(CPPFLAGS) -o $@ basic2.$(SRCSUFF)
+
+$(OBJDIR)/canvas.o: canvas.$(SRCSUFF) basic.h misc.h canvas.h
+ $(CC) -c $(CPPFLAGS) -o $@ canvas.$(SRCSUFF)
+
+$(OBJDIR)/lines.o: lines.$(SRCSUFF) basic.h misc.h canvas.h lines.h
+ $(CC) -c $(CPPFLAGS) -o $@ lines.$(SRCSUFF)
+
+$(OBJDIR)/misc.o: misc.$(SRCSUFF) basic.h misc.h constrnt.h
+ $(CC) -c $(CPPFLAGS) -o $@ misc.$(SRCSUFF)
+
+$(OBJDIR)/divided.o: divided.$(SRCSUFF) basic.h misc.h canvas.h divided.h
+ $(CC) -c $(CPPFLAGS) -o $@ divided.$(SRCSUFF)
+
+$(OBJDIR)/constrnt.o: constrnt.$(SRCSUFF) basic.h constrnt.h
+ $(CC) -c $(CPPFLAGS) -o $@ constrnt.$(SRCSUFF)
+
+$(OBJDIR)/composit.o: composit.$(SRCSUFF) basic.h misc.h canvas.h constrnt.h composit.h
+ $(CC) -c $(CPPFLAGS) -o $@ composit.$(SRCSUFF)
+
+$(OBJDIR)/drawn.o: drawn.$(SRCSUFF) basic.h misc.h canvas.h drawn.h drawnp.h
+ $(CC) -c $(CPPFLAGS) -o $@ drawn.$(SRCSUFF)
+
+$(OBJDIR)/bitmap.o: bitmap.$(SRCSUFF) basic.h misc.h canvas.h bitmap.h
+ $(CC) -c $(CPPFLAGS) -o $@ bitmap.$(SRCSUFF)
+
+$(OBJDIR)/ogldiag.o: ogldiag.$(SRCSUFF) basic.h misc.h canvas.h bitmap.h ogldiag.h
+ $(CC) -c $(CPPFLAGS) -o $@ ogldiag.$(SRCSUFF)
+
+HTMLDIR=/home/hardy/html/wx/manuals
+
+docs: ps xlp
+ps: $(OGLDIR)/docs/ogl.ps
+xlp: $(OGLDIR)/docs/ogl.xlp
+html: $(HTMLDIR)/ogl/ogl_contents.html
+
+$(OGLDIR)/docs/ogl.xlp: $(OGLDIR)/docs/classes.tex $(OGLDIR)/docs/ogl.tex $(OGLDIR)/docs/topics.tex $(OGLDIR)/docs/changes.tex $(OGLDIR)/docs/intro.tex
+ cd ../docs; tex2rtf ogl.tex tmp.xlp -xlp -twice
+ sed -e "s/WXHELPCONTENTS/OGL Manual/g" < $(OGLDIR)/docs/tmp.xlp > $(OGLDIR)/docs/ogl.xlp
+ /bin/rm -f $(OGLDIR)/docs/tmp.xlp
+
+$(HTMLDIR)/ogl/ogl_contents.html: $(OGLDIR)/docs/classes.tex $(OGLDIR)/docs/ogl.tex $(OGLDIR)/docs/topics.tex $(OGLDIR)/docs/changes.tex $(OGLDIR)/docs/intro.tex
+ cd ../docs; tex2rtf ogl.tex $(HTMLDIR)/ogl/ogl -twice -html
+
+$(OGLDIR)/docs/ogl.dvi: $(OGLDIR)/docs/ogl.tex $(OGLDIR)/docs/classes.tex $(OGLDIR)/docs/topics.tex $(OGLDIR)/docs/changes.tex $(OGLDIR)/docs/intro.tex
+ cd $(OGLDIR)/docs; latex ogl; latex ogl; makeindex ogl; latex ogl; \
+
+$(OGLDIR)/docs/ogl.ps: $(OGLDIR)/docs/ogl.dvi
+ cd $(OGLDIR)/docs; dvips -f -r < ogl.dvi > ogl.ps
+
+cleaneach:
+ rm -f $(OBJECTS) $(OGLLIB) core
+
+clean_motif:
+ $(MAKE) -f makefile.unx GUISUFFIX=_motif cleaneach
+
+clean_ol:
+ $(MAKE) -f makefile.unx GUISUFFIX=_ol cleaneach
+
+clean_hp:
+ $(MAKE) -f makefile.unx GUISUFFIX=_hp cleaneach
+
--- /dev/null
+# Objects makefile
+
+WXDIR = ..\..\..
+
+!include $(WXDIR)\src\makewat.env
+
+EXTRACPPFLAGS=/DPROLOGIO
+
+OBJECTSLIB = $(WXDIR)\utils\objects\lib\graphics.lib
+THISDIR = $(WXDIR)\utils\objects\src
+
+NAME = graphics
+LNK = $(name).lnk
+
+IFLAGS = -i=$(WXINC) -i=$(WXBASEINC) -i=..\..\mfutils\src -i=..\..\prologio\src
+
+OBJECTS = basic.obj basic2.obj canvas.obj lines.obj misc.obj divided.obj constrnt.obj composit.obj drawn.obj bitmap.obj
+
+all: $(OBJECTSLIB)
+
+$(OBJECTSLIB): $(OBJECTS)
+ *wlib /b /c /n /P=256 $(OBJECTSLIB) $(OBJECTS)
+
+clean: .SYMBOLIC
+ -erase *.obj *.bak *.err *.pch $(OBJECTSLIB) *.lbc
+
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 "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 == 0x9AC6CDD7)
+ {
+ long hmf = getshort(handle);
+ int iLeft, iTop, iRight, iBottom;
+ iLeft = getsignedshort(handle);
+ iTop = getsignedshort(handle);
+ iRight = getsignedshort(handle);
+ iBottom = getsignedshort(handle);
+
+ left = (float)iLeft;
+ top = (float)iTop;
+ right = (float)iRight;
+ bottom = (float)iBottom;
+
+ int inch = getshort(handle);
+ long reserved = getint(handle);
+ int checksum = getshort(handle);
+/*
+ float widthInUnits = (float)right - left;
+ float heightInUnits = (float)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(lastX, lastY, (float)x1, (float)y1);
+ break;
+ }
+ case META_MOVETO:
+ {
+ lastX = (float)rec->param1;
+ lastY = (float)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((float)rec->param1, (float)rec->param2,
+ (float)rec->param3 - rec->param1,
+ (float)rec->param4 - rec->param2);
+ break;
+ }
+ case META_ROUNDRECT:
+ {
+ dc->DrawRoundedRectangle((float)rec->param1, (float)rec->param2,
+ (float)rec->param3 - rec->param1,
+ (float)rec->param4 - rec->param2,
+ (float)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;
+}
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 __WXMSW__
+#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 !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 */
+#if !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:
+ float lastX;
+ float lastY;
+ bool ok;
+
+ float left;
+ float top;
+ float right;
+ float 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_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#include <wx/types.h>
+
+#if USE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "misc.h"
+#include "constrnt.h"
+#include "composit.h"
+
+wxFont *g_oglNormalFont;
+
+wxPen *black_pen;
+wxPen *white_background_pen;
+wxPen *transparent_pen;
+wxBrush *white_background_brush;
+wxPen *black_foreground_pen;
+
+char *GraphicsBuffer = NULL;
+wxCursor *GraphicsBullseyeCursor = NULL;
+
+wxList wxObjectCopyMapping(wxKEY_INTEGER);
+
+void wxOGLInitialize()
+{
+ GraphicsBullseyeCursor = new wxCursor(wxCURSOR_BULLSEYE);
+
+ g_oglNormalFont = new wxFont(12, wxMODERN, wxNORMAL, wxNORMAL);
+
+ black_pen = new wxPen("BLACK", 1, wxSOLID);
+
+ white_background_pen = new wxPen("WHITE", 1, wxSOLID);
+ transparent_pen = new wxPen("WHITE", 1, wxTRANSPARENT);
+ white_background_brush = new wxBrush("WHITE", wxSOLID);
+ black_foreground_pen = new wxPen("BLACK", 1, wxSOLID);
+
+ OGLInitializeConstraintTypes();
+
+ // Initialize big buffer used when writing images
+ GraphicsBuffer = new char[3000];
+
+ if (!oglPopupDivisionMenu)
+ {
+ oglPopupDivisionMenu = new wxMenu("", (wxFunction)oglGraphicsDivisionMenuProc);
+ oglPopupDivisionMenu->Append(DIVISION_MENU_SPLIT_HORIZONTALLY, "Split horizontally");
+ oglPopupDivisionMenu->Append(DIVISION_MENU_SPLIT_VERTICALLY, "Split vertically");
+ oglPopupDivisionMenu->AppendSeparator();
+ oglPopupDivisionMenu->Append(DIVISION_MENU_EDIT_LEFT_EDGE, "Edit left edge");
+ oglPopupDivisionMenu->Append(DIVISION_MENU_EDIT_TOP_EDGE, "Edit top edge");
+ }
+}
+
+void wxOGLCleanUp()
+{
+ if (GraphicsBuffer)
+ {
+ delete[] GraphicsBuffer;
+ GraphicsBuffer = NULL;
+ }
+ GraphicsBuffer = NULL;
+ if (oglPopupDivisionMenu)
+ {
+ delete oglPopupDivisionMenu;
+ oglPopupDivisionMenu = NULL;
+ }
+}
+
+wxFont *MatchFont(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 MatchFont(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 CentreText(wxDC& dc, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height,
+ int formatMode)
+{
+ int n = text_list->Number();
+
+ if (!text_list || (n == 0))
+ return;
+
+ // First, get maximum dimensions of box enclosing text
+
+ float char_height = 0;
+ float max_width = 0;
+ float current_width = 0;
+
+ // Store text extents for speed
+ float *widths = new float[n];
+
+ wxNode *current = text_list->First();
+ int i = 0;
+ while (current)
+ {
+ wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+ dc.GetTextExtent(line->GetText(), ¤t_width, &char_height);
+ widths[i] = current_width;
+
+ if (current_width > max_width)
+ max_width = current_width;
+ current = current->Next();
+ i ++;
+ }
+
+ float max_height = n*char_height;
+
+ float xoffset, yoffset, xOffset, yOffset;
+
+ if (formatMode & FORMAT_CENTRE_VERT)
+ {
+ if (max_height < height)
+ yoffset = (float)(m_ypos - (height/2.0) + (height - max_height)/2.0);
+ else
+ yoffset = (float)(m_ypos - (height/2.0));
+ yOffset = m_ypos;
+ }
+ else
+ {
+ yoffset = 0.0;
+ yOffset = 0.0;
+ }
+
+ if (formatMode & FORMAT_CENTRE_HORIZ)
+ {
+ xoffset = (float)(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();
+
+ float x;
+ if ((formatMode & FORMAT_CENTRE_HORIZ) && (widths[i] < width))
+ x = (float)((width - widths[i])/2.0 + xoffset);
+ else
+ x = xoffset;
+ float y = (float)(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 CentreTextNoClipping(wxDC& dc, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height)
+{
+ int n = text_list->Number();
+
+ if (!text_list || (n == 0))
+ return;
+
+ // First, get maximum dimensions of box enclosing text
+
+ float char_height = 0;
+ float max_width = 0;
+ float current_width = 0;
+
+ // Store text extents for speed
+ float *widths = new float[n];
+
+ wxNode *current = text_list->First();
+ int i = 0;
+ while (current)
+ {
+ wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+ dc.GetTextExtent(line->GetText(), ¤t_width, &char_height);
+ widths[i] = current_width;
+
+ if (current_width > max_width)
+ max_width = current_width;
+ current = current->Next();
+ i ++;
+ }
+
+ float max_height = n*char_height;
+
+ float yoffset = (float)(m_ypos - (height/2.0) + (height - max_height)/2.0);
+
+ float xoffset = (float)(m_xpos - width/2.0);
+
+ current = text_list->First();
+ i = 0;
+
+ while (current)
+ {
+ wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+
+ float x = (float)((width - widths[i])/2.0 + xoffset);
+ float y = (float)(i*char_height + yoffset);
+
+ line->SetX( x - m_xpos ); line->SetY( y - m_ypos );
+ current = current->Next();
+ i ++;
+ }
+ delete widths;
+}
+
+void GetCentredTextExtent(wxDC& dc, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height,
+ float *actual_width, float *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
+
+ float char_height = 0;
+ float max_width = 0;
+ float current_width = 0;
+
+ wxNode *current = text_list->First();
+ int i = 0;
+ while (current)
+ {
+ wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+ dc.GetTextExtent(line->GetText(), ¤t_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.
+wxList *FormatText(wxDC& dc, const wxString& text, float width, float height, int formatMode)
+{
+ // First, parse the string into a list of words
+ wxList 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.Append((wxObject *)copystring(word));
+ end_word = FALSE;
+ }
+ if (new_line)
+ {
+ word_list.Append((wxObject *)NULL);
+ new_line = FALSE;
+ }
+ }
+ // Now, make a list of strings which can fit in the box
+ wxList *string_list = new wxList;
+
+ char buffer[400];
+ buffer[0] = 0;
+ wxNode *node = word_list.First();
+ float x, y;
+
+ while (node)
+ {
+ char *keep_string = copystring(buffer);
+
+ char *s = (char *)node->Data();
+ if (!s)
+ {
+ // FORCE NEW LINE
+ if (strlen(keep_string) > 0)
+ string_list->Append((wxObject *)keep_string);
+ else
+ delete[] keep_string;
+
+ 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 (strlen(keep_string) > 0)
+ string_list->Append((wxObject *)keep_string);
+ else
+ delete[] keep_string;
+
+ buffer[0] = 0;
+ strcat(buffer, s);
+ delete[] s;
+ }
+ else
+ delete[] keep_string;
+ }
+
+ node = node->Next();
+ }
+ if (buffer[0] != 0)
+ string_list->Append((wxObject *)copystring(buffer));
+
+ return string_list;
+}
+
+void DrawFormattedText(wxDC& dc, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height,
+ int formatMode)
+{
+ float xoffset, yoffset;
+ if (formatMode & FORMAT_CENTRE_HORIZ)
+ xoffset = m_xpos;
+ else
+ xoffset = (float)(m_xpos - (width / 2.0));
+
+ if (formatMode & FORMAT_CENTRE_VERT)
+ yoffset = m_ypos;
+ else
+ yoffset = (float)(m_ypos - (height / 2.0));
+
+ dc.SetClippingRegion(
+ (float)(m_xpos - width/2.0), (float)(m_ypos - height/2.0),
+ (float)width, (float)height);
+
+ wxNode *current = text_list->First();
+ while (current)
+ {
+ wxShapeTextLine *line = (wxShapeTextLine *)current->Data();
+
+ dc.DrawText(line->GetText(), xoffset + line->GetX(), yoffset + line->GetY());
+ current = current->Next();
+ }
+
+ dc.DestroyClippingRegion();
+}
+
+/*
+ * Find centroid given list of points comprising polyline
+ *
+ */
+
+void find_polyline_centroid(wxList *points, float *x, float *y)
+{
+ float xcount = 0;
+ float 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 check_line_intersection(float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4,
+ float *ratio1, float *ratio2)
+{
+ float denominator_term = (y4 - y3)*(x2 - x1) - (y2 - y1)*(x4 - x3);
+ float numerator_term = (x3 - x1)*(y4 - y3) + (x4 - x3)*(y1 - y3);
+
+ float line_constant;
+ float length_ratio = 1.0;
+ float 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 find_end_for_polyline(float n, float xvec[], float yvec[],
+ float x1, float y1, float x2, float y2, float *x3, float *y3)
+{
+ int i;
+ float lastx = xvec[0];
+ float lasty = yvec[0];
+
+ float min_ratio = 1.0;
+ float line_ratio;
+ float other_ratio;
+
+ for (i = 1; i < n; i++)
+ {
+ check_line_intersection(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 floats are not identical
+ if (!(xvec[0] == lastx && yvec[0] == lasty))
+ {
+ check_line_intersection(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 find_end_for_box(float width, float height,
+ float x1, float y1, // Centre of box (possibly)
+ float x2, float y2, // other end of line
+ float *x3, float *y3) // End on box edge
+{
+ float xvec[5];
+ float yvec[5];
+
+ xvec[0] = (float)(x1 - width/2.0);
+ yvec[0] = (float)(y1 - height/2.0);
+ xvec[1] = (float)(x1 - width/2.0);
+ yvec[1] = (float)(y1 + height/2.0);
+ xvec[2] = (float)(x1 + width/2.0);
+ yvec[2] = (float)(y1 + height/2.0);
+ xvec[3] = (float)(x1 + width/2.0);
+ yvec[3] = (float)(y1 - height/2.0);
+ xvec[4] = (float)(x1 - width/2.0);
+ yvec[4] = (float)(y1 - height/2.0);
+
+ find_end_for_polyline(5, xvec, yvec, x2, y2, x1, y1, x3, y3);
+}
+
+/*
+ * Find where the line hits the circle.
+ *
+ */
+
+void find_end_for_circle(float radius,
+ float x1, float y1, // Centre of circle
+ float x2, float y2, // Other end of line
+ float *x3, float *y3)
+{
+ float H = (float)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 get_arrow_points(float x1, float y1, float x2, float y2,
+ float length, float width,
+ float *tip_x, float *tip_y,
+ float *side1_x, float *side1_y,
+ float *side2_x, float *side2_y)
+{
+ float l = (float)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+
+ if (l < 0.01)
+ l = (float) 0.01;
+
+ float i_bar = (x2 - x1)/l;
+ float j_bar = (y2 - y1)/l;
+
+ float x3 = (- length*i_bar) + x2;
+ float 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 draw_arc_to_ellipse(float x1, float y1, float width1, float height1, float x2, float y2, float x3, float y3,
+ float *x4, float *y4)
+{
+ float a1 = (float)(width1/2.0);
+ float b1 = (float)(height1/2.0);
+
+ // These are required to give top left x and y coordinates for DrawEllipse
+// float top_left_x1 = (float)(x1 - a1);
+// float top_left_y1 = (float)(y1 - b1);
+/*
+ // Check for vertical line
+ if (fabs(x2 - x3) < 0.05)
+ {
+ *x4 = x3;
+ if (y2 < y3)
+ *y4 = (float)(y1 - b1);
+ else
+ *y4 = (float)(y1 + b1);
+ return;
+ }
+*/
+ // Check that x2 != x3
+ if (fabs(x2 - x3) < 0.05)
+ {
+ *x4 = x2;
+ if (y3 > y2)
+ *y4 = (float)(y1 - sqrt((b1*b1 - (((x2-x1)*(x2-x1))*(b1*b1)/(a1*a1)))));
+ else
+ *y4 = (float)(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
+
+ float A, B, C, D, E, F, G, H, K;
+ float ellipse1_x, ellipse1_y;
+
+ A = (float)(1/(a1 * a1));
+ B = (float)((y3 - y2) * (y3 - y2)) / ((x3 - x2) * (x3 - x2) * b1 * b1);
+ C = (float)(2 * (y3 - y2) * (y2 - y1)) / ((x3 - x2) * b1 * b1);
+ D = (float)((y2 - y1) * (y2 - y1)) / (b1 * b1);
+ E = (float)(A + B);
+ F = (float)(C - (2 * A * x1) - (2 * B * x2));
+ G = (float)((A * x1 * x1) + (B * x2 * x2) - (C * x2) + D - 1);
+ H = (float)((y3 - y2) / (x3 - x2));
+ K = (float)((F * F) - (4 * E * G));
+
+ if (K >= 0)
+ // In this case the line intersects the ellipse, so calculate intersection
+ {
+ if(x2 >= x1)
+ {
+ ellipse1_x = (float)(((F * -1) + sqrt(K)) / (2 * E));
+ ellipse1_y = (float)((H * (ellipse1_x - x2)) + y2);
+ }
+ else
+ {
+ ellipse1_x = (float)(((F * -1) - sqrt(K)) / (2 * E));
+ ellipse1_y = (float)((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 .
+
+ float circle_x = ellipse1_x - 2.0;
+ float 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();
+ }
+}
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 wxObjectCopyMapping;
+
+/*
+ * 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 CentreText(wxDC& dc, wxList *text, float m_xpos, float m_ypos,
+ float width, float 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.
+wxList *FormatText(wxDC& dc, const wxString& text, float width, float height, int formatMode = 0);
+
+// Centres the list of wxShapeTextLine strings, doesn't clip.
+// Doesn't actually draw into the DC.
+void CentreTextNoClipping(wxDC& dc, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height);
+
+// Gets the maximum width and height of the given list of wxShapeTextLines.
+void GetCentredTextExtent(wxDC& dc, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height,
+ float *actual_width, float *actual_height);
+
+// Actually draw the preformatted list of wxShapeTextLines.
+void DrawFormattedText(wxDC& context, wxList *text_list,
+ float m_xpos, float m_ypos, float width, float height,
+ int formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT);
+
+// Give it a list of points, finds the centre.
+void find_polyline_centroid(wxList *points, float *x, float *y);
+
+void check_line_intersection(float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4,
+ float *ratio1, float *ratio2);
+
+void find_end_for_polyline(float n, float xvec[], float yvec[],
+ float x1, float y1, float x2, float y2, float *x3, float *y3);
+
+
+void find_end_for_box(float width, float height,
+ float x1, float y1, // Centre of box (possibly)
+ float x2, float y2, // other end of line
+ float *x3, float *y3); // End on box edge
+
+void find_end_for_circle(float radius,
+ float x1, float y1, // Centre of circle
+ float x2, float y2, // Other end of line
+ float *x3, float *y3);
+
+void get_arrow_points(float x1, float y1, float x2, float y2,
+ float length, float width,
+ float *tip_x, float *tip_y,
+ float *side1_x, float *side1_y,
+ float *side2_x, float *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 draw_arc_to_ellipse(float x1, float y1, float a1, float b1, float x2, float y2, float x3, float y3,
+ float *x4, float *y4);
+
+extern wxFont *g_oglNormalFont;
+extern wxPen *black_pen;
+
+extern wxPen *white_background_pen;
+extern wxPen *transparent_pen;
+extern wxBrush *white_background_brush;
+extern wxPen *black_foreground_pen;
+
+extern wxCursor *GraphicsBullseyeCursor;
+
+extern wxFont *MatchFont(int point_size);
+
+#endif
+ // _OGL_MISC_H_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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 "basic.h" // Basic shapes
+#include "lines.h" // Lines and splines
+#include "divided.h" // Vertically-divided rectangle
+#include "composit.h" // Composite images
+#include "canvas.h" // wxShapeCanvas for displaying objects
+#include "ogldiag.h" // wxDiagram
+
+extern void wxOGLInitialize();
+extern void wxOGLCleanUp();
+
+#endif
+ // _OGL_OGL_H_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+#ifdef PROLOGIO
+#include <wx/wxexpr.h>
+#endif
+
+#if USE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <fstream.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "canvas.h"
+#include "ogldiag.h"
+#include "lines.h"
+#include "composit.h"
+#include "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(float spacing)
+{
+ m_gridSpacing = spacing;
+}
+
+void wxDiagram::Snap(float *x, float *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, float x1, float y1, float x2, float y2)
+{
+ wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
+ dc.SetPen(dottedPen);
+ dc.SetBrush((* wxTRANSPARENT_BRUSH));
+
+ wxPoint points[5];
+
+ points[0].x = x1;
+ points[0].y = y1;
+
+ points[1].x = x2;
+ points[1].y = y1;
+
+ points[2].x = x2;
+ points[2].y = y2;
+
+ points[3].x = x1;
+ points[3].y = y2;
+
+ points[4].x = x1;
+ points[4].y = 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);
+ ofstream stream(tempFile);
+ if (stream.bad())
+ {
+ wxEndBusyCursor();
+ delete database;
+ return FALSE;
+ }
+
+ database->Write(stream);
+ stream.close();
+ 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(PrologInteger, "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);
+ RegisterId(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);
+
+ 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.WritePrologAttributes(&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.ReadPrologAttributes(&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;
+}
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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, float x1, float y1, float x2, float 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(float spacing);
+ inline float GetGridSpacing() { return m_gridSpacing; }
+ inline bool GetSnapToGrid() const { return m_snapToGrid; }
+ void Snap(float *x, float *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);
+
+ inline void SetMouseTolerance(int tol) { m_mouseTolerance = tol; }
+ inline int GetMouseTolerance() const { return m_mouseTolerance; }
+ inline wxList *GetShapeList() const { return m_shapeList; }
+
+ // Make sure all text that should be centred, is centred.
+ void RecentreAll(wxDC& dc);
+
+#ifdef PROLOGIO
+ // Prolog database stuff
+ 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;
+ float m_gridSpacing;
+ int m_mouseTolerance;
+ wxList* m_shapeList;
+};
+
+#endif
+ // _OGL_OGLDIAG_H_