1# -*- coding: utf-8 -*-
2#----------------------------------------------------------------------------
3# Name:         art_msw.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_msw` is responsible for drawing all the components of the ribbon
16interface using a Windows 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 . import panel as PANEL
50from . import page as PAGE
51
52from .art_internal import RibbonLoadPixmap, RibbonInterpolateColour, RibbonDrawParallelGradientLines
53from .art_internal import RibbonCanLabelBreakAtPosition
54from .art_internal import RibbonHSLColour
55
56from .art import *
57
58
59gallery_up_xpm = [b"5 5 2 1", b"  c None", b"x c #FF00FF", b"     ", b"  x  ", b" xxx ", b"xxxxx", b"     "]
60gallery_down_xpm = [b"5 5 2 1", b"  c None", b"x c #FF00FF", b"     ", b"xxxxx", b" xxx ", b"  x  ", b"     "]
61gallery_left_xpm = [b"5 5 2 1", b"  c None", b"x c #FF00FF", b"   x ", b"  xx ", b" xxx ", b"  xx ", b"   x "]
62gallery_right_xpm = [b"5 5 2 1", b"  c None", b"x c #FF00FF", b" x   ", b" xx  ", b" xxx ", b" xx  ", b" x   "]
63gallery_extension_xpm = [b"5 5 2 1", b"  c None", b"x c #FF00FF", b"xxxxx", b"     ", b"xxxxx", b" xxx ", b"  x  "]
64panel_extension_xpm = [b"7 7 2 1", b"  c None", b"x c #FF00FF", b"xxxxxx ", b"x      ", b"x      ",
65                       b"x  x  x", b"x   xxx", b"x   xxx", b"   xxxx"]
66
67
68def LikePrimary(primary_hsl, is_gray, h, s, l):
69
70    return primary_hsl.ShiftHue(h).Saturated((is_gray and [0] or [s])[0]).Lighter(l).ToRGB()
71
72
73def LikeSecondary(secondary_hsl, is_gray, h, s, l):
74
75    return secondary_hsl.ShiftHue(h).Saturated((is_gray and [0] or [s])[0]).Lighter(l).ToRGB()
76
77
78def SingleLine(dc, rect, start, finish):
79
80    dc.DrawLine(start.x + rect.x, start.y + rect.y, finish.x + rect.x, finish.y + rect.y)
81
82
83class RibbonMSWArtProvider(object):
84
85    def __init__(self, set_colour_scheme=True):
86
87        self._flags = 0
88        self._tab_label_font = wx.NORMAL_FONT
89        self._button_bar_label_font = wx.NORMAL_FONT
90        self._panel_label_font = wx.NORMAL_FONT
91
92        self._gallery_up_bitmap = [wx.NullBitmap for i in range(4)]
93        self._gallery_down_bitmap = [wx.NullBitmap for i in range(4)]
94        self._gallery_extension_bitmap = [wx.NullBitmap for i in range(4)]
95        self._panel_extension_bitmap = [wx.NullBitmap for i in range(2)]
96
97        if set_colour_scheme:
98            self.SetColourScheme(wx.Colour(194, 216, 241), wx.Colour(255, 223, 114), wx.Colour(0, 0, 0))
99
100        self._cached_tab_separator_visibility = -10.0 # valid visibilities are in range [0, 1]
101        self._tab_separation_size = 3
102        self._page_border_left = 2
103        self._page_border_top = 1
104        self._page_border_right = 2
105        self._page_border_bottom = 3
106        self._panel_x_separation_size = 1
107        self._panel_y_separation_size = 1
108        self._tool_group_separation_size = 3
109        self._gallery_bitmap_padding_left_size = 4
110        self._gallery_bitmap_padding_right_size = 4
111        self._gallery_bitmap_padding_top_size = 4
112        self._gallery_bitmap_padding_bottom__size = 4
113        self._cached_tab_separator = wx.NullBitmap
114
115
116    def GetColourScheme(self, primary, secondary, tertiary):
117        """
118        Get the current colour scheme.
119
120        Returns three colours such that if :meth:`~RibbonMSWArtProvider.SetColourScheme` were called with them, the
121        colour scheme would be restored to what it was when :meth:`~RibbonMSWArtProvider.SetColourScheme` was last
122        called. In practice, this usually means that the returned values are the three
123        colours given in the last call to :meth:`~RibbonMSWArtProvider.SetColourScheme`, however if
124        :meth:`~RibbonMSWArtProvider.SetColourScheme` performs an idempotent operation upon the colours it is given
125        (like clamping a component of the colour), then the returned values may not be
126        the three colours given in the last call to :meth:`~RibbonMSWArtProvider.SetColourScheme`.
127
128        If :meth:`~RibbonMSWArtProvider.SetColourScheme` has not been called, then the returned values should result
129        in a colour scheme similar to, if not identical to, the default colours of the
130        art provider. Note that if :meth:`~RibbonMSWArtProvider.SetColour` is called, then :meth:`~RibbonMSWArtProvider.GetColourScheme` does
131        not try and return a colour scheme similar to colours being used - it's return
132        values are dependant upon the last values given to :meth:`~RibbonMSWArtProvider.SetColourScheme`, as
133        described above.
134
135        :param `primary`: Pointer to a location to store the primary colour, or ``None``;
136        :param `secondary`: Pointer to a location to store the secondary colour, or ``None``;
137        :param `tertiary`: Pointer to a location to store the tertiary colour, or ``None``.
138
139        """
140
141        if primary != None:
142            primary = self._primary_scheme_colour
143        if secondary != None:
144            secondary = self._secondary_scheme_colour
145        if tertiary != None:
146            tertiary = self._tertiary_scheme_colour
147
148        return primary, secondary, tertiary
149
150
151    def SetColourScheme(self, primary, secondary, tertiary):
152        """
153        Set all applicable colour settings from a few base colours.
154
155        Uses any or all of the three given colours to create a colour scheme, and then
156        sets all colour settings which are relevant to the art provider using that
157        scheme. Note that some art providers may not use the tertiary colour for
158        anything, and some may not use the secondary colour either.
159
160        :param `primary`: MISSING DESCRIPTION;
161        :param `secondary`: MISSING DESCRIPTION;
162        :param `tertiary`: MISSING DESCRIPTION.
163
164        :see: :meth:`~RibbonMSWArtProvider.SetColour`, :meth:`~RibbonMSWArtProvider.GetColourScheme`
165        """
166
167        self._primary_scheme_colour = primary
168        self._secondary_scheme_colour = secondary
169        self._tertiary_scheme_colour = tertiary
170
171        primary_hsl = RibbonHSLColour(primary)
172        secondary_hsl = RibbonHSLColour(secondary)
173        # tertiary not used for anything
174
175        # Map primary saturation from [0, 1] to [.25, .75]
176        primary_is_gray = False
177        gray_saturation_threshold = 0.01
178
179        if primary_hsl.saturation <= gray_saturation_threshold:
180            primary_is_gray = True
181        else:
182            primary_hsl.saturation = cos(primary_hsl.saturation * M_PI) * -0.25 + 0.5
183
184        # Map primary luminance from [0, 1] to [.23, .83]
185        primary_hsl.luminance = cos(primary_hsl.luminance * M_PI) * -0.3 + 0.53
186
187        # Map secondary saturation from [0, 1] to [0.16, 0.84]
188        secondary_is_gray = False
189
190        if secondary_hsl.saturation <= gray_saturation_threshold:
191            secondary_is_gray = True
192        else:
193            secondary_hsl.saturation = cos(secondary_hsl.saturation * M_PI) * -0.34 + 0.5
194
195        # Map secondary luminance from [0, 1] to [0.1, 0.9]
196        secondary_hsl.luminance = cos(secondary_hsl.luminance * M_PI) * -0.4 + 0.5
197
198        self._page_border_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, 1.4, 0.00, -0.08))
199        self._page_background_top_colour = LikePrimary(primary_hsl, primary_is_gray, -0.1, -0.03, 0.12)
200        self._page_hover_background_top_colour = LikePrimary(primary_hsl, primary_is_gray, -2.8, 0.27, 0.17)
201        self._page_background_top_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 0.1, -0.10, 0.08)
202        self._page_hover_background_top_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 3.2, 0.16, 0.13)
203        self._page_background_colour = LikePrimary(primary_hsl, primary_is_gray, 0.4, -0.09, 0.05)
204        self._page_hover_background_colour = LikePrimary(primary_hsl, primary_is_gray, 0.1, 0.19, 0.10)
205        self._page_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, -3.2, 0.27, 0.10)
206        self._page_hover_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.8, 0.01, 0.15)
207
208        self._tab_active_background_colour = LikePrimary(primary_hsl, primary_is_gray, -0.1, -0.31, 0.16)
209        self._tab_active_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, -0.1, -0.03, 0.12)
210        self._tab_separator_colour = LikePrimary(primary_hsl, primary_is_gray, 0.9, 0.24, 0.05)
211        self._tab_ctrl_background_brush = wx.Brush(LikePrimary(primary_hsl, primary_is_gray, 1.0, 0.39, 0.07))
212        self._tab_hover_background_colour = LikePrimary(primary_hsl, primary_is_gray, 1.3, 0.15, 0.10)
213        self._tab_hover_background_top_colour = LikePrimary(primary_hsl, primary_is_gray, 1.4, 0.36, 0.08)
214        self._tab_border_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, 1.4, 0.03, -0.05)  )
215        self._tab_separator_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.7, -0.15, -0.18)
216        self._tab_hover_background_top_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.8, 0.34, 0.13)
217        self._tab_label_colour = LikePrimary(primary_hsl, primary_is_gray, 4.3, 0.13, -0.49)
218        self._tab_hover_background_gradient_colour = LikeSecondary(primary_hsl, secondary_is_gray, -1.5, -0.34, 0.01)
219
220        self._panel_minimised_border_gradient_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, -6.9, -0.17, -0.09))
221        self._panel_minimised_border_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, -5.3, -0.24, -0.06))
222        self._panel_border_gradient_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, -5.2, -0.15, -0.06))
223        self._panel_border_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, -2.8, -0.32, 0.02))
224        self._panel_label_background_brush = wx.Brush(LikePrimary(primary_hsl, primary_is_gray, -1.5, 0.03, 0.05))
225        self._panel_active_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 0.5, 0.34, 0.05)
226        self._panel_hover_label_background_brush = wx.Brush(LikePrimary(primary_hsl, primary_is_gray, 1.0, 0.30, 0.09))
227        self._panel_active_background_top_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.4, -0.17, -0.13)
228        self._panel_active_background_colour = LikePrimary(primary_hsl, primary_is_gray, 1.6, -0.18, -0.18)
229        self._panel_active_background_top_colour = LikePrimary(primary_hsl, primary_is_gray, 1.7, -0.20, -0.03)
230        self._panel_label_colour = LikePrimary(primary_hsl, primary_is_gray, 2.8, -0.14, -0.35)
231        self._panel_hover_label_colour = self._panel_label_colour
232        self._panel_minimised_label_colour = self._tab_label_colour
233
234        self._panel_hover_button_background_brush = wx.Brush(LikeSecondary(secondary_hsl, secondary_is_gray, -0.9, 0.16, -0.07))
235        self._panel_hover_button_border_pen = wx.Pen(LikeSecondary(secondary_hsl, secondary_is_gray, -3.9, -0.16, -0.14))
236        self.SetColour(RIBBON_ART_PANEL_BUTTON_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 1.4, -0.21, -0.23))
237        self.SetColour(RIBBON_ART_PANEL_BUTTON_HOVER_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 1.5, -0.24, -0.29))
238
239        self._gallery_button_disabled_background_colour = LikePrimary(primary_hsl, primary_is_gray, -2.8, -0.46, 0.09)
240        self._gallery_button_disabled_background_top_brush = wx.Brush(LikePrimary(primary_hsl, primary_is_gray, -2.8, -0.36, 0.15))
241        self._gallery_hover_background_brush = wx.Brush(LikePrimary(primary_hsl, primary_is_gray, -0.8, 0.05, 0.15))
242        self._gallery_border_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, 0.7, -0.02, 0.03))
243        self._gallery_button_background_top_brush = wx.Brush(LikePrimary(primary_hsl, primary_is_gray, 0.8, 0.34, 0.13))
244        self._gallery_button_background_colour = LikePrimary(primary_hsl, primary_is_gray, 1.3, 0.10, 0.08)
245
246        # SetColour used so that the relevant bitmaps are generated
247        self.SetColour(RIBBON_ART_GALLERY_BUTTON_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 1.4, -0.21, -0.23))
248        self.SetColour(RIBBON_ART_GALLERY_BUTTON_HOVER_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 1.5, -0.24, -0.29))
249        self.SetColour(RIBBON_ART_GALLERY_BUTTON_ACTIVE_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 1.5, -0.24, -0.29))
250        self.SetColour(RIBBON_ART_GALLERY_BUTTON_DISABLED_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 0.0, -1.0, 0.0))
251        self._gallery_button_disabled_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.5, -0.43, 0.12)
252        self._gallery_button_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.7, 0.11, 0.09)
253        self._gallery_item_border_pen = wx.Pen(LikeSecondary(secondary_hsl, secondary_is_gray, -3.9, -0.16, -0.14))
254        self._gallery_button_hover_background_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -0.9, 0.16, -0.07)
255        self._gallery_button_hover_background_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, 0.1, 0.12, 0.03)
256        self._gallery_button_hover_background_top_brush = wx.Brush(LikeSecondary(secondary_hsl, secondary_is_gray, 4.3, 0.16, 0.17))
257
258        self._gallery_button_active_background_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -9.9, 0.03, -0.22)
259        self._gallery_button_active_background_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -9.5, 0.14, -0.11)
260        self._gallery_button_active_background_top_brush = wx.Brush(LikeSecondary(secondary_hsl, secondary_is_gray, -9.0, 0.15, -0.08))
261
262        self._button_bar_label_colour = self._tab_label_colour
263        self._button_bar_hover_border_pen = wx.Pen(LikeSecondary(secondary_hsl, secondary_is_gray, -6.2, -0.47, -0.14))
264        self._button_bar_hover_background_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -0.6, 0.16, 0.04)
265        self._button_bar_hover_background_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -0.2, 0.16, -0.10)
266        self._button_bar_hover_background_top_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, 0.2, 0.16, 0.03)
267        self._button_bar_hover_background_top_colour = LikeSecondary(secondary_hsl, secondary_is_gray, 8.8, 0.16, 0.17)
268        self._button_bar_active_border_pen = wx.Pen(LikeSecondary(secondary_hsl, secondary_is_gray, -6.2, -0.47, -0.25))
269        self._button_bar_active_background_top_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -8.4, 0.08, 0.06)
270        self._button_bar_active_background_top_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -9.7, 0.13, -0.07)
271        self._button_bar_active_background_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -9.9, 0.14, -0.14)
272        self._button_bar_active_background_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -8.7, 0.17, -0.03)
273
274        self._toolbar_border_pen = wx.Pen(LikePrimary(primary_hsl, primary_is_gray, 1.4, -0.21, -0.16))
275        self.SetColour(RIBBON_ART_TOOLBAR_FACE_COLOUR, LikePrimary(primary_hsl, primary_is_gray, 1.4, -0.17, -0.22))
276        self._tool_background_top_colour = LikePrimary(primary_hsl, primary_is_gray, -1.9, -0.07, 0.06)
277        self._tool_background_top_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.4, 0.12, 0.08)
278        self._tool_background_colour = LikePrimary(primary_hsl, primary_is_gray, 1.4, -0.09, 0.03)
279        self._tool_background_gradient_colour = LikePrimary(primary_hsl, primary_is_gray, 1.9, 0.11, 0.09)
280        self._tool_hover_background_top_colour = LikeSecondary(secondary_hsl, secondary_is_gray, 3.4, 0.11, 0.16)
281        self._tool_hover_background_top_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -1.4, 0.04, 0.08)
282        self._tool_hover_background_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -1.8, 0.16, -0.12)
283        self._tool_hover_background_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -2.6, 0.16, 0.05)
284        self._tool_active_background_top_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -9.9, -0.12, -0.09)
285        self._tool_active_background_top_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -8.5, 0.16, -0.12)
286        self._tool_active_background_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -7.9, 0.16, -0.20)
287        self._tool_active_background_gradient_colour = LikeSecondary(secondary_hsl, secondary_is_gray, -6.6, 0.16, -0.10)
288
289        # Invalidate cached tab separator
290        self._cached_tab_separator_visibility = -1.0
291
292
293    def Clone(self):
294        """
295        Create a new art provider which is a clone of this one.
296        """
297
298        copy = RibbonMSWArtProvider()
299        self.CloneTo(copy)
300        return copy
301
302
303    def CloneTo(self, copy):
304
305        for i in range(4):
306            copy._gallery_up_bitmap[i] = self._gallery_up_bitmap[i]
307            copy._gallery_down_bitmap[i] = self._gallery_down_bitmap[i]
308            copy._gallery_extension_bitmap[i] = self._gallery_extension_bitmap[i]
309
310        for i in range(2):
311            copy._panel_extension_bitmap[i] = self._panel_extension_bitmap[i]
312
313        copy._toolbar_drop_bitmap = self._toolbar_drop_bitmap
314
315        copy._primary_scheme_colour = self._primary_scheme_colour
316        copy._secondary_scheme_colour = self._secondary_scheme_colour
317        copy._tertiary_scheme_colour = self._tertiary_scheme_colour
318
319        copy._button_bar_label_colour = self._button_bar_label_colour
320        copy._tab_label_colour = self._tab_label_colour
321        copy._tab_separator_colour = self._tab_separator_colour
322        copy._tab_separator_gradient_colour = self._tab_separator_gradient_colour
323        copy._tab_active_background_colour = self._tab_hover_background_colour
324        copy._tab_active_background_gradient_colour = self._tab_hover_background_gradient_colour
325        copy._tab_hover_background_colour = self._tab_hover_background_colour
326        copy._tab_hover_background_gradient_colour = self._tab_hover_background_gradient_colour
327        copy._tab_hover_background_top_colour = self._tab_hover_background_top_colour
328        copy._tab_hover_background_top_gradient_colour = self._tab_hover_background_top_gradient_colour
329        copy._panel_label_colour = self._panel_label_colour
330        copy._panel_hover_label_colour = self._panel_hover_label_colour
331        copy._panel_minimised_label_colour = self._panel_minimised_label_colour
332        copy._panel_button_face_colour = self._panel_button_face_colour
333        copy._panel_button_hover_face_colour = self._panel_button_hover_face_colour
334        copy._panel_active_background_colour = self._panel_active_background_colour
335        copy._panel_active_background_gradient_colour = self._panel_active_background_gradient_colour
336        copy._panel_active_background_top_colour = self._panel_active_background_top_colour
337        copy._panel_active_background_top_gradient_colour = self._panel_active_background_top_gradient_colour
338        copy._page_background_colour = self._page_background_colour
339        copy._page_background_gradient_colour = self._page_background_gradient_colour
340        copy._page_background_top_colour = self._page_background_top_colour
341        copy._page_background_top_gradient_colour = self._page_background_top_gradient_colour
342        copy._page_hover_background_colour = self._page_hover_background_colour
343        copy._page_hover_background_gradient_colour = self._page_hover_background_gradient_colour
344        copy._page_hover_background_top_colour = self._page_hover_background_top_colour
345        copy._page_hover_background_top_gradient_colour = self._page_hover_background_top_gradient_colour
346        copy._button_bar_hover_background_colour = self._button_bar_hover_background_colour
347        copy._button_bar_hover_background_gradient_colour = self._button_bar_hover_background_gradient_colour
348        copy._button_bar_hover_background_top_colour = self._button_bar_hover_background_top_colour
349        copy._button_bar_hover_background_top_gradient_colour = self._button_bar_hover_background_top_gradient_colour
350        copy._button_bar_active_background_colour = self._button_bar_active_background_colour
351        copy._button_bar_active_background_gradient_colour = self._button_bar_active_background_gradient_colour
352        copy._button_bar_active_background_top_colour = self._button_bar_active_background_top_colour
353        copy._button_bar_active_background_top_gradient_colour = self._button_bar_active_background_top_gradient_colour
354        copy._gallery_button_background_colour = self._gallery_button_background_colour
355        copy._gallery_button_background_gradient_colour = self._gallery_button_background_gradient_colour
356        copy._gallery_button_hover_background_colour = self._gallery_button_hover_background_colour
357        copy._gallery_button_hover_background_gradient_colour = self._gallery_button_hover_background_gradient_colour
358        copy._gallery_button_active_background_colour = self._gallery_button_active_background_colour
359        copy._gallery_button_active_background_gradient_colour = self._gallery_button_active_background_gradient_colour
360        copy._gallery_button_disabled_background_colour = self._gallery_button_disabled_background_colour
361        copy._gallery_button_disabled_background_gradient_colour = self._gallery_button_disabled_background_gradient_colour
362        copy._gallery_button_face_colour = self._gallery_button_face_colour
363        copy._gallery_button_hover_face_colour = self._gallery_button_hover_face_colour
364        copy._gallery_button_active_face_colour = self._gallery_button_active_face_colour
365        copy._gallery_button_disabled_face_colour = self._gallery_button_disabled_face_colour
366
367        copy._tab_ctrl_background_brush = self._tab_ctrl_background_brush
368        copy._panel_label_background_brush = self._panel_label_background_brush
369        copy._panel_hover_label_background_brush = self._panel_hover_label_background_brush
370        copy._panel_hover_button_background_brush = self._panel_hover_button_background_brush
371        copy._gallery_hover_background_brush = self._gallery_hover_background_brush
372        copy._gallery_button_background_top_brush = self._gallery_button_background_top_brush
373        copy._gallery_button_hover_background_top_brush = self._gallery_button_hover_background_top_brush
374        copy._gallery_button_active_background_top_brush = self._gallery_button_active_background_top_brush
375        copy._gallery_button_disabled_background_top_brush = self._gallery_button_disabled_background_top_brush
376
377        copy._tab_label_font = self._tab_label_font
378        copy._button_bar_label_font = self._button_bar_label_font
379        copy._panel_label_font = self._panel_label_font
380
381        copy._page_border_pen = self._page_border_pen
382        copy._panel_border_pen = self._panel_border_pen
383        copy._panel_border_gradient_pen = self._panel_border_gradient_pen
384        copy._panel_hover_button_border_pen = self._panel_hover_button_border_pen
385        copy._panel_minimised_border_pen = self._panel_minimised_border_pen
386        copy._panel_minimised_border_gradient_pen = self._panel_minimised_border_gradient_pen
387        copy._tab_border_pen = self._tab_border_pen
388        copy._gallery_border_pen = self._gallery_border_pen
389        copy._button_bar_hover_border_pen = self._button_bar_hover_border_pen
390        copy._button_bar_active_border_pen = self._button_bar_active_border_pen
391        copy._gallery_item_border_pen = self._gallery_item_border_pen
392        copy._toolbar_border_pen = self._toolbar_border_pen
393
394        copy._flags = self._flags
395        copy._tab_separation_size = self._tab_separation_size
396        copy._page_border_left = self._page_border_left
397        copy._page_border_top = self._page_border_top
398        copy._page_border_right = self._page_border_right
399        copy._page_border_bottom = self._page_border_bottom
400        copy._panel_x_separation_size = self._panel_x_separation_size
401        copy._panel_y_separation_size = self._panel_y_separation_size
402        copy._gallery_bitmap_padding_left_size = self._gallery_bitmap_padding_left_size
403        copy._gallery_bitmap_padding_right_size = self._gallery_bitmap_padding_right_size
404        copy._gallery_bitmap_padding_top_size = self._gallery_bitmap_padding_top_size
405        copy._gallery_bitmap_padding_bottom__size = self._gallery_bitmap_padding_bottom__size
406
407
408    def GetFlags(self):
409        """
410        Get the previously set style flags.
411        """
412
413        return self._flags
414
415
416    def SetFlags(self, flags):
417        """
418        Set the style flags.
419
420        Normally called automatically by :meth:`RibbonBar.SetArtProvider() <lib.agw.ribbon.bar.RibbonBar.SetArtProvider>` with the ribbon
421        bar's style flags, so that the art provider has the same flags as the bar which
422        it is serving.
423
424        :param `flags`: MISSING DESCRIPTION.
425
426        """
427
428        if (flags ^ self._flags) & RIBBON_BAR_FLOW_VERTICAL:
429            if flags & RIBBON_BAR_FLOW_VERTICAL:
430                self._page_border_left += 1
431                self._page_border_right += 1
432                self._page_border_top -= 1
433                self._page_border_bottom -= 1
434            else:
435                self._page_border_left -= 1
436                self._page_border_right -= 1
437                self._page_border_top += 1
438                self._page_border_bottom += 1
439
440        self._flags = flags
441
442        # Need to reload some bitmaps when flags change
443        self.Reload(RIBBON_ART_GALLERY_BUTTON_FACE_COLOUR)
444        self.Reload(RIBBON_ART_GALLERY_BUTTON_HOVER_FACE_COLOUR)
445        self.Reload(RIBBON_ART_GALLERY_BUTTON_ACTIVE_FACE_COLOUR)
446        self.Reload(RIBBON_ART_GALLERY_BUTTON_DISABLED_FACE_COLOUR)
447        self.Reload(RIBBON_ART_PANEL_BUTTON_FACE_COLOUR)
448        self.Reload(RIBBON_ART_PANEL_BUTTON_HOVER_FACE_COLOUR)
449
450
451    def Reload(self, setting):
452
453        self.SetColour(setting, self.GetColour(setting))
454
455
456    def GetMetric(self, id):
457        """
458        Get the value of a certain integer setting.
459
460        can be one of the size values of `RibbonArtSetting`.
461
462        :param `id`: a metric id.
463
464        """
465
466        if id == RIBBON_ART_TAB_SEPARATION_SIZE:
467            return self._tab_separation_size
468        elif id == RIBBON_ART_PAGE_BORDER_LEFT_SIZE:
469            return self._page_border_left
470        elif id == RIBBON_ART_PAGE_BORDER_TOP_SIZE:
471            return self._page_border_top
472        elif id == RIBBON_ART_PAGE_BORDER_RIGHT_SIZE:
473            return self._page_border_right
474        elif id == RIBBON_ART_PAGE_BORDER_BOTTOM_SIZE:
475            return self._page_border_bottom
476        elif id == RIBBON_ART_PANEL_X_SEPARATION_SIZE:
477            return self._panel_x_separation_size
478        elif id == RIBBON_ART_PANEL_Y_SEPARATION_SIZE:
479            return self._panel_y_separation_size
480        elif id == RIBBON_ART_TOOL_GROUP_SEPARATION_SIZE:
481            return self._tool_group_separation_size
482        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_LEFT_SIZE:
483            return self._gallery_bitmap_padding_left_size
484        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_RIGHT_SIZE:
485            return self._gallery_bitmap_padding_right_size
486        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_TOP_SIZE:
487            return self._gallery_bitmap_padding_top_size
488        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_BOTTOM_SIZE:
489            return self._gallery_bitmap_padding_bottom__size
490        else:
491            raise Exception("Invalid Metric Ordinal")
492
493
494    def SetMetric(self, id, new_val):
495        """
496        Set the value of a certain integer setting to the value.
497
498        can be one of the size values of `RibbonArtSetting`.
499
500        :param `id`: a metric id;
501        :param `new_val`: the new value of the metric setting.
502
503        """
504
505        if id == RIBBON_ART_TAB_SEPARATION_SIZE:
506            self._tab_separation_size = new_val
507        elif id == RIBBON_ART_PAGE_BORDER_LEFT_SIZE:
508            self._page_border_left = new_val
509        elif id == RIBBON_ART_PAGE_BORDER_TOP_SIZE:
510            self._page_border_top = new_val
511        elif id == RIBBON_ART_PAGE_BORDER_RIGHT_SIZE:
512            self._page_border_right = new_val
513        elif id == RIBBON_ART_PAGE_BORDER_BOTTOM_SIZE:
514            self._page_border_bottom = new_val
515        elif id == RIBBON_ART_PANEL_X_SEPARATION_SIZE:
516            self._panel_x_separation_size = new_val
517        elif id == RIBBON_ART_PANEL_Y_SEPARATION_SIZE:
518            self._panel_y_separation_size = new_val
519        elif id == RIBBON_ART_TOOL_GROUP_SEPARATION_SIZE:
520            self._tool_group_separation_size = new_val
521        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_LEFT_SIZE:
522            self._gallery_bitmap_padding_left_size = new_val
523        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_RIGHT_SIZE:
524            self._gallery_bitmap_padding_right_size = new_val
525        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_TOP_SIZE:
526            self._gallery_bitmap_padding_top_size = new_val
527        elif id == RIBBON_ART_GALLERY_BITMAP_PADDING_BOTTOM_SIZE:
528            self._gallery_bitmap_padding_bottom__size = new_val
529        else:
530            raise Exception("Invalid Metric Ordinal")
531
532
533    def SetFont(self, id, font):
534        """
535        Set the value of a certain font setting to the value.
536
537        can be one of the font values of `RibbonArtSetting`.
538
539        :param `id`: a font id;
540        :param `font`: the new font.
541
542        """
543
544        if id == RIBBON_ART_TAB_LABEL_FONT:
545            self._tab_label_font = font
546        elif id == RIBBON_ART_BUTTON_BAR_LABEL_FONT:
547            self._button_bar_label_font = font
548        elif id == RIBBON_ART_PANEL_LABEL_FONT:
549            self._panel_label_font = font
550        else:
551            raise Exception("Invalid Font Ordinal")
552
553
554    def GetFont(self, id):
555        """
556        Get the value of a certain font setting.
557
558        can be one of the font values of `RibbonArtSetting`.
559
560        :param `id`: the font id.
561
562        """
563
564        if id == RIBBON_ART_TAB_LABEL_FONT:
565            return self._tab_label_font
566        elif id == RIBBON_ART_BUTTON_BAR_LABEL_FONT:
567            return self._button_bar_label_font
568        elif id == RIBBON_ART_PANEL_LABEL_FONT:
569            return self._panel_label_font
570        else:
571            raise Exception("Invalid Font Ordinal")
572
573
574    def GetColour(self, id):
575        """
576        Get the value of a certain colour setting.
577
578        can be one of the colour values of `RibbonArtSetting`.
579
580        :param `id`: the colour id.
581
582        """
583
584        if id == RIBBON_ART_BUTTON_BAR_LABEL_COLOUR:
585            return self._button_bar_label_colour
586        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BORDER_COLOUR:
587            return self._button_bar_hover_border_pen.GetColour()
588        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_TOP_COLOUR:
589            return self._button_bar_hover_background_top_colour
590        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR:
591            return self._button_bar_hover_background_top_gradient_colour
592        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_COLOUR:
593            return self._button_bar_hover_background_colour
594        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_GRADIENT_COLOUR:
595            return self._button_bar_hover_background_gradient_colour
596        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BORDER_COLOUR:
597            return self._button_bar_active_border_pen.GetColour()
598        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_TOP_COLOUR:
599            return self._button_bar_active_background_top_colour
600        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR:
601            return self._button_bar_active_background_top_gradient_colour
602        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_COLOUR:
603            return self._button_bar_active_background_colour
604        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
605            return self._button_bar_active_background_gradient_colour
606        elif id == RIBBON_ART_GALLERY_BORDER_COLOUR:
607            return self._gallery_border_pen.GetColour()
608        elif id == RIBBON_ART_GALLERY_HOVER_BACKGROUND_COLOUR:
609            return self._gallery_hover_background_brush.GetColour()
610        elif id == RIBBON_ART_GALLERY_BUTTON_BACKGROUND_COLOUR:
611            return self._gallery_button_background_colour
612        elif id == RIBBON_ART_GALLERY_BUTTON_BACKGROUND_GRADIENT_COLOUR:
613            return self._gallery_button_background_gradient_colour
614        elif id == RIBBON_ART_GALLERY_BUTTON_BACKGROUND_TOP_COLOUR:
615            return self._gallery_button_background_top_brush.GetColour()
616        elif id == RIBBON_ART_GALLERY_BUTTON_FACE_COLOUR:
617            return self._gallery_button_face_colour
618        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_COLOUR:
619            return self._gallery_button_hover_background_colour
620        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_GRADIENT_COLOUR:
621            return self._gallery_button_hover_background_gradient_colour
622        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_TOP_COLOUR:
623            return self._gallery_button_hover_background_top_brush.GetColour()
624        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_FACE_COLOUR:
625            return self._gallery_button_face_colour
626        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_COLOUR:
627            return self._gallery_button_active_background_colour
628        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
629            return self._gallery_button_active_background_gradient_colour
630        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_TOP_COLOUR:
631            return self._gallery_button_background_top_brush.GetColour()
632        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_FACE_COLOUR:
633            return self._gallery_button_active_face_colour
634        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_COLOUR:
635            return self._gallery_button_disabled_background_colour
636        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_GRADIENT_COLOUR:
637            return self._gallery_button_disabled_background_gradient_colour
638        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_TOP_COLOUR:
639            return self._gallery_button_disabled_background_top_brush.GetColour()
640        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_FACE_COLOUR:
641            return self._gallery_button_disabled_face_colour
642        elif id == RIBBON_ART_GALLERY_ITEM_BORDER_COLOUR:
643            return self._gallery_item_border_pen.GetColour()
644        elif id in [RIBBON_ART_TAB_CTRL_BACKGROUND_COLOUR, RIBBON_ART_TAB_CTRL_BACKGROUND_GRADIENT_COLOUR]:
645            return self._tab_ctrl_background_brush.GetColour()
646        elif id == RIBBON_ART_TAB_LABEL_COLOUR:
647            return self._tab_label_colour
648        elif id == RIBBON_ART_TAB_SEPARATOR_COLOUR:
649            return self._tab_separator_colour
650        elif id == RIBBON_ART_TAB_SEPARATOR_GRADIENT_COLOUR:
651            return self._tab_separator_gradient_colour
652        elif id in [RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_COLOUR, RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR]:
653            return wx.Colour(0, 0, 0)
654        elif id == RIBBON_ART_TAB_ACTIVE_BACKGROUND_COLOUR:
655            return self._tab_active_background_colour
656        elif id == RIBBON_ART_TAB_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
657            return self._tab_active_background_gradient_colour
658        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_TOP_COLOUR:
659            return self._tab_hover_background_top_colour
660        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR:
661            return self._tab_hover_background_top_gradient_colour
662        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_COLOUR:
663            return self._tab_hover_background_colour
664        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_GRADIENT_COLOUR:
665            return self._tab_hover_background_gradient_colour
666        elif id == RIBBON_ART_TAB_BORDER_COLOUR:
667            return self._tab_border_pen.GetColour()
668        elif id == RIBBON_ART_PANEL_BORDER_COLOUR:
669            return self._panel_border_pen.GetColour()
670        elif id == RIBBON_ART_PANEL_BORDER_GRADIENT_COLOUR:
671            return self._panel_border_gradient_pen.GetColour()
672        elif id == RIBBON_ART_PANEL_MINIMISED_BORDER_COLOUR:
673            return self._panel_minimised_border_pen.GetColour()
674        elif id == RIBBON_ART_PANEL_MINIMISED_BORDER_GRADIENT_COLOUR:
675            return self._panel_minimised_border_gradient_pen.GetColour()
676        elif id in [RIBBON_ART_PANEL_LABEL_BACKGROUND_COLOUR, RIBBON_ART_PANEL_LABEL_BACKGROUND_GRADIENT_COLOUR]:
677            return self._panel_label_background_brush.GetColour()
678        elif id == RIBBON_ART_PANEL_LABEL_COLOUR:
679            return self._panel_label_colour
680        elif id == RIBBON_ART_PANEL_MINIMISED_LABEL_COLOUR:
681            return self._panel_minimised_label_colour
682        elif id in [RIBBON_ART_PANEL_HOVER_LABEL_BACKGROUND_COLOUR, RIBBON_ART_PANEL_HOVER_LABEL_BACKGROUND_GRADIENT_COLOUR]:
683            return self._panel_hover_label_background_brush.GetColour()
684        elif id == RIBBON_ART_PANEL_HOVER_LABEL_COLOUR:
685            return self._panel_hover_label_colour
686        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_TOP_COLOUR:
687            return self._panel_active_background_top_colour
688        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR:
689            return self._panel_active_background_top_gradient_colour
690        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_COLOUR:
691            return self._panel_active_background_colour
692        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
693            return self._panel_active_background_gradient_colour
694        elif id == RIBBON_ART_PANEL_BUTTON_FACE_COLOUR:
695            return self._panel_button_face_colour
696        elif id == RIBBON_ART_PANEL_BUTTON_HOVER_FACE_COLOUR:
697            return self._panel_button_hover_face_colour
698        elif id == RIBBON_ART_PAGE_BORDER_COLOUR:
699            return self._page_border_pen.GetColour()
700        elif id == RIBBON_ART_PAGE_BACKGROUND_TOP_COLOUR:
701            return self._page_background_top_colour
702        elif id == RIBBON_ART_PAGE_BACKGROUND_TOP_GRADIENT_COLOUR:
703            return self._page_background_top_gradient_colour
704        elif id == RIBBON_ART_PAGE_BACKGROUND_COLOUR:
705            return self._page_background_colour
706        elif id == RIBBON_ART_PAGE_BACKGROUND_GRADIENT_COLOUR:
707            return self._page_background_gradient_colour
708        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_TOP_COLOUR:
709            return self._page_hover_background_top_colour
710        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR:
711            return self._page_hover_background_top_gradient_colour
712        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_COLOUR:
713            return self._page_hover_background_colour
714        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_GRADIENT_COLOUR:
715            return self._page_hover_background_gradient_colour
716        elif id in [RIBBON_ART_TOOLBAR_BORDER_COLOUR, RIBBON_ART_TOOLBAR_HOVER_BORDER_COLOUR]:
717            return self._toolbar_border_pen.GetColour()
718        elif id == RIBBON_ART_TOOLBAR_FACE_COLOUR:
719            return self._tool_face_colour
720        else:
721            raise Exception("Invalid Colour Ordinal")
722
723
724    def SetColour(self, id, colour):
725        """
726        Set the value of a certain colour setting to the value.
727
728        can be one of the colour values of `RibbonArtSetting`, though not all colour
729        settings will have an affect on every art provider.
730
731        :param `id`: the colour id;
732        :param `colour`: the colour.
733
734        :see: :meth:`~RibbonMSWArtProvider.SetColourScheme`
735        """
736
737        if id == RIBBON_ART_BUTTON_BAR_LABEL_COLOUR:
738            self._button_bar_label_colour = colour
739        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BORDER_COLOUR:
740            self._button_bar_hover_border_pen.SetColour(colour)
741        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_TOP_COLOUR:
742            self._button_bar_hover_background_top_colour = colour
743        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR:
744            self._button_bar_hover_background_top_gradient_colour = colour
745        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_COLOUR:
746            self._button_bar_hover_background_colour = colour
747        elif id == RIBBON_ART_BUTTON_BAR_HOVER_BACKGROUND_GRADIENT_COLOUR:
748            self._button_bar_hover_background_gradient_colour = colour
749        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BORDER_COLOUR:
750            self._button_bar_active_border_pen.SetColour(colour)
751        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_TOP_COLOUR:
752            self._button_bar_active_background_top_colour = colour
753        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR:
754            self._button_bar_active_background_top_gradient_colour = colour
755        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_COLOUR:
756            self._button_bar_active_background_colour = colour
757        elif id == RIBBON_ART_BUTTON_BAR_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
758            self._button_bar_active_background_gradient_colour = colour
759        elif id == RIBBON_ART_GALLERY_BORDER_COLOUR:
760            self._gallery_border_pen.SetColour(colour)
761        elif id == RIBBON_ART_GALLERY_HOVER_BACKGROUND_COLOUR:
762            self._gallery_hover_background_brush.SetColour(colour)
763        elif id == RIBBON_ART_GALLERY_BUTTON_BACKGROUND_COLOUR:
764            self._gallery_button_background_colour = colour
765        elif id == RIBBON_ART_GALLERY_BUTTON_BACKGROUND_GRADIENT_COLOUR:
766            self._gallery_button_background_gradient_colour = colour
767        elif id == RIBBON_ART_GALLERY_BUTTON_BACKGROUND_TOP_COLOUR:
768            self._gallery_button_background_top_brush.SetColour(colour)
769        elif id == RIBBON_ART_GALLERY_BUTTON_FACE_COLOUR:
770            self._gallery_button_face_colour = colour
771
772            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
773                self._gallery_up_bitmap[0] = RibbonLoadPixmap(gallery_left_xpm, colour)
774                self._gallery_down_bitmap[0] = RibbonLoadPixmap(gallery_right_xpm, colour)
775            else:
776                self._gallery_up_bitmap[0] = RibbonLoadPixmap(gallery_up_xpm, colour)
777                self._gallery_down_bitmap[0] = RibbonLoadPixmap(gallery_down_xpm, colour)
778
779            self._gallery_extension_bitmap[0] = RibbonLoadPixmap(gallery_extension_xpm, colour)
780
781        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_COLOUR:
782            self._gallery_button_hover_background_colour = colour
783        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_GRADIENT_COLOUR:
784            self._gallery_button_hover_background_gradient_colour = colour
785        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_BACKGROUND_TOP_COLOUR:
786            self._gallery_button_hover_background_top_brush.SetColour(colour)
787        elif id == RIBBON_ART_GALLERY_BUTTON_HOVER_FACE_COLOUR:
788            self._gallery_button_hover_face_colour = colour
789
790            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
791                self._gallery_up_bitmap[1] = RibbonLoadPixmap(gallery_left_xpm, colour)
792                self._gallery_down_bitmap[1] = RibbonLoadPixmap(gallery_right_xpm, colour)
793            else:
794                self._gallery_up_bitmap[1] = RibbonLoadPixmap(gallery_up_xpm, colour)
795                self._gallery_down_bitmap[1] = RibbonLoadPixmap(gallery_down_xpm, colour)
796
797            self._gallery_extension_bitmap[1] = RibbonLoadPixmap(gallery_extension_xpm, colour)
798
799        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_COLOUR:
800            self._gallery_button_active_background_colour = colour
801        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
802            self._gallery_button_active_background_gradient_colour = colour
803        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_BACKGROUND_TOP_COLOUR:
804            self._gallery_button_background_top_brush.SetColour(colour)
805        elif id == RIBBON_ART_GALLERY_BUTTON_ACTIVE_FACE_COLOUR:
806            self._gallery_button_active_face_colour = colour
807
808            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
809                self._gallery_up_bitmap[2] = RibbonLoadPixmap(gallery_left_xpm, colour)
810                self._gallery_down_bitmap[2] = RibbonLoadPixmap(gallery_right_xpm, colour)
811            else:
812                self._gallery_up_bitmap[2] = RibbonLoadPixmap(gallery_up_xpm, colour)
813                self._gallery_down_bitmap[2] = RibbonLoadPixmap(gallery_down_xpm, colour)
814
815            self._gallery_extension_bitmap[2] = RibbonLoadPixmap(gallery_extension_xpm, colour)
816
817        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_COLOUR:
818            self._gallery_button_disabled_background_colour = colour
819        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_GRADIENT_COLOUR:
820            self._gallery_button_disabled_background_gradient_colour = colour
821        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_BACKGROUND_TOP_COLOUR:
822            self._gallery_button_disabled_background_top_brush.SetColour(colour)
823        elif id == RIBBON_ART_GALLERY_BUTTON_DISABLED_FACE_COLOUR:
824            self._gallery_button_disabled_face_colour = colour
825
826            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
827                self._gallery_up_bitmap[3] = RibbonLoadPixmap(gallery_left_xpm, colour)
828                self._gallery_down_bitmap[3] = RibbonLoadPixmap(gallery_right_xpm, colour)
829            else:
830                self._gallery_up_bitmap[3] = RibbonLoadPixmap(gallery_up_xpm, colour)
831                self._gallery_down_bitmap[3] = RibbonLoadPixmap(gallery_down_xpm, colour)
832
833            self._gallery_extension_bitmap[3] = RibbonLoadPixmap(gallery_extension_xpm, colour)
834
835        elif id == RIBBON_ART_GALLERY_ITEM_BORDER_COLOUR:
836            self._gallery_item_border_pen.SetColour(colour)
837
838        elif id in [RIBBON_ART_TAB_CTRL_BACKGROUND_COLOUR, RIBBON_ART_TAB_CTRL_BACKGROUND_GRADIENT_COLOUR]:
839            self._tab_ctrl_background_brush.SetColour(colour)
840            self._cached_tab_separator_visibility = -1.0
841        elif id == RIBBON_ART_TAB_LABEL_COLOUR:
842            self._tab_label_colour = colour
843        elif id == RIBBON_ART_TAB_SEPARATOR_COLOUR:
844            self._tab_separator_colour = colour
845            self._cached_tab_separator_visibility = -1.0
846        elif id == RIBBON_ART_TAB_SEPARATOR_GRADIENT_COLOUR:
847            self._tab_separator_gradient_colour = colour
848            self._cached_tab_separator_visibility = -1.0
849        elif id in [RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_COLOUR, RIBBON_ART_TAB_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR]:
850            pass
851        elif id == RIBBON_ART_TAB_ACTIVE_BACKGROUND_COLOUR:
852            self._tab_active_background_colour = colour
853        elif id == RIBBON_ART_TAB_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
854            self._tab_active_background_gradient_colour = colour
855        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_TOP_COLOUR:
856            self._tab_hover_background_top_colour = colour
857        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR:
858            self._tab_hover_background_top_gradient_colour = colour
859        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_COLOUR:
860            self._tab_hover_background_colour = colour
861        elif id == RIBBON_ART_TAB_HOVER_BACKGROUND_GRADIENT_COLOUR:
862            self._tab_hover_background_gradient_colour = colour
863        elif id == RIBBON_ART_TAB_BORDER_COLOUR:
864            self._tab_border_pen.SetColour(colour)
865        elif id == RIBBON_ART_PANEL_BORDER_COLOUR:
866            self._panel_border_pen.SetColour(colour)
867        elif id == RIBBON_ART_PANEL_BORDER_GRADIENT_COLOUR:
868            self._panel_border_gradient_pen.SetColour(colour)
869        elif id == RIBBON_ART_PANEL_MINIMISED_BORDER_COLOUR:
870            self._panel_minimised_border_pen.SetColour(colour)
871        elif id == RIBBON_ART_PANEL_MINIMISED_BORDER_GRADIENT_COLOUR:
872            self._panel_minimised_border_gradient_pen.SetColour(colour)
873        elif id in [RIBBON_ART_PANEL_LABEL_BACKGROUND_COLOUR, RIBBON_ART_PANEL_LABEL_BACKGROUND_GRADIENT_COLOUR]:
874            self._panel_label_background_brush.SetColour(colour)
875        elif id == RIBBON_ART_PANEL_LABEL_COLOUR:
876            self._panel_label_colour = colour
877        elif id in [RIBBON_ART_PANEL_HOVER_LABEL_BACKGROUND_COLOUR, RIBBON_ART_PANEL_HOVER_LABEL_BACKGROUND_GRADIENT_COLOUR]:
878            self._panel_hover_label_background_brush.SetColour(colour)
879        elif id == RIBBON_ART_PANEL_HOVER_LABEL_COLOUR:
880            self._panel_hover_label_colour = colour
881        elif id == RIBBON_ART_PANEL_MINIMISED_LABEL_COLOUR:
882            self._panel_minimised_label_colour = colour
883        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_TOP_COLOUR:
884            self._panel_active_background_top_colour = colour
885        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_TOP_GRADIENT_COLOUR:
886            self._panel_active_background_top_gradient_colour = colour
887        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_COLOUR:
888            self._panel_active_background_colour = colour
889        elif id == RIBBON_ART_PANEL_ACTIVE_BACKGROUND_GRADIENT_COLOUR:
890            self._panel_active_background_gradient_colour = colour
891        elif id == RIBBON_ART_PANEL_BUTTON_FACE_COLOUR:
892            self._panel_button_face_colour = colour
893            self._panel_extension_bitmap[0] = RibbonLoadPixmap(panel_extension_xpm, colour)
894        elif id == RIBBON_ART_PANEL_BUTTON_HOVER_FACE_COLOUR:
895            self._panel_button_hover_face_colour = colour
896            self._panel_extension_bitmap[1] = RibbonLoadPixmap(panel_extension_xpm, colour)
897        elif id == RIBBON_ART_PAGE_BORDER_COLOUR:
898            self._page_border_pen.SetColour(colour)
899        elif id == RIBBON_ART_PAGE_BACKGROUND_TOP_COLOUR:
900            self._page_background_top_colour = colour
901        elif id == RIBBON_ART_PAGE_BACKGROUND_TOP_GRADIENT_COLOUR:
902            self._page_background_top_gradient_colour = colour
903        elif id == RIBBON_ART_PAGE_BACKGROUND_COLOUR:
904            self._page_background_colour = colour
905        elif id == RIBBON_ART_PAGE_BACKGROUND_GRADIENT_COLOUR:
906            self._page_background_gradient_colour = colour
907        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_TOP_COLOUR:
908            self._page_hover_background_top_colour = colour
909        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_TOP_GRADIENT_COLOUR:
910            self._page_hover_background_top_gradient_colour = colour
911        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_COLOUR:
912            self._page_hover_background_colour = colour
913        elif id == RIBBON_ART_PAGE_HOVER_BACKGROUND_GRADIENT_COLOUR:
914            self._page_hover_background_gradient_colour = colour
915        elif id in [RIBBON_ART_TOOLBAR_BORDER_COLOUR, RIBBON_ART_TOOLBAR_HOVER_BORDER_COLOUR]:
916            self._toolbar_border_pen.SetColour(colour)
917        elif id == RIBBON_ART_TOOLBAR_FACE_COLOUR:
918            self._tool_face_colour = colour
919            self._toolbar_drop_bitmap = RibbonLoadPixmap(gallery_down_xpm, colour)
920        else:
921            raise Exception("Invalid Colour Ordinal")
922
923
924    def DrawTabCtrlBackground(self, dc, wnd, rect):
925        """
926        Draw the background of the tab region of a ribbon bar.
927
928        :param `dc`: The device context to draw onto;
929        :param `wnd`: The window which is being drawn onto;
930        :param `rect`: The rectangle within which to draw.
931
932        """
933
934        dc.SetPen(wx.TRANSPARENT_PEN)
935
936        dc.SetBrush(self._tab_ctrl_background_brush)
937        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
938
939        dc.SetPen(self._page_border_pen)
940
941        if rect.width > 6:
942            dc.DrawLine(rect.x + 3, rect.y + rect.height - 1, rect.x + rect.width - 3, rect.y + rect.height - 1)
943        else:
944            dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1)
945
946
947    def DrawTab(self, dc, wnd, tab):
948        """
949        Draw a single tab in the tab region of a ribbon bar.
950
951        :param `dc`: The device context to draw onto;
952        :param `wnd`: The window which is being drawn onto (not the :class:`~wx.lib.agw.ribbon.page.RibbonPage` associated
953         with the tab being drawn);
954        :param `tab`: The rectangle within which to draw, and also the tab label, icon, and
955         state (active and/or hovered). The drawing rectangle will be entirely within a
956         rectangle on the same device context previously painted with :meth:`~RibbonMSWArtProvider.DrawTabCtrlBackground`.
957         The rectangle's width will be at least the minimum value returned by :meth:`~RibbonMSWArtProvider.GetBarTabWidth`,
958         and height will be the value returned by :meth:`~RibbonMSWArtProvider.GetTabCtrlHeight`.
959
960        """
961
962        if tab.rect.height <= 2:
963            return
964
965        if tab.active or tab.hovered:
966            if tab.active:
967                background = wx.Rect(*tab.rect)
968                background.x += 2
969                background.y += 2
970                background.width -= 4
971                background.height -= 2
972
973                dc.GradientFillLinear(background, self._tab_active_background_colour,
974                                      self._tab_active_background_gradient_colour, wx.SOUTH)
975
976                # TODO: active and hovered
977
978            elif tab.hovered:
979                background = wx.Rect(*tab.rect)
980                background.x += 2
981                background.y += 2
982                background.width -= 4
983                background.height -= 3
984                h = background.height
985                background.height /= 2
986                dc.GradientFillLinear(background, self._tab_hover_background_top_colour,
987                                      self._tab_hover_background_top_gradient_colour, wx.SOUTH)
988
989                background.y += background.height
990                background.height = h - background.height
991                dc.GradientFillLinear(background, self._tab_hover_background_colour,
992                                      self._tab_hover_background_gradient_colour, wx.SOUTH)
993
994            border_points = [wx.Point() for i in range(6)]
995            border_points[0] = wx.Point(1, tab.rect.height - 2)
996            border_points[1] = wx.Point(1, 3)
997            border_points[2] = wx.Point(3, 1)
998            border_points[3] = wx.Point(tab.rect.width - 4, 1)
999            border_points[4] = wx.Point(tab.rect.width - 2, 3)
1000            border_points[5] = wx.Point(tab.rect.width - 2, tab.rect.height - 1)
1001
1002            dc.SetPen(self._tab_border_pen)
1003            dc.DrawLines(border_points, tab.rect.x, tab.rect.y)
1004
1005            if tab.active:
1006                # Give the tab a curved outward border at the bottom
1007                dc.DrawPoint(tab.rect.x, tab.rect.y + tab.rect.height - 2)
1008                dc.DrawPoint(tab.rect.x + tab.rect.width - 1, tab.rect.y + tab.rect.height - 2)
1009
1010                p = wx.Pen(self._tab_active_background_gradient_colour)
1011                dc.SetPen(p)
1012
1013                # Technically the first two points are the wrong colour, but they're near enough
1014                dc.DrawPoint(tab.rect.x + 1, tab.rect.y + tab.rect.height - 2)
1015                dc.DrawPoint(tab.rect.x + tab.rect.width - 2, tab.rect.y + tab.rect.height - 2)
1016                dc.DrawPoint(tab.rect.x + 1, tab.rect.y + tab.rect.height - 1)
1017                dc.DrawPoint(tab.rect.x, tab.rect.y + tab.rect.height - 1)
1018                dc.DrawPoint(tab.rect.x + tab.rect.width - 2, tab.rect.y + tab.rect.height - 1)
1019                dc.DrawPoint(tab.rect.x + tab.rect.width - 1, tab.rect.y + tab.rect.height - 1)
1020
1021        if self._flags & RIBBON_BAR_SHOW_PAGE_ICONS:
1022            icon = tab.page.GetIcon()
1023
1024            if icon.IsOk():
1025                x = tab.rect.x + 4
1026                if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS == 0:
1027                    x = tab.rect.x + (tab.rect.width - icon.GetWidth()) / 2
1028
1029                dc.DrawBitmap(icon, x, tab.rect.y + 1 + (tab.rect.height - 1 - icon.GetHeight()) / 2, True)
1030
1031        if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS:
1032            label = tab.page.GetLabel()
1033            if label.strip():
1034                dc.SetFont(self._tab_label_font)
1035                dc.SetTextForeground(self._tab_label_colour)
1036                dc.SetBackgroundMode(wx.TRANSPARENT)
1037
1038                text_width, text_height = dc.GetTextExtent(label)
1039                width = tab.rect.width - 5
1040                x = tab.rect.x + 3
1041
1042                if self._flags & RIBBON_BAR_SHOW_PAGE_ICONS:
1043                    x += 3 + tab.page.GetIcon().GetWidth()
1044                    width -= 3 + tab.page.GetIcon().GetWidth()
1045
1046                y = tab.rect.y + (tab.rect.height - text_height) / 2
1047
1048                if width <= text_width:
1049                    dc.SetClippingRegion(x, tab.rect.y, width, tab.rect.height)
1050                    dc.DrawText(label, x, y)
1051                else:
1052                    dc.DrawText(label, x + (width - text_width) / 2 + 1, y)
1053
1054
1055    def DrawTabSeparator(self, dc, wnd, rect, visibility):
1056        """
1057        Draw a separator between two tabs in a ribbon bar.
1058
1059        :param `dc`: The device context to draw onto;
1060        :param `wnd`: The window which is being drawn onto;
1061        :param `rect`: The rectangle within which to draw, which will be entirely
1062         within a rectangle on the same device context previously painted with
1063         :meth:`~RibbonMSWArtProvider.DrawTabCtrlBackground`;
1064        :param `visibility`: The opacity with which to draw the separator. Values
1065         are in the range [0, 1], with 0 being totally transparent, and 1 being totally
1066         opaque.
1067
1068        """
1069
1070        if visibility <= 0.0:
1071            return
1072
1073        if visibility > 1.0:
1074            visibility = 1.0
1075
1076        # The tab separator is relatively expensive to draw (for its size), and is
1077        # usually drawn multiple times sequentially (in different positions), so it
1078        # makes sense to draw it once and cache it.
1079        if not self._cached_tab_separator.IsOk() or self._cached_tab_separator.GetSize() != rect.GetSize() or \
1080           visibility != self._cached_tab_separator_visibility:
1081
1082            size = wx.Rect(0, 0, *rect.GetSize())
1083            self.ReallyDrawTabSeparator(wnd, size, visibility)
1084
1085        dc.DrawBitmap(self._cached_tab_separator, rect.x, rect.y, False)
1086
1087
1088    def ReallyDrawTabSeparator(self, wnd, rect, visibility):
1089
1090        if not self._cached_tab_separator.IsOk() or self._cached_tab_separator.GetSize() != rect.GetSize():
1091            self._cached_tab_separator = wx.Bitmap(*rect.GetSize())
1092
1093        dc = wx.MemoryDC(self._cached_tab_separator)
1094        self.DrawTabCtrlBackground(dc, wnd, rect)
1095
1096        x = rect.x + rect.width / 2
1097        h = float(rect.height - 1)
1098
1099        r1 = self._tab_ctrl_background_brush.GetColour().Red() * (1.0 - visibility) + 0.5
1100        g1 = self._tab_ctrl_background_brush.GetColour().Green() * (1.0 - visibility) + 0.5
1101        b1 = self._tab_ctrl_background_brush.GetColour().Blue() * (1.0 - visibility) + 0.5
1102        r2 = self._tab_separator_colour.Red()
1103        g2 = self._tab_separator_colour.Green()
1104        b2 = self._tab_separator_colour.Blue()
1105        r3 = self._tab_separator_gradient_colour.Red()
1106        g3 = self._tab_separator_gradient_colour.Green()
1107        b3 = self._tab_separator_gradient_colour.Blue()
1108
1109        for i in range(rect.height-1):
1110
1111            p = float(i)/h
1112
1113            r = int((p * r3 + (1.0 - p) * r2) * visibility + r1)
1114            g = int((p * g3 + (1.0 - p) * g2) * visibility + g1)
1115            b = int((p * b3 + (1.0 - p) * b2) * visibility + b1)
1116
1117            P = wx.Pen(wx.Colour(r, g, b))
1118            dc.SetPen(P)
1119            dc.DrawPoint(x, rect.y + i)
1120
1121        self._cached_tab_separator_visibility = visibility
1122
1123
1124    def DrawPartialPageBackground(self, dc, wnd, rect, allow_hovered_or_page=True, offset=None, hovered=False):
1125
1126        if isinstance(allow_hovered_or_page, bool):
1127            self.DrawPartialPageBackground2(dc, wnd, rect, allow_hovered_or_page)
1128        else:
1129            self.DrawPartialPageBackground1(dc, wnd, rect, allow_hovered_or_page, offset, hovered)
1130
1131
1132    def DrawPartialPageBackground1(self, dc, wnd, rect, page, offset, hovered=False):
1133
1134        background = wx.Rect(0, 0, *page.GetSize())
1135        background = page.AdjustRectToIncludeScrollButtons(background)
1136        background.height -= 2
1137
1138        # Page background isn't dependant upon the width of the page
1139        # (at least not the part of it intended to be painted by this
1140        # function). Set to wider than the page itself for when externally
1141        # expanded panels need a background - the expanded panel can be wider
1142        # than the bar.
1143
1144        background.x = 0
1145        background.width = 10000
1146
1147        # upper_rect, lower_rect, paint_rect are all in page co-ordinates
1148        upper_rect = wx.Rect(*background)
1149        upper_rect.height /= 5
1150
1151        lower_rect = wx.Rect(*background)
1152        lower_rect.y += upper_rect.height
1153        lower_rect.height -= upper_rect.height
1154
1155        paint_rect = wx.Rect(*rect)
1156        paint_rect.x += offset.x
1157        paint_rect.y += offset.y
1158
1159        if hovered:
1160            bg_top = self._page_hover_background_top_colour
1161            bg_top_grad = self._page_hover_background_top_gradient_colour
1162            bg_btm = self._page_hover_background_colour
1163            bg_btm_grad = self._page_hover_background_gradient_colour
1164        else:
1165            bg_top = self._page_background_top_colour
1166            bg_top_grad = self._page_background_top_gradient_colour
1167            bg_btm = self._page_background_colour
1168            bg_btm_grad = self._page_background_gradient_colour
1169
1170        if paint_rect.Intersects(upper_rect):
1171            rect = wx.Rect(*upper_rect)
1172            rect.Intersect(paint_rect)
1173            rect.x -= offset.x
1174            rect.y -= offset.y
1175            starting_colour = RibbonInterpolateColour(bg_top, bg_top_grad,
1176                                                      paint_rect.y, upper_rect.y,
1177                                                      upper_rect.y + upper_rect.height)
1178            ending_colour = RibbonInterpolateColour(bg_top, bg_top_grad,
1179                                                    paint_rect.y + paint_rect.height, upper_rect.y,
1180                                                    upper_rect.y + upper_rect.height)
1181            dc.GradientFillLinear(rect, starting_colour, ending_colour, wx.SOUTH)
1182
1183
1184        if paint_rect.Intersects(lower_rect):
1185            rect = wx.Rect(*lower_rect)
1186            rect.Intersect(paint_rect)
1187            rect.x -= offset.x
1188            rect.y -= offset.y
1189            starting_colour = RibbonInterpolateColour(bg_btm, bg_btm_grad,
1190                                                      paint_rect.y, lower_rect.y,
1191                                                      lower_rect.y + lower_rect.height)
1192            ending_colour = RibbonInterpolateColour(bg_btm, bg_btm_grad,
1193                                                    paint_rect.y + paint_rect.height,
1194                                                    lower_rect.y, lower_rect.y + lower_rect.height)
1195
1196            dc.GradientFillLinear(rect, starting_colour, ending_colour, wx.SOUTH)
1197
1198
1199    def DrawPageBackground(self, dc, wnd, rect):
1200        """
1201        Draw the background of a ribbon page.
1202
1203        :param `dc`: The device context to draw onto;
1204        :param `wnd`: The window which is being drawn onto (which is commonly the
1205         :class:`~wx.lib.agw.ribbon.page.RibbonPage` whose background is being drawn, but doesn't have to be);
1206        :param `rect`: The rectangle within which to draw.
1207
1208        :see: :meth:`~RibbonMSWArtProvider.GetPageBackgroundRedrawArea`
1209        """
1210
1211        dc.SetPen(wx.TRANSPARENT_PEN)
1212        dc.SetBrush(self._tab_ctrl_background_brush)
1213
1214        edge = wx.Rect(*rect)
1215
1216        edge.width = 2
1217        dc.DrawRectangle(edge.x, edge.y, edge.width, edge.height)
1218
1219        edge.x += rect.width - 2
1220        dc.DrawRectangle(edge.x, edge.y, edge.width, edge.height)
1221
1222        edge = wx.Rect(*rect)
1223        edge.height = 2
1224        edge.y += (rect.height - edge.height)
1225        dc.DrawRectangle(edge.x, edge.y, edge.width, edge.height)
1226
1227        background = wx.Rect(*rect)
1228        background.x += 2
1229        background.width -= 4
1230        background.height -= 2
1231
1232        background.height /= 5
1233        dc.GradientFillLinear(background, self._page_background_top_colour,
1234                              self._page_background_top_gradient_colour, wx.SOUTH)
1235
1236        background.y += background.height
1237        background.height = rect.height - 2 - background.height
1238        dc.GradientFillLinear(background, self._page_background_colour,
1239                              self._page_background_gradient_colour, wx.SOUTH)
1240
1241        border_points = [wx.Point() for i in range(8)]
1242        border_points[0] = wx.Point(2, 0)
1243        border_points[1] = wx.Point(1, 1)
1244        border_points[2] = wx.Point(1, rect.height - 4)
1245        border_points[3] = wx.Point(3, rect.height - 2)
1246        border_points[4] = wx.Point(rect.width - 4, rect.height - 2)
1247        border_points[5] = wx.Point(rect.width - 2, rect.height - 4)
1248        border_points[6] = wx.Point(rect.width - 2, 1)
1249        border_points[7] = wx.Point(rect.width - 4, -1)
1250
1251        dc.SetPen(self._page_border_pen)
1252        dc.DrawLines(border_points, rect.x, rect.y)
1253
1254
1255    def DrawScrollButton(self, dc, wnd, rect_, style):
1256        """
1257        Draw a ribbon-style scroll button.
1258
1259        :param `dc`: The device context to draw onto;
1260        :param `wnd`: The window which is being drawn onto;
1261        :param `rect`: The rectangle within which to draw. The size of this rectangle
1262         will be at least the size returned by :meth:`~RibbonMSWArtProvider.GetScrollButtonMinimumSize` for a
1263         scroll button with the same style. For tab scroll buttons, this rectangle
1264         will be entirely within a rectangle on the same device context previously
1265         painted with :meth:`~RibbonMSWArtProvider.DrawTabCtrlBackground`, but this is not guaranteed for other
1266         types of button (for example, page scroll buttons will not be painted on an
1267         area previously painted with :meth:`~RibbonMSWArtProvider.DrawPageBackground` );
1268        :param `style`: A combination of flags from `RibbonScrollButtonStyle`,
1269         including a direction, a for flag, and one or more states.
1270
1271        """
1272
1273        rect = wx.Rect(*rect_)
1274
1275        if (style & RIBBON_SCROLL_BTN_FOR_MASK) == RIBBON_SCROLL_BTN_FOR_PAGE:
1276
1277            # Page scroll buttons do not have the luxury of rendering on top of anything
1278            # else, and their size includes some padding, hence the background painting
1279            # and size adjustment.
1280            dc.SetPen(wx.TRANSPARENT_PEN)
1281            dc.SetBrush(self._tab_ctrl_background_brush)
1282            dc.DrawRectangle(rect)
1283            dc.SetClippingRegion(rect)
1284
1285            result = style & RIBBON_SCROLL_BTN_DIRECTION_MASK
1286
1287            if result == RIBBON_SCROLL_BTN_LEFT:
1288                rect.x += 1
1289            elif result == RIBBON_SCROLL_BTN_RIGHT:
1290                rect.y -= 1
1291                rect.width -= 1
1292            elif result == RIBBON_SCROLL_BTN_UP:
1293                rect.x += 1
1294                rect.y -= 1
1295                rect.width -= 2
1296                rect.height += 1
1297            elif result == RIBBON_SCROLL_BTN_DOWN:
1298                rect.x += 1
1299                rect.width -= 2
1300                rect.height -= 1
1301
1302        background = wx.Rect(*rect)
1303        background.x += 1
1304        background.y += 1
1305        background.width -= 2
1306        background.height -= 2
1307
1308        if style & RIBBON_SCROLL_BTN_UP:
1309            background.height /= 2
1310        else:
1311            background.height /= 5
1312
1313        dc.GradientFillLinear(background, self._page_background_top_colour,
1314                              self._page_background_top_gradient_colour, wx.SOUTH)
1315
1316        background.y += background.height
1317        background.height = rect.height - 2 - background.height
1318        dc.GradientFillLinear(background, self._page_background_colour,
1319                              self._page_background_gradient_colour, wx.SOUTH)
1320
1321        border_points = [wx.Point() for i in range(7)]
1322        result = style & RIBBON_SCROLL_BTN_DIRECTION_MASK
1323
1324        if result == RIBBON_SCROLL_BTN_LEFT:
1325            border_points[0] = wx.Point(2, 0)
1326            border_points[1] = wx.Point(rect.width - 1, 0)
1327            border_points[2] = wx.Point(rect.width - 1, rect.height - 1)
1328            border_points[3] = wx.Point(2, rect.height - 1)
1329            border_points[4] = wx.Point(0, rect.height - 3)
1330            border_points[5] = wx.Point(0, 2)
1331
1332        elif result == RIBBON_SCROLL_BTN_RIGHT:
1333            border_points[0] = wx.Point(0, 0)
1334            border_points[1] = wx.Point(rect.width - 3, 0)
1335            border_points[2] = wx.Point(rect.width - 1, 2)
1336            border_points[3] = wx.Point(rect.width - 1, rect.height - 3)
1337            border_points[4] = wx.Point(rect.width - 3, rect.height - 1)
1338            border_points[5] = wx.Point(0, rect.height - 1)
1339
1340        elif result == RIBBON_SCROLL_BTN_UP:
1341            border_points[0] = wx.Point(2, 0)
1342            border_points[1] = wx.Point(rect.width - 3, 0)
1343            border_points[2] = wx.Point(rect.width - 1, 2)
1344            border_points[3] = wx.Point(rect.width - 1, rect.height - 1)
1345            border_points[4] = wx.Point(0, rect.height - 1)
1346            border_points[5] = wx.Point(0, 2)
1347
1348        elif result == RIBBON_SCROLL_BTN_DOWN:
1349            border_points[0] = wx.Point(0, 0)
1350            border_points[1] = wx.Point(rect.width - 1, 0)
1351            border_points[2] = wx.Point(rect.width - 1, rect.height - 3)
1352            border_points[3] = wx.Point(rect.width - 3, rect.height - 1)
1353            border_points[4] = wx.Point(2, rect.height - 1)
1354            border_points[5] = wx.Point(0, rect.height - 3)
1355
1356        border_points[6] = border_points[0]
1357
1358        dc.SetPen(self._page_border_pen)
1359        dc.DrawLines(border_points, rect.x, rect.y)
1360
1361        # NB: Code for handling hovered/active state is temporary
1362        arrow_points = [wx.Point() for i in range(3)]
1363        result = style & RIBBON_SCROLL_BTN_DIRECTION_MASK
1364
1365        if result == RIBBON_SCROLL_BTN_LEFT:
1366            arrow_points[0] = wx.Point(rect.width / 2 - 2, rect.height / 2)
1367            if style & RIBBON_SCROLL_BTN_ACTIVE:
1368                arrow_points[0].y += 1
1369            arrow_points[1] = arrow_points[0] + wx.Point(3, -3)
1370            arrow_points[2] = arrow_points[0] + wx.Point(3,  3)
1371
1372        elif result == RIBBON_SCROLL_BTN_RIGHT:
1373            arrow_points[0] = wx.Point(rect.width / 2 + 2, rect.height / 2)
1374            if style & RIBBON_SCROLL_BTN_ACTIVE:
1375                arrow_points[0].y += 1
1376            arrow_points[1] = arrow_points[0] - wx.Point(3,  3)
1377            arrow_points[2] = arrow_points[0] - wx.Point(3, -3)
1378
1379        elif result == RIBBON_SCROLL_BTN_UP:
1380            arrow_points[0] = wx.Point(rect.width / 2, rect.height / 2 - 2)
1381            if style & RIBBON_SCROLL_BTN_ACTIVE:
1382                arrow_points[0].y += 1
1383            arrow_points[1] = arrow_points[0] + wx.Point( 3, 3)
1384            arrow_points[2] = arrow_points[0] + wx.Point(-3, 3)
1385
1386        elif result == RIBBON_SCROLL_BTN_DOWN:
1387            arrow_points[0] = wx.Point(rect.width / 2, rect.height / 2 + 2)
1388            if style & RIBBON_SCROLL_BTN_ACTIVE:
1389                arrow_points[0].y += 1
1390            arrow_points[1] = arrow_points[0] - wx.Point( 3, 3)
1391            arrow_points[2] = arrow_points[0] - wx.Point(-3, 3)
1392
1393        dc.SetPen(wx.TRANSPARENT_PEN)
1394        B = wx.Brush((style & RIBBON_SCROLL_BTN_HOVERED and [self._tab_active_background_colour] or [self._tab_label_colour])[0])
1395        dc.SetBrush(B)
1396        dc.DrawPolygon(arrow_points, rect.x, rect.y)
1397
1398
1399    def DrawDropdownArrow(self, dc, x, y, colour):
1400
1401        arrow_points = [wx.Point() for i in range(3)]
1402        brush = wx.Brush(colour)
1403        arrow_points[0] = wx.Point(1, 2)
1404        arrow_points[1] = arrow_points[0] + wx.Point(-3, -3)
1405        arrow_points[2] = arrow_points[0] + wx.Point( 3, -3)
1406        dc.SetPen(wx.TRANSPARENT_PEN)
1407        dc.SetBrush(brush)
1408        dc.DrawPolygon(arrow_points, x, y)
1409
1410
1411    def RemovePanelPadding(self, rect):
1412
1413        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1414            rect.y += 1
1415            rect.height -= 2
1416        else:
1417            rect.x += 1
1418            rect.width -= 2
1419
1420        return rect
1421
1422
1423    def DrawPanelBackground(self, dc, wnd, rect):
1424        """
1425        Draw the background and chrome for a ribbon panel.
1426
1427        This should draw the border, background, label, and any other items of a panel
1428        which are outside the client area of a panel. Note that when a panel is
1429        minimised, this function is not called - only :meth:`~RibbonMSWArtProvider.DrawMinimisedPanel` is called,
1430        so a background should be explicitly painted by that if required.
1431
1432        :param `dc`: The device context to draw onto;
1433        :param `wnd`: The window which is being drawn onto, which is always the panel
1434         whose background and chrome is being drawn. The panel label and other panel
1435         attributes can be obtained by querying this;
1436        :param `rect`: The rectangle within which to draw.
1437
1438        """
1439
1440        self.DrawPartialPageBackground(dc, wnd, rect, False)
1441
1442        true_rect = wx.Rect(*rect)
1443        true_rect = self.RemovePanelPadding(true_rect)
1444
1445        dc.SetFont(self._panel_label_font)
1446        dc.SetPen(wx.TRANSPARENT_PEN)
1447
1448        has_ext_button = wnd.HasExtButton()
1449
1450        if wnd.IsHovered():
1451            dc.SetBrush(self._panel_hover_label_background_brush)
1452            dc.SetTextForeground(self._panel_hover_label_colour)
1453        else:
1454            dc.SetBrush(self._panel_label_background_brush)
1455            dc.SetTextForeground(self._panel_label_colour)
1456
1457        label_rect = wx.Rect(*true_rect)
1458        label = wnd.GetLabel().strip()
1459        clip_label = False
1460        label_size = wx.Size(*dc.GetTextExtent(label))
1461
1462        label_rect.SetX(label_rect.GetX() + 1)
1463        label_rect.SetWidth(label_rect.GetWidth() - 2)
1464        label_rect.SetHeight(label_size.GetHeight() + 2)
1465        label_rect.SetY(true_rect.GetBottom() - label_rect.GetHeight())
1466        label_height = label_rect.GetHeight()
1467
1468        label_bg_rect = wx.Rect(*label_rect)
1469
1470        if has_ext_button:
1471            label_rect.SetWidth(label_rect.GetWidth() - 13)
1472
1473        if label_size.GetWidth() > label_rect.GetWidth():
1474            # Test if there is enough length for 3 letters and ...
1475            new_label = label[0:3] + "..."
1476            label_size = wx.Size(*dc.GetTextExtent(new_label))
1477
1478            if label_size.GetWidth() > label_rect.GetWidth():
1479                # Not enough room for three characters and ...
1480                # Display the entire label and just crop it
1481                clip_label = True
1482            else:
1483                # Room for some characters and ...
1484                # Display as many characters as possible and append ...
1485                for l in range(len(label)-1, 3, -1):
1486                    new_label = label[0:l] + "..."
1487                    label_size = wx.Size(*dc.GetTextExtent(new_label))
1488                    if label_size.GetWidth() <= label_rect.GetWidth():
1489                        label = new_label
1490                        break
1491
1492        dc.DrawRectangle(label_rect)
1493
1494        if clip_label:
1495            clip = wx.DCClipper(dc, label_rect)
1496            dc.DrawText(label, label_rect.x, label_rect.y + (label_rect.GetHeight() - label_size.GetHeight()) / 2)
1497        else:
1498            dc.DrawText(label, label_rect.x + (label_rect.GetWidth() - label_size.GetWidth()) / 2,
1499                        label_rect.y + (label_rect.GetHeight() - label_size.GetHeight()) / 2)
1500
1501        if has_ext_button:
1502            if wnd.IsExtButtonHovered():
1503                dc.SetPen(self._panel_hover_button_border_pen)
1504                dc.SetBrush(self._panel_hover_button_background_brush)
1505                dc.DrawRoundedRectangle(label_rect.GetRight(), label_rect.GetBottom() - 13, 13, 13, 1)
1506                dc.DrawBitmap(self._panel_extension_bitmap[1], label_rect.GetRight() + 3, label_rect.GetBottom() - 10, True)
1507            else:
1508                dc.DrawBitmap(self._panel_extension_bitmap[0], label_rect.GetRight() + 3, label_rect.GetBottom() - 10, True)
1509
1510        if wnd.IsHovered():
1511            client_rect = wx.Rect(*true_rect)
1512            client_rect.x += 1
1513            client_rect.width -= 2
1514            client_rect.y += 1
1515            client_rect.height -= 2 + label_height
1516            self.DrawPartialPageBackground(dc, wnd, client_rect, True)
1517
1518        self.DrawPanelBorder(dc, true_rect, self._panel_border_pen, self._panel_border_gradient_pen)
1519
1520
1521    def GetPanelExtButtonArea(self, dc, wnd, rect):
1522        """
1523        Retrieve the extension button area rectangle.
1524
1525        :param `dc`: The device context used to measure text extents;
1526        :param `wnd`: The panel where the extension button resides;
1527        :param `rect`: The panel client rectangle.
1528        """
1529
1530        true_rect = wx.Rect(self.RemovePanelPadding(rect))
1531        true_rect = wx.Rect(true_rect.GetRight()-13, true_rect.GetBottom()-13, 13, 13)
1532        return true_rect
1533
1534
1535    def DrawGalleryBackground(self, dc, wnd, rect):
1536        """
1537        Draw the background and chrome for a :class:`~wx.lib.agw.ribbon.gallery.RibbonGallery` control.
1538
1539        This should draw the border, brackground, scroll buttons, extension button, and
1540        any other UI elements which are not attached to a specific gallery item.
1541
1542        :param `dc`: The device context to draw onto;
1543        :param `wnd`: The window which is being drawn onto, which is always the gallery
1544         whose background and chrome is being drawn. Attributes used during drawing like
1545         the gallery hover state and individual button states can be queried from this
1546         parameter by :meth:`RibbonGallery.IsHovered() <lib.agw.ribbon.gallery.RibbonGallery.IsHovered>`,
1547         :meth:`RibbonGallery.GetExtensionButtonState() <lib.agw.ribbon.gallery.RibbonGallery.GetExtensionButtonState>`,
1548         :meth:`RibbonGallery.GetUpButtonState() <lib.agw.ribbon.gallery.RibbonGallery.GetUpButtonState>`, and
1549         :meth:`RibbonGallery.GetDownButtonState() <lib.agw.ribbon.gallery.RibbonGallery.GetDownButtonState>`;
1550        :param `rect`: The rectangle within which to draw. This rectangle is the entire
1551         area of the gallery control, not just the client rectangle.
1552        """
1553
1554        self.DrawPartialPageBackground(dc, wnd, rect)
1555
1556        if wnd.IsHovered():
1557            dc.SetPen(wx.TRANSPARENT_PEN)
1558            dc.SetBrush(self._gallery_hover_background_brush)
1559            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1560                dc.DrawRectangle(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 16)
1561            else:
1562                dc.DrawRectangle(rect.x + 1, rect.y + 1, rect.width - 16, rect.height - 2)
1563
1564        dc.SetPen(self._gallery_border_pen)
1565        # Outline
1566        dc.DrawLine(rect.x + 1, rect.y, rect.x + rect.width - 1, rect.y)
1567        dc.DrawLine(rect.x, rect.y + 1, rect.x, rect.y + rect.height - 1)
1568        dc.DrawLine(rect.x + 1, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1)
1569        dc.DrawLine(rect.x + rect.width - 1, rect.y + 1, rect.x + rect.width - 1, rect.y + rect.height - 1)
1570
1571        self.DrawGalleryBackgroundCommon(dc, wnd, rect)
1572
1573
1574    def DrawGalleryBackgroundCommon(self, dc, wnd, rect):
1575
1576        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1577            # Divider between items and buttons
1578            dc.DrawLine(rect.x, rect.y + rect.height - 15, rect.x + rect.width, rect.y + rect.height - 15)
1579
1580            up_btn = wx.Rect(rect.x, rect.y + rect.height - 15, rect.width / 3, 15)
1581            down_btn = wx.Rect(up_btn.GetRight() + 1, up_btn.GetTop(), up_btn.GetWidth(), up_btn.GetHeight())
1582            dc.DrawLine(down_btn.GetLeft(), down_btn.GetTop(), down_btn.GetLeft(), down_btn.GetBottom())
1583            ext_btn = wx.Rect(down_btn.GetRight() + 1, up_btn.GetTop(), rect.width - up_btn.GetWidth() - down_btn.GetWidth() - 1, up_btn.GetHeight())
1584            dc.DrawLine(ext_btn.GetLeft(), ext_btn.GetTop(), ext_btn.GetLeft(), ext_btn.GetBottom())
1585
1586        else:
1587            # Divider between items and buttons
1588            dc.DrawLine(rect.x + rect.width - 15, rect.y, rect.x + rect.width - 15, rect.y + rect.height)
1589
1590            up_btn = wx.Rect(rect.x + rect.width - 15, rect.y, 15, rect.height / 3)
1591            down_btn = wx.Rect(up_btn.GetLeft(), up_btn.GetBottom() + 1, up_btn.GetWidth(), up_btn.GetHeight())
1592            dc.DrawLine(down_btn.GetLeft(), down_btn.GetTop(), down_btn.GetRight(), down_btn.GetTop())
1593            ext_btn = wx.Rect(up_btn.GetLeft(), down_btn.GetBottom() + 1, up_btn.GetWidth(), rect.height - up_btn.GetHeight() - down_btn.GetHeight() - 1)
1594            dc.DrawLine(ext_btn.GetLeft(), ext_btn.GetTop(), ext_btn.GetRight(), ext_btn.GetTop())
1595
1596        self.DrawGalleryButton(dc, up_btn, wnd.GetUpButtonState(), self._gallery_up_bitmap)
1597        self.DrawGalleryButton(dc, down_btn, wnd.GetDownButtonState(), self._gallery_down_bitmap)
1598        self.DrawGalleryButton(dc, ext_btn, wnd.GetExtensionButtonState(), self._gallery_extension_bitmap)
1599
1600
1601    def DrawGalleryButton(self, dc, rect, state, bitmaps):
1602
1603        if state == RIBBON_GALLERY_BUTTON_NORMAL:
1604            btn_top_brush = self._gallery_button_background_top_brush
1605            btn_colour = self._gallery_button_background_colour
1606            btn_grad_colour = self._gallery_button_background_gradient_colour
1607            btn_bitmap = bitmaps[0]
1608        elif state == RIBBON_GALLERY_BUTTON_HOVERED:
1609            btn_top_brush = self._gallery_button_hover_background_top_brush
1610            btn_colour = self._gallery_button_hover_background_colour
1611            btn_grad_colour = self._gallery_button_hover_background_gradient_colour
1612            btn_bitmap = bitmaps[1]
1613        elif state == RIBBON_GALLERY_BUTTON_ACTIVE:
1614            btn_top_brush = self._gallery_button_active_background_top_brush
1615            btn_colour = self._gallery_button_active_background_colour
1616            btn_grad_colour = self._gallery_button_active_background_gradient_colour
1617            btn_bitmap = bitmaps[2]
1618        elif state == RIBBON_GALLERY_BUTTON_DISABLED:
1619            btn_top_brush = self._gallery_button_disabled_background_top_brush
1620            btn_colour = self._gallery_button_disabled_background_colour
1621            btn_grad_colour = self._gallery_button_disabled_background_gradient_colour
1622            btn_bitmap = bitmaps[3]
1623
1624        rect.x += 1
1625        rect.y += 1
1626
1627        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1628            rect.width -= 1
1629            rect.height -= 2
1630        else:
1631            rect.width -= 2
1632            rect.height -= 1
1633
1634        dc.SetPen(wx.TRANSPARENT_PEN)
1635        dc.SetBrush(btn_top_brush)
1636        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height / 2)
1637
1638        lower = wx.Rect(*rect)
1639        lower.height = (lower.height + 1) / 2
1640        lower.y += rect.height - lower.height
1641        dc.GradientFillLinear(lower, btn_colour, btn_grad_colour, wx.SOUTH)
1642
1643        dc.DrawBitmap(btn_bitmap, rect.x + rect.width / 2 - 2, lower.y - 2, True)
1644
1645
1646    def DrawGalleryItemBackground(self, dc, wnd, rect, item):
1647        """
1648        Draw the background of a single item in a :class:`~wx.lib.agw.ribbon.gallery.RibbonGallery` control.
1649
1650        This is painted on top of a gallery background, and behind the items bitmap.
1651        Unlike :meth:`~RibbonMSWArtProvider.DrawButtonBarButton` and :meth:`~RibbonMSWArtProvider.DrawTool`, it is not expected to draw the
1652        item bitmap - that is done by the gallery control itself.
1653
1654        :param `dc`: The device context to draw onto;
1655        :param `wnd`: The window which is being drawn onto, which is always the gallery
1656         which contains the item being drawn;
1657        :param `rect`: The rectangle within which to draw. The size of this rectangle
1658         will be the size of the item's bitmap, expanded by gallery item padding values
1659         (``RIBBON_ART_GALLERY_BITMAP_PADDING_LEFT_SIZE``, ``RIBBON_ART_GALLERY_BITMAP_PADDING_RIGHT_SIZE``,
1660         ``RIBBON_ART_GALLERY_BITMAP_PADDING_TOP_SIZE``, and ``RIBBON_ART_GALLERY_BITMAP_PADDING_BOTTOM_SIZE``).
1661         The drawing rectangle will be entirely within a rectangle on the same device
1662         context previously painted with :meth:`~RibbonMSWArtProvider.DrawGalleryBackground`;
1663        :param `item`: The item whose background is being painted. Typically the background
1664         will vary if the item is hovered, active, or selected; :meth:`RibbonGallery.GetSelection() <lib.agw.ribbon.gallery.RibbonGallery.GetSelection>`,
1665         :meth:`RibbonGallery.GetActiveItem() <lib.agw.ribbon.gallery.RibbonGallery.GetActiveItem>`, and
1666         :meth:`RibbonGallery.GetHoveredItem() <lib.agw.ribbon.gallery.RibbonGallery.GetHoveredItem>` can be
1667         called to test if the given item is in one of these states.
1668
1669        """
1670
1671        if wnd.GetHoveredItem() != item and wnd.GetActiveItem() != item and \
1672           wnd.GetSelection() != item:
1673            return
1674
1675        dc.SetPen(self._gallery_item_border_pen)
1676        dc.DrawLine(rect.x + 1, rect.y, rect.x + rect.width - 1, rect.y)
1677        dc.DrawLine(rect.x, rect.y + 1, rect.x, rect.y + rect.height - 1)
1678        dc.DrawLine(rect.x + 1, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1)
1679        dc.DrawLine(rect.x + rect.width - 1, rect.y + 1, rect.x + rect.width - 1, rect.y + rect.height - 1)
1680
1681        if wnd.GetActiveItem() == item or wnd.GetSelection() == item:
1682            top_brush = self._gallery_button_active_background_top_brush
1683            bg_colour = self._gallery_button_active_background_colour
1684            bg_gradient_colour = self._gallery_button_active_background_gradient_colour
1685        else:
1686            top_brush = self._gallery_button_hover_background_top_brush
1687            bg_colour = self._gallery_button_hover_background_colour
1688            bg_gradient_colour = self._gallery_button_hover_background_gradient_colour
1689
1690        upper = wx.Rect(*rect)
1691        upper.x += 1
1692        upper.width -= 2
1693        upper.y += 1
1694        upper.height /= 3
1695        dc.SetPen(wx.TRANSPARENT_PEN)
1696        dc.SetBrush(top_brush)
1697        dc.DrawRectangle(upper.x, upper.y, upper.width, upper.height)
1698
1699        lower = wx.Rect(*upper)
1700        lower.y += lower.height
1701        lower.height = rect.height - 2 - lower.height
1702        dc.GradientFillLinear(lower, bg_colour, bg_gradient_colour, wx.SOUTH)
1703
1704
1705    def DrawPanelBorder(self, dc, rect, primary_colour, secondary_colour):
1706
1707        border_points = [wx.Point() for i in range(9)]
1708        border_points[0] = wx.Point(2, 0)
1709        border_points[1] = wx.Point(rect.width - 3, 0)
1710        border_points[2] = wx.Point(rect.width - 1, 2)
1711        border_points[3] = wx.Point(rect.width - 1, rect.height - 3)
1712        border_points[4] = wx.Point(rect.width - 3, rect.height - 1)
1713        border_points[5] = wx.Point(2, rect.height - 1)
1714        border_points[6] = wx.Point(0, rect.height - 3)
1715        border_points[7] = wx.Point(0, 2)
1716
1717        if primary_colour.GetColour() == secondary_colour.GetColour():
1718            border_points[8] = border_points[0]
1719            dc.SetPen(primary_colour)
1720            dc.DrawLines(border_points, rect.x, rect.y)
1721        else:
1722            dc.SetPen(primary_colour)
1723            dc.DrawLines(border_points[0:3], rect.x, rect.y)
1724
1725            SingleLine(dc, rect, border_points[0], border_points[7])
1726            dc.SetPen(secondary_colour)
1727            dc.DrawLines(border_points[4:7], rect.x, rect.y)
1728            SingleLine(dc, rect, border_points[4], border_points[3])
1729
1730            border_points[6] = border_points[2]
1731            RibbonDrawParallelGradientLines(dc, 2, border_points[6:8], 0, 1,
1732                                            border_points[3].y - border_points[2].y + 1, rect.x, rect.y,
1733                                            primary_colour.GetColour(), secondary_colour.GetColour())
1734
1735
1736    def DrawMinimisedPanel(self, dc, wnd, rect, bitmap):
1737        """
1738        Draw a minimised ribbon panel.
1739
1740        :param `dc`: The device context to draw onto;
1741        :param `wnd`: The window which is being drawn onto, which is always the panel
1742         which is minimised. The panel label can be obtained from this window. The
1743         minimised icon obtained from querying the window may not be the size requested
1744         by :meth:`~RibbonMSWArtProvider.GetMinimisedPanelMinimumSize` - the argument contains the icon in the
1745         requested size;
1746        :param `rect`: The rectangle within which to draw. The size of the rectangle
1747         will be at least the size returned by :meth:`~RibbonMSWArtProvider.GetMinimisedPanelMinimumSize`;
1748        :param `bitmap`: A copy of the panel's minimised bitmap rescaled to the size
1749         returned by :meth:`~RibbonMSWArtProvider.GetMinimisedPanelMinimumSize`.
1750
1751        """
1752
1753        self.DrawPartialPageBackground(dc, wnd, rect, False)
1754
1755        true_rect = wx.Rect(*rect)
1756        true_rect = self.RemovePanelPadding(true_rect)
1757
1758        if wnd.GetExpandedPanel() != None:
1759            client_rect = wx.Rect(*true_rect)
1760            client_rect.x += 1
1761            client_rect.width -= 2
1762            client_rect.y += 1
1763            client_rect.height = (rect.y + rect.height / 5) - client_rect.x
1764            dc.GradientFillLinear(client_rect,
1765                                  self._panel_active_background_top_colour,
1766                                  self._panel_active_background_top_gradient_colour, wx.SOUTH)
1767
1768            client_rect.y += client_rect.height
1769            client_rect.height = (true_rect.y + true_rect.height) - client_rect.y
1770            dc.GradientFillLinear(client_rect,
1771                                  self._panel_active_background_colour,
1772                                  self._panel_active_background_gradient_colour, wx.SOUTH)
1773
1774        elif wnd.IsHovered():
1775            client_rect = wx.Rect(*true_rect)
1776            client_rect.x += 1
1777            client_rect.width -= 2
1778            client_rect.y += 1
1779            client_rect.height -= 2
1780            self.DrawPartialPageBackground(dc, wnd, client_rect, True)
1781
1782        preview = self.DrawMinimisedPanelCommon(dc, wnd, true_rect)
1783
1784        dc.SetBrush(self._panel_hover_label_background_brush)
1785        dc.SetPen(wx.TRANSPARENT_PEN)
1786        dc.DrawRectangle(preview.x + 1, preview.y + preview.height - 8, preview.width - 2, 7)
1787
1788        mid_pos = rect.y + rect.height / 5 - preview.y
1789
1790        if mid_pos < 0 or mid_pos >= preview.height:
1791            full_rect = wx.Rect(*preview)
1792            full_rect.x += 1
1793            full_rect.y += 1
1794            full_rect.width -= 2
1795            full_rect.height -= 9
1796            if mid_pos < 0:
1797                dc.GradientFillLinear(full_rect, self._page_hover_background_colour,
1798                                      self._page_hover_background_gradient_colour, wx.SOUTH)
1799            else:
1800                dc.GradientFillLinear(full_rect, self._page_hover_background_top_colour,
1801                                      self._page_hover_background_top_gradient_colour, wx.SOUTH)
1802
1803        else:
1804            top_rect = wx.Rect(*preview)
1805            top_rect.x += 1
1806            top_rect.y += 1
1807            top_rect.width -= 2
1808            top_rect.height = mid_pos
1809            dc.GradientFillLinear(top_rect, self._page_hover_background_top_colour,
1810                                  self._page_hover_background_top_gradient_colour, wx.SOUTH)
1811
1812            btm_rect = wx.Rect(*top_rect)
1813            btm_rect.y = preview.y + mid_pos
1814            btm_rect.height = preview.y + preview.height - 7 - btm_rect.y
1815            dc.GradientFillLinear(btm_rect, self._page_hover_background_colour,
1816                                  self._page_hover_background_gradient_colour, wx.SOUTH)
1817
1818        if bitmap.IsOk():
1819            dc.DrawBitmap(bitmap, preview.x + (preview.width - bitmap.GetWidth()) / 2,
1820                          preview.y + (preview.height - 7 - bitmap.GetHeight()) / 2, True)
1821
1822        self.DrawPanelBorder(dc, preview, self._panel_border_pen, self._panel_border_gradient_pen)
1823        self.DrawPanelBorder(dc, true_rect, self._panel_minimised_border_pen, self._panel_minimised_border_gradient_pen)
1824
1825
1826    def DrawMinimisedPanelCommon(self, dc, wnd, true_rect):
1827
1828        preview = wx.Rect(0, 0, 32, 32)
1829
1830        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1831            preview.x = true_rect.x + 4
1832            preview.y = true_rect.y + (true_rect.height - preview.height) / 2
1833        else:
1834            preview.x = true_rect.x + (true_rect.width - preview.width) / 2
1835            preview.y = true_rect.y + 4
1836
1837        dc.SetFont(self._panel_label_font)
1838        label_width, label_height = dc.GetTextExtent(wnd.GetLabel())
1839
1840        xpos = true_rect.x + (true_rect.width - label_width + 1) / 2
1841        ypos = preview.y + preview.height + 5
1842
1843        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1844            xpos = preview.x + preview.width + 5
1845            ypos = true_rect.y + (true_rect.height - label_height) / 2
1846
1847        dc.SetTextForeground(self._panel_minimised_label_colour)
1848        dc.DrawText(wnd.GetLabel(), xpos, ypos)
1849
1850        arrow_points = [wx.Point() for i in range(3)]
1851
1852        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
1853            xpos += label_width
1854            arrow_points[0] = wx.Point(xpos + 5, ypos + label_height / 2)
1855            arrow_points[1] = arrow_points[0] + wx.Point(-3,  3)
1856            arrow_points[2] = arrow_points[0] + wx.Point(-3, -3)
1857        else:
1858            ypos += label_height
1859            arrow_points[0] = wx.Point(true_rect.width / 2, ypos + 5)
1860            arrow_points[1] = arrow_points[0] + wx.Point(-3, -3)
1861            arrow_points[2] = arrow_points[0] + wx.Point( 3, -3)
1862
1863        dc.SetPen(wx.TRANSPARENT_PEN)
1864        B = wx.Brush(self._panel_minimised_label_colour)
1865        dc.SetBrush(B)
1866        dc.DrawPolygon(arrow_points, true_rect.x, true_rect.y)
1867
1868        return preview
1869
1870
1871    def DrawButtonBarBackground(self, dc, wnd, rect):
1872        """
1873        Draw the background for a :class:`~wx.lib.agw.ribbon.buttonbar.RibbonButtonBar` control.
1874
1875        :param `dc`: The device context to draw onto;
1876        :param `wnd`: The window which is being drawn onto (which will typically be
1877         the button bar itself, though this is not guaranteed);
1878        :param `rect`: The rectangle within which to draw.
1879
1880        """
1881
1882        self.DrawPartialPageBackground(dc, wnd, rect, True)
1883
1884
1885    def DrawPartialPageBackground2(self, dc, wnd, rect, allow_hovered=True):
1886
1887        # Assume the window is a child of a ribbon page, and also check for a
1888        # hovered panel somewhere between the window and the page, as it causes
1889        # the background to change.
1890        offset = wx.Point(*wnd.GetPosition())
1891        page = None
1892        parent = wnd.GetParent()
1893        hovered = False
1894        panel = None
1895
1896        if isinstance(wnd, PANEL.RibbonPanel):
1897            panel = wnd
1898            hovered = allow_hovered and panel.IsHovered()
1899            if panel.GetExpandedDummy() != None:
1900                offset = panel.GetExpandedDummy().GetPosition()
1901                parent = panel.GetExpandedDummy().GetParent()
1902
1903        while 1:
1904
1905            if panel is None:
1906                panel = parent
1907                if isinstance(panel, PANEL.RibbonPanel):
1908                    hovered = allow_hovered and panel.IsHovered()
1909                    if panel.GetExpandedDummy() != None:
1910                        parent = panel.GetExpandedDummy()
1911
1912            page = parent
1913            if isinstance(page, PAGE.RibbonPage):
1914                break
1915
1916            offset += parent.GetPosition()
1917            parent = parent.GetParent()
1918            if parent is None:
1919                break
1920
1921        if page != None:
1922            self.DrawPartialPageBackground(dc, wnd, rect, page, offset, hovered)
1923            return
1924
1925        # No page found - fallback to painting with a stock brush
1926        dc.SetBrush(wx.WHITE_BRUSH)
1927        dc.SetPen(wx.TRANSPARENT_PEN)
1928        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
1929
1930
1931    def DrawButtonBarButton(self, dc, wnd, rect, kind, state, label, bitmap_large, bitmap_small):
1932        """
1933        Draw a single button for a :class:`~wx.lib.agw.ribbon.buttonbar.RibbonButtonBar` control.
1934
1935        :param `dc`: The device context to draw onto;
1936        :param `wnd`: The window which is being drawn onto;
1937        :param `rect`: The rectangle within which to draw. The size of this rectangle
1938         will be a size previously returned by :meth:`~RibbonMSWArtProvider.GetButtonBarButtonSize`, and the
1939         rectangle will be entirely within a rectangle on the same device context
1940         previously painted with :meth:`~RibbonMSWArtProvider.DrawButtonBarBackground`;
1941        :param `kind`: The kind of button to draw (normal, dropdown or hybrid);
1942        :param `state`: Combination of a size flag and state flags from the
1943         `RibbonButtonBarButtonState` enumeration;
1944        :param `label`: The label of the button;
1945        :param `bitmap_large`: The large bitmap of the button (or the large disabled
1946         bitmap when ``RIBBON_BUTTONBAR_BUTTON_DISABLED`` is set in `state`);
1947        :param `bitmap_small`: The small bitmap of the button (or the small disabled
1948         bitmap when ``RIBBON_BUTTONBAR_BUTTON_DISABLED`` is set in `state`).
1949
1950        """
1951
1952        if kind == RIBBON_BUTTON_TOGGLE:
1953            kind = RIBBON_BUTTON_NORMAL
1954            if state & RIBBON_BUTTONBAR_BUTTON_TOGGLED:
1955                state ^= RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK
1956
1957        if state & (RIBBON_BUTTONBAR_BUTTON_HOVER_MASK | RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK):
1958            if state & RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK:
1959                dc.SetPen(self._button_bar_active_border_pen)
1960            else:
1961                dc.SetPen(self._button_bar_hover_border_pen)
1962
1963            bg_rect = wx.Rect(*rect)
1964            bg_rect.x += 1
1965            bg_rect.y += 1
1966            bg_rect.width -= 2
1967            bg_rect.height -= 2
1968
1969            bg_rect_top = wx.Rect(*bg_rect)
1970            bg_rect_top.height /= 3
1971            bg_rect.y += bg_rect_top.height
1972            bg_rect.height -= bg_rect_top.height
1973
1974            if kind == RIBBON_BUTTON_HYBRID:
1975
1976                result = state & RIBBON_BUTTONBAR_BUTTON_SIZE_MASK
1977
1978                if result == RIBBON_BUTTONBAR_BUTTON_LARGE:
1979                    iYBorder = rect.y + bitmap_large.GetHeight() + 4
1980                    partial_bg = wx.Rect(*rect)
1981
1982                    if state & RIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED:
1983                        partial_bg.SetBottom(iYBorder - 1)
1984                    else:
1985                        partial_bg.height -= (iYBorder - partial_bg.y + 1)
1986                        partial_bg.y = iYBorder + 1
1987
1988                    dc.DrawLine(rect.x, iYBorder, rect.x + rect.width, iYBorder)
1989                    bg_rect.Intersect(partial_bg)
1990                    bg_rect_top.Intersect(partial_bg)
1991
1992                elif result == RIBBON_BUTTONBAR_BUTTON_MEDIUM:
1993                    iArrowWidth = 9
1994
1995                    if state & RIBBON_BUTTONBAR_BUTTON_NORMAL_HOVERED:
1996                        bg_rect.width -= iArrowWidth
1997                        bg_rect_top.width -= iArrowWidth
1998                        dc.DrawLine(bg_rect_top.x + bg_rect_top.width, rect.y, bg_rect_top.x + bg_rect_top.width,
1999                                    rect.y + rect.height)
2000                    else:
2001                        iArrowWidth -= 1
2002                        bg_rect.x += bg_rect.width - iArrowWidth
2003                        bg_rect_top.x += bg_rect_top.width - iArrowWidth
2004                        bg_rect.width = iArrowWidth
2005                        bg_rect_top.width = iArrowWidth
2006                        dc.DrawLine(bg_rect_top.x - 1, rect.y, bg_rect_top.x - 1, rect.y + rect.height)
2007
2008            if state & RIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK:
2009
2010                dc.GradientFillLinear(bg_rect_top, self._button_bar_active_background_top_colour,
2011                                      self._button_bar_active_background_top_gradient_colour, wx.SOUTH)
2012                dc.GradientFillLinear(bg_rect, self._button_bar_active_background_colour,
2013                                      self._button_bar_active_background_gradient_colour, wx.SOUTH)
2014
2015            else:
2016                dc.GradientFillLinear(bg_rect_top, self._button_bar_hover_background_top_colour,
2017                                      self._button_bar_hover_background_top_gradient_colour, wx.SOUTH)
2018                dc.GradientFillLinear(bg_rect, self._button_bar_hover_background_colour,
2019                                      self._button_bar_hover_background_gradient_colour, wx.SOUTH)
2020
2021            border_points = [wx.Point() for i in range(9)]
2022            border_points[0] = wx.Point(2, 0)
2023            border_points[1] = wx.Point(rect.width - 3, 0)
2024            border_points[2] = wx.Point(rect.width - 1, 2)
2025            border_points[3] = wx.Point(rect.width - 1, rect.height - 3)
2026            border_points[4] = wx.Point(rect.width - 3, rect.height - 1)
2027            border_points[5] = wx.Point(2, rect.height - 1)
2028            border_points[6] = wx.Point(0, rect.height - 3)
2029            border_points[7] = wx.Point(0, 2)
2030            border_points[8] = border_points[0]
2031
2032            dc.DrawLines(border_points, rect.x, rect.y)
2033
2034        dc.SetFont(self._button_bar_label_font)
2035        dc.SetTextForeground(self._button_bar_label_colour)
2036        self.DrawButtonBarButtonForeground(dc, rect, kind, state, label, bitmap_large, bitmap_small)
2037
2038
2039    def DrawButtonBarButtonForeground(self, dc, rect, kind, state, label, bitmap_large, bitmap_small):
2040
2041        result = state & RIBBON_BUTTONBAR_BUTTON_SIZE_MASK
2042
2043        if result == RIBBON_BUTTONBAR_BUTTON_LARGE:
2044
2045            padding = 2
2046            dc.DrawBitmap(bitmap_large, rect.x + (rect.width - bitmap_large.GetWidth()) / 2,
2047                          rect.y + padding, True)
2048            ypos = rect.y + padding + bitmap_large.GetHeight() + padding
2049            arrow_width = (kind == RIBBON_BUTTON_NORMAL and [0] or [8])[0]
2050
2051            label_w, label_h = dc.GetTextExtent(label)
2052
2053            if label_w + 2 * padding <= rect.width:
2054
2055                dc.DrawText(label, rect.x + (rect.width - label_w) / 2, ypos)
2056                if arrow_width != 0:
2057                    self.DrawDropdownArrow(dc, rect.x + rect.width / 2,
2058                                           ypos + (label_h * 3) / 2,
2059                                           self._button_bar_label_colour)
2060            else:
2061                breaki = len(label)
2062
2063                while breaki > 0:
2064                    breaki -= 1
2065                    if RibbonCanLabelBreakAtPosition(label, breaki):
2066                        label_top = label[0:breaki]
2067                        label_w, label_h = dc.GetTextExtent(label_top)
2068
2069                        if label_w + 2 * padding <= rect.width:
2070                            dc.DrawText(label_top, rect.x + (rect.width - label_w) / 2, ypos)
2071                            ypos += label_h
2072                            label_bottom = label[breaki:]
2073                            label_w, label_h = dc.GetTextExtent(label_bottom)
2074                            label_w += arrow_width
2075                            iX = rect.x + (rect.width - label_w) / 2
2076                            dc.DrawText(label_bottom, iX, ypos)
2077
2078                            if arrow_width != 0:
2079                                self.DrawDropdownArrow(dc, iX + 2 +label_w - arrow_width,
2080                                                       ypos + label_h / 2 + 1,
2081                                                       self._button_bar_label_colour)
2082
2083                            break
2084
2085        elif result == RIBBON_BUTTONBAR_BUTTON_MEDIUM:
2086
2087            x_cursor = rect.x + 2
2088            dc.DrawBitmap(bitmap_small, x_cursor, rect.y + (rect.height - bitmap_small.GetHeight())/2, True)
2089            x_cursor += bitmap_small.GetWidth() + 2
2090            label_w, label_h = dc.GetTextExtent(label)
2091            dc.DrawText(label, x_cursor, rect.y + (rect.height - label_h) / 2)
2092            x_cursor += label_w + 3
2093
2094            if kind != RIBBON_BUTTON_NORMAL:
2095                self.DrawDropdownArrow(dc, x_cursor, rect.y + rect.height / 2,
2096                                       self._button_bar_label_colour)
2097
2098        else:
2099            # TODO
2100            pass
2101
2102
2103    def DrawToolBarBackground(self, dc, wnd, rect):
2104        """
2105        Draw the background for a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar` control.
2106
2107
2108        :param `dc`: The device context to draw onto;
2109        :param `wnd`: The which is being drawn onto. In most cases this will be a
2110         :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`, but it doesn't have to be;
2111        :param `rect`: The rectangle within which to draw. Some of this rectangle
2112         will later be drawn over using :meth:`~RibbonMSWArtProvider.DrawToolGroupBackground` and :meth:`~RibbonMSWArtProvider.DrawTool`,
2113         but not all of it will (unless there is only a single group of tools).
2114
2115        """
2116
2117        self.DrawPartialPageBackground(dc, wnd, rect)
2118
2119
2120    def DrawToolGroupBackground(self, dc, wnd, rect):
2121        """
2122        Draw the background for a group of tools on a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar` control.
2123
2124        :param `dc`: The device context to draw onto;
2125        :param `wnd`: The window which is being drawn onto. In most cases this will
2126         be a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`, but it doesn't have to be;
2127        :param `rect`: The rectangle within which to draw. This rectangle is a union
2128         of the individual tools' rectangles. As there are no gaps between tools,
2129         this rectangle will be painted over exactly once by calls to :meth:`~RibbonMSWArtProvider.DrawTool`.
2130         The group background could therefore be painted by :meth:`~RibbonMSWArtProvider.DrawTool`, though it
2131         can be conceptually easier and more efficient to draw it all at once here.
2132         The rectangle will be entirely within a rectangle on the same device context
2133         previously painted with :meth:`~RibbonMSWArtProvider.DrawToolBarBackground`.
2134
2135        """
2136
2137        dc.SetPen(self._toolbar_border_pen)
2138        outline = [wx.Point() for i in range(9)]
2139        outline[0] = wx.Point(2, 0)
2140        outline[1] = wx.Point(rect.width - 3, 0)
2141        outline[2] = wx.Point(rect.width - 1, 2)
2142        outline[3] = wx.Point(rect.width - 1, rect.height - 3)
2143        outline[4] = wx.Point(rect.width - 3, rect.height - 1)
2144        outline[5] = wx.Point(2, rect.height - 1)
2145        outline[6] = wx.Point(0, rect.height - 3)
2146        outline[7] = wx.Point(0, 2)
2147        outline[8] = outline[0]
2148
2149        dc.DrawLines(outline, rect.x, rect.y)
2150
2151
2152    def DrawTool(self, dc, wnd, rect, bitmap, kind, state):
2153        """
2154        Draw a single tool (for a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar` control).
2155
2156        :param `dc`: The device context to draw onto;
2157        :param `wnd`: The window which is being drawn onto. In most cases this will
2158         be a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`, but it doesn't have to be;
2159        :param `rect`: The rectangle within which to draw. The size of this rectangle
2160         will at least the size returned by :meth:`~RibbonMSWArtProvider.GetToolSize`, and the height of it will
2161         be equal for all tools within the same group. The rectangle will be entirely
2162         within a rectangle on the same device context previously painted with
2163         :meth:`~RibbonMSWArtProvider.DrawToolGroupBackground`;
2164        :param `bitmap`: The bitmap to use as the tool's foreground. If the tool is a
2165         hybrid or dropdown tool, then the foreground should also contain a standard
2166         dropdown button;
2167        :param `kind`: The kind of tool to draw (normal, dropdown, or hybrid);
2168        :param `state`: A combination of `RibbonToolBarToolState` flags giving the
2169         state of the tool and it's relative position within a tool group.
2170
2171        """
2172
2173        if kind == RIBBON_BUTTON_TOGGLE:
2174            if state & RIBBON_TOOLBAR_TOOL_TOGGLED:
2175                state ^= RIBBON_TOOLBAR_TOOL_ACTIVE_MASK
2176
2177        bg_rect = wx.Rect(*rect)
2178        bg_rect.Deflate(1, 1)
2179
2180        if state & RIBBON_TOOLBAR_TOOL_LAST == 0:
2181            bg_rect.width += 1
2182
2183        is_split_hybrid = (kind == RIBBON_BUTTON_HYBRID and (state & (RIBBON_TOOLBAR_TOOL_HOVER_MASK | RIBBON_TOOLBAR_TOOL_ACTIVE_MASK)))
2184
2185        # Background
2186        bg_rect_top = wx.Rect(*bg_rect)
2187        bg_rect_top.height = (bg_rect_top.height * 2) / 5
2188        bg_rect_btm = wx.Rect(*bg_rect)
2189        bg_rect_btm.y += bg_rect_top.height
2190        bg_rect_btm.height -= bg_rect_top.height
2191
2192        bg_top_colour = self._tool_background_top_colour
2193        bg_top_grad_colour = self._tool_background_top_gradient_colour
2194        bg_colour = self._tool_background_colour
2195        bg_grad_colour = self._tool_background_gradient_colour
2196
2197        if state & RIBBON_TOOLBAR_TOOL_ACTIVE_MASK:
2198            bg_top_colour = self._tool_active_background_top_colour
2199            bg_top_grad_colour = self._tool_active_background_top_gradient_colour
2200            bg_colour = self._tool_active_background_colour
2201            bg_grad_colour = self._tool_active_background_gradient_colour
2202
2203        elif state & RIBBON_TOOLBAR_TOOL_HOVER_MASK:
2204            bg_top_colour = self._tool_hover_background_top_colour
2205            bg_top_grad_colour = self._tool_hover_background_top_gradient_colour
2206            bg_colour = self._tool_hover_background_colour
2207            bg_grad_colour = self._tool_hover_background_gradient_colour
2208
2209        dc.GradientFillLinear(bg_rect_top, bg_top_colour, bg_top_grad_colour, wx.SOUTH)
2210        dc.GradientFillLinear(bg_rect_btm, bg_colour, bg_grad_colour, wx.SOUTH)
2211
2212        if is_split_hybrid:
2213            nonrect = wx.Rect(*bg_rect)
2214            if state & (RIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED | RIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE):
2215                nonrect.width -= 8
2216            else:
2217                nonrect.x += nonrect.width - 8
2218                nonrect.width = 8
2219
2220            B = wx.Brush(self._tool_hover_background_top_colour)
2221            dc.SetPen(wx.TRANSPARENT_PEN)
2222            dc.SetBrush(B)
2223            dc.DrawRectangle(nonrect.x, nonrect.y, nonrect.width, nonrect.height)
2224
2225        # Border
2226        dc.SetPen(self._toolbar_border_pen)
2227
2228        if state & RIBBON_TOOLBAR_TOOL_FIRST:
2229            dc.DrawPoint(rect.x + 1, rect.y + 1)
2230            dc.DrawPoint(rect.x + 1, rect.y + rect.height - 2)
2231        else:
2232            dc.DrawLine(rect.x, rect.y + 1, rect.x, rect.y + rect.height - 1)
2233
2234        if state & RIBBON_TOOLBAR_TOOL_LAST:
2235            dc.DrawPoint(rect.x + rect.width - 2, rect.y + 1)
2236            dc.DrawPoint(rect.x + rect.width - 2, rect.y + rect.height - 2)
2237
2238        # Foreground
2239        avail_width = bg_rect.GetWidth()
2240
2241        if kind & RIBBON_BUTTON_DROPDOWN:
2242            avail_width -= 8
2243            if is_split_hybrid:
2244                dc.DrawLine(rect.x + avail_width + 1, rect.y, rect.x + avail_width + 1, rect.y + rect.height)
2245
2246            dc.DrawBitmap(self._toolbar_drop_bitmap, bg_rect.x + avail_width + 2,
2247                          bg_rect.y + (bg_rect.height / 2) - 2, True)
2248
2249        dc.DrawBitmap(bitmap, bg_rect.x + (avail_width - bitmap.GetWidth()) / 2,
2250                      bg_rect.y + (bg_rect.height - bitmap.GetHeight()) / 2, True)
2251
2252
2253    def GetBarTabWidth(self, dc, wnd, label, bitmap, ideal=None, small_begin_need_separator=None,
2254                       small_must_have_separator=None, minimum=None):
2255
2256        """
2257        Calculate the ideal and minimum width (in pixels) of a tab in a ribbon bar.
2258
2259        :param `dc`: A device context to use when one is required for size calculations;
2260        :param `wnd`: The window onto which the tab will eventually be drawn;
2261        :param `label`: The tab's label (or "" if it has none);
2262        :param `bitmap`: The tab's icon (or :class:`NullBitmap` if it has none);
2263        :param `ideal`: The ideal width (in pixels) of the tab;
2264        :param `small_begin_need_separator`: A size less than the size, at which a
2265         tab separator should begin to be drawn (i.e. drawn, but still fairly transparent);
2266        :param `small_must_have_separator`: A size less than the size, at which a
2267         tab separator must be drawn (i.e. drawn at full opacity);
2268        :param `minimum`: A size less than the size, and greater than or equal to
2269         zero, which is the minimum pixel width for the tab.
2270
2271        """
2272
2273        width = 0
2274        mini = 0
2275
2276        if (self._flags & RIBBON_BAR_SHOW_PAGE_LABELS) and label.strip():
2277            dc.SetFont(self._tab_label_font)
2278            width += dc.GetTextExtent(label)[0]
2279            mini += min(25, width) # enough for a few chars
2280
2281            if bitmap.IsOk():
2282                # gap between label and bitmap
2283                width += 4
2284                mini += 2
2285
2286        if (self._flags & RIBBON_BAR_SHOW_PAGE_ICONS) and bitmap.IsOk():
2287            width += bitmap.GetWidth()
2288            mini += bitmap.GetWidth()
2289
2290        ideal = width + 30
2291        small_begin_need_separator = width + 20
2292        small_must_have_separator = width + 10
2293        minimum = mini
2294
2295        return ideal, small_begin_need_separator, small_must_have_separator, minimum
2296
2297
2298    def GetTabCtrlHeight(self, dc, wnd, pages):
2299        """
2300        Calculate the height (in pixels) of the tab region of a ribbon bar.
2301
2302        Note that as the tab region can contain scroll buttons, the height should be
2303        greater than or equal to the minimum height for a tab scroll button.
2304
2305        :param `dc`: A device context to use when one is required for size calculations;
2306        :param `wnd`: The window onto which the tabs will eventually be drawn;
2307        :param `pages`: The tabs which will acquire the returned height.
2308
2309        """
2310
2311        text_height = 0
2312        icon_height = 0
2313
2314        if len(pages) <= 1 and (self._flags & RIBBON_BAR_ALWAYS_SHOW_TABS) == 0:
2315            # To preserve space, a single tab need not be displayed. We still need
2316            # two pixels of border / padding though.
2317            return 2
2318
2319        if self._flags & RIBBON_BAR_SHOW_PAGE_LABELS:
2320            dc.SetFont(self._tab_label_font)
2321            text_height = dc.GetTextExtent("ABCDEFXj")[1] + 10
2322
2323        if self._flags & RIBBON_BAR_SHOW_PAGE_ICONS:
2324            for info in pages:
2325                if info.page.GetIcon().IsOk():
2326                    icon_height = max(icon_height, info.page.GetIcon().GetHeight() + 4)
2327
2328        return max(text_height, icon_height)
2329
2330
2331    def GetScrollButtonMinimumSize(self, dc, wnd, style):
2332        """
2333        Calculate the minimum size (in pixels) of a scroll button.
2334
2335        :param `dc`: A device context to use when one is required for size calculations;
2336        :param `wnd`: The window onto which the scroll button will eventually be drawn;
2337        :param `style`: A combination of flags from `RibbonScrollButtonStyle`, including
2338         a direction, and a for flag (state flags may be given too, but should be ignored,
2339         as a button should retain a constant size, regardless of its state).
2340
2341        """
2342
2343        return wx.Size(12, 12)
2344
2345
2346    def GetPanelSize(self, dc, wnd, client_size, client_offset=None):
2347        """
2348        Calculate the size of a panel for a given client size.
2349
2350        This should increment the given size by enough to fit the panel label and other
2351        chrome.
2352
2353        :param `dc`: A device context to use if one is required for size calculations;
2354        :param `wnd`: The ribbon panel in question;
2355        :param `client_size`: The client size;
2356        :param `client_offset`: The offset where the client rectangle begins within the
2357         panel (may be ``None``).
2358
2359        :see: :meth:`~RibbonMSWArtProvider.GetPanelClientSize`
2360        """
2361
2362        dc.SetFont(self._panel_label_font)
2363        label_size = wx.Size(*dc.GetTextExtent(wnd.GetLabel()))
2364
2365        client_size.IncBy(0, label_size.GetHeight())
2366
2367        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2368            client_size.IncBy(4, 8)
2369        else:
2370            client_size.IncBy(6, 6)
2371
2372        if client_offset != None:
2373            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2374                client_offset = wx.Point(2, 3)
2375            else:
2376                client_offset = wx.Point(3, 2)
2377
2378        return client_size
2379
2380
2381    def GetPanelClientSize(self, dc, wnd, size, client_offset=None):
2382        """
2383        Calculate the client size of a panel for a given overall size.
2384
2385        This should act as the inverse to :meth:`~RibbonMSWArtProvider.GetPanelSize`, and decrement the given size
2386        by enough to fit the panel label and other chrome.
2387
2388        :param `dc`: A device context to use if one is required for size calculations;
2389        :param `wnd`: The ribbon panel in question;
2390        :param `size`: The overall size to calculate client size for;
2391        :param `client_offset`: The offset where the returned client size begins within
2392         the given (may be ``None``).
2393
2394        :see: :meth:`~RibbonMSWArtProvider.GetPanelSize`
2395        """
2396
2397        dc.SetFont(self._panel_label_font)
2398        label_size = wx.Size(*dc.GetTextExtent(wnd.GetLabel()))
2399
2400        size.DecBy(0, label_size.GetHeight())
2401
2402        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2403            size.DecBy(4, 8)
2404        else:
2405            size.DecBy(6, 6)
2406
2407        if client_offset != None:
2408            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2409                client_offset = wx.Point(2, 3)
2410            else:
2411                client_offset = wx.Point(3, 2)
2412
2413        if size.x < 0:
2414            size.x = 0
2415        if size.y < 0:
2416            size.y = 0
2417
2418        return size, client_offset
2419
2420
2421    def GetGallerySize(self, dc, wnd, client_size):
2422        """
2423        Calculate the size of a :class:`~wx.lib.agw.ribbon.gallery.RibbonGallery` control for a given client size.
2424
2425        This should increment the given size by enough to fit the gallery border,
2426        buttons, and any other chrome.
2427
2428        :param `dc`: A device context to use if one is required for size calculations;
2429        :param `wnd`: The gallery in question;
2430        :param `client_size`: The client size.
2431
2432        :see: :meth:`~RibbonMSWArtProvider.GetGalleryClientSize`
2433        """
2434
2435        client_size.IncBy(2, 1) # Left / top padding
2436
2437        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2438            client_size.IncBy(1, 16) # Right / bottom padding
2439        else:
2440            client_size.IncBy(16, 1) # Right / bottom padding
2441
2442        return client_size
2443
2444
2445    def GetGalleryClientSize(self, dc, wnd, size, client_offset=None, scroll_up_button=None,
2446                             scroll_down_button=None, extension_button=None):
2447
2448        """
2449        Calculate the client size of a :class:`~wx.lib.agw.ribbon.gallery.RibbonGallery` control for a given size.
2450
2451        This should act as the inverse to :meth:`~RibbonMSWArtProvider.GetGallerySize`, and decrement the given
2452        size by enough to fir the gallery border, buttons, and other chrome.
2453
2454        :param `dc`: A device context to use if one is required for size calculations;
2455        :param `wnd`: The gallery in question;
2456        :param `size`: The overall size to calculate the client size for;
2457        :param `client_offset`: The position within the given size at which the
2458         returned client size begins;
2459        :param `scroll_up_button`: The rectangle within the given size which the
2460         scroll up button occupies;
2461        :param `scroll_down_button`: The rectangle within the given size which the
2462         scroll down button occupies;
2463        :param `extension_button`: The rectangle within the given size which the
2464         extension button occupies.
2465
2466        """
2467
2468        scroll_up = wx.Rect()
2469        scroll_down = wx.Rect()
2470        extension = wx.Rect()
2471
2472        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2473            # Flow is vertical - put buttons on bottom
2474            scroll_up.y = size.GetHeight() - 15
2475            scroll_up.height = 15
2476            scroll_up.x = 0
2477            scroll_up.width = (size.GetWidth() + 2) / 3
2478            scroll_down.y = scroll_up.y
2479            scroll_down.height = scroll_up.height
2480            scroll_down.x = scroll_up.x + scroll_up.width
2481            scroll_down.width = scroll_up.width
2482            extension.y = scroll_down.y
2483            extension.height = scroll_down.height
2484            extension.x = scroll_down.x + scroll_down.width
2485            extension.width = size.GetWidth() - scroll_up.width - scroll_down.width
2486            size.DecBy(1, 16)
2487            size.DecBy(2, 1)
2488
2489        else:
2490            # Flow is horizontal - put buttons on right
2491            scroll_up.x = size.GetWidth() - 15
2492            scroll_up.width = 15
2493            scroll_up.y = 0
2494            scroll_up.height = (size.GetHeight() + 2) / 3
2495            scroll_down.x = scroll_up.x
2496            scroll_down.width = scroll_up.width
2497            scroll_down.y = scroll_up.y + scroll_up.height
2498            scroll_down.height = scroll_up.height
2499            extension.x = scroll_down.x
2500            extension.width = scroll_down.width
2501            extension.y = scroll_down.y + scroll_down.height
2502            extension.height = size.GetHeight() - scroll_up.height - scroll_down.height
2503            size.DecBy(16, 1)
2504            size.DecBy( 2, 1)
2505
2506        client_offset = wx.Point(2, 1)
2507        scroll_up_button = scroll_up
2508        scroll_down_button = scroll_down
2509        extension_button = extension
2510
2511        return size, client_offset, scroll_up_button, scroll_down_button, extension_button
2512
2513
2514    def GetPageBackgroundRedrawArea(self, dc, wnd, page_old_size, page_new_size):
2515        """
2516        Calculate the portion of a page background which needs to be redrawn when a page
2517        is resized.
2518
2519        To optimise the drawing of page backgrounds, as small an area as possible should
2520        be returned. Of couse, if the way in which a background is drawn means that the
2521        entire background needs to be repainted on resize, then the entire new size
2522        should be returned.
2523
2524        :param `dc`: A device context to use when one is required for size calculations;
2525        :param `wnd`: The page which is being resized;
2526        :param `page_old_size`: The size of the page prior to the resize (which has
2527         already been painted);
2528        :param `page_new_size`: The size of the page after the resize.
2529
2530        """
2531
2532        if page_new_size.GetWidth() != page_old_size.GetWidth():
2533            if page_new_size.GetHeight() != page_old_size.GetHeight():
2534                # Width and height both changed - redraw everything
2535                return wx.Rect(0, 0, *page_new_size)
2536            else:
2537                # Only width changed - redraw right hand side
2538                right_edge_width = 4
2539                new_rect = wx.Rect(page_new_size.GetWidth() - right_edge_width, 0, right_edge_width, page_new_size.GetHeight())
2540                old_rect = wx.Rect(page_old_size.GetWidth() - right_edge_width, 0, right_edge_width, page_old_size.GetHeight())
2541
2542        else:
2543            if page_new_size.GetHeight() == page_old_size.GetHeight():
2544                # Nothing changed (should never happen) - redraw nothing
2545                return wx.Rect(0, 0, 0, 0)
2546            else:
2547                # Height changed - need to redraw everything (as the background
2548                # gradient is done vertically).
2549                return wx.Rect(0, 0, *page_new_size)
2550
2551        new_rect.Union(old_rect)
2552        new_rect.Intersect(wx.Rect(0, 0, *page_new_size))
2553        return new_rect
2554
2555
2556    def GetButtonBarButtonSize(self, dc, wnd, kind, size, label, bitmap_size_large, bitmap_size_small,
2557                               button_size=None, normal_region=None, dropdown_region=None):
2558        """
2559        Calculate the size of a button within a :class:`~wx.lib.agw.ribbon.buttonbar.RibbonButtonBar`.
2560
2561        :param `dc`: A device context to use when one is required for size calculations;
2562        :param `wnd`: The window onto which the button will eventually be drawn
2563         (which is normally a :class:`~wx.lib.agw.ribbon.buttonbar.RibbonButtonBar`, though this is not guaranteed);
2564        :param `kind`: The kind of button;
2565        :param `size`: The size-class to calculate the size for. Buttons on a button
2566         bar can have three distinct sizes: ``RIBBON_BUTTONBAR_BUTTON_SMALL``,
2567         ``RIBBON_BUTTONBAR_BUTTON_MEDIUM``, and ``RIBBON_BUTTONBAR_BUTTON_LARGE``.
2568         If the requested size-class is not applicable, then ``False`` should be returned;
2569        :param `label`: The label of the button;
2570        :param `bitmap_size_large`: The size of all "large" bitmaps on the button bar;
2571        :param `bitmap_size_small`: The size of all "small" bitmaps on the button bar;
2572        :param `button_size`: The size, in pixels, of the button;
2573        :param `normal_region`: The region of the button which constitutes the normal button;
2574        :param `dropdown_region`: The region of the button which constitutes the dropdown button.
2575
2576        :returns: ``True`` if a size exists for the button, ``False`` otherwise.
2577        """
2578
2579        drop_button_width = 8
2580
2581        normal_region = wx.Rect()
2582        dropdown_region = wx.Rect()
2583
2584        dc.SetFont(self._button_bar_label_font)
2585        result = size & RIBBON_BUTTONBAR_BUTTON_SIZE_MASK
2586
2587        if result == RIBBON_BUTTONBAR_BUTTON_SMALL:
2588            # Small bitmap, no label
2589            button_size = wx.Size(bitmap_size_small + wx.Size(6, 4))
2590
2591            if kind in [RIBBON_BUTTON_NORMAL, RIBBON_BUTTON_TOGGLE]:
2592                normal_region = wx.Rect(0, 0, *button_size)
2593                dropdown_region = wx.Rect(0, 0, 0, 0)
2594
2595            elif kind == RIBBON_BUTTON_DROPDOWN:
2596                button_size += wx.Size(drop_button_width, 0)
2597                dropdown_region = wx.Rect(0, 0, *button_size)
2598                normal_region = wx.Rect(0, 0, 0, 0)
2599
2600            elif kind == RIBBON_BUTTON_HYBRID:
2601                normal_region = wx.Rect(0, 0, *button_size)
2602                dropdown_region = wx.Rect(button_size.GetWidth(), 0, drop_button_width, button_size.GetHeight())
2603                button_size += wx.Size(drop_button_width, 0)
2604
2605        elif result == RIBBON_BUTTONBAR_BUTTON_MEDIUM:
2606            # Small bitmap, with label to the right
2607            is_supported, button_size, normal_region, dropdown_region = self.GetButtonBarButtonSize(dc, wnd, kind,
2608                                                                                                    RIBBON_BUTTONBAR_BUTTON_SMALL,
2609                                                                                                    label, bitmap_size_large,
2610                                                                                                    bitmap_size_small)
2611            text_size = dc.GetTextExtent(label)[0]
2612            button_size.SetWidth(button_size.GetWidth() + text_size)
2613
2614            if kind == RIBBON_BUTTON_DROPDOWN:
2615                dropdown_region.SetWidth(dropdown_region.GetWidth() + text_size)
2616
2617            elif kind == RIBBON_BUTTON_HYBRID:
2618                dropdown_region.SetX(dropdown_region.GetX() + text_size)
2619                normal_region.SetWidth(normal_region.GetWidth() + text_size)
2620                # no break
2621            elif kind in [RIBBON_BUTTON_NORMAL, RIBBON_BUTTON_TOGGLE]:
2622                normal_region.SetWidth(normal_region.GetWidth() + text_size)
2623
2624        elif result == RIBBON_BUTTONBAR_BUTTON_LARGE:
2625            # Large bitmap, with label below (possibly split over 2 lines)
2626
2627            icon_size = wx.Size(*bitmap_size_large)
2628            icon_size += wx.Size(4, 4)
2629            best_width, label_height = dc.GetTextExtent(label)
2630            last_line_extra_width = 0
2631
2632            if kind not in [RIBBON_BUTTON_NORMAL, RIBBON_BUTTON_TOGGLE]:
2633                last_line_extra_width += 8
2634
2635            for i in range(0, len(label)):
2636                if RibbonCanLabelBreakAtPosition(label, i):
2637
2638                    width = max(dc.GetTextExtent(label[0:i])[0],
2639                                dc.GetTextExtent(label[i+1:])[0] + last_line_extra_width)
2640                    if width < best_width:
2641                        best_width = width
2642
2643            label_height *= 2 # Assume two lines even when only one is used
2644                              # (to give all buttons a consistent height)
2645            icon_size.SetWidth(max(icon_size.GetWidth(), best_width) + 6)
2646            icon_size.SetHeight(icon_size.GetHeight() + label_height)
2647            button_size = wx.Size(*icon_size)
2648
2649            if kind == RIBBON_BUTTON_DROPDOWN:
2650                dropdown_region = wx.Rect(0, 0, *icon_size)
2651            elif kind == RIBBON_BUTTON_HYBRID:
2652                normal_region = wx.Rect(0, 0, *icon_size)
2653                normal_region.height -= 2 + label_height
2654                dropdown_region.x = 0
2655                dropdown_region.y = normal_region.height
2656                dropdown_region.width = icon_size.GetWidth()
2657                dropdown_region.height = icon_size.GetHeight() - normal_region.height
2658            elif kind in [RIBBON_BUTTON_NORMAL, RIBBON_BUTTON_TOGGLE]:
2659                normal_region = wx.Rect(0, 0, *icon_size)
2660
2661        return True, button_size, normal_region, dropdown_region
2662
2663
2664    def GetMinimisedPanelMinimumSize(self, dc, wnd, desired_bitmap_size=None, expanded_panel_direction=None):
2665        """
2666        Calculate the size of a minimised ribbon panel.
2667
2668        :param `dc`: A device context to use when one is required for size calculations;
2669        :param `wnd`: The ribbon panel in question. Attributes like the panel label can
2670         be queried from this;
2671        :param `desired_bitmap_size`: MISSING DESCRIPTION;
2672        :param `expanded_panel_direction`: MISSING DESCRIPTION.
2673
2674        """
2675
2676        if desired_bitmap_size != None:
2677            desired_bitmap_size = wx.Size(16, 16)
2678
2679        if expanded_panel_direction != None:
2680            if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2681                expanded_panel_direction = wx.EAST
2682            else:
2683                expanded_panel_direction = wx.SOUTH
2684
2685        base_size = wx.Size(42, 42)
2686
2687        dc.SetFont(self._panel_label_font)
2688        label_size = wx.Size(*dc.GetTextExtent(wnd.GetLabel()))
2689        label_size.IncBy(2, 2) # Allow for differences between this DC and a paint DC
2690        label_size.IncBy(6, 0) # Padding
2691        label_size.y *= 2 # Second line for dropdown button
2692
2693        if self._flags & RIBBON_BAR_FLOW_VERTICAL:
2694            # Label alongside icon
2695            return wx.Size(base_size.x + label_size.x, max(base_size.y, label_size.y)), \
2696                   desired_bitmap_size, expanded_panel_direction
2697        else:
2698            # Label beneath icon
2699            return wx.Size(max(base_size.x, label_size.x), base_size.y + label_size.y), \
2700                   desired_bitmap_size, expanded_panel_direction
2701
2702
2703    def GetToolSize(self, dc, wnd, bitmap_size, kind, is_first, is_last, dropdown_region=None):
2704        """
2705        Calculate the size of a tool within a :class:`~wx.lib.agw.ribbon.toolbar.RibbonToolBar`.
2706
2707        :param `dc`: A device context to use when one is required for size calculations;
2708        :param `wnd`: The window onto which the tool will eventually be drawn;
2709        :param `bitmap_size`: The size of the tool's foreground bitmap;
2710        :param `kind`: The kind of tool (normal, dropdown, or hybrid);
2711        :param `is_first`: ``True`` if the tool is the first within its group. ``False``
2712         otherwise;
2713        :param `is_last`: ``True`` if the tool is the last within its group. ``False``
2714         otherwise;
2715        :param `dropdown_region`: For dropdown and hybrid tools, the region within the
2716         returned size which counts as the dropdown part.
2717        """
2718
2719        size = wx.Size(*bitmap_size)
2720        size.IncBy(7, 6)
2721
2722        if is_last:
2723            size.IncBy(1, 0)
2724
2725        if kind & RIBBON_BUTTON_DROPDOWN:
2726            size.IncBy(8, 0)
2727            if kind == RIBBON_BUTTON_DROPDOWN:
2728                dropdown_region = wx.Rect(0, 0, *size)
2729            else:
2730                dropdown_region = wx.Rect(size.GetWidth() - 8, 0, 8, size.GetHeight())
2731        else:
2732            dropdown_region = wx.Rect(0, 0, 0, 0)
2733
2734        return size, dropdown_region
2735
2736