]>
git.saurik.com Git - wxWidgets.git/blob - utils/ogl/src/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>
28 #include <wx/wxexpr.h>
49 #define CONTROL_POINT_SIZE 6
51 // Control point types
52 // Rectangle and most other shapes
53 #define CONTROL_POINT_VERTICAL 1
54 #define CONTROL_POINT_HORIZONTAL 2
55 #define CONTROL_POINT_DIAGONAL 3
58 #define CONTROL_POINT_ENDPOINT_TO 4
59 #define CONTROL_POINT_ENDPOINT_FROM 5
60 #define CONTROL_POINT_LINE 6
62 extern wxCursor
*GraphicsBullseyeCursor
;
64 IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas
, wxScrolledWindow
)
66 BEGIN_EVENT_TABLE(wxShapeCanvas
, wxScrolledWindow
)
67 EVT_PAINT(wxShapeCanvas::OnPaint
)
68 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent
)
72 wxShapeCanvas::wxShapeCanvas(wxWindow
*parent
, wxWindowID id
, const wxPoint
& pos
, const wxSize
& size
, long style
):
73 wxScrolledWindow(parent
, id
, pos
, size
, style
)
75 m_shapeDiagram
= NULL
;
76 m_dragState
= NoDragging
;
77 m_draggedShape
= NULL
;
82 m_checkTolerance
= TRUE
;
85 wxShapeCanvas::~wxShapeCanvas()
89 void wxShapeCanvas::OnPaint(wxPaintEvent
& event
)
98 GetDiagram()->Redraw(dc
);
101 void wxShapeCanvas::OnMouseEvent(wxMouseEvent
& event
)
106 wxPoint
logPos(event
.GetLogicalPosition(dc
));
109 x
= (float) logPos
.x
;
110 y
= (float) logPos
.y
;
113 if (event
.ShiftDown())
114 keys
= keys
| KEY_SHIFT
;
115 if (event
.ControlDown())
116 keys
= keys
| KEY_CTRL
;
118 bool dragging
= event
.Dragging();
120 // Check if we're within the tolerance for mouse movements.
121 // If we're very close to the position we started dragging
122 // from, this may not be an intentional drag at all.
125 int dx
= abs(dc
.LogicalToDeviceX(x
- m_firstDragX
));
126 int dy
= abs(dc
.LogicalToDeviceY(y
- m_firstDragY
));
127 if (m_checkTolerance
&& (dx
<= GetDiagram()->GetMouseTolerance()) && (dy
<= GetDiagram()->GetMouseTolerance()))
132 // If we've ignored the tolerance once, then ALWAYS ignore
133 // tolerance in this drag, even if we come back within
134 // the tolerance range.
135 m_checkTolerance
= FALSE
;
138 // Dragging - note that the effect of dragging is left entirely up
139 // to the object, so no movement is done unless explicitly done by
141 if (dragging
&& m_draggedShape
&& m_dragState
== StartDraggingLeft
)
143 m_dragState
= ContinueDraggingLeft
;
145 // If the object isn't m_draggable, transfer message to canvas
146 if (m_draggedShape
->Draggable())
147 m_draggedShape
->GetEventHandler()->OnBeginDragLeft((float)x
, (float)y
, keys
, m_draggedAttachment
);
150 m_draggedShape
= NULL
;
151 OnBeginDragLeft((float)x
, (float)y
, keys
);
154 m_oldDragX
= x
; m_oldDragY
= y
;
156 else if (dragging
&& m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
159 m_draggedShape
->GetEventHandler()->OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
160 m_draggedShape
->GetEventHandler()->OnDragLeft(TRUE
, (float)x
, (float)y
, keys
, m_draggedAttachment
);
161 m_oldDragX
= x
; m_oldDragY
= y
;
163 else if (event
.LeftUp() && m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
165 m_dragState
= NoDragging
;
166 m_checkTolerance
= TRUE
;
168 m_draggedShape
->GetEventHandler()->OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
170 m_draggedShape
->GetEventHandler()->OnEndDragLeft((float)x
, (float)y
, keys
, m_draggedAttachment
);
171 m_draggedShape
= NULL
;
173 else if (dragging
&& m_draggedShape
&& m_dragState
== StartDraggingRight
)
175 m_dragState
= ContinueDraggingRight
;
177 if (m_draggedShape
->Draggable())
178 m_draggedShape
->GetEventHandler()->OnBeginDragRight((float)x
, (float)y
, keys
, m_draggedAttachment
);
181 m_draggedShape
= NULL
;
182 OnBeginDragRight((float)x
, (float)y
, keys
);
184 m_oldDragX
= x
; m_oldDragY
= y
;
186 else if (dragging
&& m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
189 m_draggedShape
->GetEventHandler()->OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
190 m_draggedShape
->GetEventHandler()->OnDragRight(TRUE
, (float)x
, (float)y
, keys
, m_draggedAttachment
);
191 m_oldDragX
= x
; m_oldDragY
= y
;
193 else if (event
.RightUp() && m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
195 m_dragState
= NoDragging
;
196 m_checkTolerance
= TRUE
;
198 m_draggedShape
->GetEventHandler()->OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
200 m_draggedShape
->GetEventHandler()->OnEndDragRight((float)x
, (float)y
, keys
, m_draggedAttachment
);
201 m_draggedShape
= NULL
;
204 // All following events sent to canvas, not object
205 else if (dragging
&& !m_draggedShape
&& m_dragState
== StartDraggingLeft
)
207 m_dragState
= ContinueDraggingLeft
;
208 OnBeginDragLeft((float)x
, (float)y
, keys
);
209 m_oldDragX
= x
; m_oldDragY
= y
;
211 else if (dragging
&& !m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
214 OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
215 OnDragLeft(TRUE
, (float)x
, (float)y
, keys
);
216 m_oldDragX
= x
; m_oldDragY
= y
;
218 else if (event
.LeftUp() && !m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
220 m_dragState
= NoDragging
;
221 m_checkTolerance
= TRUE
;
223 OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
224 OnEndDragLeft((float)x
, (float)y
, keys
);
225 m_draggedShape
= NULL
;
227 else if (dragging
&& !m_draggedShape
&& m_dragState
== StartDraggingRight
)
229 m_dragState
= ContinueDraggingRight
;
230 OnBeginDragRight((float)x
, (float)y
, keys
);
231 m_oldDragX
= x
; m_oldDragY
= y
;
233 else if (dragging
&& !m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
236 OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
237 OnDragRight(TRUE
, (float)x
, (float)y
, keys
);
238 m_oldDragX
= x
; m_oldDragY
= y
;
240 else if (event
.RightUp() && !m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
242 m_dragState
= NoDragging
;
243 m_checkTolerance
= TRUE
;
245 OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
);
246 OnEndDragRight((float)x
, (float)y
, keys
);
247 m_draggedShape
= NULL
;
250 // Non-dragging events
251 else if (event
.IsButton())
253 m_checkTolerance
= TRUE
;
255 // Find the nearest object
257 wxShape
*nearest_object
= FindShape(x
, y
, &attachment
);
258 if (nearest_object
) // Object event
260 if (event
.LeftDown())
262 m_draggedShape
= nearest_object
;
263 m_draggedAttachment
= attachment
;
264 m_dragState
= StartDraggingLeft
;
268 else if (event
.LeftUp())
270 // N.B. Only register a click if the same object was
271 // identified for down *and* up.
272 if (nearest_object
== m_draggedShape
)
273 nearest_object
->GetEventHandler()->OnLeftClick((float)x
, (float)y
, keys
, attachment
);
275 m_draggedShape
= NULL
;
276 m_dragState
= NoDragging
;
278 else if (event
.RightDown())
280 m_draggedShape
= nearest_object
;
281 m_draggedAttachment
= attachment
;
282 m_dragState
= StartDraggingRight
;
286 else if (event
.RightUp())
288 if (nearest_object
== m_draggedShape
)
289 nearest_object
->GetEventHandler()->OnRightClick((float)x
, (float)y
, keys
, attachment
);
291 m_draggedShape
= NULL
;
292 m_dragState
= NoDragging
;
295 else // Canvas event (no nearest object)
297 if (event
.LeftDown())
299 m_draggedShape
= NULL
;
300 m_dragState
= StartDraggingLeft
;
304 else if (event
.LeftUp())
306 OnLeftClick((float)x
, (float)y
, keys
);
308 m_draggedShape
= NULL
;
309 m_dragState
= NoDragging
;
311 else if (event
.RightDown())
313 m_draggedShape
= NULL
;
314 m_dragState
= StartDraggingRight
;
318 else if (event
.RightUp())
320 OnRightClick((float)x
, (float)y
, keys
);
322 m_draggedShape
= NULL
;
323 m_dragState
= NoDragging
;
330 * Try to find a sensitive object, working up the hierarchy of composites.
333 wxShape
*wxShapeCanvas::FindFirstSensitiveShape(float x
, float y
, int *new_attachment
, int op
)
335 wxShape
*image
= FindShape(x
, y
, new_attachment
);
336 if (!image
) return NULL
;
338 wxShape
*actualImage
= FindFirstSensitiveShape1(image
, op
);
342 // Find actual attachment
343 actualImage
->HitTest(x
, y
, new_attachment
, &dist
);
348 wxShape
*wxShapeCanvas::FindFirstSensitiveShape1(wxShape
*image
, int op
)
350 if (image
->GetSensitivityFilter() & op
)
352 if (image
->GetParent())
353 return FindFirstSensitiveShape1(image
->GetParent(), op
);
357 // Helper function: TRUE if 'contains' wholly contains 'contained'.
358 static bool WhollyContains(wxShape
*contains
, wxShape
*contained
)
360 float xp1
, yp1
, xp2
, yp2
;
361 float w1
, h1
, w2
, h2
;
362 float left1
, top1
, right1
, bottom1
, left2
, top2
, right2
, bottom2
;
364 xp1
= contains
->GetX(); yp1
= contains
->GetY(); xp2
= contained
->GetX(); yp2
= contained
->GetY();
365 contains
->GetBoundingBoxMax(&w1
, &h1
);
366 contained
->GetBoundingBoxMax(&w2
, &h2
);
368 left1
= (float)(xp1
- (w1
/ 2.0));
369 top1
= (float)(yp1
- (h1
/ 2.0));
370 right1
= (float)(xp1
+ (w1
/ 2.0));
371 bottom1
= (float)(yp1
+ (h1
/ 2.0));
373 left2
= (float)(xp2
- (w2
/ 2.0));
374 top2
= (float)(yp2
- (h2
/ 2.0));
375 right2
= (float)(xp2
+ (w2
/ 2.0));
376 bottom2
= (float)(yp2
+ (h2
/ 2.0));
378 return ((left1
<= left2
) && (top1
<= top2
) && (right1
>= right2
) && (bottom1
>= bottom2
));
381 wxShape
*wxShapeCanvas::FindShape(float x
, float y
, int *attachment
, wxClassInfo
*info
, wxShape
*notObject
)
383 float nearest
= 100000.0;
384 int nearest_attachment
= 0;
385 wxShape
*nearest_object
= NULL
;
387 // Go backward through the object list, since we want:
388 // (a) to have the control points drawn LAST to overlay
390 // (b) to find the control points FIRST if they exist
392 wxNode
*current
= GetDiagram()->GetShapeList()->Last();
395 wxShape
*object
= (wxShape
*)current
->Data();
400 // First pass for lines, which might be inside a container, so we
401 // want lines to take priority over containers. This first loop
402 // could fail if we clickout side a line, so then we'll
404 if (object
->IsShown() &&
405 object
->IsKindOf(CLASSINFO(wxLineShape
)) &&
406 object
->HitTest(x
, y
, &temp_attachment
, &dist
) &&
407 ((info
== NULL
) || object
->IsKindOf(info
)) &&
408 (!notObject
|| !notObject
->HasDescendant(object
)))
410 // A line is trickier to spot than a normal object.
411 // For a line, since it's the diagonal of the box
412 // we use for the hit test, we may have several
413 // lines in the box and therefore we need to be able
414 // to specify the nearest point to the centre of the line
415 // as our hit criterion, to give the user some room for
420 nearest_object
= object
;
421 nearest_attachment
= temp_attachment
;
425 current
= current
->Previous();
428 current
= GetDiagram()->GetShapeList()->Last();
431 wxShape
*object
= (wxShape
*)current
->Data();
435 // On second pass, only ever consider non-composites or divisions. If children want to pass
436 // up control to the composite, that's up to them.
437 if (object
->IsShown() && (object
->IsKindOf(CLASSINFO(wxDivisionShape
)) || !object
->IsKindOf(CLASSINFO(wxCompositeShape
)))
438 && object
->HitTest(x
, y
, &temp_attachment
, &dist
) && ((info
== NULL
) || object
->IsKindOf(info
)) &&
439 (!notObject
|| !notObject
->HasDescendant(object
)))
441 if (!object
->IsKindOf(CLASSINFO(wxLineShape
)))
443 // If we've hit a container, and we have already found a line in the
444 // first pass, then ignore the container in case the line is in the container.
445 // Check for division in case line straddles divisions (i.e. is not wholly contained).
446 if (!nearest_object
|| !(object
->IsKindOf(CLASSINFO(wxDivisionShape
)) || WhollyContains(object
, nearest_object
)))
449 nearest_object
= object
;
450 nearest_attachment
= temp_attachment
;
456 current
= current
->Previous();
459 *attachment
= nearest_attachment
;
460 return nearest_object
;
464 * Higher-level events called by OnEvent
468 void wxShapeCanvas::OnLeftClick(float x
, float y
, int keys
)
472 void wxShapeCanvas::OnRightClick(float x
, float y
, int keys
)
476 void wxShapeCanvas::OnDragLeft(bool draw
, float x
, float y
, int keys
)
480 void wxShapeCanvas::OnBeginDragLeft(float x
, float y
, int keys
)
484 void wxShapeCanvas::OnEndDragLeft(float x
, float y
, int keys
)
488 void wxShapeCanvas::OnDragRight(bool draw
, float x
, float y
, int keys
)
492 void wxShapeCanvas::OnBeginDragRight(float x
, float y
, int keys
)
496 void wxShapeCanvas::OnEndDragRight(float x
, float y
, int keys
)
500 void wxShapeCanvas::AddShape(wxShape
*object
, wxShape
*addAfter
)
501 { GetDiagram()->AddShape(object
, addAfter
); }
502 void wxShapeCanvas::InsertShape(wxShape
*object
)
503 { GetDiagram()->InsertShape(object
); }
504 void wxShapeCanvas::RemoveShape(wxShape
*object
)
505 { GetDiagram()->RemoveShape(object
); }
506 bool wxShapeCanvas::GetQuickEditMode()
507 { return GetDiagram()->GetQuickEditMode(); }
508 void wxShapeCanvas::Redraw(wxDC
& dc
)
509 { GetDiagram()->Redraw(dc
); }
510 void wxShapeCanvas::Snap(float *x
, float *y
)
511 { GetDiagram()->Snap(x
, y
); }