]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/ogl/canvas.cpp
Copied/merged from the 2.2 branch.
[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 #if wxUSE_IOSTREAMH
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 <wx/ogl/basic.h>
40 #include <wx/ogl/basicp.h>
41 #include <wx/ogl/canvas.h>
42 #include <wx/ogl/ogldiag.h>
43 #include <wx/ogl/misc.h>
44 #include <wx/ogl/lines.h>
45 #include <wx/ogl/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
60 extern wxCursor *g_oglBullseyeCursor;
61
62 IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
63
64 BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
65 EVT_PAINT(wxShapeCanvas::OnPaint)
66 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
67 END_EVENT_TABLE()
68
69 // Object canvas
70 wxShapeCanvas::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
83 wxShapeCanvas::~wxShapeCanvas()
84 {
85 }
86
87 void 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
99 void wxShapeCanvas::OnMouseEvent(wxMouseEvent& event)
100 {
101 wxClientDC dc(this);
102 PrepareDC(dc);
103
104 wxPoint logPos(event.GetLogicalPosition(dc));
105
106 double x, y;
107 x = (double) logPos.x;
108 y = (double) logPos.y;
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 {
123 int dx = abs(dc.LogicalToDeviceX((long) (x - m_firstDragX)));
124 int dy = abs(dc.LogicalToDeviceY((long) (y - m_firstDragY)));
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())
145 m_draggedShape->GetEventHandler()->OnBeginDragLeft((double)x, (double)y, keys, m_draggedAttachment);
146 else
147 {
148 m_draggedShape = NULL;
149 OnBeginDragLeft((double)x, (double)y, keys);
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);
158 m_draggedShape->GetEventHandler()->OnDragLeft(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
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
168 m_draggedShape->GetEventHandler()->OnEndDragLeft((double)x, (double)y, keys, m_draggedAttachment);
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())
176 m_draggedShape->GetEventHandler()->OnBeginDragRight((double)x, (double)y, keys, m_draggedAttachment);
177 else
178 {
179 m_draggedShape = NULL;
180 OnBeginDragRight((double)x, (double)y, keys);
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);
188 m_draggedShape->GetEventHandler()->OnDragRight(TRUE, (double)x, (double)y, keys, m_draggedAttachment);
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
198 m_draggedShape->GetEventHandler()->OnEndDragRight((double)x, (double)y, keys, m_draggedAttachment);
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;
206 OnBeginDragLeft((double)x, (double)y, keys);
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);
213 OnDragLeft(TRUE, (double)x, (double)y, keys);
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);
222 OnEndDragLeft((double)x, (double)y, keys);
223 m_draggedShape = NULL;
224 }
225 else if (dragging && !m_draggedShape && m_dragState == StartDraggingRight)
226 {
227 m_dragState = ContinueDraggingRight;
228 OnBeginDragRight((double)x, (double)y, keys);
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);
235 OnDragRight(TRUE, (double)x, (double)y, keys);
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);
244 OnEndDragRight((double)x, (double)y, keys);
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)
271 nearest_object->GetEventHandler()->OnLeftClick((double)x, (double)y, keys, attachment);
272
273 m_draggedShape = NULL;
274 m_dragState = NoDragging;
275 }
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 }
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)
294 nearest_object->GetEventHandler()->OnRightClick((double)x, (double)y, keys, attachment);
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 {
311 OnLeftClick((double)x, (double)y, keys);
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 {
325 OnRightClick((double)x, (double)y, keys);
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 */
338 wxShape *wxShapeCanvas::FindFirstSensitiveShape(double x, double y, int *new_attachment, int op)
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 {
346 double dist;
347 // Find actual attachment
348 actualImage->HitTest(x, y, new_attachment, &dist);
349 }
350 return actualImage;
351 }
352
353 wxShape *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'.
363 static bool WhollyContains(wxShape *contains, wxShape *contained)
364 {
365 double xp1, yp1, xp2, yp2;
366 double w1, h1, w2, h2;
367 double left1, top1, right1, bottom1, left2, top2, right2, bottom2;
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
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));
377
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));
382
383 return ((left1 <= left2) && (top1 <= top2) && (right1 >= right2) && (bottom1 >= bottom2));
384 }
385
386 wxShape *wxShapeCanvas::FindShape(double x, double y, int *attachment, wxClassInfo *info, wxShape *notObject)
387 {
388 double nearest = 100000.0;
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
402 double dist;
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();
437 double dist;
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
473 void wxShapeCanvas::OnLeftClick(double x, double y, int keys)
474 {
475 }
476
477 void wxShapeCanvas::OnRightClick(double x, double y, int keys)
478 {
479 }
480
481 void wxShapeCanvas::OnDragLeft(bool draw, double x, double y, int keys)
482 {
483 }
484
485 void wxShapeCanvas::OnBeginDragLeft(double x, double y, int keys)
486 {
487 }
488
489 void wxShapeCanvas::OnEndDragLeft(double x, double y, int keys)
490 {
491 }
492
493 void wxShapeCanvas::OnDragRight(bool draw, double x, double y, int keys)
494 {
495 }
496
497 void wxShapeCanvas::OnBeginDragRight(double x, double y, int keys)
498 {
499 }
500
501 void wxShapeCanvas::OnEndDragRight(double x, double y, int keys)
502 {
503 }
504
505 void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
506 { GetDiagram()->AddShape(object, addAfter); }
507 void wxShapeCanvas::InsertShape(wxShape *object)
508 { GetDiagram()->InsertShape(object); }
509 void wxShapeCanvas::RemoveShape(wxShape *object)
510 { GetDiagram()->RemoveShape(object); }
511 bool wxShapeCanvas::GetQuickEditMode()
512 { return GetDiagram()->GetQuickEditMode(); }
513 void wxShapeCanvas::Redraw(wxDC& dc)
514 { GetDiagram()->Redraw(dc); }
515 void wxShapeCanvas::Snap(double *x, double *y)
516 { GetDiagram()->Snap(x, y); }