...
[iramuteq] / aui / tabart.py
1 """
2 Tab art provider code - a tab provider provides all drawing functionality to
3 the L{AuiNotebook}. This allows the L{AuiNotebook} to have a plugable look-and-feel.
4
5 By default, a L{AuiNotebook} uses an instance of this class called L{AuiDefaultTabArt}
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 tab art class. Call L{AuiNotebook.SetArtProvider} to make use this
9 new tab art.
10 """
11
12 __author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
13 __date__ = "31 March 2009"
14
15
16 import wx
17
18 if wx.Platform == '__WXMAC__':
19     import Carbon.Appearance
20
21 from aui_utilities import BitmapFromBits, StepColour, IndentPressedBitmap, ChopText
22 from aui_utilities import GetBaseColour, DrawMACCloseButton, LightColour, TakeScreenShot
23 from aui_utilities import CopyAttributes
24
25 from aui_constants import *
26
27
28 # -- GUI helper classes and functions --
29 class AuiCommandCapture(wx.PyEvtHandler):
30     """ A class to handle the dropdown window menu. """
31
32     def __init__(self):
33         """ Default class constructor. """
34
35         wx.PyEvtHandler.__init__(self)        
36         self._last_id = 0
37
38
39     def GetCommandId(self):
40         """ Returns the event command identifier. """
41
42         return self._last_id 
43
44
45     def ProcessEvent(self, event):
46         """
47         Processes an event, searching event tables and calling zero or more suitable
48         event handler function(s).
49
50         :param `event`: the event to process.
51
52         :note: Normally, your application would not call this function: it is called
53          in the wxPython implementation to dispatch incoming user interface events
54          to the framework (and application).
55          However, you might need to call it if implementing new functionality (such as
56          a new control) where you define new event types, as opposed to allowing the
57          user to override functions.
58
59          An instance where you might actually override the L{ProcessEvent} function is where
60          you want to direct event processing to event handlers not normally noticed by
61          wxPython. For example, in the document/view architecture, documents and views
62          are potential event handlers. When an event reaches a frame, L{ProcessEvent} will
63          need to be called on the associated document and view in case event handler
64          functions are associated with these objects. 
65
66          The normal order of event table searching is as follows:
67
68          1. If the object is disabled (via a call to `SetEvtHandlerEnabled`) the function
69             skips to step (6).
70          2. If the object is a `wx.Window`, L{ProcessEvent} is recursively called on the window's 
71             `wx.Validator`. If this returns ``True``, the function exits.
72          3. wxWidgets `SearchEventTable` is called for this event handler. If this fails, the
73             base class table is tried, and so on until no more tables exist or an appropriate
74             function was found, in which case the function exits.
75          4. The search is applied down the entire chain of event handlers (usually the chain
76             has a length of one). If this succeeds, the function exits.
77          5. If the object is a `wx.Window` and the event is a `wx.CommandEvent`, L{ProcessEvent} is
78             recursively applied to the parent window's event handler. If this returns ``True``,
79             the function exits.
80          6. Finally, L{ProcessEvent} is called on the `wx.App` object.
81         """
82         
83         if event.GetEventType() == wx.wxEVT_COMMAND_MENU_SELECTED:
84             self._last_id = event.GetId()
85             return True
86         
87         if self.GetNextHandler():
88             return self.GetNextHandler().ProcessEvent(event)
89
90         return False
91     
92
93 class AuiDefaultTabArt(object):
94     """
95     Tab art provider code - a tab provider provides all drawing functionality to
96     the L{AuiNotebook}. This allows the L{AuiNotebook} to have a plugable look-and-feel.
97
98     By default, a L{AuiNotebook} uses an instance of this class called L{AuiDefaultTabArt}
99     which provides bitmap art and a colour scheme that is adapted to the major platforms'
100     look. You can either derive from that class to alter its behaviour or write a
101     completely new tab art class. Call L{AuiNotebook.SetArtProvider} to make use this
102     new tab art.
103     """
104     
105     def __init__(self):
106         """ Default class constructor. """
107
108         self._normal_font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
109         self._selected_font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
110         self._selected_font.SetWeight(wx.BOLD)
111         self._measuring_font = self._selected_font
112
113         self._fixed_tab_width = 100
114         self._tab_ctrl_height = 0
115         self._buttonRect = wx.Rect()
116
117         self.SetDefaultColours()
118
119         if wx.Platform == "__WXMAC__":
120             bmp_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)
121             self._active_close_bmp = DrawMACCloseButton(bmp_colour)
122             self._disabled_close_bmp = DrawMACCloseButton(wx.Colour(128, 128, 128))
123         else:
124             self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
125             self._disabled_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.Colour(128, 128, 128))
126
127         self._hover_close_bmp = self._active_close_bmp
128         self._pressed_close_bmp = self._active_close_bmp
129
130         self._active_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.BLACK)
131         self._disabled_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.Colour(128, 128, 128))
132
133         self._active_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.BLACK)
134         self._disabled_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.Colour(128, 128, 128))
135
136         self._active_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.BLACK)
137         self._disabled_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.Colour(128, 128, 128))
138
139         if wx.Platform == "__WXMAC__":
140             # Get proper highlight colour for focus rectangle from the
141             # current Mac theme.  kThemeBrushFocusHighlight is
142             # available on Mac OS 8.5 and higher
143             if hasattr(wx, 'MacThemeColour'):
144                 c = wx.MacThemeColour(Carbon.Appearance.kThemeBrushFocusHighlight)
145             else:
146                 brush = wx.Brush(wx.BLACK)
147                 brush.MacSetTheme(Carbon.Appearance.kThemeBrushFocusHighlight)
148                 c = brush.GetColour()
149             self._focusPen = wx.Pen(c, 2, wx.SOLID)
150         else:
151             self._focusPen = wx.Pen(wx.BLACK, 1, wx.USER_DASH)
152             self._focusPen.SetDashes([1, 1])
153             self._focusPen.SetCap(wx.CAP_BUTT)
154             
155             
156     def SetBaseColour(self, base_colour):
157         """
158         Sets a new base colour.
159
160         :param `base_colour`: an instance of `wx.Colour`.
161         """
162         
163         self._base_colour = base_colour
164         self._base_colour_pen = wx.Pen(self._base_colour)
165         self._base_colour_brush = wx.Brush(self._base_colour)
166
167
168     def SetDefaultColours(self, base_colour=None):
169         """
170         Sets the default colours, which are calculated from the given base colour.
171
172         :param `base_colour`: an instance of `wx.Colour`. If defaulted to ``None``, a colour
173          is generated accordingly to the platform and theme.
174         """
175
176         if base_colour is None:
177             base_colour = GetBaseColour()
178
179         self.SetBaseColour( base_colour )
180         self._border_colour = StepColour(base_colour, 75)
181         self._border_pen = wx.Pen(self._border_colour)
182
183         self._background_top_colour = StepColour(self._base_colour, 90)
184         self._background_bottom_colour = StepColour(self._base_colour, 170)
185         
186         self._tab_top_colour = self._base_colour
187         self._tab_bottom_colour = wx.WHITE
188         self._tab_gradient_highlight_colour = wx.WHITE
189
190         self._tab_inactive_top_colour = self._base_colour
191         self._tab_inactive_bottom_colour = StepColour(self._tab_inactive_top_colour, 160)
192         
193         self._tab_text_colour = lambda page: page.text_colour
194         self._tab_disabled_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
195
196
197     def Clone(self):
198         """ Clones the art object. """
199
200         art = type(self)()
201         art.SetNormalFont(self.GetNormalFont())
202         art.SetSelectedFont(self.GetSelectedFont())
203         art.SetMeasuringFont(self.GetMeasuringFont())
204
205         art = CopyAttributes(art, self)
206         return art
207
208
209     def SetAGWFlags(self, agwFlags):
210         """
211         Sets the tab art flags.
212
213         :param `agwFlags`: a combination of the following values:
214
215          ==================================== ==================================
216          Flag name                            Description
217          ==================================== ==================================
218          ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
219          ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet.
220          ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet.
221          ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
222          ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
223          ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
224          ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
225          ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
226          ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
227          ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
228          ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
229          ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
230          ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
231          ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close L{AuiNotebook} tabs by mouse middle button click
232          ``AUI_NB_SUB_NOTEBOOK``              This style is used by L{AuiManager} to create automatic AuiNotebooks
233          ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
234          ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
235          ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
236          ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
237          ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
238          ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
239          ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
240          ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
241          ==================================== ==================================
242         
243         """
244
245         self._agwFlags = agwFlags
246
247
248     def GetAGWFlags(self):
249         """
250         Returns the tab art flags.
251
252         :see: L{SetAGWFlags} for a list of possible return values.
253         """
254
255         return self._agwFlags
256     
257             
258     def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
259         """
260         Sets the tab sizing information.
261         
262         :param `tab_ctrl_size`: the size of the tab control area;
263         :param `tab_count`: the number of tabs;
264         :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
265          to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
266         """
267         
268         self._fixed_tab_width = 100
269         minTabWidth, maxTabWidth = minMaxTabWidth
270
271         tot_width = tab_ctrl_size.x - self.GetIndentSize() - 4
272         agwFlags = self.GetAGWFlags()
273         
274         if agwFlags & AUI_NB_CLOSE_BUTTON:
275             tot_width -= self._active_close_bmp.GetWidth()
276         if agwFlags & AUI_NB_WINDOWLIST_BUTTON:
277             tot_width -= self._active_windowlist_bmp.GetWidth()
278
279         if tab_count > 0:
280             self._fixed_tab_width = tot_width/tab_count
281
282         if self._fixed_tab_width < 100:
283             self._fixed_tab_width = 100
284
285         if self._fixed_tab_width > tot_width/2:
286             self._fixed_tab_width = tot_width/2
287
288         if self._fixed_tab_width > 220:
289             self._fixed_tab_width = 220
290
291         if minTabWidth > -1:
292             self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
293         if maxTabWidth > -1:
294             self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
295
296         self._tab_ctrl_height = tab_ctrl_size.y
297     
298
299     def DrawBackground(self, dc, wnd, rect):
300         """
301         Draws the tab area background.
302
303         :param `dc`: a `wx.DC` device context;
304         :param `wnd`: a `wx.Window` instance object;
305         :param `rect`: the tab control rectangle.
306         """
307
308         self._buttonRect = wx.Rect()
309
310         # draw background
311         agwFlags = self.GetAGWFlags()
312         if agwFlags & AUI_NB_BOTTOM:
313             r = wx.Rect(rect.x, rect.y, rect.width+2, rect.height)
314
315         # TODO: else if (agwFlags & AUI_NB_LEFT) 
316         # TODO: else if (agwFlags & AUI_NB_RIGHT) 
317         else: #for AUI_NB_TOP
318             r = wx.Rect(rect.x, rect.y, rect.width+2, rect.height-3)
319
320         dc.GradientFillLinear(r, self._background_top_colour, self._background_bottom_colour, wx.SOUTH)
321
322         # draw base lines
323
324         dc.SetPen(self._border_pen)
325         y = rect.GetHeight()
326         w = rect.GetWidth()
327
328         if agwFlags & AUI_NB_BOTTOM:
329             dc.SetBrush(wx.Brush(self._background_bottom_colour))
330             dc.DrawRectangle(-1, 0, w+2, 4)
331
332         # TODO: else if (agwFlags & AUI_NB_LEFT) 
333         # TODO: else if (agwFlags & AUI_NB_RIGHT)
334         
335         else: # for AUI_NB_TOP
336             dc.SetBrush(self._base_colour_brush)
337             dc.DrawRectangle(-1, y-4, w+2, 4)
338
339
340     def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
341         """
342         Draws a single tab.
343
344         :param `dc`: a `wx.DC` device context;
345         :param `wnd`: a `wx.Window` instance object;
346         :param `page`: the tab control page associated with the tab;
347         :param `in_rect`: rectangle the tab should be confined to;
348         :param `close_button_state`: the state of the close button on the tab;
349         :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
350         """
351
352         # if the caption is empty, measure some temporary text
353         caption = page.caption
354         if not caption:
355             caption = "Xj"
356
357         dc.SetFont(self._selected_font)
358         selected_textx, selected_texty, dummy = dc.GetMultiLineTextExtent(caption)
359
360         dc.SetFont(self._normal_font)
361         normal_textx, normal_texty, dummy = dc.GetMultiLineTextExtent(caption)
362
363         control = page.control
364
365         # figure out the size of the tab
366         tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
367                                              page.active, close_button_state, control)
368
369         tab_height = self._tab_ctrl_height - 3
370         tab_width = tab_size[0]
371         tab_x = in_rect.x
372         tab_y = in_rect.y + in_rect.height - tab_height
373
374         caption = page.caption
375
376         # select pen, brush and font for the tab to be drawn
377
378         if page.active:
379         
380             dc.SetFont(self._selected_font)
381             textx, texty = selected_textx, selected_texty
382         
383         else:
384         
385             dc.SetFont(self._normal_font)
386             textx, texty = normal_textx, normal_texty
387
388         if not page.enabled:
389             dc.SetTextForeground(self._tab_disabled_text_colour)
390             pagebitmap = page.dis_bitmap
391         else:
392             dc.SetTextForeground(self._tab_text_colour(page))
393             pagebitmap = page.bitmap
394             
395         # create points that will make the tab outline
396
397         clip_width = tab_width
398         if tab_x + clip_width > in_rect.x + in_rect.width:
399             clip_width = in_rect.x + in_rect.width - tab_x
400
401         # since the above code above doesn't play well with WXDFB or WXCOCOA,
402         # we'll just use a rectangle for the clipping region for now --
403         dc.SetClippingRegion(tab_x, tab_y, clip_width+1, tab_height-3)
404
405         border_points = [wx.Point() for i in xrange(6)]
406         agwFlags = self.GetAGWFlags()
407         
408         if agwFlags & AUI_NB_BOTTOM:
409         
410             border_points[0] = wx.Point(tab_x,             tab_y)
411             border_points[1] = wx.Point(tab_x,             tab_y+tab_height-6)
412             border_points[2] = wx.Point(tab_x+2,           tab_y+tab_height-4)
413             border_points[3] = wx.Point(tab_x+tab_width-2, tab_y+tab_height-4)
414             border_points[4] = wx.Point(tab_x+tab_width,   tab_y+tab_height-6)
415             border_points[5] = wx.Point(tab_x+tab_width,   tab_y)
416         
417         else: #if (agwFlags & AUI_NB_TOP) 
418         
419             border_points[0] = wx.Point(tab_x,             tab_y+tab_height-4)
420             border_points[1] = wx.Point(tab_x,             tab_y+2)
421             border_points[2] = wx.Point(tab_x+2,           tab_y)
422             border_points[3] = wx.Point(tab_x+tab_width-2, tab_y)
423             border_points[4] = wx.Point(tab_x+tab_width,   tab_y+2)
424             border_points[5] = wx.Point(tab_x+tab_width,   tab_y+tab_height-4)
425         
426         # TODO: else if (agwFlags & AUI_NB_LEFT) 
427         # TODO: else if (agwFlags & AUI_NB_RIGHT) 
428
429         drawn_tab_yoff = border_points[1].y
430         drawn_tab_height = border_points[0].y - border_points[1].y
431
432         if page.active:
433         
434             # draw active tab
435
436             # draw base background colour
437             r = wx.Rect(tab_x, tab_y, tab_width, tab_height)
438             dc.SetPen(self._base_colour_pen)
439             dc.SetBrush(self._base_colour_brush)
440             dc.DrawRectangle(r.x+1, r.y+1, r.width-1, r.height-4)
441
442             # this white helps fill out the gradient at the top of the tab
443             dc.SetPen( wx.Pen(self._tab_gradient_highlight_colour) )
444             dc.SetBrush( wx.Brush(self._tab_gradient_highlight_colour) )
445             dc.DrawRectangle(r.x+2, r.y+1, r.width-3, r.height-4)
446
447             # these two points help the rounded corners appear more antialiased
448             dc.SetPen(self._base_colour_pen)
449             dc.DrawPoint(r.x+2, r.y+1)
450             dc.DrawPoint(r.x+r.width-2, r.y+1)
451
452             # set rectangle down a bit for gradient drawing
453             r.SetHeight(r.GetHeight()/2)
454             r.x += 2
455             r.width -= 2
456             r.y += r.height
457             r.y -= 2
458
459             # draw gradient background
460             top_colour = self._tab_bottom_colour
461             bottom_colour = self._tab_top_colour
462             dc.GradientFillLinear(r, bottom_colour, top_colour, wx.NORTH)
463         
464         else:
465         
466             # draw inactive tab
467
468             r = wx.Rect(tab_x, tab_y+1, tab_width, tab_height-3)
469
470             # start the gradent up a bit and leave the inside border inset
471             # by a pixel for a 3D look.  Only the top half of the inactive
472             # tab will have a slight gradient
473             r.x += 3
474             r.y += 1
475             r.width -= 4
476             r.height /= 2
477             r.height -= 1
478
479             # -- draw top gradient fill for glossy look
480             top_colour = self._tab_inactive_top_colour
481             bottom_colour = self._tab_inactive_bottom_colour
482             dc.GradientFillLinear(r, bottom_colour, top_colour, wx.NORTH)
483
484             r.y += r.height
485             r.y -= 1
486
487             # -- draw bottom fill for glossy look
488             top_colour = self._tab_inactive_bottom_colour
489             bottom_colour = self._tab_inactive_bottom_colour
490             dc.GradientFillLinear(r, top_colour, bottom_colour, wx.SOUTH)
491         
492         # draw tab outline
493         dc.SetPen(self._border_pen)
494         dc.SetBrush(wx.TRANSPARENT_BRUSH)
495         dc.DrawPolygon(border_points)
496
497         # there are two horizontal grey lines at the bottom of the tab control,
498         # this gets rid of the top one of those lines in the tab control
499         if page.active:
500         
501             if agwFlags & AUI_NB_BOTTOM:
502                 dc.SetPen(wx.Pen(self._background_bottom_colour))
503                 
504             # TODO: else if (agwFlags & AUI_NB_LEFT) 
505             # TODO: else if (agwFlags & AUI_NB_RIGHT) 
506             else: # for AUI_NB_TOP
507                 dc.SetPen(self._base_colour_pen)
508                 
509             dc.DrawLine(border_points[0].x+1,
510                         border_points[0].y,
511                         border_points[5].x,
512                         border_points[5].y)
513         
514         text_offset = tab_x + 8
515         close_button_width = 0
516
517         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
518             close_button_width = self._active_close_bmp.GetWidth()
519
520             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
521                 text_offset += close_button_width - 5
522                 
523         bitmap_offset = 0
524         
525         if pagebitmap.IsOk():
526         
527             bitmap_offset = tab_x + 8
528             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
529                 bitmap_offset += close_button_width - 5
530
531             # draw bitmap
532             dc.DrawBitmap(pagebitmap,
533                           bitmap_offset,
534                           drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2),
535                           True)
536
537             text_offset = bitmap_offset + pagebitmap.GetWidth()
538             text_offset += 3 # bitmap padding
539
540         else:
541
542             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
543                 text_offset = tab_x + 8
544         
545         draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
546
547         ypos = drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2) - 1
548
549         offset_focus = text_offset     
550         if control is not None:
551             if control.GetPosition() != wx.Point(text_offset+1, ypos):
552                 control.SetPosition(wx.Point(text_offset+1, ypos))
553
554             if not control.IsShown():
555                 control.Show()
556
557             if paint_control:
558                 bmp = TakeScreenShot(control.GetScreenRect())
559                 dc.DrawBitmap(bmp, text_offset+1, ypos, True)
560                 
561             controlW, controlH = control.GetSize()
562             text_offset += controlW + 4
563             textx += controlW + 4
564             
565         # draw tab text
566         rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
567         dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
568
569         # draw focus rectangle
570         if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
571             self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff, drawn_tab_height, rectx, recty)
572         
573         out_button_rect = wx.Rect()
574         
575         # draw close button if necessary
576         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
577         
578             bmp = self._disabled_close_bmp
579
580             if close_button_state == AUI_BUTTON_STATE_HOVER:
581                 bmp = self._hover_close_bmp
582             elif close_button_state == AUI_BUTTON_STATE_PRESSED:
583                 bmp = self._pressed_close_bmp
584
585             shift = (agwFlags & AUI_NB_BOTTOM and [1] or [0])[0]
586
587             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
588                 rect = wx.Rect(tab_x + 4, tab_y + (tab_height - bmp.GetHeight())/2 - shift,
589                                close_button_width, tab_height)
590             else:
591                 rect = wx.Rect(tab_x + tab_width - close_button_width - 1,
592                                tab_y + (tab_height - bmp.GetHeight())/2 - shift,
593                                close_button_width, tab_height)
594
595             rect = IndentPressedBitmap(rect, close_button_state)
596             dc.DrawBitmap(bmp, rect.x, rect.y, True)
597
598             out_button_rect = rect
599         
600         out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
601
602         dc.DestroyClippingRegion()
603
604         return out_tab_rect, out_button_rect, x_extent
605     
606
607     def SetCustomButton(self, bitmap_id, button_state, bmp):
608         """
609         Sets a custom bitmap for the close, left, right and window list
610         buttons.
611         
612         :param `bitmap_id`: the button identifier;
613         :param `button_state`: the button state;
614         :param `bmp`: the custom bitmap to use for the button.
615         """
616
617         if bitmap_id == AUI_BUTTON_CLOSE:
618             if button_state == AUI_BUTTON_STATE_NORMAL:
619                 self._active_close_bmp = bmp
620                 self._hover_close_bmp = self._active_close_bmp
621                 self._pressed_close_bmp = self._active_close_bmp
622                 self._disabled_close_bmp = self._active_close_bmp
623                     
624             elif button_state == AUI_BUTTON_STATE_HOVER:
625                 self._hover_close_bmp = bmp
626             elif button_state == AUI_BUTTON_STATE_PRESSED:
627                 self._pressed_close_bmp = bmp
628             else:
629                 self._disabled_close_bmp = bmp
630
631         elif bitmap_id == AUI_BUTTON_LEFT:
632             if button_state & AUI_BUTTON_STATE_DISABLED:
633                 self._disabled_left_bmp = bmp
634             else:
635                 self._active_left_bmp = bmp
636
637         elif bitmap_id == AUI_BUTTON_RIGHT:
638             if button_state & AUI_BUTTON_STATE_DISABLED:
639                 self._disabled_right_bmp = bmp
640             else:
641                 self._active_right_bmp = bmp
642
643         elif bitmap_id == AUI_BUTTON_WINDOWLIST:
644             if button_state & AUI_BUTTON_STATE_DISABLED:
645                 self._disabled_windowlist_bmp = bmp
646             else:
647                 self._active_windowlist_bmp = bmp
648         
649
650     def GetIndentSize(self):
651         """ Returns the tabs indent size. """
652
653         return 5
654
655
656     def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
657         """
658         Returns the tab size for the given caption, bitmap and button state.
659
660         :param `dc`: a `wx.DC` device context;
661         :param `wnd`: a `wx.Window` instance object;
662         :param `caption`: the tab text caption;
663         :param `bitmap`: the bitmap displayed on the tab;
664         :param `active`: whether the tab is selected or not;
665         :param `close_button_state`: the state of the close button on the tab;
666         :param `control`: a `wx.Window` instance inside a tab (or ``None``).
667         """
668
669         dc.SetFont(self._measuring_font)
670         measured_textx, measured_texty, dummy = dc.GetMultiLineTextExtent(caption)
671
672         # add padding around the text
673         tab_width = measured_textx
674         tab_height = measured_texty
675
676         # if the close button is showing, add space for it
677         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
678             tab_width += self._active_close_bmp.GetWidth() + 3
679
680         # if there's a bitmap, add space for it
681         if bitmap.IsOk():
682             tab_width += bitmap.GetWidth()
683             tab_width += 3 # right side bitmap padding
684             tab_height = max(tab_height, bitmap.GetHeight())
685         
686         # add padding
687         tab_width += 16
688         tab_height += 10
689
690         agwFlags = self.GetAGWFlags()
691         if agwFlags & AUI_NB_TAB_FIXED_WIDTH:
692             tab_width = self._fixed_tab_width
693
694         if control is not None:
695             tab_width += control.GetSize().GetWidth() + 4
696             
697         x_extent = tab_width
698
699         return (tab_width, tab_height), x_extent
700
701
702     def DrawButton(self, dc, wnd, in_rect, button, orientation):
703         """
704         Draws a button on the tab or on the tab area, depending on the button identifier. 
705
706         :param `dc`: a `wx.DC` device context;
707         :param `wnd`: a `wx.Window` instance object;
708         :param `in_rect`: rectangle the tab should be confined to;
709         :param `button`: an instance of the button class;
710         :param `orientation`: the tab orientation.
711         """
712
713         bitmap_id, button_state = button.id, button.cur_state
714         
715         if bitmap_id == AUI_BUTTON_CLOSE:
716             if button_state & AUI_BUTTON_STATE_DISABLED:
717                 bmp = self._disabled_close_bmp
718             elif button_state & AUI_BUTTON_STATE_HOVER:
719                 bmp = self._hover_close_bmp
720             elif button_state & AUI_BUTTON_STATE_PRESSED:
721                 bmp = self._pressed_close_bmp
722             else:
723                 bmp = self._active_close_bmp
724
725         elif bitmap_id == AUI_BUTTON_LEFT:
726             if button_state & AUI_BUTTON_STATE_DISABLED:
727                 bmp = self._disabled_left_bmp
728             else:
729                 bmp = self._active_left_bmp
730
731         elif bitmap_id == AUI_BUTTON_RIGHT:
732             if button_state & AUI_BUTTON_STATE_DISABLED:
733                 bmp = self._disabled_right_bmp
734             else:
735                 bmp = self._active_right_bmp
736
737         elif bitmap_id == AUI_BUTTON_WINDOWLIST:
738             if button_state & AUI_BUTTON_STATE_DISABLED:
739                 bmp = self._disabled_windowlist_bmp
740             else:
741                 bmp = self._active_windowlist_bmp
742
743         else:
744             if button_state & AUI_BUTTON_STATE_DISABLED:
745                 bmp = button.dis_bitmap
746             else:
747                 bmp = button.bitmap
748                 
749         if not bmp.IsOk():
750             return
751
752         rect = wx.Rect(*in_rect)
753
754         if orientation == wx.LEFT:
755         
756             rect.SetX(in_rect.x)
757             rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2))
758             rect.SetWidth(bmp.GetWidth())
759             rect.SetHeight(bmp.GetHeight())
760         
761         else:
762         
763             rect = wx.Rect(in_rect.x + in_rect.width - bmp.GetWidth(),
764                            ((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
765                            bmp.GetWidth(), bmp.GetHeight())
766         
767         rect = IndentPressedBitmap(rect, button_state)
768         dc.DrawBitmap(bmp, rect.x, rect.y, True)
769
770         out_rect = rect
771
772         if bitmap_id == AUI_BUTTON_RIGHT:
773             self._buttonRect = wx.Rect(rect.x, rect.y, 30, rect.height)
774         
775         return out_rect
776
777
778     def DrawFocusRectangle(self, dc, page, wnd, draw_text, text_offset, bitmap_offset, drawn_tab_yoff, drawn_tab_height, textx, texty):
779         """
780         Draws the focus rectangle on a tab.
781
782         :param `dc`: a `wx.DC` device context;
783         :param `page`: the page associated with the tab;
784         :param `wnd`: a `wx.Window` instance object;
785         :param `draw_text`: the text that has been drawn on the tab;
786         :param `text_offset`: the text offset on the tab;
787         :param `bitmap_offset`: the bitmap offset on the tab;
788         :param `drawn_tab_yoff`: the y offset of the tab text;
789         :param `drawn_tab_height`: the height of the tab;
790         :param `textx`: the x text extent;
791         :param `texty`: the y text extent.
792         """
793
794         if self.GetAGWFlags() & AUI_NB_NO_TAB_FOCUS:
795             return
796         
797         if page.active and wx.Window.FindFocus() == wnd:
798         
799             focusRectText = wx.Rect(text_offset, (drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2)),
800                                     textx, texty)
801
802             if page.bitmap.IsOk():
803                 focusRectBitmap = wx.Rect(bitmap_offset, drawn_tab_yoff + (drawn_tab_height/2) - (page.bitmap.GetHeight()/2),
804                                           page.bitmap.GetWidth(), page.bitmap.GetHeight())
805
806             if page.bitmap.IsOk() and draw_text == "":
807                 focusRect = wx.Rect(*focusRectBitmap)
808             elif not page.bitmap.IsOk() and draw_text != "":
809                 focusRect = wx.Rect(*focusRectText)
810             elif page.bitmap.IsOk() and draw_text != "":
811                 focusRect = focusRectText.Union(focusRectBitmap)
812
813             focusRect.Inflate(2, 2)
814
815             dc.SetBrush(wx.TRANSPARENT_BRUSH)
816             dc.SetPen(self._focusPen)
817             dc.DrawRoundedRectangleRect(focusRect, 2)
818         
819
820     def GetBestTabCtrlSize(self, wnd, pages, required_bmp_size):
821         """
822         Returns the best tab control size.
823
824         :param `wnd`: a `wx.Window` instance object;
825         :param `pages`: the pages associated with the tabs;
826         :param `required_bmp_size`: the size of the bitmap on the tabs.
827         """
828
829         dc = wx.ClientDC(wnd)
830         dc.SetFont(self._measuring_font)
831
832         # sometimes a standard bitmap size needs to be enforced, especially
833         # if some tabs have bitmaps and others don't.  This is important because
834         # it prevents the tab control from resizing when tabs are added.
835
836         measure_bmp = wx.NullBitmap
837         
838         if required_bmp_size.IsFullySpecified():
839             measure_bmp = wx.EmptyBitmap(required_bmp_size.x,
840                                          required_bmp_size.y)
841         
842         max_y = 0
843         
844         for page in pages:
845         
846             if measure_bmp.IsOk():
847                 bmp = measure_bmp
848             else:
849                 bmp = page.bitmap
850
851             # we don't use the caption text because we don't
852             # want tab heights to be different in the case
853             # of a very short piece of text on one tab and a very
854             # tall piece of text on another tab
855             s, x_ext = self.GetTabSize(dc, wnd, page.caption, bmp, True, AUI_BUTTON_STATE_HIDDEN, None)
856             max_y = max(max_y, s[1])
857
858             if page.control:
859                 controlW, controlH = page.control.GetSize()
860                 max_y = max(max_y, controlH+4)
861
862         return max_y + 2
863
864
865     def SetNormalFont(self, font):
866         """
867         Sets the normal font for drawing tab labels.
868
869         :param `font`: a `wx.Font` object.
870         """
871
872         self._normal_font = font
873
874
875     def SetSelectedFont(self, font):
876         """
877         Sets the selected tab font for drawing tab labels.
878
879         :param `font`: a `wx.Font` object.
880         """
881
882         self._selected_font = font
883
884
885     def SetMeasuringFont(self, font):
886         """
887         Sets the font for calculating text measurements.
888
889         :param `font`: a `wx.Font` object.
890         """
891
892         self._measuring_font = font
893
894
895     def GetNormalFont(self):
896         """ Returns the normal font for drawing tab labels. """
897
898         return self._normal_font
899
900
901     def GetSelectedFont(self):
902         """ Returns the selected tab font for drawing tab labels. """
903
904         return self._selected_font
905
906
907     def GetMeasuringFont(self):
908         """ Returns the font for calculating text measurements. """
909
910         return self._measuring_font
911     
912
913     def ShowDropDown(self, wnd, pages, active_idx):
914         """
915         Shows the drop-down window menu on the tab area.
916
917         :param `wnd`: a `wx.Window` derived window instance;
918         :param `pages`: the pages associated with the tabs;
919         :param `active_idx`: the active tab index.
920         """
921         
922         useImages = self.GetAGWFlags() & AUI_NB_USE_IMAGES_DROPDOWN
923         menuPopup = wx.Menu()
924
925         longest = 0
926         for i, page in enumerate(pages):
927         
928             caption = page.caption
929
930             # if there is no caption, make it a space.  This will prevent
931             # an assert in the menu code.
932             if caption == "":
933                 caption = " "
934
935             # Save longest caption width for calculating menu width with
936             width = wnd.GetTextExtent(caption)[0]
937             if width > longest:
938                 longest = width
939
940             if useImages:
941                 menuItem = wx.MenuItem(menuPopup, 1000+i, caption)
942                 if page.bitmap:
943                     menuItem.SetBitmap(page.bitmap)
944
945                 menuPopup.AppendItem(menuItem)
946                 
947             else:
948                 
949                 menuPopup.AppendCheckItem(1000+i, caption)
950                 
951             menuPopup.Enable(1000+i, page.enabled)
952
953         if active_idx != -1 and not useImages:
954         
955             menuPopup.Check(1000+active_idx, True)
956         
957         # find out the screen coordinate at the bottom of the tab ctrl
958         cli_rect = wnd.GetClientRect()
959
960         # Calculate the approximate size of the popupmenu for setting the
961         # position of the menu when its shown.
962         # Account for extra padding on left/right of text on mac menus
963         if wx.Platform in ['__WXMAC__', '__WXMSW__']:
964             longest += 32
965
966         # Bitmap/Checkmark width + padding
967         longest += 20
968
969         if self.GetAGWFlags() & AUI_NB_CLOSE_BUTTON:
970             longest += 16
971
972         pt = wx.Point(cli_rect.x + cli_rect.GetWidth() - longest,
973                      cli_rect.y + cli_rect.height)
974
975         cc = AuiCommandCapture()
976         wnd.PushEventHandler(cc)
977         wnd.PopupMenu(menuPopup, pt)
978         command = cc.GetCommandId()
979         wnd.PopEventHandler(True)
980
981         if command >= 1000:
982             return command - 1000
983
984         return -1
985
986
987 class AuiSimpleTabArt(object):
988     """ A simple-looking implementation of a tab art. """
989
990     def __init__(self):
991         """ Default class constructor. """
992
993         self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
994         self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
995         self._selected_font.SetWeight(wx.BOLD)
996         self._measuring_font = self._selected_font
997
998         self._agwFlags = 0
999         self._fixed_tab_width = 100
1000
1001         base_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
1002
1003         background_colour = base_colour
1004         normaltab_colour = base_colour
1005         selectedtab_colour = wx.WHITE
1006
1007         self._bkbrush = wx.Brush(background_colour)
1008         self._normal_bkbrush = wx.Brush(normaltab_colour)
1009         self._normal_bkpen = wx.Pen(normaltab_colour)
1010         self._selected_bkbrush = wx.Brush(selectedtab_colour)
1011         self._selected_bkpen = wx.Pen(selectedtab_colour)
1012
1013         self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
1014         self._disabled_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.Colour(128, 128, 128))
1015
1016         self._active_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.BLACK)
1017         self._disabled_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.Colour(128, 128, 128))
1018
1019         self._active_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.BLACK)
1020         self._disabled_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.Colour(128, 128, 128))
1021
1022         self._active_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.BLACK)
1023         self._disabled_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.Colour(128, 128, 128))
1024
1025
1026     def Clone(self):
1027         """ Clones the art object. """
1028
1029         art = type(self)()
1030         art.SetNormalFont(self.GetNormalFont())
1031         art.SetSelectedFont(self.GetSelectedFont())
1032         art.SetMeasuringFont(self.GetMeasuringFont())
1033
1034         art = CopyAttributes(art, self)
1035         return art
1036
1037
1038     def SetAGWFlags(self, agwFlags):
1039         """
1040         Sets the tab art flags.
1041
1042         :param `agwFlags`: a combination of the following values:
1043
1044          ==================================== ==================================
1045          Flag name                            Description
1046          ==================================== ==================================
1047          ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
1048          ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet.
1049          ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet.
1050          ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
1051          ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
1052          ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
1053          ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
1054          ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
1055          ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
1056          ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
1057          ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
1058          ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
1059          ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
1060          ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close L{AuiNotebook} tabs by mouse middle button click
1061          ``AUI_NB_SUB_NOTEBOOK``              This style is used by L{AuiManager} to create automatic AuiNotebooks
1062          ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
1063          ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
1064          ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
1065          ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
1066          ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
1067          ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
1068          ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
1069          ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
1070          ==================================== ==================================
1071         
1072         """
1073
1074         self._agwFlags = agwFlags
1075
1076
1077     def GetAGWFlags(self):
1078         """
1079         Returns the tab art flags.
1080
1081         :see: L{SetAGWFlags} for a list of possible return values.
1082         """
1083
1084         return self._agwFlags
1085     
1086
1087     def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
1088         """
1089         Sets the tab sizing information.
1090         
1091         :param `tab_ctrl_size`: the size of the tab control area;
1092         :param `tab_count`: the number of tabs;
1093         :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
1094          to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
1095         """
1096         
1097         self._fixed_tab_width = 100
1098         minTabWidth, maxTabWidth = minMaxTabWidth
1099
1100         tot_width = tab_ctrl_size.x - self.GetIndentSize() - 4
1101
1102         if self._agwFlags & AUI_NB_CLOSE_BUTTON:
1103             tot_width -= self._active_close_bmp.GetWidth()
1104         if self._agwFlags & AUI_NB_WINDOWLIST_BUTTON:
1105             tot_width -= self._active_windowlist_bmp.GetWidth()
1106
1107         if tab_count > 0:
1108             self._fixed_tab_width = tot_width/tab_count
1109         
1110         if self._fixed_tab_width < 100:
1111             self._fixed_tab_width = 100
1112
1113         if self._fixed_tab_width > tot_width/2:
1114             self._fixed_tab_width = tot_width/2
1115
1116         if self._fixed_tab_width > 220:
1117             self._fixed_tab_width = 220
1118
1119         if minTabWidth > -1:
1120             self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
1121         if maxTabWidth > -1:
1122             self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
1123
1124         self._tab_ctrl_height = tab_ctrl_size.y
1125         
1126
1127     def DrawBackground(self, dc, wnd, rect):
1128         """
1129         Draws the tab area background.
1130
1131         :param `dc`: a `wx.DC` device context;
1132         :param `wnd`: a `wx.Window` instance object;
1133         :param `rect`: the tab control rectangle.
1134         """
1135         
1136         # draw background
1137         dc.SetBrush(self._bkbrush)
1138         dc.SetPen(wx.TRANSPARENT_PEN)
1139         dc.DrawRectangle(-1, -1, rect.GetWidth()+2, rect.GetHeight()+2)
1140
1141         # draw base line
1142         dc.SetPen(wx.GREY_PEN)
1143         dc.DrawLine(0, rect.GetHeight()-1, rect.GetWidth(), rect.GetHeight()-1)
1144
1145
1146     def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
1147         """
1148         Draws a single tab.
1149
1150         :param `dc`: a `wx.DC` device context;
1151         :param `wnd`: a `wx.Window` instance object;
1152         :param `page`: the tab control page associated with the tab;
1153         :param `in_rect`: rectangle the tab should be confined to;
1154         :param `close_button_state`: the state of the close button on the tab;
1155         :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
1156         """
1157         
1158         # if the caption is empty, measure some temporary text
1159         caption = page.caption
1160         if caption == "":
1161             caption = "Xj"
1162
1163         agwFlags = self.GetAGWFlags()
1164         
1165         dc.SetFont(self._selected_font)
1166         selected_textx, selected_texty, dummy = dc.GetMultiLineTextExtent(caption)
1167
1168         dc.SetFont(self._normal_font)
1169         normal_textx, normal_texty, dummy = dc.GetMultiLineTextExtent(caption)
1170
1171         control = page.control
1172
1173         # figure out the size of the tab
1174         tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
1175                                              page.active, close_button_state, control)
1176
1177         tab_height = tab_size[1]
1178         tab_width = tab_size[0]
1179         tab_x = in_rect.x
1180         tab_y = in_rect.y + in_rect.height - tab_height
1181
1182         caption = page.caption
1183         # select pen, brush and font for the tab to be drawn
1184
1185         if page.active:
1186         
1187             dc.SetPen(self._selected_bkpen)
1188             dc.SetBrush(self._selected_bkbrush)
1189             dc.SetFont(self._selected_font)
1190             textx = selected_textx
1191             texty = selected_texty
1192         
1193         else:
1194         
1195             dc.SetPen(self._normal_bkpen)
1196             dc.SetBrush(self._normal_bkbrush)
1197             dc.SetFont(self._normal_font)
1198             textx = normal_textx
1199             texty = normal_texty
1200
1201         if not page.enabled:
1202             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
1203         else:
1204             dc.SetTextForeground(page.text_colour)
1205         
1206         # -- draw line --
1207
1208         points = [wx.Point() for i in xrange(7)]
1209         points[0].x = tab_x
1210         points[0].y = tab_y + tab_height - 1
1211         points[1].x = tab_x + tab_height - 3
1212         points[1].y = tab_y + 2
1213         points[2].x = tab_x + tab_height + 3
1214         points[2].y = tab_y
1215         points[3].x = tab_x + tab_width - 2
1216         points[3].y = tab_y
1217         points[4].x = tab_x + tab_width
1218         points[4].y = tab_y + 2
1219         points[5].x = tab_x + tab_width
1220         points[5].y = tab_y + tab_height - 1
1221         points[6] = points[0]
1222
1223         dc.SetClippingRect(in_rect)
1224         dc.DrawPolygon(points)
1225
1226         dc.SetPen(wx.GREY_PEN)
1227         dc.DrawLines(points)
1228
1229         close_button_width = 0
1230         
1231         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
1232         
1233             close_button_width = self._active_close_bmp.GetWidth()
1234             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1235                 if control:
1236                     text_offset = tab_x + (tab_height/2) + close_button_width - (textx/2) - 2
1237                 else:
1238                     text_offset = tab_x + (tab_height/2) + ((tab_width+close_button_width)/2) - (textx/2) - 2
1239             else:
1240                 if control:
1241                     text_offset = tab_x + (tab_height/2) + close_button_width - (textx/2)
1242                 else:
1243                     text_offset = tab_x + (tab_height/2) + ((tab_width-close_button_width)/2) - (textx/2)
1244         
1245         else:
1246         
1247             text_offset = tab_x + (tab_height/3) + (tab_width/2) - (textx/2)
1248             if control:
1249                 if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1250                     text_offset = tab_x + (tab_height/3) - (textx/2) + close_button_width + 2
1251                 else:
1252                     text_offset = tab_x + (tab_height/3) - (textx/2)
1253         
1254         # set minimum text offset
1255         if text_offset < tab_x + tab_height:
1256             text_offset = tab_x + tab_height
1257
1258         # chop text if necessary
1259         if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1260             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x))
1261         else:
1262             draw_text = ChopText(dc, caption,
1263                                  tab_width - (text_offset-tab_x) - close_button_width)
1264
1265         ypos = (tab_y + tab_height)/2 - (texty/2) + 1
1266
1267         if control is not None:
1268             if control.GetPosition() != wx.Point(text_offset+1, ypos):
1269                 control.SetPosition(wx.Point(text_offset+1, ypos))
1270
1271             if not control.IsShown():
1272                 control.Show()
1273
1274             if paint_control:
1275                 bmp = TakeScreenShot(control.GetScreenRect())
1276                 dc.DrawBitmap(bmp, text_offset+1, ypos, True)
1277                 
1278             controlW, controlH = control.GetSize()
1279             text_offset += controlW + 4
1280
1281         # draw tab text
1282         rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
1283         dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
1284
1285         # draw focus rectangle
1286         if page.active and wx.Window.FindFocus() == wnd and (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
1287         
1288             focusRect = wx.Rect(text_offset, ((tab_y + tab_height)/2 - (texty/2) + 1),
1289                                 selected_textx, selected_texty)
1290
1291             focusRect.Inflate(2, 2)
1292             # TODO:
1293             # This should be uncommented when DrawFocusRect will become
1294             # available in wxPython
1295             # wx.RendererNative.Get().DrawFocusRect(wnd, dc, focusRect, 0)
1296
1297         out_button_rect = wx.Rect()        
1298         # draw close button if necessary
1299         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
1300         
1301             if page.active:
1302                 bmp = self._active_close_bmp
1303             else:
1304                 bmp = self._disabled_close_bmp
1305
1306             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1307                 rect = wx.Rect(tab_x + tab_height - 2,
1308                                tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
1309                                close_button_width, tab_height - 1)
1310             else:                
1311                 rect = wx.Rect(tab_x + tab_width - close_button_width - 1,
1312                                tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
1313                                close_button_width, tab_height - 1)
1314             
1315             self.DrawButtons(dc, rect, bmp, wx.WHITE, close_button_state)
1316             out_button_rect = wx.Rect(*rect)
1317         
1318         out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
1319         dc.DestroyClippingRegion()
1320
1321         return out_tab_rect, out_button_rect, x_extent  
1322
1323
1324     def DrawButtons(self, dc, _rect, bmp, bkcolour, button_state):
1325         """
1326         Convenience method to draw tab buttons.
1327
1328         :param `dc`: a `wx.DC` device context;
1329         :param `_rect`: the tab rectangle;
1330         :param `bmp`: the tab bitmap;
1331         :param `bkcolour`: the tab background colour;
1332         :param `button_state`: the state of the tab button.
1333         """
1334
1335         rect = wx.Rect(*_rect)
1336
1337         if button_state == AUI_BUTTON_STATE_PRESSED:
1338             rect.x += 1
1339             rect.y += 1
1340
1341         if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]:
1342             dc.SetBrush(wx.Brush(StepColour(bkcolour, 120)))
1343             dc.SetPen(wx.Pen(StepColour(bkcolour, 75)))
1344
1345             # draw the background behind the button
1346             dc.DrawRectangle(rect.x, rect.y, 15, 15)
1347
1348         # draw the button itself
1349         dc.DrawBitmap(bmp, rect.x, rect.y, True)
1350
1351     
1352     def GetIndentSize(self):
1353         """ Returns the tabs indent size. """
1354         
1355         return 0
1356
1357
1358     def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
1359         """
1360         Returns the tab size for the given caption, bitmap and button state.
1361
1362         :param `dc`: a `wx.DC` device context;
1363         :param `wnd`: a `wx.Window` instance object;
1364         :param `caption`: the tab text caption;
1365         :param `bitmap`: the bitmap displayed on the tab;
1366         :param `active`: whether the tab is selected or not;
1367         :param `close_button_state`: the state of the close button on the tab;
1368         :param `control`: a `wx.Window` instance inside a tab (or ``None``).
1369         """
1370         
1371         dc.SetFont(self._measuring_font)
1372         measured_textx, measured_texty, dummy = dc.GetMultiLineTextExtent(caption)
1373
1374         tab_height = measured_texty + 4
1375         tab_width = measured_textx + tab_height + 5
1376
1377         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
1378             tab_width += self._active_close_bmp.GetWidth()
1379
1380         if self._agwFlags & AUI_NB_TAB_FIXED_WIDTH:
1381             tab_width = self._fixed_tab_width
1382
1383         if control is not None:
1384             controlW, controlH = control.GetSize()
1385             tab_width += controlW + 4
1386
1387         x_extent = tab_width - (tab_height/2) - 1
1388
1389         return (tab_width, tab_height), x_extent
1390
1391
1392     def DrawButton(self, dc, wnd, in_rect, button, orientation):
1393         """
1394         Draws a button on the tab or on the tab area, depending on the button identifier. 
1395
1396         :param `dc`: a `wx.DC` device context;
1397         :param `wnd`: a `wx.Window` instance object;
1398         :param `in_rect`: rectangle the tab should be confined to;
1399         :param `button`: an instance of the button class;
1400         :param `orientation`: the tab orientation.
1401         """
1402
1403         bitmap_id, button_state = button.id, button.cur_state
1404         
1405         if bitmap_id == AUI_BUTTON_CLOSE:
1406             if button_state & AUI_BUTTON_STATE_DISABLED:
1407                 bmp = self._disabled_close_bmp
1408             else:
1409                 bmp = self._active_close_bmp
1410
1411         elif bitmap_id == AUI_BUTTON_LEFT:
1412             if button_state & AUI_BUTTON_STATE_DISABLED:
1413                 bmp = self._disabled_left_bmp
1414             else:
1415                 bmp = self._active_left_bmp
1416
1417         elif bitmap_id == AUI_BUTTON_RIGHT:
1418             if button_state & AUI_BUTTON_STATE_DISABLED:
1419                 bmp = self._disabled_right_bmp
1420             else:
1421                 bmp = self._active_right_bmp
1422
1423         elif bitmap_id == AUI_BUTTON_WINDOWLIST:
1424             if button_state & AUI_BUTTON_STATE_DISABLED:
1425                 bmp = self._disabled_windowlist_bmp
1426             else:
1427                 bmp = self._active_windowlist_bmp
1428
1429         else:
1430             if button_state & AUI_BUTTON_STATE_DISABLED:
1431                 bmp = button.dis_bitmap
1432             else:
1433                 bmp = button.bitmap
1434             
1435         if not bmp.IsOk():
1436             return
1437
1438         rect = wx.Rect(*in_rect)
1439
1440         if orientation == wx.LEFT:
1441         
1442             rect.SetX(in_rect.x)
1443             rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2))
1444             rect.SetWidth(bmp.GetWidth())
1445             rect.SetHeight(bmp.GetHeight())
1446         
1447         else:
1448         
1449             rect = wx.Rect(in_rect.x + in_rect.width - bmp.GetWidth(),
1450                            ((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
1451                            bmp.GetWidth(), bmp.GetHeight())
1452
1453         self.DrawButtons(dc, rect, bmp, wx.WHITE, button_state)
1454
1455         out_rect = wx.Rect(*rect)
1456         return out_rect
1457
1458
1459     def ShowDropDown(self, wnd, pages, active_idx):
1460         """
1461         Shows the drop-down window menu on the tab area.
1462
1463         :param `wnd`: a `wx.Window` derived window instance;
1464         :param `pages`: the pages associated with the tabs;
1465         :param `active_idx`: the active tab index.
1466         """
1467         
1468         menuPopup = wx.Menu()
1469         useImages = self.GetAGWFlags() & AUI_NB_USE_IMAGES_DROPDOWN
1470         
1471         for i, page in enumerate(pages):
1472
1473             if useImages:
1474                 menuItem = wx.MenuItem(menuPopup, 1000+i, page.caption)
1475                 if page.bitmap:
1476                     menuItem.SetBitmap(page.bitmap)
1477
1478                 menuPopup.AppendItem(menuItem)
1479                 
1480             else:
1481                 
1482                 menuPopup.AppendCheckItem(1000+i, page.caption)
1483                 
1484             menuPopup.Enable(1000+i, page.enabled)
1485         
1486         if active_idx != -1 and not useImages:
1487             menuPopup.Check(1000+active_idx, True)
1488         
1489         # find out where to put the popup menu of window
1490         # items.  Subtract 100 for now to center the menu
1491         # a bit, until a better mechanism can be implemented
1492         pt = wx.GetMousePosition()
1493         pt = wnd.ScreenToClient(pt)
1494         
1495         if pt.x < 100:
1496             pt.x = 0
1497         else:
1498             pt.x -= 100
1499
1500         # find out the screen coordinate at the bottom of the tab ctrl
1501         cli_rect = wnd.GetClientRect()
1502         pt.y = cli_rect.y + cli_rect.height
1503
1504         cc = AuiCommandCapture()
1505         wnd.PushEventHandler(cc)
1506         wnd.PopupMenu(menuPopup, pt)
1507         command = cc.GetCommandId()
1508         wnd.PopEventHandler(True)
1509
1510         if command >= 1000:
1511             return command-1000
1512
1513         return -1
1514
1515
1516     def GetBestTabCtrlSize(self, wnd, pages, required_bmp_size):
1517         """
1518         Returns the best tab control size.
1519
1520         :param `wnd`: a `wx.Window` instance object;
1521         :param `pages`: the pages associated with the tabs;
1522         :param `required_bmp_size`: the size of the bitmap on the tabs.
1523         """
1524         
1525         dc = wx.ClientDC(wnd)
1526         dc.SetFont(self._measuring_font)
1527         s, x_extent = self.GetTabSize(dc, wnd, "ABCDEFGHIj", wx.NullBitmap, True,
1528                                       AUI_BUTTON_STATE_HIDDEN, None)
1529
1530         max_y = s[1]
1531
1532         for page in pages:
1533             if page.control:
1534                 controlW, controlH = page.control.GetSize()
1535                 max_y = max(max_y, controlH+4)
1536                 
1537             textx, texty, dummy = dc.GetMultiLineTextExtent(page.caption)
1538             max_y = max(max_y, texty)
1539         
1540         return max_y + 3
1541
1542
1543     def SetNormalFont(self, font):
1544         """
1545         Sets the normal font for drawing tab labels.
1546
1547         :param `font`: a `wx.Font` object.
1548         """
1549         
1550         self._normal_font = font
1551
1552
1553     def SetSelectedFont(self, font):
1554         """
1555         Sets the selected tab font for drawing tab labels.
1556
1557         :param `font`: a `wx.Font` object.
1558         """
1559         
1560         self._selected_font = font
1561
1562
1563     def SetMeasuringFont(self, font):
1564         """
1565         Sets the font for calculating text measurements.
1566
1567         :param `font`: a `wx.Font` object.
1568         """
1569         
1570         self._measuring_font = font
1571
1572
1573     def GetNormalFont(self):
1574         """ Returns the normal font for drawing tab labels. """
1575
1576         return self._normal_font
1577
1578
1579     def GetSelectedFont(self):
1580         """ Returns the selected tab font for drawing tab labels. """
1581
1582         return self._selected_font
1583
1584
1585     def GetMeasuringFont(self):
1586         """ Returns the font for calculating text measurements. """
1587
1588         return self._measuring_font
1589
1590
1591     def SetCustomButton(self, bitmap_id, button_state, bmp):
1592         """
1593         Sets a custom bitmap for the close, left, right and window list
1594         buttons.
1595         
1596         :param `bitmap_id`: the button identifier;
1597         :param `button_state`: the button state;
1598         :param `bmp`: the custom bitmap to use for the button.
1599         """
1600         
1601         if bitmap_id == AUI_BUTTON_CLOSE:
1602             if button_state == AUI_BUTTON_STATE_NORMAL:
1603                 self._active_close_bmp = bmp
1604                 self._hover_close_bmp = self._active_close_bmp
1605                 self._pressed_close_bmp = self._active_close_bmp
1606                 self._disabled_close_bmp = self._active_close_bmp
1607                     
1608             elif button_state == AUI_BUTTON_STATE_HOVER:
1609                 self._hover_close_bmp = bmp
1610             elif button_state == AUI_BUTTON_STATE_PRESSED:
1611                 self._pressed_close_bmp = bmp
1612             else:
1613                 self._disabled_close_bmp = bmp
1614
1615         elif bitmap_id == AUI_BUTTON_LEFT:
1616             if button_state & AUI_BUTTON_STATE_DISABLED:
1617                 self._disabled_left_bmp = bmp
1618             else:
1619                 self._active_left_bmp = bmp
1620
1621         elif bitmap_id == AUI_BUTTON_RIGHT:
1622             if button_state & AUI_BUTTON_STATE_DISABLED:
1623                 self._disabled_right_bmp = bmp
1624             else:
1625                 self._active_right_bmp = bmp
1626
1627         elif bitmap_id == AUI_BUTTON_WINDOWLIST:
1628             if button_state & AUI_BUTTON_STATE_DISABLED:
1629                 self._disabled_windowlist_bmp = bmp
1630             else:
1631                 self._active_windowlist_bmp = bmp
1632     
1633
1634 class VC71TabArt(AuiDefaultTabArt):
1635     """ A class to draw tabs using the Visual Studio 2003 (VC71) style. """
1636
1637     def __init__(self):
1638         """ Default class constructor. """
1639
1640         AuiDefaultTabArt.__init__(self)
1641
1642
1643     def Clone(self):
1644         """ Clones the art object. """
1645
1646         art = type(self)()
1647         art.SetNormalFont(self.GetNormalFont())
1648         art.SetSelectedFont(self.GetSelectedFont())
1649         art.SetMeasuringFont(self.GetMeasuringFont())
1650
1651         art = CopyAttributes(art, self)
1652         return art
1653
1654
1655     def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
1656         """
1657         Draws a single tab.
1658
1659         :param `dc`: a `wx.DC` device context;
1660         :param `wnd`: a `wx.Window` instance object;
1661         :param `page`: the tab control page associated with the tab;
1662         :param `in_rect`: rectangle the tab should be confined to;
1663         :param `close_button_state`: the state of the close button on the tab;
1664         :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
1665         """
1666         
1667         # Visual studio 7.1 style
1668         # This code is based on the renderer included in FlatNotebook
1669
1670         # figure out the size of the tab
1671
1672         control = page.control
1673         tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
1674                                              close_button_state, control)
1675
1676         tab_height = self._tab_ctrl_height - 3
1677         tab_width = tab_size[0]
1678         tab_x = in_rect.x
1679         tab_y = in_rect.y + in_rect.height - tab_height
1680         clip_width = tab_width
1681
1682         if tab_x + clip_width > in_rect.x + in_rect.width - 4:
1683             clip_width = (in_rect.x + in_rect.width) - tab_x - 4
1684             
1685         dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
1686         agwFlags = self.GetAGWFlags()
1687
1688         if agwFlags & AUI_NB_BOTTOM:
1689             tab_y -= 1
1690
1691         dc.SetPen((page.active and [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DHIGHLIGHT))] or \
1692                    [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW))])[0])
1693         dc.SetBrush((page.active and [wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))] or \
1694                      [wx.TRANSPARENT_BRUSH])[0])
1695
1696         if page.active:
1697
1698             tabH = tab_height - 2
1699             dc.DrawRectangle(tab_x, tab_y, tab_width, tabH)
1700
1701             rightLineY1 = (agwFlags & AUI_NB_BOTTOM and [vertical_border_padding - 2] or \
1702                            [vertical_border_padding - 1])[0]
1703             rightLineY2 = tabH + 3
1704             dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW)))
1705             dc.DrawLine(tab_x + tab_width - 1, rightLineY1 + 1, tab_x + tab_width - 1, rightLineY2)
1706             
1707             if agwFlags & AUI_NB_BOTTOM:
1708                 dc.DrawLine(tab_x + 1, rightLineY2 - 3 , tab_x + tab_width - 1, rightLineY2 - 3)
1709                 
1710             dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)))
1711             dc.DrawLine(tab_x + tab_width, rightLineY1, tab_x + tab_width, rightLineY2)
1712             
1713             if agwFlags & AUI_NB_BOTTOM:
1714                 dc.DrawLine(tab_x, rightLineY2 - 2, tab_x + tab_width, rightLineY2 - 2)
1715
1716         else:
1717         
1718             # We dont draw a rectangle for non selected tabs, but only
1719             # vertical line on the right
1720             blackLineY1 = (agwFlags & AUI_NB_BOTTOM and [vertical_border_padding + 2] or \
1721                            [vertical_border_padding + 1])[0]
1722             blackLineY2 = tab_height - 5
1723             dc.DrawLine(tab_x + tab_width, blackLineY1, tab_x + tab_width, blackLineY2)
1724         
1725         border_points = [0, 0]
1726         
1727         if agwFlags & AUI_NB_BOTTOM:
1728         
1729             border_points[0] = wx.Point(tab_x, tab_y)
1730             border_points[1] = wx.Point(tab_x, tab_y + tab_height - 6)
1731         
1732         else: # if (agwFlags & AUI_NB_TOP)
1733         
1734             border_points[0] = wx.Point(tab_x, tab_y + tab_height - 4)
1735             border_points[1] = wx.Point(tab_x, tab_y + 2)
1736
1737         drawn_tab_yoff = border_points[1].y
1738         drawn_tab_height = border_points[0].y - border_points[1].y
1739
1740         text_offset = tab_x + 8
1741         close_button_width = 0
1742
1743         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
1744             close_button_width = self._active_close_bmp.GetWidth()
1745             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1746                 text_offset += close_button_width - 5
1747
1748         if not page.enabled:
1749             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
1750             pagebitmap = page.dis_bitmap
1751         else:
1752             dc.SetTextForeground(page.text_colour)
1753             pagebitmap = page.bitmap
1754
1755         shift = 0
1756         if agwFlags & AUI_NB_BOTTOM:
1757             shift = (page.active and [1] or [2])[0]
1758             
1759         bitmap_offset = 0
1760         if pagebitmap.IsOk():
1761             bitmap_offset = tab_x + 8
1762             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
1763                 bitmap_offset += close_button_width - 5
1764
1765             # draw bitmap
1766             dc.DrawBitmap(pagebitmap, bitmap_offset,
1767                           drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
1768                           True)
1769
1770             text_offset = bitmap_offset + pagebitmap.GetWidth()
1771             text_offset += 3 # bitmap padding
1772         
1773         else:
1774             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
1775                 text_offset = tab_x + 8
1776         
1777         # if the caption is empty, measure some temporary text
1778         caption = page.caption
1779
1780         if caption == "":
1781             caption = "Xj"
1782
1783         if page.active:
1784             dc.SetFont(self._selected_font)
1785             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
1786         else:
1787             dc.SetFont(self._normal_font)
1788             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
1789
1790         draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
1791
1792         ypos = drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2) - 1 + shift
1793
1794         offset_focus = text_offset
1795         
1796         if control is not None:
1797             if control.GetPosition() != wx.Point(text_offset+1, ypos):
1798                 control.SetPosition(wx.Point(text_offset+1, ypos))
1799
1800             if not control.IsShown():
1801                 control.Show()
1802
1803             if paint_control:
1804                 bmp = TakeScreenShot(control.GetScreenRect())
1805                 dc.DrawBitmap(bmp, text_offset+1, ypos, True)
1806                 
1807             controlW, controlH = control.GetSize()
1808             text_offset += controlW + 4
1809             textx += controlW + 4
1810
1811         # draw tab text
1812         rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
1813         dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
1814
1815         out_button_rect = wx.Rect()
1816
1817         # draw focus rectangle
1818         if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
1819             self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
1820                                     drawn_tab_height+shift, rectx, recty)
1821                 
1822         # draw 'x' on tab (if enabled)
1823         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
1824             close_button_width = self._active_close_bmp.GetWidth()
1825
1826             bmp = self._disabled_close_bmp
1827
1828             if close_button_state == AUI_BUTTON_STATE_HOVER:
1829                 bmp = self._hover_close_bmp
1830             elif close_button_state == AUI_BUTTON_STATE_PRESSED:
1831                 bmp = self._pressed_close_bmp
1832
1833             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1834                 rect = wx.Rect(tab_x + 4,
1835                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
1836                                close_button_width, tab_height)
1837             else:
1838                 rect = wx.Rect(tab_x + tab_width - close_button_width - 3,
1839                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
1840                                close_button_width, tab_height)
1841
1842             # Indent the button if it is pressed down:
1843             rect = IndentPressedBitmap(rect, close_button_state)
1844             dc.DrawBitmap(bmp, rect.x, rect.y, True)
1845
1846             out_button_rect = rect        
1847
1848         out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
1849         dc.DestroyClippingRegion()
1850
1851         return out_tab_rect, out_button_rect, x_extent
1852
1853
1854 class FF2TabArt(AuiDefaultTabArt):
1855     """ A class to draw tabs using the Firefox 2 (FF2) style. """
1856
1857     def __init__(self):
1858         """ Default class constructor. """
1859
1860         AuiDefaultTabArt.__init__(self)
1861
1862
1863     def Clone(self):
1864         """ Clones the art object. """
1865
1866         art = type(self)()
1867         art.SetNormalFont(self.GetNormalFont())
1868         art.SetSelectedFont(self.GetSelectedFont())
1869         art.SetMeasuringFont(self.GetMeasuringFont())
1870
1871         art = CopyAttributes(art, self)
1872         return art
1873
1874
1875     def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control):
1876         """
1877         Returns the tab size for the given caption, bitmap and button state.
1878
1879         :param `dc`: a `wx.DC` device context;
1880         :param `wnd`: a `wx.Window` instance object;
1881         :param `caption`: the tab text caption;
1882         :param `bitmap`: the bitmap displayed on the tab;
1883         :param `active`: whether the tab is selected or not;
1884         :param `close_button_state`: the state of the close button on the tab;
1885         :param `control`: a `wx.Window` instance inside a tab (or ``None``).
1886         """
1887         
1888         tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
1889                                                          active, close_button_state, control)
1890
1891         tab_width, tab_height = tab_size        
1892
1893         # add some vertical padding
1894         tab_height += 2
1895         
1896         return (tab_width, tab_height), x_extent
1897
1898
1899     def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
1900         """
1901         Draws a single tab.
1902
1903         :param `dc`: a `wx.DC` device context;
1904         :param `wnd`: a `wx.Window` instance object;
1905         :param `page`: the tab control page associated with the tab;
1906         :param `in_rect`: rectangle the tab should be confined to;
1907         :param `close_button_state`: the state of the close button on the tab;
1908         :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
1909         """
1910         
1911         # Firefox 2 style
1912
1913         control = page.control
1914
1915         # figure out the size of the tab
1916         tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
1917                                              page.active, close_button_state, control)
1918
1919         tab_height = self._tab_ctrl_height - 2
1920         tab_width = tab_size[0]
1921         tab_x = in_rect.x
1922         tab_y = in_rect.y + in_rect.height - tab_height
1923
1924         clip_width = tab_width
1925         if tab_x + clip_width > in_rect.x + in_rect.width - 4:
1926             clip_width = (in_rect.x + in_rect.width) - tab_x - 4
1927             
1928         dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
1929
1930         tabPoints = [wx.Point() for i in xrange(7)]
1931         
1932         adjust = 0
1933         if not page.active:
1934             adjust = 1
1935
1936         agwFlags = self.GetAGWFlags()
1937         
1938         tabPoints[0].x = tab_x + 3
1939         tabPoints[0].y = (agwFlags & AUI_NB_BOTTOM and [3] or [tab_height - 2])[0]
1940
1941         tabPoints[1].x = tabPoints[0].x
1942         tabPoints[1].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - (vertical_border_padding + 2) - adjust] or \
1943                           [(vertical_border_padding + 2) + adjust])[0]
1944
1945         tabPoints[2].x = tabPoints[1].x+2
1946         tabPoints[2].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding - adjust] or \
1947                           [vertical_border_padding + adjust])[0]
1948
1949         tabPoints[3].x = tab_x + tab_width - 2
1950         tabPoints[3].y = tabPoints[2].y
1951
1952         tabPoints[4].x = tabPoints[3].x + 2
1953         tabPoints[4].y = tabPoints[1].y
1954
1955         tabPoints[5].x = tabPoints[4].x
1956         tabPoints[5].y = tabPoints[0].y
1957
1958         tabPoints[6].x = tabPoints[0].x
1959         tabPoints[6].y = tabPoints[0].y
1960
1961         rr = wx.RectPP(tabPoints[2], tabPoints[5])
1962         self.DrawTabBackground(dc, rr, page.active, (agwFlags & AUI_NB_BOTTOM) == 0)
1963
1964         dc.SetBrush(wx.TRANSPARENT_BRUSH)
1965         dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
1966
1967         # Draw the tab as rounded rectangle
1968         dc.DrawPolygon(tabPoints)
1969
1970         if page.active:
1971             dc.DrawLine(tabPoints[0].x + 1, tabPoints[0].y, tabPoints[5].x , tabPoints[0].y)
1972         
1973         drawn_tab_yoff = tabPoints[1].y
1974         drawn_tab_height = tabPoints[0].y - tabPoints[2].y
1975
1976         text_offset = tab_x + 8
1977         close_button_width = 0
1978         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
1979             close_button_width = self._active_close_bmp.GetWidth()
1980             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
1981                 text_offset += close_button_width - 4
1982
1983         if not page.enabled:
1984             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
1985             pagebitmap = page.dis_bitmap
1986         else:
1987             dc.SetTextForeground(page.text_colour)
1988             pagebitmap = page.bitmap
1989
1990         shift = -1
1991         if agwFlags & AUI_NB_BOTTOM:
1992             shift = 2
1993         
1994         bitmap_offset = 0
1995         if pagebitmap.IsOk():
1996             bitmap_offset = tab_x + 8
1997             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
1998                 bitmap_offset += close_button_width - 4
1999
2000             # draw bitmap
2001             dc.DrawBitmap(pagebitmap, bitmap_offset,
2002                           drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
2003                           True)
2004
2005             text_offset = bitmap_offset + pagebitmap.GetWidth()
2006             text_offset += 3 # bitmap padding
2007         
2008         else:
2009         
2010             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
2011                 text_offset = tab_x + 8
2012         
2013         # if the caption is empty, measure some temporary text
2014         caption = page.caption
2015         if caption == "":
2016             caption = "Xj"
2017
2018         if page.active:
2019             dc.SetFont(self._selected_font)
2020             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
2021         else:
2022             dc.SetFont(self._normal_font)
2023             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
2024
2025         if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2026             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width + 1)
2027         else:
2028             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
2029
2030         ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1 + shift
2031
2032         offset_focus = text_offset
2033         
2034         if control is not None:
2035             if control.GetPosition() != wx.Point(text_offset+1, ypos):
2036                 control.SetPosition(wx.Point(text_offset+1, ypos))
2037
2038             if not control.IsShown():
2039                 control.Show()
2040
2041             if paint_control:
2042                 bmp = TakeScreenShot(control.GetScreenRect())
2043                 dc.DrawBitmap(bmp, text_offset+1, ypos, True)
2044                 
2045             controlW, controlH = control.GetSize()
2046             text_offset += controlW + 4
2047             textx += controlW + 4
2048         
2049         # draw tab text
2050         rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
2051         dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
2052
2053         # draw focus rectangle
2054         if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
2055             self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
2056                                     drawn_tab_height, rectx, recty)
2057         
2058         out_button_rect = wx.Rect()
2059         # draw 'x' on tab (if enabled)
2060         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
2061         
2062             close_button_width = self._active_close_bmp.GetWidth()
2063             bmp = self._disabled_close_bmp
2064
2065             if close_button_state == AUI_BUTTON_STATE_HOVER:
2066                 bmp = self._hover_close_bmp
2067             elif close_button_state == AUI_BUTTON_STATE_PRESSED:
2068                 bmp = self._pressed_close_bmp
2069
2070             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2071                 rect = wx.Rect(tab_x + 5,
2072                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
2073                                close_button_width, tab_height)
2074             else:
2075                 rect = wx.Rect(tab_x + tab_width - close_button_width - 3,
2076                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
2077                                close_button_width, tab_height)
2078
2079             # Indent the button if it is pressed down:
2080             rect = IndentPressedBitmap(rect, close_button_state)
2081             dc.DrawBitmap(bmp, rect.x, rect.y, True)
2082             out_button_rect = rect
2083         
2084         out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
2085         dc.DestroyClippingRegion()
2086     
2087         return out_tab_rect, out_button_rect, x_extent
2088
2089
2090     def DrawTabBackground(self, dc, rect, focus, upperTabs):
2091         """
2092         Draws the tab background for the Firefox 2 style.
2093         This is more consistent with L{FlatNotebook} than before.
2094
2095         :param `dc`: a `wx.DC` device context;
2096         :param `rect`: rectangle the tab should be confined to;
2097         :param `focus`: whether the tab has focus or not;
2098         :param `upperTabs`: whether the style is ``AUI_NB_TOP`` or ``AUI_NB_BOTTOM``.
2099         """
2100
2101         # Define the rounded rectangle base on the given rect
2102         # we need an array of 9 points for it
2103         regPts = [wx.Point() for indx in xrange(9)]
2104
2105         if focus:
2106             if upperTabs:
2107                 leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*8)
2108                 rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*8)
2109             else:
2110                 leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*5)
2111                 rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*5)
2112         else:
2113             leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
2114             rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 2))
2115
2116         # Define the top region
2117         top = wx.RectPP(rect.GetTopLeft(), rightPt)
2118         bottom = wx.RectPP(leftPt, rect.GetBottomRight())
2119
2120         topStartColour = wx.WHITE
2121
2122         if not focus:
2123             topStartColour = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 50)
2124
2125         topEndColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
2126         bottomStartColour = topEndColour
2127         bottomEndColour = topEndColour
2128
2129         # Incase we use bottom tabs, switch the colours
2130         if upperTabs:
2131             if focus:
2132                 dc.GradientFillLinear(top, topStartColour, topEndColour, wx.SOUTH)
2133                 dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
2134             else:
2135                 dc.GradientFillLinear(top, topEndColour , topStartColour, wx.SOUTH)
2136                 dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
2137
2138         else:
2139             if focus:
2140                 dc.GradientFillLinear(bottom, topEndColour, bottomEndColour, wx.SOUTH)
2141                 dc.GradientFillLinear(top, topStartColour, topStartColour, wx.SOUTH)
2142             else:
2143                 dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
2144                 dc.GradientFillLinear(top, topEndColour, topStartColour, wx.SOUTH)
2145         
2146         dc.SetBrush(wx.TRANSPARENT_BRUSH)
2147
2148
2149 class VC8TabArt(AuiDefaultTabArt):
2150     """ A class to draw tabs using the Visual Studio 2005 (VC8) style. """
2151
2152     def __init__(self):
2153         """ Default class constructor. """
2154
2155         AuiDefaultTabArt.__init__(self)
2156
2157
2158     def Clone(self):
2159         """ Clones the art object. """
2160
2161         art = type(self)()
2162         art.SetNormalFont(self.GetNormalFont())
2163         art.SetSelectedFont(self.GetSelectedFont())
2164         art.SetMeasuringFont(self.GetMeasuringFont())
2165
2166         art = CopyAttributes(art, self)
2167         return art
2168
2169
2170     def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
2171         """
2172         Sets the tab sizing information.
2173         
2174         :param `tab_ctrl_size`: the size of the tab control area;
2175         :param `tab_count`: the number of tabs;
2176         :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
2177          to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
2178         """
2179         
2180         AuiDefaultTabArt.SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth)
2181
2182         minTabWidth, maxTabWidth = minMaxTabWidth
2183         if minTabWidth > -1:
2184             self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
2185         if maxTabWidth > -1:
2186             self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
2187         
2188         self._fixed_tab_width -= 5
2189
2190
2191     def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
2192         """
2193         Returns the tab size for the given caption, bitmap and button state.
2194
2195         :param `dc`: a `wx.DC` device context;
2196         :param `wnd`: a `wx.Window` instance object;
2197         :param `caption`: the tab text caption;
2198         :param `bitmap`: the bitmap displayed on the tab;
2199         :param `active`: whether the tab is selected or not;
2200         :param `close_button_state`: the state of the close button on the tab;
2201         :param `control`: a `wx.Window` instance inside a tab (or ``None``).
2202         """
2203         
2204         tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
2205                                                          active, close_button_state, control)
2206
2207         tab_width, tab_height = tab_size        
2208
2209         # add some padding
2210         tab_width += 10
2211         tab_height += 2
2212
2213         return (tab_width, tab_height), x_extent
2214
2215
2216     def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
2217         """
2218         Draws a single tab.
2219
2220         :param `dc`: a `wx.DC` device context;
2221         :param `wnd`: a `wx.Window` instance object;
2222         :param `page`: the tab control page associated with the tab;
2223         :param `in_rect`: rectangle the tab should be confined to;
2224         :param `close_button_state`: the state of the close button on the tab;
2225         :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
2226         """
2227         
2228         # Visual Studio 8 style
2229
2230         control = page.control
2231
2232         # figure out the size of the tab
2233         tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
2234                                              page.active, close_button_state, control)
2235
2236         tab_height = self._tab_ctrl_height - 1
2237         tab_width = tab_size[0]
2238         tab_x = in_rect.x
2239         tab_y = in_rect.y + in_rect.height - tab_height
2240
2241         clip_width = tab_width + 3
2242         if tab_x + clip_width > in_rect.x + in_rect.width - 4:
2243             clip_width = (in_rect.x + in_rect.width) - tab_x - 4
2244         
2245         tabPoints = [wx.Point() for i in xrange(8)]
2246
2247         # If we draw the first tab or the active tab, 
2248         # we draw a full tab, else we draw a truncated tab
2249         #
2250         #             X(2)                  X(3)
2251         #        X(1)                            X(4)
2252         #                                          
2253         #                                           X(5)
2254         #                                           
2255         # X(0),(7)                                  X(6)
2256         #
2257         #
2258
2259         adjust = 0
2260         if not page.active:
2261             adjust = 1
2262
2263         agwFlags = self.GetAGWFlags()
2264         tabPoints[0].x = (agwFlags & AUI_NB_BOTTOM and [tab_x] or [tab_x + adjust])[0]
2265         tabPoints[0].y = (agwFlags & AUI_NB_BOTTOM and [2] or [tab_height - 3])[0]
2266
2267         tabPoints[1].x = tabPoints[0].x + tab_height - vertical_border_padding - 3 - adjust
2268         tabPoints[1].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - (vertical_border_padding+2)] or \
2269                           [(vertical_border_padding+2)])[0]
2270
2271         tabPoints[2].x = tabPoints[1].x + 4
2272         tabPoints[2].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding] or \
2273                           [vertical_border_padding])[0]
2274
2275         tabPoints[3].x = tabPoints[2].x + tab_width - tab_height + vertical_border_padding
2276         tabPoints[3].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding] or \
2277                           [vertical_border_padding])[0]
2278
2279         tabPoints[4].x = tabPoints[3].x + 1
2280         tabPoints[4].y = (agwFlags & AUI_NB_BOTTOM and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0]
2281
2282         tabPoints[5].x = tabPoints[4].x + 1
2283         tabPoints[5].y = (agwFlags & AUI_NB_BOTTOM and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0]
2284
2285         tabPoints[6].x = tabPoints[2].x + tab_width - tab_height + 2 + vertical_border_padding
2286         tabPoints[6].y = tabPoints[0].y
2287
2288         tabPoints[7].x = tabPoints[0].x
2289         tabPoints[7].y = tabPoints[0].y
2290
2291         self.FillVC8GradientColour(dc, tabPoints, page.active)        
2292
2293         dc.SetBrush(wx.TRANSPARENT_BRUSH)
2294
2295         dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
2296         dc.DrawPolygon(tabPoints)
2297
2298         if page.active:
2299             # Delete the bottom line (or the upper one, incase we use wxBOTTOM) 
2300             dc.SetPen(wx.WHITE_PEN)
2301             dc.DrawLine(tabPoints[0].x, tabPoints[0].y, tabPoints[6].x, tabPoints[6].y)
2302
2303         dc.SetClippingRegion(tab_x, tab_y, clip_width + 2, tab_height - 3)            
2304
2305         drawn_tab_yoff = tabPoints[1].y
2306         drawn_tab_height = tabPoints[0].y - tabPoints[2].y
2307
2308         text_offset = tab_x + 20
2309         close_button_width = 0
2310         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
2311             close_button_width = self._active_close_bmp.GetWidth()
2312             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2313                 text_offset += close_button_width
2314
2315         if not page.enabled:
2316             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
2317             pagebitmap = page.dis_bitmap
2318         else:
2319             dc.SetTextForeground(page.text_colour)
2320             pagebitmap = page.bitmap
2321
2322         shift = 0
2323         if agwFlags & AUI_NB_BOTTOM:
2324             shift = (page.active and [1] or [2])[0]
2325         
2326         bitmap_offset = 0
2327         if pagebitmap.IsOk():
2328             bitmap_offset = tab_x + 20
2329             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
2330                 bitmap_offset += close_button_width
2331
2332             # draw bitmap
2333             dc.DrawBitmap(pagebitmap, bitmap_offset,
2334                           drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
2335                           True)
2336
2337             text_offset = bitmap_offset + pagebitmap.GetWidth()
2338             text_offset += 3 # bitmap padding
2339         
2340         else:
2341             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
2342                 text_offset = tab_x + tab_height
2343         
2344         # if the caption is empty, measure some temporary text
2345         caption = page.caption
2346         if caption == "":
2347             caption = "Xj"
2348
2349         if page.active:
2350             dc.SetFont(self._selected_font)
2351             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
2352         else:
2353             dc.SetFont(self._normal_font)
2354             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
2355
2356         if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2357             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x))
2358         else:
2359             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
2360
2361         ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1 + shift
2362
2363         offset_focus = text_offset
2364         
2365         if control is not None:
2366             if control.GetPosition() != wx.Point(text_offset+1, ypos):
2367                 control.SetPosition(wx.Point(text_offset+1, ypos))
2368
2369             if not control.IsShown():
2370                 control.Show()
2371
2372             if paint_control:
2373                 bmp = TakeScreenShot(control.GetScreenRect())
2374                 dc.DrawBitmap(bmp, text_offset+1, ypos, True)
2375                 
2376             controlW, controlH = control.GetSize()
2377             text_offset += controlW + 4
2378             textx += controlW + 4
2379
2380         # draw tab text
2381         rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
2382         dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
2383         
2384         # draw focus rectangle
2385         if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
2386             self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
2387                                     drawn_tab_height+shift, rectx, recty)
2388         
2389         out_button_rect = wx.Rect()
2390         # draw 'x' on tab (if enabled)
2391         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
2392         
2393             close_button_width = self._active_close_bmp.GetWidth()
2394             bmp = self._disabled_close_bmp
2395
2396             if close_button_state == AUI_BUTTON_STATE_HOVER:
2397                 bmp = self._hover_close_bmp
2398             elif close_button_state == AUI_BUTTON_STATE_PRESSED:
2399                 bmp = self._pressed_close_bmp
2400                 
2401             if page.active:
2402                 xpos = tab_x + tab_width - close_button_width + 3
2403             else:
2404                 xpos = tab_x + tab_width - close_button_width - 5
2405
2406             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2407                 rect = wx.Rect(tab_x + 20,
2408                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
2409                                close_button_width, tab_height)
2410             else:
2411                 rect = wx.Rect(xpos,
2412                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
2413                                close_button_width, tab_height)
2414
2415             # Indent the button if it is pressed down:
2416             rect = IndentPressedBitmap(rect, close_button_state)
2417             dc.DrawBitmap(bmp, rect.x, rect.y, True)
2418             out_button_rect = rect
2419         
2420         out_tab_rect = wx.Rect(tab_x, tab_y, x_extent, tab_height)
2421         dc.DestroyClippingRegion()
2422
2423         return out_tab_rect, out_button_rect, x_extent
2424         
2425
2426     def FillVC8GradientColour(self, dc, tabPoints, active):
2427         """
2428         Fills the tab with the Visual Studio 2005 gradient background.
2429
2430         :param `dc`: a `wx.DC` device context;
2431         :param `tabPoints`: a list of `wx.Point` objects describing the tab shape;
2432         :param `active`: whether the tab is selected or not.
2433         """
2434
2435         xList = [pt.x for pt in tabPoints]
2436         yList = [pt.y for pt in tabPoints]
2437         
2438         minx, maxx = min(xList), max(xList)
2439         miny, maxy = min(yList), max(yList)
2440
2441         rect = wx.Rect(minx, maxy, maxx-minx, miny-maxy+1)        
2442         region = wx.RegionFromPoints(tabPoints)
2443
2444         if self._buttonRect.width > 0:
2445             buttonRegion = wx.Region(*self._buttonRect)
2446             region.XorRegion(buttonRegion)
2447         
2448         dc.SetClippingRegionAsRegion(region)
2449
2450         if active:
2451             bottom_colour = top_colour = wx.WHITE
2452         else:
2453             bottom_colour = StepColour(self._base_colour, 90)
2454             top_colour = StepColour(self._base_colour, 170)
2455
2456         dc.GradientFillLinear(rect, top_colour, bottom_colour, wx.SOUTH)
2457         dc.DestroyClippingRegion()
2458         
2459
2460 class ChromeTabArt(AuiDefaultTabArt):
2461     """
2462     A class to draw tabs using the Google Chrome browser style.
2463     It uses custom bitmap to render the tabs, so that the look and feel is as close
2464     as possible to the Chrome style.
2465     """
2466
2467     def __init__(self):
2468         """ Default class constructor. """
2469
2470         AuiDefaultTabArt.__init__(self)
2471
2472         self.SetBitmaps(mirror=False)
2473         
2474         closeBmp = tab_close.GetBitmap()
2475         closeHBmp = tab_close_h.GetBitmap()
2476         closePBmp = tab_close_p.GetBitmap()
2477
2478         self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_NORMAL, closeBmp)
2479         self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_HOVER, closeHBmp)
2480         self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_PRESSED, closePBmp)
2481         
2482
2483     def SetAGWFlags(self, agwFlags):
2484         """
2485         Sets the tab art flags.
2486
2487         :param `agwFlags`: a combination of the following values:
2488
2489          ==================================== ==================================
2490          Flag name                            Description
2491          ==================================== ==================================
2492          ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
2493          ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet.
2494          ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet.
2495          ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
2496          ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
2497          ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
2498          ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
2499          ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
2500          ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
2501          ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
2502          ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
2503          ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
2504          ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
2505          ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close L{AuiNotebook} tabs by mouse middle button click
2506          ``AUI_NB_SUB_NOTEBOOK``              This style is used by L{AuiManager} to create automatic AuiNotebooks
2507          ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
2508          ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
2509          ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
2510          ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
2511          ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
2512          ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
2513          ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
2514          ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
2515          ==================================== ==================================
2516
2517         :note: Overridden from L{AuiDefaultTabArt}.
2518         """
2519
2520         if agwFlags & AUI_NB_TOP:
2521             self.SetBitmaps(mirror=False)
2522         elif agwFlags & AUI_NB_BOTTOM:
2523             self.SetBitmaps(mirror=True)
2524
2525         AuiDefaultTabArt.SetAGWFlags(self, agwFlags)            
2526
2527
2528     def SetBitmaps(self, mirror):
2529         """
2530         Assigns the tab custom bitmaps
2531
2532         :param `mirror`: whether to vertically mirror the bitmap or not.
2533         """
2534
2535         bmps = [tab_active_left.GetBitmap(), tab_active_center.GetBitmap(),
2536                 tab_active_right.GetBitmap(), tab_inactive_left.GetBitmap(),
2537                 tab_inactive_center.GetBitmap(), tab_inactive_right.GetBitmap()]
2538
2539         if mirror:
2540             for indx, bmp in enumerate(bmps):
2541                 img = bmp.ConvertToImage()
2542                 img = img.Mirror(horizontally=False)
2543                 bmps[indx] = img.ConvertToBitmap()
2544                 
2545         self._leftActiveBmp = bmps[0]
2546         self._centerActiveBmp = bmps[1]
2547         self._rightActiveBmp = bmps[2]
2548         self._leftInactiveBmp = bmps[3]
2549         self._centerInactiveBmp = bmps[4]
2550         self._rightInactiveBmp = bmps[5]
2551             
2552
2553     def Clone(self):
2554         """ Clones the art object. """
2555
2556         art = type(self)()
2557         art.SetNormalFont(self.GetNormalFont())
2558         art.SetSelectedFont(self.GetSelectedFont())
2559         art.SetMeasuringFont(self.GetMeasuringFont())
2560
2561         art = CopyAttributes(art, self)
2562         return art
2563
2564
2565     def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
2566         """
2567         Sets the tab sizing information.
2568         
2569         :param `tab_ctrl_size`: the size of the tab control area;
2570         :param `tab_count`: the number of tabs;
2571         :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
2572          to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
2573         """
2574         
2575         AuiDefaultTabArt.SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth)
2576
2577         minTabWidth, maxTabWidth = minMaxTabWidth
2578         if minTabWidth > -1:
2579             self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
2580         if maxTabWidth > -1:
2581             self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
2582
2583         self._fixed_tab_width -= 5
2584
2585
2586     def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
2587         """
2588         Returns the tab size for the given caption, bitmap and button state.
2589
2590         :param `dc`: a `wx.DC` device context;
2591         :param `wnd`: a `wx.Window` instance object;
2592         :param `caption`: the tab text caption;
2593         :param `bitmap`: the bitmap displayed on the tab;
2594         :param `active`: whether the tab is selected or not;
2595         :param `close_button_state`: the state of the close button on the tab;
2596         :param `control`: a `wx.Window` instance inside a tab (or ``None``).
2597         """
2598         
2599         tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
2600                                                          active, close_button_state, control)
2601
2602         tab_width, tab_height = tab_size        
2603
2604         # add some padding
2605         tab_width += self._leftActiveBmp.GetWidth()
2606         tab_height += 2
2607
2608         tab_height = max(tab_height, self._centerActiveBmp.GetHeight())        
2609
2610         return (tab_width, tab_height), x_extent
2611
2612
2613     def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
2614         """
2615         Draws a single tab.
2616
2617         :param `dc`: a `wx.DC` device context;
2618         :param `wnd`: a `wx.Window` instance object;
2619         :param `page`: the tab control page associated with the tab;
2620         :param `in_rect`: rectangle the tab should be confined to;
2621         :param `close_button_state`: the state of the close button on the tab;
2622         :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
2623         """
2624         
2625         # Chrome tab style
2626
2627         control = page.control
2628         # figure out the size of the tab
2629         tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
2630                                              close_button_state, control)
2631
2632         agwFlags = self.GetAGWFlags()
2633         
2634         tab_height = self._tab_ctrl_height - 1
2635         tab_width = tab_size[0]
2636         tab_x = in_rect.x
2637         tab_y = in_rect.y + in_rect.height - tab_height
2638         clip_width = tab_width
2639
2640         if tab_x + clip_width > in_rect.x + in_rect.width - 4:
2641             clip_width = (in_rect.x + in_rect.width) - tab_x - 4
2642             
2643         dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
2644         drawn_tab_yoff = 1
2645
2646         if page.active:
2647             left = self._leftActiveBmp
2648             center = self._centerActiveBmp
2649             right = self._rightActiveBmp
2650         else:
2651             left = self._leftInactiveBmp
2652             center = self._centerInactiveBmp
2653             right = self._rightInactiveBmp
2654
2655         dc.DrawBitmap(left, tab_x, tab_y)
2656         leftw = left.GetWidth()
2657         centerw = center.GetWidth()
2658         rightw = right.GetWidth()
2659
2660         available = tab_x + tab_width - rightw
2661         posx = tab_x + leftw
2662         
2663         while 1:
2664             if posx >= available:
2665                 break
2666             dc.DrawBitmap(center, posx, tab_y)
2667             posx += centerw
2668
2669         dc.DrawBitmap(right, posx, tab_y)
2670
2671         drawn_tab_height = center.GetHeight()
2672         text_offset = tab_x + leftw
2673         
2674         close_button_width = 0
2675         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
2676             close_button_width = self._active_close_bmp.GetWidth()
2677             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2678                 text_offset += close_button_width
2679
2680         if not page.enabled:
2681             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
2682             pagebitmap = page.dis_bitmap
2683         else:
2684             dc.SetTextForeground(page.text_colour)
2685             pagebitmap = page.bitmap
2686         
2687         bitmap_offset = 0
2688         if pagebitmap.IsOk():
2689             bitmap_offset = tab_x + leftw
2690             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
2691                 bitmap_offset += close_button_width
2692
2693             # draw bitmap
2694             dc.DrawBitmap(pagebitmap, bitmap_offset,
2695                           drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2),
2696                           True)
2697
2698             text_offset = bitmap_offset + pagebitmap.GetWidth()
2699             text_offset += 3 # bitmap padding
2700         
2701         else:
2702         
2703             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
2704                 text_offset = tab_x + leftw
2705         
2706         # if the caption is empty, measure some temporary text
2707         caption = page.caption
2708         if caption == "":
2709             caption = "Xj"
2710
2711         if page.active:
2712             dc.SetFont(self._selected_font)
2713             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
2714         else:
2715             dc.SetFont(self._normal_font)
2716             textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
2717
2718         if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2719             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - leftw)
2720         else:
2721             draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width - leftw)
2722
2723         ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1
2724
2725         if control is not None:
2726             if control.GetPosition() != wx.Point(text_offset+1, ypos):
2727                 control.SetPosition(wx.Point(text_offset+1, ypos))
2728
2729             if not control.IsShown():
2730                 control.Show()
2731
2732             if paint_control:
2733                 bmp = TakeScreenShot(control.GetScreenRect())
2734                 dc.DrawBitmap(bmp, text_offset+1, ypos, True)
2735                 
2736             controlW, controlH = control.GetSize()
2737             text_offset += controlW + 4
2738
2739         # draw tab text
2740         rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
2741         dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
2742                 
2743         out_button_rect = wx.Rect()
2744         # draw 'x' on tab (if enabled)
2745         if close_button_state != AUI_BUTTON_STATE_HIDDEN:
2746         
2747             close_button_width = self._active_close_bmp.GetWidth()
2748             bmp = self._disabled_close_bmp
2749
2750             if close_button_state == AUI_BUTTON_STATE_HOVER:
2751                 bmp = self._hover_close_bmp
2752             elif close_button_state == AUI_BUTTON_STATE_PRESSED:
2753                 bmp = self._pressed_close_bmp
2754
2755             if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
2756                 rect = wx.Rect(tab_x + leftw - 2,
2757                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + 1,
2758                                close_button_width, tab_height)
2759             else:
2760                 rect = wx.Rect(tab_x + tab_width - close_button_width - rightw + 2,
2761                                drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + 1,
2762                                close_button_width, tab_height)
2763
2764             if agwFlags & AUI_NB_BOTTOM:
2765                 rect.y -= 1
2766                 
2767             # Indent the button if it is pressed down:
2768             rect = IndentPressedBitmap(rect, close_button_state)
2769             dc.DrawBitmap(bmp, rect.x, rect.y, True)
2770             out_button_rect = rect
2771             
2772         out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
2773         dc.DestroyClippingRegion()
2774
2775         return out_tab_rect, out_button_rect, x_extent        
2776
2777