]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/ogl/canvas.cpp
synthetize 'button up' event before doubleclick, too
[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
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".
92a19c2e 17#include "wx/wxprec.h"
1fc25a89
JS
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
3f1802b5
JS
29#ifdef new
30#undef new
31#endif
32
1fc25a89
JS
33#if wxUSE_IOSTREAMH
34#include <iostream.h>
35#else
36#include <iostream>
37#endif
38
39#include <ctype.h>
40#include <math.h>
41#include <stdlib.h>
42
43#include <wx/ogl/basic.h>
44#include <wx/ogl/basicp.h>
45#include <wx/ogl/canvas.h>
46#include <wx/ogl/ogldiag.h>
47#include <wx/ogl/misc.h>
48#include <wx/ogl/lines.h>
49#include <wx/ogl/composit.h>
50
51#define CONTROL_POINT_SIZE 6
52
53// Control point types
54// Rectangle and most other shapes
55#define CONTROL_POINT_VERTICAL 1
56#define CONTROL_POINT_HORIZONTAL 2
57#define CONTROL_POINT_DIAGONAL 3
58
59// Line
60#define CONTROL_POINT_ENDPOINT_TO 4
61#define CONTROL_POINT_ENDPOINT_FROM 5
62#define CONTROL_POINT_LINE 6
63
1fc25a89
JS
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
ebe25075 95 dc.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
1fc25a89
JS
96 dc.Clear();
97
ebe25075
RD
98 if (GetDiagram())
99 GetDiagram()->Redraw(dc);
1fc25a89
JS
100}
101
102void 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 */
341wxShape *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
356wxShape *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'.
366static 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
389wxShape *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
476void wxShapeCanvas::OnLeftClick(double x, double y, int keys)
477{
478}
479
480void wxShapeCanvas::OnRightClick(double x, double y, int keys)
481{
482}
483
484void wxShapeCanvas::OnDragLeft(bool draw, double x, double y, int keys)
485{
486}
487
488void wxShapeCanvas::OnBeginDragLeft(double x, double y, int keys)
489{
490}
491
492void wxShapeCanvas::OnEndDragLeft(double x, double y, int keys)
493{
494}
495
496void wxShapeCanvas::OnDragRight(bool draw, double x, double y, int keys)
497{
498}
499
500void wxShapeCanvas::OnBeginDragRight(double x, double y, int keys)
501{
502}
503
504void wxShapeCanvas::OnEndDragRight(double x, double y, int keys)
505{
506}
507
508void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
509 { GetDiagram()->AddShape(object, addAfter); }
510void wxShapeCanvas::InsertShape(wxShape *object)
511 { GetDiagram()->InsertShape(object); }
512void wxShapeCanvas::RemoveShape(wxShape *object)
513 { GetDiagram()->RemoveShape(object); }
514bool wxShapeCanvas::GetQuickEditMode()
515 { return GetDiagram()->GetQuickEditMode(); }
516void wxShapeCanvas::Redraw(wxDC& dc)
517 { GetDiagram()->Redraw(dc); }
518void wxShapeCanvas::Snap(double *x, double *y)
519 { GetDiagram()->Snap(x, y); }