~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to wxPython/wx/lib/agw/artmanager.py

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
This module contains drawing routines and customizations for the AGW widgets
 
3
:class:`~lib.agw.labelbook.LabelBook` and :class:`~lib.agw.flatmenu.FlatMenu`.
 
4
"""
 
5
 
 
6
import wx
 
7
import cStringIO
 
8
import random
 
9
 
 
10
from fmresources import *
 
11
 
 
12
# ---------------------------------------------------------------------------- #
 
13
# Class DCSaver
 
14
# ---------------------------------------------------------------------------- #
 
15
 
 
16
_ = wx.GetTranslation
 
17
 
 
18
_libimported = None
 
19
 
 
20
if wx.Platform == "__WXMSW__":
 
21
    osVersion = wx.GetOsVersion()
 
22
    # Shadows behind menus are supported only in XP
 
23
    if osVersion[1] == 5 and osVersion[2] == 1:
 
24
        try:
 
25
            import win32api
 
26
            import win32con
 
27
            import winxpgui
 
28
            _libimported = "MH"
 
29
        except:
 
30
            try:
 
31
                import ctypes
 
32
                _libimported = "ctypes"
 
33
            except:
 
34
                pass
 
35
    else:
 
36
        _libimported = None
 
37
 
 
38
 
 
39
class DCSaver(object):
 
40
    """
 
41
    Construct a DC saver. The dc is copied as-is.
 
42
    """
 
43
 
 
44
    def __init__(self, pdc):
 
45
        """
 
46
        Default class constructor.
 
47
 
 
48
        :param `pdc`: an instance of :class:`DC`.        
 
49
        """
 
50
 
 
51
        self._pdc = pdc
 
52
        self._pen = pdc.GetPen()
 
53
        self._brush = pdc.GetBrush()
 
54
 
 
55
 
 
56
    def __del__(self):
 
57
        """ While destructing, restores the dc pen and brush. """
 
58
 
 
59
        if self._pdc:
 
60
            self._pdc.SetPen(self._pen)
 
61
            self._pdc.SetBrush(self._brush)
 
62
 
 
63
 
 
64
# ---------------------------------------------------------------------------- #
 
65
# Class RendererBase
 
66
# ---------------------------------------------------------------------------- #
 
67
 
 
68
class RendererBase(object):
 
69
    """ Base class for all theme renderers. """
 
70
    
 
71
    def __init__(self):
 
72
        """ Default class constructor. Intentionally empty. """
 
73
        
 
74
        pass
 
75
 
 
76
 
 
77
    def DrawButtonBorders(self, dc, rect, penColour, brushColour):
 
78
        """
 
79
        Draws borders for buttons.
 
80
 
 
81
        :param `dc`: an instance of :class:`DC`;
 
82
        :param Rect `rect`: the button's client rectangle;
 
83
        :param `penColour`: a valid :class:`Colour` for the pen border;
 
84
        :param `brushColour`: a valid :class:`Colour` for the brush.
 
85
        """
 
86
 
 
87
        # Keep old pen and brush
 
88
        dcsaver = DCSaver(dc)
 
89
        dc.SetPen(wx.Pen(penColour))
 
90
        dc.SetBrush(wx.Brush(brushColour))
 
91
        dc.DrawRectangleRect(rect)
 
92
 
 
93
 
 
94
    def DrawBitmapArea(self, dc, xpm_name, rect, baseColour, flipSide):
 
95
        """
 
96
        Draws the area below a bitmap and the bitmap itself using a gradient shading.
 
97
 
 
98
        :param `dc`: an instance of :class:`DC`;
 
99
        :param string `xpm_name`: a name of a XPM bitmap;
 
100
        :param Rect `rect`: the bitmap client rectangle;
 
101
        :param `baseColour`: a valid :class:`Colour` for the bitmap background;
 
102
        :param bool `flipSide`: ``True`` to flip the gradient direction, ``False`` otherwise.
 
103
        """
 
104
 
 
105
        # draw the gradient area
 
106
        if not flipSide:
 
107
            ArtManager.Get().PaintDiagonalGradientBox(dc, rect, wx.WHITE,
 
108
                                                      ArtManager.Get().LightColour(baseColour, 20),
 
109
                                                      True, False)
 
110
        else:
 
111
            ArtManager.Get().PaintDiagonalGradientBox(dc, rect, ArtManager.Get().LightColour(baseColour, 20),
 
112
                                                      wx.WHITE, True, False)
 
113
 
 
114
        # draw arrow
 
115
        arrowDown = wx.BitmapFromXPMData(xpm_name)
 
116
        arrowDown.SetMask(wx.Mask(arrowDown, wx.WHITE))
 
117
        dc.DrawBitmap(arrowDown, rect.x + 1 , rect.y + 1, True)
 
118
 
 
119
 
 
120
    def DrawBitmapBorders(self, dc, rect, penColour, bitmapBorderUpperLeftPen):
 
121
        """
 
122
        Draws borders for a bitmap.
 
123
 
 
124
        :param `dc`: an instance of :class:`DC`;
 
125
        :param Rect `rect`: the button's client rectangle;
 
126
        :param `penColour`: a valid :class:`Colour` for the pen border;
 
127
        :param `bitmapBorderUpperLeftPen`: a valid :class:`Colour` for the pen upper
 
128
         left border.
 
129
        """
 
130
 
 
131
        # Keep old pen and brush
 
132
        dcsaver = DCSaver(dc)
 
133
 
 
134
        # lower right size
 
135
        dc.SetPen(wx.Pen(penColour))
 
136
        dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1)
 
137
        dc.DrawLine(rect.x + rect.width - 1, rect.y, rect.x + rect.width - 1, rect.y + rect.height)
 
138
        
 
139
        # upper left side
 
140
        dc.SetPen(wx.Pen(bitmapBorderUpperLeftPen))
 
141
        dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y)
 
142
        dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height)
 
143
 
 
144
 
 
145
    def GetMenuFaceColour(self):
 
146
        """
 
147
        Returns the foreground colour for the menu.
 
148
 
 
149
        :return: An instance of :class:`Colour`.
 
150
        """
 
151
        
 
152
        return ArtManager.Get().LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 80)
 
153
 
 
154
 
 
155
    def GetTextColourEnable(self):
 
156
        """
 
157
        Returns the colour used for text colour when enabled.
 
158
 
 
159
        :return: An instance of :class:`Colour`.
 
160
        """
 
161
 
 
162
        return wx.BLACK
 
163
 
 
164
 
 
165
    def GetTextColourDisable(self):
 
166
        """
 
167
        Returns the colour used for text colour when disabled.
 
168
 
 
169
        :return: An instance of :class:`Colour`.
 
170
        """
 
171
 
 
172
        return ArtManager.Get().LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT), 30)
 
173
 
 
174
 
 
175
    def GetFont(self):
 
176
        """
 
177
        Returns the font used for text.
 
178
 
 
179
        :return: An instance of :class:`Font`.
 
180
        """
 
181
 
 
182
        return wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
 
183
 
 
184
                
 
185
# ---------------------------------------------------------------------------- #
 
186
# Class RendererXP
 
187
# ---------------------------------------------------------------------------- #
 
188
 
 
189
class RendererXP(RendererBase):
 
190
    """ Xp-Style renderer. """
 
191
    
 
192
    def __init__(self):
 
193
        """ Default class constructor. """
 
194
 
 
195
        RendererBase.__init__(self)
 
196
 
 
197
 
 
198
    def DrawButton(self, dc, rect, state, input=None):
 
199
        """
 
200
        Draws a button using the XP theme.
 
201
 
 
202
        :param `dc`: an instance of :class:`DC`;
 
203
        :param Rect `rect`: the button's client rectangle;
 
204
        :param integer `state`: the button state;
 
205
        :param `input`: a flag used to call the right method.
 
206
        """
 
207
 
 
208
        if input is None or type(input) == type(False):
 
209
            self.DrawButtonTheme(dc, rect, state, input)
 
210
        else:
 
211
            self.DrawButtonColour(dc, rect, state, input)
 
212
 
 
213
            
 
214
    def DrawButtonTheme(self, dc, rect, state, useLightColours=None):
 
215
        """
 
216
        Draws a button using the XP theme.
 
217
 
 
218
        :param `dc`: an instance of :class:`DC`;
 
219
        :param Rect `rect`: the button's client rectangle;
 
220
        :param integer `state`: the button state;
 
221
        :param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
 
222
        """
 
223
 
 
224
        # switch according to the status
 
225
        if state == ControlFocus:
 
226
            penColour = ArtManager.Get().FrameColour()
 
227
            brushColour = ArtManager.Get().BackgroundColour()
 
228
        elif state == ControlPressed:
 
229
            penColour = ArtManager.Get().FrameColour()
 
230
            brushColour = ArtManager.Get().HighlightBackgroundColour()
 
231
        else:
 
232
            penColour = ArtManager.Get().FrameColour()
 
233
            brushColour = ArtManager.Get().BackgroundColour()        
 
234
 
 
235
        # Draw the button borders
 
236
        self.DrawButtonBorders(dc, rect, penColour, brushColour)
 
237
 
 
238
 
 
239
    def DrawButtonColour(self, dc, rect, state, colour):
 
240
        """
 
241
        Draws a button using the XP theme.
 
242
 
 
243
        :param `dc`: an instance of :class:`DC`;
 
244
        :param Rect `rect`: the button's client rectangle;
 
245
        :param integer `state`: the button state;
 
246
        :param `colour`: a valid :class:`Colour` instance.
 
247
        """
 
248
 
 
249
        # switch according to the status        
 
250
        if statet == ControlFocus:
 
251
            penColour = colour
 
252
            brushColour = ArtManager.Get().LightColour(colour, 75)
 
253
        elif state == ControlPressed:
 
254
            penColour = colour
 
255
            brushColour = ArtManager.Get().LightColour(colour, 60)
 
256
        else:
 
257
            penColour = colour
 
258
            brushColour = ArtManager.Get().LightColour(colour, 75)
 
259
 
 
260
        # Draw the button borders
 
261
        self.DrawButtonBorders(dc, rect, penColour, brushColour)
 
262
 
 
263
 
 
264
    def DrawMenuBarBg(self, dc, rect):
 
265
        """
 
266
        Draws the menu bar background according to the active theme.
 
267
 
 
268
        :param `dc`: an instance of :class:`DC`;
 
269
        :param Rect `rect`: the menu bar's client rectangle.
 
270
        """
 
271
 
 
272
        # For office style, we simple draw a rectangle with a gradient colouring
 
273
        artMgr = ArtManager.Get()
 
274
        vertical = artMgr.GetMBVerticalGradient()
 
275
 
 
276
        dcsaver = DCSaver(dc)
 
277
 
 
278
        # fill with gradient
 
279
        startColour = artMgr.GetMenuBarFaceColour()
 
280
        if artMgr.IsDark(startColour):
 
281
            startColour = artMgr.LightColour(startColour, 50)
 
282
 
 
283
        endColour = artMgr.LightColour(startColour, 90)
 
284
        artMgr.PaintStraightGradientBox(dc, rect, startColour, endColour, vertical)
 
285
 
 
286
        # Draw the border
 
287
        if artMgr.GetMenuBarBorder():
 
288
 
 
289
            dc.SetPen(wx.Pen(startColour))
 
290
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
 
291
            dc.DrawRectangleRect(rect)
 
292
 
 
293
 
 
294
    def DrawToolBarBg(self, dc, rect):
 
295
        """
 
296
        Draws the toolbar background according to the active theme.
 
