]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/ogl/_canvas.py
Use top-level parent for the dialog parent
[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 import wx
15 from _lines import LineShape
16 from _composit import *
17
18 NoDragging, StartDraggingLeft, ContinueDraggingLeft, StartDraggingRight, ContinueDraggingRight = 0, 1, 2, 3, 4
19
20 KEY_SHIFT, KEY_CTRL = 1, 2
21
22
23
24 # Helper function: True if 'contains' wholly contains 'contained'.
25 def WhollyContains(contains, contained):
26 xp1, yp1 = contains.GetX(), contains.GetY()
27 xp2, yp2 = contained.GetX(), contained.GetY()
28
29 w1, h1 = contains.GetBoundingBoxMax()
30 w2, h2 = contained.GetBoundingBoxMax()
31
32 left1 = xp1 - w1 / 2.0
33 top1 = yp1 - h1 / 2.0
34 right1 = xp1 + w1 / 2.0
35 bottom1 = yp1 + h1 / 2.0
36
37 left2 = xp2 - w2 / 2.0
38 top2 = yp2 - h2 / 2.0
39 right2 = xp2 + w2 / 2.0
40 bottom2 = yp2 + h2 / 2.0
41
42 return ((left1 <= left2) and (top1 <= top2) and (right1 >= right2) and (bottom1 >= bottom2))
43
44
45
46 class ShapeCanvas(wx.ScrolledWindow):
47 def __init__(self, parent = None, id = -1, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.BORDER, name = "ShapeCanvas"):
48 wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name)
49
50 self._shapeDiagram = None
51 self._dragState = NoDragging
52 self._draggedShape = None
53 self._oldDragX = 0
54 self._oldDragY = 0
55 self._firstDragX = 0
56 self._firstDragY = 0
57 self._checkTolerance = True
58
59 wx.EVT_PAINT(self, self.OnPaint)
60 wx.EVT_MOUSE_EVENTS(self, self.OnMouseEvent)
61
62 def SetDiagram(self, diag):
63 self._shapeDiagram = diag
64
65 def GetDiagram(self):
66 return self._shapeDiagram
67
68 def OnPaint(self, evt):
69 dc = wx.PaintDC(self)
70 self.PrepareDC(dc)
71
72 dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
73 dc.Clear()
74
75 if self.GetDiagram():
76 self.GetDiagram().Redraw(dc)
77
78 def OnMouseEvent(self, evt):
79 dc = wx.ClientDC(self)
80 self.PrepareDC(dc)
81
82 x, y = evt.GetLogicalPosition(dc)
83
84 keys = 0
85 if evt.ShiftDown():
86 keys |= KEY_SHIFT
87 if evt.ControlDown():
88 keys |= KEY_CTRL
89
90 dragging = evt.Dragging()
91
92 # Check if we're within the tolerance for mouse movements.
93 # If we're very close to the position we started dragging
94 # from, this may not be an intentional drag at all.
95 if dragging:
96 dx = abs(dc.LogicalToDeviceX(x - self._firstDragX))
97 dy = abs(dc.LogicalToDeviceY(y - self._firstDragY))
98 if self._checkTolerance and (dx <= self.GetDiagram().GetMouseTolerance()) and (dy <= self.GetDiagram().GetMouseTolerance()):
99 return
100 # If we've ignored the tolerance once, then ALWAYS ignore
101 # tolerance in this drag, even if we come back within
102 # the tolerance range.
103 self._checkTolerance = False
104
105 # Dragging - note that the effect of dragging is left entirely up
106 # to the object, so no movement is done unless explicitly done by
107 # object.
108 if dragging and self._draggedShape and self._dragState == StartDraggingLeft:
109 self._dragState = ContinueDraggingLeft
110
111 # If the object isn't m_draggable, transfer message to canvas
112 if self._draggedShape.Draggable():
113 self._draggedShape.GetEventHandler().OnBeginDragLeft(x, y, keys, self._draggedAttachment)
114 else:
115 self._draggedShape = None
116 self.OnBeginDragLeft(x, y, keys)
117
118 self._oldDragX, self._oldDragY = x, y
119
120 elif dragging and self._draggedShape and self._dragState == ContinueDraggingLeft:
121 # Continue dragging
122 self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
123 self._draggedShape.GetEventHandler().OnDragLeft(True, x, y, keys, self._draggedAttachment)
124 self._oldDragX, self._oldDragY = x, y
125
126 elif evt.LeftUp() and self._draggedShape and self._dragState == ContinueDraggingLeft:
127 self._dragState = NoDragging
128 self._checkTolerance = True
129
130 self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
131 self._draggedShape.GetEventHandler().OnEndDragLeft(x, y, keys, self._draggedAttachment)
132 self._draggedShape = None
133
134 elif dragging and self._draggedShape and self._dragState == StartDraggingRight:
135 self._dragState = ContinueDraggingRight
136 if self._draggedShape.Draggable:
137 self._draggedShape.GetEventHandler().OnBeginDragRight(x, y, keys, self._draggedAttachment)
138 else:
139 self._draggedShape = None
140 self.OnBeginDragRight(x, y, keys)
141 self._oldDragX, self._oldDragY = x, y
142
143 elif dragging and self._draggedShape and self._dragState == ContinueDraggingRight:
144 # Continue dragging
145 self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
146 self._draggedShape.GetEventHandler().OnDragRight(True, x, y, keys, self._draggedAttachment)
147 self._oldDragX, self._oldDragY = x, y
148
149 elif evt.RightUp() and self._draggedShape and self._dragState == ContinueDraggingRight:
150 self._dragState = NoDragging
151 self._checkTolerance = True
152
153 self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
154 self._draggedShape.GetEventHandler().OnEndDragRight(x, y, keys, self._draggedAttachment)
155 self._draggedShape = None
156
157 # All following events sent to canvas, not object
158 elif dragging and not self._draggedShape and self._dragState == StartDraggingLeft:
159 self._dragState = ContinueDraggingLeft
160 self.OnBeginDragLeft(x, y, keys)
161 self._oldDragX, self._oldDragY = x, y
162
163 elif dragging and not self._draggedShape and self._dragState == ContinueDraggingLeft:
164 # Continue dragging
165 self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
166 self.OnDragLeft(True, x, y, keys)
167 self._oldDragX, self._oldDragY = x, y
168
169 elif evt.LeftUp() and not self._draggedShape and self._dragState == ContinueDraggingLeft:
170 self._dragState = NoDragging
171 self._checkTolerance = True
172
173 self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
174 self.OnEndDragLeft(x, y, keys)
175 self._draggedShape = None
176
177 elif dragging and not self._draggedShape and self._dragState == StartDraggingRight:
178 self._dragState = ContinueDraggingRight
179 self.OnBeginDragRight(x, y, keys)
180 self._oldDragX, self._oldDragY = x, y
181
182 elif dragging and not self._draggedShape and self._dragState == ContinueDraggingRight:
183 # Continue dragging
184 self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
185 self.OnDragRight(True, x, y, keys)
186 self._oldDragX, self._oldDragY = x, y
187
188 elif evt.RightUp() and not self._draggedShape and self._dragState == ContinueDraggingRight:
189 self._dragState = NoDragging
190 self._checkTolerance = True
191
192 self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
193 self.OnEndDragRight(x, y, keys)
194 self._draggedShape = None
195
196 # Non-dragging events
197 elif evt.IsButton():
198 self._checkTolerance = True
199
200 # Find the nearest object
201 attachment = 0
202
203 nearest_object, attachment = self.FindShape(x, y)
204 if nearest_object: # Object event
205 if evt.LeftDown():
206 self._draggedShape = nearest_object
207 self._draggedAttachment = attachment
208 self._dragState = StartDraggingLeft
209 self._firstDragX = x
210 self._firstDragY = y
211
212 elif evt.LeftUp():
213 # N.B. Only register a click if the same object was
214 # identified for down *and* up.
215 if nearest_object == self._draggedShape:
216 nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
217 self._draggedShape = None
218 self._dragState = NoDragging
219
220 elif evt.LeftDClick():
221 nearest_object.GetEventHandler().OnLeftDoubleClick(x, y, keys, attachment)
222 self._draggedShape = None
223 self._dragState = NoDragging
224
225 elif evt.RightDown():
226 self._draggedShape = nearest_object
227 self._draggedAttachment = attachment
228 self._dragState = StartDraggingRight
229 self._firstDragX = x
230 self._firstDragY = y
231
232 elif evt.RightUp():
233 if nearest_object == self._draggedShape:
234 nearest_object.GetEventHandler().OnRightClick(x, y, keys, attachment)
235 self._draggedShape = None
236 self._dragState = NoDragging
237
238 else: # Canvas event
239 if evt.LeftDown():
240 self._draggedShape = None
241 self._dragState = StartDraggingLeft
242 self._firstDragX = x
243 self._firstDragY = y
244
245 elif evt.LeftUp():
246 self.OnLeftClick(x, y, keys)
247 self._draggedShape = None
248 self._dragState = NoDragging
249
250 elif evt.RightDown():
251 self._draggedShape = None
252 self._dragState = StartDraggingRight
253 self._firstDragX = x
254 self._firstDragY = y
255
256 elif evt.RightUp():
257 self.OnRightClick(x, y, keys)
258 self._draggedShape = None
259 self._dragState = NoDragging
260
261 def FindShape(self, x, y, info = None, notObject = None):
262 nearest = 100000.0
263 nearest_attachment = 0
264 nearest_object = None
265
266 # Go backward through the object list, since we want:
267 # (a) to have the control points drawn LAST to overlay
268 # the other objects
269 # (b) to find the control points FIRST if they exist
270
271 rl = self.GetDiagram().GetShapeList()[:]
272 rl.reverse()
273 for object in rl:
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 rl:
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