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