297
 
 
298
        :param `dc`: an instance of :class:`DC`;
 
299
        :param Rect `rect`: the toolbar's client rectangle.
 
300
        """
 
301
 
 
302
        artMgr = ArtManager.Get()
 
303
        
 
304
        if not artMgr.GetRaiseToolbar():
 
305
            return
 
306
 
 
307
        # For office style, we simple draw a rectangle with a gradient colouring
 
308
        vertical = artMgr.GetMBVerticalGradient()
 
309
 
 
310
        dcsaver = DCSaver(dc)
 
311
 
 
312
        # fill with gradient
 
313
        startColour = artMgr.GetMenuBarFaceColour()
 
314
        if artMgr.IsDark(startColour):
 
315
            startColour = artMgr.LightColour(startColour, 50)
 
316
    
 
317
        startColour = artMgr.LightColour(startColour, 20)
 
318
 
 
319
        endColour   = artMgr.LightColour(startColour, 90)
 
320
        artMgr.PaintStraightGradientBox(dc, rect, startColour, endColour, vertical)
 
321
        artMgr.DrawBitmapShadow(dc, rect)
 
322
 
 
323
 
 
324
    def GetTextColourEnable(self):
 
325
        """
 
326
        Returns the colour used for text colour when enabled.
 
327
 
 
328
        :return: An instance of :class:`Colour`.
 
329
        """
 
330
 
 
331
        return wx.BLACK
 
332
 
 
333
    
 
334
# ---------------------------------------------------------------------------- #
 
335
# Class RendererMSOffice2007
 
336
# ---------------------------------------------------------------------------- #
 
337
 
 
338
class RendererMSOffice2007(RendererBase):
 
339
    """ Windows MS Office 2007 style. """
 
340
    
 
341
    def __init__(self):
 
342
        """ Default class constructor. """
 
343
 
 
344
        RendererBase.__init__(self)
 
345
 
 
346
 
 
347
    def GetColoursAccordingToState(self, state):
 
348
        """
 
349
        Returns a :class:`Colour` according to the menu item state.
 
350
 
 
351
        :param integer `state`: one of the following bits:
 
352
 
 
353
         ==================== ======= ==========================
 
354
         Item State            Value  Description
 
355
         ==================== ======= ==========================         
 
356
         ``ControlPressed``         0 The item is pressed
 
357
         ``ControlFocus``           1 The item is focused
 
358
         ``ControlDisabled``        2 The item is disabled
 
359
         ``ControlNormal``          3 Normal state
 
360
         ==================== ======= ==========================
 
361
 
 
362
        :return: An instance of :class:`Colour`.        
 
363
        """
 
364
 
 
365
        # switch according to the status        
 
366
        if state == ControlFocus:
 
367
            upperBoxTopPercent = 95
 
368
            upperBoxBottomPercent = 50
 
369
            lowerBoxTopPercent = 40
 
370
            lowerBoxBottomPercent = 90
 
371
            concaveUpperBox = True
 
372
            concaveLowerBox = True
 
373
            
 
374
        elif state == ControlPressed:
 
375
            upperBoxTopPercent = 75
 
376
            upperBoxBottomPercent = 90
 
377
            lowerBoxTopPercent = 90
 
378
            lowerBoxBottomPercent = 40
 
379
            concaveUpperBox = True
 
380
            concaveLowerBox = True
 
381
 
 
382
        elif state == ControlDisabled:
 
383
            upperBoxTopPercent = 100
 
384
            upperBoxBottomPercent = 100
 
385
            lowerBoxTopPercent = 70
 
386
            lowerBoxBottomPercent = 70
 
387
            concaveUpperBox = True
 
388
            concaveLowerBox = True
 
389
 
 
390
        else:
 
391
            upperBoxTopPercent = 90
 
392
            upperBoxBottomPercent = 50
 
393
            lowerBoxTopPercent = 30
 
394
            lowerBoxBottomPercent = 75
 
395
            concaveUpperBox = True
 
396
            concaveLowerBox = True
 
397
 
 
398
        return upperBoxTopPercent, upperBoxBottomPercent, lowerBoxTopPercent, lowerBoxBottomPercent, \
 
399
               concaveUpperBox, concaveLowerBox
 
400
 
 
401
        
 
402
    def DrawButton(self, dc, rect, state, useLightColours):
 
403
        """
 
404
        Draws a button using the MS Office 2007 theme.
 
405
 
 
406
        :param `dc`: an instance of :class:`DC`;
 
407
        :param Rect `rect`: the button's client rectangle;
 
408
        :param integer `state`: the button state;
 
409
        :param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
 
410
        """
 
411
 
 
412
        self.DrawButtonColour(dc, rect, state, ArtManager.Get().GetThemeBaseColour(useLightColours))
 
413
 
 
414
 
 
415
    def DrawButtonColour(self, dc, rect, state, colour):
 
416
        """
 
417
        Draws a button using the MS Office 2007 theme.
 
418
 
 
419
        :param `dc`: an instance of :class:`DC`;
 
420
        :param Rect `rect`: the button's client rectangle;
 
421
        :param integer `state`: the button state;
 
422
        :param `colour`: a valid :class:`Colour` instance.
 
423
        """
 
424
 
 
425
        artMgr = ArtManager.Get()
 
426
        
 
427
        # Keep old pen and brush
 
428
        dcsaver = DCSaver(dc)
 
429
        
 
430
        # Define the rounded rectangle base on the given rect
 
431
        # we need an array of 9 points for it        
 
432
        baseColour = colour
 
433
 
 
434
        # Define the middle points
 
435
        leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
 
436
        rightPt = wx.Point(rect.x + rect.width-1, rect.y + (rect.height / 2))
 
437
 
 
438
        # Define the top region
 
439
        top = wx.RectPP((rect.GetLeft(), rect.GetTop()), rightPt)
 
440
        bottom = wx.RectPP(leftPt, (rect.GetRight(), rect.GetBottom()))
 
441
 
 
442
        upperBoxTopPercent, upperBoxBottomPercent, lowerBoxTopPercent, lowerBoxBottomPercent, \
 
443
                            concaveUpperBox, concaveLowerBox = self.GetColoursAccordingToState(state)
 
444
 
 
445
        topStartColour = artMgr.LightColour(baseColour, upperBoxTopPercent)
 
446
        topEndColour = artMgr.LightColour(baseColour, upperBoxBottomPercent)
 
447
        bottomStartColour = artMgr.LightColour(baseColour, lowerBoxTopPercent)
 
448
        bottomEndColour = artMgr.LightColour(baseColour, lowerBoxBottomPercent)
 
449
 
 
450
        artMgr.PaintStraightGradientBox(dc, top, topStartColour, topEndColour)
 
451
        artMgr.PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
 
452
 
 
453
        rr = wx.Rect(rect.x, rect.y, rect.width, rect.height)
 
454
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
 
455
 
 
456
        frameColour = artMgr.LightColour(baseColour, 60)
 
457
        dc.SetPen(wx.Pen(frameColour))
 
458
        dc.DrawRectangleRect(rr)
 
459
 
 
460
        wc = artMgr.LightColour(baseColour, 80)
 
461
        dc.SetPen(wx.Pen(wc))
 
462
        rr.Deflate(1, 1)
 
463
        dc.DrawRectangleRect(rr)
 
464
 
 
465
 
 
466
    def DrawMenuBarBg(self, dc, rect):
 
467
        """
 
468
        Draws the menu bar background according to the active theme.
 
469
 
 
470
        :param `dc`: an instance of :class:`DC`;
 
471
        :param Rect `rect`: the menu bar's client rectangle.
 
472
        """
 
473
 
 
474
        # Keep old pen and brush
 
475
        dcsaver = DCSaver(dc)
 
476
        artMgr = ArtManager.Get()
 
477
        baseColour = artMgr.GetMenuBarFaceColour()
 
478
 
 
479
        dc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
 
480
        dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
 
481
        dc.DrawRectangleRect(rect)
 
482
 
 
483
        # Define the rounded rectangle base on the given rect
 
484
        # we need an array of 9 points for it
 
485
        regPts = [wx.Point() for ii in xrange(9)]
 
486
        radius = 2
 
487
        
 
488
        regPts[0] = wx.Point(rect.x, rect.y + radius)
 
489
        regPts[1] = wx.Point(rect.x+radius, rect.y)
 
490
        regPts[2] = wx.Point(rect.x+rect.width-radius-1, rect.y)
 
491
        regPts[3] = wx.Point(rect.x+rect.width-1, rect.y + radius)
 
492
        regPts[4] = wx.Point(rect.x+rect.width-1, rect.y + rect.height - radius - 1)
 
493
        regPts[5] = wx.Point(rect.x+rect.width-radius-1, rect.y + rect.height-1)
 
494
        regPts[6] = wx.Point(rect.x+radius, rect.y + rect.height-1)
 
495
        regPts[7] = wx.Point(rect.x, rect.y + rect.height - radius - 1)
 
496
        regPts[8] = regPts[0]
 
497
 
 
498
        # Define the middle points
 
499
 
 
500
        factor = artMgr.GetMenuBgFactor()
 
501
        
 
502
        leftPt1 = wx.Point(rect.x, rect.y + (rect.height / factor))
 
503
        leftPt2 = wx.Point(rect.x, rect.y + (rect.height / factor)*(factor-1))
 
504
 
 
505
        rightPt1 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor))
 
506
        rightPt2 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor)*(factor-1))
 
507
 
 
508
        # Define the top region
 
509
        topReg = [wx.Point() for ii in xrange(7)]
 
510
        topReg[0] = regPts[0]
 
511
        topReg[1] = regPts[1]
 
512
        topReg[2] = wx.Point(regPts[2].x+1, regPts[2].y)
 
513
        topReg[3] = wx.Point(regPts[3].x + 1, regPts[3].y)
 
514
        topReg[4] = wx.Point(rightPt1.x, rightPt1.y+1)
 
515
        topReg[5] = wx.Point(leftPt1.x, leftPt1.y+1)
 
516
        topReg[6] = topReg[0]
 
517
 
 
518
        # Define the middle region
 
519
        middle = wx.RectPP(leftPt1, wx.Point(rightPt2.x - 2, rightPt2.y))
 
520
            
 
521
        # Define the bottom region
 
522
        bottom = wx.RectPP(leftPt2, wx.Point(rect.GetRight() - 1, rect.GetBottom()))
 
523
 
 
524
        topStartColour   = artMgr.LightColour(baseColour, 90)
 
525
        topEndColour = artMgr.LightColour(baseColour, 60)
 
526
        bottomStartColour = artMgr.LightColour(baseColour, 40)
 
527
        bottomEndColour   = artMgr.LightColour(baseColour, 20)
 
528
        
 
529
        topRegion = wx.RegionFromPoints(topReg)
 
530
 
 
531
        artMgr.PaintGradientRegion(dc, topRegion, topStartColour, topEndColour)
 
532
        artMgr.PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
 
533
        artMgr.PaintStraightGradientBox(dc, middle, topEndColour, bottomStartColour)
 
534
     
 
535
 
 
536
    def DrawToolBarBg(self, dc, rect):
 
537
        """
 
538
        Draws the toolbar background according to the active theme.
 
539
 
 
540
        :param `dc`: an instance of :class:`DC`;
 
541
        :param Rect `rect`: the toolbar's client rectangle.
 
