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