]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/ogl/_divided.py
added support for drop down toolbar buttons (patch 1713470)
[wxWidgets.git] / wxPython / wx / lib / ogl / _divided.py
1 # -*- coding: iso-8859-1 -*-
2 #----------------------------------------------------------------------------
3 # Name: divided.py
4 # Purpose: DividedShape 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 sys
15 import wx
16
17 from _basic import ControlPoint, RectangleShape, Shape
18 from _oglmisc import *
19
20
21
22 class DividedShapeControlPoint(ControlPoint):
23 def __init__(self, the_canvas, object, region, size, the_m_xoffset, the_m_yoffset, the_type):
24 ControlPoint.__init__(self, the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type)
25 self.regionId = region
26
27 # Implement resizing of divided object division
28 def OnDragLeft(self, draw, x, y, keys = 0, attachment = 0):
29 dc = wx.ClientDC(self.GetCanvas())
30 self.GetCanvas().PrepareDC(dc)
31
32 dc.SetLogicalFunction(OGLRBLF)
33 dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
34 dc.SetPen(dottedPen)
35 dc.SetBrush(wx.TRANSPARENT_BRUSH)
36
37 dividedObject = self._shape
38 x1 = dividedObject.GetX() - dividedObject.GetWidth() / 2.0
39 y1 = y
40 x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2.0
41 y2 = y
42
43 dc.DrawLine(x1, y1, x2, y2)
44
45 def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
46 dc = wx.ClientDC(self.GetCanvas())
47 self.GetCanvas().PrepareDC(dc)
48
49 dc.SetLogicalFunction(OGLRBLF)
50 dottedPen = wx.Pen(wx.Colour(0, 0, 0), 1, wx.DOT)
51 dc.SetPen(dottedPen)
52 dc.SetBrush(wx.TRANSPARENT_BRUSH)
53
54 dividedObject = self._shape
55
56 x1 = dividedObject.GetX() - dividedObject.GetWidth() / 2.0
57 y1 = y
58 x2 = dividedObject.GetX() + dividedObject.GetWidth() / 2.0
59 y2 = y
60
61 dc.DrawLine(x1, y1, x2, y2)
62 self._canvas.CaptureMouse()
63
64 def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
65 dc = wx.ClientDC(self.GetCanvas())
66 self.GetCanvas().PrepareDC(dc)
67
68 dividedObject = self._shape
69 if not dividedObject.GetRegions()[self.regionId]:
70 return
71
72 thisRegion = dividedObject.GetRegions()[self.regionId]
73 nextRegion = None
74
75 dc.SetLogicalFunction(wx.COPY)
76
77 if self._canvas.HasCapture():
78 self._canvas.ReleaseMouse()
79
80 # Find the old top and bottom of this region,
81 # and calculate the new proportion for this region
82 # if legal.
83 currentY = dividedObject.GetY() - dividedObject.GetHeight() / 2.0
84 maxY = dividedObject.GetY() + dividedObject.GetHeight() / 2.0
85
86 # Save values
87 theRegionTop = 0
88 nextRegionBottom = 0
89
90 for i in range(len(dividedObject.GetRegions())):
91 region = dividedObject.GetRegions()[i]
92 proportion = region._regionProportionY
93 yy = currentY + dividedObject.GetHeight() * proportion
94 actualY = min(maxY, yy)
95
96 if region == thisRegion:
97 thisRegionTop = currentY
98
99 if i + 1 < len(dividedObject.GetRegions()):
100 nextRegion = dividedObject.GetRegions()[i + 1]
101 if region == nextRegion:
102 nextRegionBottom = actualY
103
104 currentY = actualY
105
106 if not nextRegion:
107 return
108
109 # Check that we haven't gone above this region or below
110 # next region.
111 if y <= thisRegionTop or y >= nextRegionBottom:
112 return
113
114 dividedObject.EraseLinks(dc)
115
116 # Now calculate the new proportions of this region and the next region
117 thisProportion = float(y - thisRegionTop) / dividedObject.GetHeight()
118 nextProportion = float(nextRegionBottom - y) / dividedObject.GetHeight()
119
120 thisRegion.SetProportions(0, thisProportion)
121 nextRegion.SetProportions(0, nextProportion)
122 self._yoffset = y - dividedObject.GetY()
123
124 # Now reformat text
125 for i, region in enumerate(dividedObject.GetRegions()):
126 if region.GetText():
127 s = region.GetText()
128 dividedObject.FormatText(dc, s, i)
129
130 dividedObject.SetRegionSizes()
131 dividedObject.Draw(dc)
132 dividedObject.GetEventHandler().OnMoveLinks(dc)
133
134
135
136 class DividedShape(RectangleShape):
137 """A DividedShape is a rectangle with a number of vertical divisions.
138 Each division may have its text formatted with independent characteristics,
139 and the size of each division relative to the whole image may be specified.
140
141 Derived from:
142 RectangleShape
143 """
144 def __init__(self, w, h):
145 RectangleShape.__init__(self, w, h)
146 self.ClearRegions()
147
148 def OnDraw(self, dc):
149 RectangleShape.OnDraw(self, dc)
150
151 def OnDrawContents(self, dc):
152 if self.GetRegions():
153 defaultProportion = 1.0 / len(self.GetRegions())
154 else:
155 defaultProportion = 0.0
156 currentY = self._ypos - self._height / 2.0
157 maxY = self._ypos + self._height / 2.0
158
159 leftX = self._xpos - self._width / 2.0
160 rightX = self._xpos + self._width / 2.0
161
162 if self._pen:
163 dc.SetPen(self._pen)
164
165 dc.SetTextForeground(self._textColour)
166
167 # For efficiency, don't do this under X - doesn't make
168 # any visible difference for our purposes.
169 if sys.platform[:3] == "win":
170 dc.SetTextBackground(self._brush.GetColour())
171
172 if self.GetDisableLabel():
173 return
174
175 xMargin = 2
176 yMargin = 2
177
178 dc.SetBackgroundMode(wx.TRANSPARENT)
179
180 for region in self.GetRegions():
181 dc.SetFont(region.GetFont())
182 dc.SetTextForeground(region.GetActualColourObject())
183
184 if region._regionProportionY < 0:
185 proportion = defaultProportion
186 else:
187 proportion = region._regionProportionY
188
189 y = currentY + self._height * proportion
190 actualY = min(maxY, y)
191
192 centreX = self._xpos
193 centreY = currentY + (actualY - currentY) / 2.0
194
195 DrawFormattedText(dc, region._formattedText, centreX, centreY, self._width - 2 * xMargin, actualY - currentY - 2 * yMargin, region._formatMode)
196
197 if y <= maxY and region != self.GetRegions()[-1]:
198 regionPen = region.GetActualPen()
199 if regionPen:
200 dc.SetPen(regionPen)
201 dc.DrawLine(leftX, y, rightX, y)
202
203 currentY = actualY
204
205 def SetSize(self, w, h, recursive = True):
206 self.SetAttachmentSize(w, h)
207 self._width = w
208 self._height = h
209 self.SetRegionSizes()
210
211 def SetRegionSizes(self):
212 """Set all region sizes according to proportions and this object
213 total size.
214 """
215 if not self.GetRegions():
216 return
217
218 if self.GetRegions():
219 defaultProportion = 1.0 / len(self.GetRegions())
220 else:
221 defaultProportion = 0.0
222 currentY = self._ypos - self._height / 2.0
223 maxY = self._ypos + self._height / 2.0
224
225 for region in self.GetRegions():
226 if region._regionProportionY <= 0:
227 proportion = defaultProportion
228 else:
229 proportion = region._regionProportionY
230
231 sizeY = proportion * self._height
232 y = currentY + sizeY
233 actualY = min(maxY, y)
234
235 centreY = currentY + (actualY - currentY) / 2.0
236
237 region.SetSize(self._width, sizeY)
238 region.SetPosition(0, centreY - self._ypos)
239
240 currentY = actualY
241
242 # Attachment points correspond to regions in the divided box
243 def GetAttachmentPosition(self, attachment, nth = 0, no_arcs = 1, line = None):
244 totalNumberAttachments = len(self.GetRegions()) * 2 + 2
245 if self.GetAttachmentMode() == ATTACHMENT_MODE_NONE or attachment >= totalNumberAttachments:
246 return Shape.GetAttachmentPosition(self, attachment, nth, no_arcs)
247
248 n = len(self.GetRegions())
249 isEnd = line and line.IsEnd(self)
250
251 left = self._xpos - self._width / 2.0
252 right = self._xpos + self._width / 2.0
253 top = self._ypos - self._height / 2.0
254 bottom = self._ypos + self._height / 2.0
255
256 # Zero is top, n + 1 is bottom
257 if attachment == 0:
258 y = top
259 if self._spaceAttachments:
260 if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
261 # Align line according to the next handle along
262 point = line.GetNextControlPoint(self)
263 if point[0] < left:
264 x = left
265 elif point[0] > right:
266 x = right
267 else:
268 x = point[0]
269 else:
270 x = left + (nth + 1) * self._width / (no_arcs + 1.0)
271 else:
272 x = self._xpos
273 elif attachment == n + 1:
274 y = bottom
275 if self._spaceAttachments:
276 if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
277 # Align line according to the next handle along
278 point = line.GetNextControlPoint(self)
279 if point[0] < left:
280 x = left
281 elif point[0] > right:
282 x = right
283 else:
284 x = point[0]
285 else:
286 x = left + (nth + 1) * self._width / (no_arcs + 1.0)
287 else:
288 x = self._xpos
289 else: # Left or right
290 isLeft = not attachment < (n + 1)
291 if isLeft:
292 i = totalNumberAttachments - attachment - 1
293 else:
294 i = attachment - 1
295
296 region = self.GetRegions()[i]
297 if region:
298 if isLeft:
299 x = left
300 else:
301 x = right
302
303 # Calculate top and bottom of region
304 top = self._ypos + region._y - region._height / 2.0
305 bottom = self._ypos + region._y + region._height / 2.0
306
307 # Assuming we can trust the absolute size and
308 # position of these regions
309 if self._spaceAttachments:
310 if line and line.GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE:
311 # Align line according to the next handle along
312 point = line.GetNextControlPoint(self)
313 if point[1] < bottom:
314 y = bottom
315 elif point[1] > top:
316 y = top
317 else:
318 y = point[1]
319 else:
320 y = top + (nth + 1) * region._height / (no_arcs + 1.0)
321 else:
322 y = self._ypos + region._y
323 else:
324 return False
325 return x, y
326
327 def GetNumberOfAttachments(self):
328 # There are two attachments for each region (left and right),
329 # plus one on the top and one on the bottom.
330 n = len(self.GetRegions()) * 2 + 2
331
332 maxN = n - 1
333 for point in self._attachmentPoints:
334 if point._id > maxN:
335 maxN = point._id
336
337 return maxN + 1
338
339 def AttachmentIsValid(self, attachment):
340 totalNumberAttachments = len(self.GetRegions()) * 2 + 2
341 if attachment >= totalNumberAttachments:
342 return Shape.AttachmentIsValid(self, attachment)
343 else:
344 return attachment >= 0
345
346 def MakeControlPoints(self):
347 RectangleShape.MakeControlPoints(self)
348 self.MakeMandatoryControlPoints()
349
350 def MakeMandatoryControlPoints(self):
351 currentY = self.GetY() - self._height / 2.0
352 maxY = self.GetY() + self._height / 2.0
353
354 for i, region in enumerate(self.GetRegions()):
355 proportion = region._regionProportionY
356
357 y = currentY + self._height * proportion
358 actualY = min(maxY, y)
359
360 if region != self.GetRegions()[-1]:
361 controlPoint = DividedShapeControlPoint(self._canvas, self, i, CONTROL_POINT_SIZE, 0, actualY - self.GetY(), 0)
362 self._canvas.AddShape(controlPoint)
363 self._controlPoints.append(controlPoint)
364
365 currentY = actualY
366
367 def ResetControlPoints(self):
368 # May only have the region handles, (n - 1) of them
369 if len(self._controlPoints) > len(self.GetRegions()) - 1:
370 RectangleShape.ResetControlPoints(self)
371
372 self.ResetMandatoryControlPoints()
373
374 def ResetMandatoryControlPoints(self):
375 currentY = self.GetY() - self._height / 2.0
376 maxY = self.GetY() + self._height / 2.0
377
378 i = 0
379 for controlPoint in self._controlPoints:
380 if isinstance(controlPoint, DividedShapeControlPoint):
381 region = self.GetRegions()[i]
382 proportion = region._regionProportionY
383
384 y = currentY + self._height * proportion
385 actualY = min(maxY, y)
386
387 controlPoint._xoffset = 0
388 controlPoint._yoffset = actualY - self.GetY()
389
390 currentY = actualY
391
392 i += 1
393
394 def EditRegions(self):
395 """Edit the region colours and styles. Not implemented."""
396 print "EditRegions() is unimplemented"
397
398 def OnRightClick(self, x, y, keys = 0, attachment = 0):
399 if keys & KEY_CTRL:
400 self.EditRegions()
401 else:
402 RectangleShape.OnRightClick(self, x, y, keys, attachment)