542
        """
 
543
 
 
544
        artMgr = ArtManager.Get()
 
545
        
 
546
        if not artMgr.GetRaiseToolbar():
 
547
            return
 
548
 
 
549
        # Keep old pen and brush
 
550
        dcsaver = DCSaver(dc)
 
551
        
 
552
        baseColour = artMgr.GetMenuBarFaceColour()
 
553
        baseColour = artMgr.LightColour(baseColour, 20)
 
554
 
 
555
        dc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
 
556
        dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
 
557
        dc.DrawRectangleRect(rect)
 
558
 
 
559
        radius = 2
 
560
        
 
561
        # Define the rounded rectangle base on the given rect
 
562
        # we need an array of 9 points for it
 
563
        regPts = [None]*9
 
564
        
 
565
        regPts[0] = wx.Point(rect.x, rect.y + radius)
 
566
        regPts[1] = wx.Point(rect.x+radius, rect.y)
 
567
        regPts[2] = wx.Point(rect.x+rect.width-radius-1, rect.y)
 
568
        regPts[3] = wx.Point(rect.x+rect.width-1, rect.y + radius)
 
569
        regPts[4] = wx.Point(rect.x+rect.width-1, rect.y + rect.height - radius - 1)
 
570
        regPts[5] = wx.Point(rect.x+rect.width-radius-1, rect.y + rect.height-1)
 
571
        regPts[6] = wx.Point(rect.x+radius, rect.y + rect.height-1)
 
572
        regPts[7] = wx.Point(rect.x, rect.y + rect.height - radius - 1)
 
573
        regPts[8] = regPts[0]
 
574
 
 
575
        # Define the middle points
 
576
        factor = artMgr.GetMenuBgFactor()
 
577
 
 
578
        leftPt1 = wx.Point(rect.x, rect.y + (rect.height / factor))
 
579
        rightPt1 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor))
 
580
        
 
581
        leftPt2 = wx.Point(rect.x, rect.y + (rect.height / factor)*(factor-1))
 
582
        rightPt2 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor)*(factor-1))
 
583
 
 
584
        # Define the top region
 
585
        topReg = [None]*7
 
586
        topReg[0] = regPts[0]
 
587
        topReg[1] = regPts[1]
 
588
        topReg[2] = wx.Point(regPts[2].x+1, regPts[2].y)
 
589
        topReg[3] = wx.Point(regPts[3].x + 1, regPts[3].y)
 
590
        topReg[4] = wx.Point(rightPt1.x, rightPt1.y+1)
 
591
        topReg[5] = wx.Point(leftPt1.x, leftPt1.y+1)
 
592
        topReg[6] = topReg[0]
 
593
 
 
594
        # Define the middle region
 
595
        middle = wx.RectPP(leftPt1, wx.Point(rightPt2.x - 2, rightPt2.y))
 
596
 
 
597
        # Define the bottom region
 
598
        bottom = wx.RectPP(leftPt2, wx.Point(rect.GetRight() - 1, rect.GetBottom()))
 
599
        
 
600
        topStartColour   = artMgr.LightColour(baseColour, 90)
 
601
        topEndColour = artMgr.LightColour(baseColour, 60)
 
602
        bottomStartColour = artMgr.LightColour(baseColour, 40)
 
603
        bottomEndColour   = artMgr.LightColour(baseColour, 20)
 
604
        
 
605
        topRegion = wx.RegionFromPoints(topReg)
 
606
 
 
607
        artMgr.PaintGradientRegion(dc, topRegion, topStartColour, topEndColour)
 
608
        artMgr.PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
 
609
        artMgr.PaintStraightGradientBox(dc, middle, topEndColour, bottomStartColour)
 
610
 
 
611
        artMgr.DrawBitmapShadow(dc, rect)
 
612
 
 
613
 
 
614
    def GetTextColourEnable(self):
 
615
        """
 
616
        Returns the colour used for text colour when enabled.
 
617
 
 
618
        :return: An instance of :class:`Colour`.
 
619
        """
 
620
 
 
621
        return wx.NamedColour("MIDNIGHT BLUE")
 
622
    
 
623
    
 
624
# ---------------------------------------------------------------------------- #
 
625
# Class ArtManager
 
626
# ---------------------------------------------------------------------------- #
 
627
 
 
628
class ArtManager(wx.EvtHandler):
 
629
 
 
630
    """
 
631
    This class provides various art utilities, such as creating shadow, providing
 
632
    lighter / darker colours for a given colour, etc...
 
633
    """
 
634
    
 
635
    _alignmentBuffer = 7
 
636
    _menuTheme = StyleXP
 
637
    _verticalGradient = False
 
638
    _renderers = {StyleXP: None, Style2007: None}
 
639
    _bmpShadowEnabled = False
 
640
    _ms2007sunken = False
 
641
    _drowMBBorder = True
 
642
    _menuBgFactor = 5
 
643
    _menuBarColourScheme = _("Default")
 
644
    _raiseTB = True
 
645
    _bitmaps = {}
 
646
    _transparency = 255
 
647
 
 
648
    def __init__(self):
 
649
        """ Default class constructor. """
 
650
 
 
651
        wx.EvtHandler.__init__(self)
 
652
        self._menuBarBgColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
 
653
        
 
654
        # connect an event handler to the system colour change event
 
655
        self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChange)
 
656
 
 
657
 
 
658
    def SetTransparency(self, amount):
 
659
        """
 
660
        Sets the alpha channel value for transparent windows.
 
661
 
 
662
        :param integer `amount`: the actual transparency value (between 0 and 255).
 
663
 
 
664
        :raise: `Exception` if the `amount` parameter is lower than ``0`` or greater than ``255``.        
 
665
        """
 
666
 
 
667
        if self._transparency == amount:
 
668
            return
 
669
 
 
670
        if amount < 0 or amount > 255:
 
671
            raise Exception("Invalid transparency value")
 
672
 
 
673
        self._transparency = amount
 
674
 
 
675
 
 
676
    def GetTransparency(self):
 
677
        """
 
678
        Returns the alpha channel value for transparent windows.
 
679
 
 
680
        :return: An integer representing the alpha channel value.
 
681
        """
 
682
 
 
683
        return self._transparency
 
684
    
 
685
 
 
686
    def ConvertToBitmap(self, xpm, alpha=None):
 
687
        """
 
688
        Convert the given image to a bitmap, optionally overlaying an alpha
 
689
        channel to it.
 
690
 
 
691
        :param `xpm`: a list of strings formatted as XPM;
 
692
        :type `xpm`: list of strings
 
693
        :param `alpha`: a list of alpha values, the same size as the xpm bitmap.
 
694
        :type `alpha`: list of integers
 
695
 
 
696
        :return: An instance of :class:`Bitmap`.        
 
697
        """
 
698
 
 
699
        if alpha is not None:
 
700
 
 
701
            img = wx.BitmapFromXPMData(xpm)
 
702
            img = img.ConvertToImage()
 
703
            x, y = img.GetWidth(), img.GetHeight()
 
704
            img.InitAlpha()
 
705
            for jj in xrange(y):
 
706
                for ii in xrange(x):
 
707
                    img.SetAlpha(ii, jj, alpha[jj*x+ii])
 
708
                    
 
709
        else:
 
710
 
 
711
            stream = cStringIO.StringIO(xpm)
 
712
            img = wx.ImageFromStream(stream)
 
713
            
 
714
        return wx.BitmapFromImage(img)
 
715
 
 
716
                
 
717
    def Initialize(self):
 
718
        """ Initializes the bitmaps and colours. """
 
719
 
 
720
        # create wxBitmaps from the xpm's
 
721
        self._rightBottomCorner = self.ConvertToBitmap(shadow_center_xpm, shadow_center_alpha)
 
722
        self._bottom = self.ConvertToBitmap(shadow_bottom_xpm, shadow_bottom_alpha)
 
723
        self._bottomLeft = self.ConvertToBitmap(shadow_bottom_left_xpm, shadow_bottom_left_alpha)
 
724
        self._rightTop = self.ConvertToBitmap(shadow_right_top_xpm, shadow_right_top_alpha)
 
725
        self._right = self.ConvertToBitmap(shadow_right_xpm, shadow_right_alpha)
 
726
 
 
727
        # initialise the colour map
 
728
        self.InitColours()
 
729
        self.SetMenuBarColour(self._menuBarColourScheme)
 
730
        
 
731
        # Create common bitmaps
 
732
        self.FillStockBitmaps()
 
733
 
 
734
 
 
735
    def FillStockBitmaps(self):
 
736
        """ Initializes few standard bitmaps. """
 
737
 
 
738
        bmp = self.ConvertToBitmap(arrow_down, alpha=None)
 
739
        bmp.SetMask(wx.Mask(bmp, wx.Colour(0, 128, 128)))
 
740
        self._bitmaps.update({"arrow_down": bmp})
 
741
 
 
742
        bmp = self.ConvertToBitmap(arrow_up, alpha=None)
 
743
        bmp.SetMask(wx.Mask(bmp, wx.Colour(0, 128, 128)))
 
744
        self._bitmaps.update({"arrow_up": bmp})
 
745
 
 
746
 
 
747
    def GetStockBitmap(self, name):
 
748
        """
 
749
        Returns a bitmap from a stock. 
 
750
 
 
751
        :param string `name`: the bitmap name.
 
752
 
 
753
        :return: The stock bitmap, if `name` was found in the stock bitmap dictionary.
 
754
         Othewise, :class:`NullBitmap` is returned.
 
755
        """
 
756
 
 
757
        if self._bitmaps.has_key(name):
 
758
            return self._bitmaps[name]
 
759
 
 
760
        return wx.NullBitmap
 
761
 
 
762
 
 
763
    def Get(self):
 
764
        """
 
765
        Accessor to the unique art manager object.
 
766
 
 
767
        :return: A unique instance of :class:`ArtManager`.
 
768
        """
 
769
 
 
770
        if not hasattr(self, "_instance"):
 
771
        
 
772
            self._instance = ArtManager()
 
773
            self._instance.Initialize()
 
774
 
 
775
            # Initialize the renderers map
 
776
            self._renderers[StyleXP] = RendererXP()
 
777
            self._renderers[Style2007] = RendererMSOffice2007()
 
778
 
 
779
        return self._instance
 
780
 
 
781
    Get = classmethod(Get)
 
782
    
 
783
    def Free(self):
 
784
        """ Destructor for the unique art manager object. """
 
785
 
 
786
        if hasattr(self, "_instance"):
 
787
        
 
788
            del self._instance
 
789
 
 
790
    Free = classmethod(Free)
 
791
 
 
792
 
 
793
    def OnSysColourChange(self, event):
 
794
        """
 
795
        Handles the ``wx.EVT_SYS_COLOUR_CHANGED`` event for :class:`ArtManager`.
 
796
 
 
797
        :param `event`: a :class:`SysColourChangedEvent` event to be processed.        
 
798
        """
 
799
 
 
800
        # reinitialise the colour map
 
801
        self.InitColours()
 
802
 
 
803
 
 
804
    def LightColour(self, colour, percent):
 
805
        """
 
806
        Return light contrast of `colour`. The colour returned is from the scale of
 
807
        `colour` ==> white.
 
808
 
 
809
        :param `colour`: the input colour to be brightened, an instance of :class:`Colour`;
 
810
        :param integer `percent`: determines how light the colour will be. `percent` = ``100``
 
811
         returns white, `percent` = ``0`` returns `colour`.
 
812
 
 
813
        :return: A light contrast of the input `colour`, an instance of :class:`Colour`.         
 
