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 / canvas.cpp < prev    next >
C/C++ Source or Header  |  2002-12-28  |  16KB  |  520 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        canvas.cpp
  3. // Purpose:     Shape canvas class
  4. // Author:      Julian Smart
  5. // Modified by:
  6. // Created:     12/07/98
  7. // RCS-ID:      $Id: canvas.cpp,v 1.6.2.1 2002/12/28 18:32:07 JS Exp $
  8. // Copyright:   (c) Julian Smart
  9. // Licence:       wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. #ifdef __GNUG__
  13. #pragma implementation "canvas.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. #ifdef new
  30. #undef new
  31. #endif
  32.  
  33. #include <ctype.h>
  34. #include <math.h>
  35. #include <stdlib.h>
  36.  
  37. #include <wx/ogl/basic.h>
  38. #include <wx/ogl/basicp.h>
  39. #include <wx/ogl/canvas.h>
  40. #include <wx/ogl/ogldiag.h>
  41. #include <wx/ogl/misc.h>
  42. #include <wx/ogl/lines.h>
  43. #include <wx/ogl/composit.h>
  44.  
  45. #define CONTROL_POINT_SIZE       6
  46.  
  47. // Control point types
  48. // Rectangle and most other shapes
  49. #define CONTROL_POINT_VERTICAL   1
  50. #define CONTROL_POINT_HORIZONTAL 2
  51. #define CONTROL_POINT_DIAGONAL   3
  52.  
  53. // Line
  54. #define CONTROL_POINT_ENDPOINT_TO 4
  55. #define CONTROL_POINT_ENDPOINT_FROM 5
  56. #define CONTROL_POINT_LINE       6
  57.  
  58. IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
  59.  
  60. BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
  61.     EVT_PAINT(wxShapeCanvas::OnPaint)
  62.     EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
  63. END_EVENT_TABLE()
  64.  
  65. wxChar* wxShapeCanvasNameStr = wxT("shapeCanvas");
  66.  
  67. // Object canvas
  68. wxShapeCanvas::wxShapeCanvas(wxWindow *parent, wxWindowID id,
  69.                              const wxPoint& pos,
  70.                              const wxSize& size,
  71.                              long style,
  72.                              const wxString& name):
  73.   wxScrolledWindow(parent, id, pos, size, style, name)
  74. {
  75.   m_shapeDiagram = NULL;
  76.   m_dragState = NoDragging;
  77.   m_draggedShape = NULL;
  78.   m_oldDragX = 0;
  79.   m_oldDragY = 0;
  80.   m_firstDragX = 0;
  81.   m_firstDragY = 0;
  82.   m_checkTolerance = TRUE;
  83. }
  84.  
  85. wxShapeCanvas::~wxShapeCanvas()
  86. {
  87. }
  88.  
  89. void wxShapeCanvas::OnPaint(wxPaintEvent& event)
  90. {
  91.     wxPaintDC dc(this);
  92.  
  93.     PrepareDC(dc);
  94.  
  95.     dc.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
  96.     dc.Clear();
  97.  
  98.     if (GetDiagram())
  99.         GetDiagram()->Redraw(dc);
  100. }
  101.  
  102. void wxShapeCanvas::OnMouseEvent(wxMouseEvent& event)
  103. {
  104.   wxClientDC dc(this);
  105.   PrepareDC(dc);
  106.  
  107.   wxPoint logPos(event.GetLogicalPosition(dc));
  108.  
  109.   double x, y;
  110.   x = (double) logPos.x;
  111.   y = (double) logPos.y;
  112.  
  113.   int keys = 0;
  114.   if (event.ShiftDown())
  115.     keys = keys | KEY_SHIFT;
  116.   if (event.ControlDown())
  117.     keys = keys | KEY_CTRL;
  118.  
  119.   bool dragging = event.Dragging();
  120.  
  121.   // Check if we're within the tolerance for mouse movements.
  122.   // If we're very close to the position we started dragging
  123.   // from, this may not be an intentional drag at all.
  124.   if (dragging)
  125.   {
  126.     int dx = abs(dc.LogicalToDeviceX((long) (x - m_firstDragX)));
  127.     int dy = abs(dc.LogicalToDeviceY((long) (y - m_firstDragY)));
  128.     if (m_checkTolerance && (dx <= GetDiagram()->GetMouseTolerance()) && (dy <= GetDiagram()->GetMouseTolerance()))
  129.     {
  130.       return;
  131.     }
  132.     else
  133.       // If we've ignored the tolerance once, then ALWAYS ignore
  134.       // tolerance in this drag, even if we come back within
  135.       // the tolerance range.
  136.       m_checkTolerance = FALSE;
  137.   }
  138.  
  139.   // Dragging - note that the effect of dragging is left entirely up
  140.   // to the object, so no movement is done unless explicitly done by
  141.   // object.
  142.   if (dragging && m_draggedShape && m_dragState == StartDraggingLeft)
  143.   {
  144.     m_dragState = ContinueDraggingLeft;
  145.  
  146.     // If the object isn't m_draggable, transfer message to canvas
  147.     if (m_draggedShape->Draggable())
  148.       m_draggedShape->GetEventHandler()->OnBeginDragLeft((double)x, (double)y, keys, m_draggedAttachment);
  149.     else
  150.     {
  151.       m_draggedShape = NULL;
  152.       OnBeginDragLeft((double)x, (double)y, keys);
  153.     }
  154.  
  155.     m_oldDragX = x; m_oldDragY = y;
  156.   }
  157.   else if (dragging && m_draggedShape && m_dragState == ContinueDraggingLeft)
  158.   {
  159.     // Continue dragging
  160.     m_draggedShape->GetEventHandler()->OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
  161.     m_draggedShape->GetEventHandler()->OnDragLeft(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
  162.     m_oldDragX = x; m_oldDragY = y;
  163.   }
  164.   else if (event.LeftUp() && m_draggedShape && m_dragState == ContinueDraggingLeft)
  165.   {
  166.     m_dragState = NoDragging;
  167.     m_checkTolerance = TRUE;
  168.  
  169.     m_draggedShape->GetEventHandler()->OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
  170.  
  171.     m_draggedShape->GetEventHandler()->OnEndDragLeft((double)x, (double)y, keys, m_draggedAttachment);
  172.     m_draggedShape = NULL;
  173.   }
  174.   else if (dragging && m_draggedShape && m_dragState == StartDraggingRight)
  175.   {
  176.     m_dragState = ContinueDraggingRight;
  177.  
  178.     if (m_draggedShape->Draggable())
  179.       m_draggedShape->GetEventHandler()->OnBeginDragRight((double)x, (double)y, keys, m_draggedAttachment);
  180.     else
  181.     {
  182.       m_draggedShape = NULL;
  183.       OnBeginDragRight((double)x, (double)y, keys);
  184.     }
  185.     m_oldDragX = x; m_oldDragY = y;
  186.   }
  187.   else if (dragging && m_draggedShape && m_dragState == ContinueDraggingRight)
  188.   {
  189.     // Continue dragging
  190.     m_draggedShape->GetEventHandler()->OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
  191.     m_draggedShape->GetEventHandler()->OnDragRight(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
  192.     m_oldDragX = x; m_oldDragY = y;
  193.   }
  194.   else if (event.RightUp() && m_draggedShape && m_dragState == ContinueDraggingRight)
  195.   {
  196.     m_dragState = NoDragging;
  197.     m_checkTolerance = TRUE;
  198.  
  199.     m_draggedShape->GetEventHandler()->OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
  200.  
  201.     m_draggedShape->GetEventHandler()->OnEndDragRight((double)x, (double)y, keys, m_draggedAttachment);
  202.     m_draggedShape = NULL;
  203.   }
  204.  
  205.   // All following events sent to canvas, not object
  206.   else if (dragging && !m_draggedShape && m_dragState == StartDraggingLeft)
  207.   {
  208.     m_dragState = ContinueDraggingLeft;
  209.     OnBeginDragLeft((double)x, (double)y, keys);
  210.     m_oldDragX = x; m_oldDragY = y;
  211.   }
  212.   else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingLeft)
  213.   {
  214.     // Continue dragging
  215.     OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys);
  216.     OnDragLeft(TRUE, (double)x, (double)y, keys);
  217.     m_oldDragX = x; m_oldDragY = y;
  218.   }
  219.   else if (event.LeftUp() && !m_draggedShape && m_dragState == ContinueDraggingLeft)
  220.   {
  221.     m_dragState = NoDragging;
  222.     m_checkTolerance = TRUE;
  223.  
  224.     OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys);
  225.     OnEndDragLeft((double)x, (double)y, keys);
  226.     m_draggedShape = NULL;
  227.   }
  228.   else if (dragging && !m_draggedShape && m_dragState == StartDraggingRight)
  229.   {
  230.     m_dragState = ContinueDraggingRight;
  231.     OnBeginDragRight((double)x, (double)y, keys);
  232.     m_oldDragX = x; m_oldDragY = y;
  233.   }
  234.   else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingRight)
  235.   {
  236.     // Continue dragging
  237.     OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys);
  238.     OnDragRight(TRUE, (double)x, (double)y, keys);
  239.     m_oldDragX = x; m_oldDragY = y;
  240.   }
  241.   else if (event.RightUp() && !m_draggedShape && m_dragState == ContinueDraggingRight)
  242.   {
  243.     m_dragState = NoDragging;
  244.     m_checkTolerance = TRUE;
  245.  
  246.     OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys);
  247.     OnEndDragRight((double)x, (double)y, keys);
  248.     m_draggedShape = NULL;
  249.   }
  250.  
  251.   // Non-dragging events
  252.   else if (event.IsButton())
  253.   {
  254.     m_checkTolerance = TRUE;
  255.  
  256.     // Find the nearest object
  257.     int attachment = 0;
  258.     wxShape *nearest_object = FindShape(x, y, &attachment);
  259.     if (nearest_object) // Object event
  260.     {
  261.       if (event.LeftDown())
  262.       {
  263.         m_draggedShape = nearest_object;
  264.         m_draggedAttachment = attachment;
  265.         m_dragState = StartDraggingLeft;
  266.         m_firstDragX = x;
  267.         m_firstDragY = y;
  268.       }
  269.       else if (event.LeftUp())
  270.       {
  271.         // N.B. Only register a click if the same object was
  272.         // identified for down *and* up.
  273.         if (nearest_object == m_draggedShape)
  274.           nearest_object->GetEventHandler()->OnLeftClick((double)x, (double)y, keys, attachment);
  275.  
  276.         m_draggedShape = NULL;
  277.         m_dragState = NoDragging;
  278.       }
  279.       else if (event.LeftDClick())
  280.       {
  281.         nearest_object->GetEventHandler()->OnLeftDoubleClick((double)x, (double)y, keys, attachment);
  282.  
  283.         m_draggedShape = NULL;
  284.         m_dragState = NoDragging;
  285.       }
  286.       else if (event.RightDown())
  287.       {
  288.         m_draggedShape = nearest_object;
  289.         m_draggedAttachment = attachment;
  290.         m_dragState = StartDraggingRight;
  291.         m_firstDragX = x;
  292.         m_firstDragY = y;
  293.       }
  294.       else if (event.RightUp())
  295.       {
  296.         if (nearest_object == m_draggedShape)
  297.           nearest_object->GetEventHandler()->OnRightClick((double)x, (double)y, keys, attachment);
  298.  
  299.         m_draggedShape = NULL;
  300.         m_dragState = NoDragging;
  301.       }
  302.     }
  303.     else // Canvas event (no nearest object)
  304.     {
  305.       if (event.LeftDown())
  306.       {
  307.         m_draggedShape = NULL;
  308.         m_dragState = StartDraggingLeft;
  309.         m_firstDragX = x;
  310.         m_firstDragY = y;
  311.       }
  312.       else if (event.LeftUp())
  313.       {
  314.         OnLeftClick((double)x, (double)y, keys);
  315.  
  316.         m_draggedShape = NULL;
  317.         m_dragState = NoDragging;
  318.       }
  319.       else if (event.RightDown())
  320.       {
  321.         m_draggedShape = NULL;
  322.         m_dragState = StartDraggingRight;
  323.         m_firstDragX = x;
  324.         m_firstDragY = y;
  325.       }
  326.       else if (event.RightUp())
  327.       {
  328.         OnRightClick((double)x, (double)y, keys);
  329.  
  330.         m_draggedShape = NULL;
  331.         m_dragState = NoDragging;
  332.       }
  333.     }
  334.   }
  335. }
  336.  
  337. /*
  338.  * Try to find a sensitive object, working up the hierarchy of composites.
  339.  *
  340.  */
  341. wxShape *wxShapeCanvas::FindFirstSensitiveShape(double x, double y, int *new_attachment, int op)
  342. {
  343.   wxShape *image = FindShape(x, y, new_attachment);
  344.   if (!image) return NULL;
  345.  
  346.   wxShape *actualImage = FindFirstSensitiveShape1(image, op);
  347.   if (actualImage)
  348.   {
  349.     double dist;
  350.     // Find actual attachment
  351.     actualImage->HitTest(x, y, new_attachment, &dist);
  352.   }
  353.   return actualImage;
  354. }
  355.  
  356. wxShape *wxShapeCanvas::FindFirstSensitiveShape1(wxShape *image, int op)
  357. {
  358.   if (image->GetSensitivityFilter() & op)
  359.     return image;
  360.   if (image->GetParent())
  361.     return FindFirstSensitiveShape1(image->GetParent(), op);
  362.   return NULL;
  363. }
  364.  
  365. // Helper function: TRUE if 'contains' wholly contains 'contained'.
  366. static bool WhollyContains(wxShape *contains, wxShape *contained)
  367. {
  368.   double xp1, yp1, xp2, yp2;
  369.   double w1, h1, w2, h2;
  370.   double left1, top1, right1, bottom1, left2, top2, right2, bottom2;
  371.  
  372.   xp1 = contains->GetX(); yp1 = contains->GetY(); xp2 = contained->GetX(); yp2 = contained->GetY();
  373.   contains->GetBoundingBoxMax(&w1, &h1);
  374.   contained->GetBoundingBoxMax(&w2, &h2);
  375.  
  376.   left1 = (double)(xp1 - (w1 / 2.0));
  377.   top1 = (double)(yp1 - (h1 / 2.0));
  378.   right1 = (double)(xp1 + (w1 / 2.0));
  379.   bottom1 = (double)(yp1 + (h1 / 2.0));
  380.  
  381.   left2 = (double)(xp2 - (w2 / 2.0));
  382.   top2 = (double)(yp2 - (h2 / 2.0));
  383.   right2 = (double)(xp2 + (w2 / 2.0));
  384.   bottom2 = (double)(yp2 + (h2 / 2.0));
  385.  
  386.   return ((left1 <= left2) && (top1 <= top2) && (right1 >= right2) && (bottom1 >= bottom2));
  387. }
  388.  
  389. wxShape *wxShapeCanvas::FindShape(double x, double y, int *attachment, wxClassInfo *info, wxShape *notObject)
  390. {
  391.   double nearest = 100000.0;
  392.   int nearest_attachment = 0;
  393.   wxShape *nearest_object = NULL;
  394.  
  395.   // Go backward through the object list, since we want:
  396.   // (a) to have the control points drawn LAST to overlay
  397.   //     the other objects
  398.   // (b) to find the control points FIRST if they exist
  399.  
  400.   wxNode *current = GetDiagram()->GetShapeList()->Last();
  401.   while (current)
  402.   {
  403.     wxShape *object = (wxShape *)current->Data();
  404.  
  405.     double dist;
  406.     int temp_attachment;
  407.  
  408.     // First pass for lines, which might be inside a container, so we
  409.     // want lines to take priority over containers. This first loop
  410.     // could fail if we clickout side a line, so then we'll
  411.     // try other shapes.
  412.     if (object->IsShown() &&
  413.         object->IsKindOf(CLASSINFO(wxLineShape)) &&
  414.         object->HitTest(x, y, &temp_attachment, &dist) &&
  415.         ((info == NULL) || object->IsKindOf(info)) &&
  416.         (!notObject || !notObject->HasDescendant(object)))
  417.     {
  418.       // A line is trickier to spot than a normal object.
  419.       // For a line, since it's the diagonal of the box
  420.       // we use for the hit test, we may have several
  421.       // lines in the box and therefore we need to be able
  422.       // to specify the nearest point to the centre of the line
  423.       // as our hit criterion, to give the user some room for
  424.       // manouevre.
  425.       if (dist < nearest)
  426.       {
  427.         nearest = dist;
  428.         nearest_object = object;
  429.         nearest_attachment = temp_attachment;
  430.       }
  431.     }
  432.     if (current)
  433.       current = current->Previous();
  434.   }
  435.  
  436.   current = GetDiagram()->GetShapeList()->Last();
  437.   while (current)
  438.   {
  439.     wxShape *object = (wxShape *)current->Data();
  440.     double dist;
  441.     int temp_attachment;
  442.  
  443.     // On second pass, only ever consider non-composites or divisions. If children want to pass
  444.     // up control to the composite, that's up to them.
  445.     if (object->IsShown() && (object->IsKindOf(CLASSINFO(wxDivisionShape)) || !object->IsKindOf(CLASSINFO(wxCompositeShape)))
  446.         && object->HitTest(x, y, &temp_attachment, &dist) && ((info == NULL) || object->IsKindOf(info)) &&
  447.         (!notObject || !notObject->HasDescendant(object)))
  448.     {
  449.       if (!object->IsKindOf(CLASSINFO(wxLineShape)))
  450.       {
  451.         // If we've hit a container, and we have already found a line in the
  452.         // first pass, then ignore the container in case the line is in the container.
  453.         // Check for division in case line straddles divisions (i.e. is not wholly contained).
  454.         if (!nearest_object || !(object->IsKindOf(CLASSINFO(wxDivisionShape)) || WhollyContains(object, nearest_object)))
  455.         {
  456.           nearest = dist;
  457.           nearest_object = object;
  458.           nearest_attachment = temp_attachment;
  459.           current = NULL;
  460.         }
  461.       }
  462.     }
  463.     if (current)
  464.       current = current->Previous();
  465.   }
  466.  
  467.   *attachment = nearest_attachment;
  468.   return nearest_object;
  469. }
  470.  
  471. /*
  472.  * Higher-level events called by OnEvent
  473.  *
  474.  */
  475.  
  476. void wxShapeCanvas::OnLeftClick(double x, double y, int keys)
  477. {
  478. }
  479.  
  480. void wxShapeCanvas::OnRightClick(double x, double y, int keys)
  481. {
  482. }
  483.  
  484. void wxShapeCanvas::OnDragLeft(bool draw, double x, double y, int keys)
  485. {
  486. }
  487.  
  488. void wxShapeCanvas::OnBeginDragLeft(double x, double y, int keys)
  489. {
  490. }
  491.  
  492. void wxShapeCanvas::OnEndDragLeft(double x, double y, int keys)
  493. {
  494. }
  495.  
  496. void wxShapeCanvas::OnDragRight(bool draw, double x, double y, int keys)
  497. {
  498. }
  499.  
  500. void wxShapeCanvas::OnBeginDragRight(double x, double y, int keys)
  501. {
  502. }
  503.  
  504. void wxShapeCanvas::OnEndDragRight(double x, double y, int keys)
  505. {
  506. }
  507.  
  508. void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
  509.  { GetDiagram()->AddShape(object, addAfter); }
  510. void wxShapeCanvas::InsertShape(wxShape *object)
  511.  { GetDiagram()->InsertShape(object); }
  512. void wxShapeCanvas::RemoveShape(wxShape *object)
  513.  { GetDiagram()->RemoveShape(object); }
  514. bool wxShapeCanvas::GetQuickEditMode()
  515.  { return GetDiagram()->GetQuickEditMode(); }
  516. void wxShapeCanvas::Redraw(wxDC& dc)
  517.  { GetDiagram()->Redraw(dc); }
  518. void wxShapeCanvas::Snap(double *x, double *y)
  519.  { GetDiagram()->Snap(x, y); }
  520.