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