814
        """
 
815
 
 
816
        end_colour = wx.WHITE
 
817
        rd = end_colour.Red() - colour.Red()
 
818
        gd = end_colour.Green() - colour.Green()
 
819
        bd = end_colour.Blue() - colour.Blue()
 
820
        high = 100
 
821
 
 
822
        # We take the percent way of the colour from colour -. white
 
823
        i = percent
 
824
        r = colour.Red() + ((i*rd*100)/high)/100
 
825
        g = colour.Green() + ((i*gd*100)/high)/100
 
826
        b = colour.Blue() + ((i*bd*100)/high)/100
 
827
 
 
828
        return wx.Colour(r, g, b)
 
829
 
 
830
 
 
831
    def DarkColour(self, colour, percent):
 
832
        """
 
833
        Like the :meth:`~ArtManager.LightColour` function, but create the colour darker by `percent`.
 
834
 
 
835
        :param `colour`: the input colour to be darkened, an instance of :class:`Colour`;
 
836
        :param integer `percent`: determines how dark the colour will be. `percent` = ``100``
 
837
         returns black, `percent` = ``0`` returns `colour`.
 
838
 
 
839
        :return: A dark contrast of the input `colour`, an instance of :class:`Colour`.                  
 
840
        """
 
841
 
 
842
        end_colour = wx.BLACK
 
843
        rd = end_colour.Red() - colour.Red()
 
844
        gd = end_colour.Green() - colour.Green()
 
845
        bd = end_colour.Blue() - colour.Blue()
 
846
        high = 100
 
847
 
 
848
        # We take the percent way of the colour from colour -. white
 
849
        i = percent
 
850
        r = colour.Red() + ((i*rd*100)/high)/100
 
851
        g = colour.Green() + ((i*gd*100)/high)/100
 
852
        b = colour.Blue() + ((i*bd*100)/high)/100
 
853
 
 
854
        return wx.Colour(r, g, b)
 
855
 
 
856
 
 
857
    def PaintStraightGradientBox(self, dc, rect, startColour, endColour, vertical=True):
 
858
        """
 
859
        Paint the rectangle with gradient colouring; the gradient lines are either
 
860
        horizontal or vertical.
 
861
 
 
862
        :param `dc`: an instance of :class:`DC`;
 
863
        :param Rect `rect`: the rectangle to be filled with gradient shading;
 
864
        :param Colour `startColour`: the first colour of the gradient shading;
 
865
        :param Colour `endColour`: the second colour of the gradient shading;
 
866
        :param bool `vertical`: ``True`` for gradient colouring in the vertical direction,
 
867
         ``False`` for horizontal shading.
 
868
        """
 
869
 
 
870
        dcsaver = DCSaver(dc)
 
871
        
 
872
        if vertical:
 
873
            high = rect.GetHeight()-1
 
874
            direction = wx.SOUTH
 
875
        else:
 
876
            high = rect.GetWidth()-1
 
877
            direction = wx.EAST
 
878
 
 
879
        if high < 1:
 
880
            return
 
881
 
 
882
        dc.GradientFillLinear(rect, startColour, endColour, direction)
 
883
        
 
884
 
 
885
    def PaintGradientRegion(self, dc, region, startColour, endColour, vertical=True):
 
886
        """
 
887
        Paint a region with gradient colouring.
 
888
 
 
889
        :param `dc`: an instance of :class:`DC`;
 
890
        :param `region`: a region to be filled with gradient shading (an instance of
 
891
         :class:`Region`);
 
892
        :param Colour `startColour`: the first colour of the gradient shading;
 
893
        :param Colour `endColour`: the second colour of the gradient shading;
 
894
        :param bool `vertical`: ``True`` for gradient colouring in the vertical direction,
 
895
         ``False`` for horizontal shading.
 
896
 
 
897
        """
 
898
 
 
899
        # The way to achieve non-rectangle 
 
900
        memDC = wx.MemoryDC()
 
901
        rect = region.GetBox()
 
902
        bitmap = wx.EmptyBitmap(rect.width, rect.height)
 
903
        memDC.SelectObject(bitmap)
 
904
 
 
905
        # Colour the whole rectangle with gradient
 
906
        rr = wx.Rect(0, 0, rect.width, rect.height)
 
907
        self.PaintStraightGradientBox(memDC, rr, startColour, endColour, vertical)
 
908
 
 
909
        # Convert the region to a black and white bitmap with the white pixels being inside the region
 
910
        # we draw the bitmap over the gradient coloured rectangle, with mask set to white, 
 
911
        # this will cause our region to be coloured with the gradient, while area outside the 
 
912
        # region will be painted with black. then we simply draw the bitmap to the dc with mask set to 
 
913
        # black
 
914
        tmpRegion = wx.Region(rect.x, rect.y, rect.width, rect.height)
 
915
        tmpRegion.Offset(-rect.x, -rect.y)
 
916
        regionBmp = tmpRegion.ConvertToBitmap()
 
917
        regionBmp.SetMask(wx.Mask(regionBmp, wx.WHITE))
 
918
 
 
919
        # The function ConvertToBitmap() return a rectangle bitmap
 
920
        # which is shorter by 1 pixl on the height and width (this is correct behavior, since 
 
921
        # DrawLine does not include the second point as part of the line)
 
922
        # we fix this issue by drawing our own line at the bottom and left side of the rectangle
 
923
        memDC.SetPen(wx.BLACK_PEN)
 
924
        memDC.DrawBitmap(regionBmp, 0, 0, True)
 
925
        memDC.DrawLine(0, rr.height - 1, rr.width, rr.height - 1)
 
926
        memDC.DrawLine(rr.width - 1, 0, rr.width - 1, rr.height)
 
927
 
 
928
        memDC.SelectObject(wx.NullBitmap)
 
929
        bitmap.SetMask(wx.Mask(bitmap, wx.BLACK))
 
930
        dc.DrawBitmap(bitmap, rect.x, rect.y, True)
 
931
 
 
932
 
 
933
    def PaintDiagonalGradientBox(self, dc, rect, startColour, endColour,
 
934
                                 startAtUpperLeft=True, trimToSquare=True):
 
935
        """
 
936
        Paint rectangle with gradient colouring; the gradient lines are diagonal
 
937
        and may start from the upper left corner or from the upper right corner.
 
938
 
 
939
        :param `dc`: an instance of :class:`DC`;
 
940
        :param Rect `rect`: the rectangle to be filled with gradient shading;
 
941
        :param Colour `startColour`: the first colour of the gradient shading;
 
942
        :param Colour `endColour`: the second colour of the gradient shading;
 
943
        :param bool `startAtUpperLeft`: ``True`` to start the gradient lines at the upper
 
944
         left corner of the rectangle, ``False`` to start at the upper right corner;
 
945
        :param bool `trimToSquare`: ``True`` to trim the gradient lines in a square.
 
946
        """
 
947
 
 
948
        # Save the current pen and brush
 
949
        savedPen = dc.GetPen()
 
950
        savedBrush = dc.GetBrush()
 
951
 
 
952
        # gradient fill from colour 1 to colour 2 with top to bottom
 
953
        if rect.height < 1 or rect.width < 1:
 
954
            return
 
955
 
 
956
        # calculate some basic numbers
 
957
        size = rect.width
 
958
        sizeX = sizeY = 0
 
959
        proportion = 1
 
960
        
 
961
        if rect.width > rect.height:
 
962
        
 
963
            if trimToSquare:
 
964
            
 
965
                size = rect.height
 
966
                sizeX = sizeY = rect.height - 1
 
967
            
 
968
            else:
 
969
            
 
970
                proportion = float(rect.height)/float(rect.width)
 
971
                size = rect.width
 
972
                sizeX = rect.width - 1
 
973
                sizeY = rect.height -1
 
974
            
 
975
        else:
 
976
        
 
977
            if trimToSquare:
 
978
            
 
979
                size = rect.width
 
980
                sizeX = sizeY = rect.width - 1
 
981
            
 
982
            else:
 
983
            
 
984
                sizeX = rect.width - 1
 
985
                size = rect.height
 
986
                sizeY = rect.height - 1
 
987
                proportion = float(rect.width)/float(rect.height)
 
988
 
 
989
        # calculate gradient coefficients
 
990
        col2 = endColour
 
991
        col1 = startColour
 
992
 
 
993
        rf, gf, bf = 0, 0, 0
 
994
        rstep = float(col2.Red() - col1.Red())/float(size)
 
995
        gstep = float(col2.Green() - col1.Green())/float(size)
 
996
        bstep = float(col2.Blue() - col1.Blue())/float(size)
 
997
        
 
998
        # draw the upper triangle
 
999
        for i in xrange(size):
 
1000
        
 
1001
            currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)
 
1002
            dc.SetBrush(wx.Brush(currCol, wx.SOLID))
 
1003
            dc.SetPen(wx.Pen(currCol))
 
1004
            
 
1005
            if startAtUpperLeft:
 
1006
            
 
1007
                if rect.width > rect.height:
 
1008
                
 
1009
                    dc.DrawLine(rect.x + i, rect.y, rect.x, int(rect.y + proportion*i))
 
1010
                    dc.DrawPoint(rect.x, int(rect.y + proportion*i))
 
1011
                
 
1012
                else:
 
1013
                
 
1014
                    dc.DrawLine(int(rect.x + proportion*i), rect.y, rect.x, rect.y + i)
 
1015
                    dc.DrawPoint(rect.x, rect.y + i)
 
1016
                
 
1017
            else:
 
1018
            
 
1019
                if rect.width > rect.height:
 
1020
                
 
1021
                    dc.DrawLine(rect.x + sizeX - i, rect.y, rect.x + sizeX, int(rect.y + proportion*i))
 
1022
                    dc.DrawPoint(rect.x + sizeX, int(rect.y + proportion*i))
 
1023
                
 
1024
                else:
 
1025
                
 
1026
                    xTo = (int(rect.x + sizeX - proportion * i) > rect.x and [int(rect.x + sizeX - proportion*i)] or [rect.x])[0]
 
1027
                    dc.DrawLine(xTo, rect.y, rect.x + sizeX, rect.y + i)
 
1028
                    dc.DrawPoint(rect.x + sizeX, rect.y + i)
 
1029
                
 
1030
            rf += rstep/2
 
1031
            gf += gstep/2
 
1032
            bf += bstep/2
 
1033
        
 
1034
        # draw the lower triangle
 
1035
        for i in xrange(size):
 
1036
 
 
1037
            currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)        
 
1038
            dc.SetBrush(wx.Brush(currCol, wx.SOLID))
 
1039
            dc.SetPen(wx.Pen(currCol))
 
1040
            
 
1041
            if startAtUpperLeft:
 
1042
            
 
1043
                if rect.width > rect.height:
 
1044
                
 
1045
                    dc.DrawLine(rect.x + i, rect.y + sizeY, rect.x + sizeX, int(rect.y + proportion * i))
 
1046
                    dc.DrawPoint(rect.x + sizeX, int(rect.y + proportion * i))
 
1047
                
 
1048
                else:
 
1049
                
 
1050
                    dc.DrawLine(int(rect.x + proportion * i), rect.y + sizeY, rect.x + sizeX, rect.y + i)
 
1051
                    dc.DrawPoint(rect.x + sizeX, rect.y + i)
 
1052
                
 
1053
            else:
 
1054
            
 
1055
                if rect.width > rect.height:
 
1056
                
 
1057
                    dc.DrawLine(rect.x, (int)(rect.y + proportion * i), rect.x + sizeX - i, rect.y + sizeY)
 
1058
                    dc.DrawPoint(rect.x + sizeX - i, rect.y + sizeY)
 
1059
                
 
1060
                else:
 
1061
                
 
1062
                    xTo = (int(rect.x + sizeX - proportion*i) > rect.x and [int(rect.x + sizeX - proportion*i)] or [rect.x])[0]
 
1063
                    dc.DrawLine(rect.x, rect.y + i, xTo, rect.y + sizeY)
 
1064
                    dc.DrawPoint(xTo, rect.y + sizeY)
 
1065
                
 
1066
            rf += rstep/2
 
1067
            gf += gstep/2
 
1068
            bf += bstep/2
 
1069
        
 
1070
 
 
1071
        # Restore the pen and brush
 
1072
        dc.SetPen( savedPen )
 
1073
        dc.SetBrush( savedBrush )
 
1074
 
 
1075
 
 
1076
    def PaintCrescentGradientBox(self, dc, rect, startColour, endColour, concave=True):
 
1077
        """
 
