]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/ogl/canvas.cpp
synthetize 'button up' event before doubleclick, too
[wxWidgets.git] / contrib / src / ogl / canvas.cpp
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 #include <wx/wxexpr.h>
28
29 #ifdef new
30 #undef new
31 #endif
32
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
64 IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
65
66 BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
67 EVT_PAINT(wxShapeCanvas::OnPaint)
68 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
69 END_EVENT_TABLE()
70
71 // Object canvas
72 wxShapeCanvas::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
85 wxShapeCanvas::~wxShapeCanvas()
86 {
87 }
88
89 void wxShapeCanvas::OnPaint(wxPaintEvent& event)
90 {
91 wxPaintDC dc(this);
92
93 PrepareDC(dc);
94
95 dc.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
96 dc.Clear();
97
98 if (GetDiagram())
99 GetDiagram()->Redraw(dc);
100 }
101
102 void 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 */
341 wxShape *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
356 wxShape *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'.
366 static 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
389 wxShape *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
476 void wxShapeCanvas::OnLeftClick(double x, double y, int keys)
477 {
478 }
479
480 void wxShapeCanvas::OnRightClick(double x, double y, int keys)
481 {
482 }
483
484 void wxShapeCanvas::OnDragLeft(bool draw, double x, double y, int keys)
485 {
486 }
487
488 void wxShapeCanvas::OnBeginDragLeft(double x, double y, int keys)
489 {
490 }
491
492 void wxShapeCanvas::OnEndDragLeft(double x, double y, int keys)
493 {
494 }
495
496 void wxShapeCanvas::OnDragRight(bool draw, double x, double y, int keys)
497 {
498 }
499
500 void wxShapeCanvas::OnBeginDragRight(double x, double y, int keys)
501 {
502 }
503
504 void wxShapeCanvas::OnEndDragRight(double x, double y, int keys)
505 {
506 }
507
508 void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
509 { GetDiagram()->AddShape(object, addAfter); }
510 void wxShapeCanvas::InsertShape(wxShape *object)
511 { GetDiagram()->InsertShape(object); }
512 void wxShapeCanvas::RemoveShape(wxShape *object)
513 { GetDiagram()->RemoveShape(object); }
514 bool wxShapeCanvas::GetQuickEditMode()
515 { return GetDiagram()->GetQuickEditMode(); }
516 void wxShapeCanvas::Redraw(wxDC& dc)
517 { GetDiagram()->Redraw(dc); }
518 void wxShapeCanvas::Snap(double *x, double *y)
519 { GetDiagram()->Snap(x, y); }