]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/ogl/canvas.cpp
Needed to add #include "wx/statusbr.h" to know that wxStatusBar is derived
[wxWidgets.git] / contrib / src / ogl / canvas.cpp
CommitLineData
1fc25a89
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
2ba06d5a 9// Licence: wxWindows licence
1fc25a89
JS
10/////////////////////////////////////////////////////////////////////////////
11
1fc25a89 12// For compilers that support precompilation, includes "wx.h".
92a19c2e 13#include "wx/wxprec.h"
1fc25a89
JS
14
15#ifdef __BORLANDC__
16#pragma hdrstop
17#endif
18
19#ifndef WX_PRECOMP
20#include <wx/wx.h>
21#endif
22
5f331691 23#if wxUSE_PROLOGIO
7c9955d1 24#include <wx/deprecated/wxexpr.h>
fd657b8a 25#endif
1fc25a89 26
3f1802b5
JS
27#ifdef new
28#undef new
29#endif
30
1fc25a89
JS
31#include <ctype.h>
32#include <math.h>
33#include <stdlib.h>
34
5f331691 35#include "wx/ogl/ogl.h"
1fc25a89
JS
36
37#define CONTROL_POINT_SIZE 6
38
39// Control point types
40// Rectangle and most other shapes
41#define CONTROL_POINT_VERTICAL 1
42#define CONTROL_POINT_HORIZONTAL 2
43#define CONTROL_POINT_DIAGONAL 3
44
45// Line
46#define CONTROL_POINT_ENDPOINT_TO 4
47#define CONTROL_POINT_ENDPOINT_FROM 5
48#define CONTROL_POINT_LINE 6
49
1fc25a89
JS
50IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
51
52BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
53 EVT_PAINT(wxShapeCanvas::OnPaint)
54 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
55END_EVENT_TABLE()
56
0a2c0398 57const wxChar* wxShapeCanvasNameStr = wxT("shapeCanvas");
4fcf77bc 58
1fc25a89 59// Object canvas
4fcf77bc
RD
60wxShapeCanvas::wxShapeCanvas(wxWindow *parent, wxWindowID id,
61 const wxPoint& pos,
62 const wxSize& size,
63 long style,
64 const wxString& name):
65 wxScrolledWindow(parent, id, pos, size, style, name)
1fc25a89
JS
66{
67 m_shapeDiagram = NULL;
68 m_dragState = NoDragging;
69 m_draggedShape = NULL;
70 m_oldDragX = 0;
71 m_oldDragY = 0;
72 m_firstDragX = 0;
73 m_firstDragY = 0;
2ba06d5a 74 m_checkTolerance = true;
1fc25a89
JS
75}
76
77wxShapeCanvas::~wxShapeCanvas()
78{
79}
80
1484b5cc 81void wxShapeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
1fc25a89
JS
82{
83 wxPaintDC dc(this);
84
85 PrepareDC(dc);
86
ebe25075 87 dc.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
1fc25a89
JS
88 dc.Clear();
89
ebe25075
RD
90 if (GetDiagram())
91 GetDiagram()->Redraw(dc);
1fc25a89
JS
92}
93
94void wxShapeCanvas::OnMouseEvent(wxMouseEvent& event)
95{
96 wxClientDC dc(this);
97 PrepareDC(dc);
98
99 wxPoint logPos(event.GetLogicalPosition(dc));
100
101 double x, y;
102 x = (double) logPos.x;
103 y = (double) logPos.y;
104
105 int keys = 0;
106 if (event.ShiftDown())
107 keys = keys | KEY_SHIFT;
108 if (event.ControlDown())
109 keys = keys | KEY_CTRL;
110
111 bool dragging = event.Dragging();
112
113 // Check if we're within the tolerance for mouse movements.
114 // If we're very close to the position we started dragging
115 // from, this may not be an intentional drag at all.
116 if (dragging)
117 {
118 int dx = abs(dc.LogicalToDeviceX((long) (x - m_firstDragX)));
119 int dy = abs(dc.LogicalToDeviceY((long) (y - m_firstDragY)));
120 if (m_checkTolerance && (dx <= GetDiagram()->GetMouseTolerance()) && (dy <= GetDiagram()->GetMouseTolerance()))
121 {
122 return;
123 }
124 else
125 // If we've ignored the tolerance once, then ALWAYS ignore
126 // tolerance in this drag, even if we come back within
127 // the tolerance range.
2ba06d5a 128 m_checkTolerance = false;
1fc25a89
JS
129 }
130
131 // Dragging - note that the effect of dragging is left entirely up
132 // to the object, so no movement is done unless explicitly done by
133 // object.
134 if (dragging && m_draggedShape && m_dragState == StartDraggingLeft)
135 {
136 m_dragState = ContinueDraggingLeft;
137
138 // If the object isn't m_draggable, transfer message to canvas
139 if (m_draggedShape->Draggable())
140 m_draggedShape->GetEventHandler()->OnBeginDragLeft((double)x, (double)y, keys, m_draggedAttachment);
141 else
142 {
143 m_draggedShape = NULL;
144 OnBeginDragLeft((double)x, (double)y, keys);
145 }
146
147 m_oldDragX = x; m_oldDragY = y;
148 }
149 else if (dragging && m_draggedShape && m_dragState == ContinueDraggingLeft)
150 {
151 // Continue dragging
2ba06d5a
WS
152 m_draggedShape->GetEventHandler()->OnDragLeft(false, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
153 m_draggedShape->GetEventHandler()->OnDragLeft(true, (double)x, (double)y, keys, m_draggedAttachment);
1fc25a89
JS
154 m_oldDragX = x; m_oldDragY = y;
155 }
156 else if (event.LeftUp() && m_draggedShape && m_dragState == ContinueDraggingLeft)
157 {
158 m_dragState = NoDragging;
2ba06d5a 159 m_checkTolerance = true;
1fc25a89 160
2ba06d5a 161 m_draggedShape->GetEventHandler()->OnDragLeft(false, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
1fc25a89
JS
162
163 m_draggedShape->GetEventHandler()->OnEndDragLeft((double)x, (double)y, keys, m_draggedAttachment);
164 m_draggedShape = NULL;
165 }
166 else if (dragging && m_draggedShape && m_dragState == StartDraggingRight)
167 {
168 m_dragState = ContinueDraggingRight;
169
170 if (m_draggedShape->Draggable())
171 m_draggedShape->GetEventHandler()->OnBeginDragRight((double)x, (double)y, keys, m_draggedAttachment);
172 else
173 {
174 m_draggedShape = NULL;
175 OnBeginDragRight((double)x, (double)y, keys);
176 }
177 m_oldDragX = x; m_oldDragY = y;
178 }
179 else if (dragging && m_draggedShape && m_dragState == ContinueDraggingRight)
180 {
181 // Continue dragging
2ba06d5a
WS
182 m_draggedShape->GetEventHandler()->OnDragRight(false, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
183 m_draggedShape->GetEventHandler()->OnDragRight(true, (double)x, (double)y, keys, m_draggedAttachment);
1fc25a89
JS
184 m_oldDragX = x; m_oldDragY = y;
185 }
186 else if (event.RightUp() && m_draggedShape && m_dragState == ContinueDraggingRight)
187 {
188 m_dragState = NoDragging;
2ba06d5a 189 m_checkTolerance = true;
1fc25a89 190
2ba06d5a 191 m_draggedShape->GetEventHandler()->OnDragRight(false, m_oldDragX, m_oldDragY, keys, m_draggedAttachment);
1fc25a89
JS
192
193 m_draggedShape->GetEventHandler()->OnEndDragRight((double)x, (double)y, keys, m_draggedAttachment);
194 m_draggedShape = NULL;
195 }
196
197 // All following events sent to canvas, not object
198 else if (dragging && !m_draggedShape && m_dragState == StartDraggingLeft)
199 {
200 m_dragState = ContinueDraggingLeft;
201 OnBeginDragLeft((double)x, (double)y, keys);
202 m_oldDragX = x; m_oldDragY = y;
203 }
204 else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingLeft)
205 {
206 // Continue dragging
2ba06d5a
WS
207 OnDragLeft(false, m_oldDragX, m_oldDragY, keys);
208 OnDragLeft(true, (double)x, (double)y, keys);
1fc25a89
JS
209 m_oldDragX = x; m_oldDragY = y;
210 }
211 else if (event.LeftUp() && !m_draggedShape && m_dragState == ContinueDraggingLeft)
212 {
213 m_dragState = NoDragging;
2ba06d5a 214 m_checkTolerance = true;
1fc25a89 215
2ba06d5a 216 OnDragLeft(false, m_oldDragX, m_oldDragY, keys);
1fc25a89
JS
217 OnEndDragLeft((double)x, (double)y, keys);
218 m_draggedShape = NULL;
219 }
220 else if (dragging && !m_draggedShape && m_dragState == StartDraggingRight)
221 {
222 m_dragState = ContinueDraggingRight;
223 OnBeginDragRight((double)x, (double)y, keys);
224 m_oldDragX = x; m_oldDragY = y;
225 }
226 else if (dragging && !m_draggedShape && m_dragState == ContinueDraggingRight)
227 {
228 // Continue dragging
2ba06d5a
WS
229 OnDragRight(false, m_oldDragX, m_oldDragY, keys);
230 OnDragRight(true, (double)x, (double)y, keys);
1fc25a89
JS
231 m_oldDragX = x; m_oldDragY = y;
232 }
233 else if (event.RightUp() && !m_draggedShape && m_dragState == ContinueDraggingRight)
234 {
235 m_dragState = NoDragging;
2ba06d5a 236 m_checkTolerance = true;
1fc25a89 237
2ba06d5a 238 OnDragRight(false, m_oldDragX, m_oldDragY, keys);
1fc25a89
JS
239 OnEndDragRight((double)x, (double)y, keys);
240 m_draggedShape = NULL;
241 }
242
243 // Non-dragging events
244 else if (event.IsButton())
245 {
2ba06d5a 246 m_checkTolerance = true;
1fc25a89
JS
247
248 // Find the nearest object
249 int attachment = 0;
250 wxShape *nearest_object = FindShape(x, y, &attachment);
251 if (nearest_object) // Object event
252 {
253 if (event.LeftDown())
254 {
255 m_draggedShape = nearest_object;
256 m_draggedAttachment = attachment;
257 m_dragState = StartDraggingLeft;
258 m_firstDragX = x;
259 m_firstDragY = y;
260 }
261 else if (event.LeftUp())
262 {
263 // N.B. Only register a click if the same object was
264 // identified for down *and* up.
265 if (nearest_object == m_draggedShape)
266 nearest_object->GetEventHandler()->OnLeftClick((double)x, (double)y, keys, attachment);
267
268 m_draggedShape = NULL;
269 m_dragState = NoDragging;
270 }
271 else if (event.LeftDClick())
272 {
273 nearest_object->GetEventHandler()->OnLeftDoubleClick((double)x, (double)y, keys, attachment);
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)
289 nearest_object->GetEventHandler()->OnRightClick((double)x, (double)y, keys, attachment);
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 {
306 OnLeftClick((double)x, (double)y, keys);
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 {
320 OnRightClick((double)x, (double)y, keys);
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 */
333wxShape *wxShapeCanvas::FindFirstSensitiveShape(double x, double y, int *new_attachment, int op)
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 {
341 double dist;
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
2ba06d5a 357// Helper function: true if 'contains' wholly contains 'contained'.
1fc25a89
JS
358static bool WhollyContains(wxShape *contains, wxShape *contained)
359{
360 double xp1, yp1, xp2, yp2;
361 double w1, h1, w2, h2;
362 double left1, top1, right1, bottom1, left2, top2, right2, bottom2;
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
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));
372
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));
377
378 return ((left1 <= left2) && (top1 <= top2) && (right1 >= right2) && (bottom1 >= bottom2));
379}
380
381wxShape *wxShapeCanvas::FindShape(double x, double y, int *attachment, wxClassInfo *info, wxShape *notObject)
382{
383 double nearest = 100000.0;
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
5e0dbc8d 392 wxObjectList::compatibility_iterator current = GetDiagram()->GetShapeList()->GetLast();
1fc25a89
JS
393 while (current)
394 {
b9ac87bc 395 wxShape *object = (wxShape *)current->GetData();
1fc25a89
JS
396
397 double dist;
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)
b9ac87bc 425 current = current->GetPrevious();
1fc25a89
JS
426 }
427
b9ac87bc 428 current = GetDiagram()->GetShapeList()->GetLast();
1fc25a89
JS
429 while (current)
430 {
b9ac87bc 431 wxShape *object = (wxShape *)current->GetData();
1fc25a89
JS
432 double dist;
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 {
1fc25a89
JS
448 nearest_object = object;
449 nearest_attachment = temp_attachment;
5e0dbc8d 450 current = GetDiagram()->GetShapeList()->GetFirst()->GetPrevious(); // finish loop
1fc25a89
JS
451 }
452 }
453 }
454 if (current)
b9ac87bc 455 current = current->GetPrevious();
1fc25a89
JS
456 }
457
458 *attachment = nearest_attachment;
459 return nearest_object;
460}
461
462/*
463 * Higher-level events called by OnEvent
464 *
465 */
466
1484b5cc 467void wxShapeCanvas::OnLeftClick(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
468{
469}
470
1484b5cc 471void wxShapeCanvas::OnRightClick(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
472{
473}
474
1484b5cc 475void wxShapeCanvas::OnDragLeft(bool WXUNUSED(draw), double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
476{
477}
478
1484b5cc 479void wxShapeCanvas::OnBeginDragLeft(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
480{
481}
482
1484b5cc 483void wxShapeCanvas::OnEndDragLeft(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
484{
485}
486
1484b5cc 487void wxShapeCanvas::OnDragRight(bool WXUNUSED(draw), double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
488{
489}
490
1484b5cc 491void wxShapeCanvas::OnBeginDragRight(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
492{
493}
494
1484b5cc 495void wxShapeCanvas::OnEndDragRight(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
496{
497}
498
499void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
500 { GetDiagram()->AddShape(object, addAfter); }
501void wxShapeCanvas::InsertShape(wxShape *object)
502 { GetDiagram()->InsertShape(object); }
503void wxShapeCanvas::RemoveShape(wxShape *object)
504 { GetDiagram()->RemoveShape(object); }
505bool wxShapeCanvas::GetQuickEditMode()
506 { return GetDiagram()->GetQuickEditMode(); }
507void wxShapeCanvas::Redraw(wxDC& dc)
508 { GetDiagram()->Redraw(dc); }
509void wxShapeCanvas::Snap(double *x, double *y)
510 { GetDiagram()->Snap(x, y); }