]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/ogl/_canvas.py
added support for drop down toolbar buttons (patch 1713470)
[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 if self._checkTolerance:
97 # the difference between two logical coordinates is a logical coordinate
98 dx = abs(x - self._firstDragX)
99 dy = abs(y - self._firstDragY)
100 toler = self.GetDiagram().GetMouseTolerance()
101 if (dx <= toler) and (dy <= toler):
102 return
103 # If we've ignored the tolerance once, then ALWAYS ignore
104 # tolerance in this drag, even if we come back within
105 # the tolerance range.
106 self._checkTolerance = False
107
108 # Dragging - note that the effect of dragging is left entirely up
109 # to the object, so no movement is done unless explicitly done by
110 # object.
111 if dragging and self._draggedShape and self._dragState == StartDraggingLeft:
112 self._dragState = ContinueDraggingLeft
113
114 # If the object isn't m_draggable, transfer message to canvas
115 if self._draggedShape.Draggable():
116 self._draggedShape.GetEventHandler().OnBeginDragLeft(x, y, keys, self._draggedAttachment)
117 else:
118 self._draggedShape = None
119 self.OnBeginDragLeft(x, y, keys)
120
121 self._oldDragX, self._oldDragY = x, y
122
123 elif dragging and self._draggedShape and self._dragState == ContinueDraggingLeft:
124 # Continue dragging
125 self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
126 self._draggedShape.GetEventHandler().OnDragLeft(True, x, y, keys, self._draggedAttachment)
127 self._oldDragX, self._oldDragY = x, y
128
129 elif evt.LeftUp() and self._draggedShape and self._dragState == ContinueDraggingLeft:
130 self._dragState = NoDragging
131 self._checkTolerance = True
132
133 self._draggedShape.GetEventHandler().OnDragLeft(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
134 self._draggedShape.GetEventHandler().OnEndDragLeft(x, y, keys, self._draggedAttachment)
135 self._draggedShape = None
136
137 elif dragging and self._draggedShape and self._dragState == StartDraggingRight:
138 self._dragState = ContinueDraggingRight
139 if self._draggedShape.Draggable:
140 self._draggedShape.GetEventHandler().OnBeginDragRight(x, y, keys, self._draggedAttachment)
141 else:
142 self._draggedShape = None
143 self.OnBeginDragRight(x, y, keys)
144 self._oldDragX, self._oldDragY = x, y
145
146 elif dragging and self._draggedShape and self._dragState == ContinueDraggingRight:
147 # Continue dragging
148 self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
149 self._draggedShape.GetEventHandler().OnDragRight(True, x, y, keys, self._draggedAttachment)
150 self._oldDragX, self._oldDragY = x, y
151
152 elif evt.RightUp() and self._draggedShape and self._dragState == ContinueDraggingRight:
153 self._dragState = NoDragging
154 self._checkTolerance = True
155
156 self._draggedShape.GetEventHandler().OnDragRight(False, self._oldDragX, self._oldDragY, keys, self._draggedAttachment)
157 self._draggedShape.GetEventHandler().OnEndDragRight(x, y, keys, self._draggedAttachment)
158 self._draggedShape = None
159
160 # All following events sent to canvas, not object
161 elif dragging and not self._draggedShape and self._dragState == StartDraggingLeft:
162 self._dragState = ContinueDraggingLeft
163 self.OnBeginDragLeft(x, y, keys)
164 self._oldDragX, self._oldDragY = x, y
165
166 elif dragging and not self._draggedShape and self._dragState == ContinueDraggingLeft:
167 # Continue dragging
168 self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
169 self.OnDragLeft(True, x, y, keys)
170 self._oldDragX, self._oldDragY = x, y
171
172 elif evt.LeftUp() and not self._draggedShape and self._dragState == ContinueDraggingLeft:
173 self._dragState = NoDragging
174 self._checkTolerance = True
175
176 self.OnDragLeft(False, self._oldDragX, self._oldDragY, keys)
177 self.OnEndDragLeft(x, y, keys)
178 self._draggedShape = None
179
180 elif dragging and not self._draggedShape and self._dragState == StartDraggingRight:
181 self._dragState = ContinueDraggingRight
182 self.OnBeginDragRight(x, y, keys)
183 self._oldDragX, self._oldDragY = x, y
184
185 elif dragging and not self._draggedShape and self._dragState == ContinueDraggingRight:
186 # Continue dragging
187 self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
188 self.OnDragRight(True, x, y, keys)
189 self._oldDragX, self._oldDragY = x, y
190
191 elif evt.RightUp() and not self._draggedShape and self._dragState == ContinueDraggingRight:
192 self._dragState = NoDragging
193 self._checkTolerance = True
194
195 self.OnDragRight(False, self._oldDragX, self._oldDragY, keys)
196 self.OnEndDragRight(x, y, keys)
197 self._draggedShape = None
198
199 # Non-dragging events
200 elif evt.IsButton():
201 self._checkTolerance = True
202
203 # Find the nearest object
204 attachment = 0
205
206 nearest_object, attachment = self.FindShape(x, y)
207 if nearest_object: # Object event
208 if evt.LeftDown():
209 self._draggedShape = nearest_object
210 self._draggedAttachment = attachment
211 self._dragState = StartDraggingLeft
212 self._firstDragX = x
213 self._firstDragY = y
214
215 elif evt.LeftUp():
216 # N.B. Only register a click if the same object was
217 # identified for down *and* up.
218 if nearest_object == self._draggedShape:
219 nearest_object.GetEventHandler().OnLeftClick(x, y, keys, attachment)
220 self._draggedShape = None
221 self._dragState = NoDragging
222
223 elif evt.LeftDClick():
224 nearest_object.GetEventHandler().OnLeftDoubleClick(x, y, keys, attachment)
225 self._draggedShape = None
226 self._dragState = NoDragging
227
228 elif evt.RightDown():
229 self._draggedShape = nearest_object
230 self._draggedAttachment = attachment
231 self._dragState = StartDraggingRight
232 self._firstDragX = x
233 self._firstDragY = y
234
235 elif evt.RightUp():
236 if nearest_object == self._draggedShape:
237 nearest_object.GetEventHandler().OnRightClick(x, y, keys, attachment)
238 self._draggedShape = None
239 self._dragState = NoDragging
240
241 else: # Canvas event
242 if evt.LeftDown():
243 self._draggedShape = None
244 self._dragState = StartDraggingLeft
245 self._firstDragX = x
246 self._firstDragY = y
247
248 elif evt.LeftUp():
249 self.OnLeftClick(x, y, keys)
250 self._draggedShape = None
251 self._dragState = NoDragging
252
253 elif evt.RightDown():
254 self._draggedShape = None
255 self._dragState = StartDraggingRight
256 self._firstDragX = x
257 self._firstDragY = y
258
259 elif evt.RightUp():
260 self.OnRightClick(x, y, keys)
261 self._draggedShape = None
262 self._dragState = NoDragging
263
264 def FindShape(self, x, y, info = None, notObject = None):
265 nearest = 100000.0
266 nearest_attachment = 0
267 nearest_object = None
268
269 # Go backward through the object list, since we want:
270 # (a) to have the control points drawn LAST to overlay
271 # the other objects
272 # (b) to find the control points FIRST if they exist
273
274 rl = self.GetDiagram().GetShapeList()[:]
275 rl.reverse()
276 for object in rl:
277 # First pass for lines, which might be inside a container, so we
278 # want lines to take priority over containers. This first loop
279 # could fail if we clickout side a line, so then we'll
280 # try other shapes.
281 if object.IsShown() and \
282 isinstance(object, LineShape) and \
283 object.HitTest(x, y) and \
284 ((info == None) or isinstance(object, info)) and \
285 (not notObject or not notObject.HasDescendant(object)):
286 temp_attachment, dist = object.HitTest(x, y)
287 # A line is trickier to spot than a normal object.
288 # For a line, since it's the diagonal of the box
289 # we use for the hit test, we may have several
290 # lines in the box and therefore we need to be able
291 # to specify the nearest point to the centre of the line
292 # as our hit criterion, to give the user some room for
293 # manouevre.
294 if dist < nearest:
295 nearest = dist
296 nearest_object = object
297 nearest_attachment = temp_attachment
298
299 for object in rl:
300 # On second pass, only ever consider non-composites or
301 # divisions. If children want to pass up control to
302 # the composite, that's up to them.
303 if (object.IsShown() and
304 (isinstance(object, DivisionShape) or
305 not isinstance(object, CompositeShape)) and
306 object.HitTest(x, y) and
307 (info == None or isinstance(object, info)) and
308 (not notObject or not notObject.HasDescendant(object))):
309 temp_attachment, dist = object.HitTest(x, y)
310 if not isinstance(object, LineShape):
311 # If we've hit a container, and we have already
312 # found a line in the first pass, then ignore
313 # the container in case the line is in the container.
314 # Check for division in case line straddles divisions
315 # (i.e. is not wholly contained).
316 if not nearest_object or not (isinstance(object, DivisionShape) or WhollyContains(object, nearest_object)):
317 nearest_object = object
318 nearest_attachment = temp_attachment
319 break
320
321 return nearest_object, nearest_attachment
322
323 def AddShape(self, object, addAfter = None):
324 self.GetDiagram().AddShape(object, addAfter)
325
326 def InsertShape(self, object):
327 self.GetDiagram().InsertShape(object)
328
329 def RemoveShape(self, object):
330 self.GetDiagram().RemoveShape(object)
331
332 def GetQuickEditMode(self):
333 return self.GetDiagram().GetQuickEditMode()
334
335 def Redraw(self, dc):
336 self.GetDiagram().Redraw(dc)
337
338 def Snap(self, x, y):
339 return self.GetDiagram().Snap(x, y)
340
341 def OnLeftClick(self, x, y, keys = 0):
342 pass
343
344 def OnRightClick(self, x, y, keys = 0):
345 pass
346
347 def OnDragLeft(self, draw, x, y, keys = 0):
348 pass
349
350 def OnBeginDragLeft(self, x, y, keys = 0):
351 pass
352
353 def OnEndDragLeft(self, x, y, keys = 0):
354 pass
355
356 def OnDragRight(self, draw, x, y, keys = 0):
357 pass
358
359 def OnBeginDragRight(self, x, y, keys = 0):
360 pass
361
362 def OnEndDragRight(self, x, y, keys = 0):
363 pass