1# -*- coding: utf-8 -*-
2#----------------------------------------------------------------------------
3# Name:         art_aui.py
4# Purpose:
5#
6# Author:       Andrea Gavana <andrea.gavana@gmail.com>
7#
8# Created:
9# Version:
10# Date:
11# Licence:      wxWindows license
12# Tags:         phoenix-port, unittest, documented, py3-port
13#----------------------------------------------------------------------------
14"""
15`art_aui` is responsible for drawing all the components of the ribbon
16interface using an AUI-compatible appearance.
17
18
19Description
20===========
21
22This allows a ribbon bar to have a pluggable look-and-feel, while retaining the same
23underlying behaviour. As a single art provider is used for all ribbon components, a
24ribbon bar usually has a consistent (though unique) appearance.
25
26By default, a :class:`~wx.lib.agw.ribbon.bar.RibbonBar` uses an instance of a class called
27:class:`~wx.lib.agw.ribbon.art_default.RibbonDefaultArtProvider`,
28which resolves to :class:`~wx.lib.agw.ribbon.art_aui.RibbonAUIArtProvider`,
29:class:`~wx.lib.agw.ribbon.art_msw.RibbonMSWArtProvider`, or
30:class:`~wx.lib.agw.ribbon.art_osx.RibbonOSXArtProvider` - whichever is most appropriate
31to the current platform. These art providers are all
32slightly configurable with regard to colours and fonts, but for larger modifications,
33you can derive from one of these classes, or write a completely new art provider class.
34
35Call :meth:`RibbonBar.SetArtProvider() <lib.agw.ribbon.bar.RibbonBar.SetArtProvider>` to change the art provider being used.
36
37
38See Also
39========
40
41:class:`~wx.lib.agw.ribbon.bar.RibbonBar`
42"""
43
44import wx
45
46from math import cos
47from math import pi as M_PI
48
49from .art_msw import RibbonMSWArtProvider
50from .art_internal import RibbonHSLColour, RibbonShiftLuminance, RibbonInterpolateColour
51
52from . import bar as BAR, panel as PANEL
53
54from .art import *
55
56if wx.Platform == "__WXMAC__":
57    try:
58        import Carbon.Appearance
59    except ImportError:
60        CARBON = False
61    else:
62        CARBON = True
63
64
65def FontFromFont(original):
66
67    newFont = wx.Font(original.GetPointSize(), original.GetFamily(),
68                      original.GetStyle(), original.GetWeight(), original.GetUnderlined(),
69                      original.GetFaceName(), original.GetEncoding())
70
71    return newFont
72
73
74class RibbonAUIArtProvider(RibbonMSWArtProvider):
75
76    def __init__(self):
77
78        RibbonMSWArtProvider.__init__(self)
79
80        if wx.Platform == "__WXMAC__":
81            k = Carbon.Appearance.kThemeBrushToolbarBackground if CARBON else 52
82            if hasattr(wx, 'MacThemeColour'):
83                base_colour = wx.MacThemeColour(k)
84            else:
85                brush = wx.Brush(wx.BLACK)
86                brush.MacSetTheme(k)
87                base_colour = brush.GetColour()
88        else:
89
90            base_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
91
92        self.SetColourScheme(base_colour, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT),
93                             wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
94
95        self._tab_active_label_font = FontFromFont(self._tab_label_font)
96        self._tab_active_label_font.SetWeight(wx.FONTWEIGHT_BOLD)
97
98        self._page_border_left = 1
99        self._page_border_right = 1
100        self._page_border_top = 1
101        self._page_border_bottom = 2
102        self._tab_separation_size = 0
103
104        self._gallery_bitmap_padding_left_size = 3
105        self._gallery_bitmap_padding_right_size = 3
106        self._gallery_bitmap_padding_top_size = 3
107        self._gallery_bitmap_padding_bottom_size = 3
108
109
110    def Clone(self):
111        """
112        Create a new art provider which is a clone of this one.
113        """
114
115        copy = RibbonAUIArtProvider()
116        self.CloneTo(copy)
117
118        copy._tab_ctrl_background_colour = self._tab_ctrl_background_colour
119        copy._tab_ctrl_background_gradient_colour = self._tab_ctrl_background_gradient_colour
120        copy._panel_label_background_colour = self._panel_label_background_colour
121        copy._panel_label_background_gradient_colour = self._panel_label_background_gradient_colour
122        copy._panel_hover_label_background_colour = self._panel_hover_label_background_colour
123        copy._panel_hover_label_background_gradient_colour = self._panel_hover_label_background_gradient_colour
124
125        copy._background_brush = self._background_brush
126        copy._tab_active_top_background_brush = self._tab_active_top_background_brush
127        copy._tab_hover_background_brush = self._tab_hover_background_brush
128        copy._button_bar_hover_background_brush = self._button_bar_hover_background_brush
129        copy._button_bar_active_background_brush = self._button_bar_active_background_brush
130        copy._gallery_button_active_background_brush = self._gallery_button_active_background_brush
131        copy._gallery_button_hover_background_brush = self._gallery_button_hover_background_brush
132        copy._gallery_button_disabled_background_brush = self._gallery_button_disabled_background_brush
133
134        copy._toolbar_hover_borden_pen = self._toolbar_hover_borden_pen
135        copy._tool_hover_background_brush = self._tool_hover_background_brush
136        copy._tool_active_background_brush = self._tool_active_background_brush
137
138        return copy
139
140
141    def SetFont(self, id, font):
142        """
143        Set the value of a certain font setting to the value.
144
145        can be one of the font values of `RibbonArtSetting`.
146
147        :param `id`: the font id;
148        :param `font`: MISSING DESCRIPTION.
149
150        """
151
152        RibbonMSWArtProvider.SetFont(self, id, font)
153
154        if id == RIBBON_ART_TAB_LABEL_FONT:
155            self._tab_active_label_font = FontFromFont(self._tab_label_font)
156            self._tab_active_label_font.SetWeight(wx.FONTWEIGHT_BOLD)
157
158
159    def GetColour(self, id):
160        """
161        Get the value of a certain colour setting.
162
163        can be one of the colour values of `RibbonArtSetting`.
164
165        :param `id`: the colour id.
166
167        """
168
169        if id in [RIBBON_ART_PAGE_BACKGROUND_COLOUR, RIBBON_ART_PAGE_BACKGROUND_GRADIENT_COLOUR]:
170            return self._background_brush.GetColour()
171        elif id == RIBBON_ART_TAB_CTRL_BACKGROUND_COLOUR:
172            return self._tab_ctrl_background_colour
173        elif id == RIBBON_ART_TAB_CTRL_BACKGROUND_GRADIENT_COLOUR:
174            return self._tab_ctrl_background_gradient_colour
175        elif id in [RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_COLOUR, RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR]:
176            return self._tab_active_top_background_brush.GetColour()
177        elif id in [RIBBON_ART_TAB_HOVER_BACKGROUND_COLOUR, RIBBON_ART_TAB_HOVER_BACKGROUND_GRADIENT_COLOUR]:
178            return self._tab_hover_background_brush.GetColour()
179        elif id == RIBBON_ART_PANEL_LABEL_BACKGROUND_COLOUR:
180            return self._panel_label_background_colour
181        elif id == RIBBON_ART_PANEL_LABEL_BACKGROUND_GRADIENT_COLOUR:
182            return self._panel_label_background_gradient_colour
183        elif id == RIBBON_ART_PANEL_HOVER_LABEL_BACKGROUND_COLOUR:
184            return self._panel_hover_label_background_colour
185        elif id == RIBBON_ART_PANEL_HOVER_LABEL_BACKGROUND_GRADIENT_COLOUR:
186            return self._panel_hover_label_background_gradient_colour
187        elif id in [RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_COLOUR, RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_GRADIENT_COLOUR]:
188            return self._button_bar_hover_background_brush.GetColour()
189        elif id in [RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_COLOUR, RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_GRADIENT_COLOUR]:
190            return self._gallery_button_hover_background_brush.GetColour()
191        elif id in [RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_COLOUR, RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_GRADIENT_COLOUR]:
192            return self._gallery_button_active_background_brush.GetColour()
193        elif id in [RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_COLOUR, RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_GRADIENT_COLOUR]:
194            return self._gallery_button_disabled_background_brush.GetColour()
195        else:
196            return RibbonMSWArtProvider.GetColour(self, id)
197
198
199    def SetColour(self, id, colour):
200        """
201        Set the value of a certain colour setting to the value.
202
203        can be one of the colour values of `RibbonArtSetting`, though not all colour
204        settings will have an affect on every art provider.
205
206        :param `id`: the colour id;
207        :param `colour`: MISSING DESCRIPTION.
208
209        :see: :meth:`~RibbonAUIArtProvider.SetColourScheme`
210        """
211
212        if id in [RIBBON_ART_PAGE_BACKGROUND_COLOUR, RIBBON_ART_PAGE_BACKGROUND_GRADIENT_COLOUR]:
213            self._background_brush.SetColour(colour)
214        elif id == RIBBON_ART_TAB_CTRL_BACKGROUND_COLOUR:
215            self._tab_ctrl_background_colour = colour
216        elif id == RIBBON_ART_TAB_CTRL_BACKGROUND_GRADIENT_COLOUR:
217            self._tab_ctrl_background_gradient_colour = colour
218        elif id in [RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_COLOUR, RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR]:
219            self._tab_active_top_background_brush.SetColour(colour)
220        elif id in [RIBBON_ART_TAB_HOVER_BACKGROUND_COLOUR, RIBBON_ART_TAB_HOVER_BACKGROUND_GRADIENT_COLOUR]:
221            self._tab_hover_background_brush.SetColour(colour)
222        elif id == RIBBON_ART_PANEL_LABEL_BACKGROUND_COLOUR:
223            self._panel_label_background_colour = colour
224        elif id == RIBBON_ART_PANEL_LABEL_BACKGROUND_GRADIENT_COLOUR:
225            self._panel_label_background_gradient_colour = colour
226        elif id in [RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_COLOUR, RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_GRADIENT_COLOUR]:
227            self._button_bar_hover_background_brush.SetColour(colour)
228        elif id in [RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_COLOUR, RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_GRADIENT_COLOUR]:
229            self._gallery_button_hover_background_brush.SetColour(colour)
230        elif id in [RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_COLOUR, RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_GRADIENT_COLOUR]:
231            self._gallery_button_active_background_brush.SetColour(colour)
232        elif id in [RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_COLOUR, RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_GRADIENT_COLOUR]:
233            self._gallery_button_disabled_background_brush.SetColour(colour)
234        else:
235            RibbonMSWArtProvider.SetColour(self, id, colour)
236
237
238    def SetColourScheme(self, primary, secondary, tertiary):
239        """
240        Set all applicable colour settings from a few base colours.
241
242        Uses any or all of the three given colours to create a colour scheme, and then
243        sets all colour settings which are relevant to the art provider using that
244        scheme. Note that some art providers may not use the tertiary colour for
245        anything, and some may not use the secondary colour either.
246
247        :param `primary`: MISSING DESCRIPTION;
248        :param `secondary`: MISSING DESCRIPTION;
249        :param `tertiary`: MISSING DESCRIPTION.
250
251        :see: :meth:`~RibbonAUIArtProvider.SetColour`, :meth:`RibbonMSWArtProvider.GetColourScheme() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetColourScheme>`
252        """
253
254        primary_hsl = RibbonHSLColour(primary)
255        secondary_hsl = RibbonHSLColour(secondary)
256        tertiary_hsl = RibbonHSLColour(tertiary)
257
258        # Map primary & secondary luminance from [0, 1] to [0.15, 0.85]
259        primary_hsl.luminance = cos(primary_hsl.luminance * M_PI) * -0.35 + 0.5
260        secondary_hsl.luminance = cos(secondary_hsl.luminance * M_PI) * -0.35 + 0.5
261
262        # TODO: Remove next line once this provider stops piggybacking MSW
263        RibbonMSWArtProvider.SetColourScheme(self, primary, secondary, tertiary)
264
265        self._tab_ctrl_background_colour = RibbonShiftLuminance(primary_hsl, 0.9).ToRGB()
266        self._tab_ctrl_background_gradient_colour = RibbonShiftLuminance(primary_hsl, 1.7).ToRGB()
267        self._tab_border_pen = wx.Pen(RibbonShiftLuminance(primary_hsl, 0.75).ToRGB())
268        self._tab_label_colour = RibbonShiftLuminance(primary_hsl, 0.1).ToRGB()
269        self._tab_hover_background_top_colour =  primary_hsl.ToRGB()
270        self._tab_hover_background_top_gradient_colour = RibbonShiftLuminance(primary_hsl, 1.6).ToRGB()
271        self._tab_hover_background_brush = wx.Brush(self._tab_hover_background_top_colour)
272        self._tab_active_background_colour = self._tab_ctrl_background_gradient_colour
273        self._tab_active_background_gradient_colour = primary_hsl.ToRGB()
274        self._tab_active_top_background_brush = wx.Brush(self._tab_active_background_colour)
275        self._panel_label_colour = self._tab_label_colour
276        self._panel_minimised_label_colour = self._panel_label_colour
277        self._panel_hover_label_colour = tertiary_hsl.ToRGB()
278        self._page_border_pen = self._tab_border_pen
279        self._panel_border_pen = self._tab_border_pen
280        self._background_brush = wx.Brush(primary_hsl.ToRGB())
281        self._page_hover_background_colour = RibbonShiftLuminance(primary_hsl, 1.5).ToRGB()
282        self._page_hover_background_gradient_colour = RibbonShiftLuminance(primary_hsl, 0.9).ToRGB()
283        self._panel_label_background_colour = RibbonShiftLuminance(primary_hsl, 0.85).ToRGB()
284        self._panel_label_background_gradient_colour = RibbonShiftLuminance(primary_hsl, 0.97).ToRGB()
285        self._panel_hover_label_background_gradient_colour = secondary_hsl.ToRGB()
286        self._panel_hover_label_background_colour = secondary_hsl.Lighter(0.2).ToRGB()
287        self._button_bar_hover_border_pen = wx.Pen(secondary_hsl.ToRGB())
288        self._button_bar_hover_background_brush = wx.Brush(RibbonShiftLuminance(secondary_hsl, 1.7).ToRGB())
289        self._button_bar_active_background_brush = wx.Brush(RibbonShiftLuminance(secondary_hsl, 1.4).ToRGB())
290        self._button_bar_label_colour = self._tab_label_colour
291        self._gallery_border_pen = self._tab_border_pen
292        self._gallery_item_border_pen = self._button_bar_hover_border_pen
293        self._gallery_hover_background_brush = wx.Brush(RibbonShiftLuminance(primary_hsl, 1.2).ToRGB())
294        self._gallery_button_background_colour = self._page_hover_background_colour
295        self._gallery_button_background_gradient_colour = self._page_hover_background_gradient_colour
296        self._gallery_button_hover_background_brush = self._button_bar_hover_background_brush
297        self._gallery_button_active_background_brush = self._button_bar_active_background_brush
298        self._gallery_button_disabled_background_brush = wx.Brush(primary_hsl.Desaturated(0.15).ToRGB())
299        self.SetColour(RIBBON_ART_GALLERY_BUTTON_FACE_COLOUR, RibbonShiftLuminance(primary_hsl, 0.1).ToRGB())
300        self.SetColour(RIBBON_ART_GALLERY_BUTTON_DISABLED_FACE_COLOUR, wx.Colour(128, 128, 128))
301        self.SetColour(RIBBON_ART_GALLERY_BUTTON_ACTIVE_FACE_COLOUR, RibbonShiftLuminance(secondary_hsl, 0.1).ToRGB())
302        self.SetColour(RIBBON_ART_GALLERY_BUTTON_HOVER_FACE_COLOUR, RibbonShiftLuminance(secondary_hsl, 0.1).ToRGB())
303        self._toolbar_border_pen = self._tab_border_pen
304        self.SetColour(RIBBON_ART_TOOLBAR_FACE_COLOUR, RibbonShiftLuminance(primary_hsl, 0.1).ToRGB())
305        self._tool_background_colour = self._page_hover_background_colour
306        self._tool_background_gradient_colour = self._page_hover_background_gradient_colour
307        self._toolbar_hover_borden_pen = self._button_bar_hover_border_pen
308        self._tool_hover_background_brush = self._button_bar_hover_background_brush
309        self._tool_active_background_brush = self._button_bar_active_background_brush
310
311
312    def DrawTabCtrlBackground(self, dc, wnd, rect):
313        """
314        Draw the background of the tab region of a ribbon bar.
315
316        :param `dc`: The device context to draw onto;
317        :param `wnd`: The window which is being drawn onto;
318        :param `rect`: The rectangle within which to draw.
319
320        """
321
322        gradient_rect = wx.Rect(*rect)
323        gradient_rect.height -= 1
324        dc.GradientFillLinear(gradient_rect, self._tab_ctrl_background_colour, self._tab_ctrl_background_gradient_colour, wx.SOUTH)
325        dc.SetPen(self._tab_border_pen)
326        dc.DrawLine(rect.x, rect.GetBottom(), rect.GetRight()+1, rect.GetBottom())
327
328
329    def GetTabCtrlHeight(self, dc, wnd, pages):
330        """
331        Calculate the height (in pixels) of the tab region of a ribbon bar.
332
333        Note that as the tab region can contain scroll buttons, the height should be
334        greater than or equal to the minimum height for a tab scroll button.
335
336        :param `dc`: A device context to use when one is required for size calculations;
337        :param `wnd`: The window onto which the tabs will eventually be drawn;
338        :param `pages`: The tabs which will acquire the returned height.
339
340        """
341
342        text_height = 0
343        icon_height = 0
344
345        if len(pages) <= 1 and (self._flags & RIBBON_BAR_ALWAYS_SHOW_TABS) == 0:
346            # To preserve space, a single tab need not be displayed. We still need
347            # one pixel of border though.
348            return 1
349
350        if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS:
351            dc.SetFont(self._tab_active_label_font)
352            text_height = dc.GetTextExtent("ABCDEFXj")[1]
353
354        if self._flags & RIBBON_BAR_SHOW_PAGE_ICONS:
355            for info in pages:
356                if info.page.GetIcon().IsOk():
357                    icon_height = max(icon_height, info.page.GetIcon().GetHeight())
358
359        return max(text_height, icon_height) + 10
360
361
362    def DrawTab(self, dc, wnd, tab):
363        """
364        Draw a single tab in the tab region of a ribbon bar.
365
366        :param `dc`: The device context to draw onto;
367        :param `wnd`: The window which is being drawn onto (not the :class:`~wx.lib.agw.ribbon.page.RibbonPage`
368         associated with the tab being drawn);
369        :param `tab`: The rectangle within which to draw, and also the tab label,
370         icon, and state (active and/or hovered). The drawing rectangle will be
371         entirely within a rectangle on the same device context previously painted
372         with :meth:`~RibbonAUIArtProvider.DrawTabCtrlBackground`. The rectangle's width will be at least the
373         minimum value returned by :meth:`~RibbonAUIArtProvider.GetBarTabWidth`, and height will be the value
374         returned by :meth:`~RibbonAUIArtProvider.GetTabCtrlHeight`.
375
376        """
377
378        if tab.rect.height <= 1:
379            return
380
381        dc.SetFont(self._tab_label_font)
382        dc.SetPen(wx.TRANSPARENT_PEN)
383
384        if tab.active or tab.hovered:
385            if tab.active:
386                dc.SetFont(self._tab_active_label_font)
387                dc.SetBrush(self._background_brush)
388                dc.DrawRectangle(tab.rect.x, tab.rect.y + tab.rect.height - 1, tab.rect.width - 1, 1)
389
390            grad_rect = wx.Rect(*tab.rect)
391            grad_rect.height -= 4
392            grad_rect.width -= 1
393            grad_rect.height /= 2
394            grad_rect.y = grad_rect.y + tab.rect.height - grad_rect.height - 1
395            dc.SetBrush(self._tab_active_top_background_brush)
396            dc.DrawRectangle(tab.rect.x, tab.rect.y + 3, tab.rect.width - 1, grad_rect.y - tab.rect.y - 3)
397            dc.GradientFillLinear(grad_rect, self._tab_active_background_colour, self._tab_active_background_gradient_colour, wx.SOUTH)
398
399        else:
400
401            btm_rect = wx.Rect(*tab.rect)
402            btm_rect.height -= 4
403            btm_rect.width -= 1
404            btm_rect.height /= 2
405            btm_rect.y = btm_rect.y + tab.rect.height - btm_rect.height - 1
406            dc.SetBrush(self._tab_hover_background_brush)
407            dc.DrawRectangle(btm_rect.x, btm_rect.y, btm_rect.width, btm_rect.height)
408            grad_rect = wx.Rect(*tab.rect)
409            grad_rect.width -= 1
410            grad_rect.y += 3
411            grad_rect.height = btm_rect.y - grad_rect.y
412            dc.GradientFillLinear(grad_rect, self._tab_hover_background_top_colour, self._tab_hover_background_top_gradient_colour, wx.SOUTH)
413
414        border_points = [wx.Point() for i in range(5)]
415        border_points[0] = wx.Point(0, 3)
416        border_points[1] = wx.Point(1, 2)
417        border_points[2] = wx.Point(tab.rect.width - 3, 2)
418        border_points[3] = wx.Point(tab.rect.width - 1, 4)
419        border_points[4] = wx.Point(tab.rect.width - 1, tab.rect.height - 1)
420
421        dc.SetPen(self._tab_border_pen)
422        dc.DrawLines(border_points, tab.rect.x, tab.rect.y)
423
424        old_clip = dc.GetClippingRect()
425        is_first_tab = False
426
427        bar = tab.page.GetParent()
428        icon = wx.NullBitmap
429
430        if isinstance(bar, BAR.RibbonBar) and bar.GetPage(0) == tab.page:
431            is_first_tab = True
432
433        if self._flags & RIBBON_BAR_SHOW_PAGE_ICONS:
434            icon = tab.page.GetIcon()
435            if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS == 0:
436                if icon.IsOk():
437                    x = tab.rect.x + (tab.rect.width - icon.GetWidth()) / 2
438                    dc.DrawBitmap(icon, x, tab.rect.y + 1 + (tab.rect.height - 1 - icon.GetHeight()) / 2, True)
439
440        if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS:
441            label = tab.page.GetLabel()
442
443            if label.strip():
444                dc.SetTextForeground(self._tab_label_colour)
445                dc.SetBackgroundMode(wx.TRANSPARENT)
446
447                offset = 0
448
449                if icon.IsOk():
450                    offset += icon.GetWidth() + 2
451
452                text_width, text_height = dc.GetTextExtent(label)
453                x = (tab.rect.width - 2 - text_width - offset) / 2
454                if x > 8:
455                    x = 8
456                elif x < 1:
457                    x = 1
458
459                width = tab.rect.width - x - 2
460                x += tab.rect.x + offset
461                y = tab.rect.y + (tab.rect.height - text_height) / 2
462
463                if icon.IsOk():
464                    dc.DrawBitmap(icon, x - offset, tab.rect.y + (tab.rect.height - icon.GetHeight()) / 2, True)
465
466                dc.SetClippingRegion(x, tab.rect.y, width, tab.rect.height)
467                dc.DrawText(label, x, y)
468
469        # Draw the left hand edge of the tab only for the first tab (subsequent
470        # tabs use the right edge of the prior tab as their left edge). As this is
471        # outside the rectangle for the tab, only draw it if the leftmost part of
472        # the tab is within the clip rectangle (the clip region has to be cleared
473        # to draw outside the tab).
474        if is_first_tab and old_clip.x <= tab.rect.x and tab.rect.x < old_clip.x + old_clip.width:
475            dc.DestroyClippingRegion()
476            dc.DrawLine(tab.rect.x - 1, tab.rect.y + 4, tab.rect.x - 1, tab.rect.y + tab.rect.height - 1)
477
478
479    def GetBarTabWidth(self, dc, wnd, label, bitmap, ideal=None, small_begin_need_separator=None,
480                       small_must_have_separator=None, minimum=None):
481        """
482        Calculate the ideal and minimum width (in pixels) of a tab in a ribbon bar.
483
484        :param `dc`: A device context to use when one is required for size calculations;
485        :param `wnd`: The window onto which the tab will eventually be drawn;
486        :param `label`: The tab's label (or an empty string if it has none);
487        :param `bitmap`: The tab's icon (or :class:`NullBitmap` if it has none);
488        :param `ideal`: The ideal width (in pixels) of the tab;
489        :param `small_begin_need_separator`: A size less than the size, at which a tab
490         separator should begin to be drawn (i.e. drawn, but still fairly transparent);
491        :param `small_must_have_separator`: A size less than the size, at which a tab
492         separator must be drawn (i.e. drawn at full opacity);
493        :param `minimum`: A size less than the size, and greater than or equal to zero,
494         which is the minimum pixel width for the tab.
495
496        """
497
498        width = mini = 0
499
500        if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS and label.strip():
501            dc.SetFont(self._tab_active_label_font)
502            width += dc.GetTextExtent(label)[0]
503            mini += min(30, width) # enough for a few chars
504
505            if bitmap.IsOk():
506                # gap between label and bitmap
507                width += 4
508                mini += 2
509
510        if self._flags & RIBBON_BAR_SHOW_PAGE_ICONS and bitmap.IsOk():
511            width += bitmap.GetWidth()
512            mini += bitmap.GetWidth()
513
514        ideal = width + 16
515        small_begin_need_separator = mini
516        small_must_have_separator = mini
517        minimum = mini
518
519        return ideal, small_begin_need_separator, small_must_have_separator, minimum
520
521
522    def DrawTabSeparator(self, dc, wnd, rect, visibility):
523        """
524        Draw a separator between two tabs in a ribbon bar.
525
526        :param `dc`: The device context to draw onto;
527        :param `wnd`: The window which is being drawn onto;
528        :param `rect`: The rectangle within which to draw, which will be entirely
529         within a rectangle on the same device context previously painted with
530         :meth:`~RibbonAUIArtProvider.DrawTabCtrlBackground`;
531        :param `visibility`: The opacity with which to draw the separator. Values
532         are in the range [0, 1], with 0 being totally transparent, and 1 being totally
533         opaque.
534
535        """
536
537        # No explicit separators between tabs
538        pass
539
540
541    def DrawPageBackground(self, dc, wnd, rect):
542        """
543        Draw the background of a ribbon page.
544
545        :param `dc`: The device context to draw onto;
546        :param `wnd`: The window which is being drawn onto (which is commonly the
547         :class:`~wx.lib.agw.ribbon.page.RibbonPage` whose background is being drawn, but doesn't have to be);
548        :param `rect`: The rectangle within which to draw.
549
550        :see: :meth:`RibbonMSWArtProvider.GetPageBackgroundRedrawArea() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetPageBackgroundRedrawArea>`
551        """
552
553        dc.SetPen(wx.TRANSPARENT_PEN)
554        dc.SetBrush(self._background_brush)
555        dc.DrawRectangle(rect.x + 1, rect.y, rect.width - 2, rect.height - 1)
556
557        dc.SetPen(self._page_border_pen)
558        dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height)
559        dc.DrawLine(rect.GetRight(), rect.y, rect.GetRight(), rect.y +rect.height)
560        dc.DrawLine(rect.x, rect.GetBottom(), rect.GetRight()+1, rect.GetBottom())
561
562
563    def GetScrollButtonMinimumSize(self, dc, wnd, style):
564        """
565        Calculate the minimum size (in pixels) of a scroll button.
566
567        :param `dc`: A device context to use when one is required for size calculations;
568        :param `wnd`: The window onto which the scroll button will eventually be drawn;
569        :param `style`: A combination of flags from `RibbonScrollButtonStyle`, including
570         a direction, and a for flag (state flags may be given too, but should be ignored,
571         as a button should retain a constant size, regardless of its state).
572
573        """
574
575        return wx.Size(11, 11)
576
577
578    def DrawScrollButton(self, dc, wnd, rect, style):
579        """
580        Draw a ribbon-style scroll button.
581
582        :param `dc`: The device context to draw onto;
583        :param `wnd`: The window which is being drawn onto;
584        :param `rect`: The rectangle within which to draw. The size of this rectangle
585         will be at least the size returned by :meth:`~RibbonAUIArtProvider.GetScrollButtonMinimumSize` for a
586         scroll button with the same style. For tab scroll buttons, this rectangle
587         will be entirely within a rectangle on the same device context previously
588         painted with :meth:`~RibbonAUIArtProvider.DrawTabCtrlBackground`, but this is not guaranteed for other
589         types of button (for example, page scroll buttons will not be painted on
590         an area previously painted with :meth:`~RibbonAUIArtProvider.DrawPageBackground`);
591        :param `style`: A combination of flags from `RibbonScrollButtonStyle`,
592         including a direction, a for flag, and one or more states.
593
594        """
595
596        true_rect = wx.Rect(*rect)
597        arrow_points = [wx.Point() for i in range(3)]
598
599        if style & RIBBON_SCROLL_BTN_FOR_MASK == RIBBON_SCROLL_BTN_FOR_TABS:
600            true_rect.y += 2
601            true_rect.height -= 2
602            dc.SetPen(self._tab_border_pen)
603        else:
604            dc.SetPen(wx.TRANSPARENT_PEN)
605            dc.SetBrush(self._background_brush)
606            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
607            dc.SetPen(self._page_border_pen)
608
609        result = style & RIBBON_SCROLL_BTN_DIRECTION_MASK
610
611        if result == RIBBON_SCROLL_BTN_LEFT:
612            dc.DrawLine(true_rect.GetRight(), true_rect.y, true_rect.GetRight(), true_rect.y + true_rect.height)
613            arrow_points[0] = wx.Point(rect.width / 2 - 2, rect.height / 2)
614            arrow_points[1] = arrow_points[0] + wx.Point(5, -5)
615            arrow_points[2] = arrow_points[0] + wx.Point(5,  5)
616
617        elif result == RIBBON_SCROLL_BTN_RIGHT:
618            dc.DrawLine(true_rect.x, true_rect.y, true_rect.x, true_rect.y + true_rect.height)
619            arrow_points[0] = wx.Point(rect.width / 2 + 3, rect.height / 2)
620            arrow_points[1] = arrow_points[0] - wx.Point(5, -5)
621            arrow_points[2] = arrow_points[0] - wx.Point(5,  5)
622
623        elif result == RIBBON_SCROLL_BTN_DOWN:
624            dc.DrawLine(true_rect.x, true_rect.y, true_rect.x + true_rect.width, true_rect.y)
625            arrow_points[0] = wx.Point(rect.width / 2, rect.height / 2 + 3)
626            arrow_points[1] = arrow_points[0] - wx.Point( 5, 5)
627            arrow_points[2] = arrow_points[0] - wx.Point(-5, 5)
628
629        elif result == RIBBON_SCROLL_BTN_UP:
630            dc.DrawLine(true_rect.x, true_rect.GetBottom(), true_rect.x + true_rect.width, true_rect.GetBottom())
631            arrow_points[0] = wx.Point(rect.width / 2, rect.height / 2 - 2)
632            arrow_points[1] = arrow_points[0] + wx.Point( 5, 5)
633            arrow_points[2] = arrow_points[0] + wx.Point(-5, 5)
634
635        else:
636            return
637
638        x = rect.x
639        y = rect.y
640
641        if style & RIBBON_SCROLL_BTN_ACTIVE:
642            x += 1
643            y += 1
644
645        dc.SetPen(wx.TRANSPARENT_PEN)
646        B = wx.Brush(self._tab_label_colour)
647        dc.SetBrush(B)
648        dc.DrawPolygon(arrow_points, x, y)
649
650
651    def GetPanelSize(self, dc, wnd, client_size, client_offset=None):
652        """
653        Calculate the size of a panel for a given client size.
654
655        This should increment the given size by enough to fit the panel label and other
656        chrome.
657
658        :param `dc`: A device context to use if one is required for size calculations;
659        :param `wnd`: The ribbon panel in question;
660        :param `client_size`: The client size;
661        :param `client_offset`: The offset where the client rectangle begins within
662         the panel (may be ``None``).
663
664        :see: :meth:`~RibbonAUIArtProvider.GetPanelClientSize`
665        """
666
667        dc.SetFont(self._panel_label_font)
668        label_size = wx.Size(*dc.GetTextExtent(wnd.GetLabel()))
669        label_height = label_size.GetHeight() + 5
670
671        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
672            client_size.IncBy(4, label_height + 6)
673            if client_offset is not None:
674                client_offset = wx.Point(2, label_height + 3)
675
676        else:
677            client_size.IncBy(6, label_height + 4)
678            if client_offset is not None:
679                client_offset = wx.Point(3, label_height + 2)
680
681        return client_size
682
683
684    def GetPanelClientSize(self, dc, wnd, size, client_offset=None):
685        """
686        Calculate the client size of a panel for a given overall size.
687
688        This should act as the inverse to :meth:`~RibbonAUIArtProvider.GetPanelSize`, and decrement the given size
689        by enough to fit the panel label and other chrome.
690
691        :param `dc`: A device context to use if one is required for size calculations;
692        :param `wnd`: The ribbon panel in question;
693        :param `size`: The overall size to calculate client size for;
694        :param `client_offset`: The offset where the returned client size begins within
695         the given (may be ``None``).
696
697        :see: :meth:`~RibbonAUIArtProvider.GetPanelSize`
698        """
699
700        dc.SetFont(self._panel_label_font)
701        label_size = wx.Size(*dc.GetTextExtent(wnd.GetLabel()))
702        label_height = label_size.GetHeight() + 5
703
704        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
705            size.DecBy(4, label_height + 6)
706            if client_offset is not None:
707                client_offset = wx.Point(2, label_height + 3)
708
709        else:
710            size.DecBy(6, label_height + 4)
711            if client_offset is not None:
712                client_offset = wx.Point(3, label_height + 2)
713
714        if size.x < 0:
715            size.x = 0
716        if size.y < 0:
717            size.y = 0
718
719        return size, client_offset
720
721
722    def GetPanelExtButtonArea(self, dc, wnd, rect):
723        """
724        Retrieve the extension button area rectangle.
725
726        :param `dc`: The device context used to measure text extents;
727        :param `wnd`: The panel where the extension button resides;
728        :param `rect`: The panel client rectangle.
729        """
730
731        true_rect = wx.Rect(rect)
732        true_rect = self.RemovePanelPadding(true_rect)
733
734        true_rect.x += 1
735        true_rect.width -= 2
736        true_rect.y += 1
737
738        dc.SetFont(self._panel_label_font)
739        label_size = dc.GetTextExtent(wnd.GetLabel())
740        label_height = label_size[1] + 5
741        label_rect = wx.Rect(*true_rect)
742        label_rect.height = label_height - 1
743
744        rect = wx.Rect(label_rect.GetRight()-13, label_rect.GetBottom()-13, 13, 13)
745        return rect
746
747
748    def DrawPanelBackground(self, dc, wnd, rect):
749        """
750        Draw the background and chrome for a ribbon panel.
751
752        This should draw the border, background, label, and any other items of a panel
753        which are outside the client area of a panel. Note that when a panel is
754        minimised, this function is not called - only :meth:`~RibbonAUIArtProvider.DrawMinimisedPanel` is called,
755        so a background should be explicitly painted by that if required.
756
757        :param `dc`: The device context to draw onto;
758        :param `wnd`: The window which is being drawn onto, which is always the panel
759         whose background and chrome is being drawn. The panel label and other panel
760         attributes can be obtained by querying this;
761        :param `rect`: The rectangle within which to draw.
762
763        """
764
765        dc.SetPen(wx.TRANSPARENT_PEN)
766        dc.SetBrush(self._background_brush)
767        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
768
769        true_rect = wx.Rect(*rect)
770        true_rect = self.RemovePanelPadding(true_rect)
771
772        dc.SetPen(self._panel_border_pen)
773        dc.SetBrush(wx.TRANSPARENT_BRUSH)
774        dc.DrawRectangle(true_rect.x, true_rect.y, true_rect.width, true_rect.height)
775
776        true_rect.x += 1
777        true_rect.width -= 2
778        true_rect.y += 1
779
780        dc.SetFont(self._panel_label_font)
781        label_size = wx.Size(*dc.GetTextExtent(wnd.GetLabel()))
782        label_height = label_size.GetHeight() + 5
783        label_rect = wx.Rect(*true_rect)
784        label_rect.height = label_height - 1
785
786        dc.DrawLine(label_rect.x, label_rect.y + label_rect.height, label_rect.x + label_rect.width, label_rect.y + label_rect.height)
787
788        label_bg_colour = self._panel_label_background_colour
789        label_bg_grad_colour = self._panel_label_background_gradient_colour
790
791        if wnd.IsHovered():
792            label_bg_colour = self._panel_hover_label_background_colour
793            label_bg_grad_colour = self._panel_hover_label_background_gradient_colour
794            dc.SetTextForeground(self._panel_hover_label_colour)
795        else:
796            dc.SetTextForeground(self._panel_label_colour)
797
798        if wx.Platform == "__WXMAC__":
799            dc.GradientFillLinear(label_rect, label_bg_grad_colour, label_bg_colour, wx.SOUTH)
800        else:
801            dc.GradientFillLinear(label_rect, label_bg_colour, label_bg_grad_colour, wx.SOUTH)
802
803        dc.SetFont(self._panel_label_font)
804        dc.DrawText(wnd.GetLabel(), label_rect.x + 3, label_rect.y + 2)
805
806        if wnd.IsHovered():
807
808            gradient_rect = wx.Rect(*true_rect)
809            gradient_rect.y += label_rect.height + 1
810            gradient_rect.height = true_rect.height - label_rect.height - 3
811            if wx.Platform == "__WXMAC__":
812                colour = self._page_hover_background_gradient_colour
813                gradient = self._page_hover_background_colour
814            else:
815                colour = self._page_hover_background_colour
816                gradient = self._page_hover_background_gradient_colour
817
818            dc.GradientFillLinear(gradient_rect, colour, gradient, wx.SOUTH)
819
820        if wnd.HasExtButton():
821            if wnd.IsExtButtonHovered():
822                dc.SetPen(self._panel_hover_button_border_pen)
823                dc.SetBrush(self._panel_hover_button_background_brush)
824                dc.DrawRoundedRectangle(label_rect.GetRight() - 13, label_rect.GetBottom() - 13, 13, 13, 1)
825                dc.DrawBitmap(self._panel_extension_bitmap[1], label_rect.GetRight() - 10, label_rect.GetBottom() - 10, True)
826
827            else:
828                dc.DrawBitmap(self._panel_extension_bitmap[0], label_rect.GetRight() - 10, label_rect.GetBottom() - 10, True)
829
830
831    def DrawMinimisedPanel(self, dc, wnd, rect, bitmap):
832        """
833        Draw a minimised ribbon panel.
834
835        :param `dc`: The device context to draw onto;
836        :param `wnd`: The window which is being drawn onto, which is always the panel
837         which is minimised. The panel label can be obtained from this window. The
838         minimised icon obtained from querying the window may not be the size requested
839         by :meth:`RibbonMSWArtProvider.GetMinimisedPanelMinimumSize() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetMinimisedPanelMinimumSize>` - the argument contains the icon in the
840         requested size;
841        :param `rect`: The rectangle within which to draw. The size of the rectangle
842         will be at least the size returned by :meth:`RibbonMSWArtProvider.GetMinimisedPanelMinimumSize() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetMinimisedPanelMinimumSize>`;
843        :param `bitmap`: A copy of the panel's minimised bitmap rescaled to the size
844         returned by :meth:`RibbonMSWArtProvider.GetMinimisedPanelMinimumSize() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetMinimisedPanelMinimumSize>`.
845
846        """
847
848        dc.SetPen(wx.TRANSPARENT_PEN)
849        dc.SetBrush(self._background_brush)
850        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
851
852        true_rect = wx.Rect(*rect)
853        true_rect = self.RemovePanelPadding(true_rect)
854
855        dc.SetPen(self._panel_border_pen)
856        dc.SetBrush(wx.TRANSPARENT_BRUSH)
857        dc.DrawRectangle(true_rect.x, true_rect.y, true_rect.width, true_rect.height)
858        true_rect.Deflate(1, 1)
859
860        if wnd.IsHovered() or wnd.GetExpandedPanel():
861            colour = self._page_hover_background_colour
862            gradient = self._page_hover_background_gradient_colour
863            if (wx.Platform == "__WXMAC__" and not wnd.GetExpandedPanel()) or \
864               (wx.Platform != "__WXMAC__" and wnd.GetExpandedPanel()):
865                temp = colour
866                colour = gradient
867                gradient = temp
868
869            dc.GradientFillLinear(true_rect, colour, gradient, wx.SOUTH)
870
871        preview = self.DrawMinimisedPanelCommon(dc, wnd, true_rect)
872
873        dc.SetPen(self._panel_border_pen)
874        dc.SetBrush(wx.TRANSPARENT_BRUSH)
875        dc.DrawRectangle(preview.x, preview.y, preview.width, preview.height)
876        preview.Deflate(1, 1)
877
878        preview_caption_rect = wx.Rect(*preview)
879        preview_caption_rect.height = 7
880        preview.y += preview_caption_rect.height
881        preview.height -= preview_caption_rect.height
882
883        if wx.Platform == "__WXMAC__":
884            dc.GradientFillLinear(preview_caption_rect, self._panel_hover_label_background_gradient_colour,
885                                  self._panel_hover_label_background_colour, wx.SOUTH)
886            dc.GradientFillLinear(preview, self._page_hover_background_gradient_colour,
887                                  self._page_hover_background_colour, wx.SOUTH)
888        else:
889            dc.GradientFillLinear(preview_caption_rect, self._panel_hover_label_background_colour,
890                                  self._panel_hover_label_background_gradient_colour, wx.SOUTH)
891            dc.GradientFillLinear(preview, self._page_hover_background_colour,
892                                  self._page_hover_background_gradient_colour, wx.SOUTH)
893
894        if bitmap.IsOk():
895            dc.DrawBitmap(bitmap, preview.x + (preview.width - bitmap.GetWidth()) / 2,
896                          preview.y + (preview.height - bitmap.GetHeight()) / 2, True)
897
898
899    def DrawPartialPanelBackground(self, dc, wnd, rect):
900
901        dc.SetPen(wx.TRANSPARENT_PEN)
902        dc.SetBrush(self._background_brush)
903        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
904
905        offset = wx.Point(*wnd.GetPosition())
906        parent = wnd.GetParent()
907        panel = None
908
909        while 1:
910            panel = parent
911            if isinstance(panel, PANEL.RibbonPanel):
912                if not panel.IsHovered():
913                    return
914                break
915
916            offset += parent.GetPosition()
917            parent = panel.GetParent()
918
919        if panel is None:
920            return
921
922        background = wx.Rect(0, 0, *panel.GetSize())
923        background = self.RemovePanelPadding(background)
924
925        background.x += 1
926        background.width -= 2
927        dc.SetFont(self._panel_label_font)
928        caption_height = dc.GetTextExtent(panel.GetLabel())[1] + 7
929        background.y += caption_height - 1
930        background.height -= caption_height
931
932        paint_rect = wx.Rect(*rect)
933        paint_rect.x += offset.x
934        paint_rect.y += offset.y
935
936        if wx.Platform == "__WXMAC__":
937            bg_grad_clr = self._page_hover_background_colour
938            bg_clr = self._page_hover_background_gradient_colour
939        else:
940            bg_clr = self._page_hover_background_colour
941            bg_grad_clr = self._page_hover_background_gradient_colour
942
943        paint_rect.Intersect(background)
944
945        if not paint_rect.IsEmpty():
946            starting_colour = RibbonInterpolateColour(bg_clr, bg_grad_clr, paint_rect.y, background.y, background.y + background.height)
947            ending_colour = RibbonInterpolateColour(bg_clr, bg_grad_clr, paint_rect.y + paint_rect.height, background.y, background.y + background.height)
948            paint_rect.x -= offset.x
949            paint_rect.y -= offset.y
950            dc.GradientFillLinear(paint_rect, starting_colour, ending_colour, wx.SOUTH)
951
952
953    def DrawGalleryBackground(self, dc, wnd, rect):
954        """
955        Draw the background and chrome for a :class:`~wx.lib.agw.ribbon.gallery.RibbonGallery` control.
956
957        This should draw the border, brackground, scroll buttons, extension button, and
958        any other UI elements which are not attached to a specific gallery item.
959
960        :param `dc`: The device context to draw onto;
961        :param `wnd`: The window which is being drawn onto, which is always the gallery
962         whose background and chrome is being drawn. Attributes used during drawing like
963         the gallery hover state and individual button states can be queried from this
964         parameter by :meth:`RibbonGallery.IsHovered() <lib.agw.ribbon.gallery.RibbonGallery.IsHovered>`,
965         :meth:`RibbonGallery.GetExtensionButtonState() <lib.agw.ribbon.gallery.RibbonGallery.GetExtensionButtonState>`,
966         :meth:`RibbonGallery.GetUpButtonState() <lib.agw.ribbon.gallery.RibbonGallery.GetUpButtonState>`, and
967         :meth:`RibbonGallery.GetDownButtonState() <lib.agw.ribbon.gallery.RibbonGallery.GetDownButtonState>`;
968        :param `rect`: The rectangle within which to draw. This rectangle is the entire
969         area of the gallery control, not just the client rectangle.
970
971        """
972
973        self.DrawPartialPanelBackground(dc, wnd, rect)
974
975        if wnd.IsHovered():
976            dc.SetPen(wx.TRANSPARENT_PEN)
977            dc.SetBrush(self._gallery_hover_background_brush)
978
979            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
980                dc.DrawRectangle(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 16)
981            else:
982                dc.DrawRectangle(rect.x + 1, rect.y + 1, rect.width - 16, rect.height - 2)
983
984        dc.SetPen(self._gallery_border_pen)
985        dc.SetBrush(wx.TRANSPARENT_BRUSH)
986        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
987
988        self.DrawGalleryBackgroundCommon(dc, wnd, rect)
989
990
991    def DrawGalleryButton(self, dc, rect, state, bitmaps):
992
993        extra_height = 0
994        extra_width = 0
995        reduced_rect = wx.Rect(*rect)
996        reduced_rect.Deflate(1, 1)
997
998        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
999            reduced_rect.width += 1
1000            extra_width = 1
1001        else:
1002            reduced_rect.height += 1
1003            extra_height = 1
1004
1005        if state == RIBBON_GALLERY_BUTTON_NORMAL:
1006            dc.GradientFillLinear(reduced_rect, self._gallery_button_background_colour, self._gallery_button_background_gradient_colour, wx.SOUTH)
1007            btn_bitmap = bitmaps[0]
1008
1009        elif state == RIBBON_GALLERY_BUTTON_HOVERED:
1010            dc.SetPen(self._gallery_item_border_pen)
1011            dc.SetBrush(self._gallery_button_hover_background_brush)
1012            dc.DrawRectangle(rect.x, rect.y, rect.width + extra_width, rect.height + extra_height)
1013            btn_bitmap = bitmaps[1]
1014
1015        elif state == RIBBON_GALLERY_BUTTON_ACTIVE:
1016            dc.SetPen(self._gallery_item_border_pen)
1017            dc.SetBrush(self._gallery_button_active_background_brush)
1018            dc.DrawRectangle(rect.x, rect.y, rect.width + extra_width, rect.height + extra_height)
1019            btn_bitmap = bitmaps[2]
1020
1021        elif state == RIBBON_GALLERY_BUTTON_DISABLED:
1022            dc.SetPen(wx.TRANSPARENT_PEN)
1023            dc.SetBrush(self._gallery_button_disabled_background_brush)
1024            dc.DrawRectangle(reduced_rect.x, reduced_rect.y, reduced_rect.width, reduced_rect.height)
1025            btn_bitmap = bitmaps[3]
1026
1027        dc.DrawBitmap(btn_bitmap, reduced_rect.x + reduced_rect.width / 2 - 2, (rect.y + rect.height / 2) - 2, True)
1028
1029
1030    def DrawGalleryItemBackground(self, dc, wnd, rect, item):
1031        """
1032        Draw the background of a single item in a :class:`~wx.lib.agw.ribbon.gallery.RibbonGallery` control.
1033
1034        This is painted on top of a gallery background, and behind the items bitmap.
1035        Unlike :meth:`~RibbonAUIArtProvider.DrawButtonBarButton` and :meth:`~RibbonAUIArtProvider.DrawTool`, it is not expected to draw the
1036        item bitmap - that is done by the gallery control itself.
1037
1038        :param `dc`: The device context to draw onto;
1039        :param `wnd`: The window which is being drawn onto, which is always the gallery
1040         which contains the item being drawn;
1041        :param `rect`: The rectangle within which to draw. The size of this rectangle
1042         will be the size of the item's bitmap, expanded by gallery item padding values
1043         (``RIBBON_ART_GALLERY_BITMAP_PADDING_LEFT_SIZE``, ``RIBBON_ART_GALLERY_BITMAP_PADDING_RIGHT_SIZE``,
1044         ``RIBBON_ART_GALLERY_BITMAP_PADDING_TOP_SIZE``, and ``RIBBON_ART_GALLERY_BITMAP_PADDING_BOTTOM_SIZE``).
1045         The drawing rectangle will be entirely within a rectangle on the same device
1046         context previously painted with :meth:`~RibbonAUIArtProvider.DrawGalleryBackground`;
1047        :param `item`: The item whose background is being painted. Typically the
1048         background will vary if the item is hovered, active, or selected;
1049         :meth:`RibbonGallery.GetSelection() <lib.agw.ribbon.gallery.RibbonGallery.GetSelection>`,
1050         :meth:`RibbonGallery.GetActiveItem() <lib.agw.ribbon.gallery.RibbonGallery.GetActiveItem>`, and
1051         :meth:`RibbonGallery.GetHoveredItem() <lib.agw.ribbon.gallery.RibbonGallery.GetHoveredItem>`
1052         can be called to test if the given item is in one of these states.
1053
1054        """
1055
1056        if wnd.GetHoveredItem() != item and wnd.GetActiveItem() != item and wnd.GetSelection() != item:
1057            return
1058
1059        dc.SetPen(self._gallery_item_border_pen)
1060
1061        if wnd.GetActiveItem() == item or wnd.GetSelection() == item:
1062            dc.SetBrush(self._gallery_button_active_background_brush)
1063        else:
1064            dc.SetBrush(self._gallery_button_hover_background_brush)
1065
1066        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
1067
1068
1069    def DrawButtonBarBackground(self, dc, wnd, rect):
1070        """
1071        Draw the background for a :class:`~wx.lib.agw.ribbon.buttonbar.RibbonButtonBar` control.
1072
1073        :param `dc`: The device context to draw onto;
1074        :param `wnd`: The window which is being drawn onto (which will typically
1075         be the button bar itself, though this is not guaranteed);
1076        :param `rect`: The rectangle within which to draw.
1077
1078        """
1079
1080        self.DrawPartialPanelBackground(dc, wnd, rect)
1081
1082
1083    def DrawButtonBarButton(self, dc, wnd, rect, kind, state, label, bitmap_large, bitmap_small):
1084        """
1085        Draw a single button for a :class:`~wx.lib.agw.ribbon.buttonbar.RibbonButtonBar` control.
1086
1087        :param `dc`: The device context to draw onto;
1088        :param `wnd`: The window which is being drawn onto;
1089        :param `rect`: The rectangle within which to draw. The size of this rectangle
1090         will be a size previously returned by :meth:`RibbonMSWArtProvider.GetButtonBarButtonSize() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetButtonBarButtonSize>`, and the
1091         rectangle will be entirely within a rectangle on the same device context
1092         previously painted with :meth:`~RibbonAUIArtProvider.DrawButtonBarBackground`;
1093        :param `kind`: The kind of button to draw (normal, dropdown or hybrid);
1094        :param `state`: Combination of a size flag and state flags from the
1095         `RibbonButtonBarButtonState` enumeration;
1096        :param `label`: The label of the button;
1097        :param `bitmap_large`: The large bitmap of the button (or the large disabled
1098         bitmap when ``RIBBON_BUTTONBAR_BUTTON_DISABLED`` is set in );
1099        :param `bitmap_small`: The small bitmap of the button (or the small disabled
1100         bitmap when ``RIBBON_BUTTONBAR_BUTTON_DISABLED`` is set in ).
1101
1102        """
1103
1104        if kind == RIBBON_BUTTON_TOGGLE:
1105            kind = RIBBON_BUTTON_NORMAL
1106            if state & RIBBON_BUTTONBAR_BUTTON_TOGGLED:
1107                state ^= RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK
1108
1109        if state & (RIBBON_BUTTONBAR_BUTTON_HOVER_MASK | RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK):
1110            dc.SetPen(self._button_bar_hover_border_pen)
1111            bg_rect = wx.Rect(*rect)
1112            bg_rect.Deflate(1, 1)
1113
1114            if kind == RIBBON_BUTTON_HYBRID:
1115                result = state & RIBBON_BUTTONBAR_BUTTON_SIZE_MASK
1116
1117                if result == RIBBON_BUTTONBAR_BUTTON_LARGE:
1118                    iYBorder = rect.y + bitmap_large.GetHeight() + 4
1119                    partial_bg = wx.Rect(*rect)
1120
1121                    if state & RIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED:
1122                        partial_bg.SetBottom(iYBorder - 1)
1123                    else:
1124                        partial_bg.height -= (iYBorder - partial_bg.y + 1)
1125                        partial_bg.y = iYBorder + 1
1126
1127                    dc.DrawLine(rect.x, iYBorder, rect.x + rect.width, iYBorder)
1128                    bg_rect.Intersect(partial_bg)
1129
1130                elif result == RIBBON_BUTTONBAR_BUTTON_MEDIUM:
1131                    iArrowWidth = 9
1132
1133                    if state & RIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED:
1134                        bg_rect.width -= iArrowWidth
1135                        dc.DrawLine(bg_rect.x + bg_rect.width, rect.y, bg_rect.x + bg_rect.width, rect.y + rect.height)
1136                    else:
1137                        iArrowWidth -= 1
1138                        bg_rect.x += bg_rect.width - iArrowWidth
1139                        bg_rect.width = iArrowWidth
1140                        dc.DrawLine(bg_rect.x - 1, rect.y, bg_rect.x - 1, rect.y + rect.height)
1141
1142            dc.SetBrush(wx.TRANSPARENT_BRUSH)
1143            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
1144
1145            dc.SetPen(wx.TRANSPARENT_PEN)
1146
1147            if state & RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK:
1148                dc.SetBrush(self._button_bar_active_background_brush)
1149            else:
1150                dc.SetBrush(self._button_bar_hover_background_brush)
1151
1152            dc.DrawRectangle(bg_rect.x, bg_rect.y, bg_rect.width, bg_rect.height)
1153
1154        dc.SetFont(self._button_bar_label_font)
1155        dc.SetTextForeground(self._button_bar_label_colour)
1156        self.DrawButtonBarButtonForeground(dc, rect, kind, state, label, bitmap_large, bitmap_small)
1157
1158
1159    def DrawToolBarBackground(self, dc, wnd, rect):
1160        """
1161        Draw the background for a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar` control.
1162
1163        :param `dc`: The device context to draw onto;
1164        :param `wnd`: The which is being drawn onto. In most cases this will be
1165         a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`, but it doesn't have to be;
1166        :param `rect`: The rectangle within which to draw. Some of this rectangle
1167         will later be drawn over using :meth:`~RibbonAUIArtProvider.DrawToolGroupBackground` and :meth:`~RibbonAUIArtProvider.DrawTool`,
1168         but not all of it will (unless there is only a single group of tools).
1169
1170        """
1171
1172        self.DrawPartialPanelBackground(dc, wnd, rect)
1173
1174
1175    def DrawToolGroupBackground(self, dc, wnd, rect):
1176        """
1177        Draw the background for a group of tools on a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar` control.
1178
1179        :param `dc`: The device context to draw onto;
1180        :param `wnd`: The window which is being drawn onto. In most cases this will
1181         be a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`, but it doesn't have to be;
1182        :param `rect`: The rectangle within which to draw. This rectangle is a union
1183         of the individual tools' rectangles. As there are no gaps between tools, this
1184         rectangle will be painted over exactly once by calls to :meth:`~RibbonAUIArtProvider.DrawTool`. The
1185         group background could therefore be painted by :meth:`~RibbonAUIArtProvider.DrawTool`, though it can be
1186         conceptually easier and more efficient to draw it all at once here. The
1187         rectangle will be entirely within a rectangle on the same device context
1188         previously painted with :meth:`~RibbonAUIArtProvider.DrawToolBarBackground`.
1189
1190        """
1191
1192        dc.SetPen(self._toolbar_border_pen)
1193        dc.SetBrush(wx.TRANSPARENT_BRUSH)
1194        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
1195        bg_rect = wx.Rect(*rect)
1196        bg_rect.Deflate(1, 1)
1197        dc.GradientFillLinear(bg_rect, self._tool_background_colour, self._tool_background_gradient_colour, wx.SOUTH)
1198
1199
1200    def DrawTool(self, dc, wnd, rect, bitmap, kind, state):
1201        """
1202        Draw a single tool (for a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar` control).
1203
1204        :param `dc`: The device context to draw onto;
1205        :param `wnd`: The window which is being drawn onto. In most cases this will
1206         be a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`, but it doesn't have to be;
1207        :param `rect`: The rectangle within which to draw. The size of this rectangle
1208         will at least the size returned by :meth:`RibbonMSWArtProvider.GetToolSize() <lib.agw.ribbon.art_msw.RibbonMSWArtProvider.GetToolSize>`,
1209         and the height of it will
1210         be equal for all tools within the same group. The rectangle will be entirely
1211         within a rectangle on the same device context previously painted with
1212         :meth:`~RibbonAUIArtProvider.DrawToolGroupBackground`;
1213        :param `bitmap`: The bitmap to use as the tool's foreground. If the tool is a
1214         hybrid or dropdown tool, then the foreground should also contain a standard
1215         dropdown button;
1216        :param `kind`: The kind of tool to draw (normal, dropdown, or hybrid);
1217        :param `state`: A combination of `RibbonToolBarToolState` flags giving the
1218         state of the tool and it's relative position within a tool group.
1219
1220        """
1221
1222        if kind == RIBBON_BUTTON_TOGGLE:
1223            if state & RIBBON_TOOLBAR_TOOL_TOGGLED:
1224                state ^= RIBBON_TOOLBAR_TOOL_ACTIVE_MASK
1225
1226        bg_rect = wx.Rect(*rect)
1227        bg_rect.Deflate(1, 1)
1228
1229        if state & RIBBON_TOOLBAR_TOOL_LAST == 0:
1230            bg_rect.width += 1
1231
1232        is_custom_bg = (state & (RIBBON_TOOLBAR_TOOL_HOVER_MASK | RIBBON_TOOLBAR_TOOL_ACTIVE_MASK)) != 0
1233        is_split_hybrid = kind == RIBBON_BUTTON_HYBRID and is_custom_bg
1234
1235        # Background
1236        if is_custom_bg:
1237            dc.SetPen(wx.TRANSPARENT_PEN)
1238            dc.SetBrush(self._tool_hover_background_brush)
1239            dc.DrawRectangle(bg_rect.x, bg_rect.y, bg_rect.width, bg_rect.height)
1240
1241            if state & RIBBON_TOOLBAR_TOOL_ACTIVE_MASK:
1242                active_rect = wx.Rect(*bg_rect)
1243
1244                if kind == RIBBON_BUTTON_HYBRID:
1245                    active_rect.width -= 8
1246
1247                    if state & RIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE:
1248                        active_rect.x += active_rect.width
1249                        active_rect.width = 8
1250
1251                dc.SetBrush(self._tool_active_background_brush)
1252                dc.DrawRectangle(active_rect.x, active_rect.y, active_rect.width, active_rect.height)
1253
1254        # Border
1255        if is_custom_bg:
1256            dc.SetPen(self._toolbar_hover_borden_pen)
1257        else:
1258            dc.SetPen(self._toolbar_border_pen)
1259
1260        if state & RIBBON_TOOLBAR_TOOL_FIRST == 0:
1261            existing = dc.GetPixel(rect.x, rect.y + 1)
1262
1263            if existing == wx.NullColour or existing != self._toolbar_hover_borden_pen.GetColour():
1264                dc.DrawLine(rect.x, rect.y + 1, rect.x, rect.y + rect.height - 1)
1265
1266        if is_custom_bg:
1267            border_rect = wx.Rect(*bg_rect)
1268            border_rect.Inflate(1, 1)
1269            dc.SetBrush(wx.TRANSPARENT_BRUSH)
1270            dc.DrawRectangle(border_rect.x, border_rect.y, border_rect.width, border_rect.height)
1271
1272        # Foreground
1273        avail_width = bg_rect.GetWidth()
1274
1275        if kind & RIBBON_BUTTON_DROPDOWN:
1276            avail_width -= 8
1277            if is_split_hybrid:
1278                dc.DrawLine(rect.x + avail_width + 1, rect.y, rect.x + avail_width + 1, rect.y + rect.height)
1279
1280            dc.DrawBitmap(self._toolbar_drop_bitmap, bg_rect.x + avail_width + 2, bg_rect.y + (bg_rect.height / 2) - 2, True)
1281
1282        dc.DrawBitmap(bitmap, bg_rect.x + (avail_width - bitmap.GetWidth()) / 2, bg_rect.y + (bg_rect.height - bitmap.GetHeight()) / 2, True)
1283
1284