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