]>
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"
28 #include <wx/deprecated/wxexpr.h>
39 #include "wx/ogl/ogl.h"
41 #define CONTROL_POINT_SIZE 6
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
50 #define CONTROL_POINT_ENDPOINT_TO 4
51 #define CONTROL_POINT_ENDPOINT_FROM 5
52 #define CONTROL_POINT_LINE 6
54 IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas
, wxScrolledWindow
)
56 BEGIN_EVENT_TABLE(wxShapeCanvas
, wxScrolledWindow
)
57 EVT_PAINT(wxShapeCanvas::OnPaint
)
58 EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent
)
61 const wxChar
* wxShapeCanvasNameStr
= wxT("shapeCanvas");
64 wxShapeCanvas::wxShapeCanvas(wxWindow
*parent
, wxWindowID id
,
68 const wxString
& name
):
69 wxScrolledWindow(parent
, id
, pos
, size
, style
, name
)
71 m_shapeDiagram
= NULL
;
72 m_dragState
= NoDragging
;
73 m_draggedShape
= NULL
;
78 m_checkTolerance
= true;
81 wxShapeCanvas::~wxShapeCanvas()
85 void wxShapeCanvas::OnPaint(wxPaintEvent
& WXUNUSED(event
))
91 dc
.SetBackground(wxBrush(GetBackgroundColour(), wxSOLID
));
95 GetDiagram()->Redraw(dc
);
98 void wxShapeCanvas::OnMouseEvent(wxMouseEvent
& event
)
103 wxPoint
logPos(event
.GetLogicalPosition(dc
));
106 x
= (double) logPos
.x
;
107 y
= (double) logPos
.y
;
110 if (event
.ShiftDown())
111 keys
= keys
| KEY_SHIFT
;
112 if (event
.ControlDown())
113 keys
= keys
| KEY_CTRL
;
115 bool dragging
= event
.Dragging();
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.
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()))
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;
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
138 if (dragging
&& m_draggedShape
&& m_dragState
== StartDraggingLeft
)
140 m_dragState
= ContinueDraggingLeft
;
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
);
147 m_draggedShape
= NULL
;
148 OnBeginDragLeft((double)x
, (double)y
, keys
);
151 m_oldDragX
= x
; m_oldDragY
= y
;
153 else if (dragging
&& m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
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
;
160 else if (event
.LeftUp() && m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
162 m_dragState
= NoDragging
;
163 m_checkTolerance
= true;
165 m_draggedShape
->GetEventHandler()->OnDragLeft(false, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
167 m_draggedShape
->GetEventHandler()->OnEndDragLeft((double)x
, (double)y
, keys
, m_draggedAttachment
);
168 m_draggedShape
= NULL
;
170 else if (dragging
&& m_draggedShape
&& m_dragState
== StartDraggingRight
)
172 m_dragState
= ContinueDraggingRight
;
174 if (m_draggedShape
->Draggable())
175 m_draggedShape
->GetEventHandler()->OnBeginDragRight((double)x
, (double)y
, keys
, m_draggedAttachment
);
178 m_draggedShape
= NULL
;
179 OnBeginDragRight((double)x
, (double)y
, keys
);
181 m_oldDragX
= x
; m_oldDragY
= y
;
183 else if (dragging
&& m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
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
;
190 else if (event
.RightUp() && m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
192 m_dragState
= NoDragging
;
193 m_checkTolerance
= true;
195 m_draggedShape
->GetEventHandler()->OnDragRight(false, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
);
197 m_draggedShape
->GetEventHandler()->OnEndDragRight((double)x
, (double)y
, keys
, m_draggedAttachment
);
198 m_draggedShape
= NULL
;
201 // All following events sent to canvas, not object
202 else if (dragging
&& !m_draggedShape
&& m_dragState
== StartDraggingLeft
)
204 m_dragState
= ContinueDraggingLeft
;
205 OnBeginDragLeft((double)x
, (double)y
, keys
);
206 m_oldDragX
= x
; m_oldDragY
= y
;
208 else if (dragging
&& !m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
211 OnDragLeft(false, m_oldDragX
, m_oldDragY
, keys
);
212 OnDragLeft(true, (double)x
, (double)y
, keys
);
213 m_oldDragX
= x
; m_oldDragY
= y
;
215 else if (event
.LeftUp() && !m_draggedShape
&& m_dragState
== ContinueDraggingLeft
)
217 m_dragState
= NoDragging
;
218 m_checkTolerance
= true;
220 OnDragLeft(false, m_oldDragX
, m_oldDragY
, keys
);
221 OnEndDragLeft((double)x
, (double)y
, keys
);
222 m_draggedShape
= NULL
;
224 else if (dragging
&& !m_draggedShape
&& m_dragState
== StartDraggingRight
)
226 m_dragState
= ContinueDraggingRight
;
227 OnBeginDragRight((double)x
, (double)y
, keys
);
228 m_oldDragX
= x
; m_oldDragY
= y
;
230 else if (dragging
&& !m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
233 OnDragRight(false, m_oldDragX
, m_oldDragY
, keys
);
234 OnDragRight(true, (double)x
, (double)y
, keys
);
235 m_oldDragX
= x
; m_oldDragY
= y
;
237 else if (event
.RightUp() && !m_draggedShape
&& m_dragState
== ContinueDraggingRight
)
239 m_dragState
= NoDragging
;
240 m_checkTolerance
= true;
242 OnDragRight(false, m_oldDragX
, m_oldDragY
, keys
);
243 OnEndDragRight((double)x
, (double)y
, keys
);
244 m_draggedShape
= NULL
;
247 // Non-dragging events
248 else if (event
.IsButton())
250 m_checkTolerance
= true;
252 // Find the nearest object
254 wxShape
*nearest_object
= FindShape(x
, y
, &attachment
);
255 if (nearest_object
) // Object event
257 if (event
.LeftDown())
259 m_draggedShape
= nearest_object
;
260 m_draggedAttachment
= attachment
;
261 m_dragState
= StartDraggingLeft
;
265 else if (event
.LeftUp())
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
);
272 m_draggedShape
= NULL
;
273 m_dragState
= NoDragging
;
275 else if (event
.LeftDClick())
277 nearest_object
->GetEventHandler()->OnLeftDoubleClick((double)x
, (double)y
, keys
, attachment
);
279 m_draggedShape
= NULL
;
280 m_dragState
= NoDragging
;
282 else if (event
.RightDown())
284 m_draggedShape
= nearest_object
;
285 m_draggedAttachment
= attachment
;
286 m_dragState
= StartDraggingRight
;
290 else if (event
.RightUp())
292 if (nearest_object
== m_draggedShape
)
293 nearest_object
->GetEventHandler()->OnRightClick((double)x
, (double)y
, keys
, attachment
);
295 m_draggedShape
= NULL
;
296 m_dragState
= NoDragging
;
299 else // Canvas event (no nearest object)
301 if (event
.LeftDown())
303 m_draggedShape
= NULL
;
304 m_dragState
= StartDraggingLeft
;
308 else if (event
.LeftUp())
310 OnLeftClick((double)x
, (double)y
, keys
);
312 m_draggedShape
= NULL
;
313 m_dragState
= NoDragging
;
315 else if (event
.RightDown())
317 m_draggedShape
= NULL
;
318 m_dragState
= StartDraggingRight
;
322 else if (event
.RightUp())
324 OnRightClick((double)x
, (double)y
, keys
);
326 m_draggedShape
= NULL
;
327 m_dragState
= NoDragging
;
334 * Try to find a sensitive object, working up the hierarchy of composites.
337 wxShape
*wxShapeCanvas::FindFirstSensitiveShape(double x
, double y
, int *new_attachment
, int op
)
339 wxShape
*image
= FindShape(x
, y
, new_attachment
);
340 if (!image
) return NULL
;
342 wxShape
*actualImage
= FindFirstSensitiveShape1(image
, op
);
346 // Find actual attachment
347 actualImage
->HitTest(x
, y
, new_attachment
, &dist
);
352 wxShape
*wxShapeCanvas::FindFirstSensitiveShape1(wxShape
*image
, int op
)
354 if (image
->GetSensitivityFilter() & op
)
356 if (image
->GetParent())
357 return FindFirstSensitiveShape1(image
->GetParent(), op
);
361 // Helper function: true if 'contains' wholly contains 'contained'.
362 static bool WhollyContains(wxShape
*contains
, wxShape
*contained
)
364 double xp1
, yp1
, xp2
, yp2
;
365 double w1
, h1
, w2
, h2
;
366 double left1
, top1
, right1
, bottom1
, left2
, top2
, right2
, bottom2
;
368 xp1
= contains
->GetX(); yp1
= contains
->GetY(); xp2
= contained
->GetX(); yp2
= contained
->GetY();
369 contains
->GetBoundingBoxMax(&w1
, &h1
);
370 contained
->GetBoundingBoxMax(&w2
, &h2
);
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));
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));
382 return ((left1
<= left2
) && (top1
<= top2
) && (right1
>= right2
) && (bottom1
>= bottom2
));
385 wxShape
*wxShapeCanvas::FindShape(double x
, double y
, int *attachment
, wxClassInfo
*info
, wxShape
*notObject
)
387 double nearest
= 100000.0;
388 int nearest_attachment
= 0;
389 wxShape
*nearest_object
= NULL
;
391 // Go backward through the object list, since we want:
392 // (a) to have the control points drawn LAST to overlay
394 // (b) to find the control points FIRST if they exist
396 wxNode
*current
= GetDiagram()->GetShapeList()->GetLast();
399 wxShape
*object
= (wxShape
*)current
->GetData();
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
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
)))
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
424 nearest_object
= object
;
425 nearest_attachment
= temp_attachment
;
429 current
= current
->GetPrevious();
432 current
= GetDiagram()->GetShapeList()->GetLast();
435 wxShape
*object
= (wxShape
*)current
->GetData();
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
)))
445 if (!object
->IsKindOf(CLASSINFO(wxLineShape
)))
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
)))
452 nearest_object
= object
;
453 nearest_attachment
= temp_attachment
;
459 current
= current
->GetPrevious();
462 *attachment
= nearest_attachment
;
463 return nearest_object
;
467 * Higher-level events called by OnEvent
471 void wxShapeCanvas::OnLeftClick(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
475 void wxShapeCanvas::OnRightClick(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
479 void wxShapeCanvas::OnDragLeft(bool WXUNUSED(draw
), double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
483 void wxShapeCanvas::OnBeginDragLeft(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
487 void wxShapeCanvas::OnEndDragLeft(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
491 void wxShapeCanvas::OnDragRight(bool WXUNUSED(draw
), double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
495 void wxShapeCanvas::OnBeginDragRight(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
499 void wxShapeCanvas::OnEndDragRight(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
))
503 void wxShapeCanvas::AddShape(wxShape
*object
, wxShape
*addAfter
)
504 { GetDiagram()->AddShape(object
, addAfter
); }
505 void wxShapeCanvas::InsertShape(wxShape
*object
)
506 { GetDiagram()->InsertShape(object
); }
507 void wxShapeCanvas::RemoveShape(wxShape
*object
)
508 { GetDiagram()->RemoveShape(object
); }
509 bool wxShapeCanvas::GetQuickEditMode()
510 { return GetDiagram()->GetQuickEditMode(); }
511 void wxShapeCanvas::Redraw(wxDC
& dc
)
512 { GetDiagram()->Redraw(dc
); }
513 void wxShapeCanvas::Snap(double *x
, double *y
)
514 { GetDiagram()->Snap(x
, y
); }