--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: canvas.cpp
+// Purpose: Shape canvas class
+// Author: Julian Smart
+// Modified by:
+// Created: 12/07/98
+// RCS-ID: $Id$
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "canvas.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include <wx/wxprec.h>
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/wxexpr.h>
+
+#if wxUSE_IOSTREAMH
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "basic.h"
+#include "basicp.h"
+#include "canvas.h"
+#include "ogldiag.h"
+#include "misc.h"
+#include "lines.h"
+#include "composit.h"
+
+#define CONTROL_POINT_SIZE 6
+
+// Control point types
+// Rectangle and most other shapes
+#define CONTROL_POINT_VERTICAL 1
+#define CONTROL_POINT_HORIZONTAL 2
+#define CONTROL_POINT_DIAGONAL 3
+
+// Line
+#define CONTROL_POINT_ENDPOINT_TO 4
+#define CONTROL_POINT_ENDPOINT_FROM 5
+#define CONTROL_POINT_LINE 6
+
+extern wxCursor *g_oglBullseyeCursor;
+
+IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
+
+BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
+ EVT_PAINT(wxShapeCanvas::OnPaint)
+ EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
+END_EVENT_TABLE()
+
+// Object canvas
+wxShapeCanvas::wxShapeCanvas(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style):
+ wxScrolledWindow(parent, id, pos, size, style)
+{
+ m_shapeDiagram = NULL;
+ m_dragState = NoDragging;
+ m_draggedShape = NULL;
+ m_oldDragX = 0;
+ m_oldDragY = 0;
+ m_firstDragX = 0;
+ m_firstDragY = 0;
+ m_checkTolerance = TRUE;
+}
+
+wxShapeCanvas::~wxShapeCanvas()
+{
+}
+
+void wxShapeCanvas::OnPaint(wxPaintEvent& event)
+{
+ wxPaintDC dc(this);
+
+ PrepareDC(dc);
+
+ dc.Clear();
+
+ if (GetDiagram())
+ GetDiagram()->Redraw(dc);
+}
+
+void wxShapeCanvas::OnMouseEvent(wxMouseEvent& event)
+{
+ wxClientDC dc(this);
+ PrepareDC(dc);
+
+ wxPoint logPos(event.GetLogicalPosition(dc));
+
+ double x, y;
+ x = (double) logPos.x;
+ y = (double) logPos.y;
+
+ int keys = 0;
+ if (event.ShiftDown())
+ keys = keys | KEY_SHIFT;
+ if (event.ControlDown())
+ keys = keys | KEY_CTRL;
+
+ bool dragging = event.Dragging();
+
+ // Check if we're within the tolerance for mouse movements.
+ // If we're very close to the position we started dragging
+ // from, this may not be an intentional drag at all.
+ if (dragging)
+ {
+ int dx = abs(dc.LogicalToDeviceX((long) (x - m_firstDragX)));
+ int dy = abs(dc.LogicalToDeviceY((long) (y - m_firstDragY)));
+ if (m_checkTolerance && (dx <= GetDiagram()->GetMouseTolerance()) && (dy <= GetDiagram()->GetMouseTolerance()))
+ {
+ return;
+ }
+ else
+ // If we've ignored the tolerance once, then ALWAYS ignore
+ // tolerance in this drag, even if we come back within
+ // the tolerance range.
+ m_checkTolerance = FALSE;
+ }
+
+ // Dragging - note that the effect of dragging is left entirely up
+ // to the object, so no movement is done unless explicitly done by
+ // object.
+ if (dragging && m_draggedShape && m_dragState == StartDraggingLeft)
+ {
+ m_dragState = ContinueDraggingLeft;
+
+ // If the object isn't m_draggable, transfer message to canvas
+ if (m_draggedShape->Draggable())
+ m_draggedShape->GetEventHandler()->OnBeginDragLeft((double)x, (double)y, keys, m_draggedAttachment);
+ else
+ {
+ m_draggedShape = NULL;
+ OnBeginDragLeft((double)x, (double)y, keys);
+ }
+
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (dragging && m_draggedShape && m_dragState == ContinueDraggingLeft)
+ {
+ // Continue dragging
+ m_draggedShape->GetEventHandler()->OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+ m_draggedShape->GetEventHandler()->OnDragLeft(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (event.LeftUp() && m_draggedShape && m_dragState == ContinueDraggingLeft)
+ {
+ m_dragState = NoDragging;
+ m_checkTolerance = TRUE;
+
+ m_draggedShape->GetEventHandler()->OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+
+ m_draggedShape->GetEventHandler()->OnEndDragLeft((double)x, (double)y, keys, m_draggedAttachment);
+ m_draggedShape = NULL;
+ }
+ else if (dragging && m_draggedShape && m_dragState == StartDraggingRight)
+ {
+ m_dragState = ContinueDraggingRight;
+
+ if (m_draggedShape->Draggable())
+ m_draggedShape->GetEventHandler()->OnBeginDragRight((double)x, (double)y, keys, m_draggedAttachment);
+ else
+ {
+ m_draggedShape = NULL;
+ OnBeginDragRight((double)x, (double)y, keys);
+ }
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (dragging && m_draggedShape && m_dragState == ContinueDraggingRight)
+ {
+ // Continue dragging
+ m_draggedShape->GetEventHandler()->OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+ m_draggedShape->GetEventHandler()->OnDragRight(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (event.RightUp() && m_draggedShape && m_dragState == ContinueDraggingRight)
+ {
+ m_dragState = NoDragging;
+ m_checkTolerance = TRUE;
+
+ m_draggedShape->GetEventHandler()->OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
+
+ m_draggedShape->GetEventHandler()->OnEndDragRight((double)x, (double)y, keys, m_draggedAttachment);
+ m_draggedShape = NULL;
+ }
+
+ // All following events sent to canvas, not object
+ else if (dragging && !m_draggedShape && m_dragState == StartDraggingLeft)
+ {
+ m_dragState = ContinueDraggingLeft;
+ OnBeginDragLeft((double)x, (double)y, keys);
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingLeft)
+ {
+ // Continue dragging
+ OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys);
+ OnDragLeft(TRUE, (double)x, (double)y, keys);
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (event.LeftUp() && !m_draggedShape && m_dragState == ContinueDraggingLeft)
+ {
+ m_dragState = NoDragging;
+ m_checkTolerance = TRUE;
+
+ OnDragLeft(FALSE, m_oldDragX, m_oldDragY, keys);
+ OnEndDragLeft((double)x, (double)y, keys);
+ m_draggedShape = NULL;
+ }
+ else if (dragging && !m_draggedShape && m_dragState == StartDraggingRight)
+ {
+ m_dragState = ContinueDraggingRight;
+ OnBeginDragRight((double)x, (double)y, keys);
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingRight)
+ {
+ // Continue dragging
+ OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys);
+ OnDragRight(TRUE, (double)x, (double)y, keys);
+ m_oldDragX = x; m_oldDragY = y;
+ }
+ else if (event.RightUp() && !m_draggedShape && m_dragState == ContinueDraggingRight)
+ {
+ m_dragState = NoDragging;
+ m_checkTolerance = TRUE;
+
+ OnDragRight(FALSE, m_oldDragX, m_oldDragY, keys);
+ OnEndDragRight((double)x, (double)y, keys);
+ m_draggedShape = NULL;
+ }
+
+ // Non-dragging events
+ else if (event.IsButton())
+ {
+ m_checkTolerance = TRUE;
+
+ // Find the nearest object
+ int attachment = 0;
+ wxShape *nearest_object = FindShape(x, y, &attachment);
+ if (nearest_object) // Object event
+ {
+ if (event.LeftDown())
+ {
+ m_draggedShape = nearest_object;
+ m_draggedAttachment = attachment;
+ m_dragState = StartDraggingLeft;
+ m_firstDragX = x;
+ m_firstDragY = y;
+ }
+ else if (event.LeftUp())
+ {
+ // N.B. Only register a click if the same object was
+ // identified for down *and* up.
+ if (nearest_object == m_draggedShape)
+ nearest_object->GetEventHandler()->OnLeftClick((double)x, (double)y, keys, attachment);
+
+ m_draggedShape = NULL;
+ m_dragState = NoDragging;
+ }
+ else if (event.LeftDClick())
+ {
+ nearest_object->GetEventHandler()->OnLeftDoubleClick((double)x, (double)y, keys, attachment);
+
+ m_draggedShape = NULL;
+ m_dragState = NoDragging;
+ }
+ else if (event.RightDown())
+ {
+ m_draggedShape = nearest_object;
+ m_draggedAttachment = attachment;
+ m_dragState = StartDraggingRight;
+ m_firstDragX = x;
+ m_firstDragY = y;
+ }
+ else if (event.RightUp())
+ {
+ if (nearest_object == m_draggedShape)
+ nearest_object->GetEventHandler()->OnRightClick((double)x, (double)y, keys, attachment);
+
+ m_draggedShape = NULL;
+ m_dragState = NoDragging;
+ }
+ }
+ else // Canvas event (no nearest object)
+ {
+ if (event.LeftDown())
+ {
+ m_draggedShape = NULL;
+ m_dragState = StartDraggingLeft;
+ m_firstDragX = x;
+ m_firstDragY = y;
+ }
+ else if (event.LeftUp())
+ {
+ OnLeftClick((double)x, (double)y, keys);
+
+ m_draggedShape = NULL;
+ m_dragState = NoDragging;
+ }
+ else if (event.RightDown())
+ {
+ m_draggedShape = NULL;
+ m_dragState = StartDraggingRight;
+ m_firstDragX = x;
+ m_firstDragY = y;
+ }
+ else if (event.RightUp())
+ {
+ OnRightClick((double)x, (double)y, keys);
+
+ m_draggedShape = NULL;
+ m_dragState = NoDragging;
+ }
+ }
+ }
+}
+
+/*
+ * Try to find a sensitive object, working up the hierarchy of composites.
+ *
+ */
+wxShape *wxShapeCanvas::FindFirstSensitiveShape(double x, double y, int *new_attachment, int op)
+{
+ wxShape *image = FindShape(x, y, new_attachment);
+ if (!image) return NULL;
+
+ wxShape *actualImage = FindFirstSensitiveShape1(image, op);
+ if (actualImage)
+ {
+ double dist;
+ // Find actual attachment
+ actualImage->HitTest(x, y, new_attachment, &dist);
+ }
+ return actualImage;
+}
+
+wxShape *wxShapeCanvas::FindFirstSensitiveShape1(wxShape *image, int op)
+{
+ if (image->GetSensitivityFilter() & op)
+ return image;
+ if (image->GetParent())
+ return FindFirstSensitiveShape1(image->GetParent(), op);
+ return NULL;
+}
+
+// Helper function: TRUE if 'contains' wholly contains 'contained'.
+static bool WhollyContains(wxShape *contains, wxShape *contained)
+{
+ double xp1, yp1, xp2, yp2;
+ double w1, h1, w2, h2;
+ double left1, top1, right1, bottom1, left2, top2, right2, bottom2;
+
+ xp1 = contains->GetX(); yp1 = contains->GetY(); xp2 = contained->GetX(); yp2 = contained->GetY();
+ contains->GetBoundingBoxMax(&w1, &h1);
+ contained->GetBoundingBoxMax(&w2, &h2);
+
+ left1 = (double)(xp1 - (w1 / 2.0));
+ top1 = (double)(yp1 - (h1 / 2.0));
+ right1 = (double)(xp1 + (w1 / 2.0));
+ bottom1 = (double)(yp1 + (h1 / 2.0));
+
+ left2 = (double)(xp2 - (w2 / 2.0));
+ top2 = (double)(yp2 - (h2 / 2.0));
+ right2 = (double)(xp2 + (w2 / 2.0));
+ bottom2 = (double)(yp2 + (h2 / 2.0));
+
+ return ((left1 <= left2) && (top1 <= top2) && (right1 >= right2) && (bottom1 >= bottom2));
+}
+
+wxShape *wxShapeCanvas::FindShape(double x, double y, int *attachment, wxClassInfo *info, wxShape *notObject)
+{
+ double nearest = 100000.0;
+ int nearest_attachment = 0;
+ wxShape *nearest_object = NULL;
+
+ // Go backward through the object list, since we want:
+ // (a) to have the control points drawn LAST to overlay
+ // the other objects
+ // (b) to find the control points FIRST if they exist
+
+ wxNode *current = GetDiagram()->GetShapeList()->Last();
+ while (current)
+ {
+ wxShape *object = (wxShape *)current->Data();
+
+ double dist;
+ int temp_attachment;
+
+ // First pass for lines, which might be inside a container, so we
+ // want lines to take priority over containers. This first loop
+ // could fail if we clickout side a line, so then we'll
+ // try other shapes.
+ if (object->IsShown() &&
+ object->IsKindOf(CLASSINFO(wxLineShape)) &&
+ object->HitTest(x, y, &temp_attachment, &dist) &&
+ ((info == NULL) || object->IsKindOf(info)) &&
+ (!notObject || !notObject->HasDescendant(object)))
+ {
+ // A line is trickier to spot than a normal object.
+ // For a line, since it's the diagonal of the box
+ // we use for the hit test, we may have several
+ // lines in the box and therefore we need to be able
+ // to specify the nearest point to the centre of the line
+ // as our hit criterion, to give the user some room for
+ // manouevre.
+ if (dist < nearest)
+ {
+ nearest = dist;
+ nearest_object = object;
+ nearest_attachment = temp_attachment;
+ }
+ }
+ if (current)
+ current = current->Previous();
+ }
+
+ current = GetDiagram()->GetShapeList()->Last();
+ while (current)
+ {
+ wxShape *object = (wxShape *)current->Data();
+ double dist;
+ int temp_attachment;
+
+ // On second pass, only ever consider non-composites or divisions. If children want to pass
+ // up control to the composite, that's up to them.
+ if (object->IsShown() && (object->IsKindOf(CLASSINFO(wxDivisionShape)) || !object->IsKindOf(CLASSINFO(wxCompositeShape)))
+ && object->HitTest(x, y, &temp_attachment, &dist) && ((info == NULL) || object->IsKindOf(info)) &&
+ (!notObject || !notObject->HasDescendant(object)))
+ {
+ if (!object->IsKindOf(CLASSINFO(wxLineShape)))
+ {
+ // If we've hit a container, and we have already found a line in the
+ // first pass, then ignore the container in case the line is in the container.
+ // Check for division in case line straddles divisions (i.e. is not wholly contained).
+ if (!nearest_object || !(object->IsKindOf(CLASSINFO(wxDivisionShape)) || WhollyContains(object, nearest_object)))
+ {
+ nearest = dist;
+ nearest_object = object;
+ nearest_attachment = temp_attachment;
+ current = NULL;
+ }
+ }
+ }
+ if (current)
+ current = current->Previous();
+ }
+
+ *attachment = nearest_attachment;
+ return nearest_object;
+}
+
+/*
+ * Higher-level events called by OnEvent
+ *
+ */
+
+void wxShapeCanvas::OnLeftClick(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnRightClick(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnDragLeft(bool draw, double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnBeginDragLeft(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnEndDragLeft(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnDragRight(bool draw, double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnBeginDragRight(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::OnEndDragRight(double x, double y, int keys)
+{
+}
+
+void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
+ { GetDiagram()->AddShape(object, addAfter); }
+void wxShapeCanvas::InsertShape(wxShape *object)
+ { GetDiagram()->InsertShape(object); }
+void wxShapeCanvas::RemoveShape(wxShape *object)
+ { GetDiagram()->RemoveShape(object); }
+bool wxShapeCanvas::GetQuickEditMode()
+ { return GetDiagram()->GetQuickEditMode(); }
+void wxShapeCanvas::Redraw(wxDC& dc)
+ { GetDiagram()->Redraw(dc); }
+void wxShapeCanvas::Snap(double *x, double *y)
+ { GetDiagram()->Snap(x, y); }