]>
Commit | Line | Data |
---|---|---|
42f5333f RD |
1 | # --------------------------------------------------------------------------- # |
2 | # FOLDPANELBAR wxPython IMPLEMENTATION | |
3 | # Ported From Jorgen Bodde & Julian Smart (Extended Demo) C++ Code By: | |
4 | # | |
5 | # Andrea Gavana, @ 23 Mar 2005 | |
6 | # Latest Revision: 28 Mar 2005, 22.30 CET | |
7 | # | |
8 | # | |
9 | # TODO List | |
10 | # | |
11 | # All The C++ TODOs Are Still Alive. I Am Not Able to Read Jorges's Mind | |
12 | # So I Don't Really Know What Will Be The New Features/Additions He Will | |
13 | # Make On His Code. At The Moment They Are: | |
14 | # | |
15 | # 1. OnPaint Function In CaptionBar Class: | |
16 | # TODO: Maybe First A Memory Dc Should Draw All, And Then Paint It On The | |
17 | # Caption. This Way A Flickering Arrow During Resize Is Not Visible. | |
18 | # | |
19 | # 2. OnChar Function In CaptionBar Class: | |
20 | # TODO: This Is Easy To Do But I Don't Have Any Useful Idea On Which Kind | |
21 | # Of Features To Add. Does Anyone Have An Intelligent Idea? | |
22 | # | |
23 | # 3. AddFoldPanelWindow Function In FoldPanelBar Class: | |
24 | # TODO: Take Old And New Heights, And If Difference, Reposition All The | |
25 | # Lower Panels. This Is Because The User Can Add New wxWindow Controls | |
26 | # Somewhere In Between When Other Panels Are Already Present. | |
27 | # Don't Know What It Means. Probably Is My Poor English... | |
28 | # | |
29 | # 4. OnSizePanel Function In FoldPanelBar Class: | |
30 | # TODO: A Smart Way To Check Wether The Old - New Width Of The | |
31 | # Panel Changed, If So No Need To Resize The Fold Panel Items | |
32 | # | |
33 | # 5. Implementing Styles Like FPB_SINGLE_FOLD and FPB_EXCLUSIVE_FOLD | |
34 | # TODO: Jorgen Has Left Undone These Jobs. I Don't Really Get What They | |
35 | # Should Supposed To Do, So If Someone Could Enlight Me, Please Let Me Know. | |
36 | # | |
37 | # | |
38 | # For The Original TODO List From Jorgen, Please Refer To: | |
39 | # http://www.solidsteel.nl/jorg/components/foldpanel/wxFoldPanelBar.php#todo_list | |
40 | # | |
41 | # | |
42 | # | |
43 | # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please | |
44 | # Write To Me At: | |
45 | # | |
46 | # andrea.gavana@agip.it | |
47 | # andrea_gavan@tin.it | |
48 | # | |
49 | # Or, Obviously, To The wxPython Mailing List!!! | |
50 | # | |
51 | # | |
52 | # End Of Comments | |
53 | # --------------------------------------------------------------------------- # | |
54 | ||
55 | ||
4e5d278c RD |
56 | """ |
57 | The `FoldPanelBar` is a control that contains multiple panels (of type | |
58 | `FoldPanelItem`) that can be expanded or collapsed. The captionbar of | |
59 | the FoldPanel can be customized by setting it to a horizontal gradient | |
60 | style, vertical gradient style, a single color, a rectangle or filled | |
61 | rectangle. The FoldPanel items can be collapsed in place or to the | |
62 | bottom of the control. `wx.Window` derived controls can be added | |
63 | dynamically, and separated by separator lines. FoldPanelBar is | |
64 | freeware and distributed under the wxPython license. | |
42f5333f RD |
65 | |
66 | ||
4e5d278c RD |
67 | How does it work |
68 | ---------------- | |
42f5333f RD |
69 | |
70 | The internals of the FoldPanelBar is a list of FoldPanelItem objects. Through | |
71 | the reference of FoldPanel these panels can be controlled by adding new controls | |
72 | to a FoldPanel or adding new FoldPanels to the FoldPanelBar. | |
73 | The CaptionBar fires events to the parent (container of all panel items) when a | |
74 | sub-panel needs resizing (either folding or expanding). The fold or expand process | |
75 | is simply a resize of the panel so it looks like all controls on it are gone. All | |
76 | controls are still child of the FoldPanel they are located on. If they don't | |
77 | handle the event (and they won't) then the owner of the FoldPanelBar gets the | |
78 | events. This is what you need to handle the controls. There isn't much to it just | |
79 | a lot of calculations to see what panel belongs where. There are no sizers | |
80 | involved in the panels, everything is purely x-y positioning. | |
81 | ||
82 | ||
4e5d278c RD |
83 | What can it do and what not? |
84 | ---------------------------- | |
42f5333f RD |
85 | |
86 | a) What it can do: | |
4e5d278c RD |
87 | * Run-time addition of panels (no deletion just yet) |
88 | * Run time addition of controls to the panel (it will be resized accordingly) | |
89 | * Creating panels in collapsed mode or expanded mode | |
90 | * Various modes of caption behaviour and filling to make it more appealing | |
91 | * Panels can be folded and collapsed (or all of them) to allow more space | |
42f5333f RD |
92 | |
93 | b) What it cannot do: | |
94 | ||
4e5d278c RD |
95 | * Selection of a panel like in a list ctrl |
96 | * Dragging and dropping the panels | |
97 | * Re-ordering the panels (not yet) | |
42f5333f RD |
98 | |
99 | ||
4e5d278c RD |
100 | Supported platforms |
101 | ------------------- | |
42f5333f RD |
102 | |
103 | FoldPanelBar is supported on the following platforms: | |
4e5d278c RD |
104 | * Windows (Verified on Windows XP, 2000) |
105 | * Linux/Unix (GTK2) (Thanks To Toni Brkic And Robin Dunn) | |
106 | * Mac OSX (Thanks To Robin Dunn For The CaptionBar Size Patch) | |
42f5333f RD |
107 | |
108 | ||
109 | Latest Revision: Andrea Gavana @ 30 Mar 2005, 22.30 CET | |
110 | ||
111 | """ | |
112 | ||
113 | import wx | |
114 | ||
115 | #---------------------------------------------------------------------- | |
116 | # Collapsed And Expanded Bitmap Images | |
117 | # Created With img2py.py | |
118 | #---------------------------------------------------------------------- | |
119 | ||
120 | def GetCollapsedIconData(): | |
121 | return \ | |
122 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
123 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
124 | \x00\x007IDAT8\x8dcddbf\xa0\x040Q\xa4{\xf0\x1b\xf0\xff\xdf\xdf\xff\x03\xe7\ | |
125 | \x02\x98\xed\x84\\A\x1b\x17\xa0\xdb\x8a\xcf\x15\xd4w\x01.\xdbp\x89S\xec\x02\ | |
126 | \xc6\xd1\xbc\xc0\x00\x00\x9a\xf5\x1b\xfa\xf9m$?\x00\x00\x00\x00IEND\xaeB`\ | |
127 | \x82' | |
128 | ||
129 | def GetCollapsedIconBitmap(): | |
130 | return wx.BitmapFromImage(GetCollapsedIconImage()) | |
131 | ||
132 | def GetCollapsedIconImage(): | |
133 | import cStringIO | |
134 | stream = cStringIO.StringIO(GetCollapsedIconData()) | |
135 | return wx.ImageFromStream(stream) | |
136 | ||
137 | #---------------------------------------------------------------------- | |
138 | def GetExpandedIconData(): | |
139 | return \ | |
140 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
141 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
142 | \x00\x00BIDAT8\x8dcddbf\xa0\x040Q\xa4{P\x18\xc0\x82.\xf0\xff\xdf\xdf\xff\xb8\ | |
143 | \x143213R\xdd\x05\x18\x06`\xb3\x05\x9f8m\x02\x11\xdd6\\\xb6\xd3\xce\x05\xc8\ | |
144 | \xb6\xe2\xb3\x9d*.`\x1c\xcd\x0b\x0c\x00\x9e\xbc\x04W\x19\xcfa\xb5\x00\x00\ | |
145 | \x00\x00IEND\xaeB`\x82' | |
146 | ||
147 | def GetExpandedIconBitmap(): | |
148 | return wx.BitmapFromImage(GetExpandedIconImage()) | |
149 | ||
150 | def GetExpandedIconImage(): | |
151 | import cStringIO | |
152 | stream = cStringIO.StringIO(GetExpandedIconData()) | |
153 | return wx.ImageFromStream(stream) | |
154 | ||
155 | #---------------------------------------------------------------------- | |
156 | ||
157 | #---------------------------------------------------------------------- | |
158 | # FOLDPANELBAR Starts Here | |
159 | #---------------------------------------------------------------------- | |
160 | ||
161 | # CAPTIONBAR STYLES | |
162 | # | |
163 | #- CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom | |
164 | #- CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from left to right | |
165 | #- CAPTIONBAR_SINGLE: Draws a single filled rectangle to draw the caption | |
166 | #- CAPTIONBAR_RECTANGLE: Draws a single colour with a rectangle around the caption | |
167 | #- CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle and a border around it | |
168 | ||
169 | CAPTIONBAR_NOSTYLE = 0 | |
170 | CAPTIONBAR_GRADIENT_V = 1 | |
171 | CAPTIONBAR_GRADIENT_H = 2 | |
172 | CAPTIONBAR_SINGLE = 3 | |
173 | CAPTIONBAR_RECTANGLE = 4 | |
174 | CAPTIONBAR_FILLED_RECTANGLE = 5 | |
175 | ||
176 | FPB_EXTRA_X = 10 | |
177 | FPB_EXTRA_Y = 4 | |
178 | ||
179 | # pixels of the bmp to be aligned from the right filled with space | |
180 | FPB_BMP_RIGHTSPACE = 2 | |
181 | ||
182 | # Not yet supported but added for future reference. Single fold forces | |
183 | # other panels to close when they are open, and only opens the current panel. | |
184 | # This will allow the open panel to gain the full size left in the client area | |
185 | FPB_SINGLE_FOLD = 0x0001 | |
186 | ||
187 | # All panels are stacked to the bottom. When they are expanded again they | |
188 | # show up at the top | |
189 | FPB_COLLAPSE_TO_BOTTOM = 0x0002 | |
190 | ||
191 | # Not yet supported, but added for future reference. Single fold plus panels | |
192 | # will be stacked at the bottom | |
193 | FPB_EXCLUSIVE_FOLD = FPB_SINGLE_FOLD | FPB_COLLAPSE_TO_BOTTOM | |
194 | ||
195 | # Orientation Flag | |
196 | FPB_HORIZONTAL = wx.HORIZONTAL | |
197 | FPB_VERTICAL = wx.VERTICAL | |
198 | ||
199 | # Default Extrastyle of the FoldPanelBar | |
200 | FPB_DEFAULT_EXTRASTYLE = 0 | |
201 | # Default style of the FoldPanelBar | |
202 | FPB_DEFAULT_STYLE = wx.TAB_TRAVERSAL | wx.NO_BORDER | |
203 | ||
204 | # FoldPanelItem default settings | |
205 | FPB_ALIGN_LEFT = 0 | |
206 | FPB_ALIGN_WIDTH = 1 | |
207 | ||
208 | FPB_DEFAULT_LEFTSPACING = 5 | |
209 | FPB_DEFAULT_RIGHTSPACING = 10 | |
210 | FPB_DEFAULT_SPACING = 8 | |
211 | ||
212 | FPB_DEFAULT_LEFTLINESPACING = 2 | |
213 | FPB_DEFAULT_RIGHTLINESPACING = 2 | |
214 | ||
215 | ||
216 | # ------------------------------------------------------------------------------ # | |
217 | # class CaptionBarStyle | |
42f5333f RD |
218 | # ------------------------------------------------------------------------------ # |
219 | ||
220 | class CaptionBarStyle: | |
4e5d278c RD |
221 | """ |
222 | This class encapsulates the styles you wish to set for the | |
223 | `CaptionBar` (this is the part of the FoldPanel where the caption | |
224 | is displayed). It can either be applied at creation time be | |
225 | reapplied when styles need to be changed. | |
226 | ||
227 | At construction time, all styles are set to their default | |
228 | transparency. This means none of the styles will be applied to | |
229 | the `CaptionBar` in question, meaning it will be created using the | |
230 | default internals. When setting i.e the color, font or panel | |
231 | style, these styles become active to be used. | |
232 | ||
233 | """ | |
42f5333f RD |
234 | |
235 | def __init__(self): | |
236 | """ Default constructor for this class.""" | |
237 | ||
238 | self.ResetDefaults() | |
239 | ||
240 | ||
241 | def ResetDefaults(self): | |
4e5d278c | 242 | """ Resets default CaptionBarStyle.""" |
42f5333f RD |
243 | self._firstColourUsed = False |
244 | self._secondColourUsed = False | |
245 | self._textColourUsed = False | |
246 | self._captionFontUsed = False | |
247 | self._captionStyleUsed = False | |
248 | self._captionStyle = CAPTIONBAR_GRADIENT_V | |
249 | ||
250 | ||
251 | # ------- CaptionBar Font ------- | |
252 | ||
253 | def SetCaptionFont(self, font): | |
254 | """ | |
255 | Sets font for the caption bar. | |
256 | ||
4e5d278c RD |
257 | If this is not set, the font property is undefined and will |
258 | not be used. Use `CaptionFontUsed` to check if this style is | |
259 | used. | |
260 | """ | |
42f5333f RD |
261 | self._captionFont = font |
262 | self._captionFontUsed = True | |
263 | ||
264 | ||
265 | def CaptionFontUsed(self): | |
4e5d278c | 266 | """ Checks if the caption bar font is set. """ |
42f5333f RD |
267 | return self._captionFontUsed |
268 | ||
269 | ||
270 | def GetCaptionFont(self): | |
271 | """ | |
272 | Returns the font for the caption bar. | |
273 | ||
274 | Please be warned this will result in an assertion failure when | |
275 | this property is not previously set. | |
42f5333f | 276 | |
4e5d278c RD |
277 | :see: `SetCaptionFont`, `CaptionFontUsed` |
278 | """ | |
42f5333f RD |
279 | return self._captionFont |
280 | ||
281 | ||
282 | # ------- First Colour ------- | |
283 | ||
284 | def SetFirstColour(self, colour): | |
285 | """ | |
286 | Sets first colour for the caption bar. | |
287 | ||
4e5d278c RD |
288 | If this is not set, the colour property is undefined and will |
289 | not be used. Use `FirstColourUsed` to check if this style is | |
290 | used. | |
291 | """ | |
42f5333f RD |
292 | self._firstColour = colour |
293 | self._firstColourUsed = True | |
294 | ||
295 | ||
296 | def FirstColourUsed(self): | |
4e5d278c | 297 | """ Checks if the first colour of the caption bar is set.""" |
42f5333f RD |
298 | return self._firstColourUsed |
299 | ||
300 | ||
301 | def GetFirstColour(self): | |
302 | """ | |
303 | Returns the first colour for the caption bar. | |
304 | ||
4e5d278c RD |
305 | Please be warned this will result in an assertion failure when |
306 | this property is not previously set. | |
42f5333f | 307 | |
4e5d278c RD |
308 | :see: `SetFirstColour`, `FirstColourUsed` |
309 | """ | |
42f5333f RD |
310 | return self._firstColour |
311 | ||
312 | ||
313 | # ------- Second Colour ------- | |
314 | ||
315 | def SetSecondColour(self, colour): | |
316 | """ | |
317 | Sets second colour for the caption bar. | |
318 | ||
4e5d278c RD |
319 | If this is not set, the colour property is undefined and will |
320 | not be used. Use `SecondColourUsed` to check if this style is | |
321 | used. | |
322 | """ | |
42f5333f RD |
323 | self._secondColour = colour |
324 | self._secondColourUsed = True | |
325 | ||
326 | ||
327 | def SecondColourUsed(self): | |
4e5d278c | 328 | """ Checks if the second colour of the caption bar is set.""" |
42f5333f RD |
329 | return self._secondColourUsed |
330 | ||
4e5d278c | 331 | |
42f5333f RD |
332 | def GetSecondColour(self): |
333 | """ | |
334 | Returns the second colour for the caption bar. | |
335 | ||
4e5d278c RD |
336 | Please be warned this will result in an assertion failure when |
337 | this property is not previously set. | |
42f5333f | 338 | |
4e5d278c RD |
339 | :see: `SetSecondColour`, `SecondColourUsed` |
340 | """ | |
42f5333f RD |
341 | return self._secondColour |
342 | ||
343 | ||
344 | # ------- Caption Text Colour ------- | |
345 | ||
346 | def SetCaptionColour(self, colour): | |
347 | """ | |
348 | Sets caption colour for the caption bar. | |
349 | ||
4e5d278c RD |
350 | If this is not set, the colour property is undefined and will |
351 | not be used. Use `CaptionColourUsed` to check if this style is | |
352 | used. | |
353 | """ | |
42f5333f RD |
354 | self._textColour = colour |
355 | self._textColourUsed = True | |
356 | ||
357 | ||
358 | def CaptionColourUsed(self): | |
4e5d278c | 359 | """ Checks if the caption colour of the caption bar is set.""" |
42f5333f RD |
360 | return self._textColourUsed |
361 | ||
362 | ||
363 | def GetCaptionColour(self): | |
364 | """ | |
365 | Returns the caption colour for the caption bar. | |
366 | ||
367 | Please be warned this will result in an assertion failure | |
368 | when this property is not previously set. | |
369 | See also SetCaptionColour(), CaptionColourUsed() | |
4e5d278c | 370 | """ |
42f5333f RD |
371 | return self._textColour |
372 | ||
373 | ||
374 | # ------- CaptionStyle ------- | |
375 | ||
376 | def SetCaptionStyle(self, style): | |
377 | """ | |
378 | Sets caption style for the caption bar. | |
379 | ||
4e5d278c RD |
380 | If this is not set, the property is undefined and will not be |
381 | used. Use CaptionStyleUsed() to check if this style is used. | |
42f5333f | 382 | The following styles can be applied: |
4e5d278c RD |
383 | |
384 | * CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom | |
385 | ||
386 | * CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from | |
387 | left to right | |
388 | ||
389 | * CAPTIONBAR_SINGLE: Draws a single filled rectangle to | |
390 | draw the caption | |
391 | ||
392 | * CAPTIONBAR_RECTANGLE: Draws a single colour with a | |
393 | rectangle around the caption | |
394 | ||
395 | * CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle | |
396 | and a border around it | |
397 | ||
398 | """ | |
42f5333f RD |
399 | self._captionStyle = style |
400 | self._captionStyleUsed = True | |
401 | ||
402 | ||
403 | def CaptionStyleUsed(self): | |
4e5d278c | 404 | """ Checks if the caption style of the caption bar is set.""" |
42f5333f RD |
405 | return self._captionStyleUsed |
406 | ||
407 | ||
408 | def GetCaptionStyle(self): | |
409 | """ | |
410 | Returns the caption style for the caption bar. | |
411 | ||
412 | Please be warned this will result in an assertion failure | |
413 | when this property is not previously set. | |
42f5333f | 414 | |
4e5d278c RD |
415 | :see: `SetCaptionStyle`, `CaptionStyleUsed` |
416 | """ | |
42f5333f RD |
417 | return self._captionStyle |
418 | ||
419 | ||
420 | #-----------------------------------# | |
421 | # CaptionBarEvent | |
422 | #-----------------------------------# | |
423 | wxEVT_CAPTIONBAR = wx.NewEventType() | |
424 | EVT_CAPTIONBAR = wx.PyEventBinder(wxEVT_CAPTIONBAR, 0) | |
425 | ||
426 | ||
427 | # ---------------------------------------------------------------------------- # | |
428 | # class CaptionBarEvent | |
42f5333f RD |
429 | # ---------------------------------------------------------------------------- # |
430 | ||
431 | class CaptionBarEvent(wx.PyCommandEvent): | |
4e5d278c RD |
432 | """ |
433 | This event will be sent when a EVT_CAPTIONBAR is mapped in the parent. | |
434 | It is to notify the parent that the bar is now in collapsed or expanded | |
435 | state. The parent should re-arrange the associated windows accordingly | |
436 | """ | |
42f5333f | 437 | def __init__(self, evtType): |
4e5d278c | 438 | """ Default Constructor For This Class.""" |
42f5333f RD |
439 | wx.PyCommandEvent.__init__(self, evtType) |
440 | ||
441 | ||
442 | def GetFoldStatus(self): | |
4e5d278c RD |
443 | """ |
444 | Returns whether the bar is expanded or collapsed. True means | |
445 | expanded. | |
446 | """ | |
42f5333f RD |
447 | return not self._bar.IsCollapsed() |
448 | ||
449 | ||
450 | def GetBar(self): | |
4e5d278c | 451 | """ Returns The CaptionBar Selected.""" |
42f5333f RD |
452 | return self._bar |
453 | ||
454 | ||
455 | def SetTag(self, tag): | |
4e5d278c | 456 | """ Assign A Tag To The Selected CaptionBar.""" |
42f5333f RD |
457 | self._tag = tag |
458 | ||
459 | ||
460 | def GetTag(self): | |
4e5d278c | 461 | """ Returns The Tag Assigned To The Selected CaptionBar.""" |
42f5333f RD |
462 | return self._tag |
463 | ||
464 | ||
465 | def SetBar(self, bar): | |
466 | """ | |
4e5d278c | 467 | Sets the bar associated with this event. |
42f5333f | 468 | |
4e5d278c RD |
469 | Should not used by any other then the originator of the event. |
470 | """ | |
42f5333f RD |
471 | self._bar = bar |
472 | ||
473 | ||
474 | # -------------------------------------------------------------------------------- # | |
475 | # class CaptionBar | |
42f5333f RD |
476 | # -------------------------------------------------------------------------------- # |
477 | ||
478 | class CaptionBar(wx.Window): | |
4e5d278c RD |
479 | """ |
480 | This class is a graphical caption component that consists of a | |
481 | caption and a clickable arrow. | |
482 | ||
483 | The CaptionBar fires an event EVT_CAPTIONBAR which is a | |
484 | `CaptionBarEvent`. This event can be caught and the parent window | |
485 | can act upon the collapsed or expanded state of the bar (which is | |
486 | actually just the icon which changed). The parent panel can | |
487 | reduce size or expand again. | |
488 | """ | |
489 | ||
42f5333f RD |
490 | # Define Empty CaptionBar Style |
491 | EmptyCaptionBarStyle = CaptionBarStyle() | |
492 | ||
493 | def __init__(self, parent, id, pos, size, caption="", | |
494 | foldIcons=None, cbstyle=EmptyCaptionBarStyle, | |
495 | rightIndent=FPB_BMP_RIGHTSPACE, | |
496 | iconWidth=16, iconHeight=16, collapsed=False): | |
497 | """ Default Class Constructor.""" | |
498 | ||
499 | wx.Window.__init__(self, parent, wx.ID_ANY, pos=wx.DefaultPosition, | |
500 | size=(20,20), style=wx.NO_BORDER) | |
501 | ||
502 | self._controlCreated = False | |
503 | self._collapsed = collapsed | |
504 | self.ApplyCaptionStyle(cbstyle, True) | |
505 | ||
506 | if foldIcons is None: | |
507 | foldIcons = wx.ImageList(16, 16) | |
508 | ||
509 | bmp = GetExpandedIconBitmap() | |
510 | foldIcons.Add(bmp) | |
511 | bmp = GetCollapsedIconBitmap() | |
512 | foldIcons.Add(bmp) | |
513 | ||
514 | # set initial size | |
515 | if foldIcons: | |
516 | assert foldIcons.GetImageCount() > 1 | |
517 | iconWidth, iconHeight = foldIcons.GetSize(0) | |
518 | ||
519 | self._caption = caption | |
520 | self._foldIcons = foldIcons | |
521 | self._style = cbstyle | |
522 | self._rightIndent = rightIndent | |
523 | self._iconWidth = iconWidth | |
524 | self._iconHeight = iconHeight | |
525 | self._oldSize = wx.Size(20,20) | |
526 | ||
527 | self._controlCreated = True | |
528 | ||
529 | self.Bind(wx.EVT_PAINT, self.OnPaint) | |
530 | self.Bind(wx.EVT_SIZE, self.OnSize) | |
531 | self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent) | |
532 | self.Bind(wx.EVT_CHAR, self.OnChar) | |
533 | ||
534 | ||
535 | def ApplyCaptionStyle(self, cbstyle=EmptyCaptionBarStyle, applyDefault=True): | |
536 | """ Applies the style defined in cbstyle to the CaptionBar.""" | |
537 | ||
538 | newstyle = cbstyle | |
539 | ||
540 | if applyDefault: | |
541 | ||
542 | # get first colour from style or make it default | |
543 | if not newstyle.FirstColourUsed(): | |
544 | newstyle.SetFirstColour(wx.WHITE) | |
545 | ||
546 | # get second colour from style or make it default | |
547 | if not newstyle.SecondColourUsed(): | |
548 | # make the second colour slightly darker then the background | |
549 | color = self.GetParent().GetBackgroundColour() | |
550 | r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) | |
551 | color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) | |
552 | newstyle.SetSecondColour(wx.Colour(color[0], color[1], color[2])) | |
553 | ||
554 | # get text colour | |
555 | if not newstyle.CaptionColourUsed(): | |
556 | newstyle.SetCaptionColour(wx.BLACK) | |
557 | ||
558 | # get font colour | |
559 | if not newstyle.CaptionFontUsed(): | |
560 | newstyle.SetCaptionFont(self.GetParent().GetFont()) | |
561 | ||
562 | # apply caption style | |
563 | if not newstyle.CaptionStyleUsed(): | |
564 | newstyle.SetCaptionStyle(CAPTIONBAR_GRADIENT_V) | |
565 | ||
566 | self._style = newstyle | |
567 | ||
568 | ||
569 | def SetCaptionStyle(self, cbstyle=EmptyCaptionBarStyle, applyDefault=True): | |
570 | """ | |
571 | Sets CaptionBar styles with CapionBarStyle class. | |
572 | ||
4e5d278c RD |
573 | All styles that are actually set, are applied. If you set |
574 | applyDefault to True, all other (not defined) styles will be | |
575 | set to default. If it is False, the styles which are not set | |
576 | in the CaptionBarStyle will be ignored. | |
577 | """ | |
42f5333f RD |
578 | self.ApplyCaptionStyle(cbstyle, applyDefault) |
579 | self.Refresh() | |
580 | ||
581 | ||
582 | def GetCaptionStyle(self): | |
583 | """ | |
4e5d278c RD |
584 | Returns the current style of the captionbar in a |
585 | `CaptionBarStyle` class. | |
42f5333f RD |
586 | |
587 | This can be used to change and set back the changes. | |
4e5d278c | 588 | """ |
42f5333f RD |
589 | return self._style |
590 | ||
591 | ||
592 | def IsCollapsed(self): | |
593 | """ | |
4e5d278c RD |
594 | Returns wether the status of the bar is expanded or collapsed. |
595 | """ | |
42f5333f RD |
596 | return self._collapsed |
597 | ||
598 | ||
599 | def SetRightIndent(self, pixels): | |
600 | """ | |
4e5d278c RD |
601 | Sets the amount of pixels on the right from which the bitmap |
602 | is trailing. | |
42f5333f | 603 | |
4e5d278c RD |
604 | If this is 0, it will be drawn all the way to the right, |
605 | default is equal to FPB_BMP_RIGHTSPACE. Assign this before | |
606 | assigning an image list to prevent a redraw. | |
42f5333f | 607 | """ |
42f5333f RD |
608 | assert pixels >= 0 |
609 | self._rightIndent = pixels | |
610 | if self._foldIcons: | |
611 | self.Refresh() | |
612 | ||
613 | ||
614 | def Collapse(self): | |
615 | """ | |
616 | This sets the internal state / representation to collapsed. | |
617 | ||
4e5d278c RD |
618 | This does not trigger a `CaptionBarEvent` to be sent to the |
619 | parent. | |
620 | """ | |
42f5333f RD |
621 | self._collapsed = True |
622 | self.RedrawIconBitmap() | |
623 | ||
624 | ||
625 | def Expand(self): | |
626 | """ | |
627 | This sets the internal state / representation to expanded. | |
628 | ||
4e5d278c RD |
629 | This does not trigger a `CaptionBarEvent` to be sent to the |
630 | parent. | |
631 | """ | |
42f5333f RD |
632 | self._collapsed = False |
633 | self.RedrawIconBitmap() | |
634 | ||
635 | ||
636 | def SetBoldFont(self): | |
637 | """ Sets the CaptionBarFont weight to BOLD.""" | |
638 | ||
639 | self.GetFont().SetWeight(wx.BOLD) | |
640 | ||
641 | ||
642 | def SetNormalFont(self): | |
643 | """ Sets the CaptionBarFont weight to NORMAL.""" | |
644 | ||
645 | self.GetFont().SetWeight(wx.NORMAL) | |
646 | ||
647 | ||
648 | def IsVertical(self): | |
649 | """ | |
650 | Returns wether the CaptionBar Has Default Orientation Or Not. | |
651 | ||
652 | Default is vertical. | |
653 | """ | |
654 | ||
655 | fld = self.GetParent().GetGrandParent() | |
656 | if isinstance(fld, FoldPanelBar): | |
657 | return self.GetParent().GetGrandParent().IsVertical() | |
658 | else: | |
659 | raise "ERROR: Wrong Parent " + repr(fld) | |
660 | ||
661 | ||
662 | def OnPaint(self, event): | |
663 | """ The paint event for flat or gradient fill. """ | |
664 | ||
665 | if not self._controlCreated: | |
666 | event.Skip() | |
667 | return | |
668 | ||
669 | dc = wx.PaintDC(self) | |
670 | wndRect = self.GetRect() | |
671 | vertical = self.IsVertical() | |
672 | ||
673 | # TODO: Maybe first a memory DC should draw all, and then paint it on | |
674 | # the caption. This way a flickering arrow during resize is not visible | |
675 | ||
676 | self.FillCaptionBackground(dc) | |
677 | dc.SetFont(self._style.GetCaptionFont()) | |
678 | ||
679 | if vertical: | |
680 | dc.DrawText(self._caption, 4, FPB_EXTRA_Y/2) | |
681 | else: | |
682 | dc.DrawRotatedText(self._caption, FPB_EXTRA_Y/2, | |
683 | wndRect.GetBottom() - 4, 90) | |
684 | ||
685 | # draw small icon, either collapsed or expanded | |
686 | # based on the state of the bar. If we have any bmp's | |
687 | ||
688 | if self._foldIcons: | |
689 | ||
690 | index = self._collapsed | |
691 | ||
692 | if vertical: | |
693 | drw = wndRect.GetRight() - self._iconWidth - self._rightIndent | |
694 | self._foldIcons.Draw(index, dc, drw, | |
695 | (wndRect.GetHeight() - self._iconHeight)/2, | |
696 | wx.IMAGELIST_DRAW_TRANSPARENT) | |
697 | else: | |
698 | self._foldIcons.Draw(index, dc, | |
699 | (wndRect.GetWidth() - self._iconWidth)/2, | |
700 | self._rightIndent, wx.IMAGELIST_DRAW_TRANSPARENT) | |
701 | ||
702 | ## event.Skip() | |
703 | ||
704 | ||
705 | def FillCaptionBackground(self, dc): | |
4e5d278c RD |
706 | """ |
707 | Fills the background of the caption with either a gradient or | |
708 | a solid color. | |
709 | """ | |
42f5333f RD |
710 | |
711 | style = self._style.GetCaptionStyle() | |
712 | ||
713 | if style == CAPTIONBAR_GRADIENT_V: | |
714 | if self.IsVertical(): | |
715 | self.DrawVerticalGradient(dc, self.GetRect()) | |
716 | else: | |
717 | self.DrawHorizontalGradient(dc, self.GetRect()) | |
718 | ||
719 | elif style == CAPTIONBAR_GRADIENT_H: | |
720 | if self.IsVertical(): | |
721 | self.DrawHorizontalGradient(dc, self.GetRect()) | |
722 | else: | |
723 | self.DrawVerticalGradient(dc, self.GetRect()) | |
724 | ||
725 | elif style == CAPTIONBAR_SINGLE: | |
726 | self.DrawSingleColour(dc, self.GetRect()) | |
727 | elif style == CAPTIONBAR_RECTANGLE or style == CAPTIONBAR_FILLED_RECTANGLE: | |
728 | self.DrawSingleRectangle(dc, self.GetRect()) | |
729 | else: | |
730 | raise "STYLE Error: Undefined Style Selected: " + repr(style) | |
731 | ||
732 | ||
733 | def OnMouseEvent(self, event): | |
734 | """ | |
735 | Catches the mouse click-double click. | |
736 | ||
4e5d278c RD |
737 | If clicked on the arrow (single) or double on the caption we |
738 | change state and an event must be fired to let this panel | |
739 | collapse or expand. | |
42f5333f RD |
740 | """ |
741 | ||
742 | send_event = False | |
743 | ||
744 | if event.LeftDown() and self._foldIcons: | |
745 | ||
746 | pt = event.GetPosition() | |
747 | rect = self.GetRect() | |
748 | vertical = self.IsVertical() | |
749 | ||
750 | drw = (rect.GetWidth() - self._iconWidth - self._rightIndent) | |
751 | if vertical and pt.x > drw or not vertical and \ | |
752 | pt.y < (self._iconHeight + self._rightIndent): | |
753 | send_event = True | |
754 | ||
755 | elif event.LeftDClick(): | |
756 | send_event = True | |
757 | ||
758 | # send the collapse, expand event to the parent | |
759 | ||
760 | if send_event: | |
761 | event = CaptionBarEvent(wxEVT_CAPTIONBAR) | |
7c2c2e07 RD |
762 | event.SetId(self.GetId()) |
763 | event.SetEventObject(self) | |
42f5333f RD |
764 | event.SetBar(self) |
765 | self.GetEventHandler().ProcessEvent(event) | |
42f5333f RD |
766 | |
767 | ||
768 | def OnChar(self, event): | |
769 | """ Unused Methods. Any Ideas?!?""" | |
42f5333f RD |
770 | # TODO: Anything here? |
771 | event.Skip() | |
772 | ||
773 | ||
774 | def DoGetBestSize(self): | |
4e5d278c RD |
775 | """ |
776 | Returns the best size for this panel, based upon the font | |
777 | assigned to this window, and the caption string | |
778 | """ | |
42f5333f RD |
779 | |
780 | if self.IsVertical(): | |
781 | x, y = self.GetTextExtent(self._caption) | |
782 | else: | |
783 | y, x = self.GetTextExtent(self._caption) | |
784 | ||
785 | if x < self._iconWidth: | |
786 | x = self._iconWidth | |
787 | ||
788 | if y < self._iconHeight: | |
789 | y = self._iconHeight | |
790 | ||
791 | # TODO: The extra FPB_EXTRA_X constants should be adjustable as well | |
792 | ||
793 | return wx.Size(x + FPB_EXTRA_X, y + FPB_EXTRA_Y) | |
794 | ||
795 | ||
796 | def DrawVerticalGradient(self, dc, rect): | |
797 | """ Gradient fill from colour 1 to colour 2 with top to bottom. """ | |
798 | ||
799 | if rect.height < 1 or rect.width < 1: | |
800 | return | |
801 | ||
802 | dc.SetPen(wx.TRANSPARENT_PEN) | |
803 | ||
804 | # calculate gradient coefficients | |
805 | col2 = self._style.GetSecondColour() | |
806 | col1 = self._style.GetFirstColour() | |
807 | ||
808 | r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) | |
809 | r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) | |
810 | ||
811 | flrect = float(rect.height) | |
812 | ||
813 | rstep = float((r2 - r1)) / flrect | |
814 | gstep = float((g2 - g1)) / flrect | |
815 | bstep = float((b2 - b1)) / flrect | |
816 | ||
817 | rf, gf, bf = 0, 0, 0 | |
818 | ||
819 | for y in range(rect.y, rect.y + rect.height): | |
820 | currCol = (r1 + rf, g1 + gf, b1 + bf) | |
821 | ||
822 | dc.SetBrush(wx.Brush(currCol, wx.SOLID)) | |
823 | dc.DrawRectangle(rect.x, rect.y + (y - rect.y), rect.width, rect.height) | |
824 | rf = rf + rstep | |
825 | gf = gf + gstep | |
826 | bf = bf + bstep | |
827 | ||
828 | ||
829 | def DrawHorizontalGradient(self, dc, rect): | |
830 | """ Gradient fill from colour 1 to colour 2 with left to right. """ | |
831 | ||
832 | if rect.height < 1 or rect.width < 1: | |
833 | return | |
834 | ||
835 | dc.SetPen(wx.TRANSPARENT_PEN) | |
836 | ||
837 | # calculate gradient coefficients | |
838 | col2 = self._style.GetSecondColour() | |
839 | col1 = self._style.GetFirstColour() | |
840 | ||
841 | r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) | |
842 | r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) | |
843 | ||
844 | flrect = float(rect.width) | |
845 | ||
846 | rstep = float((r2 - r1)) / flrect | |
847 | gstep = float((g2 - g1)) / flrect | |
848 | bstep = float((b2 - b1)) / flrect | |
849 | ||
850 | rf, gf, bf = 0, 0, 0 | |
851 | ||
852 | for x in range(rect.x, rect.x + rect.width): | |
853 | currCol = (r1 + rf, g1 + gf, b1 + bf) | |
854 | ||
855 | dc.SetBrush(wx.Brush(currCol, wx.SOLID)) | |
856 | dc.DrawRectangle(rect.x + (x - rect.x), rect.y, 1, rect.height) | |
857 | rf = rf + rstep | |
858 | gf = gf + gstep | |
859 | bf = bf + bstep | |
860 | ||
861 | ||
862 | def DrawSingleColour(self, dc, rect): | |
863 | """ Single colour fill. This is the most easy one to find. """ | |
864 | ||
865 | if rect.height < 1 or rect.width < 1: | |
866 | return | |
867 | ||
868 | dc.SetPen(wx.TRANSPARENT_PEN) | |
869 | ||
870 | # draw simple rectangle | |
871 | dc.SetBrush(wx.Brush(self._style.GetFirstColour(), wx.SOLID)) | |
872 | dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) | |
873 | ||
874 | ||
875 | def DrawSingleRectangle(self, dc, rect): | |
876 | """ Single rectangle. This is the most easy one to find. """ | |
877 | ||
878 | if rect.height < 2 or rect.width < 1: | |
879 | return | |
880 | ||
881 | # single frame, set up internal fill colour | |
882 | ||
883 | if self._style.GetCaptionStyle() == CAPTIONBAR_RECTANGLE: | |
884 | color = self.GetParent().GetBackgroundColour() | |
885 | br = wx.Brush(color, wx.SOLID) | |
886 | else: | |
887 | color = self._style.GetFirstColour() | |
888 | br = wx.Brush(color, wx.SOLID) | |
889 | ||
890 | # setup the pen frame | |
891 | ||
892 | pen = wx.Pen(self._style.GetSecondColour()) | |
893 | dc.SetPen(pen) | |
894 | dc.SetBrush(br) | |
895 | dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height - 1) | |
896 | ||
897 | bgpen = wx.Pen(self.GetParent().GetBackgroundColour()) | |
898 | dc.SetPen(bgpen) | |
899 | dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, | |
900 | rect.y + rect.height - 1) | |
901 | ||
902 | ||
903 | def OnSize(self, event): | |
904 | """ Handles the size events for the CaptionBar.""" | |
905 | ||
906 | if not self._controlCreated: | |
907 | event.Skip() | |
908 | return | |
909 | ||
910 | size = event.GetSize() | |
911 | ||
912 | if self._foldIcons: | |
913 | ||
914 | # What I am doing here is simply invalidating the part of the window | |
915 | # exposed. So when I make a rect with as width the newly exposed part, | |
916 | # and the x,y of the old window size origin, I don't need a bitmap | |
917 | # calculation in it, or do I ? The bitmap needs redrawing anyway. | |
918 | # Leave it like this until I figured it out. | |
919 | ||
920 | # set rect to redraw as old bitmap area which is entitled to redraw | |
921 | ||
922 | rect = wx.Rect(size.GetWidth() - self._iconWidth - self._rightIndent, 0, | |
923 | self._iconWidth + self._rightIndent, | |
924 | self._iconWidth + self._rightIndent) | |
925 | ||
926 | # adjust rectangle when more is slided so we need to redraw all | |
927 | # the old stuff but not all (ugly flickering) | |
928 | ||
929 | diffX = size.GetWidth() - self._oldSize.GetWidth() | |
930 | ||
931 | if diffX > 1: | |
932 | ||
933 | # adjust the rect with all the crap to redraw | |
934 | ||
935 | rect.SetWidth(rect.GetWidth() + diffX + 10) | |
936 | rect.SetX(rect.GetX() - diffX - 10) | |
937 | ||
938 | self.RefreshRect(rect) | |
939 | ||
940 | else: | |
941 | ||
942 | rect = self.GetRect() | |
943 | self.RefreshRect(rect) | |
944 | ||
945 | self._oldSize = size | |
946 | ||
947 | ||
948 | def RedrawIconBitmap(self): | |
949 | """ Redraws the icons (if they exists). """ | |
950 | ||
951 | if self._foldIcons: | |
952 | ||
953 | # invalidate the bitmap area and force a redraw | |
954 | ||
955 | rect = self.GetRect() | |
956 | ||
957 | rect.SetX(rect.GetWidth() - self._iconWidth - self._rightIndent) | |
958 | rect.SetWidth(self._iconWidth + self._rightIndent) | |
959 | self.RefreshRect(rect) | |
960 | ||
961 | ||
962 | # ---------------------------------------------------------------------------------- # | |
963 | # class FoldPanelBar | |
42f5333f RD |
964 | # ---------------------------------------------------------------------------------- # |
965 | ||
966 | class FoldPanelBar(wx.Panel): | |
4e5d278c RD |
967 | """ |
968 | The FoldPanelBar is a class which can maintain a list of | |
969 | collapsable panels. Once a panel is collapsed, only it's caption | |
970 | bar is visible to the user. This will provide more space for the | |
971 | other panels, or allow the user to close panels which are not used | |
972 | often to get the most out of the work area. | |
973 | ||
974 | This control is easy to use. Simply create it as a child for a | |
975 | panel or sash window, and populate panels with | |
976 | `AddFoldPanel`. Then use the AdddFoldPanelWindow` to add | |
977 | `wx.Window` derived controls to the current fold panel. Use | |
978 | `AddFoldPanelSeparator` to put separators between the groups of | |
979 | controls that need a visual separator to group them | |
980 | together. After all is constructed, the user can fold the panels | |
981 | by doubleclicking on the bar or single click on the arrow, which | |
982 | will indicate the collapsed or expanded state. | |
983 | """ | |
42f5333f RD |
984 | # Define Empty CaptionBar Style |
985 | EmptyCaptionBarStyle = CaptionBarStyle() | |
986 | ||
987 | def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, | |
988 | style=FPB_DEFAULT_STYLE, extraStyle=FPB_DEFAULT_EXTRASTYLE): | |
989 | """ Default Class Constructor. """ | |
990 | ||
991 | self._controlCreated = False | |
992 | self._extraStyle = extraStyle | |
993 | ||
994 | # make sure there is any orientation | |
995 | if style & FPB_HORIZONTAL != FPB_HORIZONTAL: | |
996 | style = style | FPB_VERTICAL | |
997 | ||
998 | if style & FPB_HORIZONTAL == 4: | |
999 | self._isVertical = False | |
1000 | else: | |
1001 | self._isVertical = True | |
1002 | ||
1003 | ||
1004 | # create the panel (duh!). This causes a size event, which we are going | |
1005 | # to skip when we are not initialised | |
1006 | ||
1007 | wx.Panel.__init__(self, parent, id, pos, size, style) | |
1008 | ||
1009 | # the fold panel area | |
1010 | ||
1011 | self._foldPanel = wx.Panel(self, wx.ID_ANY, pos, size, | |
1012 | wx.NO_BORDER | wx.TAB_TRAVERSAL) | |
1013 | ||
1014 | self._controlCreated = True | |
1015 | self._panels = [] | |
1016 | ||
1017 | self.Bind(EVT_CAPTIONBAR, self.OnPressCaption) | |
1018 | self.Bind(wx.EVT_SIZE, self.OnSizePanel) | |
1019 | ||
1020 | ||
1021 | def AddFoldPanel(self, caption="", collapsed=False, foldIcons=None, | |
1022 | cbstyle=EmptyCaptionBarStyle): | |
1023 | """ | |
1024 | Adds a fold panel to the list of panels. | |
1025 | ||
4e5d278c RD |
1026 | If the flag collapsed is set to True, the panel is collapsed |
1027 | initially. The FoldPanel item which is returned, can be used | |
1028 | as a reference to perform actions upon the fold panel like | |
1029 | collapsing it, expanding it, or deleting it from the list. | |
42f5333f RD |
1030 | |
1031 | Use this foldpanel to add windows to it. Please consult | |
4e5d278c RD |
1032 | `AddFoldPanelWindow` and `AddFoldPanelSeparator` to know how |
1033 | to add items derived from `wx.Window` to the panels. | |
42f5333f RD |
1034 | """ |
1035 | ||
1036 | # create a fold panel item, which is first only the caption. | |
1037 | # the user can now add a panel area which will be folded in | |
1038 | # when pressed. | |
1039 | ||
1040 | if foldIcons is None: | |
1041 | foldIcons = wx.ImageList(16, 16) | |
1042 | ||
1043 | bmp = GetExpandedIconBitmap() | |
1044 | foldIcons.Add(bmp) | |
1045 | bmp = GetCollapsedIconBitmap() | |
1046 | foldIcons.Add(bmp) | |
1047 | ||
1048 | item = FoldPanelItem(self._foldPanel, -1, caption=caption, | |
1049 | foldIcons=foldIcons, | |
1050 | collapsed=collapsed, cbstyle=cbstyle) | |
1051 | ||
1052 | pos = 0 | |
1053 | if len(self._panels) > 0: | |
1054 | pos = self._panels[-1].GetItemPos() + self._panels[-1].GetPanelLength() | |
1055 | ||
1056 | item.Reposition(pos) | |
1057 | self._panels.append(item) | |
1058 | ||
1059 | return item | |
1060 | ||
1061 | ||
1062 | def AddFoldPanelWindow(self, panel, window, flags=FPB_ALIGN_WIDTH, | |
1063 | Spacing=FPB_DEFAULT_SPACING, | |
1064 | leftSpacing=FPB_DEFAULT_LEFTLINESPACING, | |
1065 | rightSpacing=FPB_DEFAULT_RIGHTLINESPACING): | |
1066 | """ | |
4e5d278c RD |
1067 | Adds a `wx.Window` derived instance to the referenced |
1068 | FoldPanel. | |
42f5333f | 1069 | |
4e5d278c | 1070 | IMPORTANT: Make the window be a child of the FoldPanel. See |
42f5333f | 1071 | example that follows. The flags to be used are: |
4e5d278c RD |
1072 | |
1073 | * FPB_ALIGN_WIDTH: Which means the wxWindow to be added | |
1074 | will be aligned to fit the width of the FoldPanel when | |
1075 | it is resized. Very handy for sizer items, buttons and | |
1076 | text boxes. | |
42f5333f | 1077 | |
4e5d278c RD |
1078 | * FPB_ALIGN_LEFT: Alligns left instead of fitting the |
1079 | width of the child window to be added. Use either this | |
1080 | one or FPB_ALIGN_WIDTH. | |
42f5333f | 1081 | |
4e5d278c RD |
1082 | The wx.Window to be added can be slightly indented from left |
1083 | and right so it is more visibly placed in the FoldPanel. Use | |
1084 | Spacing > 0 to give the control an y offset from the previous | |
1085 | wx.Window added, use leftSpacing to give it a slight indent | |
1086 | from the left, and rightSpacing also reserves a little space | |
1087 | on the right so the wxWindow can be properly placed in the | |
1088 | FoldPanel. | |
42f5333f | 1089 | |
4e5d278c RD |
1090 | The following example adds a FoldPanel to the FoldPanelBar and |
1091 | adds two wx.Window derived controls to the FoldPanel:: | |
42f5333f RD |
1092 | |
1093 | # create the FoldPanelBar | |
1094 | >>> m_pnl = FoldPanelBar(self, wx.ID_ANY, wx.DefaultPosition, | |
1095 | wx.DefaultSize, FPB_DEFAULT_STYLE, | |
1096 | FPB_COLLAPSE_TO_BOTTOM) | |
1097 | ||
1098 | # add a foldpanel to the control. "Test me" is the caption and it is | |
1099 | # initially not collapsed. | |
1100 | >>> item = m_pnl.AddFoldPanel("Test me", False) | |
1101 | ||
1102 | # now add a button to the fold panel. Mind that the button should be | |
1103 | # made child of the FoldPanel and not of the main form. | |
1104 | >>> m_pnl.AddFoldPanelWindow(item, wx.Button(item, ID_COLLAPSEME, | |
1105 | "Collapse Me")) | |
1106 | ||
1107 | # add a separator between the two controls. This is purely a visual | |
1108 | # line that can have a certain color and also the indents and width | |
1109 | # aligning like a control. | |
1110 | >>> m_pnl.AddFoldPanelSeparator(item) | |
1111 | ||
1112 | # now add a text ctrl. Also very easy. Align this on width so that | |
1113 | # when the control gets wider the text control also sizes along. | |
1114 | >>> m_pnl.AddFoldPanelWindow(item, wx.TextCtrl(item, wx.ID_ANY, "Comment"), | |
1115 | FPB_ALIGN_WIDTH, FPB_DEFAULT_SPACING, 20) | |
1116 | ||
42f5333f RD |
1117 | """ |
1118 | ||
1119 | try: | |
1120 | item = self._panels.index(panel) | |
1121 | except: | |
1122 | raise "ERROR: Invalid Panel Passed To AddFoldPanelWindow: " + repr(panel) | |
1123 | ||
1124 | panel.AddWindow(window, flags, Spacing, leftSpacing, rightSpacing) | |
1125 | ||
1126 | # TODO: Take old and new height, and if difference, reposition all the lower | |
1127 | # panels this is because the user can add new wxWindow controls somewhere in | |
1128 | # between when other panels are already present. | |
1129 | ||
1130 | return 0 | |
1131 | ||
1132 | ||
1133 | def AddFoldPanelSeparator(self, panel, colour=wx.BLACK, | |
1134 | Spacing=FPB_DEFAULT_SPACING, | |
1135 | leftSpacing=FPB_DEFAULT_LEFTLINESPACING, | |
1136 | rightSpacing=FPB_DEFAULT_RIGHTLINESPACING): | |
1137 | """ | |
1138 | Adds a separator line to the current FoldPanel. | |
1139 | ||
4e5d278c RD |
1140 | The seperator is a simple line which is drawn and is no real |
1141 | component. It can be used to separate groups of controls | |
1142 | which belong to each other. The colour is adjustable, and it | |
1143 | takes the same Spacing, leftSpacing and rightSpacing as | |
1144 | `AddFoldPanelWindow`. | |
42f5333f RD |
1145 | """ |
1146 | ||
1147 | try: | |
1148 | item = self._panels.index(panel) | |
1149 | except: | |
1150 | raise "ERROR: Invalid Panel Passed To AddFoldPanelSeparator: " + repr(panel) | |
1151 | ||
1152 | panel.AddSeparator(colour, Spacing, leftSpacing, rightSpacing) | |
1153 | return 0 | |
1154 | ||
1155 | ||
1156 | def OnSizePanel(self, event): | |
4e5d278c | 1157 | """ Handles the EVT_SIZE event for the FoldPanelBar. """ |
42f5333f RD |
1158 | |
1159 | # skip all stuff when we are not initialised yet | |
1160 | ||
1161 | if not self._controlCreated: | |
1162 | event.Skip() | |
1163 | return | |
1164 | ||
1165 | foldrect = self.GetRect() | |
1166 | ||
1167 | # fold panel itself. If too little space, | |
1168 | # don't show it | |
1169 | ||
1170 | foldrect.SetX(0) | |
1171 | foldrect.SetY(0) | |
1172 | ||
1173 | self._foldPanel.SetSize(foldrect[2:]) | |
1174 | ||
1175 | if self._extraStyle & FPB_COLLAPSE_TO_BOTTOM: | |
1176 | rect = self.RepositionCollapsedToBottom() | |
1177 | vertical = self.IsVertical() | |
1178 | if vertical and rect.GetHeight() > 0 or not vertical and rect.GetWidth() > 0: | |
1179 | self.RefreshRect(rect) | |
1180 | ||
1181 | # TODO: A smart way to check wether the old - new width of the | |
1182 | # panel changed, if so no need to resize the fold panel items | |
1183 | ||
1184 | self.RedisplayFoldPanelItems() | |
1185 | ||
1186 | ||
1187 | def OnPressCaption(self, event): | |
1188 | """ Handles the EVT_CAPTIONBAR event in the FoldPanelBar. """ | |
1189 | ||
1190 | # act upon the folding or expanding status of the bar | |
1191 | # to expand or collapse the panel(s) | |
1192 | ||
1193 | if event.GetFoldStatus(): | |
1194 | self.Collapse(event.GetTag()) | |
1195 | else: | |
1196 | self.Expand(event.GetTag()) | |
1197 | ||
1198 | event.Skip() | |
1199 | ||
1200 | ||
1201 | def RefreshPanelsFrom(self, item): | |
1202 | """ Refreshes all the panels from given index down to last one. """ | |
1203 | ||
1204 | try: | |
1205 | i = self._panels.index(item) | |
1206 | except: | |
1207 | raise "ERROR: Invalid Panel Passed To RefreshPanelsFrom: " + repr(item) | |
1208 | ||
1209 | self.Freeze() | |
1210 | ||
1211 | # if collapse to bottom is on, the panels that are not expanded | |
1212 | # should be drawn at the bottom. All panels that are expanded | |
1213 | # are drawn on top. The last expanded panel gets all the extra space | |
1214 | ||
1215 | if self._extraStyle & FPB_COLLAPSE_TO_BOTTOM: | |
1216 | ||
1217 | offset = 0 | |
1218 | ||
1219 | for panels in self._panels: | |
1220 | ||
1221 | if panels.IsExpanded(): | |
1222 | offset = offset + panels.Reposition(offset) | |
1223 | ||
1224 | # put all non collapsed panels at the bottom where there is space, | |
1225 | # else put them right behind the expanded ones | |
1226 | ||
1227 | self.RepositionCollapsedToBottom() | |
1228 | ||
1229 | else: | |
1230 | ||
1231 | pos = self._panels[i].GetItemPos() + self._panels[i].GetPanelLength() | |
1232 | for j in range(i+1, len(self._panels)): | |
1233 | pos = pos + self._panels[j].Reposition(pos) | |
42f5333f RD |
1234 | |
1235 | self.Thaw() | |
42f5333f RD |
1236 | |
1237 | ||
1238 | def RedisplayFoldPanelItems(self): | |
1239 | """ Resizes the fold panels so they match the width. """ | |
42f5333f | 1240 | # resize them all. No need to reposition |
42f5333f RD |
1241 | for panels in self._panels: |
1242 | panels.ResizePanel() | |
1243 | panels.Refresh() | |
1244 | ||
1245 | ||
1246 | def RepositionCollapsedToBottom(self): | |
1247 | """ | |
1248 | Repositions all the collapsed panels to the bottom. | |
1249 | ||
4e5d278c RD |
1250 | When it is not possible to align them to the bottom, stick |
1251 | them behind the visible panels. The Rect holds the slack area | |
1252 | left between last repositioned panel and the bottom | |
1253 | panels. This needs to get a refresh. | |
42f5333f RD |
1254 | """ |
1255 | ||
1256 | value = wx.Rect(0,0,0,0) | |
1257 | vertical = self.IsVertical() | |
1258 | ||
1259 | # determine wether the number of panels left | |
1260 | # times the size of their captions is enough | |
1261 | # to be placed in the left over space | |
1262 | ||
1263 | expanded = 0 | |
1264 | collapsed = 0 | |
1265 | collapsed, expanded, values = self.GetPanelsLength(collapsed, expanded) | |
1266 | ||
1267 | # if no room stick them behind the normal ones, else | |
1268 | # at the bottom | |
1269 | ||
1270 | if (vertical and [self.GetSize().GetHeight()] or \ | |
1271 | [self.GetSize().GetWidth()])[0] - expanded - collapsed < 0: | |
1272 | offset = expanded | |
1273 | else: | |
1274 | ||
1275 | # value is the region which is left unpainted | |
1276 | # I will send it back as 'slack' so it does not need to | |
1277 | # be recalculated. | |
1278 | ||
1279 | value.SetHeight(self.GetSize().GetHeight()) | |
1280 | value.SetWidth(self.GetSize().GetWidth()) | |
1281 | ||
1282 | if vertical: | |
1283 | value.SetY(expanded) | |
1284 | value.SetHeight(value.GetHeight() - expanded) | |
1285 | else: | |
1286 | value.SetX(expanded) | |
1287 | value.SetWidth(value.GetWidth() - expanded) | |
1288 | ||
1289 | offset = (vertical and [self.GetSize().GetHeight()] or \ | |
1290 | [self.GetSize().GetWidth()])[0] - collapsed | |
1291 | ||
1292 | ||
1293 | # go reposition | |
1294 | ||
1295 | for panels in self._panels: | |
1296 | if not panels.IsExpanded(): | |
1297 | offset = offset + panels.Reposition(offset) | |
1298 | ||
1299 | return value | |
1300 | ||
1301 | ||
1302 | def GetPanelsLength(self, collapsed, expanded): | |
1303 | """ | |
4e5d278c RD |
1304 | Returns the length of the panels that are expanded and |
1305 | collapsed. | |
42f5333f | 1306 | |
4e5d278c RD |
1307 | This is useful to determine quickly what size is used to |
1308 | display, and what is left at the bottom (right) to align the | |
1309 | collapsed panels. | |
42f5333f RD |
1310 | """ |
1311 | ||
1312 | value = 0 | |
1313 | ||
1314 | # assumed here that all the panels that are expanded | |
1315 | # are positioned after each other from 0,0 to end. | |
1316 | ||
1317 | for j in range(0, len(self._panels)): | |
1318 | offset = self._panels[j].GetPanelLength() | |
1319 | value = value + offset | |
1320 | if self._panels[j].IsExpanded(): | |
1321 | expanded = expanded + offset | |
1322 | else: | |
1323 | collapsed = collapsed + offset | |
1324 | ||
1325 | return collapsed, expanded, value | |
1326 | ||
1327 | ||
1328 | def Collapse(self, foldpanel): | |
1329 | """ | |
4e5d278c RD |
1330 | Collapses the given FoldPanel reference, and updates the |
1331 | foldpanel bar. | |
42f5333f | 1332 | |
4e5d278c RD |
1333 | In the FPB_COLLAPSE_TO_BOTTOM style, all collapsed captions |
1334 | are put at the bottom of the control. In the normal mode, they | |
1335 | stay where they are. | |
42f5333f RD |
1336 | """ |
1337 | ||
1338 | try: | |
1339 | item = self._panels.index(foldpanel) | |
1340 | except: | |
1341 | raise "ERROR: Invalid Panel Passed To Collapse: " + repr(foldpanel) | |
1342 | ||
1343 | foldpanel.Collapse() | |
1344 | self.RefreshPanelsFrom(foldpanel) | |
1345 | ||
1346 | ||
1347 | def Expand(self, foldpanel): | |
1348 | """ | |
4e5d278c RD |
1349 | Expands the given FoldPanel reference, and updates the |
1350 | foldpanel bar. | |
42f5333f | 1351 | |
4e5d278c RD |
1352 | In the FPB_COLLAPSE_TO_BOTTOM style, they will be removed from |
1353 | the bottom and the order where the panel originally was placed | |
1354 | is restored. | |
42f5333f RD |
1355 | """ |
1356 | ||
1357 | foldpanel.Expand() | |
1358 | self.RefreshPanelsFrom(foldpanel) | |
1359 | ||
1360 | ||
1361 | def ApplyCaptionStyle(self, foldpanel, cbstyle): | |
1362 | """ | |
4e5d278c RD |
1363 | Sets the style of the caption bar (`CaptionBar`) of the |
1364 | FoldPanel. | |
42f5333f | 1365 | |
4e5d278c RD |
1366 | The changes are applied immediately. All styles not set in the |
1367 | CaptionBarStyle class are not applied. Use the CaptionBar | |
1368 | reference to indicate what captionbar you want to apply the | |
1369 | style to. To apply one style to all CaptionBar items, use | |
1370 | `ApplyCaptionStyleAll` | |
1371 | """ | |
42f5333f RD |
1372 | foldpanel.ApplyCaptionStyle(cbstyle) |
1373 | ||
1374 | ||
1375 | def ApplyCaptionStyleAll(self, cbstyle): | |
4e5d278c RD |
1376 | """ |
1377 | Sets the style of all the caption bars of the FoldPanel. | |
42f5333f | 1378 | |
4e5d278c RD |
1379 | The changes are applied immediately. |
1380 | """ | |
42f5333f RD |
1381 | for panels in self._panels: |
1382 | self.ApplyCaptionStyle(panels, cbstyle) | |
1383 | ||
1384 | ||
1385 | def GetCaptionStyle(self, foldpanel): | |
1386 | """ | |
1387 | Returns the currently used caption style for the FoldPanel. | |
1388 | ||
4e5d278c RD |
1389 | It is returned as a CaptionBarStyle class. After modifying it, |
1390 | it can be set again. | |
1391 | """ | |
42f5333f RD |
1392 | return foldpanel.GetCaptionStyle() |
1393 | ||
1394 | ||
1395 | def IsVertical(self): | |
1396 | """ | |
4e5d278c | 1397 | Returns whether the CaptionBar has default orientation or not. |
42f5333f RD |
1398 | |
1399 | Default is vertical. | |
4e5d278c | 1400 | """ |
42f5333f RD |
1401 | return self._isVertical |
1402 | ||
1403 | ||
1404 | def GetFoldPanel(self, item): | |
1405 | """ | |
1406 | Returns the panel associated with the index "item". | |
1407 | ||
1408 | See the example at the bottom of the module, especially the events | |
1409 | for the "Collapse Me" and "Expand Me" buttons. | |
1410 | """ | |
42f5333f RD |
1411 | try: |
1412 | ind = self._panels[item] | |
1413 | return self._panels[item] | |
1414 | except: | |
1415 | raise "ERROR: List Index Out Of Range Or Bad Item Passed: " + repr(item) + \ | |
1416 | ". Item Should Be An Integer Between " + repr(0) + " And " + \ | |
1417 | repr(len(self._panels)) | |
1418 | ||
1419 | ||
1420 | def GetCount(self): | |
1421 | """ Returns the number of panels in the FoldPanelBar. """ | |
1422 | ||
1423 | try: | |
1424 | return len(self._panels) | |
1425 | except: | |
1426 | raise "ERROR: No Panels Have Been Added To FoldPanelBar" | |
1427 | ||
1428 | ||
1429 | ||
1430 | # --------------------------------------------------------------------------------- # | |
1431 | # class FoldPanelItem | |
42f5333f RD |
1432 | # --------------------------------------------------------------------------------- # |
1433 | ||
1434 | class FoldPanelItem(wx.Panel): | |
4e5d278c RD |
1435 | """ |
1436 | This class is a child sibling of the `FoldPanelBar` class. It will | |
1437 | contain a `CaptionBar` class for receiving of events, and a the | |
1438 | rest of the area can be populated by a `wx.Panel` derived class. | |
1439 | """ | |
42f5333f RD |
1440 | # Define Empty CaptionBar Style |
1441 | EmptyCaptionBarStyle = CaptionBarStyle() | |
1442 | ||
1443 | def __init__(self, parent, id=wx.ID_ANY, caption="", foldIcons=None, | |
1444 | collapsed=False, cbstyle=EmptyCaptionBarStyle): | |
1445 | """ Default Class Constructor. """ | |
1446 | ||
1447 | wx.Panel.__init__(self, parent, id, style=wx.CLIP_CHILDREN) | |
1448 | self._controlCreated = False | |
1449 | self._UserSize = 0 | |
1450 | self._PanelSize = 0 | |
1451 | self._LastInsertPos = 0 | |
1452 | self._itemPos = 0 | |
1453 | self._userSized = False | |
1454 | ||
1455 | if foldIcons is None: | |
1456 | foldIcons = wx.ImageList(16, 16) | |
1457 | ||
1458 | bmp = GetExpandedIconBitmap() | |
1459 | foldIcons.Add(bmp) | |
1460 | bmp = GetCollapsedIconBitmap() | |
1461 | foldIcons.Add(bmp) | |
1462 | ||
1463 | self._foldIcons = foldIcons | |
1464 | ||
1465 | # create the caption bar, in collapsed or expanded state | |
1466 | ||
1467 | self._captionBar = CaptionBar(self, wx.ID_ANY, wx.Point(0,0), | |
1468 | size=wx.DefaultSize, caption=caption, | |
1469 | foldIcons=foldIcons, cbstyle=cbstyle) | |
1470 | ||
1471 | if collapsed: | |
1472 | self._captionBar.Collapse() | |
1473 | ||
1474 | self._controlCreated = True | |
1475 | ||
1476 | # make initial size for component, if collapsed, the | |
1477 | # size is determined on the panel height and won't change | |
1478 | ||
1479 | size = self._captionBar.GetSize() | |
1480 | ||
1481 | self._PanelSize = (self.IsVertical() and [size.GetHeight()] or \ | |
1482 | [size.GetWidth()])[0] | |
1483 | ||
1484 | self._LastInsertPos = self._PanelSize | |
1485 | self._items = [] | |
1486 | ||
1487 | self.Bind(EVT_CAPTIONBAR, self.OnPressCaption) | |
1488 | self.Bind(wx.EVT_PAINT, self.OnPaint) | |
1489 | ||
1490 | ||
1491 | def AddWindow(self, window, flags=FPB_ALIGN_WIDTH, Spacing=FPB_DEFAULT_SPACING, | |
1492 | leftSpacing=FPB_DEFAULT_LEFTLINESPACING, | |
1493 | rightSpacing=FPB_DEFAULT_RIGHTLINESPACING): | |
1494 | """ | |
1495 | Adds a window item to the list of items on this panel. | |
1496 | ||
4e5d278c RD |
1497 | The flags are FPB_ALIGN_LEFT for a non sizing window element, |
1498 | and FPB_ALIGN_WIDTH for a width aligned item. The Spacing | |
1499 | parameter reserves a number of pixels before the window | |
1500 | element, and leftSpacing is an indent. rightSpacing is only | |
1501 | relevant when the style FPB_ALIGN_WIDTH is chosen. | |
42f5333f RD |
1502 | """ |
1503 | ||
1504 | wi = FoldWindowItem(self, window, Type="WINDOW", flags=flags, Spacing=Spacing, | |
1505 | leftSpacing=leftSpacing, rightSpacing=rightSpacing) | |
1506 | ||
1507 | self._items.append(wi) | |
1508 | ||
1509 | vertical = self.IsVertical() | |
1510 | ||
1511 | self._Spacing = Spacing | |
1512 | self._leftSpacing = leftSpacing | |
1513 | self._rightSpacing = rightSpacing | |
1514 | ||
1515 | xpos = (vertical and [leftSpacing] or [self._LastInsertPos + Spacing])[0] | |
1516 | ypos = (vertical and [self._LastInsertPos + Spacing] or [leftSpacing])[0] | |
1517 | ||
1518 | window.SetDimensions(xpos, ypos, -1, -1, wx.SIZE_USE_EXISTING) | |
1519 | ||
1520 | self._LastInsertPos = self._LastInsertPos + wi.GetWindowLength(vertical) | |
1521 | self.ResizePanel() | |
1522 | ||
1523 | ||
1524 | def AddSeparator(self, colour=wx.BLACK, Spacing=FPB_DEFAULT_SPACING, | |
1525 | leftSpacing=FPB_DEFAULT_LEFTSPACING, | |
1526 | rightSpacing=FPB_DEFAULT_RIGHTSPACING): | |
1527 | """ | |
1528 | Adds a separator item to the list of items on this panel. """ | |
1529 | ||
1530 | wi = FoldWindowItem(self, window=None, Type="SEPARATOR", | |
1531 | flags=FPB_ALIGN_WIDTH, y=self._LastInsertPos, | |
1532 | colour=colour, Spacing=Spacing, leftSpacing=leftSpacing, | |
1533 | rightSpacing=rightSpacing) | |
1534 | ||
1535 | self._items.append(wi) | |
1536 | self._LastInsertPos = self._LastInsertPos + \ | |
1537 | wi.GetWindowLength(self.IsVertical()) | |
1538 | ||
1539 | self.ResizePanel() | |
1540 | ||
1541 | ||
1542 | def Reposition(self, pos): | |
4e5d278c RD |
1543 | """ |
1544 | Repositions this FoldPanelBar and reports the length occupied | |
1545 | for the next FoldPanelBar in the list. | |
1546 | """ | |
42f5333f RD |
1547 | # NOTE: Call Resize before Reposition when an item is added, because the new |
1548 | # size needed will be calculated by Resize. Of course the relative position | |
1549 | # of the controls have to be correct in respect to the caption bar | |
1550 | ||
1551 | self.Freeze() | |
1552 | ||
1553 | vertical = self.IsVertical() | |
1554 | xpos = (vertical and [-1] or [pos])[0] | |
1555 | ypos = (vertical and [pos] or [-1])[0] | |
1556 | ||
1557 | self.SetDimensions(xpos, ypos, -1, -1, wx.SIZE_USE_EXISTING) | |
1558 | self._itemPos = pos | |
1559 | ||
1560 | self.Thaw() | |
42f5333f RD |
1561 | |
1562 | return self.GetPanelLength() | |
1563 | ||
1564 | ||
1565 | def OnPressCaption(self, event): | |
1566 | """ Handles the EVT_CAPTIONBAR event in the FoldPanelItem. """ | |
1567 | ||
1568 | # tell the upper container we are responsible | |
1569 | # for this event, so it can fold the panel item | |
1570 | # and do a refresh | |
1571 | ||
1572 | event.SetTag(self) | |
1573 | event.Skip() | |
1574 | ||
1575 | ||
1576 | def ResizePanel(self): | |
1577 | """ Resizes the panel. """ | |
1578 | ||
1579 | # prevent unnecessary updates by blocking repaints for a sec | |
1580 | ||
1581 | self.Freeze() | |
1582 | ||
1583 | vertical = self.IsVertical() | |
1584 | # force this panel to take the width of the parent panel and the y of the | |
1585 | # user or calculated width (which will be recalculated by the contents here) | |
1586 | ||
1587 | ||
1588 | if self._captionBar.IsCollapsed(): | |
1589 | size = self._captionBar.GetSize() | |
1590 | self._PanelSize = (vertical and [size.GetHeight()] or [size.GetWidth()])[0] | |
1591 | else: | |
1592 | size = self.GetBestSize() | |
1593 | self._PanelSize = (vertical and [size.GetHeight()] or [size.GetWidth()])[0] | |
1594 | ||
1595 | if self._UserSize: | |
1596 | if vertical: | |
1597 | size.SetHeight(self._UserSize) | |
1598 | else: | |
1599 | size.SetWidth(self._UserSize) | |
1600 | ||
1601 | pnlsize = self.GetParent().GetSize() | |
1602 | ||
1603 | if vertical: | |
1604 | size.SetWidth(pnlsize.GetWidth()) | |
1605 | else: | |
1606 | size.SetHeight(pnlsize.GetHeight()) | |
1607 | ||
1608 | # resize caption bar | |
1609 | xsize = (vertical and [size.GetWidth()] or [-1])[0] | |
1610 | ysize = (vertical and [-1] or [size.GetHeight()])[0] | |
1611 | ||
1612 | self._captionBar.SetSize((xsize, ysize)) | |
1613 | ||
1614 | # resize the panel | |
1615 | self.SetSize(size) | |
1616 | ||
1617 | # go by all the controls and call Layout | |
1618 | ||
1619 | for items in self._items: | |
1620 | items.ResizeItem((vertical and [size.GetWidth()] or \ | |
1621 | [size.GetHeight()])[0], vertical) | |
1622 | ||
1623 | self.Thaw() | |
42f5333f RD |
1624 | |
1625 | ||
1626 | def OnPaint(self, event): | |
1627 | """ Handles the EVT_PAINT event in the FoldPanelItem. """ | |
1628 | ||
1629 | # draw all the items that are lines | |
1630 | ||
1631 | dc = wx.PaintDC(self) | |
1632 | vertical = self.IsVertical() | |
1633 | ||
1634 | for item in self._items: | |
1635 | ||
1636 | if item.GetType() == "SEPARATOR": | |
1637 | pen = wx.Pen(item.GetLineColour(), 1, wx.SOLID) | |
1638 | dc.SetPen(pen) | |
1639 | a = item.GetLeftSpacing() | |
1640 | b = item.GetLineY() + item.GetSpacing() | |
1641 | c = item.GetLineLength() | |
1642 | d = a + c | |
1643 | ||
1644 | if vertical: | |
1645 | dc.DrawLine(a, b, d, b) | |
1646 | else: | |
1647 | dc.DrawLine(b, a, b, d) | |
1648 | ||
1649 | event.Skip() | |
1650 | ||
1651 | ||
1652 | def IsVertical(self): | |
1653 | """ | |
1654 | Returns wether the CaptionBar Has Default Orientation Or Not. | |
1655 | ||
1656 | Default is vertical. | |
1657 | """ | |
1658 | ||
1659 | # grandparent of FoldPanelItem is FoldPanelBar | |
1660 | # default is vertical | |
1661 | ||
1662 | if isinstance(self.GetGrandParent(), FoldPanelBar): | |
1663 | return self.GetGrandParent().IsVertical() | |
1664 | else: | |
1665 | raise "ERROR: Wrong Parent " + repr(self.GetGrandParent()) | |
1666 | ||
1667 | ||
1668 | def IsExpanded(self): | |
4e5d278c RD |
1669 | """ |
1670 | Returns expanded or collapsed status. If the panel is | |
1671 | expanded, True is returned. | |
1672 | """ | |
42f5333f RD |
1673 | |
1674 | return not self._captionBar.IsCollapsed() | |
1675 | ||
1676 | ||
1677 | def GetItemPos(self): | |
1678 | """ Returns item's position. """ | |
1679 | ||
1680 | return self._itemPos | |
1681 | ||
1682 | ||
1683 | def Collapse(self): | |
1684 | # this should not be called by the user, because it doesn't trigger the | |
1685 | # parent to tell it that we are collapsed or expanded, it only changes | |
1686 | # visual state | |
1687 | ||
1688 | self._captionBar.Collapse() | |
1689 | self.ResizePanel() | |
1690 | ||
1691 | ||
1692 | def Expand(self): | |
1693 | # this should not be called by the user, because it doesn't trigger the | |
1694 | # parent to tell it that we are collapsed or expanded, it only changes | |
1695 | # visual state | |
1696 | ||
1697 | self._captionBar.Expand() | |
1698 | self.ResizePanel() | |
1699 | ||
1700 | ||
1701 | def GetPanelLength(self): | |
1702 | """ Returns size of panel. """ | |
1703 | ||
1704 | if self._captionBar.IsCollapsed(): | |
1705 | return self.GetCaptionLength() | |
1706 | elif self._userSized: | |
1707 | return self._UserSize | |
1708 | ||
1709 | return self._PanelSize | |
1710 | ||
1711 | ||
1712 | def GetCaptionLength(self): | |
4e5d278c RD |
1713 | """ |
1714 | Returns height of caption only. This is for folding | |
1715 | calculation purposes. | |
1716 | """ | |
42f5333f RD |
1717 | |
1718 | size = self._captionBar.GetSize() | |
1719 | return (self.IsVertical() and [size.GetHeight()] or [size.GetWidth()])[0] | |
1720 | ||
1721 | ||
1722 | def ApplyCaptionStyle(self, cbstyle): | |
1723 | """ Applies the style defined in cbstyle to the CaptionBar.""" | |
1724 | ||
1725 | self._captionBar.SetCaptionStyle(cbstyle) | |
1726 | ||
1727 | ||
1728 | def GetCaptionStyle(self): | |
1729 | """ | |
4e5d278c RD |
1730 | Returns the current style of the captionbar in a |
1731 | CaptionBarStyle class. | |
42f5333f RD |
1732 | |
1733 | This can be used to change and set back the changes. | |
1734 | """ | |
1735 | ||
1736 | return self._captionBar.GetCaptionStyle() | |
1737 | ||
1738 | ||
1739 | # ----------------------------------------------------------------------------------- # | |
1740 | # class FoldWindowItem | |
42f5333f RD |
1741 | # ----------------------------------------------------------------------------------- # |
1742 | ||
1743 | class FoldWindowItem: | |
4e5d278c RD |
1744 | """ |
1745 | This class is a child sibling of the `FoldPanelItem` class. It | |
1746 | will contain wx.Window that can be either a separator (a colored | |
1747 | line simulated by a wx.Window) or a wxPython controls (such as a | |
1748 | wx.Button, a wx.ListCtrl etc...). | |
1749 | """ | |
42f5333f RD |
1750 | def __init__(self, parent, window=None, **kw): |
1751 | """ | |
1752 | Default Class Constructor | |
1753 | ||
4e5d278c | 1754 | Initialize with:: |
42f5333f RD |
1755 | |
1756 | Type = "WINDOW", flags = FPB_ALIGN_WIDTH, | |
1757 | Spacing = FPB_DEFAULT_SPACING, | |
1758 | leftSpacing = FPB_DEFAULT_LEFTSPACING, | |
1759 | rightSpacing = FPB_DEFAULT_RIGHTSPACING | |
1760 | ||
4e5d278c | 1761 | or:: |
42f5333f RD |
1762 | |
1763 | Type = "SEPARATOR" | |
1764 | y, lineColor = wx.BLACK, | |
1765 | flags = FPB_ALIGN_WIDTH, | |
1766 | Spacing = FPB_DEFAULT_SPACING, | |
1767 | leftSpacing = FPB_DEFAULT_LEFTLINESPACING, | |
1768 | rightSpacing = FPB_DEFAULT_RIGHTLINESPACING | |
1769 | """ | |
1770 | ||
1771 | ||
1772 | if not kw.has_key("Type"): | |
1773 | raise 'ERROR: Missing Window Type Information. This Should Be "WINDOW" Or "SEPARATOR"' | |
1774 | ||
1775 | if kw.get("Type") == "WINDOW": | |
1776 | # Window constructor. This initialises the class as a wx.Window Type | |
1777 | ||
1778 | if kw.has_key("flags"): | |
1779 | self._flags = kw.get("flags") | |
1780 | else: | |
1781 | self._flags = FPB_ALIGN_WIDTH | |
1782 | if kw.has_key("Spacing"): | |
1783 | self._Spacing = kw.get("Spacing") | |
1784 | else: | |
1785 | self._Spacing = FPB_DEFAULT_SPACING | |
1786 | if kw.has_key("leftSpacing"): | |
1787 | self._leftSpacing = kw.get("leftSpacing") | |
1788 | else: | |
1789 | self._leftSpacing = FPB_DEFAULT_LEFTSPACING | |
1790 | if kw.has_key("rightSpacing"): | |
1791 | self._rightSpacing = kw.get("rightSpacing") | |
1792 | else: | |
1793 | self._rightSpacing = FPB_DEFAULT_RIGHTSPACING | |
1794 | ||
1795 | self._lineY = 0 | |
1796 | self._sepLineColour = None | |
1797 | self._wnd = window | |
1798 | ||
1799 | ||
1800 | elif kw.get("Type") == "SEPARATOR": | |
1801 | # separator constructor. This initialises the class as a separator type | |
1802 | ||
1803 | if kw.has_key("y"): | |
1804 | self._lineY = kw.get("y") | |
1805 | else: | |
1806 | raise "ERROR: Undefined Y Position For The Separator" | |
1807 | if kw.has_key("lineColour"): | |
1808 | self._sepLineColour = kw.get("lineColour") | |
1809 | else: | |
1810 | self._sepLineColour = wx.BLACK | |
1811 | if kw.has_key("flags"): | |
1812 | self._flags = kw.get("flags") | |
1813 | else: | |
1814 | self._flags = FPB_ALIGN_WIDTH | |
1815 | if kw.has_key("Spacing"): | |
1816 | self._Spacing = kw.get("Spacing") | |
1817 | else: | |
1818 | self._Spacing = FPB_DEFAULT_SPACING | |
1819 | if kw.has_key("leftSpacing"): | |
1820 | self._leftSpacing = kw.get("leftSpacing") | |
1821 | else: | |
1822 | self._leftSpacing = FPB_DEFAULT_LEFTSPACING | |
1823 | if kw.has_key("rightSpacing"): | |
1824 | self._rightSpacing = kw.get("rightSpacing") | |
1825 | else: | |
1826 | self._rightSpacing = FPB_DEFAULT_RIGHTSPACING | |
1827 | ||
1828 | self._wnd = window | |
1829 | ||
1830 | else: | |
1831 | raise "ERROR: Undefined Window Type Selected: " + repr(kw.get("Type")) | |
1832 | ||
1833 | self._type = kw.get("Type") | |
1834 | self._lineLength = 0 | |
1835 | ||
1836 | ||
1837 | def GetType(self): | |
1838 | return self._type | |
1839 | ||
1840 | def GetLineY(self): | |
1841 | return self._lineY | |
1842 | ||
1843 | def GetLineLength(self): | |
1844 | return self._lineLength | |
1845 | ||
1846 | def GetLineColour(self): | |
1847 | return self._sepLineColour | |
1848 | ||
1849 | def GetLeftSpacing(self): | |
1850 | return self._leftSpacing | |
1851 | ||
1852 | def GetRightSpacing(self): | |
1853 | return self._rightSpacing | |
1854 | ||
1855 | def GetSpacing(self): | |
1856 | return self._Spacing | |
1857 | ||
1858 | ||
1859 | def GetWindowLength(self, vertical=True): | |
4e5d278c RD |
1860 | """ |
1861 | Returns space needed by the window if type is FoldWindowItem | |
1862 | "WINDOW" and returns the total size plus the extra spacing. | |
1863 | """ | |
42f5333f RD |
1864 | |
1865 | value = 0 | |
1866 | if self._type == "WINDOW": | |
1867 | size = self._wnd.GetSize() | |
1868 | value = (vertical and [size.GetHeight()] or [size.GetWidth()])[0] + \ | |
1869 | self._Spacing | |
1870 | ||
1871 | elif self._type == "SEPARATOR": | |
1872 | value = 1 + self._Spacing | |
1873 | ||
1874 | return value | |
1875 | ||
1876 | ||
1877 | def ResizeItem(self, size, vertical=True): | |
1878 | """ | |
1879 | Resizes the element, whatever it is. | |
1880 | ||
1881 | A separator or line will be always aligned by width or height | |
1882 | depending on orientation of the whole panel. | |
1883 | """ | |
1884 | ||
1885 | if self._flags & FPB_ALIGN_WIDTH: | |
1886 | # align by taking full width | |
1887 | mySize = size - self._leftSpacing - self._rightSpacing | |
1888 | ||
1889 | if mySize < 0: | |
1890 | mySize = 10 # can't have negative width | |
1891 | ||
1892 | if self._type == "SEPARATOR": | |
1893 | self._lineLength = mySize | |
1894 | else: | |
1895 | xsize = (vertical and [mySize] or [-1])[0] | |
1896 | ysize = (vertical and [-1] or [mySize])[0] | |
1897 | ||
1898 | self._wnd.SetSize((xsize, ysize)) | |
1899 |