]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/ogl/canvas.cpp
This should work on SuSE and Mandrake icon themes.
[wxWidgets.git] / contrib / src / ogl / canvas.cpp
CommitLineData
1fc25a89
JS
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".
92a19c2e 17#include "wx/wxprec.h"
1fc25a89
JS
18
19#ifdef __BORLANDC__
20#pragma hdrstop
21#endif
22
23#ifndef WX_PRECOMP
24#include <wx/wx.h>
25#endif
26
5f331691 27#if wxUSE_PROLOGIO
7c9955d1 28#include <wx/deprecated/wxexpr.h>
fd657b8a 29#endif
1fc25a89 30
3f1802b5
JS
31#ifdef new
32#undef new
33#endif
34
1fc25a89
JS
35#include <ctype.h>
36#include <math.h>
37#include <stdlib.h>
38
5f331691 39#include "wx/ogl/ogl.h"
1fc25a89
JS
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
1fc25a89
JS
54IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas, wxScrolledWindow)
55
56BEGIN_EVENT_TABLE(wxShapeCanvas, wxScrolledWindow)
57 EVT_PAINT(wxShapeCanvas::OnPaint)
58 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent)
59END_EVENT_TABLE()
60
0a2c0398 61const wxChar* wxShapeCanvasNameStr = wxT("shapeCanvas");
4fcf77bc 62
1fc25a89 63// Object canvas
4fcf77bc
RD
64wxShapeCanvas::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)
1fc25a89
JS
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
81wxShapeCanvas::~wxShapeCanvas()
82{
83}
84
1484b5cc 85void wxShapeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
1fc25a89
JS
86{
87 wxPaintDC dc(this);
88
89 PrepareDC(dc);
90
ebe25075 91 dc.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
1fc25a89
JS
92 dc.Clear();
93
ebe25075
RD
94 if (GetDiagram())
95 GetDiagram()->Redraw(dc);
1fc25a89
JS
96}
97
98void 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 */
337wxShape *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
352wxShape *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'.
362static 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
385wxShape *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
b9ac87bc 396 wxNode *current = GetDiagram()->GetShapeList()->GetLast();
1fc25a89
JS
397 while (current)
398 {
b9ac87bc 399 wxShape *object = (wxShape *)current->GetData();
1fc25a89
JS
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)
b9ac87bc 429 current = current->GetPrevious();
1fc25a89
JS
430 }
431
b9ac87bc 432 current = GetDiagram()->GetShapeList()->GetLast();
1fc25a89
JS
433 while (current)
434 {
b9ac87bc 435 wxShape *object = (wxShape *)current->GetData();
1fc25a89
JS
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 {
1fc25a89
JS
452 nearest_object = object;
453 nearest_attachment = temp_attachment;
454 current = NULL;
455 }
456 }
457 }
458 if (current)
b9ac87bc 459 current = current->GetPrevious();
1fc25a89
JS
460 }
461
462 *attachment = nearest_attachment;
463 return nearest_object;
464}
465
466/*
467 * Higher-level events called by OnEvent
468 *
469 */
470
1484b5cc 471void wxShapeCanvas::OnLeftClick(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
472{
473}
474
1484b5cc 475void wxShapeCanvas::OnRightClick(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
476{
477}
478
1484b5cc 479void wxShapeCanvas::OnDragLeft(bool WXUNUSED(draw), double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
480{
481}
482
1484b5cc 483void wxShapeCanvas::OnBeginDragLeft(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
484{
485}
486
1484b5cc 487void wxShapeCanvas::OnEndDragLeft(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
488{
489}
490
1484b5cc 491void wxShapeCanvas::OnDragRight(bool WXUNUSED(draw), double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
492{
493}
494
1484b5cc 495void wxShapeCanvas::OnBeginDragRight(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
496{
497}
498
1484b5cc 499void wxShapeCanvas::OnEndDragRight(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys))
1fc25a89
JS
500{
501}
502
503void wxShapeCanvas::AddShape(wxShape *object, wxShape *addAfter)
504 { GetDiagram()->AddShape(object, addAfter); }
505void wxShapeCanvas::InsertShape(wxShape *object)
506 { GetDiagram()->InsertShape(object); }
507void wxShapeCanvas::RemoveShape(wxShape *object)
508 { GetDiagram()->RemoveShape(object); }
509bool wxShapeCanvas::GetQuickEditMode()
510 { return GetDiagram()->GetQuickEditMode(); }
511void wxShapeCanvas::Redraw(wxDC& dc)
512 { GetDiagram()->Redraw(dc); }
513void wxShapeCanvas::Snap(double *x, double *y)
514 { GetDiagram()->Snap(x, y); }