X-Git-Url: http://iramuteq.org/git?p=iramuteq;a=blobdiff_plain;f=aui%2Fdockart.py;fp=aui%2Fdockart.py;h=17da477ef57bd39c633e3df51929693d887eaf30;hp=0000000000000000000000000000000000000000;hb=7fb5b2b86f6c9a0617208ee85211177c23d12f47;hpb=22f93a602f3584ddc6ba68114556212c90307a50 diff --git a/aui/dockart.py b/aui/dockart.py new file mode 100644 index 0000000..17da477 --- /dev/null +++ b/aui/dockart.py @@ -0,0 +1,1162 @@ +""" +Dock art provider code - a dock provider provides all drawing functionality to +the AUI dock manager. This allows the dock manager to have a plugable look-and-feel. + +By default, a L{AuiManager} uses an instance of this class called L{AuiDefaultDockArt} +which provides bitmap art and a colour scheme that is adapted to the major platforms' +look. You can either derive from that class to alter its behaviour or write a +completely new dock art class. Call L{AuiManager.SetArtProvider} to make use this +new dock art. +""" + +__author__ = "Andrea Gavana " +__date__ = "31 March 2009" + + +import wx +import types + +from aui_utilities import BitmapFromBits, StepColour, ChopText, GetBaseColour +from aui_utilities import DrawGradientRectangle, DrawMACCloseButton +from aui_utilities import DarkenBitmap, LightContrastColour +from aui_constants import * + +optionActive = 2**14 + +_ctypes = False + +# Try to import winxptheme for ModernDockArt +if wx.Platform == "__WXMSW__": + try: + import ctypes + import winxptheme + _ctypes = True + except ImportError: + pass + +# -- AuiDefaultDockArt class implementation -- + +class AuiDefaultDockArt(object): + """ + Dock art provider code - a dock provider provides all drawing functionality + to the AUI dock manager. This allows the dock manager to have a plugable + look-and-feel. + + By default, a L{AuiManager} uses an instance of this class called L{AuiDefaultDockArt} + which provides bitmap art and a colour scheme that is adapted to the major + platforms' look. You can either derive from that class to alter its behaviour or + write a completely new dock art class. + + Call L{AuiManager.SetArtProvider} to make use this new dock art. + + + **Metric Ordinals** + + These are the possible pane dock art settings for L{AuiManager}: + + ================================================ ====================================== + Metric Ordinal Constant Description + ================================================ ====================================== + ``AUI_DOCKART_SASH_SIZE`` Customizes the sash size + ``AUI_DOCKART_CAPTION_SIZE`` Customizes the caption size + ``AUI_DOCKART_GRIPPER_SIZE`` Customizes the gripper size + ``AUI_DOCKART_PANE_BORDER_SIZE`` Customizes the pane border size + ``AUI_DOCKART_PANE_BUTTON_SIZE`` Customizes the pane button size + ``AUI_DOCKART_BACKGROUND_COLOUR`` Customizes the background colour + ``AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR`` Customizes the background gradient colour + ``AUI_DOCKART_SASH_COLOUR`` Customizes the sash colour + ``AUI_DOCKART_ACTIVE_CAPTION_COLOUR`` Customizes the active caption colour + ``AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR`` Customizes the active caption gradient colour + ``AUI_DOCKART_INACTIVE_CAPTION_COLOUR`` Customizes the inactive caption colour + ``AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR`` Customizes the inactive gradient caption colour + ``AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR`` Customizes the active caption text colour + ``AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR`` Customizes the inactive caption text colour + ``AUI_DOCKART_BORDER_COLOUR`` Customizes the border colour + ``AUI_DOCKART_GRIPPER_COLOUR`` Customizes the gripper colour + ``AUI_DOCKART_CAPTION_FONT`` Customizes the caption font + ``AUI_DOCKART_GRADIENT_TYPE`` Customizes the gradient type (no gradient, vertical or horizontal) + ``AUI_DOCKART_DRAW_SASH_GRIP`` Draw a sash grip on the sash + ================================================ ====================================== + + + **Gradient Types** + + These are the possible gradient dock art settings for L{AuiManager}: + + ============================================ ====================================== + Gradient Constant Description + ============================================ ====================================== + ``AUI_GRADIENT_NONE`` No gradient on the captions + ``AUI_GRADIENT_VERTICAL`` Vertical gradient on the captions + ``AUI_GRADIENT_HORIZONTAL`` Horizontal gradient on the captions + ============================================ ====================================== + + + **Button States** + + These are the possible pane button / L{AuiNotebook} button / L{AuiToolBar} button states: + + ============================================ ====================================== + Button State Constant Description + ============================================ ====================================== + ``AUI_BUTTON_STATE_NORMAL`` Normal button state + ``AUI_BUTTON_STATE_HOVER`` Hovered button state + ``AUI_BUTTON_STATE_PRESSED`` Pressed button state + ``AUI_BUTTON_STATE_DISABLED`` Disabled button state + ``AUI_BUTTON_STATE_HIDDEN`` Hidden button state + ``AUI_BUTTON_STATE_CHECKED`` Checked button state + ============================================ ====================================== + + + **Button Identifiers** + + These are the possible pane button / L{AuiNotebook} button / L{AuiToolBar} button identifiers: + + ============================================ ====================================== + Button Identifier Description + ============================================ ====================================== + ``AUI_BUTTON_CLOSE`` Shows a close button on the pane + ``AUI_BUTTON_MAXIMIZE_RESTORE`` Shows a maximize/restore button on the pane + ``AUI_BUTTON_MINIMIZE`` Shows a minimize button on the pane + ``AUI_BUTTON_PIN`` Shows a pin button on the pane + ``AUI_BUTTON_OPTIONS`` Shows an option button on the pane (not implemented) + ``AUI_BUTTON_WINDOWLIST`` Shows a window list button on the pane (for L{AuiNotebook}) + ``AUI_BUTTON_LEFT`` Shows a left button on the pane (for L{AuiNotebook}) + ``AUI_BUTTON_RIGHT`` Shows a right button on the pane (for L{AuiNotebook}) + ``AUI_BUTTON_UP`` Shows an up button on the pane (not implemented) + ``AUI_BUTTON_DOWN`` Shows a down button on the pane (not implemented) + ``AUI_BUTTON_CUSTOM1`` Shows a custom button on the pane (not implemented) + ``AUI_BUTTON_CUSTOM2`` Shows a custom button on the pane (not implemented) + ``AUI_BUTTON_CUSTOM3`` Shows a custom button on the pane (not implemented) + ============================================ ====================================== + + """ + + def __init__(self): + """ Default class constructor. """ + + self.Init() + + isMac = wx.Platform == "__WXMAC__" + + if isMac: + self._caption_font = wx.SMALL_FONT + else: + self._caption_font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False) + + self.SetDefaultPaneBitmaps(isMac) + self._restore_bitmap = wx.BitmapFromXPMData(restore_xpm) + + # default metric values + self._sash_size = 4 + + if isMac: + # This really should be implemented in wx.SystemSettings + # There is no way to do this that I am aware outside of using + # the cocoa python bindings. 8 pixels looks correct on my system + # so hard coding it for now. + + # How do I translate this?!? Not sure of the below implementation... + # SInt32 height; + # GetThemeMetric( kThemeMetricSmallPaneSplitterHeight , &height ); + # self._sash_size = height; + + self._sash_size = 8 # Carbon.Appearance.kThemeMetricPaneSplitterHeight + + elif wx.Platform == "__WXGTK__": + self._sash_size = wx.RendererNative.Get().GetSplitterParams(wx.GetTopLevelWindows()[0]).widthSash + + else: + self._sash_size = 4 + + self._caption_size = 19 + self._border_size = 1 + self._button_size = 14 + self._gripper_size = 9 + self._gradient_type = AUI_GRADIENT_VERTICAL + self._draw_sash = False + + + def Init(self): + """ Initializes the dock art. """ + + base_colour = GetBaseColour() + darker1_colour = StepColour(base_colour, 85) + darker2_colour = StepColour(base_colour, 75) + darker3_colour = StepColour(base_colour, 60) + darker4_colour = StepColour(base_colour, 40) + + self._background_colour = base_colour + self._background_gradient_colour = StepColour(base_colour, 180) + + isMac = wx.Platform == "__WXMAC__" + + if isMac: + self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) + else: + self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION) + + self._active_caption_gradient_colour = LightContrastColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)) + self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) + self._inactive_caption_colour = darker1_colour + self._inactive_caption_gradient_colour = StepColour(base_colour, 97) + self._inactive_caption_text_colour = wx.BLACK + + self._sash_brush = wx.Brush(base_colour) + self._background_brush = wx.Brush(base_colour) + self._border_pen = wx.Pen(darker2_colour) + self._gripper_brush = wx.Brush(base_colour) + self._gripper_pen1 = wx.Pen(darker4_colour) + self._gripper_pen2 = wx.Pen(darker3_colour) + self._gripper_pen3 = wx.WHITE_PEN + + + def GetMetric(self, id): + """ + Gets the value of a certain setting. + + :param `id`: can be one of the size values in `Metric Ordinals`. + """ + + + if id == AUI_DOCKART_SASH_SIZE: + return self._sash_size + elif id == AUI_DOCKART_CAPTION_SIZE: + return self._caption_size + elif id == AUI_DOCKART_GRIPPER_SIZE: + return self._gripper_size + elif id == AUI_DOCKART_PANE_BORDER_SIZE: + return self._border_size + elif id == AUI_DOCKART_PANE_BUTTON_SIZE: + return self._button_size + elif id == AUI_DOCKART_GRADIENT_TYPE: + return self._gradient_type + elif id == AUI_DOCKART_DRAW_SASH_GRIP: + return self._draw_sash + else: + raise Exception("Invalid Metric Ordinal.") + + + def SetMetric(self, id, new_val): + """ + Sets the value of a certain setting using `new_val` + + :param `id`: can be one of the size values in `Metric Ordinals`; + :param `new_val`: the new value of the setting. + """ + + if id == AUI_DOCKART_SASH_SIZE: + self._sash_size = new_val + elif id == AUI_DOCKART_CAPTION_SIZE: + self._caption_size = new_val + elif id == AUI_DOCKART_GRIPPER_SIZE: + self._gripper_size = new_val + elif id == AUI_DOCKART_PANE_BORDER_SIZE: + self._border_size = new_val + elif id == AUI_DOCKART_PANE_BUTTON_SIZE: + self._button_size = new_val + elif id == AUI_DOCKART_GRADIENT_TYPE: + self._gradient_type = new_val + elif id == AUI_DOCKART_DRAW_SASH_GRIP: + self._draw_sash = new_val + else: + raise Exception("Invalid Metric Ordinal.") + + + def GetColor(self, id): + """ + Gets the colour of a certain setting. + + :param `id`: can be one of the colour values in `Metric Ordinals`. + """ + + if id == AUI_DOCKART_BACKGROUND_COLOUR: + return self._background_brush.GetColour() + elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR: + return self._background_gradient_colour + elif id == AUI_DOCKART_SASH_COLOUR: + return self._sash_brush.GetColour() + elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR: + return self._inactive_caption_colour + elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR: + return self._inactive_caption_gradient_colour + elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR: + return self._inactive_caption_text_colour + elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR: + return self._active_caption_colour + elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR: + return self._active_caption_gradient_colour + elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR: + return self._active_caption_text_colour + elif id == AUI_DOCKART_BORDER_COLOUR: + return self._border_pen.GetColour() + elif id == AUI_DOCKART_GRIPPER_COLOUR: + return self._gripper_brush.GetColour() + else: + raise Exception("Invalid Colour Ordinal.") + + + def SetColor(self, id, colour): + """ + Sets the colour of a certain setting. + + :param `id`: can be one of the colour values in `Metric Ordinals`; + :param `colour`: the new value of the setting. + """ + + if isinstance(colour, basestring): + colour = wx.NamedColour(colour) + elif isinstance(colour, types.TupleType): + colour = wx.Colour(*colour) + elif isinstance(colour, types.IntType): + colour = wx.ColourRGB(colour) + + if id == AUI_DOCKART_BACKGROUND_COLOUR: + self._background_brush.SetColour(colour) + elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR: + self._background_gradient_colour = colour + elif id == AUI_DOCKART_SASH_COLOUR: + self._sash_brush.SetColour(colour) + elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR: + self._inactive_caption_colour = colour + if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__": + # No custom bitmaps for the pane close button + # Change the MAC close bitmap colour + self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, colour) + + elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR: + self._inactive_caption_gradient_colour = colour + elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR: + self._inactive_caption_text_colour = colour + elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR: + self._active_caption_colour = colour + if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__": + # No custom bitmaps for the pane close button + # Change the MAC close bitmap colour + self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, colour) + + elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR: + self._active_caption_gradient_colour = colour + elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR: + self._active_caption_text_colour = colour + elif id == AUI_DOCKART_BORDER_COLOUR: + self._border_pen.SetColour(colour) + elif id == AUI_DOCKART_GRIPPER_COLOUR: + self._gripper_brush.SetColour(colour) + self._gripper_pen1.SetColour(StepColour(colour, 40)) + self._gripper_pen2.SetColour(StepColour(colour, 60)) + else: + raise Exception("Invalid Colour Ordinal.") + + + GetColour = GetColor + SetColour = SetColor + + def SetFont(self, id, font): + """ + Sets a font setting. + + :param `id`: must be ``AUI_DOCKART_CAPTION_FONT``; + :param `font`: an instance of `wx.Font`. + """ + + if id == AUI_DOCKART_CAPTION_FONT: + self._caption_font = font + + + def GetFont(self, id): + """ + Gets a font setting. + + :param `id`: must be ``AUI_DOCKART_CAPTION_FONT``, otherwise `wx.NullFont` is returned. + """ + + if id == AUI_DOCKART_CAPTION_FONT: + return self._caption_font + + return wx.NullFont + + + def DrawSash(self, dc, window, orient, rect): + """ + Draws a sash between two windows. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `orient`: the sash orientation; + :param `rect`: the sash rectangle. + """ + + # AG: How do we make this work?!? + # RendererNative does not use the sash_brush chosen by the user + # and the rect.GetSize() is ignored as the sash is always drawn + # 3 pixel wide + # wx.RendererNative.Get().DrawSplitterSash(window, dc, rect.GetSize(), pos, orient) + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetBrush(self._sash_brush) + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + draw_sash = self.GetMetric(AUI_DOCKART_DRAW_SASH_GRIP) + if draw_sash: + self.DrawSashGripper(dc, orient, rect) + + + def DrawBackground(self, dc, window, orient, rect): + """ + Draws a background. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `orient`: the gradient (if any) orientation; + :param `rect`: the background rectangle. + """ + + dc.SetPen(wx.TRANSPARENT_PEN) + if wx.Platform == "__WXMAC__": + # we have to clear first, otherwise we are drawing a light striped pattern + # over an already darker striped background + dc.SetBrush(wx.WHITE_BRUSH) + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + DrawGradientRectangle(dc, rect, self._background_brush.GetColour(), + self._background_gradient_colour, + AUI_GRADIENT_HORIZONTAL, rect.x, 700) + + + def DrawBorder(self, dc, window, rect, pane): + """ + Draws the pane border. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `rect`: the border rectangle; + :param `pane`: the pane for which the border is drawn. + """ + + drect = wx.Rect(*rect) + + dc.SetPen(self._border_pen) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + + border_width = self.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE) + + if pane.IsToolbar(): + + for ii in xrange(0, border_width): + + dc.SetPen(wx.WHITE_PEN) + dc.DrawLine(drect.x, drect.y, drect.x+drect.width, drect.y) + dc.DrawLine(drect.x, drect.y, drect.x, drect.y+drect.height) + dc.SetPen(self._border_pen) + dc.DrawLine(drect.x, drect.y+drect.height-1, + drect.x+drect.width, drect.y+drect.height-1) + dc.DrawLine(drect.x+drect.width-1, drect.y, + drect.x+drect.width-1, drect.y+drect.height) + drect.Deflate(1, 1) + + else: + + for ii in xrange(0, border_width): + + dc.DrawRectangle(drect.x, drect.y, drect.width, drect.height) + drect.Deflate(1, 1) + + + def DrawCaptionBackground(self, dc, rect, pane): + """ + Draws the text caption background in the pane. + + :param `dc`: a `wx.DC` device context; + :param `rect`: the text caption rectangle; + :param `pane`: the pane for which the text background is drawn. + """ + + active = pane.state & optionActive + + if self._gradient_type == AUI_GRADIENT_NONE: + if active: + dc.SetBrush(wx.Brush(self._active_caption_colour)) + else: + dc.SetBrush(wx.Brush(self._inactive_caption_colour)) + + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + else: + + switch_gradient = pane.HasCaptionLeft() + gradient_type = self._gradient_type + if switch_gradient: + gradient_type = (self._gradient_type == AUI_GRADIENT_HORIZONTAL and [AUI_GRADIENT_VERTICAL] or \ + [AUI_GRADIENT_HORIZONTAL])[0] + + if active: + if wx.Platform == "__WXMAC__": + DrawGradientRectangle(dc, rect, self._active_caption_colour, + self._active_caption_gradient_colour, + gradient_type) + else: + DrawGradientRectangle(dc, rect, self._active_caption_gradient_colour, + self._active_caption_colour, + gradient_type) + else: + if wx.Platform == "__WXMAC__": + DrawGradientRectangle(dc, rect, self._inactive_caption_gradient_colour, + self._inactive_caption_colour, + gradient_type) + else: + DrawGradientRectangle(dc, rect, self._inactive_caption_colour, + self._inactive_caption_gradient_colour, + gradient_type) + + + def DrawIcon(self, dc, rect, pane): + """ + Draws the icon in the pane caption area. + + :param `dc`: a `wx.DC` device context; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the icon is drawn. + """ + + # Draw the icon centered vertically + if pane.icon.Ok(): + if pane.HasCaptionLeft(): + bmp = wx.ImageFromBitmap(pane.icon).Rotate90(clockwise=False) + dc.DrawBitmap(bmp.ConvertToBitmap(), rect.x+(rect.width-pane.icon.GetWidth())/2, rect.y+rect.height-2-pane.icon.GetHeight(), True) + else: + dc.DrawBitmap(pane.icon, rect.x+2, rect.y+(rect.height-pane.icon.GetHeight())/2, True) + + + def DrawCaption(self, dc, window, text, rect, pane): + """ + Draws the text in the pane caption. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `text`: the text to be displayed; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the text is drawn. + """ + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetFont(self._caption_font) + + self.DrawCaptionBackground(dc, rect, pane) + + if pane.state & optionActive: + dc.SetTextForeground(self._active_caption_text_colour) + else: + dc.SetTextForeground(self._inactive_caption_text_colour) + + w, h = dc.GetTextExtent("ABCDEFHXfgkj") + + clip_rect = wx.Rect(*rect) + btns = pane.CountButtons() + + captionLeft = pane.HasCaptionLeft() + variable = (captionLeft and [rect.height] or [rect.width])[0] + + variable -= 3 # text offset + variable -= 2 # button padding + + caption_offset = 0 + if pane.icon: + if captionLeft: + caption_offset += pane.icon.GetHeight() + 3 + else: + caption_offset += pane.icon.GetWidth() + 3 + + self.DrawIcon(dc, rect, pane) + + variable -= caption_offset + variable -= btns*(self._button_size + self._border_size) + draw_text = ChopText(dc, text, variable) + + if captionLeft: + dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-1, rect.y+rect.height-3-caption_offset, 90) + else: + dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-1) + + + def RequestUserAttention(self, dc, window, text, rect, pane): + """ + Requests the user attention by intermittently highlighting the pane caption. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `text`: the text to be displayed; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the text is drawn. + """ + + state = pane.state + pane.state &= ~optionActive + + for indx in xrange(6): + active = (indx%2 == 0 and [True] or [False])[0] + if active: + pane.state |= optionActive + else: + pane.state &= ~optionActive + + self.DrawCaptionBackground(dc, rect, pane) + self.DrawCaption(dc, window, text, rect, pane) + wx.SafeYield() + wx.MilliSleep(350) + + pane.state = state + + + def DrawGripper(self, dc, window, rect, pane): + """ + Draws a gripper on the pane. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the gripper is drawn. + """ + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetBrush(self._gripper_brush) + + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + if not pane.HasGripperTop(): + y = 4 + while 1: + dc.SetPen(self._gripper_pen1) + dc.DrawPoint(rect.x+3, rect.y+y) + dc.SetPen(self._gripper_pen2) + dc.DrawPoint(rect.x+3, rect.y+y+1) + dc.DrawPoint(rect.x+4, rect.y+y) + dc.SetPen(self._gripper_pen3) + dc.DrawPoint(rect.x+5, rect.y+y+1) + dc.DrawPoint(rect.x+5, rect.y+y+2) + dc.DrawPoint(rect.x+4, rect.y+y+2) + y = y + 4 + if y > rect.GetHeight() - 4: + break + else: + x = 4 + while 1: + dc.SetPen(self._gripper_pen1) + dc.DrawPoint(rect.x+x, rect.y+3) + dc.SetPen(self._gripper_pen2) + dc.DrawPoint(rect.x+x+1, rect.y+3) + dc.DrawPoint(rect.x+x, rect.y+4) + dc.SetPen(self._gripper_pen3) + dc.DrawPoint(rect.x+x+1, rect.y+5) + dc.DrawPoint(rect.x+x+2, rect.y+5) + dc.DrawPoint(rect.x+x+2, rect.y+4) + x = x + 4 + if x > rect.GetWidth() - 4: + break + + + def DrawPaneButton(self, dc, window, button, button_state, _rect, pane): + """ + Draws a pane button in the pane caption area. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `button`: the button to be drawn; + :param `button_state`: the pane button state; + :param `_rect`: the pane caption rectangle; + :param `pane`: the pane for which the button is drawn. + """ + + if not pane: + return + + if button == AUI_BUTTON_CLOSE: + if pane.state & optionActive: + bmp = self._active_close_bitmap + else: + bmp = self._inactive_close_bitmap + + elif button == AUI_BUTTON_PIN: + if pane.state & optionActive: + bmp = self._active_pin_bitmap + else: + bmp = self._inactive_pin_bitmap + + elif button == AUI_BUTTON_MAXIMIZE_RESTORE: + if pane.IsMaximized(): + if pane.state & optionActive: + bmp = self._active_restore_bitmap + else: + bmp = self._inactive_restore_bitmap + else: + if pane.state & optionActive: + bmp = self._active_maximize_bitmap + else: + bmp = self._inactive_maximize_bitmap + + elif button == AUI_BUTTON_MINIMIZE: + if pane.state & optionActive: + bmp = self._active_minimize_bitmap + else: + bmp = self._inactive_minimize_bitmap + + isVertical = pane.HasCaptionLeft() + + rect = wx.Rect(*_rect) + + if isVertical: + old_x = rect.x + rect.x = rect.x + (rect.width/2) - (bmp.GetWidth()/2) + rect.width = old_x + rect.width - rect.x - 1 + else: + old_y = rect.y + rect.y = rect.y + (rect.height/2) - (bmp.GetHeight()/2) + rect.height = old_y + rect.height - rect.y - 1 + + if button_state == AUI_BUTTON_STATE_PRESSED: + rect.x += 1 + rect.y += 1 + + if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]: + + if pane.state & optionActive: + + dc.SetBrush(wx.Brush(StepColour(self._active_caption_colour, 120))) + dc.SetPen(wx.Pen(StepColour(self._active_caption_colour, 70))) + + else: + + dc.SetBrush(wx.Brush(StepColour(self._inactive_caption_colour, 120))) + dc.SetPen(wx.Pen(StepColour(self._inactive_caption_colour, 70))) + + if wx.Platform != "__WXMAC__": + # draw the background behind the button + dc.DrawRectangle(rect.x, rect.y, 15, 15) + else: + # Darker the bitmap a bit + bmp = DarkenBitmap(bmp, self._active_caption_colour, StepColour(self._active_caption_colour, 110)) + + if isVertical: + bmp = wx.ImageFromBitmap(bmp).Rotate90(clockwise=False).ConvertToBitmap() + + # draw the button itself + dc.DrawBitmap(bmp, rect.x, rect.y, True) + + + def DrawSashGripper(self, dc, orient, rect): + """ + Draws a sash gripper on a sash between two windows. + + :param `dc`: a `wx.DC` device context; + :param `orient`: the sash orientation; + :param `rect`: the sash rectangle. + """ + + dc.SetBrush(self._gripper_brush) + + if orient == wx.HORIZONTAL: # horizontal sash + + x = rect.x + int((1.0/4.0)*rect.width) + xend = rect.x + int((3.0/4.0)*rect.width) + y = rect.y + (rect.height/2) - 1 + + while 1: + dc.SetPen(self._gripper_pen3) + dc.DrawRectangle(x, y, 2, 2) + dc.SetPen(self._gripper_pen2) + dc.DrawPoint(x+1, y+1) + x = x + 5 + + if x >= xend: + break + + else: + + y = rect.y + int((1.0/4.0)*rect.height) + yend = rect.y + int((3.0/4.0)*rect.height) + x = rect.x + (rect.width/2) - 1 + + while 1: + dc.SetPen(self._gripper_pen3) + dc.DrawRectangle(x, y, 2, 2) + dc.SetPen(self._gripper_pen2) + dc.DrawPoint(x+1, y+1) + y = y + 5 + + if y >= yend: + break + + + def SetDefaultPaneBitmaps(self, isMac): + """ + Assigns the default pane bitmaps. + + :param `isMac`: whether we are on wxMAC or not. + """ + + if isMac: + self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, self._inactive_caption_colour) + self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, self._active_caption_colour) + else: + self._inactive_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._inactive_caption_text_colour) + self._active_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._active_caption_text_colour) + + if isMac: + self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE) + self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE) + else: + self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._inactive_caption_text_colour) + self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._active_caption_text_colour) + + if isMac: + self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE) + self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE) + else: + self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._inactive_caption_text_colour) + self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._active_caption_text_colour) + + if isMac: + self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE) + self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE) + else: + self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._inactive_caption_text_colour) + self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._active_caption_text_colour) + + self._inactive_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._inactive_caption_text_colour) + self._active_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._active_caption_text_colour) + + self._custom_pane_bitmaps = False + + + def SetCustomPaneBitmap(self, bmp, button, active, maximize=False): + """ + Sets a custom button bitmap for the pane button. + + :param `bmp`: the actual bitmap to set; + :param `button`: the button identifier; + :param `active`: whether it is the bitmap for the active button or not; + :param `maximize`: used to distinguish between the maximize and restore bitmaps. + """ + + if bmp.GetWidth() > 16 or bmp.GetHeight() > 16: + raise Exception("The input bitmap is too big") + + if button == AUI_BUTTON_CLOSE: + if active: + self._active_close_bitmap = bmp + else: + self._inactive_close_bitmap = bmp + + if wx.Platform == "__WXMAC__": + self._custom_pane_bitmaps = True + + elif button == AUI_BUTTON_PIN: + if active: + self._active_pin_bitmap = bmp + else: + self._inactive_pin_bitmap = bmp + + elif button == AUI_BUTTON_MAXIMIZE_RESTORE: + if maximize: + if active: + self._active_maximize_bitmap = bmp + else: + self._inactive_maximize_bitmap = bmp + else: + if active: + self._active_restore_bitmap = bmp + else: + self._inactive_restore_bitmap = bmp + + elif button == AUI_BUTTON_MINIMIZE: + if active: + self._active_minimize_bitmap = bmp + else: + self._inactive_minimize_bitmap = bmp + + +if _ctypes: + class RECT(ctypes.Structure): + """ Used to handle L{ModernDockArt} on Windows XP/Vista/7. """ + _fields_ = [('left', ctypes.c_ulong),('top', ctypes.c_ulong),('right', ctypes.c_ulong),('bottom', ctypes.c_ulong)] + + def dump(self): + """ Dumps `self` as a `wx.Rect`. """ + return map(int, (self.left, self.top, self.right, self.bottom)) + + + class SIZE(ctypes.Structure): + """ Used to handle L{ModernDockArt} on Windows XP/Vista/7. """ + _fields_ = [('x', ctypes.c_long),('y', ctypes.c_long)] + + +class ModernDockArt(AuiDefaultDockArt): + """ + ModernDockArt is a custom `AuiDockArt` class, that implements a look similar to + Firefox and other recents applications. + + Is uses the `winxptheme` module and XP themes whenever possible, so it should + look good even if the user has a custom theme. + + :note: This dock art is Windows only and will only work if you have installed + Mark Hammond's `pywin32` module (http://sourceforge.net/projects/pywin32/). + """ + + def __init__(self, win): + """ + Default class constructor. + + :param `win`: the window managed by L{AuiManager}. + """ + + AuiDefaultDockArt.__init__(self) + + self.win = win + + # Get the size of a small close button (themed) + hwnd = self.win.GetHandle() + + self.hTheme1 = winxptheme.OpenThemeData(hwnd, "Window") + + self.usingTheme = True + + if not self.hTheme1: + self.usingTheme = False + + self._button_size = 13 + + self._button_border_size = 3 + self._caption_text_indent = 6 + self._caption_size = 22 + + # We only highlight the active pane with the caption text being in bold. + # So we do not want a special colour for active elements. + self._active_close_bitmap = self._inactive_close_bitmap + + self.Init() + + + def Init(self): + """ Initializes the dock art. """ + + AuiDefaultDockArt.Init(self) + + self._active_caption_colour = self._inactive_caption_colour + self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_CAPTIONTEXT) + self._inactive_caption_text_colour = self._active_caption_text_colour + + + def DrawCaption(self, dc, window, text, rect, pane): + """ + Draws the text in the pane caption. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `text`: the text to be displayed; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the text is drawn. + """ + + dc.SetPen(wx.TRANSPARENT_PEN) + self.DrawCaptionBackground(dc, rect, pane) + + active = ((pane.state & optionActive) and [True] or [False])[0] + + self._caption_font.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(self._caption_font) + + if active: + dc.SetTextForeground(self._active_caption_text_colour) + else: + dc.SetTextForeground(self._inactive_caption_text_colour) + + w, h = dc.GetTextExtent("ABCDEFHXfgkj") + + clip_rect = wx.Rect(*rect) + btns = pane.CountButtons() + + captionLeft = pane.HasCaptionLeft() + variable = (captionLeft and [rect.height] or [rect.width])[0] + + variable -= 3 # text offset + variable -= 2 # button padding + + caption_offset = 0 + if pane.icon: + if captionLeft: + caption_offset += pane.icon.GetHeight() + 3 + else: + caption_offset += pane.icon.GetWidth() + 3 + + self.DrawIcon(dc, rect, pane) + + diff = -2 + if self.usingTheme: + diff = -1 + + variable -= caption_offset + variable -= btns*(self._button_size + self._button_border_size) + draw_text = ChopText(dc, text, variable) + + if captionLeft: + dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-diff, rect.y+rect.height-3-caption_offset, 90) + else: + dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-diff) + + + def DrawCaptionBackground(self, dc, rect, pane): + """ + Draws the text caption background in the pane. + + :param `dc`: a `wx.DC` device context; + :param `rect`: the text caption rectangle; + :param `pane`: the pane for which we are drawing the caption background. + """ + + dc.SetBrush(self._background_brush) + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + active = ((pane.state & optionActive) and [True] or [False])[0] + + if self.usingTheme: + + rectangle = wx.Rect() + + rc = RECT(rectangle.x, rectangle.y, rectangle.width, rectangle.height) + + # If rect x/y values are negative rc.right/bottom values will overflow and winxptheme.DrawThemeBackground + # will raise a TypeError. Ensure they are never negative. + rect.x = max(0, rect.x) + rect.y = max(0, rect.y) + + rc.top = rect.x + rc.left = rect.y + rc.right = rect.x + rect.width + rc.bottom = rect.y + rect.height + + if active: + winxptheme.DrawThemeBackground(self.hTheme1, dc.GetHDC(), 5, 1, (rc.top, rc.left, rc.right, rc.bottom), None) + else: + winxptheme.DrawThemeBackground(self.hTheme1, dc.GetHDC(), 5, 2, (rc.top, rc.left, rc.right, rc.bottom), None) + + else: + + AuiDefaultDockArt.DrawCaptionBackground(self, dc, rect, pane) + + + def RequestUserAttention(self, dc, window, text, rect, pane): + """ + Requests the user attention by intermittently highlighting the pane caption. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `text`: the text to be displayed; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the text is drawn. + """ + + state = pane.state + pane.state &= ~optionActive + + for indx in xrange(6): + active = (indx%2 == 0 and [True] or [False])[0] + if active: + pane.state |= optionActive + else: + pane.state &= ~optionActive + + self.DrawCaptionBackground(dc, rect, pane) + self.DrawCaption(dc, window, text, rect, pane) + wx.SafeYield() + wx.MilliSleep(350) + + pane.state = state + + + def DrawPaneButton(self, dc, window, button, button_state, rect, pane): + """ + Draws a pane button in the pane caption area. + + :param `dc`: a `wx.DC` device context; + :param `window`: an instance of `wx.Window`; + :param `button`: the button to be drawn; + :param `button_state`: the pane button state; + :param `rect`: the pane caption rectangle; + :param `pane`: the pane for which the button is drawn. + """ + + if self.usingTheme: + + hTheme = self.hTheme1 + + # Get the real button position (compensating for borders) + drect = wx.Rect(rect.x, rect.y, self._button_size, self._button_size) + + # Draw the themed close button + rc = RECT(0, 0, 0, 0) + if pane.HasCaptionLeft(): + rc.top = rect.x + self._button_border_size + rc.left = int(rect.y + 1.5*self._button_border_size) + rc.right = rect.x + self._button_size + self._button_border_size + rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size) + else: + rc.top = rect.x - self._button_border_size + rc.left = int(rect.y + 1.5*self._button_border_size) + rc.right = rect.x + self._button_size- self._button_border_size + rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size) + + if button == AUI_BUTTON_CLOSE: + btntype = 19 + + elif button == AUI_BUTTON_PIN: + btntype = 23 + + elif button == AUI_BUTTON_MAXIMIZE_RESTORE: + if not pane.IsMaximized(): + btntype = 17 + else: + btntype = 21 + else: + btntype = 15 + + state = 4 # CBS_DISABLED + + if pane.state & optionActive: + + if button_state == AUI_BUTTON_STATE_NORMAL: + state = 1 # CBS_NORMAL + + elif button_state == AUI_BUTTON_STATE_HOVER: + state = 2 # CBS_HOT + + elif button_state == AUI_BUTTON_STATE_PRESSED: + state = 3 # CBS_PUSHED + + else: + raise Exception("ERROR: Unknown State.") + + else: # inactive pane + + if button_state == AUI_BUTTON_STATE_NORMAL: + state = 5 # CBS_NORMAL + + elif button_state == AUI_BUTTON_STATE_HOVER: + state = 6 # CBS_HOT + + elif button_state == AUI_BUTTON_STATE_PRESSED: + state = 7 # CBS_PUSHED + + else: + raise Exception("ERROR: Unknown State.") + + try: + winxptheme.DrawThemeBackground(hTheme, dc.GetHDC(), btntype, state, (rc.top, rc.left, rc.right, rc.bottom), None) + except TypeError: + return + + else: + + # Fallback to default closebutton if themes are not enabled + rect2 = wx.Rect(rect.x-4, rect.y+2, rect.width, rect.height) + AuiDefaultDockArt.DrawPaneButton(self, dc, window, button, button_state, rect2, pane) +