1078
        Paint a region with gradient colouring. The gradient is in crescent shape
 
1079
        which fits the 2007 style.
 
1080
 
 
1081
        :param `dc`: an instance of :class:`DC`;
 
1082
        :param Rect `rect`: the rectangle to be filled with gradient shading;
 
1083
        :param Colour `startColour`: the first colour of the gradient shading;
 
1084
        :param Colour `endColour`: the second colour of the gradient shading;
 
1085
        :param bool `concave`: ``True`` for a concave effect, ``False`` for a convex one.
 
1086
        """
 
1087
 
 
1088
        diagonalRectWidth = rect.GetWidth()/4
 
1089
        spare = rect.width - 4*diagonalRectWidth
 
1090
        leftRect = wx.Rect(rect.x, rect.y, diagonalRectWidth, rect.GetHeight())
 
1091
        rightRect = wx.Rect(rect.x + 3 * diagonalRectWidth + spare, rect.y, diagonalRectWidth, rect.GetHeight())
 
1092
        
 
1093
        if concave:
 
1094
        
 
1095
            self.PaintStraightGradientBox(dc, rect, self.MixColours(startColour, endColour, 50), endColour)
 
1096
            self.PaintDiagonalGradientBox(dc, leftRect, startColour, endColour, True, False) 
 
1097
            self.PaintDiagonalGradientBox(dc, rightRect, startColour, endColour, False, False) 
 
1098
        
 
1099
        else:
 
1100
        
 
1101
            self.PaintStraightGradientBox(dc, rect, endColour, self.MixColours(endColour, startColour, 50))
 
1102
            self.PaintDiagonalGradientBox(dc, leftRect, endColour, startColour, False, False) 
 
1103
            self.PaintDiagonalGradientBox(dc, rightRect, endColour, startColour, True, False) 
 
1104
 
 
1105
 
 
1106
    def FrameColour(self):
 
1107
        """
 
1108
        Return the surrounding colour for a control.
 
1109
 
 
1110
        :return: An instance of :class:`Colour`.
 
1111
        """
 
1112
 
 
1113
        return wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
 
1114
 
 
1115
 
 
1116
    def BackgroundColour(self):
 
1117
        """
 
1118
        Returns the background colour of a control when not in focus.
 
1119
 
 
1120
        :return: An instance of :class:`Colour`.
 
1121
        """
 
1122
 
 
1123
        return self.LightColour(self.FrameColour(), 75)
 
1124
 
 
1125
 
 
1126
    def HighlightBackgroundColour(self):
 
1127
        """
 
1128
        Returns the background colour of a control when it is in focus.
 
1129
 
 
1130
        :return: An instance of :class:`Colour`.
 
1131
        """
 
1132
 
 
1133
        return self.LightColour(self.FrameColour(), 60)
 
1134
 
 
1135
 
 
1136
    def MixColours(self, firstColour, secondColour, percent):
 
1137
        """
 
1138
        Return mix of input colours.
 
1139
 
 
1140
        :param `firstColour`: the first colour to be mixed, an instance of :class:`Colour`;
 
1141
        :param `secondColour`: the second colour to be mixed, an instance of :class:`Colour`;
 
1142
        :param integer `percent`: the relative percentage of `firstColour` with respect to
 
1143
         `secondColour`.
 
1144
 
 
1145
        :return: An instance of :class:`Colour`.
 
1146
        """
 
1147
 
 
1148
        # calculate gradient coefficients
 
1149
        redOffset = float((secondColour.Red() * (100 - percent) / 100) - (firstColour.Red() * percent / 100))
 
1150
        greenOffset = float((secondColour.Green() * (100 - percent) / 100) - (firstColour.Green() * percent / 100))
 
1151
        blueOffset = float((secondColour.Blue() * (100 - percent) / 100) -  (firstColour.Blue() * percent / 100))
 
1152
 
 
1153
        return wx.Colour(firstColour.Red() + redOffset, firstColour.Green() + greenOffset,
 
1154
                        firstColour.Blue() + blueOffset)
 
1155
 
 
1156
 
 
1157
    def RandomColour(): 
 
1158
        """
 
1159
        Creates a random colour.
 
1160
 
 
1161
        :return: An instance of :class:`Colour`.
 
1162
        """
 
1163
        
 
1164
        r = random.randint(0, 255) # Random value betweem 0-255
 
1165
        g = random.randint(0, 255) # Random value betweem 0-255
 
1166
        b = random.randint(0, 255) # Random value betweem 0-255
 
1167
 
 
1168
        return wx.Colour(r, g, b)
 
1169
 
 
1170
 
 
1171
    def IsDark(self, colour):
 
1172
        """
 
1173
        Returns whether a colour is dark or light.
 
1174
 
 
1175
        :param `colour`: an instance of :class:`Colour`.
 
1176
 
 
1177
        :return: ``True`` if the average RGB values are dark, ``False`` otherwise.
 
1178
        """
 
1179
 
 
1180
        evg = (colour.Red() + colour.Green() + colour.Blue())/3
 
1181
        
 
1182
        if evg < 127:
 
1183
            return True
 
1184
 
 
1185
        return False
 
1186
 
 
1187
 
 
1188
    def TruncateText(self, dc, text, maxWidth):
 
1189
        """
 
1190
        Truncates a given string to fit given width size. if the text does not fit
 
1191
        into the given width it is truncated to fit. the format of the fixed text
 
1192
        is <truncate text ...>.
 
1193
 
 
1194
        :param `dc`: an instance of :class:`DC`;
 
1195
        :param string `text`: the text to be (eventually) truncated;
 
1196
        :param integer `maxWidth`: the maximum width allowed for the text.
 
1197
 
 
1198
        :return: A new string containining the (possibly) truncated text.
 
1199
        """
 
1200
 
 
1201
        textLen = len(text)
 
1202
        tempText = text
 
1203
        rectSize = maxWidth
 
1204
 
 
1205
        fixedText = ""
 
1206
        
 
1207
        textW, textH = dc.GetTextExtent(text)
 
1208
 
 
1209
        if rectSize >= textW:        
 
1210
            return text
 
1211
        
 
1212
        # The text does not fit in the designated area, 
 
1213
        # so we need to truncate it a bit
 
1214
        suffix = ".."
 
1215
        w, h = dc.GetTextExtent(suffix)
 
1216
        rectSize -= w
 
1217
 
 
1218
        for i in xrange(textLen, -1, -1):
 
1219
        
 
1220
            textW, textH = dc.GetTextExtent(tempText)
 
1221
            if rectSize >= textW:
 
1222
                fixedText = tempText
 
1223
                fixedText += ".."
 
1224
                return fixedText
 
1225
            
 
1226
            tempText = tempText[:-1]
 
1227
 
 
1228
 
 
1229
    def DrawButton(self, dc, rect, theme, state, input=None):
 
1230
        """
 
1231
        Colour rectangle according to the theme.
 
1232
 
 
1233
        :param `dc`: an instance of :class:`DC`;
 
1234
        :param Rect `rect`: the rectangle to be filled with gradient shading;
 
1235
        :param string `theme`: the theme to use to draw the button;
 
1236
        :param integer `state`: the button state;
 
1237
        :param `input`: a flag used to call the right method.
 
1238
        """
 
1239
 
 
1240
        if input is None or type(input) == type(False):
 
1241
            self.DrawButtonTheme(dc, rect, theme, state, input)
 
1242
        else:
 
1243
            self.DrawButtonColour(dc, rect, theme, state, input)
 
1244
            
 
1245
                           
 
1246
    def DrawButtonTheme(self, dc, rect, theme, state, useLightColours=True):
 
1247
        """
 
1248
        Draws a button using the appropriate theme.
 
1249
 
 
1250
        :param `dc`: an instance of :class:`DC`;
 
1251
        :param Rect `rect`: the button's client rectangle;
 
1252
        :param string `theme`: the theme to use to draw the button;
 
1253
        :param integer `state`: the button state;
 
