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