text: //////////////////////////////////////////////////////////////////////////
  //
  // pgAdmin III - PostgreSQL Tools
  //
  // Portions Copyright (C) 1998 - 2011, Julian Smart
  // Portions Copyright (C) 2011 - 2016, The pgAdmin Development Team
  // This software is released under the PostgreSQL Licence
  //
  // composit.cpp - Composite OGL class
  //
  //////////////////////////////////////////////////////////////////////////
  
  #include "pgAdmin3.h"
  
  #include "ogl/ogl.h"
  
  #if wxUSE_PROLOGIO
  // Sometimes, objects need to access the whole database to
  // construct themselves.
  wxExprDatabase *GlobalwxExprDatabase = NULL;
  #endif
  
  /*
   * Division control point
   */
  
  class wxDivisionControlPoint: public wxControlPoint
  {
  	DECLARE_DYNAMIC_CLASS(wxDivisionControlPoint)
  public:
  	wxDivisionControlPoint() {}
  	wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type);
  	~wxDivisionControlPoint();
  
  	void OnDragLeft(bool draw, double x, double y, int keys = 0, int attachment = 0);
  	void OnBeginDragLeft(double x, double y, int keys = 0, int attachment = 0);
  	void OnEndDragLeft(double x, double y, int keys = 0, int attachment = 0);
  };
  
  IMPLEMENT_DYNAMIC_CLASS(wxDivisionControlPoint, wxControlPoint)
  
  /*
   * Composite object
   *
   */
  
  IMPLEMENT_DYNAMIC_CLASS(wxCompositeShape, wxRectangleShape)
  
  wxCompositeShape::wxCompositeShape(): wxRectangleShape(10.0, 10.0)
  {
  //  selectable = FALSE;
  	m_oldX = m_xpos;
  	m_oldY = m_ypos;
  }
  
  wxCompositeShape::~wxCompositeShape()
  {
  	wxNode *node = m_constraints.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  		delete constraint;
  		node = node->GetNext();
  	}
  	node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  		wxNode *next = node->GetNext();
  		object->Unlink();
  		delete object;
  		node = next;
  	}
  }
  
  void wxCompositeShape::OnDraw(wxDC &dc)
  {
  	double x1 = (double)(m_xpos - m_width / 2.0);
  	double y1 = (double)(m_ypos - m_height / 2.0);
  
  	if (m_shadowMode != SHADOW_NONE)
  	{
  		if (m_shadowBrush)
  			dc.SetBrush(* m_shadowBrush);
  		dc.SetPen(* g_oglTransparentPen);
  
  		if (m_cornerRadius != 0.0)
  			dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
  			                        WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
  		else
  			dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
  	}
  }
  
  void wxCompositeShape::OnDrawContents(wxDC &dc)
  {
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  		object->Draw(dc);
  		object->DrawLinks(dc);
  		node = node->GetNext();
  	}
  	wxShape::OnDrawContents(dc);
  }
  
  bool wxCompositeShape::OnMovePre(double x, double y, double oldx, double oldy, bool display)
  {
  	double diffX = x - oldx;
  	double diffY = y - oldy;
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  
  		object->Move(object->GetX() + diffX, object->GetY() + diffY, display);
  
  		node = node->GetNext();
  	}
  	return TRUE;
  }
  
  static double objectStartX = 0.0;
  static double objectStartY = 0.0;
  
  void wxCompositeShape::OnDragLeft(bool WXUNUSED(draw), double x, double y, int WXUNUSED(keys), int WXUNUSED(attachment))
  {
  	double xx = x;
  	double yy = y;
  	m_canvas->Snap(&xx, &yy);
  	double offsetX = xx - objectStartX;
  	double offsetY = yy - objectStartY;
  
  	wxClientDC dc(GetCanvas());
  	GetCanvas()->PrepareDC(dc);
  
  	dc.SetLogicalFunction(OGLRBLF);
  #if wxCHECK_VERSION(3, 0, 0)
  	wxPen dottedPen(wxColour(0, 0, 0), 1, wxPENSTYLE_DOT);
  #else
  	wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
  #endif
  	dc.SetPen(dottedPen);
  	dc.SetBrush((* wxTRANSPARENT_BRUSH));
  
  	GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
  //  wxShape::OnDragLeft(draw, x, y, keys, attachment);
  }
  
  void wxCompositeShape::OnBeginDragLeft(double x, double y, int WXUNUSED(keys), int WXUNUSED(attachment))
  {
  	objectStartX = x;
  	objectStartY = y;
  
  	wxClientDC dc(GetCanvas());
  	GetCanvas()->PrepareDC(dc);
  
  	Erase();
  
  	dc.SetLogicalFunction(OGLRBLF);
  
  #if wxCHECK_VERSION(3, 0, 0)
  	wxPen dottedPen(wxColour(0, 0, 0), 1, wxPENSTYLE_DOT);
  #else
  	wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
  #endif
  	dc.SetPen(dottedPen);
  	dc.SetBrush((* wxTRANSPARENT_BRUSH));
  	m_canvas->CaptureMouse();
  
  	double xx = x;
  	double yy = y;
  	m_canvas->Snap(&xx, &yy);
  	double offsetX = xx - objectStartX;
  	double offsetY = yy - objectStartY;
  
  	GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
  
  //  wxShape::OnBeginDragLeft(x, y, keys, attachment);
  }
  
  void wxCompositeShape::OnEndDragLeft(double x, double y, int keys, int WXUNUSED(attachment))
  {
  //  wxShape::OnEndDragLeft(x, y, keys, attachment);
  
  	wxClientDC dc(GetCanvas());
  	GetCanvas()->PrepareDC(dc);
  
  	m_canvas->ReleaseMouse();
  
  	if (!m_draggable)
  	{
  		if (m_parent) m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, 0);
  		return;
  	}
  
  	dc.SetLogicalFunction(wxCOPY);
  	double xx = x;
  	double yy = y;
  	m_canvas->Snap(&xx, &yy);
  	double offsetX = xx - objectStartX;
  	double offsetY = yy - objectStartY;
  
  	Move(GetX() + offsetX, GetY() + offsetY);
  
  	if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
  }
  
  void wxCompositeShape::OnRightClick(double x, double y, int keys, int WXUNUSED(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.GetFirst();
  		while (node)
  		{
  			wxDivisionShape *division = (wxDivisionShape *)node->GetData();
  			wxNode *next = node->GetNext();
  			int attach = 0;
  			double dist = 0.0;
  			if (division->HitTest(x, y, &attach, &dist))
  			{
  				division->GetEventHandler()->OnRightClick(x, y, keys, attach);
  				node = NULL;
  			}
  			if (node)
  				node = next;
  		}
  	}
  }
  
  void wxCompositeShape::SetSize(double w, double h, bool recursive)
  {
  	SetAttachmentSize(w, h);
  
  	double xScale = (double)(w / (wxMax(1.0, GetWidth())));
  	double yScale = (double)(h / (wxMax(1.0, GetHeight())));
  
  	m_width = w;
  	m_height = h;
  
  	if (!recursive) return;
  
  	wxNode *node = m_children.GetFirst();
  
  	wxClientDC dc(GetCanvas());
  	GetCanvas()->PrepareDC(dc);
  
  	double xBound, yBound;
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  
  		// Scale the position first
  		double newX = (double)(((object->GetX() - GetX()) * xScale) + GetX());
  		double newY = (double)(((object->GetY() - GetY()) * yScale) + GetY());
  		object->Show(FALSE);
  		object->Move(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->GetNext();
  	}
  	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.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  		wxNode *nextNode = node->GetNext();
  
  		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.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  		wxNode *nextNode = node->GetNext();
  
  		if (constraint->m_constrainedObjects.Member(child))
  			constraint->m_constrainedObjects.DeleteObject(child);
  		if (constraint->m_constrainingObject == child)
  			constraint->m_constrainingObject = NULL;
  
  		// Delete the constraint if no participants left
  		if (!constraint->m_constrainingObject)
  		{
  			delete constraint;
  			delete node;
  		}
  
  		node = nextNode;
  	}
  }
  
  void wxCompositeShape::Copy(wxShape &copy)
  {
  	wxRectangleShape::Copy(copy);
  
  	wxASSERT( copy.IsKindOf(CLASSINFO(wxCompositeShape)) ) ;
  
  	wxCompositeShape &compositeCopy = (wxCompositeShape &) copy;
  
  	// Associate old and new copies for compositeCopying constraints and division geometry
  	oglObjectCopyMapping.Append((long)this, &compositeCopy);
  
  	// Copy the children
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  		wxShape *newObject = object->CreateNewCopy(FALSE, FALSE);
  		if (newObject->GetId() == 0)
  			newObject->SetId(wxNewId());
  
  		newObject->SetParent(&compositeCopy);
  		compositeCopy.m_children.Append(newObject);
  
  		// Some m_children may be divisions
  		if (m_divisions.Member(object))
  			compositeCopy.m_divisions.Append(newObject);
  
  		oglObjectCopyMapping.Append((long)object, newObject);
  
  		node = node->GetNext();
  	}
  
  	// Copy the constraints
  	node = m_constraints.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  
  		wxShape *newConstraining = (wxShape *)(oglObjectCopyMapping.Find((long)constraint->m_constrainingObject)->GetData());
  
  		wxList newConstrainedList;
  		wxNode *node2 = constraint->m_constrainedObjects.GetFirst();
  		while (node2)
  		{
  			wxShape *constrainedObject = (wxShape *)node2->GetData();
  			wxShape *newConstrained = (wxShape *)(oglObjectCopyMapping.Find((long)constrainedObject)->GetData());
  			newConstrainedList.Append(newConstrained);
  			node2 = node2->GetNext();
  		}
  
  		wxOGLConstraint *newConstraint = new wxOGLConstraint(constraint->m_constraintType, newConstraining,
  		        newConstrainedList);
  		newConstraint->m_constraintId = constraint->m_constraintId;
  		if (!constraint->m_constraintName.IsEmpty())
  		{
  			newConstraint->m_constraintName = constraint->m_constraintName;
  		}
  		newConstraint->SetSpacing(constraint->m_xSpacing, constraint->m_ySpacing);
  		compositeCopy.m_constraints.Append(newConstraint);
  
  		node = node->GetNext();
  	}
  
  	// Now compositeCopy the division geometry
  	node = m_divisions.GetFirst();
  	while (node)
  	{
  		wxDivisionShape *division = (wxDivisionShape *)node->GetData();
  		wxNode *node1 = oglObjectCopyMapping.Find((long)division);
  		wxNode *leftNode = NULL;
  		wxNode *topNode = NULL;
  		wxNode *rightNode = NULL;
  		wxNode *bottomNode = NULL;
  		if (division->GetLeftSide())
  			leftNode = oglObjectCopyMapping.Find((long)division->GetLeftSide());
  		if (division->GetTopSide())
  			topNode = oglObjectCopyMapping.Find((long)division->GetTopSide());
  		if (division->GetRightSide())
  			rightNode = oglObjectCopyMapping.Find((long)division->GetRightSide());
  		if (division->GetBottomSide())
  			bottomNode = oglObjectCopyMapping.Find((long)division->GetBottomSide());
  		if (node1)
  		{
  			wxDivisionShape *newDivision = (wxDivisionShape *)node1->GetData();
  			if (leftNode)
  				newDivision->SetLeftSide((wxDivisionShape *)leftNode->GetData());
  			if (topNode)
  				newDivision->SetTopSide((wxDivisionShape *)topNode->GetData());
  			if (rightNode)
  				newDivision->SetRightSide((wxDivisionShape *)rightNode->GetData());
  			if (bottomNode)
  				newDivision->SetBottomSide((wxDivisionShape *)bottomNode->GetData());
  		}
  		node = node->GetNext();
  	}
  }
  
  wxOGLConstraint *wxCompositeShape::AddConstraint(wxOGLConstraint *constraint)
  {
  	m_constraints.Append(constraint);
  	if (constraint->m_constraintId == 0)
  		constraint->m_constraintId = wxNewId();
  	return constraint;
  }
  
  wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxList &constrained)
  {
  	wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, constrained);
  	if (constraint->m_constraintId == 0)
  		constraint->m_constraintId = wxNewId();
  	m_constraints.Append(constraint);
  	return constraint;
  }
  
  wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxShape *constrained)
  {
  	wxList l;
  	l.Append(constrained);
  	wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, l);
  	if (constraint->m_constraintId == 0)
  		constraint->m_constraintId = wxNewId();
  	m_constraints.Append(constraint);
  	return constraint;
  }
  
  wxOGLConstraint *wxCompositeShape::FindConstraint(long cId, wxCompositeShape **actualComposite)
  {
  	wxNode *node = m_constraints.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  		if (constraint->m_constraintId == cId)
  		{
  			if (actualComposite)
  				*actualComposite = this;
  			return constraint;
  		}
  		node = node->GetNext();
  	}
  	// If not found, try children.
  	node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *child = (wxShape *)node->GetData();
  		if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
  		{
  			wxOGLConstraint *constraint = ((wxCompositeShape *)child)->FindConstraint(cId, actualComposite);
  			if (constraint)
  			{
  				if (actualComposite)
  					*actualComposite = (wxCompositeShape *)child;
  				return constraint;
  			}
  		}
  		node = node->GetNext();
  	}
  	return NULL;
  }
  
  void wxCompositeShape::DeleteConstraint(wxOGLConstraint *constraint)
  {
  	m_constraints.DeleteObject(constraint);
  	delete constraint;
  }
  
  void wxCompositeShape::CalculateSize()
  {
  	double maxX = (double) - 999999.9;
  	double maxY = (double) - 999999.9;
  	double minX = (double)  999999.9;
  	double minY = (double)  999999.9;
  
  	double w, h;
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  
  		// Recalculate size of composite objects because may not conform
  		// to size it was set to - depends on the children.
  		object->CalculateSize();
  
  		object->GetBoundingBoxMax(&w, &h);
  		if ((object->GetX() + (w / 2.0)) > maxX)
  			maxX = (double)(object->GetX() + (w / 2.0));
  		if ((object->GetX() - (w / 2.0)) < minX)
  			minX = (double)(object->GetX() - (w / 2.0));
  		if ((object->GetY() + (h / 2.0)) > maxY)
  			maxY = (double)(object->GetY() + (h / 2.0));
  		if ((object->GetY() - (h / 2.0)) < minY)
  			minY = (double)(object->GetY() - (h / 2.0));
  
  		node = node->GetNext();
  	}
  	m_width = maxX - minX;
  	m_height = maxY - minY;
  	m_xpos = (double)(m_width / 2.0 + minX);
  	m_ypos = (double)(m_height / 2.0 + minY);
  }
  
  bool wxCompositeShape::Recompute()
  {
  	int noIterations = 0;
  	bool changed = TRUE;
  	while (changed && (noIterations < 500))
  	{
  		changed = Constrain();
  		noIterations ++;
  	}
  	/*
  	#ifdef wx_x
  	  if (changed)
  	    cerr << "Warning: constraint algorithm failed after 500 iterations.\n";
  	#endif
  	*/
  	return (!changed);
  }
  
  bool wxCompositeShape::Constrain()
  {
  	CalculateSize();
  
  	bool changed = FALSE;
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  		if (object->Constrain())
  			changed = TRUE;
  		node = node->GetNext();
  	}
  
  	node = m_constraints.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  		if (constraint->Evaluate()) changed = TRUE;
  		node = node->GetNext();
  	}
  	return changed;
  }
  
  #if wxUSE_PROLOGIO
  void wxCompositeShape::WriteAttributes(wxExpr *clause)
  {
  	wxRectangleShape::WriteAttributes(clause);
  
  //  clause->AddAttributeValue("selectable", (long)selectable);
  
  	// Output constraints as constraint1 = (...), constraint2 = (...), etc.
  	int constraintNo = 1;
  	wxChar m_constraintNameBuf[20];
  	wxNode *node = m_constraints.GetFirst();
  	while (node)
  	{
  		wxOGLConstraint *constraint = (wxOGLConstraint *)node->GetData();
  		wxSprintf(m_constraintNameBuf, wxT("constraint%d"), constraintNo);
  
  		// Each constraint is stored in the form
  		// (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
  		wxExpr *constraintExpr = new wxExpr(wxExprList);
  		constraintExpr->Append(new wxExpr((long)constraint->m_constraintType));
  		constraintExpr->Append(new wxExpr(wxExprString, constraint->m_constraintName));
  		constraintExpr->Append(new wxExpr(constraint->m_constraintId));
  		constraintExpr->Append(new wxExpr(constraint->m_xSpacing));
  		constraintExpr->Append(new wxExpr(constraint->m_ySpacing));
  		constraintExpr->Append(new wxExpr(constraint->m_constrainingObject->GetId()));
  
  		wxExpr *objectList = new wxExpr(wxExprList);
  		wxNode *node1 = constraint->m_constrainedObjects.GetFirst();
  		while (node1)
  		{
  			wxShape *obj = (wxShape *)node1->GetData();
  			objectList->Append(new wxExpr(obj->GetId()));
  			node1 = node1->GetNext();
  		}
  		constraintExpr->Append(objectList);
  
  		clause->AddAttributeValue(m_constraintNameBuf, constraintExpr);
  
  		node = node->GetNext();
  		constraintNo ++;
  	}
  
  	// Write the ids of all the child images
  	wxExpr *childrenExpr = new wxExpr(wxExprList);
  	node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *child = (wxShape *)node->GetData();
  		childrenExpr->Append(new wxExpr(child->GetId()));
  		node = node->GetNext();
  	}
  	clause->AddAttributeValue(wxT("children"), childrenExpr);
  
  	// Write the ids of all the division images
  	if (m_divisions.GetCount() > 0)
  	{
  		wxExpr *divisionsExpr = new wxExpr(wxExprList);
  		node = m_divisions.GetFirst();
  		while (node)
  		{
  			wxShape *child = (wxShape *)node->GetData();
  			divisionsExpr->Append(new wxExpr(child->GetId()));
  			node = node->GetNext();
  		}
  		clause->AddAttributeValue(wxT("divisions"), divisionsExpr);
  	}
  }
  
  // Problem. Child images are always written AFTER the parent
  // so as to be able to link up to parent. So we may not be able
  // to find the constraint participants until we've read everything
  // in. Need to have another pass for composites.
  void wxCompositeShape::ReadAttributes(wxExpr *clause)
  {
  	wxRectangleShape::ReadAttributes(clause);
  
  //  clause->GetAttributeValue("selectable", selectable);
  }
  
  void wxCompositeShape::ReadConstraints(wxExpr *clause, wxExprDatabase *database)
  {
  	// Constraints are output as constraint1 = (...), constraint2 = (...), etc.
  	int constraintNo = 1;
  	wxChar m_constraintNameBuf[20];
  	bool haveConstraints = TRUE;
  
  	while (haveConstraints)
  	{
  		wxSprintf(m_constraintNameBuf, wxT("constraint%d"), constraintNo);
  		wxExpr *constraintExpr = NULL;
  		clause->GetAttributeValue(m_constraintNameBuf, &constraintExpr);
  		if (!constraintExpr)
  		{
  			haveConstraints = FALSE;
  			break;
  		}
  		wxString cName = wxEmptyString;
  		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);
  
  		int cType = (int)typeExpr->IntegerValue();
  		double cXSpacing = xExpr->RealValue();
  		double cYSpacing = yExpr->RealValue();
  		cName = nameExpr->StringValue();
  		long cId = idExpr->IntegerValue();
  
  		wxExpr *objExpr1 = database->HashFind(wxT("node_image"), constrainingExpr->IntegerValue());
  		if (objExpr1 && objExpr1->GetClientData())
  			m_constrainingObject = (wxShape *)objExpr1->GetClientData();
  		else
  			wxLogFatalError(wxT("Object graphics error: Couldn't find constraining image of composite."));
  
  		int i = 0;
  		wxExpr *currentIdExpr = constrainedExpr->Nth(i);
  		while (currentIdExpr)
  		{
  			long currentId = currentIdExpr->IntegerValue();
  			wxExpr *objExpr2 = database->HashFind(wxT("node_image"), currentId);
  			if (objExpr2 && objExpr2->GetClientData())
  			{
  				m_constrainedObjects.Append((wxShape *)objExpr2->GetClientData());
  			}
  			else
  			{
  				wxLogFatalError(wxT("Object graphics error: Couldn't find constrained image of composite."));
  			}
  
  			i ++;
  			currentIdExpr = constrainedExpr->Nth(i);
  		}
  		wxOGLConstraint *newConstraint = AddConstraint(cType, m_constrainingObject, m_constrainedObjects);
  		newConstraint->SetSpacing(cXSpacing, cYSpacing);
  		newConstraint->m_constraintId = cId;
  		newConstraint->m_constraintName = 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(GetX(), GetY());
  	Recompute();
  	division->Show(TRUE);
  }
  
  wxDivisionShape *wxCompositeShape::OnCreateDivision()
  {
  	return new wxDivisionShape;
  }
  
  wxShape *wxCompositeShape::FindContainerImage()
  {
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *child = (wxShape *)node->GetData();
  		if (!m_divisions.Member(child))
  			return child;
  		node = node->GetNext();
  	}
  	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.GetFirst();
  	while (node)
  	{
  		wxShape *child = (wxShape *)node->GetData();
  		if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
  		{
  			bool ans = ((wxCompositeShape *)child)->ContainsDivision(division);
  			if (ans)
  				return TRUE;
  		}
  		node = node->GetNext();
  	}
  	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 = (wxPen *) wxBLACK_PEN;
  	m_topSidePen = (wxPen *) wxBLACK_PEN;
  	m_leftSideColour = wxT("BLACK");
  	m_topSideColour = wxT("BLACK");
  	m_leftSideStyle = wxT("Solid");
  	m_topSideStyle = wxT("Solid");
  	ClearRegions();
  }
  
  wxDivisionShape::~wxDivisionShape()
  {
  }
  
  void wxDivisionShape::OnDraw(wxDC &dc)
  {
  	dc.SetBrush(* wxTRANSPARENT_BRUSH);
  	dc.SetBackgroundMode(wxTRANSPARENT);
  
  	double x1 = (double)(GetX() - (GetWidth() / 2.0));
  	double y1 = (double)(GetY() - (GetHeight() / 2.0));
  	double x2 = (double)(GetX() + (GetWidth() / 2.0));
  	double y2 = (double)(GetY() + (GetHeight() / 2.0));
  
  	// Should subtract 1 pixel if drawing under Windows
  #ifdef __WXMSW__
  	y2 -= (double)1.0;
  #endif
  
  	if (m_leftSide)
  	{
  		dc.SetPen(* m_leftSidePen);
  		dc.DrawLine(WXROUND(x1), WXROUND(y2), WXROUND(x1), WXROUND(y1));
  	}
  	if (m_topSide)
  	{
  		dc.SetPen(* m_topSidePen);
  		dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y1));
  	}
  
  	// For testing purposes, draw a rectangle so we know
  	// how big the division is.
  //    SetBrush(* wxCYAN_BRUSH);
  //    wxRectangleShape::OnDraw(dc);
  }
  
  void wxDivisionShape::OnDrawContents(wxDC &dc)
  {
  	wxCompositeShape::OnDrawContents(dc);
  }
  
  bool wxDivisionShape::OnMovePre(double x, double y, double oldx, double oldy, bool display)
  {
  	double diffX = x - oldx;
  	double diffY = y - oldy;
  	wxNode *node = m_children.GetFirst();
  	while (node)
  	{
  		wxShape *object = (wxShape *)node->GetData();
  		object->Erase();
  		object->Move(object->GetX() + diffX, object->GetY() + diffY, display);
  		node = node->GetNext();
  	}
  	return TRUE;
  }
  
  void wxDivisionShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
  {
  	if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
  	{
  		attachment = 0;
  		double dist;
  		if (m_parent)
  		{
  			m_parent->HitTest(x, y, &attachment, &dist);
  			m_parent->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
  		}
  		return;
  	}
  	wxShape::OnDragLeft(draw, x, y, keys, attachment);
  }
  
  void wxDivisionShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
  {
  	if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
  	{
  		attachment = 0;
  		double dist;
  		if (m_parent)
  		{
  			m_parent->HitTest(x, y, &attachment, &dist);
  			m_parent->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
  		}
  		return;
  	}
  
  	wxShape::OnBeginDragLeft(x, y, keys, attachment);
  }
  
  void wxDivisionShape::OnEndDragLeft(double x, double y, int keys, int attachment)
  {
  	m_canvas->ReleaseMouse();
  	if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
  	{
  		attachment = 0;
  		double dist;
  		if (m_parent)
  		{
  			m_parent->HitTest(x, y, &attachment, &dist);
  			m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
  		}
  		return;
  	}
  
  #if 0
  	wxClientDC dc(GetCanvas());
  	GetCanvas()->PrepareDC(dc);
  
  	dc.SetLogicalFunction(wxCOPY);
  #endif
  
  	m_canvas->Snap(&m_xpos, &m_ypos);
  	GetEventHandler()->OnMovePre(x, y, m_oldX, m_oldY);
  
  	ResetControlPoints();
  	MoveLinks();
  
  	if (m_canvas)
  		m_canvas->Refresh();
  }
  
  void wxDivisionShape::SetSize(double w, double h, bool recursive)
  {
  	m_width = w;
  	m_height = h;
  	wxRectangleShape::SetSize(w, h, recursive);
  }
  
  void wxDivisionShape::CalculateSize()
  {
  }
  
  void wxDivisionShape::Copy(wxShape &copy)
  {
  	wxCompositeShape::Copy(copy);
  
  	wxASSERT( copy.IsKindOf(CLASSINFO(wxDivisionShape)) ) ;
  
  	wxDivisionShape &divisionCopy = (wxDivisionShape &) copy;
  
  	divisionCopy.m_leftSideStyle = m_leftSideStyle;
  	divisionCopy.m_topSideStyle = m_topSideStyle;
  	divisionCopy.m_leftSideColour = m_leftSideColour;
  	divisionCopy.m_topSideColour = m_topSideColour;
  
  	divisionCopy.m_leftSidePen = m_leftSidePen;
  	divisionCopy.m_topSidePen = m_topSidePen;
  	divisionCopy.m_handleSide = m_handleSide;
  
  	// Division geometry copying is handled at the wxCompositeShape level.
  }
  
  #if wxUSE_PROLOGIO
  void wxDivisionShape::WriteAttributes(wxExpr *clause)
  {
  	wxCompositeShape::WriteAttributes(clause);
  
  	if (m_leftSide)
  		clause->AddAttributeValue(wxT("left_side"), (long)m_leftSide->GetId());
  	if (m_topSide)
  		clause->AddAttributeValue(wxT("top_side"), (long)m_topSide->GetId());
  	if (m_rightSide)
  		clause->AddAttributeValue(wxT("right_side"), (long)m_rightSide->GetId());
  	if (m_bottomSide)
  		clause->AddAttributeValue(wxT("bottom_side"), (long)m_bottomSide->GetId());
  
  	clause->AddAttributeValue(wxT("handle_side"), (long)m_handleSide);
  	clause->AddAttributeValueString(wxT("left_colour"), m_leftSideColour);
  	clause->AddAttributeValueString(wxT("top_colour"), m_topSideColour);
  	clause->AddAttributeValueString(wxT("left_style"), m_leftSideStyle);
  	clause->AddAttributeValueString(wxT("top_style"), m_topSideStyle);
  }
  
  void wxDivisionShape::ReadAttributes(wxExpr *clause)
  {
  	wxCompositeShape::ReadAttributes(clause);
  
  	clause->GetAttributeValue(wxT("handle_side"), m_handleSide);
  	clause->GetAttributeValue(wxT("left_colour"), m_leftSideColour);
  	clause->GetAttributeValue(wxT("top_colour"), m_topSideColour);
  	clause->GetAttributeValue(wxT("left_style"), m_leftSideStyle);
  	clause->GetAttributeValue(wxT("top_style"), m_topSideStyle);
  }
  #endif
  
  // Experimental
  void wxDivisionShape::OnRightClick(double x, double y, int keys, int attachment)
  {
  	if (keys & KEY_CTRL)
  	{
  		PopupMenu(x, y);
  	}
  	/*
  	  else if (keys & KEY_SHIFT)
  	  {
  	    if (m_leftSide || m_topSide || m_rightSide || m_bottomSide)
  	    {
  	      if (Selected())
  	      {
  	        Select(FALSE);
  	        GetParent()->Draw(dc);
  	      }
  	      else
  	        Select(TRUE);
  	    }
  	  }
  	*/
  	else
  	{
  		attachment = 0;
  		double dist;
  		if (m_parent)
  		{
  			m_parent->HitTest(x, y, &attachment, &dist);
  			m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
  		}
  		return;
  	}
  }
  
  
  // Divide wxHORIZONTALly or wxVERTICALly
  bool wxDivisionShape::Divide(int direction)
  {
  	// Calculate existing top-left, bottom-right
  	double x1 = (double)(GetX() - (GetWidth() / 2.0));
  	double y1 = (double)(GetY() - (GetHeight() / 2.0));
  	wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
  	double oldWidth = GetWidth();
  	double oldHeight = GetHeight();
  	if (Selected())
  		Select(FALSE);
  
  	//wxClientDC dc(GetCanvas());
  	//GetCanvas()->PrepareDC(dc);
  
  	if (direction == wxVERTICAL)
  	{
  		// Dividing vertically means notionally putting a horizontal line through it.
  		// Break existing piece into two.
  		double newXPos1 = GetX();
  		double newYPos1 = (double)(y1 + (GetHeight() / 4.0));
  		double newXPos2 = GetX();
  		double newYPos2 = (double)(y1 + (3.0 * GetHeight() / 4.0));
  		wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
  		newDivision->Show(TRUE);
  
  		// Erase();
  
  		// Anything adjoining the bottom of this division now adjoins the
  		// bottom of the new division.
  		wxNode *node = compositeParent->GetDivisions().GetFirst();
  		while (node)
  		{
  			wxDivisionShape *obj = (wxDivisionShape *)node->GetData();
  			if (obj->GetTopSide() == this)
  				obj->SetTopSide(newDivision);
  			node = node->GetNext();
  		}
  		newDivision->SetTopSide(this);
  		newDivision->SetBottomSide(m_bottomSide);
  		newDivision->SetLeftSide(m_leftSide);
  		newDivision->SetRightSide(m_rightSide);
  		m_bottomSide = newDivision;
  
  		compositeParent->GetDivisions().Append(newDivision);
  
  		// CHANGE: Need to insert this division at start of divisions in the object
  		// list, because e.g.:
  		// 1) Add division
  		// 2) Add contained object
  		// 3) Add division
  		// Division is now receiving mouse events _before_ the contained object,
  		// because it was added last (on top of all others)
  
  		// Add after the image that visualizes the container
  		compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
  
  		m_handleSide = DIVISION_SIDE_BOTTOM;
  		newDivision->SetHandleSide(DIVISION_SIDE_TOP);
  
  		SetSize(oldWidth, (double)(oldHeight / 2.0));
  		Move(newXPos1, newYPos1);
  
  		newDivision->SetSize(oldWidth, (double)(oldHeight / 2.0));
  		newDivision->Move(newXPos2, newYPos2);
  	}
  	else
  	{
  		// Dividing horizontally means notionally putting a vertical line through it.
  		// Break existing piece into two.
  		double newXPos1 = (double)(x1 + (GetWidth() / 4.0));
  		double newYPos1 = GetY();
  		double newXPos2 = (double)(x1 + (3.0 * GetWidth() / 4.0));
  		double newYPos2 = GetY();
  		wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
  		newDivision->Show(TRUE);
  
  		// Erase(dc);
  
  		// Anything adjoining the left of this division now adjoins the
  		// left of the new division.
  		wxNode *node = compositeParent->GetDivisions().GetFirst();
  		while (node)
  		{
  			wxDivisionShape *obj = (wxDivisionShape *)node->GetData();
  			if (obj->GetLeftSide() == this)
  				obj->SetLeftSide(newDivision);
  			node = node->GetNext();
  		}
  		newDivision->SetTopSide(m_topSide);
  		newDivision->SetBottomSide(m_bottomSide);
  		newDivision->SetLeftSide(this);
  		newDivision->SetRightSide(m_rightSide);
  		m_rightSide = newDivision;
  
  		compositeParent->GetDivisions().Append(newDivision);
  		compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
  
  		m_handleSide = DIVISION_SIDE_RIGHT;
  		newDivision->SetHandleSide(DIVISION_SIDE_LEFT);
  
  		SetSize((double)(oldWidth / 2.0), oldHeight);
  		Move(newXPos1, newYPos1);
  
  		newDivision->SetSize((double)(oldWidth / 2.0), oldHeight);
  		newDivision->Move(newXPos2, newYPos2);
  	}
  	if (compositeParent->Selected())
  	{
  		compositeParent->DeleteControlPoints();
  		compositeParent->MakeControlPoints();
  		compositeParent->MakeMandatoryControlPoints();
  	}
  	// compositeParent->Draw(dc);
  
  	if (GetCanvas())
  		GetCanvas()->Refresh();
  	return TRUE;
  }
  
  // Make one control point for every visible line
  void wxDivisionShape::MakeControlPoints()
  {
  	MakeMandatoryControlPoints();
  }
  
  void wxDivisionShape::MakeMandatoryControlPoints()
  {
  	double maxX, maxY;
  
  	GetBoundingBoxMax(&maxX, &maxY);
  	double x = 0.0 , y = 0.0;
  	int direction = 0;
  	/*
  	  if (m_leftSide)
  	  {
  	    x = (double)(-maxX/2.0);
  	    y = 0.0;
  	    wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
  	                                             CONTROL_POINT_HORIZONTAL);
  	    m_canvas->AddShape(control);
  	    m_controlPoints.Append(control);
  	  }
  	  if (m_topSide)
  	  {
  	    x = 0.0;
  	    y = (double)(-maxY/2.0);
  	    wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
  	                                             CONTROL_POINT_VERTICAL);
  	    m_canvas->AddShape(control);
  	    m_controlPoints.Append(control);
  	  }
  	*/
  	switch (m_handleSide)
  	{
  		case DIVISION_SIDE_LEFT:
  		{
  			x = (double)(-maxX / 2.0);
  			y = 0.0;
  			direction = CONTROL_POINT_HORIZONTAL;
  			break;
  		}
  		case DIVISION_SIDE_TOP:
  		{
  			x = 0.0;
  			y = (double)(-maxY / 2.0);
  			direction = CONTROL_POINT_VERTICAL;
  			break;
  		}
  		case DIVISION_SIDE_RIGHT:
  		{
  			x = (double)(maxX / 2.0);
  			y = 0.0;
  			direction = CONTROL_POINT_HORIZONTAL;
  			break;
  		}
  		case DIVISION_SIDE_BOTTOM:
  		{
  			x = 0.0;
  			y = (double)(maxY / 2.0);
  			direction = CONTROL_POINT_VERTICAL;
  			break;
  		}
  		default:
  			break;
  	}
  	if (m_handleSide != DIVISION_SIDE_NONE)
  	{
  		wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
  		        direction);
  		m_canvas->AddShape(control);
  		m_controlPoints.Append(control);
  	}
  }
  
  void wxDivisionShape::ResetControlPoints()
  {
  	ResetMandatoryControlPoints();
  }
  
  void wxDivisionShape::ResetMandatoryControlPoints()
  {
  	if (m_controlPoints.GetCount() < 1)
  		return;
  
  	double maxX, maxY;
  
  	GetBoundingBoxMax(&maxX, &maxY);
  	/*
  	  wxNode *node = m_controlPoints.GetFirst();
  	  while (node)
  	  {
  	    wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->GetData();
  	    if (control->type == CONTROL_POINT_HORIZONTAL)
  	    {
  	      control->xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
  	    }
  	    else if (control->type == CONTROL_POINT_VERTICAL)
  	    {
  	      control->xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
  	    }
  	    node = node->GetNext();
  	  }
  	*/
  	wxNode *node = m_controlPoints.GetFirst();
  	if ((m_handleSide == DIVISION_SIDE_LEFT) && node)
  	{
  		wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->GetData();
  		control->m_xoffset = (double)(-maxX / 2.0);
  		control->m_yoffset = 0.0;
  	}
  
  	if ((m_handleSide == DIVISION_SIDE_TOP) && node)
  	{
  		wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->GetData();
  		control->m_xoffset = 0.0;
  		control->m_yoffset = (double)(-maxY / 2.0);
  	}
  
  	if ((m_handleSide == DIVISION_SIDE_RIGHT) && node)
  	{
  		wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->GetData();
  		control->m_xoffset = (double)(maxX / 2.0);
  		control->m_yoffset = 0.0;
  	}
  
  	if ((m_handleSide == DIVISION_SIDE_BOTTOM) && node)
  	{
  		wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->GetData();
  		control->m_xoffset = 0.0;
  		control->m_yoffset = (double)(maxY / 2.0);
  	}
  }
  
  // Adjust a side, returning FALSE if it's not physically possible.
  bool wxDivisionShape::AdjustLeft(double left, bool test)
  {
  	double x2 = (double)(GetX() + (GetWidth() / 2.0));
  
  	if (left >= x2)
  		return FALSE;
  	if (test)
  		return TRUE;
  
  	double newW = x2 - left;
  	double newX = (double)(left + newW / 2.0);
  	SetSize(newW, GetHeight());
  
  	Move(newX, GetY());
  
  	if (GetCanvas())
  		GetCanvas()->Refresh();
  
  	return TRUE;
  }
  
  bool wxDivisionShape::AdjustTop(double top, bool test)
  {
  	double y2 = (double)(GetY() + (GetHeight() / 2.0));
  
  	if (top >= y2)
  		return FALSE;
  	if (test)
  		return TRUE;
  
  	double newH = y2 - top;
  	double newY = (double)(top + newH / 2.0);
  	SetSize(GetWidth(), newH);
  
  	Move(GetX(), newY);
  
  	if (GetCanvas())
  		GetCanvas()->Refresh();
  
  	return TRUE;
  }
  
  bool wxDivisionShape::AdjustRight(double right, bool test)
  {
  	double x1 = (double)(GetX() - (GetWidth() / 2.0));
  
  	if (right <= x1)
  		return FALSE;
  	if (test)
  		return TRUE;
  
  	double newW = right - x1;
  	double newX = (double)(x1 + newW / 2.0);
  	SetSize(newW, GetHeight());
  
  	Move(newX, GetY());
  
  	if (GetCanvas())
  		GetCanvas()->Refresh();
  
  	return TRUE;
  }
  
  bool wxDivisionShape::AdjustBottom(double bottom, bool test)
  {
  	double y1 = (double)(GetY() - (GetHeight() / 2.0));
  
  	if (bottom <= y1)
  		return FALSE;
  	if (test)
  		return TRUE;
  
  	double newH = bottom - y1;
  	double newY = (double)(y1 + newH / 2.0);
  	SetSize(GetWidth(), newH);
  
  	Move(GetX(), newY);
  
  	if (GetCanvas())
  		GetCanvas()->Refresh();
  
  	return TRUE;
  }
  
  wxDivisionControlPoint::wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):
  	wxControlPoint(the_canvas, object, size, the_xoffset, the_yoffset, the_type)
  {
  	SetEraseObject(FALSE);
  }
  
  wxDivisionControlPoint::~wxDivisionControlPoint()
  {
  }
  
  static double originalX = 0.0;
  static double originalY = 0.0;
  static double originalW = 0.0;
  static double originalH = 0.0;
  
  // Implement resizing of canvas object
  void wxDivisionControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
  {
  	wxControlPoint::OnDragLeft(draw, x, y, keys, attachment);
  }
  
  void wxDivisionControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
  {
  	wxDivisionShape *division = (wxDivisionShape *)m_shape;
  	originalX = division->GetX();
  	originalY = division->GetY();
  	originalW = division->GetWidth();
  	originalH = division->GetHeight();
  
  	wxControlPoint::OnBeginDragLeft(x, y, keys, attachment);
  }
  
  void wxDivisionControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
  {
  	wxControlPoint::OnEndDragLeft(x, y, keys, attachment);
  
  	wxDivisionShape *division = (wxDivisionShape *)m_shape;
  	wxCompositeShape *divisionParent = (wxCompositeShape *)division->GetParent();
  
  	// Need to check it's within the bounds of the parent composite.
  	double x1 = (double)(divisionParent->GetX() - (divisionParent->GetWidth() / 2.0));
  	double y1 = (double)(divisionParent->GetY() - (divisionParent->GetHeight() / 2.0));
  	double x2 = (double)(divisionParent->GetX() + (divisionParent->GetWidth() / 2.0));
  	double y2 = (double)(divisionParent->GetY() + (divisionParent->GetHeight() / 2.0));
  
  	// Need to check it has not made the division zero or negative width/height
  	double dx1 = (double)(division->GetX() - (division->GetWidth() / 2.0));
  	double dy1 = (double)(division->GetY() - (division->GetHeight() / 2.0));
  	double dx2 = (double)(division->GetX() + (division->GetWidth() / 2.0));
  	double dy2 = (double)(division->GetY() + (division->GetHeight() / 2.0));
  
  	bool success = TRUE;
  	switch (division->GetHandleSide())
  	{
  		case DIVISION_SIDE_LEFT:
  		{
  			if ((x <= x1) || (x >= x2) || (x >= dx2))
  				success = FALSE;
  			// Try it out first...
  			else if (!division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, TRUE))
  				success = FALSE;
  			else
  				division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, FALSE);
  
  			break;
  		}
  		case DIVISION_SIDE_TOP:
  		{
  			if ((y <= y1) || (y >= y2) || (y >= dy2))
  				success = FALSE;
  			else if (!division->ResizeAdjoining(DIVISION_SIDE_TOP, y, TRUE))
  				success = FALSE;
  			else
  				division->ResizeAdjoining(DIVISION_SIDE_TOP, y, FALSE);
  
  			break;
  		}
  		case DIVISION_SIDE_RIGHT:
  		{
  			if ((x <= x1) || (x >= x2) || (x <= dx1))
  				success = FALSE;
  			else if (!division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, TRUE))
  				success = FALSE;
  			else
  				division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, FALSE);
  
  			break;
  		}
  		case DIVISION_SIDE_BOTTOM:
  		{
  			if ((y <= y1) || (y >= y2) || (y <= dy1))
  				success = FALSE;
  			else if (!division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, TRUE))
  				success = FALSE;
  			else
  				division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, FALSE);
  
  			break;
  		}
  	}
  	if (!success)
  	{
  		division->SetSize(originalW, originalH);
  		division->Move(originalX, originalY);
  	}
  
  	if (GetCanvas())
  		GetCanvas()->Refresh();
  
  }
  
  /* Resize adjoining divisions.
   *
     Behaviour should be as follows:
     If right edge moves, find all objects whose left edge
     adjoins this object, and move left edge accordingly.
     If left..., move ... right.
     If top..., move ... bottom.
     If bottom..., move top.
     If size goes to zero or end position is other side of start position,
     resize to original size and return.
   */
  bool wxDivisionShape::ResizeAdjoining(int side, double newPos, bool test)
  {
  	wxCompositeShape *divisionParent = (wxCompositeShape *)GetParent();
  	wxNode *node = divisionParent->GetDivisions().GetFirst();
  	while (node)
  	{
  		wxDivisionShape *division = (wxDivisionShape *)node->GetData();
  		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->GetNext();
  	}
  
  	return TRUE;
  }
  
  /*
   * Popup menu for editing divisions
   *
   */
  class OGLPopupDivisionMenu : public wxMenu
  {
  public:
  	OGLPopupDivisionMenu() : wxMenu()
  	{
  		Append(DIVISION_MENU_SPLIT_HORIZONTALLY, wxT("Split horizontally"));
  		Append(DIVISION_MENU_SPLIT_VERTICALLY, wxT("Split vertically"));
  		AppendSeparator();
  		Append(DIVISION_MENU_EDIT_LEFT_EDGE, wxT("Edit left edge"));
  		Append(DIVISION_MENU_EDIT_TOP_EDGE, wxT("Edit top edge"));
  	}
  
  	void OnMenu(wxCommandEvent &event);
  
  	DECLARE_EVENT_TABLE()
  };
  
  BEGIN_EVENT_TABLE(OGLPopupDivisionMenu, wxMenu)
  	EVT_MENU_RANGE(DIVISION_MENU_SPLIT_HORIZONTALLY,
  	               DIVISION_MENU_EDIT_BOTTOM_EDGE,
  	               OGLPopupDivisionMenu::OnMenu)
  END_EVENT_TABLE()
  
  
  void OGLPopupDivisionMenu::OnMenu(wxCommandEvent &event)
  {
  	wxDivisionShape *division = (wxDivisionShape *)GetClientData();
  	switch (event.GetInt())
  	{
  		case DIVISION_MENU_SPLIT_HORIZONTALLY:
  		{
  			division->Divide(wxHORIZONTAL);
  			break;
  		}
  		case DIVISION_MENU_SPLIT_VERTICALLY:
  		{
  			division->Divide(wxVERTICAL);
  			break;
  		}
  		case DIVISION_MENU_EDIT_LEFT_EDGE:
  		{
  			division->EditEdge(DIVISION_SIDE_LEFT);
  			break;
  		}
  		case DIVISION_MENU_EDIT_TOP_EDGE:
  		{
  			division->EditEdge(DIVISION_SIDE_TOP);
  			break;
  		}
  		default:
  			break;
  	}
  }
  
  void wxDivisionShape::EditEdge(int WXUNUSED(side))
  {
  	wxMessageBox(wxT("EditEdge() not implemented"), wxT("OGL"), wxOK);
  
  #if 0
  	wxBeginBusyCursor();
  
  	wxPen *currentPen = NULL;
  	char **pColour = NULL;
  	char **pStyle = NULL;
  	if (side == DIVISION_SIDE_LEFT)
  	{
  		currentPen = m_leftSidePen;
  		pColour = &m_leftSideColour;
  		pStyle = &m_leftSideStyle;
  	}
  	else
  	{
  		currentPen = m_topSidePen;
  		pColour = &m_topSideColour;
  		pStyle = &m_topSideStyle;
  	}
  
  	GraphicsForm *form = new GraphicsForm("Containers");
  	int lineWidth = currentPen->GetWidth();
  
  	form->Add(wxMakeFormShort("Width", &lineWidth, wxFORM_DEFAULT, NULL, NULL, wxVERTICAL,
  	                          150));
  	form->Add(wxMakeFormString("Colour", pColour, wxFORM_CHOICE,
  	                           new wxList(wxMakeConstraintStrings(
  	                                   "BLACK"            ,
  	                                   "BLUE"             ,
  	                                   "BROWN"            ,
  	                                   "CORAL"            ,
  	                                   "CYAN"             ,
  	                                   "DARK GREY"        ,
  	                                   "DARK GREEN"       ,
  	                                   "DIM GREY"         ,
  	                                   "GREY"             ,
  	                                   "GREEN"            ,
  	                                   "LIGHT BLUE"       ,
  	                                   "LIGHT GREY"       ,
  	                                   "MAGENTA"          ,
  	                                   "MAROON"           ,
  	                                   "NAVY"             ,
  	                                   "ORANGE"           ,
  	                                   "PURPLE"           ,
  	                                   "RED"              ,
  	                                   "TURQUOISE"        ,
  	                                   "VIOLET"           ,
  	                                   "WHITE"            ,
  	                                   "YELLOW"           ,
  	                                   NULL),
  	                                      NULL), NULL, wxVERTICAL, 150));
  	form->Add(wxMakeFormString("Style", pStyle, wxFORM_CHOICE,
  	                           new wxList(wxMakeConstraintStrings(
  	                                   "Solid"            ,
  	                                   "Short Dash"       ,
  	                                   "Long Dash"        ,
  	                                   "Dot"              ,
  	                                   "Dot Dash"         ,
  	                                   NULL),
  	                                      NULL), NULL, wxVERTICAL, 100));
  
  	wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Division properties", 10, 10, 500, 500);
  	if (GraphicsLabelFont)
  		dialog->SetLabelFont(GraphicsLabelFont);
  	if (GraphicsButtonFont)
  		dialog->SetButtonFont(GraphicsButtonFont);
  
  	form->AssociatePanel(dialog);
  	form->dialog = dialog;
  
  	dialog->Fit();
  	dialog->Centre(wxBOTH);
  
  	wxEndBusyCursor();
  	dialog->Show(TRUE);
  
  	int lineStyle = wxSOLID;
  	if (*pStyle)
  	{
  		if (strcmp(*pStyle, "Solid") == 0)
  			lineStyle = wxSOLID;
  		else if (strcmp(*pStyle, "Dot") == 0)
  			lineStyle = wxDOT;
  		else if (strcmp(*pStyle, "Short Dash") == 0)
  			lineStyle = wxSHORT_DASH;
  		else if (strcmp(*pStyle, "Long Dash") == 0)
  			lineStyle = wxLONG_DASH;
  		else if (strcmp(*pStyle, "Dot Dash") == 0)
  			lineStyle = wxDOT_DASH;
  	}
  
  	wxPen *newPen = wxThePenList->FindOrCreatePen(*pColour, lineWidth, lineStyle);
  	if (!pen)
  		pen = wxBLACK_PEN;
  	if (side == DIVISION_SIDE_LEFT)
  		m_leftSidePen = newPen;
  	else
  		m_topSidePen = newPen;
  
  	// Need to draw whole image again
  	wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
  	compositeParent->Draw(dc);
  #endif
  }
  
  // Popup menu
  void wxDivisionShape::PopupMenu(double x, double y)
  {
  	wxMenu *oglPopupDivisionMenu = new OGLPopupDivisionMenu;
  
  	oglPopupDivisionMenu->SetClientData((void *)this);
  	if (m_leftSide)
  		oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, TRUE);
  	else
  		oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, FALSE);
  	if (m_topSide)
  		oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, TRUE);
  	else
  		oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, FALSE);
  
  	int x1, y1;
  	m_canvas->GetViewStart(&x1, &y1);
  
  	int unit_x, unit_y;
  	m_canvas->GetScrollPixelsPerUnit(&unit_x, &unit_y);
  
  	wxClientDC dc(GetCanvas());
  	GetCanvas()->PrepareDC(dc);
  
  	int mouse_x = (int)(dc.LogicalToDeviceX((long)(x - x1 * unit_x)));
  	int mouse_y = (int)(dc.LogicalToDeviceY((long)(y - y1 * unit_y)));
  
  	m_canvas->PopupMenu(oglPopupDivisionMenu, mouse_x, mouse_y);
  	delete oglPopupDivisionMenu;
  }
  
  void wxDivisionShape::SetLeftSideColour(const wxString &colour)
  {
  	m_leftSideColour = colour;
  }
  
  void wxDivisionShape::SetTopSideColour(const wxString &colour)
  {
  	m_topSideColour = colour;
  }
  
  void wxDivisionShape::SetLeftSideStyle(const wxString &style)
  {
  	m_leftSideStyle = style;
  }
  
  void wxDivisionShape::SetTopSideStyle(const wxString &style)
  {
  	m_topSideStyle = style;
  }
  
  