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