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