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