home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / contrib / src / ogl / composit.cpp < prev    next >
C/C++ Source or Header  |  2002-12-18  |  50KB  |  1,786 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        composit.cpp
  3. // Purpose:     Composite OGL class
  4. // Author:      Julian Smart
  5. // Modified by:
  6. // Created:     12/07/98
  7. // RCS-ID:      $Id: composit.cpp,v 1.5.2.2 2002/12/18 06:12:59 RD Exp $
  8. // Copyright:   (c) Julian Smart
  9. // Licence:       wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. #ifdef __GNUG__
  13. #pragma implementation "composit.h"
  14. #endif
  15.  
  16. // For compilers that support precompilation, includes "wx.h".
  17. #include "wx/wxprec.h"
  18.  
  19. #ifdef __BORLANDC__
  20. #pragma hdrstop
  21. #endif
  22.  
  23. #ifndef WX_PRECOMP
  24. #include <wx/wx.h>
  25. #endif
  26.  
  27. #include <wx/wxexpr.h>
  28.  
  29. #include <wx/ogl/basic.h>
  30. #include <wx/ogl/basicp.h>
  31. #include <wx/ogl/constrnt.h>
  32. #include <wx/ogl/composit.h>
  33. #include <wx/ogl/misc.h>
  34. #include <wx/ogl/canvas.h>
  35.  
  36. #if wxUSE_PROLOGIO
  37. // Sometimes, objects need to access the whole database to
  38. // construct themselves.
  39. wxExprDatabase *GlobalwxExprDatabase = NULL;
  40. #endif
  41.  
  42. /*
  43.  * Division control point
  44.  */
  45.  
  46. class wxDivisionControlPoint: public wxControlPoint
  47. {
  48.  DECLARE_DYNAMIC_CLASS(wxDivisionControlPoint)
  49.  public:
  50.   wxDivisionControlPoint() {}
  51.   wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type);
  52.   ~wxDivisionControlPoint();
  53.  
  54.   void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
  55.   void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
  56.   void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
  57. };
  58.  
  59. IMPLEMENT_DYNAMIC_CLASS(wxDivisionControlPoint, wxControlPoint)
  60.  
  61. /*
  62.  * Composite object
  63.  *
  64.  */
  65.  
  66. IMPLEMENT_DYNAMIC_CLASS(wxCompositeShape, wxRectangleShape)
  67.  
  68. wxCompositeShape::wxCompositeShape(): wxRectangleShape(10.0, 10.0)
  69. {
  70. //  selectable = FALSE;
  71.   m_oldX = m_xpos;
  72.   m_oldY = m_ypos;
  73. }
  74.  
  75. wxCompositeShape::~wxCompositeShape()
  76. {
  77.   wxNode *node = m_constraints.First();
  78.   while (node)
  79.   {
  80.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  81.     delete constraint;
  82.     node = node->Next();
  83.   }
  84.   node = m_children.First();
  85.   while (node)
  86.   {
  87.     wxShape *object = (wxShape *)node->Data();
  88.     wxNode *next = node->Next();
  89.     object->Unlink();
  90.     delete object;
  91.     node = next;
  92.   }
  93. }
  94.  
  95. void wxCompositeShape::OnDraw(wxDC& dc)
  96. {
  97.   double x1 = (double)(m_xpos - m_width/2.0);
  98.   double y1 = (double)(m_ypos - m_height/2.0);
  99.  
  100.   if (m_shadowMode != SHADOW_NONE)
  101.   {
  102.     if (m_shadowBrush)
  103.       dc.SetBrush(* m_shadowBrush);
  104.     dc.SetPen(* g_oglTransparentPen);
  105.  
  106.     if (m_cornerRadius != 0.0)
  107.       dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
  108.                                WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
  109.     else
  110.       dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
  111.   }
  112. }
  113.  
  114. void wxCompositeShape::OnDrawContents(wxDC& dc)
  115. {
  116.   wxNode *node = m_children.First();
  117.   while (node)
  118.   {
  119.     wxShape *object = (wxShape *)node->Data();
  120.     object->Draw(dc);
  121.     object->DrawLinks(dc);
  122.     node = node->Next();
  123.   }
  124.   wxShape::OnDrawContents(dc);
  125. }
  126.  
  127. bool wxCompositeShape::OnMovePre(wxDC& dc, double x, double y, double oldx, double oldy, bool display)
  128. {
  129.   double diffX = x - oldx;
  130.   double diffY = y - oldy;
  131.   wxNode *node = m_children.First();
  132.   while (node)
  133.   {
  134.     wxShape *object = (wxShape *)node->Data();
  135.  
  136.     object->Erase(dc);
  137.     object->Move(dc, object->GetX() + diffX, object->GetY() + diffY, display);
  138.  
  139.     node = node->Next();
  140.   }
  141.   return TRUE;
  142. }
  143.  
  144. void wxCompositeShape::OnErase(wxDC& dc)
  145. {
  146.   wxRectangleShape::OnErase(dc);
  147.   wxNode *node = m_children.First();
  148.   while (node)
  149.   {
  150.     wxShape *object = (wxShape *)node->Data();
  151.     object->Erase(dc);
  152.     node = node->Next();
  153.   }
  154. }
  155.  
  156. static double objectStartX = 0.0;
  157. static double objectStartY = 0.0;
  158.  
  159. void wxCompositeShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
  160. {
  161.   double xx = x;
  162.   double yy = y;
  163.   m_canvas->Snap(&xx, &yy);
  164.   double offsetX = xx - objectStartX;
  165.   double offsetY = yy - objectStartY;
  166.  
  167.   wxClientDC dc(GetCanvas());
  168.   GetCanvas()->PrepareDC(dc);
  169.  
  170.   dc.SetLogicalFunction(OGLRBLF);
  171.   wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
  172.   dc.SetPen(dottedPen);
  173.   dc.SetBrush((* wxTRANSPARENT_BRUSH));
  174.  
  175.   GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
  176. //  wxShape::OnDragLeft(draw, x, y, keys, attachment);
  177. }
  178.  
  179. void wxCompositeShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
  180. {
  181.   objectStartX = x;
  182.   objectStartY = y;
  183.  
  184.   wxClientDC dc(GetCanvas());
  185.   GetCanvas()->PrepareDC(dc);
  186.  
  187.   Erase(dc);
  188.  
  189.   dc.SetLogicalFunction(OGLRBLF);
  190.  
  191.   wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
  192.   dc.SetPen(dottedPen);
  193.   dc.SetBrush((* wxTRANSPARENT_BRUSH));
  194.   m_canvas->CaptureMouse();
  195.  
  196.   double xx = x;
  197.   double yy = y;
  198.   m_canvas->Snap(&xx, &yy);
  199.   double offsetX = xx - objectStartX;
  200.   double offsetY = yy - objectStartY;
  201.  
  202.   GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
  203.  
  204. //  wxShape::OnBeginDragLeft(x, y, keys, attachment);
  205. }
  206.  
  207. void wxCompositeShape::OnEndDragLeft(double x, double y, int keys, int attachment)
  208. {
  209. //  wxShape::OnEndDragLeft(x, y, keys, attachment);
  210.  
  211.   wxClientDC dc(GetCanvas());
  212.   GetCanvas()->PrepareDC(dc);
  213.  
  214.   m_canvas->ReleaseMouse();
  215.  
  216.   if (!m_draggable)
  217.   {
  218.     if (m_parent) m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, 0);
  219.     return;
  220.   }
  221.  
  222.   dc.SetLogicalFunction(wxCOPY);
  223.   double xx = x;
  224.   double yy = y;
  225.   m_canvas->Snap(&xx, &yy);
  226.   double offsetX = xx - objectStartX;
  227.   double offsetY = yy - objectStartY;
  228.  
  229.   Move(dc, GetX() + offsetX, GetY() + offsetY);
  230.  
  231.   if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
  232. }
  233.  
  234. void wxCompositeShape::OnRightClick(double x, double y, int keys, int attachment)
  235. {
  236.   // If we get a ctrl-right click, this means send the message to
  237.   // the division, so we can invoke a user interface for dealing with regions.
  238.   if (keys & KEY_CTRL)
  239.   {
  240.     wxNode *node = m_divisions.First();
  241.     while (node)
  242.     {
  243.       wxDivisionShape *division = (wxDivisionShape *)node->Data();
  244.       wxNode *next = node->Next();
  245.       int attach = 0;
  246.       double dist = 0.0;
  247.       if (division->HitTest(x, y, &attach, &dist))
  248.       {
  249.         division->GetEventHandler()->OnRightClick(x, y, keys, attach);
  250.         node = NULL;
  251.       }
  252.       if (node)
  253.         node = next;
  254.     }
  255.   }
  256. }
  257.  
  258. void wxCompositeShape::SetSize(double w, double h, bool recursive)
  259. {
  260.   SetAttachmentSize(w, h);
  261.  
  262.   double xScale = (double)(w/(wxMax(1.0, GetWidth())));
  263.   double yScale = (double)(h/(wxMax(1.0, GetHeight())));
  264.  
  265.   m_width = w;
  266.   m_height = h;
  267.  
  268.   if (!recursive) return;
  269.  
  270.   wxNode *node = m_children.First();
  271.  
  272.   wxClientDC dc(GetCanvas());
  273.   GetCanvas()->PrepareDC(dc);
  274.  
  275.   double xBound, yBound;
  276.   while (node)
  277.   {
  278.     wxShape *object = (wxShape *)node->Data();
  279.  
  280.     // Scale the position first
  281.     double newX = (double)(((object->GetX() - GetX())*xScale) + GetX());
  282.     double newY = (double)(((object->GetY() - GetY())*yScale) + GetY());
  283.     object->Show(FALSE);
  284.     object->Move(dc, newX, newY);
  285.     object->Show(TRUE);
  286.  
  287.     // Now set the scaled size
  288.     object->GetBoundingBoxMin(&xBound, &yBound);
  289.     object->SetSize(object->GetFixedWidth() ? xBound : xScale*xBound,
  290.                     object->GetFixedHeight() ? yBound : yScale*yBound);
  291.  
  292.     node = node->Next();
  293.   }
  294.   SetDefaultRegionSize();
  295. }
  296.  
  297. void wxCompositeShape::AddChild(wxShape *child, wxShape *addAfter)
  298. {
  299.   m_children.Append(child);
  300.   child->SetParent(this);
  301.   if (m_canvas)
  302.   {
  303.     // Ensure we add at the right position
  304.     if (addAfter)
  305.       child->RemoveFromCanvas(m_canvas);
  306.     child->AddToCanvas(m_canvas, addAfter);
  307.   }
  308. }
  309.  
  310. void wxCompositeShape::RemoveChild(wxShape *child)
  311. {
  312.   m_children.DeleteObject(child);
  313.   m_divisions.DeleteObject(child);
  314.   RemoveChildFromConstraints(child);
  315.   child->SetParent(NULL);
  316. }
  317.  
  318. void wxCompositeShape::DeleteConstraintsInvolvingChild(wxShape *child)
  319. {
  320.   wxNode *node = m_constraints.First();
  321.   while (node)
  322.   {
  323.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  324.     wxNode *nextNode = node->Next();
  325.  
  326.     if ((constraint->m_constrainingObject == child) ||
  327.         constraint->m_constrainedObjects.Member(child))
  328.     {
  329.       delete constraint;
  330.       delete node;
  331.     }
  332.     node = nextNode;
  333.   }
  334. }
  335.  
  336. void wxCompositeShape::RemoveChildFromConstraints(wxShape *child)
  337. {
  338.   wxNode *node = m_constraints.First();
  339.   while (node)
  340.   {
  341.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  342.     wxNode *nextNode = node->Next();
  343.  
  344.     if (constraint->m_constrainedObjects.Member(child))
  345.       constraint->m_constrainedObjects.DeleteObject(child);
  346.     if (constraint->m_constrainingObject == child)
  347.       constraint->m_constrainingObject = NULL;
  348.  
  349.     // Delete the constraint if no participants left
  350.     if (!constraint->m_constrainingObject)
  351.     {
  352.       delete constraint;
  353.       delete node;
  354.     }
  355.  
  356.     node = nextNode;
  357.   }
  358. }
  359.  
  360. void wxCompositeShape::Copy(wxShape& copy)
  361. {
  362.   wxRectangleShape::Copy(copy);
  363.  
  364.   wxASSERT( copy.IsKindOf(CLASSINFO(wxCompositeShape)) ) ;
  365.  
  366.   wxCompositeShape& compositeCopy = (wxCompositeShape&) copy;
  367.  
  368.   // Associate old and new copies for compositeCopying constraints and division geometry
  369.   oglObjectCopyMapping.Append((long)this, &compositeCopy);
  370.  
  371.   // Copy the children
  372.   wxNode *node = m_children.First();
  373.   while (node)
  374.   {
  375.     wxShape *object = (wxShape *)node->Data();
  376.     wxShape *newObject = object->CreateNewCopy(FALSE, FALSE);
  377.     if (newObject->GetId() == 0)
  378.       newObject->SetId(wxNewId());
  379.  
  380.     newObject->SetParent(&compositeCopy);
  381.     compositeCopy.m_children.Append(newObject);
  382.  
  383.     // Some m_children may be divisions
  384.     if (m_divisions.Member(object))
  385.       compositeCopy.m_divisions.Append(newObject);
  386.  
  387.     oglObjectCopyMapping.Append((long)object, newObject);
  388.  
  389.     node = node->Next();
  390.   }
  391.  
  392.   // Copy the constraints
  393.   node = m_constraints.First();
  394.   while (node)
  395.   {
  396.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  397.  
  398.     wxShape *newConstraining = (wxShape *)(oglObjectCopyMapping.Find((long)constraint->m_constrainingObject)->Data());
  399.  
  400.     wxList newConstrainedList;
  401.     wxNode *node2 = constraint->m_constrainedObjects.First();
  402.     while (node2)
  403.     {
  404.       wxShape *constrainedObject = (wxShape *)node2->Data();
  405.       wxShape *newConstrained = (wxShape *)(oglObjectCopyMapping.Find((long)constrainedObject)->Data());
  406.       newConstrainedList.Append(newConstrained);
  407.       node2 = node2->Next();
  408.     }
  409.  
  410.     wxOGLConstraint *newConstraint = new wxOGLConstraint(constraint->m_constraintType, newConstraining,
  411.                                             newConstrainedList);
  412.     newConstraint->m_constraintId = constraint->m_constraintId;
  413.     if (constraint->m_constraintName)
  414.     {
  415.       newConstraint->m_constraintName = constraint->m_constraintName;
  416.     }
  417.     newConstraint->SetSpacing(constraint->m_xSpacing, constraint->m_ySpacing);
  418.     compositeCopy.m_constraints.Append(newConstraint);
  419.  
  420.     node = node->Next();
  421.   }
  422.  
  423.   // Now compositeCopy the division geometry
  424.   node = m_divisions.First();
  425.   while (node)
  426.   {
  427.     wxDivisionShape *division = (wxDivisionShape *)node->Data();
  428.     wxNode *node1 = oglObjectCopyMapping.Find((long)division);
  429.     wxNode *leftNode = NULL;
  430.     wxNode *topNode = NULL;
  431.     wxNode *rightNode = NULL;
  432.     wxNode *bottomNode = NULL;
  433.     if (division->GetLeftSide())
  434.       leftNode = oglObjectCopyMapping.Find((long)division->GetLeftSide());
  435.     if (division->GetTopSide())
  436.       topNode = oglObjectCopyMapping.Find((long)division->GetTopSide());
  437.     if (division->GetRightSide())
  438.       rightNode = oglObjectCopyMapping.Find((long)division->GetRightSide());
  439.     if (division->GetBottomSide())
  440.       bottomNode = oglObjectCopyMapping.Find((long)division->GetBottomSide());
  441.     if (node1)
  442.     {
  443.       wxDivisionShape *newDivision = (wxDivisionShape *)node1->Data();
  444.       if (leftNode)
  445.         newDivision->SetLeftSide((wxDivisionShape *)leftNode->Data());
  446.       if (topNode)
  447.         newDivision->SetTopSide((wxDivisionShape *)topNode->Data());
  448.       if (rightNode)
  449.         newDivision->SetRightSide((wxDivisionShape *)rightNode->Data());
  450.       if (bottomNode)
  451.         newDivision->SetBottomSide((wxDivisionShape *)bottomNode->Data());
  452.     }
  453.     node = node->Next();
  454.   }
  455. }
  456.  
  457. wxOGLConstraint *wxCompositeShape::AddConstraint(wxOGLConstraint *constraint)
  458. {
  459.   m_constraints.Append(constraint);
  460.   if (constraint->m_constraintId == 0)
  461.     constraint->m_constraintId = wxNewId();
  462.   return constraint;
  463. }
  464.  
  465. wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxList& constrained)
  466. {
  467.   wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, constrained);
  468.   if (constraint->m_constraintId == 0)
  469.     constraint->m_constraintId = wxNewId();
  470.   m_constraints.Append(constraint);
  471.   return constraint;
  472. }
  473.  
  474. wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxShape *constrained)
  475. {
  476.   wxList l;
  477.   l.Append(constrained);
  478.   wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, l);
  479.   if (constraint->m_constraintId == 0)
  480.     constraint->m_constraintId = wxNewId();
  481.   m_constraints.Append(constraint);
  482.   return constraint;
  483. }
  484.  
  485. wxOGLConstraint *wxCompositeShape::FindConstraint(long cId, wxCompositeShape **actualComposite)
  486. {
  487.   wxNode *node = m_constraints.First();
  488.   while (node)
  489.   {
  490.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  491.     if (constraint->m_constraintId == cId)
  492.     {
  493.       if (actualComposite)
  494.         *actualComposite = this;
  495.       return constraint;
  496.     }
  497.     node = node->Next();
  498.   }
  499.   // If not found, try children.
  500.   node = m_children.First();
  501.   while (node)
  502.   {
  503.     wxShape *child = (wxShape *)node->Data();
  504.     if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
  505.     {
  506.       wxOGLConstraint *constraint = ((wxCompositeShape *)child)->FindConstraint(cId, actualComposite);
  507.       if (constraint)
  508.       {
  509.         if (actualComposite)
  510.           *actualComposite = (wxCompositeShape *)child;
  511.         return constraint;
  512.       }
  513.     }
  514.     node = node->Next();
  515.   }
  516.   return NULL;
  517. }
  518.  
  519. void wxCompositeShape::DeleteConstraint(wxOGLConstraint *constraint)
  520. {
  521.   m_constraints.DeleteObject(constraint);
  522.   delete constraint;
  523. }
  524.  
  525. void wxCompositeShape::CalculateSize()
  526. {
  527.   double maxX = (double) -999999.9;
  528.   double maxY = (double) -999999.9;
  529.   double minX = (double)  999999.9;
  530.   double minY = (double)  999999.9;
  531.  
  532.   double w, h;
  533.   wxNode *node = m_children.First();
  534.   while (node)
  535.   {
  536.     wxShape *object = (wxShape *)node->Data();
  537.  
  538.     // Recalculate size of composite objects because may not conform
  539.     // to size it was set to - depends on the children.
  540.     object->CalculateSize();
  541.  
  542.     object->GetBoundingBoxMax(&w, &h);
  543.     if ((object->GetX() + (w/2.0)) > maxX)
  544.       maxX = (double)(object->GetX() + (w/2.0));
  545.     if ((object->GetX() - (w/2.0)) < minX)
  546.       minX = (double)(object->GetX() - (w/2.0));
  547.     if ((object->GetY() + (h/2.0)) > maxY)
  548.       maxY = (double)(object->GetY() + (h/2.0));
  549.     if ((object->GetY() - (h/2.0)) < minY)
  550.       minY = (double)(object->GetY() - (h/2.0));
  551.  
  552.     node = node->Next();
  553.   }
  554.   m_width = maxX - minX;
  555.   m_height = maxY - minY;
  556.   m_xpos = (double)(m_width/2.0 + minX);
  557.   m_ypos = (double)(m_height/2.0 + minY);
  558. }
  559.  
  560. bool wxCompositeShape::Recompute()
  561. {
  562.   int noIterations = 0;
  563.   bool changed = TRUE;
  564.   while (changed && (noIterations < 500))
  565.   {
  566.     changed = Constrain();
  567.     noIterations ++;
  568.   }
  569. /*
  570. #ifdef wx_x
  571.   if (changed)
  572.     cerr << "Warning: constraint algorithm failed after 500 iterations.\n";
  573. #endif
  574. */
  575.   return (!changed);
  576. }
  577.  
  578. bool wxCompositeShape::Constrain()
  579. {
  580.   CalculateSize();
  581.  
  582.   bool changed = FALSE;
  583.   wxNode *node = m_children.First();
  584.   while (node)
  585.   {
  586.     wxShape *object = (wxShape *)node->Data();
  587.     if (object->Constrain())
  588.       changed = TRUE;
  589.     node = node->Next();
  590.   }
  591.  
  592.   node = m_constraints.First();
  593.   while (node)
  594.   {
  595.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  596.     if (constraint->Evaluate()) changed = TRUE;
  597.     node = node->Next();
  598.   }
  599.   return changed;
  600. }
  601.  
  602. #if wxUSE_PROLOGIO
  603. void wxCompositeShape::WriteAttributes(wxExpr *clause)
  604. {
  605.   wxRectangleShape::WriteAttributes(clause);
  606.  
  607. //  clause->AddAttributeValue("selectable", (long)selectable);
  608.  
  609.   // Output constraints as constraint1 = (...), constraint2 = (...), etc.
  610.   int constraintNo = 1;
  611.   char m_constraintNameBuf[20];
  612.   wxNode *node = m_constraints.First();
  613.   while (node)
  614.   {
  615.     wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
  616.     sprintf(m_constraintNameBuf, "constraint%d", constraintNo);
  617.  
  618.     // Each constraint is stored in the form
  619.     // (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
  620.     wxExpr *constraintExpr = new wxExpr(wxExprList);
  621.     constraintExpr->Append(new wxExpr((long)constraint->m_constraintType));
  622.     constraintExpr->Append(new wxExpr(wxExprString, constraint->m_constraintName));
  623.     constraintExpr->Append(new wxExpr(constraint->m_constraintId));
  624.     constraintExpr->Append(new wxExpr(constraint->m_xSpacing));
  625.     constraintExpr->Append(new wxExpr(constraint->m_ySpacing));
  626.     constraintExpr->Append(new wxExpr(constraint->m_constrainingObject->GetId()));
  627.  
  628.     wxExpr *objectList = new wxExpr(wxExprList);
  629.     wxNode *node1 = constraint->m_constrainedObjects.First();
  630.     while (node1)
  631.     {
  632.       wxShape *obj = (wxShape *)node1->Data();
  633.       objectList->Append(new wxExpr(obj->GetId()));
  634.       node1 = node1->Next();
  635.     }
  636.     constraintExpr->Append(objectList);
  637.  
  638.     clause->AddAttributeValue(m_constraintNameBuf, constraintExpr);
  639.  
  640.     node = node->Next();
  641.     constraintNo ++;
  642.   }
  643.  
  644.   // Write the ids of all the child images
  645.   wxExpr *childrenExpr = new wxExpr(wxExprList);
  646.   node = m_children.First();
  647.   while (node)
  648.   {
  649.     wxShape *child = (wxShape *)node->Data();
  650.     childrenExpr->Append(new wxExpr(child->GetId()));
  651.     node = node->Next();
  652.   }
  653.   clause->AddAttributeValue("children", childrenExpr);
  654.  
  655.   // Write the ids of all the division images
  656.   if (m_divisions.Number() > 0)
  657.   {
  658.     wxExpr *divisionsExpr = new wxExpr(wxExprList);
  659.     node = m_divisions.First();
  660.     while (node)
  661.     {
  662.       wxShape *child = (wxShape *)node->Data();
  663.       divisionsExpr->Append(new wxExpr(child->GetId()));
  664.       node = node->Next();
  665.     }
  666.     clause->AddAttributeValue("divisions", divisionsExpr);
  667.   }
  668. }
  669.  
  670. // Problem. Child images are always written AFTER the parent
  671. // so as to be able to link up to parent. So we may not be able
  672. // to find the constraint participants until we've read everything
  673. // in. Need to have another pass for composites.
  674. void wxCompositeShape::ReadAttributes(wxExpr *clause)
  675. {
  676.   wxRectangleShape::ReadAttributes(clause);
  677.  
  678. //  clause->GetAttributeValue("selectable", selectable);
  679. }
  680.  
  681. void wxCompositeShape::ReadConstraints(wxExpr *clause, wxExprDatabase *database)
  682. {
  683.   // Constraints are output as constraint1 = (...), constraint2 = (...), etc.
  684.   int constraintNo = 1;
  685.   char m_constraintNameBuf[20];
  686.   bool haveConstraints = TRUE;
  687.  
  688.   while (haveConstraints)
  689.   {
  690.     sprintf(m_constraintNameBuf, "constraint%d", constraintNo);
  691.     wxExpr *constraintExpr = NULL;
  692.     clause->GetAttributeValue(m_constraintNameBuf, &constraintExpr);
  693.     if (!constraintExpr)
  694.     {
  695.       haveConstraints = FALSE;
  696.       break;
  697.     }
  698.     int cType = 0;
  699.     double cXSpacing = 0.0;
  700.     double cYSpacing = 0.0;
  701.     wxString cName("");
  702.     long cId = 0;
  703.     wxShape *m_constrainingObject = NULL;
  704.     wxList m_constrainedObjects;
  705.  
  706.     // Each constraint is stored in the form
  707.     // (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
  708.  
  709.     wxExpr *typeExpr = constraintExpr->Nth(0);
  710.     wxExpr *nameExpr = constraintExpr->Nth(1);
  711.     wxExpr *idExpr = constraintExpr->Nth(2);
  712.     wxExpr *xExpr = constraintExpr->Nth(3);
  713.     wxExpr *yExpr = constraintExpr->Nth(4);
  714.     wxExpr *constrainingExpr = constraintExpr->Nth(5);
  715.     wxExpr *constrainedExpr = constraintExpr->Nth(6);
  716.  
  717.     cType = (int)typeExpr->IntegerValue();
  718.     cXSpacing = xExpr->RealValue();
  719.     cYSpacing = yExpr->RealValue();
  720.     cName = nameExpr->StringValue();
  721.     cId = idExpr->IntegerValue();
  722.  
  723.     wxExpr *objExpr1 = database->HashFind("node_image", constrainingExpr->IntegerValue());
  724.     if (objExpr1 && objExpr1->GetClientData())
  725.       m_constrainingObject = (wxShape *)objExpr1->GetClientData();
  726.     else
  727.       wxLogFatalError(wxT("Object graphics error: Couldn't find constraining image of composite."));
  728.  
  729.     int i = 0;
  730.     wxExpr *currentIdExpr = constrainedExpr->Nth(i);
  731.     while (currentIdExpr)
  732.     {
  733.       long currentId = currentIdExpr->IntegerValue();
  734.       wxExpr *objExpr2 = database->HashFind("node_image", currentId);
  735.       if (objExpr2 && objExpr2->GetClientData())
  736.       {
  737.         m_constrainedObjects.Append((wxShape *)objExpr2->GetClientData());
  738.       }
  739.       else
  740.       {
  741.         wxLogFatalError(wxT("Object graphics error: Couldn't find constrained image of composite."));
  742.       }
  743.  
  744.       i ++;
  745.       currentIdExpr = constrainedExpr->Nth(i);
  746.     }
  747.     wxOGLConstraint *newConstraint = AddConstraint(cType, m_constrainingObject, m_constrainedObjects);
  748.     newConstraint->SetSpacing(cXSpacing, cYSpacing);
  749.     newConstraint->m_constraintId = cId;
  750.     newConstraint->m_constraintName = cName;
  751.     constraintNo ++;
  752.   }
  753. }
  754. #endif
  755.  
  756. // Make this composite into a container by creating one wxDivisionShape
  757. void wxCompositeShape::MakeContainer()
  758. {
  759.   wxDivisionShape *division = OnCreateDivision();
  760.   m_divisions.Append(division);
  761.   AddChild(division);
  762.  
  763.   division->SetSize(m_width, m_height);
  764.  
  765.   wxClientDC dc(GetCanvas());
  766.   GetCanvas()->PrepareDC(dc);
  767.  
  768.   division->Move(dc, GetX(), GetY());
  769.   Recompute();
  770.   division->Show(TRUE);
  771. }
  772.  
  773. wxDivisionShape *wxCompositeShape::OnCreateDivision()
  774. {
  775.   return new wxDivisionShape;
  776. }
  777.  
  778. wxShape *wxCompositeShape::FindContainerImage()
  779. {
  780.   wxNode *node = m_children.First();
  781.   while (node)
  782.   {
  783.     wxShape *child = (wxShape *)node->Data();
  784.     if (!m_divisions.Member(child))
  785.       return child;
  786.     node = node->Next();
  787.   }
  788.   return NULL;
  789. }
  790.  
  791. // Returns TRUE if division is a descendant of this container
  792. bool wxCompositeShape::ContainsDivision(wxDivisionShape *division)
  793. {
  794.   if (m_divisions.Member(division))
  795.     return TRUE;
  796.   wxNode *node = m_children.First();
  797.   while (node)
  798.   {
  799.     wxShape *child = (wxShape *)node->Data();
  800.     if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
  801.     {
  802.       bool ans = ((wxCompositeShape *)child)->ContainsDivision(division);
  803.       if (ans)
  804.         return TRUE;
  805.     }
  806.     node = node->Next();
  807.   }
  808.   return FALSE;
  809. }
  810.  
  811. /*
  812.  * Division object
  813.  *
  814.  */
  815.  
  816. IMPLEMENT_DYNAMIC_CLASS(wxDivisionShape, wxCompositeShape)
  817.  
  818. wxDivisionShape::wxDivisionShape()
  819. {
  820.   SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT);
  821.   SetCentreResize(FALSE);
  822.   SetAttachmentMode(TRUE);
  823.   m_leftSide = NULL;
  824.   m_rightSide = NULL;
  825.   m_topSide = NULL;
  826.   m_bottomSide = NULL;
  827.   m_handleSide = DIVISION_SIDE_NONE;
  828.   m_leftSidePen = wxBLACK_PEN;
  829.   m_topSidePen = wxBLACK_PEN;
  830.   m_leftSideColour = wxT("BLACK");
  831.   m_topSideColour = wxT("BLACK");
  832.   m_leftSideStyle = wxT("Solid");
  833.   m_topSideStyle = wxT("Solid");
  834.   ClearRegions();
  835. }
  836.  
  837. wxDivisionShape::~wxDivisionShape()
  838. {
  839. }
  840.  
  841. void wxDivisionShape::OnDraw(wxDC& dc)
  842. {
  843.     dc.SetBrush(* wxTRANSPARENT_BRUSH);
  844.     dc.SetBackgroundMode(wxTRANSPARENT);
  845.  
  846.     double x1 = (double)(GetX() - (GetWidth()/2.0));
  847.     double y1 = (double)(GetY() - (GetHeight()/2.0));
  848.     double x2 = (double)(GetX() + (GetWidth()/2.0));
  849.     double y2 = (double)(GetY() + (GetHeight()/2.0));
  850.  
  851.     // Should subtract 1 pixel if drawing under Windows
  852. #ifdef __WXMSW__
  853.     y2 -= (double)1.0;
  854. #endif
  855.  
  856.     if (m_leftSide)
  857.     {
  858.       dc.SetPen(* m_leftSidePen);
  859.       dc.DrawLine(WXROUND(x1), WXROUND(y2), WXROUND(x1), WXROUND(y1));
  860.     }
  861.     if (m_topSide)
  862.     {
  863.       dc.SetPen(* m_topSidePen);
  864.       dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y1));
  865.     }
  866.  
  867.     // For testing purposes, draw a rectangle so we know
  868.     // how big the division is.
  869. //    SetBrush(* wxCYAN_BRUSH);
  870. //    wxRectangleShape::OnDraw(dc);
  871. }
  872.  
  873. void wxDivisionShape::OnDrawContents(wxDC& dc)
  874. {
  875.   wxCompositeShape::OnDrawContents(dc);
  876. }
  877.  
  878. bool wxDivisionShape::OnMovePre(wxDC& dc, double x, double y, double oldx, double oldy, bool display)
  879. {
  880.   double diffX = x - oldx;
  881.   double diffY = y - oldy;
  882.   wxNode *node = m_children.First();
  883.   while (node)
  884.   {
  885.     wxShape *object = (wxShape *)node->Data();
  886.     object->Erase(dc);
  887.     object->Move(dc, object->GetX() + diffX, object->GetY() + diffY, display);
  888.     node = node->Next();
  889.   }
  890.   return TRUE;
  891. }
  892.  
  893. void wxDivisionShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
  894. {
  895.   if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
  896.   {
  897.     attachment = 0;
  898.     double dist;
  899.     if (m_parent)
  900.     {
  901.       m_parent->HitTest(x, y, &attachment, &dist);
  902.       m_parent->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
  903.     }
  904.     return;
  905.   }
  906.   wxShape::OnDragLeft(draw, x, y, keys, attachment);
  907. }
  908.  
  909. void wxDivisionShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
  910. {
  911.   if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
  912.   {
  913.     attachment = 0;
  914.     double dist;
  915.     if (m_parent)
  916.     {
  917.       m_parent->HitTest(x, y, &attachment, &dist);
  918.       m_parent->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
  919.     }
  920.     return;
  921.   }
  922.  
  923.   wxShape::OnBeginDragLeft(x, y, keys, attachment);
  924. }
  925.  
  926. void wxDivisionShape::OnEndDragLeft(double x, double y, int keys, int attachment)
  927. {
  928.   m_canvas->ReleaseMouse();
  929.   if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
  930.   {
  931.     attachment = 0;
  932.     double dist;
  933.     if (m_parent)
  934.     {
  935.       m_parent->HitTest(x, y, &attachment, &dist);
  936.       m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
  937.     }
  938.     return;
  939.   }
  940.  
  941.   wxClientDC dc(GetCanvas());
  942.   GetCanvas()->PrepareDC(dc);
  943.  
  944.   dc.SetLogicalFunction(wxCOPY);
  945.  
  946.   m_canvas->Snap(&m_xpos, &m_ypos);
  947.   GetEventHandler()->OnMovePre(dc, x, y, m_oldX, m_oldY);
  948.  
  949.   ResetControlPoints();
  950.   Draw(dc);
  951.   MoveLinks(dc);
  952.   GetEventHandler()->OnDrawControlPoints(dc);
  953.  
  954.   if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
  955. }
  956.  
  957. void wxDivisionShape::SetSize(double w, double h, bool recursive)
  958. {
  959.   m_width = w;
  960.   m_height = h;
  961.   wxRectangleShape::SetSize(w, h, recursive);
  962. }
  963.  
  964. void wxDivisionShape::CalculateSize()
  965. {
  966. }
  967.  
  968. void wxDivisionShape::Copy(wxShape& copy)
  969. {
  970.   wxCompositeShape::Copy(copy);
  971.  
  972.   wxASSERT( copy.IsKindOf(CLASSINFO(wxDivisionShape)) ) ;
  973.  
  974.   wxDivisionShape& divisionCopy = (wxDivisionShape&) copy;
  975.  
  976.   divisionCopy.m_leftSideStyle = m_leftSideStyle;
  977.   divisionCopy.m_topSideStyle = m_topSideStyle;
  978.   divisionCopy.m_leftSideColour = m_leftSideColour;
  979.   divisionCopy.m_topSideColour = m_topSideColour;
  980.  
  981.   divisionCopy.m_leftSidePen = m_leftSidePen;
  982.   divisionCopy.m_topSidePen = m_topSidePen;
  983.   divisionCopy.m_handleSide = m_handleSide;
  984.  
  985.   // Division geometry copying is handled at the wxCompositeShape level.
  986. }
  987.  
  988. #if wxUSE_PROLOGIO
  989. void wxDivisionShape::WriteAttributes(wxExpr *clause)
  990. {
  991.   wxCompositeShape::WriteAttributes(clause);
  992.  
  993.   if (m_leftSide)
  994.     clause->AddAttributeValue("left_side", (long)m_leftSide->GetId());
  995.   if (m_topSide)
  996.     clause->AddAttributeValue("top_side", (long)m_topSide->GetId());
  997.   if (m_rightSide)
  998.     clause->AddAttributeValue("right_side", (long)m_rightSide->GetId());
  999.   if (m_bottomSide)
  1000.     clause->AddAttributeValue("bottom_side", (long)m_bottomSide->GetId());
  1001.  
  1002.   clause->AddAttributeValue("handle_side", (long)m_handleSide);
  1003.   clause->AddAttributeValueString("left_colour", m_leftSideColour);
  1004.   clause->AddAttributeValueString("top_colour", m_topSideColour);
  1005.   clause->AddAttributeValueString("left_style", m_leftSideStyle);
  1006.   clause->AddAttributeValueString("top_style", m_topSideStyle);
  1007. }
  1008.  
  1009. void wxDivisionShape::ReadAttributes(wxExpr *clause)
  1010. {
  1011.   wxCompositeShape::ReadAttributes(clause);
  1012.  
  1013.   clause->GetAttributeValue("handle_side", m_handleSide);
  1014.   clause->GetAttributeValue("left_colour", m_leftSideColour);
  1015.   clause->GetAttributeValue("top_colour", m_topSideColour);
  1016.   clause->GetAttributeValue("left_style", m_leftSideStyle);
  1017.   clause->GetAttributeValue("top_style", m_topSideStyle);
  1018. }
  1019. #endif
  1020.  
  1021. // Experimental
  1022. void wxDivisionShape::OnRightClick(double x, double y, int keys, int attachment)
  1023. {
  1024.   if (keys & KEY_CTRL)
  1025.   {
  1026.     PopupMenu(x, y);
  1027.   }
  1028. /*
  1029.   else if (keys & KEY_SHIFT)
  1030.   {
  1031.     if (m_leftSide || m_topSide || m_rightSide || m_bottomSide)
  1032.     {
  1033.       if (Selected())
  1034.       {
  1035.         Select(FALSE);
  1036.         GetParent()->Draw(dc);
  1037.       }
  1038.       else
  1039.         Select(TRUE);
  1040.     }
  1041.   }
  1042. */
  1043.   else
  1044.   {
  1045.     attachment = 0;
  1046.     double dist;
  1047.     if (m_parent)
  1048.     {
  1049.       m_parent->HitTest(x, y, &attachment, &dist);
  1050.       m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
  1051.     }
  1052.     return;
  1053.   }
  1054. }
  1055.  
  1056.  
  1057. // Divide wxHORIZONTALly or wxVERTICALly
  1058. bool wxDivisionShape::Divide(int direction)
  1059. {
  1060.   // Calculate existing top-left, bottom-right
  1061.   double x1 = (double)(GetX() - (GetWidth()/2.0));
  1062.   double y1 = (double)(GetY() - (GetHeight()/2.0));
  1063.   wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
  1064.   double oldWidth = GetWidth();
  1065.   double oldHeight = GetHeight();
  1066.   if (Selected())
  1067.     Select(FALSE);
  1068.  
  1069.   wxClientDC dc(GetCanvas());
  1070.   GetCanvas()->PrepareDC(dc);
  1071.  
  1072.   if (direction == wxVERTICAL)
  1073.   {
  1074.     // Dividing vertically means notionally putting a horizontal line through it.
  1075.     // Break existing piece into two.
  1076.     double newXPos1 = GetX();
  1077.     double newYPos1 = (double)(y1 + (GetHeight()/4.0));
  1078.     double newXPos2 = GetX();
  1079.     double newYPos2 = (double)(y1 + (3.0*GetHeight()/4.0));
  1080.     wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
  1081.     newDivision->Show(TRUE);
  1082.  
  1083.     Erase(dc);
  1084.  
  1085.     // Anything adjoining the bottom of this division now adjoins the
  1086.     // bottom of the new division.
  1087.     wxNode *node = compositeParent->GetDivisions().First();
  1088.     while (node)
  1089.     {
  1090.       wxDivisionShape *obj = (wxDivisionShape *)node->Data();
  1091.       if (obj->GetTopSide() == this)
  1092.         obj->SetTopSide(newDivision);
  1093.       node = node->Next();
  1094.     }
  1095.     newDivision->SetTopSide(this);
  1096.     newDivision->SetBottomSide(m_bottomSide);
  1097.     newDivision->SetLeftSide(m_leftSide);
  1098.     newDivision->SetRightSide(m_rightSide);
  1099.     m_bottomSide = newDivision;
  1100.  
  1101.     compositeParent->GetDivisions().Append(newDivision);
  1102.  
  1103.     // CHANGE: Need to insert this division at start of divisions in the object
  1104.     // list, because e.g.:
  1105.     // 1) Add division
  1106.     // 2) Add contained object
  1107.     // 3) Add division
  1108.     // Division is now receiving mouse events _before_ the contained object,
  1109.     // because it was added last (on top of all others)
  1110.  
  1111.     // Add after the image that visualizes the container
  1112.     compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
  1113.  
  1114.     m_handleSide = DIVISION_SIDE_BOTTOM;
  1115.     newDivision->SetHandleSide(DIVISION_SIDE_TOP);
  1116.  
  1117.     SetSize(oldWidth, (double)(oldHeight/2.0));
  1118.     Move(dc, newXPos1, newYPos1);
  1119.  
  1120.     newDivision->SetSize(oldWidth, (double)(oldHeight/2.0));
  1121.     newDivision->Move(dc, newXPos2, newYPos2);
  1122.   }
  1123.   else
  1124.   {
  1125.     // Dividing horizontally means notionally putting a vertical line through it.
  1126.     // Break existing piece into two.
  1127.     double newXPos1 = (double)(x1 + (GetWidth()/4.0));
  1128.     double newYPos1 = GetY();
  1129.     double newXPos2 = (double)(x1 + (3.0*GetWidth()/4.0));
  1130.     double newYPos2 = GetY();
  1131.     wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
  1132.     newDivision->Show(TRUE);
  1133.  
  1134.     Erase(dc);
  1135.  
  1136.     // Anything adjoining the left of this division now adjoins the
  1137.     // left of the new division.
  1138.     wxNode *node = compositeParent->GetDivisions().First();
  1139.     while (node)
  1140.     {
  1141.       wxDivisionShape *obj = (wxDivisionShape *)node->Data();
  1142.       if (obj->GetLeftSide() == this)
  1143.         obj->SetLeftSide(newDivision);
  1144.       node = node->Next();
  1145.     }
  1146.     newDivision->SetTopSide(m_topSide);
  1147.     newDivision->SetBottomSide(m_bottomSide);
  1148.     newDivision->SetLeftSide(this);
  1149.     newDivision->SetRightSide(m_rightSide);
  1150.     m_rightSide = newDivision;
  1151.  
  1152.     compositeParent->GetDivisions().Append(newDivision);
  1153.     compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
  1154.  
  1155.     m_handleSide = DIVISION_SIDE_RIGHT;
  1156.     newDivision->SetHandleSide(DIVISION_SIDE_LEFT);
  1157.  
  1158.     SetSize((double)(oldWidth/2.0), oldHeight);
  1159.     Move(dc, newXPos1, newYPos1);
  1160.  
  1161.     newDivision->SetSize((double)(oldWidth/2.0), oldHeight);
  1162.     newDivision->Move(dc, newXPos2, newYPos2);
  1163.   }
  1164.   if (compositeParent->Selected())
  1165.   {
  1166.     compositeParent->DeleteControlPoints(& dc);
  1167.     compositeParent->MakeControlPoints();
  1168.     compositeParent->MakeMandatoryControlPoints();
  1169.   }
  1170.   compositeParent->Draw(dc);
  1171.   return TRUE;
  1172. }
  1173.  
  1174. // Make one control point for every visible line
  1175. void wxDivisionShape::MakeControlPoints()
  1176. {
  1177.   MakeMandatoryControlPoints();
  1178. }
  1179.  
  1180. void wxDivisionShape::MakeMandatoryControlPoints()
  1181. {
  1182.   double maxX, maxY;
  1183.  
  1184.   GetBoundingBoxMax(&maxX, &maxY);
  1185.   double x, y;
  1186.   int direction;
  1187. /*
  1188.   if (m_leftSide)
  1189.   {
  1190.     x = (double)(-maxX/2.0);
  1191.     y = 0.0;
  1192.     wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
  1193.                                              CONTROL_POINT_HORIZONTAL);
  1194.     m_canvas->AddShape(control);
  1195.     m_controlPoints.Append(control);
  1196.   }
  1197.   if (m_topSide)
  1198.   {
  1199.     x = 0.0;
  1200.     y = (double)(-maxY/2.0);
  1201.     wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
  1202.                                              CONTROL_POINT_VERTICAL);
  1203.     m_canvas->AddShape(control);
  1204.     m_controlPoints.Append(control);
  1205.   }
  1206. */
  1207.   switch (m_handleSide)
  1208.   {
  1209.     case DIVISION_SIDE_LEFT:
  1210.     {
  1211.       x = (double)(-maxX/2.0);
  1212.       y = 0.0;
  1213.       direction = CONTROL_POINT_HORIZONTAL;
  1214.       break;
  1215.     }
  1216.     case DIVISION_SIDE_TOP:
  1217.     {
  1218.       x = 0.0;
  1219.       y = (double)(-maxY/2.0);
  1220.       direction = CONTROL_POINT_VERTICAL;
  1221.       break;
  1222.     }
  1223.     case DIVISION_SIDE_RIGHT:
  1224.     {
  1225.       x = (double)(maxX/2.0);
  1226.       y = 0.0;
  1227.       direction = CONTROL_POINT_HORIZONTAL;
  1228.       break;
  1229.     }
  1230.     case DIVISION_SIDE_BOTTOM:
  1231.     {
  1232.       x = 0.0;
  1233.       y = (double)(maxY/2.0);
  1234.       direction = CONTROL_POINT_VERTICAL;
  1235.       break;
  1236.     }
  1237.     default:
  1238.       break;
  1239.   }
  1240.   if (m_handleSide != DIVISION_SIDE_NONE)
  1241.   {
  1242.     wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
  1243.                                              direction);
  1244.     m_canvas->AddShape(control);
  1245.     m_controlPoints.Append(control);
  1246.   }
  1247. }
  1248.  
  1249. void wxDivisionShape::ResetControlPoints()
  1250. {
  1251.   ResetMandatoryControlPoints();
  1252. }
  1253.  
  1254. void wxDivisionShape::ResetMandatoryControlPoints()
  1255. {
  1256.   if (m_controlPoints.Number() < 1)
  1257.     return;
  1258.  
  1259.   double maxX, maxY;
  1260.  
  1261.   GetBoundingBoxMax(&maxX, &maxY);
  1262. /*
  1263.   wxNode *node = m_controlPoints.First();
  1264.   while (node)
  1265.   {
  1266.     wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
  1267.     if (control->type == CONTROL_POINT_HORIZONTAL)
  1268.     {
  1269.       control->xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
  1270.     }
  1271.     else if (control->type == CONTROL_POINT_VERTICAL)
  1272.     {
  1273.       control->xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
  1274.     }
  1275.     node = node->Next();
  1276.   }
  1277. */
  1278.   wxNode *node = m_controlPoints.First();
  1279.   if ((m_handleSide == DIVISION_SIDE_LEFT) && node)
  1280.   {
  1281.     wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
  1282.     control->m_xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
  1283.   }
  1284.  
  1285.   if ((m_handleSide == DIVISION_SIDE_TOP) && node)
  1286.   {
  1287.     wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
  1288.     control->m_xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
  1289.   }
  1290.  
  1291.   if ((m_handleSide == DIVISION_SIDE_RIGHT) && node)
  1292.   {
  1293.     wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
  1294.     control->m_xoffset = (double)(maxX/2.0); control->m_yoffset = 0.0;
  1295.   }
  1296.  
  1297.   if ((m_handleSide == DIVISION_SIDE_BOTTOM) && node)
  1298.   {
  1299.     wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
  1300.     control->m_xoffset = 0.0; control->m_yoffset = (double)(maxY/2.0);
  1301.   }
  1302. }
  1303.  
  1304. // Adjust a side, returning FALSE if it's not physically possible.
  1305. bool wxDivisionShape::AdjustLeft(double left, bool test)
  1306. {
  1307.   double x2 = (double)(GetX() + (GetWidth()/2.0));
  1308.  
  1309.   if (left >= x2)
  1310.     return FALSE;
  1311.   if (test)
  1312.     return TRUE;
  1313.  
  1314.   double newW = x2 - left;
  1315.   double newX = (double)(left + newW/2.0);
  1316.   SetSize(newW, GetHeight());
  1317.  
  1318.   wxClientDC dc(GetCanvas());
  1319.   GetCanvas()->PrepareDC(dc);
  1320.  
  1321.   Move(dc, newX, GetY());
  1322.  
  1323.   return TRUE;
  1324. }
  1325.  
  1326. bool wxDivisionShape::AdjustTop(double top, bool test)
  1327. {
  1328.   double y2 = (double)(GetY() + (GetHeight()/2.0));
  1329.  
  1330.   if (top >= y2)
  1331.     return FALSE;
  1332.   if (test)
  1333.     return TRUE;
  1334.  
  1335.   double newH = y2 - top;
  1336.   double newY = (double)(top + newH/2.0);
  1337.   SetSize(GetWidth(), newH);
  1338.  
  1339.   wxClientDC dc(GetCanvas());
  1340.   GetCanvas()->PrepareDC(dc);
  1341.  
  1342.   Move(dc, GetX(), newY);
  1343.  
  1344.   return TRUE;
  1345. }
  1346.  
  1347. bool wxDivisionShape::AdjustRight(double right, bool test)
  1348. {
  1349.   double x1 = (double)(GetX() - (GetWidth()/2.0));
  1350.  
  1351.   if (right <= x1)
  1352.     return FALSE;
  1353.   if (test)
  1354.     return TRUE;
  1355.  
  1356.   double newW = right - x1;
  1357.   double newX = (double)(x1 + newW/2.0);
  1358.   SetSize(newW, GetHeight());
  1359.  
  1360.   wxClientDC dc(GetCanvas());
  1361.   GetCanvas()->PrepareDC(dc);
  1362.  
  1363.   Move(dc, newX, GetY());
  1364.  
  1365.   return TRUE;
  1366. }
  1367.  
  1368. bool wxDivisionShape::AdjustBottom(double bottom, bool test)
  1369. {
  1370.   double y1 = (double)(GetY() - (GetHeight()/2.0));
  1371.  
  1372.   if (bottom <= y1)
  1373.     return FALSE;
  1374.   if (test)
  1375.     return TRUE;
  1376.  
  1377.   double newH = bottom - y1;
  1378.   double newY = (double)(y1 + newH/2.0);
  1379.   SetSize(GetWidth(), newH);
  1380.  
  1381.   wxClientDC dc(GetCanvas());
  1382.   GetCanvas()->PrepareDC(dc);
  1383.  
  1384.   Move(dc, GetX(), newY);
  1385.  
  1386.   return TRUE;
  1387. }
  1388.  
  1389. wxDivisionControlPoint::wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):
  1390.   wxControlPoint(the_canvas, object, size, the_xoffset, the_yoffset, the_type)
  1391. {
  1392.   SetEraseObject(FALSE);
  1393. }
  1394.  
  1395. wxDivisionControlPoint::~wxDivisionControlPoint()
  1396. {
  1397. }
  1398.  
  1399. static double originalX = 0.0;
  1400. static double originalY = 0.0;
  1401. static double originalW = 0.0;
  1402. static double originalH = 0.0;
  1403.  
  1404. // Implement resizing of canvas object
  1405. void wxDivisionControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
  1406. {
  1407.   wxControlPoint::OnDragLeft(draw, x, y, keys, attachment);
  1408. }
  1409.  
  1410. void wxDivisionControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
  1411. {
  1412.   wxDivisionShape *division = (wxDivisionShape *)m_shape;
  1413.   originalX = division->GetX();
  1414.   originalY = division->GetY();
  1415.   originalW = division->GetWidth();
  1416.   originalH = division->GetHeight();
  1417.  
  1418.   wxControlPoint::OnBeginDragLeft(x, y, keys, attachment);
  1419. }
  1420.  
  1421. void wxDivisionControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
  1422. {
  1423.   wxControlPoint::OnEndDragLeft(x, y, keys, attachment);
  1424.  
  1425.   wxClientDC dc(GetCanvas());
  1426.   GetCanvas()->PrepareDC(dc);
  1427.  
  1428.   wxDivisionShape *division = (wxDivisionShape *)m_shape;
  1429.   wxCompositeShape *divisionParent = (wxCompositeShape *)division->GetParent();
  1430.  
  1431.   // Need to check it's within the bounds of the parent composite.
  1432.   double x1 = (double)(divisionParent->GetX() - (divisionParent->GetWidth()/2.0));
  1433.   double y1 = (double)(divisionParent->GetY() - (divisionParent->GetHeight()/2.0));
  1434.   double x2 = (double)(divisionParent->GetX() + (divisionParent->GetWidth()/2.0));
  1435.   double y2 = (double)(divisionParent->GetY() + (divisionParent->GetHeight()/2.0));
  1436.  
  1437.   // Need to check it has not made the division zero or negative width/height
  1438.   double dx1 = (double)(division->GetX() - (division->GetWidth()/2.0));
  1439.   double dy1 = (double)(division->GetY() - (division->GetHeight()/2.0));
  1440.   double dx2 = (double)(division->GetX() + (division->GetWidth()/2.0));
  1441.   double dy2 = (double)(division->GetY() + (division->GetHeight()/2.0));
  1442.  
  1443.   bool success = TRUE;
  1444.   switch (division->GetHandleSide())
  1445.   {
  1446.     case DIVISION_SIDE_LEFT:
  1447.     {
  1448.       if ((x <= x1) || (x >= x2) || (x >= dx2))
  1449.         success = FALSE;
  1450.       // Try it out first...
  1451.       else if (!division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, TRUE))
  1452.         success = FALSE;
  1453.       else
  1454.         division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, FALSE);
  1455.  
  1456.       break;
  1457.     }
  1458.     case DIVISION_SIDE_TOP:
  1459.     {
  1460.       if ((y <= y1) || (y >= y2) || (y >= dy2))
  1461.         success = FALSE;
  1462.       else if (!division->ResizeAdjoining(DIVISION_SIDE_TOP, y, TRUE))
  1463.         success = FALSE;
  1464.       else
  1465.         division->ResizeAdjoining(DIVISION_SIDE_TOP, y, FALSE);
  1466.  
  1467.       break;
  1468.     }
  1469.     case DIVISION_SIDE_RIGHT:
  1470.     {
  1471.       if ((x <= x1) || (x >= x2) || (x <= dx1))
  1472.         success = FALSE;
  1473.       else if (!division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, TRUE))
  1474.         success = FALSE;
  1475.       else
  1476.         division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, FALSE);
  1477.  
  1478.       break;
  1479.     }
  1480.     case DIVISION_SIDE_BOTTOM:
  1481.     {
  1482.       if ((y <= y1) || (y >= y2) || (y <= dy1))
  1483.         success = FALSE;
  1484.       else if (!division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, TRUE))
  1485.         success = FALSE;
  1486.       else
  1487.         division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, FALSE);
  1488.  
  1489.       break;
  1490.     }
  1491.   }
  1492.   if (!success)
  1493.   {
  1494.     division->SetSize(originalW, originalH);
  1495.     division->Move(dc, originalX, originalY);
  1496.   }
  1497.   divisionParent->Draw(dc);
  1498.   division->GetEventHandler()->OnDrawControlPoints(dc);
  1499. }
  1500.  
  1501. /* Resize adjoining divisions.
  1502.  *
  1503.    Behaviour should be as follows:
  1504.    If right edge moves, find all objects whose left edge
  1505.    adjoins this object, and move left edge accordingly.
  1506.    If left..., move ... right.
  1507.    If top..., move ... bottom.
  1508.    If bottom..., move top.
  1509.    If size goes to zero or end position is other side of start position,
  1510.    resize to original size and return.
  1511.  */
  1512. bool wxDivisionShape::ResizeAdjoining(int side, double newPos, bool test)
  1513. {
  1514.   wxCompositeShape *divisionParent = (wxCompositeShape *)GetParent();
  1515.   wxNode *node = divisionParent->GetDivisions().First();
  1516.   while (node)
  1517.   {
  1518.     wxDivisionShape *division = (wxDivisionShape *)node->Data();
  1519.     switch (side)
  1520.     {
  1521.       case DIVISION_SIDE_LEFT:
  1522.       {
  1523.         if (division->m_rightSide == this)
  1524.         {
  1525.           bool success = division->AdjustRight(newPos, test);
  1526.           if (!success && test)
  1527.             return FALSE;
  1528.         }
  1529.         break;
  1530.       }
  1531.       case DIVISION_SIDE_TOP:
  1532.       {
  1533.         if (division->m_bottomSide == this)
  1534.         {
  1535.           bool success = division->AdjustBottom(newPos, test);
  1536.           if (!success && test)
  1537.             return FALSE;
  1538.         }
  1539.         break;
  1540.       }
  1541.       case DIVISION_SIDE_RIGHT:
  1542.       {
  1543.         if (division->m_leftSide == this)
  1544.         {
  1545.           bool success = division->AdjustLeft(newPos, test);
  1546.           if (!success && test)
  1547.             return FALSE;
  1548.         }
  1549.         break;
  1550.       }
  1551.       case DIVISION_SIDE_BOTTOM:
  1552.       {
  1553.         if (division->m_topSide == this)
  1554.         {
  1555.           bool success = division->AdjustTop(newPos, test);
  1556.           if (!success && test)
  1557.             return FALSE;
  1558.         }
  1559.         break;
  1560.       }
  1561.       default:
  1562.         break;
  1563.     }
  1564.     node = node->Next();
  1565.   }
  1566.  
  1567.   return TRUE;
  1568. }
  1569.  
  1570. /*
  1571.  * Popup menu for editing divisions
  1572.  *
  1573.  */
  1574. class OGLPopupDivisionMenu : public wxMenu {
  1575. public:
  1576.     OGLPopupDivisionMenu() : wxMenu() {
  1577.         Append(DIVISION_MENU_SPLIT_HORIZONTALLY, wxT("Split horizontally"));
  1578.         Append(DIVISION_MENU_SPLIT_VERTICALLY, wxT("Split vertically"));
  1579.         AppendSeparator();
  1580.         Append(DIVISION_MENU_EDIT_LEFT_EDGE, wxT("Edit left edge"));
  1581.         Append(DIVISION_MENU_EDIT_TOP_EDGE, wxT("Edit top edge"));
  1582.     }
  1583.  
  1584.     void OnMenu(wxCommandEvent& event);
  1585.  
  1586.     DECLARE_EVENT_TABLE()
  1587. };
  1588.  
  1589. BEGIN_EVENT_TABLE(OGLPopupDivisionMenu, wxMenu)
  1590.     EVT_CUSTOM_RANGE(wxEVT_COMMAND_MENU_SELECTED,
  1591.                      DIVISION_MENU_SPLIT_HORIZONTALLY,
  1592.                      DIVISION_MENU_EDIT_BOTTOM_EDGE,
  1593.                      OGLPopupDivisionMenu::OnMenu)
  1594. END_EVENT_TABLE()
  1595.  
  1596.  
  1597. void OGLPopupDivisionMenu::OnMenu(wxCommandEvent& event)
  1598. {
  1599.   wxDivisionShape *division = (wxDivisionShape *)GetClientData();
  1600.   switch (event.GetInt())
  1601.   {
  1602.     case DIVISION_MENU_SPLIT_HORIZONTALLY:
  1603.     {
  1604.       division->Divide(wxHORIZONTAL);
  1605.       break;
  1606.     }
  1607.     case DIVISION_MENU_SPLIT_VERTICALLY:
  1608.     {
  1609.       division->Divide(wxVERTICAL);
  1610.       break;
  1611.     }
  1612.     case DIVISION_MENU_EDIT_LEFT_EDGE:
  1613.     {
  1614.       division->EditEdge(DIVISION_SIDE_LEFT);
  1615.       break;
  1616.     }
  1617.     case DIVISION_MENU_EDIT_TOP_EDGE:
  1618.     {
  1619.       division->EditEdge(DIVISION_SIDE_TOP);
  1620.       break;
  1621.     }
  1622.     default:
  1623.       break;
  1624.   }
  1625. }
  1626.  
  1627. void wxDivisionShape::EditEdge(int side)
  1628. {
  1629.   wxMessageBox(wxT("EditEdge() not implemented"), wxT("OGL"), wxOK);
  1630.  
  1631. #if 0
  1632.   wxBeginBusyCursor();
  1633.  
  1634.   wxPen *currentPen = NULL;
  1635.   char **pColour = NULL;
  1636.   char **pStyle = NULL;
  1637.   if (side == DIVISION_SIDE_LEFT)
  1638.   {
  1639.     currentPen = m_leftSidePen;
  1640.     pColour = &m_leftSideColour;
  1641.     pStyle = &m_leftSideStyle;
  1642.   }
  1643.   else
  1644.   {
  1645.     currentPen = m_topSidePen;
  1646.     pColour = &m_topSideColour;
  1647.     pStyle = &m_topSideStyle;
  1648.   }
  1649.  
  1650.   GraphicsForm *form = new GraphicsForm("Containers");
  1651.   int lineWidth = currentPen->GetWidth();
  1652.  
  1653.   form->Add(wxMakeFormShort("Width", &lineWidth, wxFORM_DEFAULT, NULL, NULL, wxVERTICAL,
  1654.                150));
  1655.   form->Add(wxMakeFormString("Colour", pColour, wxFORM_CHOICE,
  1656.             new wxList(wxMakeConstraintStrings(
  1657.   "BLACK"            ,
  1658.   "BLUE"             ,
  1659.   "BROWN"            ,
  1660.   "CORAL"            ,
  1661.   "CYAN"             ,
  1662.   "DARK GREY"        ,
  1663.   "DARK GREEN"       ,
  1664.   "DIM GREY"         ,
  1665.   "GREY"             ,
  1666.   "GREEN"            ,
  1667.   "LIGHT BLUE"       ,
  1668.   "LIGHT GREY"       ,
  1669.   "MAGENTA"          ,
  1670.   "MAROON"           ,
  1671.   "NAVY"             ,
  1672.   "ORANGE"           ,
  1673.   "PURPLE"           ,
  1674.   "RED"              ,
  1675.   "TURQUOISE"        ,
  1676.   "VIOLET"           ,
  1677.   "WHITE"            ,
  1678.   "YELLOW"           ,
  1679.   NULL),
  1680.   NULL), NULL, wxVERTICAL, 150));
  1681.   form->Add(wxMakeFormString("Style", pStyle, wxFORM_CHOICE,
  1682.             new wxList(wxMakeConstraintStrings(
  1683.   "Solid"            ,
  1684.   "Short Dash"       ,
  1685.   "Long Dash"        ,
  1686.   "Dot"              ,
  1687.   "Dot Dash"         ,
  1688.   NULL),
  1689.   NULL), NULL, wxVERTICAL, 100));
  1690.  
  1691.   wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Division properties", 10, 10, 500, 500);
  1692.   if (GraphicsLabelFont)
  1693.     dialog->SetLabelFont(GraphicsLabelFont);
  1694.   if (GraphicsButtonFont)
  1695.     dialog->SetButtonFont(GraphicsButtonFont);
  1696.  
  1697.   form->AssociatePanel(dialog);
  1698.   form->dialog = dialog;
  1699.  
  1700.   dialog->Fit();
  1701.   dialog->Centre(wxBOTH);
  1702.  
  1703.   wxEndBusyCursor();
  1704.   dialog->Show(TRUE);
  1705.  
  1706.   int lineStyle = wxSOLID;
  1707.   if (*pStyle)
  1708.   {
  1709.     if (strcmp(*pStyle, "Solid") == 0)
  1710.       lineStyle = wxSOLID;
  1711.     else if (strcmp(*pStyle, "Dot") == 0)
  1712.       lineStyle = wxDOT;
  1713.     else if (strcmp(*pStyle, "Short Dash") == 0)
  1714.       lineStyle = wxSHORT_DASH;
  1715.     else if (strcmp(*pStyle, "Long Dash") == 0)
  1716.       lineStyle = wxLONG_DASH;
  1717.     else if (strcmp(*pStyle, "Dot Dash") == 0)
  1718.       lineStyle = wxDOT_DASH;
  1719.   }
  1720.  
  1721.   wxPen *newPen = wxThePenList->FindOrCreatePen(*pColour, lineWidth, lineStyle);
  1722.   if (!pen)
  1723.     pen = wxBLACK_PEN;
  1724.   if (side == DIVISION_SIDE_LEFT)
  1725.     m_leftSidePen = newPen;
  1726.   else
  1727.     m_topSidePen = newPen;
  1728.  
  1729.   // Need to draw whole image again
  1730.   wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
  1731.   compositeParent->Draw(dc);
  1732. #endif
  1733. }
  1734.  
  1735. // Popup menu
  1736. void wxDivisionShape::PopupMenu(double x, double y)
  1737. {
  1738.   wxMenu* oglPopupDivisionMenu = new OGLPopupDivisionMenu;
  1739.  
  1740.   oglPopupDivisionMenu->SetClientData((void *)this);
  1741.   if (m_leftSide)
  1742.     oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, TRUE);
  1743.   else
  1744.     oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, FALSE);
  1745.   if (m_topSide)
  1746.     oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, TRUE);
  1747.   else
  1748.     oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, FALSE);
  1749.  
  1750.   int x1, y1;
  1751.   m_canvas->GetViewStart(&x1, &y1);
  1752.  
  1753.   int unit_x, unit_y;
  1754.   m_canvas->GetScrollPixelsPerUnit(&unit_x, &unit_y);
  1755.  
  1756.   wxClientDC dc(GetCanvas());
  1757.   GetCanvas()->PrepareDC(dc);
  1758.  
  1759.   int mouse_x = (int)(dc.LogicalToDeviceX((long)(x - x1*unit_x)));
  1760.   int mouse_y = (int)(dc.LogicalToDeviceY((long)(y - y1*unit_y)));
  1761.  
  1762.   m_canvas->PopupMenu(oglPopupDivisionMenu, mouse_x, mouse_y);
  1763.   delete oglPopupDivisionMenu;
  1764. }
  1765.  
  1766. void wxDivisionShape::SetLeftSideColour(const wxString& colour)
  1767. {
  1768.   m_leftSideColour = colour;
  1769. }
  1770.  
  1771. void wxDivisionShape::SetTopSideColour(const wxString& colour)
  1772. {
  1773.   m_topSideColour = colour;
  1774. }
  1775.  
  1776. void wxDivisionShape::SetLeftSideStyle(const wxString& style)
  1777. {
  1778.   m_leftSideStyle = style;
  1779. }
  1780.  
  1781. void wxDivisionShape::SetTopSideStyle(const wxString& style)
  1782. {
  1783.   m_topSideStyle = style;
  1784. }
  1785.  
  1786.