]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/ogl/canvas.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Shape canvas class
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "canvas.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
27 #include <wx/wxexpr.h>
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>
51 #define CONTROL_POINT_SIZE 6
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
60 #define CONTROL_POINT_ENDPOINT_TO 4
61 #define CONTROL_POINT_ENDPOINT_FROM 5
62 #define CONTROL_POINT_LINE 6
64 IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas
, wxScrolledWindow
)
66 BEGIN_EVENT_TABLE(wxShapeCanvas
, wxScrolledWindow
)
67 EVT_PAINT(wxShapeCanvas::OnPaint
)
68 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent
)
71 wxChar
* wxShapeCanvasNameStr
= wxT("shapeCanvas");
74 wxShapeCanvas::wxShapeCanvas(wxWindow
*parent
, wxWindowID id
,
78 const wxString
& name
):
79 wxScrolledWindow(parent
, id
, pos
, size
, style
, name
)
81 m_shapeDiagram
= NULL
;
82 m_dragState
= NoDragging
;
83 m_draggedShape
= NULL
;
88 m_checkTolerance
= TRUE
;
91 wxShapeCanvas::~wxShapeCanvas()
95 void wxShapeCanvas::OnPaint(wxPaintEvent
& event
)
101 dc
.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID
));
105 GetDiagram()->Redraw(dc
);
108 void wxShapeCanvas::OnMouseEvent(wxMouseEvent
& event
)
113 wxPoint
logPos(event
.GetLogicalPosition(dc
));
116 x
= (double) logPos
.x
;
117 y
= (double) logPos
.y
;
120 if (event
.ShiftDown())
121 keys
= keys
| KEY_SHIFT
;
122 if (event
.ControlDown())
123 keys
= keys
| KEY_CTRL
;
125 bool dragging
= event
.Dragging();
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.
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()))
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
;
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
148 if (dragging
&& m_draggedShape
&& m_dragState
== StartDraggingLeft
)
150 m_dragState
= ContinueDraggingLeft
;
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
);
157 m_draggedShape
= NULL
;
158 OnBeginDragLeft((double)x
, (double)y
, keys
);
161 m_oldDragX
= x
; m_oldDragY
= y
;
163 else if (dragging
&& m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
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
;
170 else if (event
.LeftUp() && m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
172 m_dragState
= NoDragging
;
173 m_checkTolerance
= TRUE
;
175 m_draggedShape
->GetEventHandler()->OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
177 m_draggedShape
->GetEventHandler()->OnEndDragLeft((double)x
, (double)y
, keys
, m_draggedAttachment
);
178 m_draggedShape
= NULL
;
180 else if (dragging
&& m_draggedShape
&& m_dragState
== StartDraggingRight
)
182 m_dragState
= ContinueDraggingRight
;
184 if (m_draggedShape
->Draggable())
185 m_draggedShape
->GetEventHandler()->OnBeginDragRight((double)x
, (double)y
, keys
, m_draggedAttachment
);
188 m_draggedShape
= NULL
;
189 OnBeginDragRight((double)x
, (double)y
, keys
);
191 m_oldDragX
= x
; m_oldDragY
= y
;
193 else if (dragging
&& m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
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
;
200 else if (event
.RightUp() && m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
202 m_dragState
= NoDragging
;
203 m_checkTolerance
= TRUE
;
205 m_draggedShape
->GetEventHandler()->OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
207 m_draggedShape
->GetEventHandler()->OnEndDragRight((double)x
, (double)y
, keys
, m_draggedAttachment
);
208 m_draggedShape
= NULL
;
211 // All following events sent to canvas, not object
212 else if (dragging
&& !m_draggedShape
&& m_dragState
== StartDraggingLeft
)
214 m_dragState
= ContinueDraggingLeft
;
215 OnBeginDragLeft((double)x
, (double)y
, keys
);
216 m_oldDragX
= x
; m_oldDragY
= y
;
218 else if (dragging
&& !m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
221 OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
222 OnDragLeft(TRUE
, (double)x
, (double)y
, keys
);
223 m_oldDragX
= x
; m_oldDragY
= y
;
225 else if (event
.LeftUp() && !m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
227 m_dragState
= NoDragging
;
228 m_checkTolerance
= TRUE
;
230 OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
231 OnEndDragLeft((double)x
, (double)y
, keys
);
232 m_draggedShape
= NULL
;
234 else if (dragging
&& !m_draggedShape
&& m_dragState
== StartDraggingRight
)
236 m_dragState
= ContinueDraggingRight
;
237 OnBeginDragRight((double)x
, (double)y
, keys
);
238 m_oldDragX
= x
; m_oldDragY
= y
;
240 else if (dragging
&& !m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
243 OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
244 OnDragRight(TRUE
, (double)x
, (double)y
, keys
);
245 m_oldDragX
= x
; m_oldDragY
= y
;
247 else if (event
.RightUp() && !m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
249 m_dragState
= NoDragging
;
250 m_checkTolerance
= TRUE
;
252 OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
253 OnEndDragRight((double)x
, (double)y
, keys
);
254 m_draggedShape
= NULL
;
257 // Non-dragging events
258 else if (event
.IsButton())
260 m_checkTolerance
= TRUE
;
262 // Find the nearest object
264 wxShape
*nearest_object
= FindShape(x
, y
, &attachment
);
265 if (nearest_object
) // Object event
267 if (event
.LeftDown())
269 m_draggedShape
= nearest_object
;
270 m_draggedAttachment
= attachment
;
271 m_dragState
= StartDraggingLeft
;
275 else if (event
.LeftUp())
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
);
282 m_draggedShape
= NULL
;
283 m_dragState
= NoDragging
;
285 else if (event
.LeftDClick())
287 nearest_object
->GetEventHandler()->OnLeftDoubleClick((double)x
, (double)y
, keys
, attachment
);
289 m_draggedShape
= NULL
;
290 m_dragState
= NoDragging
;
292 else if (event
.RightDown())
294 m_draggedShape
= nearest_object
;
295 m_draggedAttachment
= attachment
;
296 m_dragState
= StartDraggingRight
;
300 else if (event
.RightUp())
302 if (nearest_object
== m_draggedShape
)
303 nearest_object
->GetEventHandler()->OnRightClick((double)x
, (double)y
, keys
, attachment
);
305 m_draggedShape
= NULL
;
306 m_dragState
= NoDragging
;
309 else // Canvas event (no nearest object)
311 if (event
.LeftDown())
313 m_draggedShape
= NULL
;
314 m_dragState
= StartDraggingLeft
;
318 else if (event
.LeftUp())
320 OnLeftClick((double)x
, (double)y
, keys
);
322 m_draggedShape
= NULL
;
323 m_dragState
= NoDragging
;
325 else if (event
.RightDown())
327 m_draggedShape
= NULL
;
328 m_dragState
= StartDraggingRight
;
332 else if (event
.RightUp())
334 OnRightClick((double)x
, (double)y
, keys
);
336 m_draggedShape
= NULL
;
337 m_dragState
= NoDragging
;
344 * Try to find a sensitive object, working up the hierarchy of composites.
347 wxShape
*wxShapeCanvas::FindFirstSensitiveShape(double x
, double y
, int *new_attachment
, int op
)
349 wxShape
*image
= FindShape(x
, y
, new_attachment
);
350 if (!image
) return NULL
;
352 wxShape
*actualImage
= FindFirstSensitiveShape1(image
, op
);
356 // Find actual attachment
357 actualImage
->HitTest(x
, y
, new_attachment
, &dist
);
362 wxShape
*wxShapeCanvas::FindFirstSensitiveShape1(wxShape
*image
, int op
)
364 if (image
->GetSensitivityFilter() & op
)
366 if (image
->GetParent())
367 return FindFirstSensitiveShape1(image
->GetParent(), op
);
371 // Helper function: TRUE if 'contains' wholly contains 'contained'.
372 static bool WhollyContains(wxShape
*contains
, wxShape
*contained
)
374 double xp1
, yp1
, xp2
, yp2
;
375 double w1
, h1
, w2
, h2
;
376 double left1
, top1
, right1
, bottom1
, left2
, top2
, right2
, bottom2
;
378 xp1
= contains
->GetX(); yp1
= contains
->GetY(); xp2
= contained
->GetX(); yp2
= contained
->GetY();
379 contains
->GetBoundingBoxMax(&w1
, &h1
);
380 contained
->GetBoundingBoxMax(&w2
, &h2
);
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));
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));
392 return ((left1
<= left2
) && (top1
<= top2
) && (right1
>= right2
) && (bottom1
>= bottom2
));
395 wxShape
*wxShapeCanvas::FindShape(double x
, double y
, int *attachment
, wxClassInfo
*info
, wxShape
*notObject
)
397 double nearest
= 100000.0;
398 int nearest_attachment
= 0;
399 wxShape
*nearest_object
= NULL
;
401 // Go backward through the object list, since we want:
402 // (a) to have the control points drawn LAST to overlay
404 // (b) to find the control points FIRST if they exist
406 wxNode
*current
= GetDiagram()->GetShapeList()->Last();
409 wxShape
*object
= (wxShape
*)current
->Data();
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
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
)))
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
434 nearest_object
= object
;
435 nearest_attachment
= temp_attachment
;
439 current
= current
->Previous();
442 current
= GetDiagram()->GetShapeList()->Last();
445 wxShape
*object
= (wxShape
*)current
->Data();
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
)))
455 if (!object
->IsKindOf(CLASSINFO(wxLineShape
)))
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
)))
463 nearest_object
= object
;
464 nearest_attachment
= temp_attachment
;
470 current
= current
->Previous();
473 *attachment
= nearest_attachment
;
474 return nearest_object
;
478 * Higher-level events called by OnEvent
482 void wxShapeCanvas::OnLeftClick(double x
, double y
, int keys
)
486 void wxShapeCanvas::OnRightClick(double x
, double y
, int keys
)
490 void wxShapeCanvas::OnDragLeft(bool draw
, double x
, double y
, int keys
)
494 void wxShapeCanvas::OnBeginDragLeft(double x
, double y
, int keys
)
498 void wxShapeCanvas::OnEndDragLeft(double x
, double y
, int keys
)
502 void wxShapeCanvas::OnDragRight(bool draw
, double x
, double y
, int keys
)
506 void wxShapeCanvas::OnBeginDragRight(double x
, double y
, int keys
)
510 void wxShapeCanvas::OnEndDragRight(double x
, double y
, int keys
)
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
); }