...
[iramuteq] / aui / dockart.py
1 """
2 Dock art provider code - a dock provider provides all drawing functionality to
3 the AUI dock manager. This allows the dock manager to have a plugable look-and-feel.
4
5 By default, a L{AuiManager} uses an instance of this class called L{AuiDefaultDockArt}
6 which provides bitmap art and a colour scheme that is adapted to the major platforms'
7 look. You can either derive from that class to alter its behaviour or write a
8 completely new dock art class. Call L{AuiManager.SetArtProvider} to make use this
9 new dock art.
10 """
11
12 __author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
13 __date__ = "31 March 2009"
14
15
16 import wx
17 import types
18
19 from aui_utilities import BitmapFromBits, StepColour, ChopText, GetBaseColour
20 from aui_utilities import DrawGradientRectangle, DrawMACCloseButton
21 from aui_utilities import DarkenBitmap, LightContrastColour
22 from aui_constants import *
23
24 optionActive = 2**14
25
26 _ctypes = False
27
28 # Try to import winxptheme for ModernDockArt
29 if wx.Platform == "__WXMSW__":
30     try:
31         import ctypes
32         import winxptheme
33         _ctypes = True
34     except ImportError:
35         pass
36
37 # -- AuiDefaultDockArt class implementation --
38
39 class AuiDefaultDockArt(object):
40     """
41     Dock art provider code - a dock provider provides all drawing functionality
42     to the AUI dock manager. This allows the dock manager to have a plugable
43     look-and-feel.
44
45     By default, a L{AuiManager} uses an instance of this class called L{AuiDefaultDockArt}
46     which provides bitmap art and a colour scheme that is adapted to the major
47     platforms' look. You can either derive from that class to alter its behaviour or
48     write a completely new dock art class.
49     
50     Call L{AuiManager.SetArtProvider} to make use this new dock art.
51
52
53     **Metric Ordinals**
54
55     These are the possible pane dock art settings for L{AuiManager}:
56
57     ================================================  ======================================
58     Metric Ordinal Constant                           Description
59     ================================================  ======================================
60     ``AUI_DOCKART_SASH_SIZE``                         Customizes the sash size
61     ``AUI_DOCKART_CAPTION_SIZE``                      Customizes the caption size
62     ``AUI_DOCKART_GRIPPER_SIZE``                      Customizes the gripper size
63     ``AUI_DOCKART_PANE_BORDER_SIZE``                  Customizes the pane border size
64     ``AUI_DOCKART_PANE_BUTTON_SIZE``                  Customizes the pane button size
65     ``AUI_DOCKART_BACKGROUND_COLOUR``                 Customizes the background colour
66     ``AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR``        Customizes the background gradient colour
67     ``AUI_DOCKART_SASH_COLOUR``                       Customizes the sash colour
68     ``AUI_DOCKART_ACTIVE_CAPTION_COLOUR``             Customizes the active caption colour
69     ``AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR``    Customizes the active caption gradient colour
70     ``AUI_DOCKART_INACTIVE_CAPTION_COLOUR``           Customizes the inactive caption colour
71     ``AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR``  Customizes the inactive gradient caption colour
72     ``AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR``        Customizes the active caption text colour
73     ``AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR``      Customizes the inactive caption text colour
74     ``AUI_DOCKART_BORDER_COLOUR``                     Customizes the border colour
75     ``AUI_DOCKART_GRIPPER_COLOUR``                    Customizes the gripper colour
76     ``AUI_DOCKART_CAPTION_FONT``                      Customizes the caption font
77     ``AUI_DOCKART_GRADIENT_TYPE``                     Customizes the gradient type (no gradient, vertical or horizontal)
78     ``AUI_DOCKART_DRAW_SASH_GRIP``                    Draw a sash grip on the sash
79     ================================================  ======================================
80
81
82     **Gradient Types**
83
84     These are the possible gradient dock art settings for L{AuiManager}:
85
86     ============================================  ======================================
87     Gradient Constant                             Description 
88     ============================================  ======================================
89     ``AUI_GRADIENT_NONE``                         No gradient on the captions
90     ``AUI_GRADIENT_VERTICAL``                     Vertical gradient on the captions
91     ``AUI_GRADIENT_HORIZONTAL``                   Horizontal gradient on the captions
92     ============================================  ======================================
93
94
95     **Button States**
96
97     These are the possible pane button / L{AuiNotebook} button / L{AuiToolBar} button states:
98
99     ============================================  ======================================
100     Button State Constant                         Description     
101     ============================================  ======================================
102     ``AUI_BUTTON_STATE_NORMAL``                   Normal button state
103     ``AUI_BUTTON_STATE_HOVER``                    Hovered button state
104     ``AUI_BUTTON_STATE_PRESSED``                  Pressed button state
105     ``AUI_BUTTON_STATE_DISABLED``                 Disabled button state
106     ``AUI_BUTTON_STATE_HIDDEN``                   Hidden button state
107     ``AUI_BUTTON_STATE_CHECKED``                  Checked button state
108     ============================================  ======================================
109
110
111     **Button Identifiers**
112
113     These are the possible pane button / L{AuiNotebook} button / L{AuiToolBar} button identifiers:
114
115     ============================================  ======================================
116     Button Identifier                             Description     
117     ============================================  ======================================
118     ``AUI_BUTTON_CLOSE``                          Shows a close button on the pane
119     ``AUI_BUTTON_MAXIMIZE_RESTORE``               Shows a maximize/restore button on the pane
120     ``AUI_BUTTON_MINIMIZE``                       Shows a minimize button on the pane
121     ``AUI_BUTTON_PIN``                            Shows a pin button on the pane
122     ``AUI_BUTTON_OPTIONS``                        Shows an option button on the pane (not implemented)
123     ``AUI_BUTTON_WINDOWLIST``                     Shows a window list button on the pane (for L{AuiNotebook})
124     ``AUI_BUTTON_LEFT``                           Shows a left button on the pane (for L{AuiNotebook})
125     ``AUI_BUTTON_RIGHT``                          Shows a right button on the pane (for L{AuiNotebook})
126     ``AUI_BUTTON_UP``                             Shows an up button on the pane (not implemented)
127     ``AUI_BUTTON_DOWN``                           Shows a down button on the pane (not implemented)
128     ``AUI_BUTTON_CUSTOM1``                        Shows a custom button on the pane (not implemented)
129     ``AUI_BUTTON_CUSTOM2``                        Shows a custom button on the pane (not implemented)
130     ``AUI_BUTTON_CUSTOM3``                        Shows a custom button on the pane (not implemented)
131     ============================================  ======================================
132     
133     """
134
135     def __init__(self):
136         """ Default class constructor. """
137
138         self.Init()
139
140         isMac = wx.Platform == "__WXMAC__"
141         
142         if isMac:
143             self._caption_font = wx.SMALL_FONT
144         else:
145             self._caption_font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False)
146
147         self.SetDefaultPaneBitmaps(isMac)
148         self._restore_bitmap = wx.BitmapFromXPMData(restore_xpm)
149         
150         # default metric values
151         self._sash_size = 4
152
153         if isMac:
154             # This really should be implemented in wx.SystemSettings
155             # There is no way to do this that I am aware outside of using
156             # the cocoa python bindings. 8 pixels looks correct on my system
157             # so hard coding it for now.
158
159             # How do I translate this?!? Not sure of the below implementation...
160             # SInt32 height;
161             # GetThemeMetric( kThemeMetricSmallPaneSplitterHeight , &height );
162             # self._sash_size = height;
163
164             self._sash_size = 8 # Carbon.Appearance.kThemeMetricPaneSplitterHeight            
165             
166         elif wx.Platform == "__WXGTK__":
167             self._sash_size = wx.RendererNative.Get().GetSplitterParams(wx.GetTopLevelWindows()[0]).widthSash
168
169         else:
170             self._sash_size = 4
171         
172         self._caption_size = 19
173         self._border_size = 1
174         self._button_size = 14
175         self._gripper_size = 9
176         self._gradient_type = AUI_GRADIENT_VERTICAL
177         self._draw_sash = False
178         
179
180     def Init(self):
181         """ Initializes the dock art. """
182
183         base_colour = GetBaseColour()
184         darker1_colour = StepColour(base_colour, 85)
185         darker2_colour = StepColour(base_colour, 75)
186         darker3_colour = StepColour(base_colour, 60)
187         darker4_colour = StepColour(base_colour, 40)
188
189         self._background_colour = base_colour
190         self._background_gradient_colour = StepColour(base_colour, 180)
191
192         isMac = wx.Platform == "__WXMAC__"
193
194         if isMac:
195             self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
196         else:
197             self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
198
199         self._active_caption_gradient_colour = LightContrastColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT))
200         self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
201         self._inactive_caption_colour = darker1_colour
202         self._inactive_caption_gradient_colour = StepColour(base_colour, 97)
203         self._inactive_caption_text_colour = wx.BLACK
204     
205         self._sash_brush = wx.Brush(base_colour)
206         self._background_brush = wx.Brush(base_colour)
207         self._border_pen = wx.Pen(darker2_colour)
208         self._gripper_brush = wx.Brush(base_colour)
209         self._gripper_pen1 = wx.Pen(darker4_colour)
210         self._gripper_pen2 = wx.Pen(darker3_colour)
211         self._gripper_pen3 = wx.WHITE_PEN
212
213         
214     def GetMetric(self, id):
215         """
216         Gets the value of a certain setting.
217
218         :param `id`: can be one of the size values in `Metric Ordinals`.
219         """
220
221
222         if id == AUI_DOCKART_SASH_SIZE:
223             return self._sash_size
224         elif id == AUI_DOCKART_CAPTION_SIZE:
225             return self._caption_size
226         elif id == AUI_DOCKART_GRIPPER_SIZE:
227             return self._gripper_size
228         elif id == AUI_DOCKART_PANE_BORDER_SIZE:
229             return self._border_size
230         elif id == AUI_DOCKART_PANE_BUTTON_SIZE:
231             return self._button_size
232         elif id == AUI_DOCKART_GRADIENT_TYPE:
233             return self._gradient_type
234         elif id == AUI_DOCKART_DRAW_SASH_GRIP:
235             return self._draw_sash
236         else:
237             raise Exception("Invalid Metric Ordinal.")
238
239
240     def SetMetric(self, id, new_val):
241         """
242         Sets the value of a certain setting using `new_val`
243
244         :param `id`: can be one of the size values in `Metric Ordinals`;
245         :param `new_val`: the new value of the setting.
246         """
247
248         if id == AUI_DOCKART_SASH_SIZE:
249             self._sash_size = new_val
250         elif id == AUI_DOCKART_CAPTION_SIZE:
251             self._caption_size = new_val
252         elif id == AUI_DOCKART_GRIPPER_SIZE:
253             self._gripper_size = new_val
254         elif id == AUI_DOCKART_PANE_BORDER_SIZE:
255             self._border_size = new_val
256         elif id == AUI_DOCKART_PANE_BUTTON_SIZE:
257             self._button_size = new_val
258         elif id == AUI_DOCKART_GRADIENT_TYPE:
259             self._gradient_type = new_val
260         elif id == AUI_DOCKART_DRAW_SASH_GRIP:
261             self._draw_sash = new_val
262         else:
263             raise Exception("Invalid Metric Ordinal.")
264
265
266     def GetColor(self, id):
267         """
268         Gets the colour of a certain setting.
269
270         :param `id`: can be one of the colour values in `Metric Ordinals`.
271         """
272
273         if id == AUI_DOCKART_BACKGROUND_COLOUR:
274             return self._background_brush.GetColour()
275         elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR:
276             return self._background_gradient_colour
277         elif id == AUI_DOCKART_SASH_COLOUR:
278             return self._sash_brush.GetColour()
279         elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR:
280             return self._inactive_caption_colour
281         elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR:
282             return self._inactive_caption_gradient_colour
283         elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR:
284             return self._inactive_caption_text_colour
285         elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR:
286             return self._active_caption_colour
287         elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR:
288             return self._active_caption_gradient_colour
289         elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR:
290             return self._active_caption_text_colour        
291         elif id == AUI_DOCKART_BORDER_COLOUR:
292             return self._border_pen.GetColour()
293         elif id == AUI_DOCKART_GRIPPER_COLOUR:
294             return self._gripper_brush.GetColour()
295         else:
296             raise Exception("Invalid Colour Ordinal.")
297
298
299     def SetColor(self, id, colour):
300         """
301         Sets the colour of a certain setting.
302
303         :param `id`: can be one of the colour values in `Metric Ordinals`;
304         :param `colour`: the new value of the setting.
305         """
306
307         if isinstance(colour, basestring):
308             colour = wx.NamedColour(colour)
309         elif isinstance(colour, types.TupleType):
310             colour = wx.Colour(*colour)
311         elif isinstance(colour, types.IntType):
312             colour = wx.ColourRGB(colour)
313         
314         if id == AUI_DOCKART_BACKGROUND_COLOUR:
315             self._background_brush.SetColour(colour)
316         elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR:
317             self._background_gradient_colour = colour
318         elif id == AUI_DOCKART_SASH_COLOUR:
319             self._sash_brush.SetColour(colour)
320         elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR:
321             self._inactive_caption_colour = colour
322             if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__":
323                 # No custom bitmaps for the pane close button
324                 # Change the MAC close bitmap colour
325                 self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, colour)
326
327         elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR:
328             self._inactive_caption_gradient_colour = colour
329         elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR:
330             self._inactive_caption_text_colour = colour
331         elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR:
332             self._active_caption_colour = colour
333             if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__":
334                 # No custom bitmaps for the pane close button
335                 # Change the MAC close bitmap colour
336                 self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, colour)
337                 
338         elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR:
339             self._active_caption_gradient_colour = colour
340         elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR:
341             self._active_caption_text_colour = colour
342         elif id == AUI_DOCKART_BORDER_COLOUR:
343             self._border_pen.SetColour(colour)
344         elif id == AUI_DOCKART_GRIPPER_COLOUR:
345             self._gripper_brush.SetColour(colour)
346             self._gripper_pen1.SetColour(StepColour(colour, 40))
347             self._gripper_pen2.SetColour(StepColour(colour, 60))
348         else:
349             raise Exception("Invalid Colour Ordinal.")
350         
351
352     GetColour = GetColor
353     SetColour = SetColor
354
355     def SetFont(self, id, font):
356         """
357         Sets a font setting.
358         
359         :param `id`: must be ``AUI_DOCKART_CAPTION_FONT``;
360         :param `font`: an instance of `wx.Font`.
361         """
362         
363         if id == AUI_DOCKART_CAPTION_FONT:
364             self._caption_font = font
365
366
367     def GetFont(self, id):
368         """
369         Gets a font setting.
370         
371         :param `id`: must be ``AUI_DOCKART_CAPTION_FONT``, otherwise `wx.NullFont` is returned.
372         """
373         
374         if id == AUI_DOCKART_CAPTION_FONT:
375             return self._caption_font
376         
377         return wx.NullFont
378
379
380     def DrawSash(self, dc, window, orient, rect):
381         """
382         Draws a sash between two windows.
383
384         :param `dc`: a `wx.DC` device context;
385         :param `window`: an instance of `wx.Window`;
386         :param `orient`: the sash orientation;
387         :param `rect`: the sash rectangle.
388         """                
389
390         # AG: How do we make this work?!?
391         # RendererNative does not use the sash_brush chosen by the user
392         # and the rect.GetSize() is ignored as the sash is always drawn
393         # 3 pixel wide
394         # wx.RendererNative.Get().DrawSplitterSash(window, dc, rect.GetSize(), pos, orient)
395
396         dc.SetPen(wx.TRANSPARENT_PEN)
397         dc.SetBrush(self._sash_brush)
398         dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)        
399
400         draw_sash = self.GetMetric(AUI_DOCKART_DRAW_SASH_GRIP)
401         if draw_sash:
402             self.DrawSashGripper(dc, orient, rect)
403
404
405     def DrawBackground(self, dc, window, orient, rect):
406         """
407         Draws a background.
408
409         :param `dc`: a `wx.DC` device context;
410         :param `window`: an instance of `wx.Window`;
411         :param `orient`: the gradient (if any) orientation;
412         :param `rect`: the background rectangle.
413         """
414
415         dc.SetPen(wx.TRANSPARENT_PEN)
416         if wx.Platform == "__WXMAC__":
417             # we have to clear first, otherwise we are drawing a light striped pattern
418             # over an already darker striped background
419             dc.SetBrush(wx.WHITE_BRUSH)
420             dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
421
422         DrawGradientRectangle(dc, rect, self._background_brush.GetColour(),
423                               self._background_gradient_colour,
424                               AUI_GRADIENT_HORIZONTAL, rect.x, 700)
425
426
427     def DrawBorder(self, dc, window, rect, pane):
428         """
429         Draws the pane border.
430
431         :param `dc`: a `wx.DC` device context;
432         :param `window`: an instance of `wx.Window`;
433         :param `rect`: the border rectangle;
434         :param `pane`: the pane for which the border is drawn.
435         """        
436
437         drect = wx.Rect(*rect)
438         
439         dc.SetPen(self._border_pen)
440         dc.SetBrush(wx.TRANSPARENT_BRUSH)
441
442         border_width = self.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
443
444         if pane.IsToolbar():
445         
446             for ii in xrange(0, border_width):
447             
448                 dc.SetPen(wx.WHITE_PEN)
449                 dc.DrawLine(drect.x, drect.y, drect.x+drect.width, drect.y)
450                 dc.DrawLine(drect.x, drect.y, drect.x, drect.y+drect.height)
451                 dc.SetPen(self._border_pen)       
452                 dc.DrawLine(drect.x, drect.y+drect.height-1,
453                             drect.x+drect.width, drect.y+drect.height-1)
454                 dc.DrawLine(drect.x+drect.width-1, drect.y,
455                             drect.x+drect.width-1, drect.y+drect.height)
456                 drect.Deflate(1, 1)
457         
458         else:
459         
460             for ii in xrange(0, border_width):
461             
462                 dc.DrawRectangle(drect.x, drect.y, drect.width, drect.height)
463                 drect.Deflate(1, 1)
464             
465
466     def DrawCaptionBackground(self, dc, rect, pane):
467         """
468         Draws the text caption background in the pane.
469
470         :param `dc`: a `wx.DC` device context;
471         :param `rect`: the text caption rectangle;
472         :param `pane`: the pane for which the text background is drawn.
473         """        
474
475         active = pane.state & optionActive
476  
477         if self._gradient_type == AUI_GRADIENT_NONE:
478             if active:
479                 dc.SetBrush(wx.Brush(self._active_caption_colour))
480             else:
481                 dc.SetBrush(wx.Brush(self._inactive_caption_colour))
482
483             dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)        
484
485         else:
486
487             switch_gradient = pane.HasCaptionLeft()
488             gradient_type = self._gradient_type
489             if switch_gradient:
490                 gradient_type = (self._gradient_type == AUI_GRADIENT_HORIZONTAL and [AUI_GRADIENT_VERTICAL] or \
491                                  [AUI_GRADIENT_HORIZONTAL])[0]
492             
493             if active:
494                 if wx.Platform == "__WXMAC__":
495                     DrawGradientRectangle(dc, rect, self._active_caption_colour,
496                                           self._active_caption_gradient_colour,
497                                           gradient_type)                    
498                 else:
499                     DrawGradientRectangle(dc, rect, self._active_caption_gradient_colour,
500                                           self._active_caption_colour,
501                                           gradient_type)
502             else:
503                 if wx.Platform == "__WXMAC__":
504                     DrawGradientRectangle(dc, rect, self._inactive_caption_gradient_colour,
505                                           self._inactive_caption_colour,
506                                           gradient_type)
507                 else:
508                     DrawGradientRectangle(dc, rect, self._inactive_caption_colour,
509                                           self._inactive_caption_gradient_colour,
510                                           gradient_type)
511
512
513     def DrawIcon(self, dc, rect, pane):
514         """
515         Draws the icon in the pane caption area.
516
517         :param `dc`: a `wx.DC` device context;
518         :param `rect`: the pane caption rectangle;
519         :param `pane`: the pane for which the icon is drawn.
520         """        
521         
522         # Draw the icon centered vertically 
523         if pane.icon.Ok():
524             if pane.HasCaptionLeft():
525                 bmp = wx.ImageFromBitmap(pane.icon).Rotate90(clockwise=False)
526                 dc.DrawBitmap(bmp.ConvertToBitmap(), rect.x+(rect.width-pane.icon.GetWidth())/2, rect.y+rect.height-2-pane.icon.GetHeight(), True)
527             else:
528                 dc.DrawBitmap(pane.icon, rect.x+2, rect.y+(rect.height-pane.icon.GetHeight())/2, True)
529
530
531     def DrawCaption(self, dc, window, text, rect, pane):
532         """
533         Draws the text in the pane caption.
534
535         :param `dc`: a `wx.DC` device context;
536         :param `window`: an instance of `wx.Window`;
537         :param `text`: the text to be displayed;
538         :param `rect`: the pane caption rectangle;
539         :param `pane`: the pane for which the text is drawn.
540         """        
541
542         dc.SetPen(wx.TRANSPARENT_PEN)
543         dc.SetFont(self._caption_font)
544         
545         self.DrawCaptionBackground(dc, rect, pane)
546
547         if pane.state & optionActive:
548             dc.SetTextForeground(self._active_caption_text_colour)
549         else:
550             dc.SetTextForeground(self._inactive_caption_text_colour)
551
552         w, h = dc.GetTextExtent("ABCDEFHXfgkj")
553
554         clip_rect = wx.Rect(*rect)
555         btns = pane.CountButtons()
556
557         captionLeft = pane.HasCaptionLeft()
558         variable = (captionLeft and [rect.height] or [rect.width])[0]
559
560         variable -= 3      # text offset
561         variable -= 2      # button padding
562
563         caption_offset = 0
564         if pane.icon:
565             if captionLeft:
566                 caption_offset += pane.icon.GetHeight() + 3
567             else:
568                 caption_offset += pane.icon.GetWidth() + 3
569                 
570             self.DrawIcon(dc, rect, pane)
571
572         variable -= caption_offset
573         variable -= btns*(self._button_size + self._border_size)
574         draw_text = ChopText(dc, text, variable)
575
576         if captionLeft:
577             dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-1, rect.y+rect.height-3-caption_offset, 90)
578         else:
579             dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-1)
580
581
582     def RequestUserAttention(self, dc, window, text, rect, pane):
583         """
584         Requests the user attention by intermittently highlighting the pane caption.
585
586         :param `dc`: a `wx.DC` device context;
587         :param `window`: an instance of `wx.Window`;
588         :param `text`: the text to be displayed;
589         :param `rect`: the pane caption rectangle;
590         :param `pane`: the pane for which the text is drawn.
591         """        
592
593         state = pane.state
594         pane.state &= ~optionActive
595         
596         for indx in xrange(6):
597             active = (indx%2 == 0 and [True] or [False])[0]
598             if active:
599                 pane.state |= optionActive
600             else:
601                 pane.state &= ~optionActive
602                 
603             self.DrawCaptionBackground(dc, rect, pane)
604             self.DrawCaption(dc, window, text, rect, pane)
605             wx.SafeYield()
606             wx.MilliSleep(350)
607
608         pane.state = state
609         
610
611     def DrawGripper(self, dc, window, rect, pane):
612         """
613         Draws a gripper on the pane.
614
615         :param `dc`: a `wx.DC` device context;
616         :param `window`: an instance of `wx.Window`;
617         :param `rect`: the pane caption rectangle;
618         :param `pane`: the pane for which the gripper is drawn.
619         """        
620
621         dc.SetPen(wx.TRANSPARENT_PEN)
622         dc.SetBrush(self._gripper_brush)
623
624         dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
625
626         if not pane.HasGripperTop():
627             y = 4
628             while 1:
629                 dc.SetPen(self._gripper_pen1)
630                 dc.DrawPoint(rect.x+3, rect.y+y) 
631                 dc.SetPen(self._gripper_pen2) 
632                 dc.DrawPoint(rect.x+3, rect.y+y+1) 
633                 dc.DrawPoint(rect.x+4, rect.y+y) 
634                 dc.SetPen(self._gripper_pen3) 
635                 dc.DrawPoint(rect.x+5, rect.y+y+1) 
636                 dc.DrawPoint(rect.x+5, rect.y+y+2) 
637                 dc.DrawPoint(rect.x+4, rect.y+y+2) 
638                 y = y + 4 
639                 if y > rect.GetHeight() - 4:
640                     break
641         else:
642             x = 4
643             while 1:
644                 dc.SetPen(self._gripper_pen1) 
645                 dc.DrawPoint(rect.x+x, rect.y+3) 
646                 dc.SetPen(self._gripper_pen2) 
647                 dc.DrawPoint(rect.x+x+1, rect.y+3) 
648                 dc.DrawPoint(rect.x+x, rect.y+4) 
649                 dc.SetPen(self._gripper_pen3) 
650                 dc.DrawPoint(rect.x+x+1, rect.y+5) 
651                 dc.DrawPoint(rect.x+x+2, rect.y+5) 
652                 dc.DrawPoint(rect.x+x+2, rect.y+4) 
653                 x = x + 4 
654                 if x > rect.GetWidth() - 4:
655                     break 
656         
657
658     def DrawPaneButton(self, dc, window, button, button_state, _rect, pane):
659         """
660         Draws a pane button in the pane caption area.
661
662         :param `dc`: a `wx.DC` device context;
663         :param `window`: an instance of `wx.Window`;
664         :param `button`: the button to be drawn;
665         :param `button_state`: the pane button state;
666         :param `_rect`: the pane caption rectangle;
667         :param `pane`: the pane for which the button is drawn.
668         """        
669         
670         if not pane:
671             return
672         
673         if button == AUI_BUTTON_CLOSE:
674             if pane.state & optionActive:
675                 bmp = self._active_close_bitmap
676             else:
677                 bmp = self._inactive_close_bitmap
678
679         elif button == AUI_BUTTON_PIN:
680             if pane.state & optionActive:
681                 bmp = self._active_pin_bitmap
682             else:
683                 bmp = self._inactive_pin_bitmap
684
685         elif button == AUI_BUTTON_MAXIMIZE_RESTORE:
686             if pane.IsMaximized():
687                 if pane.state & optionActive:
688                     bmp = self._active_restore_bitmap
689                 else:
690                     bmp = self._inactive_restore_bitmap
691             else:
692                 if pane.state & optionActive:
693                     bmp = self._active_maximize_bitmap
694                 else:
695                     bmp = self._inactive_maximize_bitmap
696
697         elif button == AUI_BUTTON_MINIMIZE:
698             if pane.state & optionActive:
699                 bmp = self._active_minimize_bitmap
700             else:
701                 bmp = self._inactive_minimize_bitmap
702
703         isVertical = pane.HasCaptionLeft()
704         
705         rect = wx.Rect(*_rect)
706
707         if isVertical:
708             old_x = rect.x
709             rect.x = rect.x + (rect.width/2) - (bmp.GetWidth()/2)
710             rect.width = old_x + rect.width - rect.x - 1
711         else:
712             old_y = rect.y
713             rect.y = rect.y + (rect.height/2) - (bmp.GetHeight()/2)
714             rect.height = old_y + rect.height - rect.y - 1
715
716         if button_state == AUI_BUTTON_STATE_PRESSED:
717             rect.x += 1
718             rect.y += 1
719
720         if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]:
721
722             if pane.state & optionActive:
723
724                 dc.SetBrush(wx.Brush(StepColour(self._active_caption_colour, 120)))
725                 dc.SetPen(wx.Pen(StepColour(self._active_caption_colour, 70)))
726
727             else:
728
729                 dc.SetBrush(wx.Brush(StepColour(self._inactive_caption_colour, 120)))
730                 dc.SetPen(wx.Pen(StepColour(self._inactive_caption_colour, 70)))
731
732             if wx.Platform != "__WXMAC__":
733                 # draw the background behind the button
734                 dc.DrawRectangle(rect.x, rect.y, 15, 15)
735             else:
736                 # Darker the bitmap a bit
737                 bmp = DarkenBitmap(bmp, self._active_caption_colour, StepColour(self._active_caption_colour, 110))
738
739         if isVertical:
740             bmp = wx.ImageFromBitmap(bmp).Rotate90(clockwise=False).ConvertToBitmap()
741             
742         # draw the button itself
743         dc.DrawBitmap(bmp, rect.x, rect.y, True)
744
745
746     def DrawSashGripper(self, dc, orient, rect):
747         """
748         Draws a sash gripper on a sash between two windows.
749
750         :param `dc`: a `wx.DC` device context;
751         :param `orient`: the sash orientation;
752         :param `rect`: the sash rectangle.
753         """
754         
755         dc.SetBrush(self._gripper_brush)
756
757         if orient == wx.HORIZONTAL:  # horizontal sash
758             
759             x = rect.x + int((1.0/4.0)*rect.width)
760             xend = rect.x + int((3.0/4.0)*rect.width)
761             y = rect.y + (rect.height/2) - 1
762
763             while 1:
764                 dc.SetPen(self._gripper_pen3)
765                 dc.DrawRectangle(x, y, 2, 2)
766                 dc.SetPen(self._gripper_pen2) 
767                 dc.DrawPoint(x+1, y+1)
768                 x = x + 5
769
770                 if x >= xend:
771                     break
772
773         else:
774
775             y = rect.y + int((1.0/4.0)*rect.height)
776             yend = rect.y + int((3.0/4.0)*rect.height)
777             x = rect.x + (rect.width/2) - 1
778
779             while 1:
780                 dc.SetPen(self._gripper_pen3)
781                 dc.DrawRectangle(x, y, 2, 2)
782                 dc.SetPen(self._gripper_pen2) 
783                 dc.DrawPoint(x+1, y+1)
784                 y = y + 5
785
786                 if y >= yend:
787                     break
788
789
790     def SetDefaultPaneBitmaps(self, isMac):
791         """
792         Assigns the default pane bitmaps.
793
794         :param `isMac`: whether we are on wxMAC or not.
795         """
796
797         if isMac:
798             self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, self._inactive_caption_colour)
799             self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, self._active_caption_colour)
800         else:
801             self._inactive_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._inactive_caption_text_colour)
802             self._active_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._active_caption_text_colour)
803             
804         if isMac:
805             self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE)
806             self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE)
807         else:
808             self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._inactive_caption_text_colour)
809             self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._active_caption_text_colour)
810
811         if isMac:
812             self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE)
813             self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE)
814         else:
815             self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._inactive_caption_text_colour)
816             self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._active_caption_text_colour)
817
818         if isMac:
819             self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE)
820             self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE)
821         else:
822             self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._inactive_caption_text_colour)
823             self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._active_caption_text_colour)
824
825         self._inactive_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._inactive_caption_text_colour)
826         self._active_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._active_caption_text_colour)
827
828         self._custom_pane_bitmaps = False
829         
830         
831     def SetCustomPaneBitmap(self, bmp, button, active, maximize=False):
832         """
833         Sets a custom button bitmap for the pane button.
834
835         :param `bmp`: the actual bitmap to set;
836         :param `button`: the button identifier;
837         :param `active`: whether it is the bitmap for the active button or not;
838         :param `maximize`: used to distinguish between the maximize and restore bitmaps.
839         """
840
841         if bmp.GetWidth() > 16 or bmp.GetHeight() > 16:
842             raise Exception("The input bitmap is too big")
843
844         if button == AUI_BUTTON_CLOSE:
845             if active:
846                 self._active_close_bitmap = bmp
847             else:
848                 self._inactive_close_bitmap = bmp
849
850             if wx.Platform == "__WXMAC__":
851                 self._custom_pane_bitmaps = True                
852
853         elif button == AUI_BUTTON_PIN:
854             if active:
855                 self._active_pin_bitmap = bmp
856             else:
857                 self._inactive_pin_bitmap = bmp
858
859         elif button == AUI_BUTTON_MAXIMIZE_RESTORE:
860             if maximize:
861                 if active:
862                     self._active_maximize_bitmap = bmp
863                 else:
864                     self._inactive_maximize_bitmap = bmp
865             else:
866                 if active:
867                     self._active_restore_bitmap = bmp
868                 else:
869                     self._inactive_restore_bitmap = bmp
870
871         elif button == AUI_BUTTON_MINIMIZE:
872             if active:
873                 self._active_minimize_bitmap = bmp
874             else:
875                 self._inactive_minimize_bitmap = bmp
876
877
878 if _ctypes:
879     class RECT(ctypes.Structure):
880         """ Used to handle L{ModernDockArt} on Windows XP/Vista/7. """
881         _fields_ = [('left', ctypes.c_ulong),('top', ctypes.c_ulong),('right', ctypes.c_ulong),('bottom', ctypes.c_ulong)]
882
883         def dump(self):
884             """ Dumps `self` as a `wx.Rect`. """
885             return map(int, (self.left, self.top, self.right, self.bottom))
886
887
888     class SIZE(ctypes.Structure):
889         """ Used to handle L{ModernDockArt} on Windows XP/Vista/7. """
890         _fields_ = [('x', ctypes.c_long),('y', ctypes.c_long)]
891
892
893 class ModernDockArt(AuiDefaultDockArt):
894     """
895     ModernDockArt is a custom `AuiDockArt` class, that implements a look similar to
896     Firefox and other recents applications. 
897
898     Is uses the `winxptheme` module and XP themes whenever possible, so it should
899     look good even if the user has a custom theme.
900
901     :note: This dock art is Windows only and will only work if you have installed
902      Mark Hammond's `pywin32` module (http://sourceforge.net/projects/pywin32/).
903     """
904
905     def __init__(self, win):
906         """
907         Default class constructor. 
908
909         :param `win`: the window managed by L{AuiManager}. 
910         """
911         
912         AuiDefaultDockArt.__init__(self)
913         
914         self.win = win
915
916         # Get the size of a small close button (themed)
917         hwnd = self.win.GetHandle()
918         
919         self.hTheme1 = winxptheme.OpenThemeData(hwnd, "Window")
920         
921         self.usingTheme = True
922         
923         if not self.hTheme1:
924             self.usingTheme = False
925
926         self._button_size = 13
927
928         self._button_border_size = 3
929         self._caption_text_indent = 6
930         self._caption_size = 22
931         
932         # We only highlight the active pane with the caption text being in bold.
933         # So we do not want a special colour for active elements.        
934         self._active_close_bitmap = self._inactive_close_bitmap
935
936         self.Init()
937         
938
939     def Init(self):
940         """ Initializes the dock art. """
941
942         AuiDefaultDockArt.Init(self)
943         
944         self._active_caption_colour = self._inactive_caption_colour
945         self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_CAPTIONTEXT)
946         self._inactive_caption_text_colour = self._active_caption_text_colour
947
948
949     def DrawCaption(self, dc, window, text, rect, pane):
950         """
951         Draws the text in the pane caption.
952
953         :param `dc`: a `wx.DC` device context;
954         :param `window`: an instance of `wx.Window`;
955         :param `text`: the text to be displayed;
956         :param `rect`: the pane caption rectangle;
957         :param `pane`: the pane for which the text is drawn.
958         """        
959
960         dc.SetPen(wx.TRANSPARENT_PEN)
961         self.DrawCaptionBackground(dc, rect, pane)
962
963         active = ((pane.state & optionActive) and [True] or [False])[0]
964
965         self._caption_font.SetWeight(wx.FONTWEIGHT_BOLD)
966         dc.SetFont(self._caption_font)
967         
968         if active:
969             dc.SetTextForeground(self._active_caption_text_colour)
970         else:
971             dc.SetTextForeground(self._inactive_caption_text_colour)
972
973         w, h = dc.GetTextExtent("ABCDEFHXfgkj")
974
975         clip_rect = wx.Rect(*rect)
976         btns = pane.CountButtons()
977
978         captionLeft = pane.HasCaptionLeft()
979         variable = (captionLeft and [rect.height] or [rect.width])[0]
980
981         variable -= 3      # text offset
982         variable -= 2      # button padding
983
984         caption_offset = 0
985         if pane.icon:
986             if captionLeft:
987                 caption_offset += pane.icon.GetHeight() + 3
988             else:
989                 caption_offset += pane.icon.GetWidth() + 3
990                 
991             self.DrawIcon(dc, rect, pane)
992
993         diff = -2
994         if self.usingTheme:
995             diff = -1
996
997         variable -= caption_offset
998         variable -= btns*(self._button_size + self._button_border_size)
999         draw_text = ChopText(dc, text, variable)
1000
1001         if captionLeft:
1002             dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-diff, rect.y+rect.height-3-caption_offset, 90)
1003         else:
1004             dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-diff)
1005
1006
1007     def DrawCaptionBackground(self, dc, rect, pane):
1008         """
1009         Draws the text caption background in the pane.
1010
1011         :param `dc`: a `wx.DC` device context;
1012         :param `rect`: the text caption rectangle;
1013         :param `pane`: the pane for which we are drawing the caption background.
1014         """        
1015
1016         dc.SetBrush(self._background_brush)
1017         dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
1018
1019         active = ((pane.state & optionActive) and [True] or [False])[0]
1020
1021         if self.usingTheme:
1022             
1023             rectangle = wx.Rect()
1024
1025             rc = RECT(rectangle.x, rectangle.y, rectangle.width, rectangle.height)
1026
1027             # If rect x/y values are negative rc.right/bottom values will overflow and winxptheme.DrawThemeBackground
1028             # will raise a TypeError. Ensure they are never negative.
1029             rect.x = max(0, rect.x)
1030             rect.y = max(0, rect.y)
1031
1032             rc.top = rect.x
1033             rc.left = rect.y
1034             rc.right = rect.x + rect.width
1035             rc.bottom = rect.y + rect.height
1036
1037             if active:
1038                 winxptheme.DrawThemeBackground(self.hTheme1, dc.GetHDC(), 5, 1, (rc.top, rc.left, rc.right, rc.bottom), None)
1039             else:
1040                 winxptheme.DrawThemeBackground(self.hTheme1, dc.GetHDC(), 5, 2, (rc.top, rc.left, rc.right, rc.bottom), None)
1041             
1042         else:
1043
1044             AuiDefaultDockArt.DrawCaptionBackground(self, dc, rect, pane)
1045
1046
1047     def RequestUserAttention(self, dc, window, text, rect, pane):
1048         """
1049         Requests the user attention by intermittently highlighting the pane caption.
1050
1051         :param `dc`: a `wx.DC` device context;
1052         :param `window`: an instance of `wx.Window`;
1053         :param `text`: the text to be displayed;
1054         :param `rect`: the pane caption rectangle;
1055         :param `pane`: the pane for which the text is drawn.
1056         """        
1057     
1058         state = pane.state
1059         pane.state &= ~optionActive
1060         
1061         for indx in xrange(6):
1062             active = (indx%2 == 0 and [True] or [False])[0]
1063             if active:
1064                 pane.state |= optionActive
1065             else:
1066                 pane.state &= ~optionActive
1067                 
1068             self.DrawCaptionBackground(dc, rect, pane)
1069             self.DrawCaption(dc, window, text, rect, pane)
1070             wx.SafeYield()
1071             wx.MilliSleep(350)
1072
1073         pane.state = state
1074
1075
1076     def DrawPaneButton(self, dc, window, button, button_state, rect, pane):
1077         """
1078         Draws a pane button in the pane caption area.
1079
1080         :param `dc`: a `wx.DC` device context;
1081         :param `window`: an instance of `wx.Window`;
1082         :param `button`: the button to be drawn;
1083         :param `button_state`: the pane button state;
1084         :param `rect`: the pane caption rectangle;
1085         :param `pane`: the pane for which the button is drawn.
1086         """        
1087
1088         if self.usingTheme:
1089
1090             hTheme = self.hTheme1            
1091                     
1092             # Get the real button position (compensating for borders)
1093             drect = wx.Rect(rect.x, rect.y, self._button_size, self._button_size)
1094             
1095             # Draw the themed close button
1096             rc = RECT(0, 0, 0, 0)
1097             if pane.HasCaptionLeft():
1098                 rc.top = rect.x + self._button_border_size
1099                 rc.left = int(rect.y + 1.5*self._button_border_size)
1100                 rc.right = rect.x + self._button_size + self._button_border_size
1101                 rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size)
1102             else:
1103                 rc.top = rect.x - self._button_border_size
1104                 rc.left = int(rect.y + 1.5*self._button_border_size)
1105                 rc.right = rect.x + self._button_size- self._button_border_size
1106                 rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size)
1107
1108             if button == AUI_BUTTON_CLOSE:
1109                 btntype = 19
1110                 
1111             elif button == AUI_BUTTON_PIN:
1112                 btntype = 23
1113
1114             elif button == AUI_BUTTON_MAXIMIZE_RESTORE:
1115                 if not pane.IsMaximized():
1116                     btntype = 17
1117                 else:
1118                     btntype = 21
1119             else:
1120                 btntype = 15
1121
1122             state = 4 # CBS_DISABLED
1123             
1124             if pane.state & optionActive:
1125
1126                 if button_state == AUI_BUTTON_STATE_NORMAL:
1127                     state = 1 # CBS_NORMAL
1128
1129                 elif button_state == AUI_BUTTON_STATE_HOVER:
1130                     state = 2 # CBS_HOT
1131
1132                 elif button_state == AUI_BUTTON_STATE_PRESSED:
1133                     state = 3 # CBS_PUSHED
1134
1135                 else:
1136                     raise Exception("ERROR: Unknown State.")
1137
1138             else: # inactive pane
1139
1140                 if button_state == AUI_BUTTON_STATE_NORMAL:
1141                     state = 5 # CBS_NORMAL
1142
1143                 elif button_state == AUI_BUTTON_STATE_HOVER:
1144                     state = 6 # CBS_HOT
1145
1146                 elif button_state == AUI_BUTTON_STATE_PRESSED:
1147                     state = 7 # CBS_PUSHED
1148
1149                 else:
1150                     raise Exception("ERROR: Unknown State.")
1151
1152             try:
1153                 winxptheme.DrawThemeBackground(hTheme, dc.GetHDC(), btntype, state, (rc.top, rc.left, rc.right, rc.bottom), None)
1154             except TypeError:
1155                 return
1156
1157         else:
1158
1159             # Fallback to default closebutton if themes are not enabled
1160             rect2 = wx.Rect(rect.x-4, rect.y+2, rect.width, rect.height)
1161             AuiDefaultDockArt.DrawPaneButton(self, dc, window, button, button_state, rect2, pane)
1162