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