1254
        :param bool  `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
 
1255
        """
 
1256
 
 
1257
        renderer = self._renderers[theme]
 
1258
        
 
1259
        # Set background colour if non given by caller
 
1260
        renderer.DrawButton(dc, rect, state, useLightColours)
 
1261
 
 
1262
 
 
1263
    def DrawButtonColour(self, dc, rect, theme, state, colour):
 
1264
        """
 
1265
        Draws a button using the appropriate theme.
 
1266
 
 
1267
        :param `dc`: an instance of :class:`DC`;
 
1268
        :param Rect `rect`: the button's client rectangle;
 
1269
        :param string `theme`: the theme to use to draw the button;
 
1270
        :param integer `state`: the button state;
 
1271
        :param `colour`: a valid :class:`Colour` instance.
 
1272
        """
 
1273
 
 
1274
        renderer = self._renderers[theme]
 
1275
        renderer.DrawButton(dc, rect, state, colour)
 
1276
 
 
1277
 
 
1278
    def CanMakeWindowsTransparent(self):
 
1279
        """
 
1280
        Used internally.
 
1281
        
 
1282
        :return: ``True`` if the system supports transparency of toplevel windows,
 
1283
         otherwise returns ``False``.
 
1284
        """
 
1285
 
 
1286
        if wx.Platform == "__WXMSW__":
 
1287
 
 
1288
            version = wx.GetOsDescription()
 
1289
            found = version.find("XP") >= 0 or version.find("2000") >= 0 or version.find("NT") >= 0
 
1290
            return found
 
1291
 
 
1292
        elif wx.Platform == "__WXMAC__":
 
1293
            return True
 
1294
        else:
 
1295
            return False
 
1296
        
 
1297
 
 
1298
    # on supported windows systems (Win2000 and greater), this function
 
1299
    # will make a frame window transparent by a certain amount
 
1300
    def MakeWindowTransparent(self, wnd, amount):
 
1301
        """
 
1302
        Used internally. Makes a toplevel window transparent if the system supports it.
 
1303
 
 
1304
        :param `wnd`: the toplevel window to make transparent, an instance of :class:`TopLevelWindow`;
 
1305
        :param integer `amount`: the window transparency to apply.
 
1306
        """
 
1307
 
 
1308
        if wnd.GetSize() == (0, 0):
 
1309
            return
 
1310
 
 
1311
        # this API call is not in all SDKs, only the newer ones, so
 
1312
        # we will runtime bind this
 
1313
        if wx.Platform == "__WXMSW__":
 
1314
            hwnd = wnd.GetHandle()
 
1315
    
 
1316
            if not hasattr(self, "_winlib"):
 
1317
                if _libimported == "MH":
 
1318
                    self._winlib = win32api.LoadLibrary("user32")
 
1319
                elif _libimported == "ctypes":
 
1320
                    self._winlib = ctypes.windll.user32
 
1321
                    
 
1322
            if _libimported == "MH":
 
1323
                pSetLayeredWindowAttributes = win32api.GetProcAddress(self._winlib,
 
1324
                                                                      "SetLayeredWindowAttributes")
 
1325
                
 
1326
                if pSetLayeredWindowAttributes == None:
 
1327
                    return
 
1328
                    
 
1329
                exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
 
1330
                if 0 == (exstyle & 0x80000):
 
1331
                    win32api.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, exstyle | 0x80000)  
 
1332
                         
 
1333
                winxpgui.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
 
1334
    
 
1335
            elif _libimported == "ctypes":
 
1336
                style = self._winlib.GetWindowLongA(hwnd, 0xffffffecL)
 
1337
                style |= 0x00080000
 
1338
                self._winlib.SetWindowLongA(hwnd, 0xffffffecL, style)
 
1339
                self._winlib.SetLayeredWindowAttributes(hwnd, 0, amount, 2)                
 
1340
        else:
 
1341
            if not wnd.CanSetTransparent():
 
1342
                return        
 
1343
            wnd.SetTransparent(amount)
 
1344
            return
 
1345
 
 
1346
 
 
1347
    # assumption: the background was already drawn on the dc
 
1348
    def DrawBitmapShadow(self, dc, rect, where=BottomShadow|RightShadow):
 
1349
        """
 
1350
        Draws a shadow using background bitmap.
 
1351
 
 
1352
        :param `dc`: an instance of :class:`DC`;
 
1353
        :param Rect `rect`: the bitmap's client rectangle;
 
1354
        :param integer `where`: where to draw the shadow. This can be any combination of the
 
1355
         following bits:
 
1356
 
 
1357
         ========================== ======= ================================
 
1358
         Shadow Settings             Value  Description
 
1359
         ========================== ======= ================================
 
1360
         ``RightShadow``                  1 Right side shadow
 
1361
         ``BottomShadow``                 2 Not full bottom shadow
 
1362
         ``BottomShadowFull``             4 Full bottom shadow
 
1363
         ========================== ======= ================================
 
1364
         
 
1365
        """
 
1366
    
 
1367
        shadowSize = 5
 
1368
 
 
1369
        # the rect must be at least 5x5 pixles
 
1370
        if rect.height < 2*shadowSize or rect.width < 2*shadowSize:
 
1371
            return
 
1372
 
 
1373
        # Start by drawing the right bottom corner
 
1374
        if where & BottomShadow or where & BottomShadowFull:
 
1375
            dc.DrawBitmap(self._rightBottomCorner, rect.x+rect.width, rect.y+rect.height, True)
 
1376
 
 
1377
        # Draw right side shadow
 
1378
        xx = rect.x + rect.width
 
1379
        yy = rect.y + rect.height - shadowSize
 
1380
 
 
1381
        if where & RightShadow:
 
1382
            while yy - rect.y > 2*shadowSize:
 
1383
                dc.DrawBitmap(self._right, xx, yy, True)
 
1384
                yy -= shadowSize
 
1385
            
 
1386
            dc.DrawBitmap(self._rightTop, xx, yy - shadowSize, True)
 
1387
 
 
1388
        if where & BottomShadow:
 
1389
            xx = rect.x + rect.width - shadowSize
 
1390
            yy = rect.height + rect.y
 
1391
            while xx - rect.x > 2*shadowSize:
 
1392
                dc.DrawBitmap(self._bottom, xx, yy, True)
 
1393
                xx -= shadowSize
 
1394
                
 
1395
            dc.DrawBitmap(self._bottomLeft, xx - shadowSize, yy, True)
 
1396
 
 
1397
        if where & BottomShadowFull:
 
1398
            xx = rect.x + rect.width - shadowSize
 
1399
            yy = rect.height + rect.y
 
1400
            while xx - rect.x >= 0:
 
1401
                dc.DrawBitmap(self._bottom, xx, yy, True)
 
1402
                xx -= shadowSize
 
1403
            
 
1404
            dc.DrawBitmap(self._bottom, xx, yy, True)
 
1405
 
 
1406
 
 
1407
    def DropShadow(self, wnd, drop=True):
 
1408
        """
 
1409
        Adds a shadow under the window (Windows only).
 
1410
 
 
1411
        :param `wnd`: the window for which we are dropping a shadow, an instance of :class:`TopLevelWindow`;
 
1412
        :param bool `drop`: ``True`` to drop a shadow, ``False`` to remove it.
 
1413
        """
 
1414
 
 
1415
        if not self.CanMakeWindowsTransparent() or not _libimported:
 
1416
            return
 
1417
        
 
1418
        if "__WXMSW__" in wx.Platform:
 
1419
 
 
1420
            hwnd = wnd.GetHandle()
 
1421
            
 
1422
            if not hasattr(self, "_winlib"):
 
1423
                if _libimported == "MH":
 
1424
                    self._winlib = win32api.LoadLibrary("user32")
 
1425
                elif _libimported == "ctypes":
 
1426
                    self._winlib = ctypes.windll.user32
 
1427
            
 
1428
            if _libimported == "MH":
 
1429
                csstyle = win32api.GetWindowLong(hwnd, win32con.GCL_STYLE)
 
1430
            else:
 
1431
                csstyle = self._winlib.GetWindowLongA(hwnd, win32con.GCL_STYLE)
 
1432
            
 
1433
            if drop:
 
1434
                if csstyle & CS_DROPSHADOW:
 
1435
                    return
 
1436
                else:
 
1437
                    csstyle |= CS_DROPSHADOW     #Nothing to be done
 
1438
                    
 
1439
            else:
 
1440
 
 
1441
                if csstyle & CS_DROPSHADOW:
 
1442
                    csstyle &= ~(CS_DROPSHADOW)
 
1443
                else:
 
1444
                    return  #Nothing to be done
 
1445
 
 
1446
            win32api.SetWindowLong(hwnd, win32con.GCL_STYLE, csstyle)
 
1447
            
 
1448
 
 
1449
    def GetBitmapStartLocation(self, dc, rect, bitmap, text="", style=0):
 
1450
        """
 
1451
        Returns the top left `x` and `y` cordinates of the bitmap drawing.
 
1452
 
 
1453
        :param `dc`: an instance of :class:`DC`;
 
1454
        :param Rect `rect`: the bitmap's client rectangle;
 
1455
        :param Bitmap `bitmap`: the bitmap associated with the button;
 
1456
        :param string `text`: the button label;
 
1457
        :param integer `style`: the button style. This can be one of the following bits:
 
1458
 
 
1459
         ============================== ======= ================================
 
1460
         Button style                    Value  Description
 
1461
         ============================== ======= ================================
 
1462
         ``BU_EXT_XP_STYLE``               1    A button with a XP style
 
1463
         ``BU_EXT_2007_STYLE``             2    A button with a MS Office 2007 style
 
1464
         ``BU_EXT_LEFT_ALIGN_STYLE``       4    A left-aligned button
 
1465
         ``BU_EXT_CENTER_ALIGN_STYLE``     8    A center-aligned button
 
1466
         ``BU_EXT_RIGHT_ALIGN_STYLE``      16   A right-aligned button
 
1467
         ``BU_EXT_RIGHT_TO_LEFT_STYLE``    32   A button suitable for right-to-left languages
 
1468
         ============================== ======= ================================
 
1469
 
 
1470
        :return: A tuple containining the top left `x` and `y` cordinates of the bitmap drawing.
 
1471
        """
 
1472
 
 
1473
        alignmentBuffer = self.GetAlignBuffer()
 
1474
 
 
1475
        # get the startLocationY
 
1476
        fixedTextWidth = fixedTextHeight = 0
 
1477
 
 
1478
        if not text:
 
1479
            fixedTextHeight = bitmap.GetHeight()
 
1480
        else:
 
1481
            fixedTextWidth, fixedTextHeight = dc.GetTextExtent(text)
 
1482
            
 
1483
        startLocationY = rect.y + (rect.height - fixedTextHeight)/2
 
1484
 
 
1485
        # get the startLocationX
 
1486
        if style & BU_EXT_RIGHT_TO_LEFT_STYLE:
 
1487
        
 
1488
            startLocationX = rect.x + rect.width - alignmentBuffer - bitmap.GetWidth()
 
1489
        
 
1490
        else:
 
1491
        
 
1492
            if style & BU_EXT_RIGHT_ALIGN_STYLE:
 
1493
            
 
1494
                maxWidth = rect.x + rect.width - (2 * alignmentBuffer) - bitmap.GetWidth() # the alignment is for both sides
 
1495
                
 
1496
                # get the truncated text. The text may stay as is, it is not a must that is will be trancated
 
1497
                fixedText = self.TruncateText(dc, text, maxWidth)
 
1498
 
 
1499
                # get the fixed text dimentions
 
1500
                fixedTextWidth, fixedTextHeight = dc.GetTextExtent(fixedText)
 
1501
 
 
1502
                # calculate the start location
 
1503
                startLocationX = maxWidth - fixedTextWidth
 
1504
            
 
1505
            elif style & BU_EXT_LEFT_ALIGN_STYLE:
 
1506
            
 
1507
                # calculate the start location
 
1508
                startLocationX = alignmentBuffer
 
1509
            
 
1510
            else: # meaning BU_EXT_CENTER_ALIGN_STYLE
 
1511
            
 
1512
                maxWidth = rect.x + rect.width - (2 * alignmentBuffer) - bitmap.GetWidth() # the alignment is for both sides
 
1513
 
 
1514
                # get the truncated text. The text may stay as is, it is not a must that is will be trancated
 
1515
                fixedText = self.TruncateText(dc, text, maxWidth)
 
1516
 
 
1517
                # get the fixed text dimentions
 
1518
                fixedTextWidth, fixedTextHeight = dc.GetTextExtent(fixedText)
 
1519
 
 
1520
                if maxWidth > fixedTextWidth:
 
1521
                
 
1522
                    # calculate the start location
 
1523
                    startLocationX = (maxWidth - fixedTextWidth) / 2
 
1524
                
 
1525
                else:
 
1526
                
 
1527
                    # calculate the start location
 
1528
                    startLocationX = maxWidth - fixedTextWidth                    
 
1529
        
 
1530
        # it is very important to validate that the start location is not less than the alignment buffer
 
1531
        if startLocationX < alignmentBuffer:
 
1532
            startLocationX = alignmentBuffer
 
1533
 
 
1534
        return startLocationX, startLocationY            
 
1535
 
 
1536
 
 
1537
    def GetTextStartLocation(self, dc, rect, bitmap, text, style=0):
 
1538
        """
 
1539
        Returns the top left `x` and `y` cordinates of the text drawing.
 
1540
        In case the text is too long, the text is being fixed (the text is cut and
 
1541
        a '...' mark is added in the end).
 
1542
 
 
1543
        :param `dc`: an instance of :class:`DC`;
 
1544
        :param Rect `rect`: the text's client rectangle;
 
1545
        :param Bitmap `bitmap`: the bitmap associated with the button;
 
1546
        :param string `text`: the button label;
 
1547
        :param integer `style`: the button style. 
 
1548
 
 
1549
        :return: A tuple containining the top left `x` and `y` cordinates of the text drawing, plus
 
1550
         the truncated version of the input `text`.
 
1551
 
 
1552
        :see: :meth:`~ArtManager.GetBitmapStartLocation` for a list of valid button styles.
 
1553
        """
 
1554
 
 
1555
        alignmentBuffer = self.GetAlignBuffer()
 
1556
 
 
1557
        # get the bitmap offset
 
1558
        bitmapOffset = 0
 
1559
        if bitmap != wx.NullBitmap:
 
1560
            bitmapOffset = bitmap.GetWidth()
 
1561
 
 
1562
        # get the truncated text. The text may stay as is, it is not a must that is will be trancated
 
1563
        maxWidth = rect.x + rect.width - (2 * alignmentBuffer) - bitmapOffset # the alignment is for both sides
 
1564
 
 
1565
        fixedText = self.TruncateText(dc, text, maxWidth)
 
1566
 
 
1567
        # get the fixed text dimentions
 
1568
        fixedTextWidth, fixedTextHeight = dc.GetTextExtent(fixedText)
 
1569
        startLocationY = (rect.height - fixedTextHeight) / 2 + rect.y
 
1570
 
 
1571
        # get the startLocationX
 
1572
        if style & BU_EXT_RIGHT_TO_LEFT_STYLE:
 
1573
        
 
1574
            startLocationX = maxWidth - fixedTextWidth + alignmentBuffer
 
1575
        
 
1576
        else:
 
1577
        
 
1578
            if style & BU_EXT_LEFT_ALIGN_STYLE:
 
1579
            
 
1580
                # calculate the start location
 
1581
                startLocationX = bitmapOffset + alignmentBuffer
 
1582
            
 
1583
            elif style & BU_EXT_RIGHT_ALIGN_STYLE:
 
1584
            
 
1585
                # calculate the start location
 
1586
                startLocationX = maxWidth - fixedTextWidth + bitmapOffset + alignmentBuffer
 
1587
            
 
1588
            else: # meaning wxBU_EXT_CENTER_ALIGN_STYLE
 
1589
            
 
1590
                # calculate the start location
 
1591
                startLocationX = (maxWidth - fixedTextWidth) / 2 + bitmapOffset + alignmentBuffer
 
1592
            
 
1593
        
 
1594
        # it is very important to validate that the start location is not less than the alignment buffer
 
1595
        if startLocationX < alignmentBuffer:
 
1596
            startLocationX = alignmentBuffer
 
1597
 
 
1598
        return startLocationX, startLocationY, fixedText
 
1599
    
 
1600
 
 
1601
    def DrawTextAndBitmap(self, dc, rect, text, enable=True, font=wx.NullFont,
 
1602
                          fontColour=wx.BLACK, bitmap=wx.NullBitmap,
 
1603
                          grayBitmap=wx.NullBitmap, style=0):
 
1604
        """
 
1605
        Draws the text & bitmap on the input dc.
 
1606
 
 
1607
        :param `dc`: an instance of :class:`DC`;
 
1608
        :param Rect `rect`: the text and bitmap client rectangle;
 
1609
        :param string `text`: the button label;
 
1610
        :param bool `enable`: ``True`` if the button is enabled, ``False`` otherwise;
 
1611
        :param `font`: the font to use to draw the text, an instance of :class:`Font`;
 
1612
        :param `fontColour`: the colour to use to draw the text, an instance of
 
1613
         :class:`Colour`;
 
1614
        :param `bitmap`: the bitmap associated with the button, an instance of :class:`Bitmap`;
 
1615
        :param `grayBitmap`: a greyed-out version of the input `bitmap` representing
 
1616
         a disabled bitmap, an instance of :class:`Bitmap`;
 
1617
        :param integer `style`: the button style. 
 
1618
 
 
1619
        :see: :meth:`~ArtManager.GetBitmapStartLocation` for a list of valid button styles.
 
1620
        """
 
1621
 
 
1622
        # enable colours
 
1623
        if enable:
 
1624
            dc.SetTextForeground(fontColour)
 
1625
        else:
 
1626
            dc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT))
 
1627
        
 
1628
        # set the font
 
1629
        
 
1630
        if font == wx.NullFont:
 
1631
            font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
 
1632
            
 
1633
        dc.SetFont(font)
 
1634
        
 
1635
        startLocationX = startLocationY = 0
 
1636
        
 
1637
        if bitmap != wx.NullBitmap:
 
1638
        
 
1639
            # calculate the bitmap start location
 
1640
            startLocationX, startLocationY = self.GetBitmapStartLocation(dc, rect, bitmap, text, style)
 
1641
 
 
1642
            # draw the bitmap
 
1643
            if enable:
 
1644
                dc.DrawBitmap(bitmap, startLocationX, startLocationY, True)
 
1645
            else:
 
1646
                dc.DrawBitmap(grayBitmap, startLocationX, startLocationY, True)
 
1647
   
 
1648
        # calculate the text start location
 
1649
        location, labelOnly = self.GetAccelIndex(text)
 
1650
        startLocationX, startLocationY, fixedText = self.GetTextStartLocation(dc, rect, bitmap, labelOnly, style)
 
1651
 
 
1652
        # after all the caculations are finished, it is time to draw the text
 
1653
        # underline the first letter that is marked with a '&'
 
1654
        if location == -1 or font.GetUnderlined() or location >= len(fixedText):
 
1655
            # draw the text
 
1656
            dc.DrawText(fixedText, startLocationX, startLocationY)
 
1657
        
 
1658
        else:
 
1659
            
 
1660
            # underline the first '&'
 
1661
            before = fixedText[0:location]
 
1662
            underlineLetter = fixedText[location] 
 
1663
            after = fixedText[location+1:]
 
1664
 
 
1665
            # before
 
1666
            dc.DrawText(before, startLocationX, startLocationY)
 
1667
 
 
1668
            # underlineLetter
 
1669
            if "__WXGTK__" not in wx.Platform:
 
1670
                w1, h = dc.GetTextExtent(before)
 
1671
                font.SetUnderlined(True)
 
1672
                dc.SetFont(font)
 
1673
                dc.DrawText(underlineLetter, startLocationX + w1, startLocationY)
 
1674
            else:
 
1675
                w1, h = dc.GetTextExtent(before)
 
1676
                dc.DrawText(underlineLetter, startLocationX + w1, startLocationY)
 
1677
 
 
1678
                # Draw the underline ourselves since using the Underline in GTK, 
 
1679
                # causes the line to be too close to the letter
 
1680
                uderlineLetterW, uderlineLetterH = dc.GetTextExtent(underlineLetter)
 
1681
 
 
1682
                curPen = dc.GetPen()
 
1683
                dc.SetPen(wx.BLACK_PEN)
 
1684
 
 
1685
                dc.DrawLine(startLocationX + w1, startLocationY + uderlineLetterH - 2,
 
1686
                            startLocationX + w1 + uderlineLetterW, startLocationY + uderlineLetterH - 2)
 
1687
                dc.SetPen(curPen)
 
1688
 
 
1689
            # after
 
1690
            w2, h = dc.GetTextExtent(underlineLetter)
 
1691
            font.SetUnderlined(False)
 
1692
            dc.SetFont(font)
 
1693
            dc.DrawText(after, startLocationX + w1 + w2, startLocationY)
 
1694
 
 
1695
 
 
1696
    def CalcButtonBestSize(self, label, bmp):
 
1697
        """
 
1698
        Returns the best fit size for the supplied label & bitmap.
 
1699
 
 
1700
        :param string `label`: the button label;
 
1701
        :param `bmp`: the bitmap associated with the button, an instance of :class:`Bitmap`.
 
1702
 
 
1703
        :return: An instance of :class:`Size`, representing the best fit size for the supplied label & bitmap.        
 
1704
        """
 
1705
 
 
1706
        if "__WXMSW__" in wx.Platform:
 
1707
            HEIGHT = 22
 
1708
        else:
 
1709
            HEIGHT = 26
 
1710
 
 
1711
        dc = wx.MemoryDC()
 
1712
        dc.SelectBitmap(wx.EmptyBitmap(1, 1))
 
1713
 
 
1714
        dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
 
1715
        width, height, dummy = dc.GetMultiLineTextExtent(label)
 
1716
 
 
1717
        width += 2*self.GetAlignBuffer() 
 
1718
 
 
1719
        if bmp.Ok():
 
1720
        
 
1721
            # allocate extra space for the bitmap
 
1722
            heightBmp = bmp.GetHeight() + 2
 
1723
            if height < heightBmp:
 
1724
                height = heightBmp
 
1725
 
 
1726
            width += bmp.GetWidth() + 2
 
1727
        
 
1728
        if height < HEIGHT:
 
1729
            height = HEIGHT
 
1730
 
 
1731
        dc.SelectBitmap(wx.NullBitmap)
 
1732
        
 
1733
        return wx.Size(width, height)
 
1734
 
 
1735
 
 
1736
    def GetMenuFaceColour(self):
 
1737
        """
 
1738
        Returns the colour used for the menu foreground.
 
1739
 
 
1740
        :return: An instance of :class:`Colour`.
 
1741
        """
 
1742
 
 
1743
        renderer = self._renderers[self.GetMenuTheme()]
 
1744
        return renderer.GetMenuFaceColour()
 
1745
 
 
1746
 
 
1747
    def GetTextColourEnable(self):
 
1748
        """
 
1749
        Returns the colour used for enabled menu items.
 
1750
 
 
1751
        :return: An instance of :class:`Colour`.
 
1752
        """
 
1753
 
 
1754
        renderer = self._renderers[self.GetMenuTheme()]
 
1755
        return renderer.GetTextColourEnable()
 
1756
 
 
1757
 
 
1758
    def GetTextColourDisable(self):
 
1759
        """
 
1760
        Returns the colour used for disabled menu items.
 
1761
 
 
1762
        :return: An instance of :class:`Colour`.
 
1763
        """
 
1764
 
 
1765
        renderer = self._renderers[self.GetMenuTheme()]
 
1766
        return renderer.GetTextColourDisable()
 
1767
 
 
1768
 
 
1769
    def GetFont(self):
 
1770
        """
 
1771
        Returns the font used by this theme.
 
1772
 
 
1773
        :return: An instance of :class:`Font`.
 
1774
        """
 
1775
 
 
1776
        renderer = self._renderers[self.GetMenuTheme()]
 
1777
        return renderer.GetFont()
 
1778
 
 
1779
 
 
1780
    def GetAccelIndex(self, label):
 
1781
        """
 
1782
        Returns the mnemonic index of the label and the label stripped of the ampersand mnemonic
 
1783
        (e.g. 'lab&el' ==> will result in 3 and labelOnly = label).
 
1784
 
 
1785
        :param string `label`: a string containining an ampersand.
 
1786
 
 
1787
        :return: A tuple containining the mnemonic index of the label and the label
 
1788
         stripped of the ampersand mnemonic.
 
1789
        """
 
1790
 
 
1791
        indexAccel = 0
 
1792
        while True:
 
1793
            indexAccel = label.find("&", indexAccel)
 
1794
            if indexAccel == -1:
 
1795
                return indexAccel, label
 
1796
            if label[indexAccel:indexAccel+2] == "&&":
 
1797
                label = label[0:indexAccel] + label[indexAccel+1:]
 
1798
                indexAccel += 1
 
1799
            else:
 
1800
                break
 
1801
 
 
1802
        labelOnly = label[0:indexAccel] + label[indexAccel+1:]
 
1803
 
 
1804
        return indexAccel, labelOnly
 
1805
        
 
1806
 
 
1807
    def GetThemeBaseColour(self, useLightColours=True):
 
1808
        """
 
1809
        Returns the theme (Blue, Silver, Green etc.) base colour, if no theme is active
 
1810
        it return the active caption colour, lighter in 30%.
 
1811
 
 
1812
        :param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
 
1813
 
 
1814
        :return: An instance of :class:`Colour`.        
 
1815
        """
 
1816
 
 
1817
        if not useLightColours and not self.IsDark(self.FrameColour()):
 
1818
            return wx.NamedColour("GOLD")
 
1819
        else:
 
1820
            return self.LightColour(self.FrameColour(), 30)
 
1821
 
 
1822
 
 
1823
    def GetAlignBuffer(self):
 
1824
        """
 
1825
        Return the padding buffer for a text or bitmap.
 
1826
 
 
1827
        :return: An integer representing the padding buffer.
 
1828
        """
 
1829
 
 
1830
        return self._alignmentBuffer
 
1831
 
 
1832
 
 
1833
    def SetMenuTheme(self, theme):
 
1834
        """
 
1835
        Set the menu theme, possible values (Style2007, StyleXP, StyleVista).
 
1836
 
 
1837
        :param string `theme`: a rendering theme class, either `StyleXP`, `Style2007` or `StyleVista`.
 
1838
        """
 
1839
        
 
1840
        self._menuTheme = theme
 
1841
 
 
1842
 
 
1843
    def GetMenuTheme(self):
 
1844
        """
 
1845
        Returns the currently used menu theme.
 
1846
 
 
1847
        :return: A string containining the currently used theme for the menu.
 
1848
        """
 
1849
 
 
1850
        return self._menuTheme
 
1851
 
 
1852
 
 
1853
    def AddMenuTheme(self, render):
 
1854
        """
 
1855
        Adds a new theme to the stock.
 
1856
 
 
1857
        :param `render`: a rendering theme class, which must be derived from
 
1858
         :class:`RendererBase`.
 
1859
 
 
1860
        :return: An integer representing the size of the renderers dictionary.
 
1861
        """
 
1862
 
 
1863
        # Add new theme
 
1864
        lastRenderer = len(self._renderers)
 
1865
        self._renderers[lastRenderer] = render
 
1866
        
 
1867
        return lastRenderer
 
1868
    
 
1869
 
 
1870
    def SetMS2007ButtonSunken(self, sunken):
 
1871
        """
 
1872
        Sets MS 2007 button style sunken or not.
 
1873
 
 
1874
        :param bool `sunken`: ``True`` to have a sunken border effect, ``False`` otherwise.
 
1875
        """
 
1876
        
 
1877
        self._ms2007sunken = sunken
 
1878
 
 
1879
 
 
1880
    def GetMS2007ButtonSunken(self):
 
1881
        """
 
1882
        Returns the sunken flag for MS 2007 buttons.
 
1883
 
 
1884
        :return: ``True`` if the MS 2007 buttons are sunken, ``False`` otherwise.
 
1885
        """
 
1886
 
 
1887
        return self._ms2007sunken
 
1888
 
 
1889
 
 
1890
    def GetMBVerticalGradient(self):
 
1891
        """ Returns ``True`` if the menu bar should be painted with vertical gradient. """
 
1892
 
 
1893
        return self._verticalGradient
 
1894
 
 
1895
 
 
1896
    def SetMBVerticalGradient(self, v):
 
1897
        """
 
1898
        Sets the menu bar gradient style.
 
1899
 
 
1900
        :param bool `v`: ``True`` for a vertical shaded gradient, ``False`` otherwise.
 
1901
        """
 
1902
 
 
1903
        self._verticalGradient = v
 
1904
 
 
1905
 
 
1906
    def DrawMenuBarBorder(self, border):
 
1907
        """
 
1908
        Enables menu border drawing (XP style only).
 
1909
 
 
1910
        :param bool `border`: ``True`` to draw the menubar border, ``False`` otherwise.
 
1911
        """
 
1912
 
 
1913
        self._drowMBBorder = border
 
1914
        
 
1915
 
 
1916
    def GetMenuBarBorder(self):
 
1917
        """
 
1918
        Returns menu bar border drawing flag.
 
1919
 
 
1920
        :return: ``True`` if the menu bar border is to be drawn, ``False`` otherwise.
 
1921
        """
 
1922
 
 
1923
        return self._drowMBBorder
 
1924
 
 
1925
 
 
1926
    def GetMenuBgFactor(self):
 
1927
        """
 
1928
        Gets the visibility depth of the menu in Metallic style.
 
1929
        The higher the value, the menu bar will look more raised
 
1930
        """
 
1931
 
 
1932
        return self._menuBgFactor
 
1933
 
 
1934
 
 
1935
    def DrawDragSash(self, rect):
 
1936
        """
 
1937
        Draws resize sash.
 
1938
 
 
1939
        :param Rect `rect`: the sash client rectangle.
 
1940
        """
 
1941
  
 
1942
        dc = wx.ScreenDC()
 
1943
        mem_dc = wx.MemoryDC()
 
1944
        
 
1945
        bmp = wx.EmptyBitmap(rect.width, rect.height)
 
1946
        mem_dc.SelectObject(bmp)
 
1947
        mem_dc.SetBrush(wx.WHITE_BRUSH)
 
1948
        mem_dc.SetPen(wx.Pen(wx.WHITE, 1))
 
1949
        mem_dc.DrawRectangle(0, 0, rect.width, rect.height)
 
1950
 
 
1951
        dc.Blit(rect.x, rect.y, rect.width, rect.height, mem_dc, 0, 0, wx.XOR)
 
1952
 
 
1953
 
 
1954
    def TakeScreenShot(self, rect, bmp):
 
1955
        """
 
1956
        Takes a screenshot of the screen at given position & size (rect).
 
1957
 
 
1958
        :param Rect `rect`: the screen rectangle we wish to capture;
 
1959
        :param Bitmap `bmp`: currently unused.
 
1960
        """
 
1961
 
 
1962
        # Create a DC for the whole screen area
 
1963
        dcScreen = wx.ScreenDC()
 
1964
 
 
1965
        # Create a Bitmap that will later on hold the screenshot image
 
1966
        # Note that the Bitmap must have a size big enough to hold the screenshot
 
1967
        # -1 means using the current default colour depth
 
1968
        bmp = wx.EmptyBitmap(rect.width, rect.height)
 
1969
 
 
1970
        # Create a memory DC that will be used for actually taking the screenshot
 
1971
        memDC = wx.MemoryDC()
 
1972
 
 
1973
        # Tell the memory DC to use our Bitmap
 
1974
        # all drawing action on the memory DC will go to the Bitmap now
 
1975
        memDC.SelectObject(bmp)
 
1976
 
 
1977
        # Blit (in this case copy) the actual screen on the memory DC
 
1978
        # and thus the Bitmap
 
1979
        memDC.Blit( 0,   # Copy to this X coordinate
 
1980
            0,           # Copy to this Y coordinate
 
1981
            rect.width,  # Copy this width
 
1982
            rect.height, # Copy this height
 
1983
            dcScreen,    # From where do we copy?
 
1984
            rect.x,      # What's the X offset in the original DC?
 
1985
            rect.y       # What's the Y offset in the original DC?
 
1986
            )
 
1987
 
 
1988
        # Select the Bitmap out of the memory DC by selecting a new
 
1989
        # uninitialized Bitmap
 
1990
        memDC.SelectObject(wx.NullBitmap)
 
1991
 
 
1992
 
 
1993
    def DrawToolBarBg(self, dc, rect):
 
1994
        """
 
1995
        Draws the toolbar background according to the active theme.
 
1996
 
 
1997
        :param `dc`: an instance of :class:`DC`;
 
1998
        :param Rect `rect`: the toolbar's client rectangle.
 
1999
        """
 
2000
 
 
2001
        renderer = self._renderers[self.GetMenuTheme()]
 
2002
        
 
2003
        # Set background colour if non given by caller
 
2004
        renderer.DrawToolBarBg(dc, rect)
 
2005
 
 
2006
 
 
2007
    def DrawMenuBarBg(self, dc, rect):
 
2008
        """
 
2009
        Draws the menu bar background according to the active theme.
 
2010
 
 
2011
        :param `dc`: an instance of :class:`DC`;
 
2012
        :param Rect `rect`: the menubar's client rectangle.
 
2013
        """
 
2014
 
 
2015
        renderer = self._renderers[self.GetMenuTheme()]
 
2016
        # Set background colour if non given by caller
 
2017
        renderer.DrawMenuBarBg(dc, rect)
 
2018
 
 
2019
 
 
2020
    def SetMenuBarColour(self, scheme):
 
2021
        """
 
2022
        Sets the menu bar colour scheme to use.
 
2023
 
 
2024
        :param string `scheme`: a string representing a colour scheme (i.e., 'Default',
 
2025
         'Dark', 'Dark Olive Green', 'Generic').
 
2026
        """
 
2027
 
 
2028
        self._menuBarColourScheme = scheme
 
2029
        # set default colour
 
2030
        if scheme in self._colourSchemeMap.keys():
 
2031
            self._menuBarBgColour = self._colourSchemeMap[scheme]
 
2032
 
 
2033
 
 
2034
    def GetMenuBarColourScheme(self):
 
2035
        """
 
2036
        Returns the current colour scheme.
 
2037
 
 
2038
        :return: A string representing the current colour scheme.
 
2039
        """
 
2040
 
 
2041
        return self._menuBarColourScheme
 
2042
 
 
2043
 
 
2044
    def GetMenuBarFaceColour(self):
 
2045
        """
 
2046
        Returns the menu bar face colour.
 
2047
 
 
2048
        :return: An instance of :class:`Colour`.
 
2049
        """
 
2050
 
 
2051
        return self._menuBarBgColour
 
2052
 
 
2053
 
 
2054
    def GetMenuBarSelectionColour(self):
 
2055
        """
 
2056
        Returns the menu bar selection colour.
 
2057
 
 
2058
        :return: An instance of :class:`Colour`.
 
2059
        """
 
2060
 
 
2061
        return self._menuBarSelColour
 
2062
 
 
2063
 
 
2064
    def InitColours(self):
 
2065
        """ Initialise the colour map. """
 
2066
 
 
2067
        self._colourSchemeMap = {_("Default"): wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE),
 
2068
                                _("Dark"): wx.BLACK,
 
2069
                                _("Dark Olive Green"): wx.NamedColour("DARK OLIVE GREEN"),
 
2070
                                _("Generic"): wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)}
 
2071
 
 
2072
 
 
2073
    def GetColourSchemes(self):
 
2074
        """
 
2075
        Returns the available colour schemes.
 
2076
 
 
2077
        :return: A list of strings representing the available colour schemes.
 
2078
        """
 
2079
 
 
2080
        return self._colourSchemeMap.keys()     
 
2081
                
 
2082
 
 
2083
    def CreateGreyBitmap(self, bmp):
 
2084
        """
 
2085
        Creates a grey bitmap image from the input bitmap.
 
2086
 
 
2087
        :param `bmp`: a valid :class:`Bitmap` object to be greyed out.
 
2088
 
 
2089
        :return: A greyed-out representation of the input bitmap, an instance of :class:`Bitmap`.
 
2090
        """
 
2091
 
 
2092
        img = bmp.ConvertToImage()
 
2093
        return wx.BitmapFromImage(img.ConvertToGreyscale())
 
2094
 
 
2095
 
 
2096
    def GetRaiseToolbar(self):
 
2097
        """ Returns ``True`` if we are dropping a shadow under a toolbar. """
 
2098
 
 
2099
        return self._raiseTB
 
2100
 
 
2101
 
 
2102
    def SetRaiseToolbar(self, rais):
 
2103
        """
 
2104
        Enables/disables toobar shadow drop.
 
2105
 
 
2106
        :param bool `rais`: ``True`` to drop a shadow below a toolbar, ``False`` otherwise.
 
2107
        """
 
2108
 
 
2109
        self._raiseTB = rais
 
2110
 
 
2111