]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/ogl/_canvas.py
105cfa22ab3bdbe1bcbf77a21bd89fbeeafcb72b
[wxWidgets.git] / wxPython / wx / lib / ogl / _canvas.py
1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
3 # Name: canvas.py
4 # Purpose: The canvas class
5 #
6 # Author: Pierre Hjälm (from C++ original by Julian Smart)
7 #
8 # Created: 2004-05-08
9 # RCS-ID: $Id$
10 # Copyright: (c) 2004 Pierre Hjälm - 1998 Julian Smart
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------------
13
14 from __future__ import division
15
16 import wx
17 from _lines import LineShape
18 from _composit import *
19
20 NoDragging, StartDraggingLeft, ContinueDraggingLeft, StartDraggingRight, ContinueDraggingRight = 0, 1, 2, 3, 4
21
22 KEY_SHIFT, KEY_CTRL = 1, 2
23
24
25
26 # Helper function: True if 'contains' wholly contains 'contained'.
27 def WhollyContains(contains, contained):
28 xp1, yp1 = contains.GetX(), contains.GetY()
29 xp2, yp2 = contained.GetX(), contained.GetY()
30
31 w1, h1 = contains.GetBoundingBoxMax()
32 w2, h2 = contained.GetBoundingBoxMax()
33
34 left1 = xp1-w1 / 2.0
35 top1 = yp1-h1 / 2.0
36 right1 = xp1 + w1 / 2.0
37 bottom1 = yp1 + h1 / 2.0
38
39 left2 = xp2-w2 / 2.0
40 top2 = yp2-h2 / 2.0
41 right2 = xp2 + w2 / 2.0
42 bottom2 = yp2 + h2 / 2.0
43
44 return ((left1 <= left2) and (top1 <= top2) and (right1 >= right2) and (bottom1 >= bottom2))
45
46
47
48 class ShapeCanvas(wx.ScrolledWindow):
49 def __init__(self, parent = None, id=-1, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.BORDER, name="ShapeCanvas"):
50 wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name)
51
52 self._shapeDiagram = None
53 self._dragState = NoDragging
54 self._draggedShape = None
55 self._oldDragX = 0
56 self._oldDragY = 0
57 self._firstDragX = 0
58 self._firstDragY = 0
59 self._checkTolerance = True
60
61 wx.EVT_PAINT(self, self.OnPaint)
62 wx.EVT_MOUSE_EVENTS(self, self.OnMouseEvent)
63
64 def SetDiagram(self, diag):
65 self._shapeDiagram = diag
66
67 def GetDiagram(self):
68 return self._shapeDiagram
69
70 def OnPaint(self, evt):
71 dc = wx.PaintDC(self)
72 self.PrepareDC(dc)
73
74 dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
75 dc.Clear()
76
77 if self.GetDiagram():
78 self.GetDiagram().Redraw(dc)
79
80 def OnMouseEvent(self, evt):
81 dc = wx.ClientDC(self)
82 self.PrepareDC(dc)
83
84 x, y = evt.GetLogicalPosition(dc)
85
86 keys = 0
87 if evt.ShiftDown():
88 keys |= KEY_SHIFT
89 if evt.ControlDown():
90 keys |= KEY_CTRL
91
92 dragging = evt.Dragging()
93
94 # Check if we're within the tolerance for mouse movements.
95 # If we're very close to the position we started dragging
96 # from, this may not be an intentional drag at all.
97 if dragging:
98 dx = abs(dc.LogicalToDeviceX(x-self._firstDragX))
99 dy = abs(dc.LogicalToDeviceY(y-self._firstDragY))
100 if self._checkTolerance and (dx <= self.GetDiagram().GetMouseTolerance()) and (dy <= self.GetDiagram().GetMouseTolerance()):
101 return
102 # If we've ignored the tolerance once, then ALWAYS ignore
103 # tolerance in this drag, even if we come back within
104 # the tolerance range.
105 self._checkTolerance = False
106
107 # Dragging - note that the effect of dragging is left entirely up
108 # to the object, so no movement is done unless explicitly done by
109 # object.
110 if dragging and self._draggedShape and self._dragState == StartDraggingLeft:
111 self._dragState = ContinueDraggingLeft
112
113 # If the object isn't m_draggable, transfer message to canvas
114 if self._draggedShape.Draggable():
115 self._draggedShape.GetEventHandler().OnBeginDragLeft(x, y, keys, self._draggedAttachment)
116 else:
117 self._draggedShape = None
118 self.OnBeginDragLeft(x, y, keys)
119
120 self._oldDragX, self._oldDragY = x, y
121
122 elif dragging and self._draggedShape and self._dragState == ContinueDraggingLeft:
123 # Continue dragging
124 self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
125 self._draggedShape.GetEventHandler().OnDragLeft(True, x, y, keys, self._draggedAttachment)
126 self._oldDragX, self._oldDragY = x, y
127
128 elif evt.LeftUp and self._draggedShape and self._dragState == ContinueDraggingLeft:
129 self._dragState = NoDragging
130 self._checkTolerance = True
131
132 self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
133 self._draggedShape.GetEventHandler().OnEndDragLeft(x, y, keys, self._draggedAttachment)
134 self._draggedShape = None
135
136 elif dragging and self._draggedShape and self._dragState == StartDraggingRight:
137 self._dragState = ContinueDraggingRight
138 if self._draggedShape.Draggable:
139 self._draggedShape.GetEventHandler().OnBeginDragRight(x, y, keys, self._draggedAttachment)
140 else:
141 self._draggedShape = None
142 self.OnBeginDragRight(x, y, keys)
143 self._oldDragX, self._oldDragY = x, y
144
145 elif dragging and self._draggedShape and self._dragState == ContinueDraggingRight:
146 # Continue dragging
147 self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
148 self._draggedShape.GetEventHandler().OnDragRight(True, x, y, keys, self._draggedAttachment)
149 self._oldDragX, self._oldDragY = x, y
150
151 elif evt.RightUp() and self._draggedShape and self._dragState == ContinueDraggingRight:
152 self._dragState = NoDragging
153 self._checkTolerance = True
154
155 self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
156 self._draggedShape.GetEventHandler().OnEndDragRight(x, y, keys, self._draggedAttachment)
157 self._draggedShape = None
158
159 # All following events sent to canvas, not object
160 elif dragging and not self._draggedShape and self._dragState == StartDraggingLeft:
161 self._dragState = ContinueDraggingLeft
162 self.OnBeginDragLeft(x, y, keys)
163 self._oldDragX, self._oldDragY = x, y
164
165 elif dragging and not self._draggedShape and self._dragState == ContinueDraggingLeft:
166 # Continue dragging
167 self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
168 self.OnDragLeft(True, x, y, keys)
169 self._oldDragX, self._oldDragY = x, y
170
171 elif evt.LeftUp() and not self._draggedShape and self._dragState == ContinueDraggingLeft:
172 self._dragState = NoDragging
173 self._checkTolerance = True
174
175 self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
176 self.OnEndDragLeft(x, y, keys)
177 self._draggedShape = None
178
179 elif dragging and not self._draggedShape and self._dragState == StartDraggingRight:
180 self._dragState = ContinueDraggingRight
181 self.OnBeginDragRight(x, y, keys)
182 self._oldDragX, self._oldDragY = x, y
183
184 elif dragging and not self._draggedShape and self._dragState == ContinueDraggingRight:
185 # Continue dragging
186 self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
187 self.OnDragRight(True, x, y, keys)
188 self._oldDragX, self._oldDragY = x, y
189
190 elif evt.RightUp() and not self._draggedShape and self._dragState == ContinueDraggingRight:
191 self._dragState = NoDragging
192 self._checkTolerance = True
193
194 self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
195 self.OnEndDragRight(x, y, keys)
196 self._draggedShape = None
197
198 # Non-dragging events
199 elif evt.IsButton():
200 self._checkTolerance = True
201
202 # Find the nearest object
203 attachment = 0
204
205 nearest_object, attachment = self.FindShape(x, y)
206 if nearest_object: # Object event
207 if evt.LeftDown():
208 self._draggedShape = nearest_object
209 self._draggedAttachment = attachment
210 self._dragState = StartDraggingLeft
211 self._firstDragX = x
212 self._firstDragY = y
213
214 elif evt.LeftUp():
215 # N.B. Only register a click if the same object was
216 # identified for down *and* up.
217 if nearest_object == self._draggedShape:
218 nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
219 self._draggedShape = None
220 self._dragState = NoDragging
221
222 elif evt.LeftDClick():
223 nearest_object.GetEventHandler().OnLeftDoubleClick(x, y, keys, attachment)
224 self._draggedShape = None
225 self._dragState = NoDragging
226
227 elif evt.RightDown():
228 self._draggedShape = nearest_object
229 self._draggedAttachment = attachment
230 self._dragState = StartDraggingRight
231 self._firstDragX = x
232 self._firstDragY = y
233
234 elif evt.RightUp():
235 if nearest_object == self._draggedShape:
236 nearest_object.GetEventHandler().OnRightClick(x, y, keys, attachment)
237 self._draggedShape = None
238 self._dragState = NoDragging
239
240 else: # Canvas event
241 if evt.LeftDown():
242 self._draggedShape = None
243 self._dragState = StartDraggingLeft
244 self._firstDragX = x
245 self._firstDragY = y
246
247 elif evt.LeftUp():
248 self.OnLeftClick(x, y, keys)
249 self._draggedShape = None
250 self._dragState = NoDragging
251
252 elif evt.RightDown():
253 self._draggedShape = None
254 self._dragState = StartDraggingRight
255 self._firstDragX = x
256 self._firstDragY = y
257
258 elif evt.RightUp():
259 self.OnRightClick(x, y, keys)
260 self._draggedShape = None
261 self._dragState = NoDragging
262
263 def FindShape(self, x, y, info = None, notObject = None):
264 nearest = 100000.0
265 nearest_attachment = 0
266 nearest_object = None
267
268 # Go backward through the object list, since we want:
269 # (a) to have the control points drawn LAST to overlay
270 # the other objects
271 # (b) to find the control points FIRST if they exist
272
273 for object in self.GetDiagram().GetShapeList()[::-1]:
274 # First pass for lines, which might be inside a container, so we
275 # want lines to take priority over containers. This first loop
276 # could fail if we clickout side a line, so then we'll
277 # try other shapes.
278 if object.IsShown() and \
279 isinstance(object, LineShape) and \
280 object.HitTest(x, y) and \
281 ((info == None) or isinstance(object, info)) and \
282 (not notObject or not notObject.HasDescendant(object)):
283 temp_attachment, dist = object.HitTest(x, y)
284 # A line is trickier to spot than a normal object.
285 # For a line, since it's the diagonal of the box
286 # we use for the hit test, we may have several
287 # lines in the box and therefore we need to be able
288 # to specify the nearest point to the centre of the line
289 # as our hit criterion, to give the user some room for
290 # manouevre.
291 if dist<nearest:
292 nearest = dist
293 nearest_object = object
294 nearest_attachment = temp_attachment
295
296 for object in self.GetDiagram().GetShapeList()[::-1]:
297 # On second pass, only ever consider non-composites or
298 # divisions. If children want to pass up control to
299 # the composite, that's up to them.
300 if (object.IsShown() and
301 (isinstance(object, DivisionShape) or
302 not isinstance(object, CompositeShape)) and
303 object.HitTest(x, y) and
304 (info == None or isinstance(object, info)) and
305 (not notObject or not notObject.HasDescendant(object))):
306 temp_attachment, dist = object.HitTest(x, y)
307 if not isinstance(object, LineShape):
308 # If we've hit a container, and we have already
309 # found a line in the first pass, then ignore
310 # the container in case the line is in the container.
311 # Check for division in case line straddles divisions
312 # (i.e. is not wholly contained).
313 if not nearest_object or not (isinstance(object, DivisionShape) or WhollyContains(object, nearest_object)):
314 nearest_object = object
315 nearest_attachment = temp_attachment
316 break
317
318 return nearest_object, nearest_attachment
319
320 def AddShape(self, object, addAfter = None):
321 self.GetDiagram().AddShape(object, addAfter)
322
323 def InsertShape(self, object):
324 self.GetDiagram().InsertShape(object)
325
326 def RemoveShape(self, object):
327 self.GetDiagram().RemoveShape(object)
328
329 def GetQuickEditMode(self):
330 return self.GetDiagram().GetQuickEditMode()
331
332 def Redraw(self, dc):
333 self.GetDiagram().Redraw(dc)
334
335 def Snap(self, x, y):
336 return self.GetDiagram().Snap(x, y)
337
338 def OnLeftClick(self, x, y, keys = 0):
339 pass
340
341 def OnRightClick(self, x, y, keys = 0):
342 pass
343
344 def OnDragLeft(self, draw, x, y, keys = 0):
345 pass
346
347 def OnBeginDragLeft(self, x, y, keys = 0):
348 pass
349
350 def OnEndDragLeft(self, x, y, keys = 0):
351 pass
352
353 def OnDragRight(self, draw, x, y, keys = 0):
354 pass
355
356 def OnBeginDragRight(self, x, y, keys = 0):
357 pass
358
359 def OnEndDragRight(self, x, y, keys = 0):
360 pass