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