Mercurial > traipse
diff orpg/tools/NotebookCtrl.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orpg/tools/NotebookCtrl.py Tue Jul 14 16:41:58 2009 -0500 @@ -0,0 +1,6112 @@ +# --------------------------------------------------------------------------- # +# NOTEBOOKCTRL Control wxPython IMPLEMENTATION +# Python Code By: +# +# Andrea Gavana, @ 11 Nov 2005 +# Latest Revision: 06 Oct 2006, 18.10 GMT +# +# +# AKNOWLEDGEMENTS +# +# A big load of thanks goes to Julianne Sharer that has implemented the new +# features of left/right tabs, rotated or horizontal, with the ability to +# switch between the two views by a single mouse click. Moreover, all the +# work done to refactor NotebookCtrl in a more readable way has been done +# by Julianne Sharer. Thanks Julianne. +# +# +# TODO List/Caveats +# +# 1. Ay Idea? +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To Me At: +# +# andrea.gavana@gmail.com +# gavana@kpo.kz +# +# Or, Obviously, To The wxPython Mailing List!!! +# +# +# End Of Comments +# --------------------------------------------------------------------------- # + + +""" +A full-featured notebook control, worked out by Andrea Gavana And Julianne Sharer. + +Description: + +NotebookCtrl Mimics The Behavior Of wx.Notebook, And Most Of Its Functionalities +Are Implemented In NotebookCtrl. However, NotebookCtrl Has A Lot Of Options That +wx.Notebook Does Not Have, And It Is Therefore Quite Customizable. +wx.Notebook Styles Not Implemented in NotebookCtrl Are: + + - wx.NB_MULTILINE (But NotebookCtrl Has A SpinButton To Navigate + Through Tabs). + +Supported Customizations For NotebookCtrl Include: + + - Setting Individual Tab Font And Text Colour; + - Images On Tabs (Line wx.Notebook); + - Setting Individual Tab Colours; + - Disabling/Enabling Individual Tabs (Also Visually Effective); + Now Supports Grayed Out Icons When A Page Is Disabled; + - Drawing Of A Small Closing "X" At The Right Of Every Tab, That Enable The User + To Close A Tab With A Mouse Click (Like eMule Tab Style); + - Enabling Highlighted Tabs On Selection; + - Drawing Focus Indicator In Each Tab (Like wx.Notebook); + - Ctrl-Tab Keyboard Navigation Between Pages; + - Tab With Animated Icons (Animation On Tabs); + - Drag And Drop Tabs In NotebookCtrl (Plus A Visual Arrow Effect + To Indicate Dropping Position); + - Drag And Drop Event; + - ToolTips On Individual Tabs, With Customizable ToolTip Time + Popup And ToolTip Window Size For Individual Tabs; + - Possibility To Hide The TabCtrl There Is Only One Tab (Thus + Maximizing The Corresponding Window); + - Possibility To Convert The Tab Image Into A Close Button While + Mouse Is Hovering On The Tab Image; + - Popup Menus On Tabs (Popup Menus Specific To Each Tab); + - Showing Pages In "Column/Row Mode", Which Means That All Pages + Will Be Shown In NotebookCtrl While The Tabs Are Hidden. They + Can Be Shown In Columns (Default) Or In Rows; + - Possibility To Hide Tabs On User Request, Thus Showing Only The + Current Panel; + - Multiple Tabs Selection (Hold Ctrl Key Down And Left Mouse + Click), Useful When You Use The Show All The Panels In + Columns/Rows. In This Case, Only The Selected Tabs Are Shown In + Columns/Rows; + - Events For Mouse Events (Left Double Click, Middle Click, Right Click); + - Possibility To Reparent A NotebookCtrl Page To A Freshly Created + Frame As A Simple Panel Or To A New NotebookCtrl Created Inside + That New Frame. + - Possibility To Add A Custom Panel To Show A Logo Or HTML + Information Or Whatever You Like When There Are No Tabs In + NotebookCtrl; + - Possibility To Change The ToolTip Window Background Colour; + - Possibility To Draw Vertical Or Horizontal Gradient Coloured Tabs + (2 Colours); + - Themes On Tabs: Built-In Themes Are KDE (Unix/Linux), Metal, + Aqua Light And Aqua Dark (MacOS), Windows Silver (Windows) Or + Generic Gradient Coloured Tabs. It's Also Possible To Define A + Separate Theme For Selected Tabs And Control Background (The + Last Two Are Work In Progress); + - Contour Line Colour Around Tabs Is Customizable; + - Highlight Colour Of Selected Tab Is Customizable; + - Each Tab Can Have Its Own Gradient Colouring (2 Colours For Every Tab); + - Custom Images May Be Drawn As A "X" Close Buttons On Tabs; + - Possibility To Hide A Particular Tab Using A wx.PopupMenu That + Is Shown If You Call EnableHiding(True). Look At The Top Right + Of NotebookCtrl; + - Allows Drag And Drop Of Tabs/Pages Between Different + NotebookCtrls In The Same Application. + - Draw tabs on the left or right side, rotated or horizontal + - Allow user to switch between rotated and horizontal displays of + tabs on the left or right side. + + +Usage: + +NotebookCtrl Construction Is Quite Similar To wx.Notebook:: + + NotebookCtrl.__init__(self, parent, id, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=style, sizer=nbsizer) + +See L{NotebookCtrl.__init__} Method For The Definition Of Non Standard (Non +wxPython) Parameters. + +NotebookCtrl Control Is Freeware And Distributed Under The wxPython License. + +Latest Revision: Andrea Gavana @ 06 Oct 2006, 18.10 GMT + +@undocumented: NC_MAC*, topaqua*, botaqua*, distaqua*, disbaqua*, + kdetheme, silvertheme*, wxEVT*, attrs, GetMenuButton*, NCDragInfo, + NCDropTarget, TabbedPage, TabCtrl, TransientTipWindow, + macPopupWindow, macTransientTipWindow, NCFrame, DEFAULT_SIZE, + NotebookSpinButton, NotebookMenuButton +""" + +__docformat__ = "epytext" + + +#---------------------------------------------------------------------- +# Beginning Of NOTEBOOKCTRL wxPython Code +#---------------------------------------------------------------------- + +from orpg.orpg_wx import * +from wx.lib.buttons import GenBitmapButton as BitmapButton +import wx.xrc as xrc + +import cStringIO, zlib +import cPickle +import weakref + +# HitTest Results +NC_HITTEST_NOWHERE = 0 # Not On Tab +"""Indicates mouse coordinates not on any tab of the notebook""" +NC_HITTEST_ONICON = 1 # On Icon +"""Indicates mouse coordinates on an icon in a tab of the notebook""" +NC_HITTEST_ONLABEL = 2 # On Label +"""Indicates mouse coordinates on a label in a tab of the notebook""" +NC_HITTEST_ONITEM = 4 # Generic, On Item +"""Indicates mouse coordinates on a tab of the notebook""" +NC_HITTEST_ONX = 8 # On Small Square On Every Page +"""Indicates mouse coordinates on the closing I{X} in a tab of the notebook""" + +# NotebookCtrl Styles +# NotebookCtrl Placed On Top (Default) +NC_TOP = 1 +"""Specify tabs at the top of the notebook control.""" +# NotebookCtrl Placed At The Bottom +NC_BOTTOM = 2 +"""Specify tabs at the bottom of the notebook control.""" +# NotebookCtrl With Fixed Width Tabs +NC_FIXED_WIDTH = 4 +"""Specify tabs of a fixed width in the notebook control.""" +# NotebookCtrl Placed At The Left +NC_LEFT = 8 +"""Specify tabs on the left side of the notebook control.""" +# NotebookCtrl Placed At The Right +NC_RIGHT = 16 +"""Specify tabs on the right side of the notebook control.""" +# NotebookCtrl tab rotated +NC_ROTATE = 32 +"""Specify rotated tabs (with vertical text) in the notebook control.""" +# NotebookCtrl switchable between compact and expanded sizes +NC_EXPANDABLE = 64 +"""Specify that the notebook control includes a toggle button to +switch between compact tabs (rotated on the left or right side) +expanded tabs (horizontal on the left or right side).""" + +NC_DEFAULT_STYLE = NC_TOP | wx.NO_BORDER +"""The default style for the notebook control (tabs on top with no border)""" +# Also wx.STATIC_BORDER Is Supported + +# NotebookCtrl theme styles +NC_GRADIENT_VERTICAL = 1 +"""Specify tabs rendered with a vertical gradient background.""" +NC_GRADIENT_HORIZONTAL = 2 +"""Specify tabs rendered with a horizontal gradient background.""" +NC_GRADIENT_SELECTION = 4 +NC_AQUA_LIGHT = 8 +"""Specify tabs rendered with a Mac I{Light Aqua}-like background.""" +NC_AQUA_DARK = 16 +"""Specify tabs rendered with a Mac I{Dark Aqua}-like background.""" +NC_AQUA = NC_AQUA_LIGHT +"""Specify tabs rendered with a Mac I{Light Aqua}-like background.""" +NC_METAL = 32 +"""Specify tabs rendered with a Mac I{Metal}-like background.""" +NC_SILVER = 64 +"""Specify tabs rendered with a Windows I{Silver}-like background.""" +NC_KDE = 128 +"""Specify tabs rendered with a KDE-style background.""" + +# Patch To Make NotebookCtrl Working Also On MacOS: Thanks To Stani ;-) +if wx.Platform == '__WXMAC__': + DEFAULT_SIZE = wx.Size(26, 26) +else: + DEFAULT_SIZE = wx.DefaultSize + +# Themes On Mac... This May Slow Down The Paint Event If You Turn It On! +NC_MAC_LIGHT = (240, 236) +NC_MAC_DARK = (232, 228) + +topaqua1 = [wx.Colour(106, 152, 231), wx.Colour(124, 173, 236)] +botaqua1 = [wx.Colour(54, 128, 213), wx.Colour(130, 225, 249)] + +topaqua2 = [wx.Colour(176, 222, 251), wx.Colour(166, 211, 245)] +botaqua2 = [wx.Colour(120, 182, 244), wx.Colour(162, 230, 245)] + +distaqua = [wx.Colour(248, 248, 248), wx.Colour(243, 243, 243)] +disbaqua = [wx.Colour(219, 219, 219), wx.Colour(248, 248, 248)] + +# Themes On KDE... This May Slow Down The Paint Event If You Turn It On! +kdetheme = [wx.Colour(0xf3,0xf7,0xf9), wx.Colour(0xf3,0xf7,0xf9), + wx.Colour(0xee,0xf3,0xf7), wx.Colour(0xee,0xf3,0xf7), + wx.Colour(0xea,0xf0,0xf4), wx.Colour(0xea,0xf0,0xf4), + wx.Colour(0xe6,0xec,0xf1), wx.Colour(0xe6,0xec,0xf1), + wx.Colour(0xe2,0xe9,0xef), wx.Colour(0xe2,0xe9,0xef), + wx.Colour(0xdd,0xe5,0xec), wx.Colour(0xdd,0xe5,0xec), + wx.Colour(0xd9,0xe2,0xea), wx.Colour(0xd9,0xe2,0xea)] + +# Themes On Windows... This May Slow Down The Paint Event If You Turn It On! +silvertheme2 = [wx.Colour(255, 255, 255), wx.Colour(190, 190, 216), + wx.Colour(180, 180, 200)] +silvertheme1 = [wx.Colour(252, 252, 254), wx.Colour(252, 252, 254)] + +# NotebookCtrl Events: +# wxEVT_NOTEBOOKCTRL_PAGE_CHANGED: Event Fired When You Switch Page; +# wxEVT_NOTEBOOKCTRL_PAGE_CHANGING: Event Fired When You Are About To Switch +# Pages, But You Can Still "Veto" The Page Changing By Avoiding To Call +# event.Skip() In Your Event Handler; +# wxEVT_NOTEBOOKCTRL_PAGE_CLOSING: Event Fired When A Page Is Closing, But +# You Can Still "Veto" The Page Changing By Avoiding To Call event.Skip() +# In Your Event Handler; +# wxEVT_NOTEBOOKCTRL_PAGE_DND: Event Fired When A Drag And Drop Action On +# Tabs Ends. +wxEVT_NOTEBOOKCTRL_PAGE_CHANGED = wx.NewEventType() +wxEVT_NOTEBOOKCTRL_PAGE_CHANGING = wx.NewEventType() +wxEVT_NOTEBOOKCTRL_PAGE_CLOSING = wx.NewEventType() +wxEVT_NOTEBOOKCTRL_PAGE_DND = wx.NewEventType() +wxEVT_NOTEBOOKCTRL_PAGE_DCLICK = wx.NewEventType() +wxEVT_NOTEBOOKCTRL_PAGE_RIGHT = wx.NewEventType() +wxEVT_NOTEBOOKCTRL_PAGE_MIDDLE = wx.NewEventType() + +#-----------------------------------# +# NotebookCtrlEvent +#-----------------------------------# + +EVT_NOTEBOOKCTRL_PAGE_CHANGED = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_CHANGED, 1) +"""Notify client objects when the active page in the notebook control +has changed.""" + +EVT_NOTEBOOKCTRL_PAGE_CHANGING = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_CHANGING, 1) +"""Notify client objects when the active page in the notebook control +is changing.""" + +EVT_NOTEBOOKCTRL_PAGE_CLOSING = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_CLOSING, 1) +"""Notify client objects when a page in the notebook control is closing.""" + +EVT_NOTEBOOKCTRL_PAGE_DND = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_DND, 1) +"""Enable client objects to override the behavior of the notebook control +when a dragged tab is dropped onto it.""" + +EVT_NOTEBOOKCTRL_PAGE_DCLICK = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_DCLICK, 1) +"""Notify client objects when the user double-clicks a tab +in the notebook control.""" + +EVT_NOTEBOOKCTRL_PAGE_RIGHT = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_RIGHT, 1) +"""Notify client objects when the user right-clicks a tab +in the notebook control.""" + +EVT_NOTEBOOKCTRL_PAGE_MIDDLE = wx.PyEventBinder(wxEVT_NOTEBOOKCTRL_PAGE_MIDDLE, 1) +"""Notify client objects when the user clicks with the +middle mouse button on a tab in the notebook control.""" + +attrs = ["_backstyle", "_backtooltip", "_borderpen", "_convertimage", "_drawx", + "_drawxstyle", "_enabledragging", "_focusindpen", "_hideonsingletab", + "_highlight", "_padding", "_selectioncolour", "_selstyle", "_tabstyle", + "_upperhigh", "_usefocus", "_usegradients"] + + +# Check for the new method in 2.7 (not present in 2.6.3.3) +if wx.VERSION_STRING < "2.7": + wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point) + + +# ---------------------------------------------------------------------------- # +def GetMenuButtonData(): + + return zlib.decompress( +"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x9c \xcc\xc1\ +\x06$\x1fLd\x13\x00R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4[x\xba8\x86HL\xed\ +\xbd`\xc8\xc7\xa0\xc0\xe1|q\xdb\x9d\xff'\xba\xb4\x1d\x05v\xff}\xe2\xab\x9a:c\ +\x99\xc4\xbe\xe9\xfd+\x9a\xc5W%t\x1a\xe5\x08\xa6\xd6,\xe2\xf0\x9a\xc2\xc8\ +\xf0\xe1\xf9r\xe6\xa3\xc9\x02b\xd9\x0c35\x80f0x\xba\xfa\xb9\xacsJh\x02\x00\ +\xcd-%1") + + +def GetMenuButtonBitmap(): + + return wx.BitmapFromImage(GetMenuButtonImage()) + + +def GetMenuButtonImage(): + + stream = cStringIO.StringIO(GetMenuButtonData()) + return wx.ImageFromStream(stream) + +# ---------------------------------------------------------------------------- # + +def GrayOut(anImage): + """ + Convert The Given Image (In Place) To A Grayed-Out Version, + Appropriate For A 'Disabled' Appearance. + """ + + factor = 0.7 # 0 < f < 1. Higher Is Grayer + + if anImage.HasMask(): + maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue()) + else: + maskColor = None + + data = map(ord, list(anImage.GetData())) + + for i in range(0, len(data), 3): + + pixel = (data[i], data[i+1], data[i+2]) + pixel = MakeGray(pixel, factor, maskColor) + + for x in range(3): + data[i+x] = pixel[x] + + anImage.SetData(''.join(map(chr, data))) + + return anImage + + +def MakeGray((r,g,b), factor, maskColor): + """ + Make A Pixel Grayed-Out. If The Pixel Matches The MaskColor, It Won't Be + Changed. + """ + + if (r,g,b) != maskColor: + return map(lambda x: int((230 - x) * factor) + x, (r,g,b)) + else: + return (r,g,b) + +def GetDefaultTabStyle(): + tabstyle = ThemeStyle() + + # Draw Mac Themes On Tabs? + if wx.Platform == "__WXMAC__" or wx.Platform == "__WXCOCOA__": + tabstyle.EnableAquaTheme(True, 2) + # Draw Windows Silver Theme On Tabs? + elif wx.Platform == "__WXMSW__": + tabstyle.EnableSilverTheme(True) + else: + tabstyle.EnableKDETheme(True) + + return tabstyle + +# ---------------------------------------------------------------------------- # +# Class NotebookCtrlEvent +# ---------------------------------------------------------------------------- # + +class NotebookCtrlEvent(wx.PyCommandEvent): + """ + Represent details of the events that the L{NotebookCtrl} object sends. + """ + + def __init__(self, eventType, id=1, nSel=-1, nOldSel=-1): + """ Default Class Constructor. """ + + wx.PyCommandEvent.__init__(self, eventType, id) + self._eventType = eventType + + + def SetSelection(self, nSel): + """ Sets Event Selection. """ + + self._selection = nSel + + + def SetOldSelection(self, nOldSel): + """ Sets Old Event Selection. """ + + self._oldselection = nOldSel + + + def GetSelection(self): + """ Returns Event Selection. """ + + return self._selection + + + def GetOldSelection(self): + """ Returns Old Event Selection """ + + return self._oldselection + + + def SetOldPosition(self, pos): + """ Sets Old Event Position. """ + + self._oldposition = pos + + + def SetNewPosition(self, pos): + """ Sets New Event Position. """ + + self._newposition = pos + + + def GetOldPosition(self): + """ Returns Old Event Position. """ + + return self._oldposition + + + def GetNewPosition(self): + """ Returns New Event Position. """ + + return self._newposition + + +# ---------------------------------------------------------------------------- # +# Class NCDragInfo +# Stores All The Information To Allow Drag And Drop Between Different +# NotebookCtrls In The Same Application. +# ---------------------------------------------------------------------------- # + +class NCDragInfo: + + _map = weakref.WeakValueDictionary() + + def __init__(self, container, pageindex): + """ Default Class Constructor. """ + + self._id = id(container) + NCDragInfo._map[self._id] = container + self._pageindex = pageindex + + + def GetContainer(self): + """ Returns The NotebookCtrl Page (Usually A Panel). """ + + return NCDragInfo._map.get(self._id, None) + + + def GetPageIndex(self): + """ Returns The Page Index Associated With A Page. """ + + return self._pageindex + + +# ---------------------------------------------------------------------------- # +# Class NCDropTarget +# Simply Used To Handle The OnDrop() Method When Dragging And Dropping Between +# Different NotebookCtrls. +# ---------------------------------------------------------------------------- # + +class NCDropTarget(wx.DropTarget): + + def __init__(self, parent): + """ Default Class Constructor. """ + + wx.DropTarget.__init__(self) + + self._parent = parent + self._dataobject = wx.CustomDataObject(wx.CustomDataFormat("NotebookCtrl")) + self.SetDataObject(self._dataobject) + + + def OnData(self, x, y, dragres): + """ Handles The OnData() Method TO Call The Real DnD Routine. """ + + if not self.GetData(): + return wx.DragNone + + draginfo = self._dataobject.GetData() + drginfo = cPickle.loads(draginfo) + + return self._parent.OnDropTarget(x, y, drginfo.GetPageIndex(), drginfo.GetContainer()) + + +# ---------------------------------------------------------------------------- # +# Class ThemeStyle. Used To Define A Custom Style For Tabs And Control +# Background Colour. +# ---------------------------------------------------------------------------- # + +class ThemeStyle: + """ + Represent the style for rendering a notebook tab. + """ + + GRADIENT_VERTICAL = 1 + GRADIENT_HORIZONTAL = 2 + DIFFERENT_GRADIENT_FOR_SELECTED = 4 + + def __init__(self): + """ Default Constructor For This Class.""" + + self.ResetDefaults() + + + def ResetDefaults(self): + """ Resets Default Theme. """ + + self._normal = True + self._aqua = False + self._metal = False + self._macstyle = False + self._kdetheme = False + self._silver = False + self._gradient = False + self._firstcolour = wx.WHITE + self._secondcolour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + self._firstcolourselected = wx.WHITE + self._secondcolourselected = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + + + def EnableMacTheme(self, enable=True, style=1): + """ + Enables/Disables Mac Themes. style=1 Is The Light Style, While style=2 + Is The Dark Style. Mainly Used For Control Background Colour, Not For Tabs. + """ + + if enable: + self._normal = False + self._macstyle = style + self._kdetheme = False + self._metal = False + self._aqua = False + self._silver = False + self._gradient = False + else: + self._macstyle = 0 + + + def EnableKDETheme(self, enable=True): + """ Globally Enables/Disables Unix-Like KDE Theme For Tabs. """ + + self._kdetheme = enable + + if enable: + self._normal = False + self._macstyle = False + self._metal = False + self._aqua = False + self._silver = False + self._gradient = False + + + def EnableMetalTheme(self, enable=True): + """ Globally Enables/Disables Mac-Like Metal Theme For Tabs. """ + + self._metal = enable + + if enable: + self._normal = False + self._macstyle = False + self._kdetheme = False + self._aqua = False + self._silver = False + self._gradient = False + + + def EnableAquaTheme(self, enable=True, style=1): + """ Globally Enables/Disables Mac-Like Aqua Theme For Tabs. """ + + if enable: + self._aqua = style + self._normal = False + self._macstyle = False + self._kdetheme = False + self._metal = False + self._silver = False + self._gradient = False + else: + self._aqua = 0 + + + def EnableSilverTheme(self, enable=True): + """ Globally Enables/Disables Windows Silver Theme For Tabs. """ + + self._silver = enable + + if enable: + self._normal = False + self._macstyle = False + self._kdetheme = False + self._metal = False + self._aqua = False + self._gradient = False + + + def EnableGradientStyle(self, enable=True, style=1): + """ + Enables/Disables Gradient Drawing On Tabs. style=1 Is The Vertical Gradient, + While style=2 Is The Horizontal Gradient. + If style flag 4 is set, the style has a separate set of colors for the + selected tab. + """ + + if enable: + self._normal = False + if style & self.GRADIENT_VERTICAL == 0 and style & self.GRADIENT_HORIZONTAL == 0: + style |= self.GRADIENT_VERTICAL + self._gradient = style + self._macstyle = False + self._kdetheme = False + self._metal = False + self._aqua = False + self._silver = False + else: + self._gradient = 0 + + + def SetFirstGradientColour(self, colour=None): + """ Sets The First Gradient Colour. """ + + if colour is None: + colour = wx.WHITE + + self._firstcolour = colour + + def SetFirstGradientColourSelected(self, colour=None): + """Sets The First Gradient Colour For The Selected Tab.""" + if colour is None: + colour = wx.WHITE + + self._firstcolourselected = colour + + def SetSecondGradientColour(self, colour=None): + """ Sets The Second Gradient Colour. """ + + if colour is None: + color = self.GetBackgroundColour() + r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) + color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) + colour = wx.Colour(color[0], color[1], color[2]) + + self._secondcolour = colour + + def SetSecondGradientColourSelected(self, colour=None): + """ Sets The Second Gradient Colour For The Selected Tab. """ + + if colour is None: + color = self.GetBackgroundColour() + r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) + color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) + colour = wx.Colour(color[0], color[1], color[2]) + + self._secondcolourselected = colour + + def GetFirstGradientColour(self, selected=False): + """ Returns The First Gradient Colour. """ + + if selected and self._gradient & self.DIFFERENT_GRADIENT_FOR_SELECTED: + return self._firstcolourselected + else: + return self._firstcolour + + def GetSecondGradientColour(self, selected=False): + """ Returns The Second Gradient Colour. """ + + if selected and self._gradient & self.DIFFERENT_GRADIENT_FOR_SELECTED: + return self._secondcolourselected + else: + return self._secondcolour + + +# ---------------------------------------------------------------------------- # +# Class TabbedPage +# This Is Just A Container Class That Initialize All The Default Settings For +# Every Tab. +# ---------------------------------------------------------------------------- # + +class TabbedPage: + + def __init__(self, text="", image=-1, hidden=False): + """ Default Class Constructor. """ + + self._text = text + self._image = image + self._font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + self._secondaryfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + self._pagetextcolour = wx.BLACK + self._pagecolour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + self._enable = True + self._animationimages = [] + self._tooltip = "" + self._tooltiptime = 500 + self._winsize = 400 + self._menu = None + self._ishidden = hidden + self._firstcolour = color = wx.WHITE + r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) + color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) + colour = wx.Colour(color[0], color[1], color[2]) + self._secondcolour = colour + + +# ---------------------------------------------------------------------------- # +# Class NotebookSpinButton +# This SpinButton Is Created/Shown Only When The Total Tabs Size Exceed The +# Client Size, Allowing The User To Navigate Between Tabs By Clicking On The +# SpinButton. It Is Very Similar To The wx.Notebook SpinButton +# ---------------------------------------------------------------------------- # + +class NotebookSpinButton(wx.SpinButton): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.SP_HORIZONTAL): + """ Default Class Constructor. """ + + wx.SpinButton.__init__(self, parent, id, pos, size, style) + self._nb = parent + self._oldvalue = 0 + self._style = style + self.Bind(wx.EVT_SPIN, self.OnSpin) + + def GetValue(self): + result = super(NotebookSpinButton, self).GetValue() + if self._style & wx.SP_VERTICAL: + result = -result + return result + + def OnSpin(self, event): + """ Handles The User's Clicks On The SpinButton. """ + + if type(event) != type(1): + pos = event.GetPosition() + else: + pos = event + + if pos < self.GetMin(): + self.SetValue(self.GetMin()) + return + + if type(event) != type(1): + if self._nb._enablehiding: + if pos < self._oldvalue: + incr = -1 + else: + incr = 1 + while self._nb._pages[pos]._ishidden: + pos = pos + incr + + self.SetValue(pos) + + if self._nb.IsLastVisible(): + if (self._style & wx.SP_HORIZONTAL and self._oldvalue < pos) or \ + (self._style & wx.SP_VERTICAL and self._oldvalue > pos): + self.SetValue(self._oldvalue) + return + + self._oldvalue = pos + + self._nb.Refresh() + + +# ---------------------------------------------------------------------------- # +# Class NotebookMenuButton +# This MenuButton Is Created/Shown Only When You Activate The Option EnableHiding +# Of NotebookCtrl. This Small Button Will Be Shown Right Above The Spin Button +# (If Present), Or In The Position Of The Spin Button. +# ---------------------------------------------------------------------------- # + +class NotebookMenuButton(BitmapButton): + + def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=(15, 11), + style=0): + """ Default Class Constructor. """ + + bmp = GetMenuButtonBitmap() + + BitmapButton.__init__(self, parent, id, bmp, pos, size, style) + + self.SetUseFocusIndicator(False) + self.SetBezelWidth(1) + + self._originalcolour = self.GetBackgroundColour() + self._nb = parent + + self.Bind(wx.EVT_BUTTON, self.OnButton) + self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + self.Bind(wx.EVT_MENU, self.OnMenu) + + + def OnButton(self, event): + """ Handles The wx.EVT_BUTTON For NotebookMenuButton (Opens The wx.PopupMenu) """ + + count = self._nb.GetPageCount() + + if count <= 0: + return + + menu = wx.Menu() + id = wx.NewId() + myids = [] + + for ii in xrange(count): + + id = id + 1 + myids.append(id) + name = self._nb.GetPageText(ii) + + if self._nb._pages[ii]._ishidden: + msg = "Page Hidden" + check = False + else: + msg = "Page Shown" + check = True + + item = wx.MenuItem(menu, id, name, msg, wx.ITEM_CHECK) + menu.AppendItem(item) + + if not self._nb._pages[ii]._ishidden: + item.Check() + + menu.SetHelpString(id, msg) + + self._myids = myids + + self.PopupMenu(menu) + + event.Skip() + + + def OnMenu(self, event): + """ Handles The wx.EVT_MENU For NotebookMenuButton. Calls HideTab(). """ + + indx = self._myids.index(event.GetId()) + checked = not event.GetEventObject().IsChecked(event.GetId()) + + self._nb.HideTab(indx, not checked) + + event.Skip() + + + def OnEnterWindow(self, event): + """ + Changes The NotebookMenuButton Background Colour When The Mouse + Enters The Button Region. + """ + + entercolour = self.GetBackgroundColour() + firstcolour = entercolour.Red() + secondcolour = entercolour.Green() + thirdcolour = entercolour.Blue() + + if entercolour.Red() > 235: + firstcolour = entercolour.Red() - 40 + if entercolour.Green() > 235: + secondcolour = entercolour.Green() - 40 + if entercolour.Blue() > 235: + thirdcolour = entercolour.Blue() - 40 + + entercolour = wx.Colour(firstcolour+20, secondcolour+20, thirdcolour+20) + + self.SetBackgroundColour(entercolour) + self.Refresh() + + event.Skip() + + + def OnLeaveWindow(self, event): + """ + Restore The NotebookMenuButton Background Colour When The Mouse + Leaves The Button Region. + """ + + self.SetBackgroundColour(self._originalcolour) + self.Refresh() + + event.Skip() + +class _TabCtrlPaintTools(object): + # Structure-like object for passing data among + # private rendering methods + def __init__(self, backBrush, backPen, borderPen, highlightPen, + shadowPen, upperHighlightPen, selectionPen, selectionEdgePen, + xPen, focusPen): + self.BackBrush = backBrush + self.BackPen = backPen + self.BorderPen = borderPen + self.HighlightPen = highlightPen + self.ShadowPen = shadowPen + self.UpperHighlightPen = upperHighlightPen + self.SelectionPen = selectionPen + self.SelectionEdgePen = selectionEdgePen + self.XPen = xPen + self.FocusPen = focusPen + + +# ---------------------------------------------------------------------------- # +# Class TabCtrl +# This Class Handles The Drawing Of Every Tab In The NotebookCtrl, And Also +# All Settings/Methods For Every Tab. +# ---------------------------------------------------------------------------- # + +class TabCtrl(wx.PyControl): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=DEFAULT_SIZE, style=NC_DEFAULT_STYLE, + validator=wx.DefaultValidator, name="TabCtrl"): + """ + Default Class Constructor. + Used Internally. Do Not Call It Explicitely! + """ + + wx.PyControl.__init__(self, parent, id, pos, size, wx.NO_BORDER | wx.WANTS_CHARS, + validator, name) + + # Set All The Default Parameters For TabCtrl + self._selection = -1 + self._imglist = 0 + self._style = style + self._expanded = False + self._pages = [] + self._enabledpages = [] + + self._padding = wx.Point(8, 4) + self._spacetabs = 2 + self._xrect = [] + self._xrefreshed = False + self._imageconverted = False + self._convertimage = False + self._disabledcolour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT) + + self._hover = False + self._parent = parent + self._firsttime = True + self._somethingchanged = True + self._isdragging = False + self._tabID = -1 + self._enabledragging = False + self._olddragpos = -1 + self._fromdnd = False + self._isleaving = False + self._highlight = False + self._usefocus = True + self._hideonsingletab = False + self._selectioncolour = wx.Colour(255, 200, 60) + self._selectionedgecolour = wx.Colour(230, 139, 44) + + self._tabstyle = ThemeStyle() + self._backstyle = ThemeStyle() + self._selstyle = ThemeStyle() + self._usegradients = False + + self._insidetab = -1 + self._showtooltip = False + self._istooltipshown = False + self._tipwindow = None + self._tiptimer = wx.PyTimer(self.OnShowToolTip) + self._backtooltip = wx.Colour(255, 255, 230) + self._xvideo = wx.SystemSettings_GetMetric(wx.SYS_SCREEN_X) + self._yvideo = wx.SystemSettings_GetMetric(wx.SYS_SCREEN_Y) + + self._selectedtabs = [] + + self._timers = [] + + self._dragcursor = wx.StockCursor(wx.CURSOR_HAND) + self._dragstartpos = wx.Point() + + self._drawx = False + self._drawxstyle = 1 + + self._pmenu = None + + self._enablehiding = False + if (style & NC_LEFT or style & NC_RIGHT) and style & NC_EXPANDABLE: + self._InitExpandableStyles(style) + self._InitExpandableTabStyles(self._style, self._expanded, self._tabstyle) + self._CreateSizeToggleButton() + else: + self._sizeToggleButton = None + + self.SetDefaultPage() + + if style & NC_TOP or style & NC_BOTTOM: + self.SetBestSize((-1, 28)) + self._firsttabpos = wx.Point(3, 0) + else: + self.SetBestSize((28, -1)) + self._firsttabpos = wx.Point(0, 3 + self._CalcSizeToggleBestSize()[1]) + + self._borderpen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + self._highlightpen2 = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self._highlightpen = wx.Pen((145, 167, 180)) + self._upperhigh = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self._shadowpen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DDKSHADOW), 2) + self._shadowpen.SetCap(wx.CAP_BUTT) + self._highlightpen.SetCap(wx.CAP_BUTT) + self._highlightpen2.SetCap(wx.CAP_BUTT) + + if wx.Platform == "__WXMAC__": + self._focusindpen = wx.Pen(wx.BLACK, 1, wx.SOLID) + else: + self._focusindpen = wx.Pen(wx.BLACK, 1, wx.USER_DASH) + self._focusindpen.SetDashes([1,1]) + self._focusindpen.SetCap(wx.CAP_BUTT) + + self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown) + self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseLeftDClick) + self.Bind(wx.EVT_MOTION, self.OnMouseMotion) + self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp) + self.Bind(wx.EVT_RIGHT_UP, self.OnMouseRightUp) + self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown) + self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMouseMiddleDown) + self.Bind(wx.EVT_SIZE, self.OnSize) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) + + self.Bind(wx.EVT_TIMER, self.AnimateTab) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + + self._droptarget = NCDropTarget(self) + self.SetDropTarget(self._droptarget) + + def Contract(self): + if self._style & NC_EXPANDABLE and self._expanded: + self._ToggleSize() + + def Expand(self): + if self._style & NC_EXPANDABLE and not self._expanded: + self._ToggleSize() + + def _ToggleSize(self, event=None): + if self._style & NC_EXPANDABLE and not self._expanded: + # contract + self._style = self._expandedstyle + self._tabstyle = self._expandedtabstyle + self._expanded = True + self._sizeToggleButton.SetLabel("<<") + elif self._style & NC_EXPANDABLE and self._expanded: + # expand + self._style = self._contractedstyle + self._tabstyle = self._contractedtabstyle + self._expanded = False + self._sizeToggleButton.SetLabel(">>") + self._OnStyleChange() + + + def OnDropTarget(self, x, y, nPage, oldcont): + """ Handles The OnDrop Action For Drag And Drop Between Different NotebookCtrl. """ + + where = self.HitTest(wx.Point(x, y)) + + oldNotebook = oldcont.GetParent() + newNotebook = self.GetParent() + + if oldNotebook == newNotebook: + if where >= 0 and where != self._tabID: + + self._isdragging = False + self._olddragpos = -1 + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_DND, self.GetId()) + eventOut.SetOldPosition(self._tabID) + eventOut.SetNewPosition(where) + eventOut.SetEventObject(self) + + if self.GetEventHandler().ProcessEvent(eventOut): + self._tabID = -1 + self._olddragpos = -1 + self.SetCursor(wx.STANDARD_CURSOR) + self.Refresh() + return + + self._parent.Freeze() + + try: + text = self.GetPageText(self._tabID) + image = self.GetPageImage(self._tabID) + font1 = self.GetPageTextFont(self._tabID) + font2 = self.GetPageTextSecondaryFont(self._tabID) + fontcolour = self.GetPageTextColour(self._tabID) + pagecolour = self.GetPageColour(self._tabID) + enabled = self.IsPageEnabled(self._tabID) + tooltip, ontime, winsize = self.GetPageToolTip(self._tabID) + menu = self.GetPagePopupMenu(self._tabID) + firstcol = self.GetPageFirstGradientColour(self._tabID) + secondcol = self.GetPageSecondGradientColour(self._tabID) + ishidden = self._pages[self._tabID]._ishidden + except: + self._parent.Thaw() + self._tabID = -1 + self.SetCursor(wx.STANDARD_CURSOR) + return + + isanimated = 0 + if self._timers[self._tabID].IsRunning(): + isanimated = 1 + timer = self._timers[self._tabID].GetInterval() + + self.StopAnimation(self._tabID) + animatedimages = self.GetAnimationImages(self._tabID) + + pagerange = range(self.GetPageCount()) + + newrange = pagerange[:] + newrange.remove(self._tabID) + newrange.insert(where, self._tabID) + + newpages = [] + counter = self.GetPageCount() - 1 + + for ii in xrange(self.GetPageCount()): + newpages.append(self._parent.GetPage(ii)) + self._parent.bsizer.Detach(counter-ii) + + cc = 0 + + self._parent._notebookpages = [] + + for jj in newrange: + self._parent.bsizer.Add(newpages[jj], 1, wx.EXPAND | wx.ALL, 2) + self._parent.bsizer.Show(cc, False) + self._parent._notebookpages.append(newpages[jj]) + cc = cc + 1 + + self.DeletePage(self._tabID) + + if enabled: + if id == self.GetPageCount(): + self.AddPage(text, True, image) + else: + self.InsertPage(where, text, True, image) + else: + if id == self.GetPageCount(): + self.AddPage(text, False, image) + else: + self.InsertPage(where, text, False, image) + + self.SetPageImage(where, image) + self.SetPageText(where, text) + self.SetPageTextFont(where, font1) + self.SetPageTextSecondaryFont(where, font2) + self.SetPageTextColour(where, fontcolour) + self.SetPageColour(where, pagecolour) + self.EnablePage(where, enabled) + self.SetPageToolTip(where, tooltip, ontime, winsize) + self.SetPagePopupMenu(where, menu) + self.SetPageFirstGradientColour(where, firstcol) + self.SetPageSecondGradientColour(where, secondcol) + self._pages[where]._ishidden = ishidden + + if isanimated and len(animatedimages) > 1: + self.SetAnimationImages(where, animatedimages) + self.StartAnimation(where, timer) + + if enabled: + self._parent.bsizer.Show(where, True) + else: + sel = self.GetSelection() + + if sel == -1: + sel = 0 + self._parent.bsizer.Show(where, False) + self._parent.SetSelection(sel) + self._parent.bsizer.Show(sel, True) + + self._parent.bsizer.Layout() + + self._parent.Thaw() + + self._isdragging = False + self._olddragpos = -1 + self._fromdnd = True + self.Refresh() + self._tabID = -1 + self.SetCursor(wx.STANDARD_CURSOR) + + return + + if nPage >= 0 and where >= 0: + panel = oldNotebook.GetPage(nPage) + + if panel: + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_DND, oldNotebook.GetId()) + eventOut.SetOldPosition(nPage) + eventOut.SetNewPosition(where) + eventOut.SetEventObject(oldNotebook) + + if oldNotebook.GetEventHandler().ProcessEvent(eventOut): + oldNotebook.nb._tabID = -1 + oldNotebook.nb._olddragpos = -1 + oldNotebook.SetCursor(wx.STANDARD_CURSOR) + oldNotebook.Refresh() + return + + oldNotebook.Freeze() + infos = oldNotebook.GetPageInfo(nPage) + + text = infos["text"] + image = infos["image"] + hidden = infos["ishidden"] + + panel.Reparent(newNotebook) + newNotebook.InsertPage(where, panel, text, True, image, hidden) + newNotebook.SetPageInfo(where, infos) + + oldNotebook.nb.DeletePage(nPage) + + oldNotebook.bsizer.Detach(nPage) + oldNotebook.bsizer.Layout() + oldNotebook.sizer.Layout() + + oldNotebook._notebookpages.pop(nPage) + + oldNotebook.AdvanceSelection() + + if oldNotebook.GetPageCount() == 0: + if oldNotebook._style & NC_TOP: + oldNotebook.sizer.Show(0, False) + oldNotebook.sizer.Show(1, False) + else: + oldNotebook.sizer.Show(1, False) + oldNotebook.sizer.Show(2, False) + + oldNotebook.sizer.Layout() + + oldNotebook.Thaw() + newNotebook.Refresh() + + return wx.DragMove + + def OnLeaveWindow(self, event): + """ Handles The wx.EVT_LEAVE_WINDOW Events For TabCtrl. """ + + if self._enabledragging: + if self._isdragging: + + page = self._parent.GetPage(self._tabID) + draginfo = NCDragInfo(page, self._tabID) + drginfo = cPickle.dumps(draginfo) + dataobject = wx.CustomDataObject(wx.CustomDataFormat("NotebookCtrl")) + dataobject.SetData(drginfo) + dragSource = wx.DropSource(self) + dragSource.SetData(dataobject) + dragSource.DoDragDrop(wx.Drag_DefaultMove) + + self._isleaving = True + self.Refresh() + + if self._istooltipshown: + self._tipwindow.Destroy() + self._istooltipshown = False + self.Refresh() + + event.Skip() + + + def OnKeyDown(self, event): + """ + Handles The wx.EVT_KEY_DOWN Event For TabCtrl. This Is Only Processed If + The User Navigate Through Tabs With Ctrl-Tab Keyboard Navigation. + """ + + if event.GetKeyCode == wx.WXK_TAB: + if event.ControlDown(): + sel = self.GetSelection() + if sel == self.GetPageCount() - 1: + sel = 0 + else: + sel = sel + 1 + + while not self.IsPageEnabled(sel): + sel = sel + 1 + if sel == self.GetPageCount() - 1: + sel = 0 + + self._parent.SetSelection(sel) + + event.Skip() + + + def AddPage(self, text, select=False, img=-1, hidden=False): + """ + Add A Page To The Notebook. + + @param text: The Tab Text; + @param select: Whether The Page Should Be Selected Or Not; + @param img: Specifies The Optional Image Index For The New Page. + """ + + self._pages.append(TabbedPage(text, img, hidden)) + self._somethingchanged = True + + self._firsttime = True + self._timers.append(wx.Timer(self)) + + # JS: The following two lines caused this control not to fire + # the EVT_NOTEBOOKCTRL_PAGE_CHANGING/CHANGED events for the + # first page. The NotebookCtrl sets the selection later anyway, + # and without these two lines, the events fire. +## if select or self.GetSelection() == -1: +## self._selection = self.GetPageCount() - 1 + + if self._style & NC_LEFT or self._style & NC_RIGHT: + self.SetBestSize((self._CalcBestWidth(wx.ClientDC(self)), -1)) + + self.Refresh() + + + def InsertPage(self, nPage, text, select=False, img=-1, hidden=False): + """ + Insert A Page Into The Notebook. + + @param nPage: Specifies The Position For The New Page; + @param text: The Tab Text; + @param select: Whether The Page Should Be Selected Or Not; + @param img: Specifies The Optional Image Index For The New Page. + """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In InsertPage: (" + str(nPage) + ")" + + oldselection = self.GetSelection() + + self._pages.insert(nPage, TabbedPage(text, img, hidden)) + self._timers.insert(nPage, wx.Timer(self)) + + self._somethingchanged = True + self._firsttime = True + + if select or self.GetSelection() == -1: + self._selection = nPage + self.SetSelection(nPage) + else: + if nPage <= oldselection: + self._selection = self._selection + 1 + + if self._style & NC_LEFT or self._style & NC_RIGHT: + self.SetBestSize((self._CalcBestWidth(wx.ClientDC(self)), -1)) + + self.Refresh() + + + def DeleteAllPages(self): + """ Deletes All NotebookCtrl Pages. """ + + for tims in self._timers: + if tims.IsRunning(): + tims.Stop() + + tims.Destroy() + + self._timers = [] + self._pages = [] + self._selection = -1 + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + + def DeletePage(self, nPage, oncontinue=True): + """ Deletes The Page nPage, And The Associated Window. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In DeletePage: (" + str(nPage) + ")" + + oldselection = self.GetSelection() + + self._pages.pop(nPage) + + if self._timers[nPage].IsRunning(): + self._timers[nPage].Stop() + + self._timers[nPage].Destroy() + + if self._istooltipshown: + self._tipwindow.Destroy() + self._istooltipshown = False + + if not oncontinue: + self._somethingchanged = True + self._firsttime = True + self.Refresh() + return + + if nPage < self._selection: + self._selection = self._selection - 1 + elif self._selection == nPage and self._selection == self.GetPageCount(): + self._selection = self._selection - 1 + else: + self._selection = oldselection + + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + + def SetSelection(self, nPage): + """ + Sets The Current Tab Selection To The Given nPage. This Call Generates The + EVT_NOTEBOOKCTRL_PAGE_CHANGING Event. + """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetSelection: (" + str(nPage) + ")" + + oldselection = self._selection + + if nPage != self._selection: + + if not self.IsPageEnabled(nPage): + return + + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_CHANGING, self.GetId()) + eventOut.SetSelection(nPage) + eventOut.SetOldSelection(self._selection) + eventOut.SetEventObject(self) + + if not self.GetEventHandler().ProcessEvent(eventOut): + + # Prevent full paint unless never fully painted + if hasattr(self, "_initrect"): + self._firsttime = False + # Program Allows The Page Change + self._selection = nPage + eventOut.SetEventType(wxEVT_NOTEBOOKCTRL_PAGE_CHANGED) + eventOut.SetOldSelection(self._selection) + self.GetEventHandler().ProcessEvent(eventOut) + + if oldselection != -1: + self._parent.bsizer.Show(oldselection, False) + + self.EnsureVisible(self._selection) + self._parent.bsizer.Show(self._selection, True) + self._parent.bsizer.Layout() + + self.Refresh() + + self._shown = nPage + + + def EnsureVisible(self, selection): + + if self.GetPageCount() < 2: + return + + if not self.HasSpinButton(): + return + + fullrect = self.GetClientSize() + count = self._tabvisible[0:selection].count(0) + currect = self._tabrect[selection-self._firstvisible-count] + + spinval = self._spinbutton.GetValue() + firstrect = self._initrect[spinval] + if self._style & NC_LEFT or self._style & NC_RIGHT: + pos = currect.y + size = currect.height + posIndex = 1 + else: + pos = currect.x + size = currect.width + posIndex = 0 + torefresh = 0 + + while pos + size > fullrect[posIndex] - self._spinbutton.GetSize()[posIndex]: + + if self._style & NC_LEFT or self._style & NC_RIGHT: + pos -= firstrect.height + else: + pos -= firstrect.width + + if not self._enablehiding: + spinval = spinval + 1 + else: + oldspinval = spinval + spinval = spinval + self._tabvisible[0:selection].count(0) + if spinval == oldspinval: + spinval = spinval + 1 + + if spinval >= len(self._initrect): + spinval = spinval - 1 + + firstrect = self._initrect[spinval] + + if self._style & NC_LEFT or self._style & NC_RIGHT: + self._spinbutton.OnSpin(-spinval) + self._spinbutton.SetValue(-spinval) + else: + self._spinbutton.OnSpin(spinval) + self._spinbutton.SetValue(spinval) + + torefresh = 1 + + if torefresh: + self.Refresh() + + + def GetPageCount(self): + """ Returns The Number Of Pages In NotebookCtrl. """ + + return len(self._pages) + + + def GetSelection(self): + """ Returns The Current Selection. """ + + return self._selection + + + def GetImageList(self): + """ Returns The Image List Associated With The NotebookCtrl. """ + + return self._imglist + + + def SetImageList(self, imagelist): + """ Associate An Image List To NotebookCtrl. """ + + self._imglist = imagelist + self._grayedlist = wx.ImageList(16, 16, True, 0) + + for ii in xrange(imagelist.GetImageCount()): + + bmp = imagelist.GetBitmap(ii) + image = wx.ImageFromBitmap(bmp) + image = GrayOut(image) + newbmp = wx.BitmapFromImage(image) + self._grayedlist.Add(newbmp) + + + def AssignImageList(self, imagelist): + """ Associate An Image List To NotebookCtrl. """ + + self._imglist = imagelist + self._grayedlist = wx.ImageList(16, 16, True, 0) + + for ii in xrange(imagelist.GetImageCount()): + + bmp = imagelist.GetBitmap(ii) + image = wx.ImageFromBitmap(bmp) + image = GrayOut(image) + newbmp = wx.BitmapFromImage(image) + self._grayedlist.Add(newbmp) + + + def GetPadding(self): + """ Returns The (Horizontal, Vertical) Padding Of The Text Inside Tabs. """ + + return self._padding + + + def SetPadding(self, padding): + """ Sets The (Horizontal, Vertical) Padding Of The Text Inside Tabs. """ + + self._padding = padding + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + + def GetPageText(self, nPage): + """ Returns The String For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageText: (" + str(nPage) + ")" + + return self._pages[nPage]._text + + + def SetPageText(self, nPage, text): + """ Sets The String For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageText: (" + str(nPage) + ")" + + if self._pages[nPage]._text != text: + self._pages[nPage]._text = text + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + + def GetPageImage(self, nPage): + """ Returns The Image Index For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageImage: (" + str(nPage) + ")" + + return self._pages[nPage]._image + + + def SetPageImage(self, nPage, img): + """ Sets The Image Index For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageImage: (" + str(nPage) + ")" + + if self._pages[nPage]._image != img: + self._pages[nPage]._image = img + self._somethingchanged = True + self._firsttime = True + + if self._style & NC_LEFT or self._style & NC_RIGHT: + self.SetBestSize((self._CalcBestWidth(wx.ClientDC(self)), -1)) + self._parent.GetSizer().Layout() + + self.Refresh() + + + def SetPageTextFont(self, nPage, font=None): + """ Sets The Primary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageTextFont: (" + str(nPage) + ")" + + if font is None: + font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + normalfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + self._pages[nPage]._font = font + + if font == normalfont: + self._parent.GetSizer().Layout() + self._somethingchanged = True + self._firsttime = True + self.Refresh() + return + + dc = wx.ClientDC(self) + dc.SetFont(font) + w1, h1 = dc.GetTextExtent("Aq") + dc.SetFont(normalfont) + wn, hn = dc.GetTextExtent("Aq") + w2, h2 = (0, 0) + + if hasattr(self._pages[nPage], "_secondaryfont"): + dc.SetFont(self._pages[nPage]._secondaryfont) + w2, h2 = dc.GetTextExtent("Aq") + + h = max(h1, h2) + + if h < hn: + self._somethingchanged = True + self._firsttime = True + self.Refresh() + return + + if h + 2*self._padding.y < 24: + newheight = 24 + else: + newheight = h + 2*self._padding.y + + oldsize = self.GetSize() + + if newheight < oldsize[1]: + newheight = oldsize[1] + + if self._style & NC_TOP or self._style & NC_BOTTOM: + self.SetBestSize((-1, newheight)) + else: + self.SetBestSize((self._CalcBestWidth(dc), -1)) + self._parent.GetSizer().Layout() + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + def SetTabHeight(self, height=28): + """ Sets The Tabs Height. """ + + if self._style & NC_TOP or self._style & NC_BOTTOM: + self.SetBestSize((-1, height)) + self._bestsize = height + + + def SetControlBackgroundColour(self, colour=None): + """ Sets The TabCtrl Background Colour (Behind The Tabs). """ + + if colour is None: + colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) + + self.SetBackgroundColour(colour) + self.Refresh() + + + def GetPageTextFont(self, nPage): + """ Returns The Primary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageTextFont: (" + str(nPage) + ")" + + return self._pages[nPage]._font + + + def SetPageTextSecondaryFont(self, nPage, font=None): + """ Sets The Secondary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageTextSecondaryFont: (" + str(nPage) + ")" + + if font is None: + font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + normalfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + self._pages[nPage]._secondaryfont = font + + if font == normalfont: + self._somethingchanged = True + self._firsttime = True + self.Refresh() + return + + dc = wx.ClientDC(self) + dc.SetFont(font) + w1, h1 = dc.GetTextExtent("Aq") + dc.SetFont(normalfont) + wn, hn = dc.GetTextExtent("Aq") + w2, h2 = (0, 0) + + if hasattr(self._pages[nPage], "_font"): + dc.SetFont(self._pages[nPage]._font) + w2, h2 = dc.GetTextExtent("Aq") + + h = max(h1, h2) + + if h < hn: + self._somethingchanged = True + self._firsttime = True + self.Refresh() + return + + if h + 2*self._padding.y < 24: + newheight = 24 + else: + newheight = h + 2*self._padding.y + + oldsize = self.GetSize() + + if newheight < oldsize[-1]: + newheight = oldsize[-1] + + if self._style & NC_TOP or self._style & NC_BOTTOM: + self.SetBestSize((-1, newheight)) + else: + self.SetBestSize((self._CalcBestWidth(dc), -1)) + self._parent.GetSizer().Layout() + + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + + def GetPageTextSecondaryFont(self, nPage): + """ Returns The Secondary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageTextSecondaryFont: (" + str(nPage) + ")" + + return self._pages[nPage]._secondaryfont + + + def SetPageTextColour(self, nPage, colour=None): + """ Sets The Text Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageTextColour: (" + str(nPage) + ")" + + if colour is None: + colour = wx.BLACK + + self._pages[nPage]._pagetextcolour = colour + self._somethingchanged = True + self.Refresh() + + + def GetPageTextColour(self, nPage): + """ Returns The Text Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageTextColour: (" + str(nPage) + ")" + + return self._pages[nPage]._pagetextcolour + + + def SetPageColour(self, nPage, colour=None): + """ Sets The Tab Background Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageColour: (" + str(nPage) + ")" + + if colour is None: + colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + + self._pages[nPage]._pagecolour = colour + self._somethingchanged = True + self.Refresh() + + + def GetPageColour(self, nPage): + """ Returns The Tab Background Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageColour: (" + str(nPage) + ")" + + if not self._tabstyle._normal or self._usegradients: + if self._usegradients: + return self._tabstyle._firstcolour + else: + return self._GetThemePageColour(nPage) + else: + return self._pages[nPage]._pagecolour + + + def EnablePage(self, nPage, enable=True): + """ Enable/Disable The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In EnablePage: (" + str(nPage) + ")" + + self._pages[nPage]._enable = enable + + if not enable and self.GetSelection() == nPage: + defpage = self.GetDefaultPage() + if defpage < 0: + self.AdvanceSelection() + else: + if defpage >= self.GetPageCount(): + self.AdvanceSelection() + else: + if defpage == nPage: + self.AdvanceSelection() + else: + self.SetSelection(defpage) + + self.Refresh() + + + def IsPageEnabled(self, nPage): + """ Returns Whether A Page Is Enabled Or Not. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In IsPageEnabled: (" + str(nPage) + ")" + + return self._pages[nPage]._enable + + + def SetHighlightSelection(self, highlight=True): + """ Globally Enables/Disables Tab Highlighting On Tab Selection. """ + + self._highlight = highlight + self.Refresh() + + + def GetHighlightSelection(self): + """ Returns Globally Enable/Disable State For Tab Highlighting On Tab Selection. """ + + return self._highlight + + + def SetUseFocusIndicator(self, focus=True): + """ Globally Enables/Disables Tab Focus Indicator. """ + + self._usefocus = focus + self.Refresh() + + + def GetUseFocusIndicator(self): + """ Returns Globally Enable/Disable State For Tab Focus Indicator. """ + + return self._usefocus + + + def SetPageToolTip(self, nPage, tooltip="", timer=500, winsize=400): + """ + Sets A ToolTip For The Given Page nPage. + + @param nPage: The Given Page; + @param tooltip: The ToolTip String; + @param timer: The Timer After Which The Tip Window Is Popped Up; + @param winsize: The Maximum Width Of The Tip Window. + """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageToolTip: (" + str(nPage) + ")" + + self._pages[nPage]._tooltip = tooltip + self._pages[nPage]._tooltiptime = timer + self._pages[nPage]._winsize = winsize + + + def GetPageToolTip(self, nPage): + """ Returns A Tuple With All Page ToolTip Parameters. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageToolTip: (" + str(nPage) + ")" + + return self._pages[nPage]._tooltip, self._pages[nPage]._tooltiptime, \ + self._pages[nPage]._winsize + + + def EnableToolTip(self, show=True): + """ Globally Enables/Disables Tab ToolTips. """ + + self._showtooltip = show + + if show: + try: + wx.PopupWindow(self) + self.TransientTipWindow = TransientTipWindow + + except NotImplementedError: + + self.TransientTipWindow = macTransientTipWindow + + else: + if self._istooltipshown: + self._tipwindow.Destroy() + self._istooltipshown = False + self.Refresh() + + if self._tiptimer.IsRunning(): + self._tiptimer.Stop() + + + def GetToolTipBackgroundColour(self): + """ Returns The ToolTip Window Background Colour. """ + + return self._backtooltip + + + def SetToolTipBackgroundColour(self, colour=None): + """ Sets The ToolTip Window Background Colour. """ + + if colour is None: + colour = wx.Colour(255, 255, 230) + + self._backtooltip = colour + + + def EnableTabGradients(self, enable=True): + """ Globally Enables/Disables Drawing Of Gradient Coloured Tabs For Each Tab. """ + + self._usegradients = enable + + if enable: + self._tabstyle.ResetDefaults() + self._selstyle.ResetDefaults() + + self.Refresh() + + + def SetPageFirstGradientColour(self, nPage, colour=None): + """ Sets The Single Tab First Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageFirstGradientColour: (" + str(nPage) + ")" + + if colour is None: + colour = wx.WHITE + + self._pages[nPage]._firstcolour = colour + self.Refresh() + + + def SetPageSecondGradientColour(self, nPage, colour=None): + """ Sets The Single Tab Second Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageSecondGradientColour: (" + str(nPage) + ")" + + if colour is None: + color = self._pages[nPage]._firstcolour + r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) + color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) + colour = wx.Colour(color[0], color[1], color[2]) + + self._pages[nPage]._secondcolour = colour + self.Refresh() + + + def GetPageFirstGradientColour(self, nPage): + """ Returns The Single Tab First Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageFirstGradientColour: (" + str(nPage) + ")" + + return self._pages[nPage]._firstcolour + + + def GetPageSecondGradientColour(self, nPage): + """ Returns The Single Tab Second Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageSecondGradientColour: (" + str(nPage) + ")" + + return self._pages[nPage]._secondcolour + + + def CancelTip(self): + """ Destroys The Tip Window (Probably You Won't Need This One). """ + + if self._istooltipshown: + self._istooltipshown = False + self._tipwindow.Destroy() + self.Refresh() + + + def AdvanceSelection(self, forward=True): + """ + Cycles Through The Tabs. The Call To This Function Generates The + EVT_NOTEBOOKCTRL_PAGE_CHANGING Event. + """ + + if self.GetPageCount() <= 1: + return + + sel = self.GetSelection() + count = 0 + + if forward: + if sel == self.GetPageCount() - 1: + sel = 0 + else: + sel = sel + 1 + + while not self.IsPageEnabled(sel) or \ + (self._enablehiding and self._pages[sel]._ishidden): + + count = count + 1 + sel = sel + 1 + + if self._enablehiding and self._pages[sel]._ishidden: + count = count + 1 + sel = sel + 1 + + if sel == self.GetPageCount() - 1: + sel = 0 + + if count > self.GetPageCount() + 1: + return None + + else: + if sel == 0: + sel = self.GetPageCount() - 1 + else: + sel = sel - 1 + + while not self.IsPageEnabled(sel): + count = count + 1 + sel = sel - 1 + if sel == 0: + sel = self.GetPageCount() - 1 + + if count > self.GetPageCount() + 1: + return None + + self._parent.SetSelection(sel) + + + def SetDefaultPage(self, defaultpage=-1): + """ + Sets The Default Page That Will Be Selected When An Active And Selected + Tab Is Made Inactive. + """ + + if defaultpage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetDefaultPage: (" + str(defaultpage) + ")" + + self._defaultpage = defaultpage + + + def GetDefaultPage(self): + """ Returns The Default Page. """ + + return self._defaultpage + + + def UpdateSpinButton(self): + """ Update The NotebookSpinButton. Used Internally. """ + + count = self.GetPageCount() + + if count == 0: + return + + nbsize = [] + nbsize.append(self._initrect[-1][0] + self._initrect[-1][2]) + nbsize.append(self._initrect[-1][1] + self._initrect[-1][3]) + clsize = self.GetClientSize() + + if self._style & NC_TOP or self._style & NC_BOTTOM: + spinstyle = wx.SP_HORIZONTAL + showspin = nbsize[0] > clsize[0] + else: + spinstyle = wx.SP_VERTICAL + showspin = nbsize[1] > clsize[1] + + if showspin: + if not hasattr(self, "_spinbutton"): + self._spinbutton = NotebookSpinButton(self, pos=(10000,10000), + style=spinstyle) + self._spinbutton.SetValue(0) + self._originalspinsize = self._spinbutton.GetSize() + + sbsize = self._spinbutton.GetSize() + if self._style & NC_LEFT or self._style & NC_RIGHT: + ypos = clsize[1] - sbsize[1] + if self._style & NC_LEFT: + xpos = nbsize[0] - (2 + sbsize[0]) + else: + xpos = 2 + else: + xpos = clsize[0] - sbsize[0] + if self._style & NC_BOTTOM: + ypos = 2 + else: + ypos = clsize[1] - sbsize[1] + + if self.HasMenuButton(): + self._spinbutton.SetSize((-1, 16)) + else: + self._spinbutton.SetSize(self._originalspinsize) + + self._spinbutton.Move((xpos, ypos)) + self._spinbutton.Show() + if self._style & NC_LEFT or self._style & NC_RIGHT: + self._spinbutton.SetRange(-(count-1), 0) + else: + self._spinbutton.SetRange(0, count-1) + + else: + + if hasattr(self, "_spinbutton") and self._spinbutton.IsShown(): + self._spinbutton.Hide() + self._spinbutton.SetValue(0) + + + def HasSpinButton(self): + """ Returns Wheter The NotebookSpinButton Exists And Is Shown. """ + + return hasattr(self, "_spinbutton") and self._spinbutton.IsShown() + + + def IsLastVisible(self): + """ Returns Whether The Last Tab Is Visible Or Not. """ + + if self.HasSpinButton(): + if self._style & NC_LEFT or self._style & NC_RIGHT: + pos, size = (1, 3) + else: + pos, size = (0, 2) + lastpos = self._tabrect[-1][pos] + self._tabrect[-1][size] + if lastpos < self._spinbutton.GetPosition()[pos]: + return True + + return False + + + def UpdateMenuButton(self, show): + """ Updates The Notebook Menu Button To Show/Hide Tabs. Used Internally. """ + + count = self.GetPageCount() + + if count == 0: + return + + if not hasattr(self, "_initrect"): + return + + if not show and not hasattr(self, "_menubutton"): + return + + if not hasattr(self, "_menubutton"): + self._menubutton = NotebookMenuButton(self, pos=(10000,10000)) + + sbsize = self._menubutton.GetSize() + nbsize = [] + nbsize.append(self._initrect[-1][0] + self._initrect[-1][2]) + nbsize.append(self._initrect[-1][1] + self._initrect[-1][3]) + clsize = self.GetClientSize() + + xpos = clsize[0] - sbsize[0] + ypos = clsize[1] - sbsize[1] + + if self.HasSpinButton(): + self._menubutton.Move((xpos-1, ypos-16)) + else: + self._menubutton.Move((xpos-1, ypos-1)) + + self._menubutton.Show(show) + + + def HasMenuButton(self): + """ Returns Wheter The NotebookMenuButton Exists And Is Shown. """ + + return hasattr(self, "_menubutton") and self._menubutton.IsShown() + + + def HideTab(self, nPage, hide=True): + """ Hides A Tab In The NotebookCtrl. """ + + if hide: + self._pages[nPage]._ishidden = True + else: + self._pages[nPage]._ishidden = False + + if nPage == self.GetSelection(): + self.AdvanceSelection() + + self._firsttime = True + self.Refresh() + + + def HitTest(self, point, flags=0): + """ + Standard NotebookCtrl HitTest() Method. If Called With 2 Outputs, It + Returns The Page Clicked (If Any) And One Of These Flags: + + NC_HITTEST_NOWHERE = 0 ==> Hit Not On Tab + NC_HITTEST_ONICON = 1 ==> Hit On Icon + NC_HITTEST_ONLABEL = 2 ==> Hit On Label + NC_HITTEST_ONITEM = 4 ==> Hit Generic, On Item + NC_HITTEST_ONX = 8 ==> Hit On Closing "X" On Every Page + """ + + mirror = self._style & NC_BOTTOM + size = self.GetSize() + dc = wx.ClientDC(self) + + height = self._tabrect[0].height + + if flags: + flags = wx.NB_HITTEST_NOWHERE + + if point.x <= 0 or point.x >= size.x: + if flags: + return wx.NOT_FOUND, flags + else: + return wx.NOT_FOUND + + if not point.y >= self._tabrect[0].y and point.y < self._tabrect[0].y + height: + if flags: + return wx.NOT_FOUND, flags + else: + return wx.NOT_FOUND + + posx = self._firsttabpos.x + posy = self._firsttabpos.y + maxwidth = max(self._maxtabwidths) + + for ii in xrange(self._firstvisible, self.GetPageCount()): + + if not self._enablehiding or not self._pages[ii]._ishidden: + + width = self._CalcTabTextWidth(dc, ii) + + bmpWidth, bmpHeight = self._CalcTabBitmapSize(ii) + + tabrect = self._CalcTabRect(ii, posx, posy, width, bmpWidth, bmpHeight) + + if tabrect.Contains(point): + + if flags: + flags = NC_HITTEST_ONITEM + + #onx attempt + if self.GetDrawX()[0]: + count = self._tabvisible[0:ii].count(0) + if flags and self._xrect[ii-self._firstvisible-count].Contains(point): + flags = NC_HITTEST_ONX + + #onicon attempt + if flags and bmpWidth > 0 and \ + wx.RectPS(wx.Point(*self._CalcTabBitmapPosition(ii, + bmpWidth, bmpHeight, tabrect)), + wx.Size(bmpWidth, bmpHeight)).Contains(point): + flags = NC_HITTEST_ONICON + + #onlabel attempt + elif flags and wx.RectPS(wx.Point(*self._CalcTabTextPosition(ii, + tabrect, self._CalcTabBitmapSpace(bmpWidth, bmpHeight))), + wx.Size(width, height)).Contains(point): + flags = NC_HITTEST_ONLABEL + + if flags: + return ii, flags + else: + return ii + + if self._style & NC_TOP or self._style & NC_BOTTOM: + posx += tabrect.width + else: + posy += tabrect.height + + if flags: + return wx.NOT_FOUND, flags + else: + return wx.NOT_FOUND + + + def EnableDragAndDrop(self, enable=True): + """ Globall Enables/Disables Tabs Drag And Drop. """ + + self._enabledragging = enable + + + def EnableHiding(self, enable=True): + """ Globally Enables/Disables Hiding On Tabs In Runtime. """ + + self._enablehiding = enable + self.UpdateMenuButton(enable) + + wx.FutureCall(1000, self.UpdateMenuButton, enable) + + + def SetAnimationImages(self, nPage, imgarray): + """ + Sets An Animation List Associated To The Given Page nPage. + + @param nPage: The Given Page + @param imgarray: A List Of Image Indexes Of Images Inside The + ImageList Associated To NotebookCtrl. + """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetAnimationImages: (" + str(nPage) + ")" + + if not imgarray: + raise "\nERROR: Invalid Image Array In SetAnimationImages: (" + repr(imgarray) + ")" + + if min(imgarray) < 0: + raise "\nERROR: Invalid Image Array In SetAnimationImages: (Min(ImgArray) = " + \ + str(min(imgarray)) + " < 0)" + + if max(imgarray) > self.GetImageList().GetImageCount() - 1: + raise "\nERROR: Invalid Image Array In SetAnimationImages: (Max(ImgArray) = " + \ + str(max(imgarray)) + " > " + str(self.GetImageList().GetImageCount()-1) + ")" + + self._pages[nPage]._animationimages = imgarray + + + def GetAnimationImages(self, nPage): + """ Returns The Animation Images List Associated To The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetAnimationImages: (" + str(nPage) + ")" + + return self._pages[nPage]._animationimages + + + def AnimateTab(self, event): + """ Called When The Refreshing Animation Timer Expires. Used Internally""" + + obj = event.GetEventObject() + nPage = self._timers.index(obj) + + if not self.IsPageEnabled(nPage): + return + + indx = self.GetPageImage(nPage) + images = self.GetAnimationImages(nPage) + myindx = images.index(indx) + + if indx == images[-1]: + myindx = -1 + + myindx = myindx + 1 + + self.SetPageImage(nPage, images[myindx]) + + + def StartAnimation(self, nPage, timer=500): + """ Starts The Animation On The Given Page, With Refreshing Time Rate "timer". """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In StartAnimation: (" + str(nPage) + ")" + + images = self.GetAnimationImages(nPage) + + if not images: + raise "\nERROR: No Images Array Defined For Page: (" + str(nPage) + ")" + + if len(images) == 1: + raise "\nERROR: Impossible To Animate Tab: " + str(nPage) + " With Only One Image" + + self._timers[nPage].Start(timer) + + + def StopAnimation(self, nPage): + """ Stops The Animation On The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In StopAnimation: (" + str(nPage) + ")" + + if self._timers[nPage].IsRunning(): + self._timers[nPage].Stop() + + + def SetDrawX(self, drawx=True, style=1, image1=None, image2=None): + """ + Globally Enables/Disables The Drawing Of A Closing "X" In The Tab. + + @param drawx: C{True} to enable drawing a closing "X"; C{False} to + disable it + @param style: the style of the X to draw when C{drawx} is C{True}; + possible values are: + - C{1}: Small "X" At The Top-Right Of The Tab; + - C{2}: Bigger "X" In The Middle Vertical Of The Tab (Like Opera Notebook); + - C{3}: Custom "X" Image Is Drawn On Tabs. + @param image1: if C{style} is C{3}, the image to use when drawing + the X on an unhighlighted tab + @param image2: if C{style} is C{3}, the image to use when drawing + the X on a highlighted tab + """ + + self._drawx = drawx + self._drawxstyle = style + + if style == 3: + self._imglist2 = wx.ImageList(16, 16, True, 0) + self._imglist2.Add(image1) + self._imglist2.Add(image2) + + if self._style & NC_LEFT or self._style & NC_RIGHT: + self.SetBestSize((self._CalcBestWidth(wx.ClientDC(self)), -1)) + self._parent.GetSizer().Layout() + + self.Refresh() + + + def GetDrawX(self): + """ + Returns The Enable/Disable State Of Drawing Of A Small "X" At The Top-Right Of + Every Page. + """ + + return self._drawx, self._drawxstyle + + + def GetInsideTab(self, pt): + """ Returns The Tab On Which The Mouse Is Hovering On. """ + + count = 0 + + for tabs in self._tabrect: + if tabs.Contains(pt): + return count + + count = count + 1 + + return -1 + + + def GetInsideX(self, pt): + """ Returns The Tab On Which The Mouse Is Hovering On The "X" Button. """ + + count = 0 + + for rects in self._xrect: + if rects.Contains(pt): + return count + + count = count + 1 + + return -1 + + + def SetImageToCloseButton(self, convert=True): + """ Set Whether The Tab Icon Should Be Converted To The Close Button Or Not. """ + + self._convertimage = convert + + + def GetImageToCloseButton(self): + """ Get Whether The Tab Icon Should Be Converted To The Close Button Or Not. """ + + return self._convertimage + + + def ConvertImageToCloseButton(self, page): + """ Globally Converts The Page Image To The "Opera" Style Close Button. """ + + bmpindex = self.GetPageImage(page) + if bmpindex < 0: + return + + tabrect = self._tabrect[page] + size = self.GetSize() + + maxfont = self._maxfont + + dc = wx.ClientDC(self) + + dc.SetFont(maxfont) + pom, height = dc.GetTextExtent("Aq") + + bmp = self._imglist.GetBitmap(bmpindex) + + bmpposx = tabrect.x + self._padding.x + bmpposy = size.y - (height + 2*self._padding.y + bmp.GetHeight())/2 - 1 + + ypos = size.y - height - self._padding.y*2 + ysize = height + self._padding.y*2 + 3 + + if page == self.GetSelection(): + bmpposx = bmpposx + 1 + bmpposy = bmpposy - 1 + ypos = ypos - 3 + ysize = ysize + 2 + + colour = self.GetPageColour(page) + bmprect = wx.Rect(bmpposx, bmpposy, bmp.GetWidth()+self._padding.x, bmp.GetHeight()) + + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.TRANSPARENT_PEN) + dc.DrawRectangleRect(bmprect) + + colour = self.GetPageTextColour(page) + + r = colour.Red() + g = colour.Green() + b = colour.Blue() + + hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64) + + colour = wx.Colour(hr, hg, hb) + back_colour = wx.WHITE + + yypos = ypos+(ysize-height-self._padding.y/2)/2 + + xrect = wx.Rect(bmprect.x+(bmprect.width - self._padding.x - height)/2, + yypos, height, height) + + # Opera Style + dc.SetPen(wx.Pen(colour, 1)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawRoundedRectangleRect(xrect, 2) + dc.SetPen(wx.Pen(back_colour, 2)) + dc.DrawLine(xrect[0]+2, xrect[1]+2, xrect[0]+xrect[2]-3, xrect[1]+xrect[3]-3) + dc.DrawLine(xrect[0]+2, xrect[1]+xrect[3]-3, xrect[0]+xrect[2]-3, xrect[1]+2) + + + def RedrawClosingX(self, pt, insidex, drawx, highlight=False): + """ Redraw The Closing "X" Accordingly To The Mouse "Hovering" Position. """ + + colour = self.GetPageTextColour(insidex) + back_colour = self.GetBackgroundColour() + imagelist = 0 + + if highlight: + r = colour.Red() + g = colour.Green() + b = colour.Blue() + + hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64) + + colour = wx.Colour(hr, hg, hb) + back_colour = wx.WHITE + imagelist = 1 + + dc = wx.ClientDC(self) + xrect = self._xrect[insidex] + + if drawx == 1: + # Emule Style + dc.SetPen(wx.Pen(colour, 1)) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRectangleRect(xrect) + elif drawx == 2: + # Opera Style + dc.SetPen(wx.Pen(colour, 1)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawRoundedRectangleRect(xrect, 2) + dc.SetPen(wx.Pen(back_colour, 2)) + dc.DrawLine(xrect[0]+2, xrect[1]+2, xrect[0]+xrect[2]-3, xrect[1]+xrect[3]-3) + dc.DrawLine(xrect[0]+2, xrect[1]+xrect[3]-3, xrect[0]+xrect[2]-3, xrect[1]+2) + else: + self._imglist2.Draw(imagelist, dc, xrect[0], xrect[1], + wx.IMAGELIST_DRAW_TRANSPARENT, True) + + + def HideOnSingleTab(self, hide=True): + """ Hides The TabCtrl When There Is Only One Tab In NotebookCtrl. """ + + self._hideonsingletab = hide + + + def SetPagePopupMenu(self, nPage, menu): + """ Sets A Popup Menu Specific To A Single Tab. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPagePopupMenu: (" + str(nPage) + ")" + + self._pages[nPage]._menu = menu + + + def GetPagePopupMenu(self, nPage): + """ Returns The Popup Menu Associated To A Single Tab. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPagePopupMenu: (" + str(nPage) + ")" + + return self._pages[nPage]._menu + + + def DrawInsertionMark(self, dc, nPage): + """ + Draw An Insertion Arrow To Let The User Understand Where A Dragged Tab Will + Be Dropped (Between Which Tabs). + """ + + if not self._enablehiding: + if nPage < 0 or nPage >= len(self._tabrect): + return + else: + if nPage < 0 or nPage >= len(self._tabrect) + self._tabvisible.count(0): + return + + colour = wx.BLACK + somehidden = False + + if self._enablehiding: + for ii in xrange(nPage): + if self._pages[ii]._ishidden: + nPage = nPage - 1 + somehidden = True + + rect = self._tabrect[nPage] + + x1 = rect.x - 4 + y1 = rect.y - 1 + x2 = rect.x + y2 = y1 + 5 + x3 = rect.x + 3 + y3 = y1 + + mybrush = wx.Brush(self.GetPageTextColour(nPage)) + + if not self._enablehiding: + if nPage > self._tabID: + x1 = x1 + rect.width + x2 = x2 + rect.width + x3 = x3 + rect.width + else: + mybrush = wx.Brush(self.GetPageTextColour(nPage)) + if nPage >= self._tabID: + x1 = x1 + rect.width + x2 = x2 + rect.width + x3 = x3 + rect.width + + dc.SetPen(wx.Pen(wx.BLACK, 1)) + dc.SetBrush(mybrush) + dc.DrawPolygon([(x1, y1), (x2, y2), (x3, y3)]) + + + def OnMouseMotion(self, event): + """ Handles The wx.EVT_MOTION Event For TabCtrl. """ + + pt = event.GetPosition() + + if self._enabledragging: + + if event.Dragging() and not event.RightIsDown() and not event.MiddleIsDown(): + + tolerance = 2 + + dx = abs(pt.x - self._dragstartpos.x) + dy = abs(pt.y - self._dragstartpos.y) + + if dx <= tolerance and dy <= tolerance: + self.SetCursor(wx.STANDARD_CURSOR) + return + + self.SetCursor(self._dragcursor) + self._isdragging = True + self._isleaving = False + newpos = self.HitTest(pt) + + if newpos >= 0 and newpos != self._olddragpos: + self._olddragpos = newpos + self.Refresh() + + else: + + self._isdragging = False + self.SetCursor(wx.STANDARD_CURSOR) + + if not event.Dragging(): + drawx = self.GetDrawX() + + if drawx[0]: + insidex = self.GetInsideX(pt) + if insidex >= 0: + if self.IsPageEnabled(insidex): + self.RedrawClosingX(pt, insidex, drawx[1], True) + self._xrefreshed = False + else: + if not self._xrefreshed: + insidetab = self.GetInsideTab(pt) + if insidetab >= 0: + if self.IsPageEnabled(insidetab): + self.RedrawClosingX(pt, insidetab, drawx[1]) + self._xrefreshed = True + else: + if self.GetImageToCloseButton(): + page, flags = self.HitTest(pt, 1) + if page >= 0: + if self.IsPageEnabled(page): + if flags == NC_HITTEST_ONICON: + if not self._imageconverted: + self.ConvertImageToCloseButton(page) + self._imageconverted = True + else: + if self._imageconverted: + self.Refresh() + self._imageconverted = False + + if self._showtooltip: + if not event.Dragging(): + if not event.LeftDown(): + + oldinside = self._insidetab + self._insidetab = self.GetInsideTab(pt) + + if self._insidetab >= 0: + if oldinside != self._insidetab: + + if self._istooltipshown: + self._tipwindow.Destroy() + self._istooltipshown = False + self.Refresh() + + if self._tiptimer.IsRunning(): + self._tiptimer.Stop() + + tip, ontime, winsize= self.GetPageToolTip(self._insidetab) + + if tip.strip() != "": + self._currenttip = tip + self._currentwinsize = winsize + self._tiptimer.Start(ontime, wx.TIMER_ONE_SHOT) + + else: + if self._istooltipshown: + self._tipwindow.Destroy() + self._istooltipshown = False + self.Refresh() + + self._mousepos = pt + + event.Skip() + + + def OnShowToolTip(self): + """ Called When The Timer For The ToolTip Expires. Used Internally. """ + + pt = self.ScreenToClient(wx.GetMousePosition()) + + oldinside = self._insidetab + self._insidetab = self.GetInsideTab(pt) + + if self._insidetab != oldinside or self._insidetab < 0: + return + + self._istooltipshown = True + self._tipwindow = self.TransientTipWindow(self, self._currenttip, + self._currentwinsize) + + xsize, ysize = self._tipwindow.GetSize() + xpos, ypos = self.ClientToScreen(self._mousepos) + + if xpos + xsize > self._xvideo - 10: + if ypos + ysize > self._yvideo - 10: # SW Tip Positioning + posx = xpos - xsize + posy = ypos - ysize + else: # NE Tip Positioning + posx = xpos - xsize + posy = ypos + else: + if ypos + ysize > self._yvideo - 10: # SE Tip Positioning + posx = xpos + 10 + posy = ypos - ysize + else: # NW Tip Positioning + posx = xpos + 10 + posy = ypos + + if posy < 0: + posy = ypos + + if posx < 0: + posx = xpos + + self._tipwindow.SetPosition((posx, posy)) + self._tipwindow.Show() + + + def OnMouseLeftDown(self, event): + """ Handles The wx.EVT_LEFT_DOWN Event For TabCtrl. """ + + pos = event.GetPosition() + page, flags = self.HitTest(pos, 1) + self._dragstartpos = pos + + if page != wx.NOT_FOUND: + + if self.IsPageEnabled(page): + + if event.m_controlDown: + if page in self._selectedtabs: + self._selectedtabs.remove(page) + else: + self._selectedtabs.append(page) + self.Refresh() + else: + self._selectedtabs = [] + if flags == NC_HITTEST_ONX or (flags == NC_HITTEST_ONICON and self.GetImageToCloseButton()): + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_CLOSING, self.GetId()) + eventOut.SetOldSelection(self._selection) + eventOut.SetSelection(page) + eventOut.SetEventObject(self) + + if not self.GetEventHandler().ProcessEvent(eventOut): + self._parent.DeletePage(page) + self._parent.bsizer.Layout() + + else: + self.SetSelection(page) + self._tabID = page + + event.Skip() + + + def OnMouseLeftDClick(self, event): + """ Handles The wx.EVT_LEFT_DCLICK Event For TabCtrl. """ + + pos = event.GetPosition() + page = self.HitTest(pos) + self._selectedtabs = [] + + if page == wx.NOT_FOUND: + return + + if not self.IsPageEnabled(page): + return + + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_DCLICK, self.GetId()) + eventOut.SetOldSelection(self._selection) + eventOut.SetSelection(page) + eventOut.SetEventObject(self) + + if not self.GetEventHandler().ProcessEvent(eventOut): + return + + event.Skip() + + + def OnMouseLeftUp(self, event): + """ Handles The wx.EVT_LEFT_UP Event For TabCtrl. """ + + if not self._enabledragging: + event.Skip() + return + + if not self._isdragging: + self.SetCursor(wx.STANDARD_CURSOR) + event.Skip() + return + + id = self.HitTest(wx.Point(event.GetX(), event.GetY())) + + if id >= 0 and id != self._tabID: + + self._isdragging = False + self._olddragpos = -1 + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_DND, self.GetId()) + eventOut.SetOldPosition(self._tabID) + eventOut.SetNewPosition(id) + eventOut.SetEventObject(self) + + if self.GetEventHandler().ProcessEvent(eventOut): + self._tabID = -1 + self._olddragpos = -1 + self.SetCursor(wx.STANDARD_CURSOR) + self.Refresh() + return + + self._parent.Freeze() + + try: + text = self.GetPageText(self._tabID) + image = self.GetPageImage(self._tabID) + font1 = self.GetPageTextFont(self._tabID) + font2 = self.GetPageTextSecondaryFont(self._tabID) + fontcolour = self.GetPageTextColour(self._tabID) + pagecolour = self.GetPageColour(self._tabID) + enabled = self.IsPageEnabled(self._tabID) + tooltip, ontime, winsize = self.GetPageToolTip(self._tabID) + menu = self.GetPagePopupMenu(self._tabID) + firstcol = self.GetPageFirstGradientColour(self._tabID) + secondcol = self.GetPageSecondGradientColour(self._tabID) + ishidden = self._pages[self._tabID]._ishidden + except: + self._parent.Thaw() + self._tabID = -1 + self.SetCursor(wx.STANDARD_CURSOR) + return + + isanimated = 0 + if self._timers[self._tabID].IsRunning(): + isanimated = 1 + timer = self._timers[self._tabID].GetInterval() + + self.StopAnimation(self._tabID) + animatedimages = self.GetAnimationImages(self._tabID) + + pagerange = range(self.GetPageCount()) + + newrange = pagerange[:] + newrange.remove(self._tabID) + newrange.insert(id, self._tabID) + + newpages = [] + counter = self.GetPageCount() - 1 + + for ii in xrange(self.GetPageCount()): + newpages.append(self._parent.GetPage(ii)) + self._parent.bsizer.Detach(counter-ii) + + cc = 0 + + self._parent._notebookpages = [] + + for jj in newrange: + self._parent.bsizer.Add(newpages[jj], 1, wx.EXPAND | wx.ALL, 2) + self._parent.bsizer.Show(cc, False) + self._parent._notebookpages.append(newpages[jj]) + cc = cc + 1 + + self.DeletePage(self._tabID) + + if enabled: + if id == self.GetPageCount(): + self.AddPage(text, True, image) + else: + self.InsertPage(id, text, True, image) + else: + if id == self.GetPageCount(): + self.AddPage(text, False, image) + else: + self.InsertPage(id, text, False, image) + + self.SetPageImage(id, image) + self.SetPageText(id, text) + self.SetPageTextFont(id, font1) + self.SetPageTextSecondaryFont(id, font2) + self.SetPageTextColour(id, fontcolour) + self.SetPageColour(id, pagecolour) + self.EnablePage(id, enabled) + self.SetPageToolTip(id, tooltip, ontime, winsize) + self.SetPagePopupMenu(id, menu) + self.SetPageFirstGradientColour(id, firstcol) + self.SetPageSecondGradientColour(id, secondcol) + self._pages[id]._ishidden = ishidden + + if isanimated and len(animatedimages) > 1: + self.SetAnimationImages(id, animatedimages) + self.StartAnimation(id, timer) + + if enabled: + self._parent.bsizer.Show(id, True) + else: + sel = self.GetSelection() + + if sel == -1: + sel = 0 + self._parent.bsizer.Show(id, False) + self._parent.SetSelection(sel) + self._parent.bsizer.Show(sel, True) + + self._parent.bsizer.Layout() + + self._parent.Thaw() + + self._isdragging = False + self._olddragpos = -1 + self._fromdnd = True + self.Refresh() + self._tabID = -1 + self.SetCursor(wx.STANDARD_CURSOR) + + event.Skip() + + + def OnSize(self, event=None): + """ Handles The wx.EVT_SIZE Event For TabCtrl. """ + + if self._sizeToggleButton: + width = self.GetSize()[0] + height = self._CalcSizeToggleBestSize()[1] + self._sizeToggleButton.SetSize(wx.Size(width, height)) + self.Refresh() + + if event is not None: + event.Skip() + + + def OnMouseRightUp(self, event): + """ Handles The wx.EVT_RIGHT_UP Event For TabCtrl. """ + + pt = event.GetPosition() + id = self.HitTest(pt) + + self._selectedtabs = [] + + if id >= 0: + if self.IsPageEnabled(id): + menu = self.GetPagePopupMenu(id) + if menu: + self.PopupMenu(menu) + + event.Skip() + + + def OnMouseRightDown(self, event): + """ Handles The wx.EVT_RIGHT_DOWN Event For TabCtrl. """ + + pos = event.GetPosition() + page = self.HitTest(pos) + + self._selectedtabs = [] + + if page == wx.NOT_FOUND: + return + + if not self.IsPageEnabled(page): + return + + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_RIGHT, self.GetId()) + eventOut.SetOldSelection(self._selection) + eventOut.SetSelection(page) + eventOut.SetEventObject(self) + + if not self.GetEventHandler().ProcessEvent(eventOut): + return + + event.Skip() + + + def OnMouseMiddleDown(self, event): + """ Handles The wx.EVT_MIDDLE_DOWN Event For TabCtrl. """ + + pos = event.GetPosition() + page = self.HitTest(pos) + + self._selectedtabs = [] + + if page == wx.NOT_FOUND: + return + + if not self.IsPageEnabled(page): + return + + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_MIDDLE, self.GetId()) + eventOut.SetOldSelection(self._selection) + eventOut.SetSelection(page) + eventOut.SetEventObject(self) + + if not self.GetEventHandler().ProcessEvent(eventOut): + return + + event.Skip() + + + def SetSelectionColour(self, colour=None): + """ Sets The Tab Selection Colour (Thin Line Above The Selected Tab). """ + + if colour is None: + colour = wx.Colour(255, 180, 0) + + self._selectioncolour = colour + + + def SetContourLineColour(self, colour=None): + """ Sets The Contour Line Colour (Controur Line Around Tabs). """ + + if colour is None: + if not self._tabstyle._normal or self._usegradients: + colour = wx.Colour(145, 167, 180) + else: + colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + + if not self._tabstyle._normal or self._usegradients: + self._highlightpen = wx.Pen(colour) + self._highlightpen.SetCap(wx.CAP_BUTT) + else: + self._highlightpen2 = wx.Pen(colour) + self._highlightpen2.SetCap(wx.CAP_BUTT) + + self.Refresh() + + + def ApplyTabTheme(self, theme=None): + """ Applies A Particular Theme To Be Drawn On Tabs. """ + + if theme is None: + theme = ThemeStyle() + + self._tabstyle = theme + if self._style & NC_EXPANDABLE: + self._InitExpandableTabStyles(self._style, self._expanded, theme) + self.Refresh() + + def DrawMacTheme(self, dc, tabrect, theme): + """ Draws The Mac Theme On Tabs, If It Is Enabled. """ + + if theme == 1: + col1, col2 = NC_MAC_LIGHT + else: + col1, col2 = NC_MAC_DARK + + colour1 = wx.Colour(col1, col1, col1) + colour2 = wx.Colour(col2, col2, col2) + + x, y, w, h = tabrect + endrange = self._style & NC_ROTATE and w or h + + index = 0 + + for ii in xrange(0, endrange, 2): + if index%2 == 0: + colour = colour1 + else: + colour = colour2 + + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + if self._style & NC_ROTATE: + if ii > 3: + dc.DrawRectangle(x+ii, y, 2, w) + else: + dc.DrawRoundedRectangle(x+ii, y, 3, 3) + else: + if ii > 3: + dc.DrawRectangle(x, y+ii, w, 2) + else: + dc.DrawRoundedRectangle(x, y+ii, w, 3, 3) + + index = index + 1 + + self._lastcolour = colour + + + def DrawKDETheme(self, dc, rect): + """ Draws Unix-Style KDE Theme On Tabs. """ + + x, y, w, h = rect + + if self._style & NC_ROTATE and self._style & NC_RIGHT: + bandrange = xrange(13, -1, -1) + self._lastcolour = kdetheme[13] + brush = wx.Brush(kdetheme[0], wx.SOLID) + else: + bandrange = xrange(14) + self._lastcolour = kdetheme[0] + brush = wx.Brush(kdetheme[13], wx.SOLID) + + dc.SetBackground(brush) + for band in bandrange: + pen = wx.Pen(kdetheme[band]) + dc.SetPen(pen) + if self._style & NC_ROTATE: + if self._style & NC_RIGHT: + dc.DrawLine(x+1+band, y+1, x+1+band, y+h-1) + dc.DrawLine(x+w-(1+band), y+1, x+w-(1+band), y+h-2) + else: + dc.DrawLine(x+1+band, y+1, x+1+band, y+h-1) + dc.DrawLine(x+w-(2+band), y+1, x+w-(2+band), y+h-2) + else: + dc.DrawLine(x+1, y+band, x+w-1, y+band) + dc.DrawLine(x+1, y+h-1-band, x+w-2, y+h-1-band) + + + def DrawSilverTheme(self, dc, rect, selected): + """ Draws Windows XP Silver-Like Theme. """ + + x, y, w, h = rect + + if selected: + r1 = silvertheme1[0].Red() + g1 = silvertheme1[0].Green() + b1 = silvertheme1[0].Blue() + r2 = silvertheme1[1].Red() + g2 = silvertheme1[1].Green() + b2 = silvertheme1[1].Blue() + else: + r1 = silvertheme2[0].Red() + g1 = silvertheme2[0].Green() + b1 = silvertheme2[0].Blue() + r2 = silvertheme2[1].Red() + g2 = silvertheme2[1].Green() + b2 = silvertheme2[1].Blue() + rend = silvertheme2[2].Red() + gend = silvertheme2[2].Green() + bend = silvertheme2[2].Blue() + + if self._style & NC_ROTATE: + flrect = float(w-2) + else: + flrect = float(h-2) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + counter = 0 + + if self._style & NC_ROTATE: + if self._style & NC_RIGHT: + bandrange = xrange(x+w-2, x, -1) + else: + bandrange = xrange(x+1, x+w-1) + else: + bandrange = xrange(y+1, y+h) + + for band in bandrange: + currCol = (int(round(r1 + rf)), int(round(g1 + gf)), int(round(b1 + bf))) + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + dc.SetPen(wx.Pen(currCol)) + if self._style & NC_ROTATE: + if counter == 0: + ypos = y + 2 + yend = h - 4 + elif counter == 1: + ypos = y + 1 + yend = h - 2 + else: + ypos = y + 1 + yend = h - 2 + + dc.DrawRectangle(band, ypos, 1, yend) + + else: + if counter == 0: + xpos = x + 2 + xend = w - 4 + elif counter == 1: + xpos = x + 1 + xend = w - 2 + else: + xpos = x + 1 + xend = w - 2 + + dc.DrawRectangle(xpos, band, xend, 1) + + counter = counter + 1 + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + self._lastcolour = currCol + + if not selected and self._style & NC_TOP: + dc.SetBrush(wx.Brush((rend, gend, bend))) + dc.SetPen(wx.Pen((rend, gend, bend))) + if self._style & NC_ROTATE: + if self._style & NC_LEFT: + xpos = x + w - 4 + else: + xpos = x + + dc.DrawRectangle(xpos, ypos, 3, yend) + else: + dc.DrawRectangle(xpos, y+h-3, xend, 3) + self._lastcolour = wx.Colour(rend, gend, bend) + + + def DrawAquaTheme(self, dc, rect, style, selected): + """ Draws Mac-Style Aqua Theme On Tabs. """ + + x, y, w, h = rect + + if selected: + if style == 1: # Dark Aqua + r1 = topaqua1[0].Red() + g1 = topaqua1[0].Green() + b1 = topaqua1[0].Blue() + + r2 = topaqua1[1].Red() + g2 = topaqua1[1].Green() + b2 = topaqua1[1].Blue() + else: + r1 = topaqua2[0].Red() + g1 = topaqua2[0].Green() + b1 = topaqua2[0].Blue() + + r2 = topaqua2[1].Red() + g2 = topaqua2[1].Green() + b2 = topaqua2[1].Blue() + + else: + r1 = distaqua[0].Red() + g1 = distaqua[0].Green() + b1 = distaqua[0].Blue() + + r2 = distaqua[1].Red() + g2 = distaqua[1].Green() + b2 = distaqua[1].Blue() + + flrect = float((h-2)/2) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + counter = 0 + dc.SetPen(wx.TRANSPARENT_PEN) + + if self._style & NC_ROTATE: + startrange, endrange = (x, w) + else: + startrange, endrange = (y, h) + if self._style & NC_ROTATE and self._style & NC_RIGHT: + bandrange = xrange(startrange+endrange, startrange+endrange/2, -1) + else: + bandrange = xrange(startrange+1, startrange+endrange/2) + + for band in bandrange: + currCol = (int(round(r1 + rf)), int(round(g1 + gf)), int(round(b1 + bf))) + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + if self._style & NC_ROTATE: + if counter == 0: + ypos = y + 2 + yend = h - 4 + elif counter == 1: + ypos = y + 1 + yend = h - 2 + else: + ypos = y + 1 + yend = h - 2 + + dc.DrawRectangle(band, ypos, 1, yend) + else: + if counter == 0: + xpos = x + 2 + xend = w - 4 + elif counter == 1: + xpos = x + 1 + xend = w - 2 + else: + xpos = x + 1 + xend = w - 2 + + dc.DrawRectangle(xpos, band, xend, 1) + + counter = counter + 1 + + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + if selected: + if style == 1: # Dark Aqua + r1 = botaqua1[0].Red() + g1 = botaqua1[0].Green() + b1 = botaqua1[0].Blue() + + r2 = botaqua1[1].Red() + g2 = botaqua1[1].Green() + b2 = botaqua1[1].Blue() + else: + r1 = botaqua2[0].Red() + g1 = botaqua2[0].Green() + b1 = botaqua2[0].Blue() + + r2 = botaqua2[1].Red() + g2 = botaqua2[1].Green() + b2 = botaqua2[1].Blue() + else: + r1 = disbaqua[0].Red() + g1 = disbaqua[0].Green() + b1 = disbaqua[0].Blue() + + r2 = disbaqua[1].Red() + g2 = disbaqua[1].Green() + b2 = disbaqua[1].Blue() + + flrect = float((h-2)/2) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + counter = 0 + + if self._style & NC_ROTATE and self._style & NC_RIGHT: + bandrange = xrange(startrange+endrange/2, startrange+1, -1) + else: + bandrange = xrange(startrange+endrange/2, startrange+endrange) + for band in bandrange: + currCol = (int(round(r1 + rf)), int(round(g1 + gf)), int(round(b1 + bf))) + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + if self._style & NC_ROTATE: + dc.DrawRectangle(band, y + 1, 1, h-2) + else: + dc.DrawRectangle(x+1, band, w-2, 1) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + self._lastcolour = currCol + + + def DrawMetalTheme(self, dc, rect): + """ Draws Mac-Style Metal Gradient On Tabs. """ + + x, y, w, h = rect + + dc.SetPen(wx.TRANSPARENT_PEN) + counter = 0 + + if self._style & NC_ROTATE: + bandrange = xrange(x+1, x+w) + else: + bandrange = xrange(y+1, h+y) + for band in bandrange: + if self._style & NC_ROTATE: + intens = (230 + 80 * (x-band)/w) + else: + intens = (230 + 80 * (y-band)/h) + + colour = wx.Colour(intens, intens, intens) + dc.SetBrush(wx.Brush(colour)) + + if self._style & NC_ROTATE: + if counter == 0: + ypos = y + 2 + yend = h - 4 + elif counter == 1: + ypos = y + 1 + yend = h - 2 + else: + ypos = y + 1 + yend = h - 2 + if self._style & NC_RIGHT: + dc.DrawRectangle(x+w-band, ypos, 1, yend) + else: + dc.DrawRectangle(x+band, ypos, 1, yend) + + else: + if counter == 0: + xpos = x + 2 + xend = w - 4 + elif counter == 1: + xpos = x + 1 + xend = w - 2 + else: + xpos = x + 1 + xend = w - 2 + dc.DrawRectangle(xpos, band, xend, 1) + + counter = counter + 1 + + self._lastcolour = colour + + + def DrawVerticalGradient(self, dc, rect, index): + """ Gradient Fill From Colour 1 To Colour 2 From Top To Bottom. """ + + dc.SetPen(wx.TRANSPARENT_PEN) + + # calculate gradient coefficients + col2 = self._tabstyle.GetSecondGradientColour(index == self.GetSelection()) + col1 = self._tabstyle.GetFirstGradientColour(index == self.GetSelection()) + + r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) + r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) + + flrect = float(rect.height) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + counter = 0 + + bandrange = xrange(rect.y+1, rect.y + rect.height-1) + lenc = len(bandrange) + + for y in bandrange: + currCol = (r1 + rf, g1 + gf, b1 + bf) + + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + + # adjust along x-axis to preserve the curved tab edge + def GetXAdjust(counter): + if counter >=2 or counter <=lenc-2: + return 1 + if self._style & NC_LEFT or self._style & NC_RIGHT and not self._style & NC_ROTATE: + if counter == 0 and self._style & NC_RIGHT or \ + counter == lenc - 1 and self._style & NC_LEFT: + return 3 + elif counter == 1 and self._style & NC_RIGHT or \ + counter == lend - 2 and self._style & NC_LEFT: + return 2 + else: + return 1 + else: + if counter == lenc - 2: + return 2 + elif counter == lenc - 1: + return 3 + else: + return 1 + + xadjust = GetXAdjust(counter) + xpos = rect.x + xadjust + xend = rect.width - xadjust + + + counter = counter + 1 + + dc.DrawRectangle(xpos, y, xend, 1) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + self._lastcolour = currCol + + + def DrawHorizontalGradient(self, dc, rect, index): + """ Gradient Fill From Colour 1 To Colour 2 From Left To Right. """ + + dc.SetPen(wx.TRANSPARENT_PEN) + + # calculate gradient coefficients + col2 = self._tabstyle.GetSecondGradientColour(index == self.GetSelection()) + col1 = self._tabstyle.GetFirstGradientColour(index == self.GetSelection()) + + r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) + r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) + + flrect = float(rect.width) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + counter = 0 + + bandrange = xrange(rect.x + 1, rect.x + rect.width - 1) + lenc = len(bandrange) + + for x in bandrange: + currCol = (r1 + rf, g1 + gf, b1 + bf) + + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + # adjust along y-axis to preserve the curved tab edge + def GetYAdjust(counter): + if counter >=2 or counter <=lenc-2: + return 1 + if self._style & NC_TOP or self._style & NC_BOTTOM or \ + (self._style & NC_LEFT and self._style & NC_ROTATE): + if counter == 0 or counter == lenc - 1 and not self._style & NC_LEFT: + return 3 + elif counter == 1 or counter == lenc - 2 and not self._style & NC_LEFT: + return 2 + else: + return 1 + else: + if counter == lenc - 2: + return 2 + elif counter == lenc - 1: + return 3 + else: + return 1 + + yadjust = GetYAdjust(counter) + ypos = rect.y + yadjust + yend = rect.height - yadjust + + counter = counter + 1 + + dc.DrawRectangle(x, ypos, 1, yend) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + self._lastcolour = currCol + + + def GetAllTextExtents(self, dc): + """ Returns All Tabs Text Extents. Used Internally. """ + + self._mintabwidths = [] + self._maxtabwidths = [] + self._mintabheights = [] + self._maxtabheights = [] + self._incrtext = [] + minheight = 0 + + for ii in xrange(self.GetPageCount()): + + txts = self.GetPageText(ii) + font1 = self.GetPageTextFont(ii) + dc.SetFont(font1) + w1, h1 = dc.GetTextExtent(txts) + minheight = max(minheight, h1) + self._mintabwidths.append(w1) + self._mintabheights.append(h1) + font2 = self.GetPageTextSecondaryFont(ii) + dc.SetFont(font2) + w2, h2 = dc.GetTextExtent(txts) + minheight = max(minheight, h2) + + self._maxtabwidths.append(w2) + self._maxtabheights.append(h2) + self._incrtext.append(abs(self._mintabwidths[ii] - self._maxtabwidths[ii])) + + mh1 = max(self._mintabheights) + font1 = self.GetPageTextFont(self._mintabheights.index(mh1)) + mh2 = max(self._maxtabheights) + font2 = self.GetPageTextSecondaryFont(self._maxtabheights.index(mh2)) + + mhend = max(mh1, mh2) + + if mhend == mh1: + maxfont = font1 + else: + maxfont = font2 + + minheight = self.GetSize()[1] + + return minheight, maxfont + + + def DrawBuiltinStyle(self, dc, style, rect, index, selection): + """ Methods That Holds All The Theme Styles. """ + + if style._aqua: + if self._selstyle._normal: + self.DrawAquaTheme(dc, rect, style._aqua, index==selection) + else: + oldselstyle = self._selstyle[:] + self._selstyle._normal = True + self.DrawBuiltinStyle(dc, self._selstyle, rect, index, selection) + self._selstyle = oldselstyle + + elif style._metal: + if self._selstyle._normal: + self.DrawMetalTheme(dc, rect) + else: + oldselstyle = self._selstyle[:] + self._selstyle._normal = True + self.DrawBuiltinStyle(dc, self._selstyle, rect, index, selection) + self._selstyle = oldselstyle + + elif style._kdetheme: + if self._selstyle._normal: + self.DrawKDETheme(dc, rect) + else: + oldselstyle = self._selstyle[:] + self._selstyle._normal = True + self.DrawBuiltinStyle(dc, self._selstyle, rect, index, selection) + self._selstyle = oldselstyle + + elif style._macstyle: + if self._selstyle._normal: + self.DrawMacTheme(dc, rect, style._macstyle) + else: + oldselstyle = self._selstyle[:] + self._selstyle._normal = True + self.DrawBuiltinStyle(dc, self._selstyle, rect, index, selection) + self._selstyle = oldselstyle + + elif style._gradient: + if self._selstyle._normal: + if style._gradient & ThemeStyle.GRADIENT_VERTICAL: + self.DrawVerticalGradient(dc, rect, index) + else: + self.DrawHorizontalGradient(dc, rect, index) + else: + oldselstyle = self._selstyle[:] + self._selstyle._normal = True + self.DrawBuiltinStyle(dc, self._selstyle, rect, index, selection) + self._selstyle = oldselstyle + + elif style._silver: + if self._selstyle._normal: + self.DrawSilverTheme(dc, rect, index==selection) + else: + oldselstyle = self._selstyle[:] + self._selstyle._normal = True + self.DrawBuiltinStyle(dc, self._selstyle, rect, index, selection) + self._selstyle = oldselstyle + + + def DrawGradientOnTab(self, dc, rect, col1, col2): + """ Draw A Gradient Coloured Tab. """ + + dc.SetPen(wx.TRANSPARENT_PEN) + + r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) + r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) + + flrect = float(rect.height) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + counter = 0 + + for y in xrange(rect.y+1, rect.y + rect.height): + currCol = (r1 + rf, g1 + gf, b1 + bf) + + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + if counter == 0: + xpos = rect.x + 2 + xend = rect.width - 4 + elif counter == 1: + xpos = rect.x + 1 + xend = rect.width - 2 + else: + xpos = rect.x + xend = rect.width + + counter = counter + 1 + + dc.DrawRectangle(xpos, y, xend, 1) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + self._lastcolour = currCol + + def _CalcBestWidth(self, dc): + return max(self._CalcMaxTabWidth(dc), self._CalcSizeToggleBestSize()[0]) + + def _CalcMaxTabWidth(self, dc): + self._CalcMaxTextHeight(dc) + textWidth = max(self._maxtabwidths) + tabIndex = self._maxtabwidths.index(textWidth) + bmpWidth, bmpHeight = self._CalcTabBitmapSize(tabIndex) + tabrect = self._CalcTabRect(tabIndex, 0, 0, textWidth, bmpWidth, bmpHeight) + # return the width based on the longest label, plus 3 for + # the additional width of the selected tab + return tabrect.width + 3 + + def _CalcMaxTextHeight(self, dc): + if self._somethingchanged: + minheight, maxfont = self.GetAllTextExtents(dc) + self._minheight = minheight + self._maxfont = maxfont + else: + minheight = self._minheight + maxfont = self._maxfont + + dc.SetFont(maxfont) + _, height = dc.GetTextExtent("Aq") + self._maxtextheight = height + + def _CalcSizeToggleBestSize(self): + if self._sizeToggleButton: + return self._sizeToggleButton.GetBestSize() + else: + return wx.Size(0,0) + + def _CalcTabBitmapPosition(self, tabIndex, bmpWidth, bmpHeight, tabrect): + + if self._style & NC_ROTATE: + bmpposx = tabrect.x + (tabrect.width - bmpWidth) / 2 + yoffset = self._padding.x + if self._style & NC_LEFT: + bmpposx += 1 + bmpposy = tabrect.y + tabrect.height - (yoffset + bmpHeight) + else: + bmpposy = tabrect.y + yoffset + if tabIndex == self.GetSelection(): + bmpposx += self._style & NC_LEFT and -1 or 1 + else: + bmpposx = tabrect.x + self._padding.x + bmpposy = tabrect.y + (tabrect.height - bmpHeight) / 2 + if tabIndex == self.GetSelection() and self._style & NC_TOP: + bmpposy -= 1 + + return (bmpposx, bmpposy) + + def _CalcTabBitmapSize(self, tabIndex): + result = (0, 0) + bmp = self._GetTabBitmap(tabIndex) + bmpOk = bmp.Ok() + if bmpOk: + result = (bmp.GetWidth(), bmp.GetHeight()) + return result + + def _CalcTabBitmapSpace(self, bmpWidth, bmpHeight): + space = self._padding.x + bmpSpace = self._style & NC_ROTATE and bmpHeight or bmpWidth + if bmpSpace: + space = space + self._padding.x + bmpSpace + return space + + def _CalcTabRect(self, tabIndex, posx, posy, textWidth, bmpWidth, bmpHeight): + + xpos = posx + if self._style & NC_BOTTOM: + ypos = 1 + elif self._style & NC_TOP: + ypos = self.GetSize().y - self._maxtextheight - self._padding.y*2 + else: + ypos = posy + + xsize = textWidth + self._CalcTabBitmapSpace(bmpWidth, bmpHeight) + \ + self._padding.x + self._incrtext[tabIndex] + \ + self._CalcXWidth() + + ysize = self._maxtextheight + self._padding.y*2 + if self._style & NC_TOP: + ysize += 3 + + if self._style & NC_ROTATE: + xsize, ysize = (ysize, xsize) + + if tabIndex == self.GetSelection(): + if self._style & NC_TOP or self._style & NC_BOTTOM: + xsize = xsize + self._spacetabs + if tabIndex > 0: + xpos = xpos - self._spacetabs + xsize = xsize + self._spacetabs + if self._style & NC_TOP: + ypos -= 3 + ysize = ysize + 2 + else: + xsize += 3 + + if self._style & NC_LEFT: + xpos = self.GetSize().width - xsize + return wx.Rect(xpos, ypos, xsize, ysize) + + def _CalcTabTextPosition(self, tabIndex, tabrect, space): + xtextpos = tabrect.x + space + self._incrtext[tabIndex]/2 + + if self._style & NC_BOTTOM: + ytextpos = self._padding.y + else: + ytextpos = tabrect.y + self._padding.y + 1 + if tabIndex == self.GetSelection(): + if tabIndex == 0 and self._style & NC_TOP or self._style & NC_BOTTOM: + xtextpos = xtextpos + self._spacetabs/2.0 + 1 + if self._style & NC_BOTTOM: + ytextpos += 2 + elif self._style & NC_TOP: + ytextpos -= 2 + + if self._style & NC_ROTATE: + xoffset = ytextpos - tabrect.y + yoffset = xtextpos - tabrect.x + if self._style & NC_LEFT: + xtextpos, ytextpos = (tabrect.x + xoffset - 1, + tabrect.y + tabrect.height - yoffset) + else: + yoffset += self._CalcXWidth() + xtextpos, ytextpos = (tabrect.x + tabrect.width - xoffset, + tabrect.y + yoffset) + + return (xtextpos, ytextpos) + + def _CalcTabTextWidth(self, dc, tabIndex): + if self._style & NC_FIXED_WIDTH: + result = max(self._maxtabwidths) + else: + dc.SetFont(self.GetPageTextFont(tabIndex)) + result, _ = dc.GetTextExtent(self.GetPageText(tabIndex)) + return result + + def _CalcXRect(self, tabrect): + result = None + drawx, dxstyle = self.GetDrawX() + if drawx: + if dxstyle == 1: + mins = min(self._padding.x, self._padding.y) + 1 + mins = min(mins, 6) + xoffset = tabrect.width-mins-3 + yoffset = 2 + xsize = ysize = mins+1 + else: + if self._style & NC_ROTATE: + xoffset = (tabrect.width-self._maxtextheight-self._padding.y/2)/2 + yoffset = self._padding.x/2 + else: + xoffset = tabrect.width-self._maxtextheight-self._padding.x + yoffset = (tabrect.height-self._maxtextheight-self._padding.y/2)/2 + xsize = ysize = self._maxtextheight + result = wx.Rect(tabrect.x+xoffset, tabrect.y+yoffset, xsize, ysize) + return result + + def _CalcXWidth(self): + drawx, dxstyle = self.GetDrawX() + if drawx: + if dxstyle == 1: + xxspace = self._padding.x/2 + else: + xxspace = self._padding.x + self._maxtextheight + else: + xxspace = 0 + return xxspace + + def _ClipAtPaperEdge(self, dc, tabrect, tabIndex): + selected = tabIndex == self.GetSelection() + if self._style & NC_TOP: + cliprect = (tabrect.x, tabrect.y, tabrect.width, + selected and tabrect.height - 2 or tabrect.height-3) + elif self._style & NC_LEFT: + cliprect = (tabrect.x, tabrect.y, tabrect.width - 2, tabrect.height) + elif self._style & NC_BOTTOM: + cliprect = (tabrect.x, tabrect.y + 2, tabrect.width, tabrect.height - 2) + else: + cliprect = (tabrect.x + 2, tabrect.y, tabrect.width - 2, tabrect.height) + dc.SetClippingRegion(*cliprect) + + def _CreateSizeToggleButton(self): + buttonlabel = self._expanded and "<<" or ">>" + self._sizeToggleButton = wx.Button(self, wx.NewId(), + pos = wx.Point(0,0,), label = buttonlabel, style=wx.BU_EXACTFIT) + font = self._sizeToggleButton.GetFont() + if font.GetPointSize() > 6: + font.SetPointSize(6) + self._sizeToggleButton.SetFont(font) + self.Bind(wx.EVT_BUTTON, self._ToggleSize, self._sizeToggleButton) + + + def _DrawBackground(self, dc, paintTools): + #background + size = self.GetSize() + dc.SetBrush(paintTools.BackBrush) + + if not (self._style & wx.NO_BORDER): + # full border + dc.SetPen(paintTools.BorderPen) + dc.SetPen(paintTools.HighlightPen) + dc.DrawRectangle(0, 0, size.x, size.y) + + else: + dc.SetPen(paintTools.BackPen) + dc.DrawRectangle(0, 0, size.x, size.y) + self._DrawPageEdge(dc, paintTools) + + def _DrawFocusIndicator(self, dc, paintTools, tabrect): + if self.GetUseFocusIndicator(): + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(paintTools.FocusPen) + dc.DrawRoundedRectangle(tabrect.x+self._padding.x/2, tabrect.y+self._padding.y/2, + tabrect.width-self._padding.x, + tabrect.height-self._padding.y-2, 2) + + def _DrawPageEdge(self, dc, paintTools): + if self._style & NC_TOP: + dc.SetPen(paintTools.HighlightPen) + dc.DrawLine(0, self.GetSize().y-1, self.GetSize().x, self.GetSize().y-1) + else: + if not self._tabstyle._normal or self._usegradients: + dc.SetPen(paintTools.HighlightPen) + else: + dc.SetPen(paintTools.BorderPen) + if self._style & NC_BOTTOM: + dc.DrawLine(0, 1, self.GetSize().x, 1) + elif self._style & NC_LEFT: + dc.DrawLine(self.GetSize().width - 1, 0, self.GetSize().width - 1, self.GetSize().height) + elif self._style & NC_RIGHT: + dc.DrawLine(0, 0, 0, self.GetSize().height) + + def _DrawTab(self, dc, paintTools, tabrect, tabIndex): + size = self.GetSize() + self._DrawTabGradientOutline(dc, paintTools, tabrect, tabIndex) + self._FillTab(dc, paintTools, tabrect, tabIndex) + self._DrawTabOutline(dc, paintTools, tabrect, tabIndex) + self._DrawTabPageEdge(dc, paintTools, tabrect, tabIndex) + self._HighlightTabEdge(dc, paintTools, tabrect) + self._ShadowTabEdge(dc, paintTools, tabrect) + + def _DrawTabBitmap(self, dc, tabIndex, bmpposx, bmpposy): + bmpindex = self.GetPageImage(tabIndex) + if self.IsPageEnabled(tabIndex): + self._imglist.Draw(bmpindex, dc, bmpposx, bmpposy, + wx.IMAGELIST_DRAW_TRANSPARENT, True) + else: + self._grayedlist.Draw(bmpindex, dc, bmpposx, bmpposy, + wx.IMAGELIST_DRAW_TRANSPARENT, True) + + def _DrawTabGradientOutline(self, dc, paintTools, tabrect, tabIndex): + if not self._tabstyle._normal or self._usegradients: + if tabIndex != self.GetSelection() and self._style & NC_TOP: + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(paintTools.ShadowPen) + dc.DrawRoundedRectangle(tabrect.x+1, tabrect.y+1, tabrect.width, tabrect.height-1, 3) + + def _DrawTabOutline(self, dc, paintTools, tabrect, tabIndex): + if not self._tabstyle._normal or self._usegradients: + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(paintTools.HighlightPen) + else: + dc.SetBrush(wx.Brush(self.GetPageColour(tabIndex))) + if self._style & NC_TOP: + dc.SetPen(paintTools.HighlightPen) + else: + dc.SetPen(paintTools.BorderPen) + self._ClipAtPaperEdge(dc, tabrect, tabIndex) + dc.DrawRoundedRectangle(tabrect.x, tabrect.y, tabrect.width, tabrect.height, 3) + dc.DestroyClippingRegion() + + def _DrawTabPageEdge(self, dc, paintTools, tabrect, tabIndex): + if not self._tabstyle._normal or self._usegradients: + edgePen = paintTools.HighlightPen + else: + if self._style & NC_TOP: + edgePen = paintTools.HighlightPen + else: + edgePen = paintTools.BorderPen + + if tabIndex == self.GetSelection(): + # un-paint the line at the paper edge + cancelPen = wx.Pen(self.GetPageColour(tabIndex)) + dc.SetPen(cancelPen) + if self._style & NC_BOTTOM: + dc.DrawLine(tabrect.x+1, tabrect.y, tabrect.x + tabrect.width, tabrect.y) + elif self._style & NC_LEFT: + dc.DrawLine(tabrect.x + tabrect.width-1, tabrect.y, tabrect.x + tabrect.width-1, tabrect.y + tabrect.height) + elif self._style & NC_RIGHT: + dc.DrawLine(tabrect.x, tabrect.y, tabrect.x, tabrect.y + tabrect.height) + + if tabIndex != self.GetSelection(): + if self._style & NC_TOP: + dc.DrawLine(tabrect.x, self.GetSize().y-1, tabrect.x + tabrect.width, self.GetSize().y-1) + + # draw sharp corners at the paper edge + dc.SetPen(edgePen) + if self._style & NC_BOTTOM: + dc.DrawLine(tabrect.x, tabrect.y, tabrect.x, tabrect.y + 2) + dc.DrawLine((tabrect.x + tabrect.width)-1, tabrect.y, + (tabrect.x + tabrect.width)-1, tabrect.y + 2) + elif self._style & NC_LEFT: + dc.DrawLine(self.GetSize().width - 2, tabrect.y, self.GetSize().width, tabrect.y) + dc.DrawLine(self.GetSize().width - 2, tabrect.y + tabrect.height - 1, self.GetSize().width, tabrect.y + tabrect.height - 1) + elif self._style & NC_RIGHT: + dc.DrawLine(tabrect.x, tabrect.y, tabrect.x + 2, tabrect.y) + dc.DrawLine(tabrect.x, tabrect.y + tabrect.height - 1, tabrect.x + 2, tabrect.y + tabrect.height - 1) + + def _DrawTabText(self, dc, tabIndex, xtextpos, ytextpos): + dc.SetFont(self.GetPageTextFont(tabIndex)) + dc.SetTextForeground(self._GetTabTextColour(tabIndex)) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + if self._style & NC_ROTATE: + angle = (self._style & NC_LEFT) and 90.0 or 270.0 + dc.DrawRotatedText(self.GetPageText(tabIndex), xtextpos, ytextpos, angle) + else: + dc.DrawText(self.GetPageText(tabIndex), xtextpos, ytextpos) + + def _DrawX(self, dc, tabrect, xrect, textColour): + drawx, dxstyle = self.GetDrawX() + if drawx: + if dxstyle == 1: + mins = min(self._padding.x, self._padding.y) + 1 + mins = min(mins, 6) + dc.SetPen(wx.Pen(textColour, 1)) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawLine(xrect.x, xrect.y, tabrect.x+tabrect.width-2, tabrect.y+3+mins) + dc.DrawLine(xrect.x, xrect.y+mins, tabrect.x+tabrect.width-2, tabrect.y+1) + dc.DrawRectangle(xrect.x, xrect.y, xrect.width, xrect.height) + elif dxstyle == 2: + dc.SetPen(wx.Pen(textColour)) + dc.SetBrush(wx.Brush(textColour)) + dc.DrawRoundedRectangle(xrect.x, xrect.y, xrect.width, xrect.height, 2) + dc.SetPen(wx.Pen(self.GetBackgroundColour(), 2)) + dc.DrawLine(xrect.x+2, xrect.y+2, xrect.x+xrect.width-3, xrect.y+xrect.height-3) + dc.DrawLine(xrect.x+2, xrect.y+xrect.height-3, xrect.x+xrect.width-3, xrect.y+2) + else: + self._imglist2.Draw(0, dc, xrect.x, xrect.y, wx.IMAGELIST_DRAW_TRANSPARENT, True) + + def _EnhanceMultiSelectedTab(self, dc, tabIndex, tabrect): + dc.SetPen(wx.Pen(self._GetTabTextColour(tabIndex), 1, wx.DOT_DASH)) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRoundedRectangle(tabrect.x+self._padding.x/2+1, + tabrect.y+self._padding.y/2+1, + tabrect.width-self._padding.x-2, + tabrect.height-self._padding.y-2-2, 2) + + def _EnhanceSelectedTab(self, dc, paintTools, tabrect): + xselpos = tabrect.x + xselsize = tabrect.width + yselsize = tabrect.height + + if self._style & NC_BOTTOM: + yselpos = (tabrect.y + tabrect.height) - 2 + elif self._style & NC_TOP: + yselpos = tabrect.y + + self._HighlightSelectedTabEdge(dc, paintTools, tabrect) + self._ShadowTabEdge(dc, paintTools, tabrect) + self._DrawFocusIndicator(dc, paintTools, tabrect) + + def _FillTab(self, dc, paintTools, tabrect, tabIndex): + if self._usegradients: + self.DrawGradientOnTab(dc, tabrect, self._pages[tabIndex]._firstcolour, + self._pages[tabIndex]._secondcolour) + elif not self._tabstyle._normal: + self.DrawBuiltinStyle(dc, self._tabstyle, tabrect, tabIndex, + self.GetSelection()) + + def _GetPaintTools(self): + back_colour = self.GetBackgroundColour() + back_brush = wx.Brush(back_colour) + back_pen = wx.Pen(back_colour) + + border_pen = self._borderpen + highlightpen = self._highlightpen + if self._tabstyle._normal and not self._usegradients: + highlightpen = self._highlightpen2 + + shadowpen = self._shadowpen + upperhighpen = self._upperhigh + + if self.GetHighlightSelection(): + selectionpen = wx.Pen(self._selectioncolour) + selectionEdgePen = wx.Pen(self._selectionedgecolour) + else: + selectionpen = selectionEdgePen = None + + x_pen = self.GetDrawX() == 1 and wx.BLACK_PEN or None + focusindpen = self.GetUseFocusIndicator() and self._focusindpen or None + + return _TabCtrlPaintTools(back_brush, back_pen, border_pen, highlightpen, + shadowpen, upperhighpen, selectionpen, selectionEdgePen, x_pen, + focusindpen) + + def _GetTabBitmap(self, tabIndex): + bmp = wx.NullBitmap + if self.GetPageImage(tabIndex) >= 0: + bmpindex = self.GetPageImage(tabIndex) + if self.IsPageEnabled(tabIndex): + bmp = self._imglist.GetBitmap(bmpindex) + else: + bmp = self._grayedlist.GetBitmap(bmpindex) + return bmp + + def _GetTabTextColour(self, tabIndex): + if self.IsPageEnabled(tabIndex): + result = self.GetPageTextColour(tabIndex) + else: + result = self._disabledcolour + return result + + def _GetThemePageColour(self, index): + if self._tabstyle._macstyle: + return NC_MAC_LIGHT + elif self._tabstyle._kdetheme: + return kdetheme[0] + elif self._tabstyle._aqua: + if index == self.GetSelection(): + return topaqua1[0] + else: + return distaqua[0] + elif self._tabstyle._metal: + intens = (230 + 80 * (self._tabrect[0].y-self._tabrect[0].y+1)/self._tabrect[0].height) + return wx.Colour(intens, intens, intens) + elif self._tabstyle._silver: + if index == self.GetSelection(): + return silvertheme1[0] + else: + return silvertheme2[0] + elif self._tabstyle._gradient: + color = wx.WHITE + if self._tabstyle._gradient & ThemeStyle.GRADIENT_VERTICAL: + if self._style & NC_TOP: + color = self._tabstyle.GetSecondGradientColour(index) + elif self._style & NC_BOTTOM: + color = self._tabstyle.GetFirstGradientColour(index) + elif self._tabstyle._gradient & ThemeStyle.GRADIENT_HORIZONTAL and \ + self._style & NC_ROTATE: + if self._style & NC_LEFT: + color = self._tabstyle.GetSecondGradientColour(index) + else: + color = self._tabstyle.GetFirstGradientColour(index) + return color + + def _HighlightSelectedTabEdge(self, dc, paintTools, tabrect): + if self._style & NC_ROTATE: + yselpos = tabrect.y + 3 + yselsize = tabrect.height - 6 + xselpos = self._style & NC_RIGHT and tabrect.x + tabrect.width - 1 or tabrect.x + if self.GetHighlightSelection(): + dc.SetBrush(paintTools.BackBrush) + dc.SetPen(paintTools.SelectionEdgePen) + dc.DrawLine(xselpos, yselpos, xselpos, yselpos + yselsize) + dc.SetPen(paintTools.SelectionPen) + for band in range(2): + if self._style & NC_RIGHT: + xselpos -= 1 + else: + xselpos += 1 + yselpos -= 1 + yselsize += 2 + dc.DrawLine(xselpos, yselpos, xselpos, yselpos + yselsize) + else: + dc.SetPen(paintTools.HighlightPen) + dc.DrawLine(xselpos, yselpos, xselpos, yselpos + yselsize) + else: + xselpos = tabrect.x + 3 + xselsize = tabrect.width - 6 + + if self._style & NC_BOTTOM: + yselpos = tabrect.y + tabrect.height - 1 + else: + yselpos = tabrect.y + dc.SetPen(paintTools.HighlightPen) + dc.DrawLine(xselpos, yselpos, xselpos + xselsize, yselpos) + + if self.GetHighlightSelection(): + dc.SetBrush(paintTools.BackBrush) + dc.SetPen(paintTools.SelectionEdgePen) + dc.DrawLine(xselpos, yselpos, xselpos + xselsize, yselpos) + dc.SetPen(paintTools.SelectionPen) + for band in range(2): + if self._style & NC_BOTTOM: + yselpos -= 1 + else: + yselpos += 1 + xselpos -= 1 + xselsize += 2 + dc.DrawLine(xselpos, yselpos, xselpos + xselsize, yselpos) + + def _HighlightTabEdge(self, dc, paintTools, tabrect): + if not self._tabstyle._normal or self._usegradients: + if self._style & NC_TOP: + dc.SetPen(paintTools.UpperHighlightPen) + dc.DrawLine(tabrect.x+2, tabrect.y-1, tabrect.x + tabrect.width - 2, tabrect.y-1) + else: + if self._style & NC_TOP: + dc.SetPen(paintTools.HighlightPen) + dc.DrawLine(tabrect.x + 3, tabrect.y, tabrect.x + tabrect.width - 3, tabrect.y) + + def _InitExpandableStyles(self, style): + self._expanded = not style & NC_ROTATE + if self._expanded: + self._expandedstyle = style + self._contractedstyle = style | NC_ROTATE + else: + self._contractedstyle = style + self._expandedstyle = (style ^ NC_ROTATE) | NC_FIXED_WIDTH + + def _InitExpandableTabStyles(self, style, expanded, tabstyle): + if tabstyle._gradient: + alternatestyle = ThemeStyle() + firstcolor = tabstyle.GetFirstGradientColour() + secondcolor = tabstyle.GetSecondGradientColour() + swapcolors = (tabstyle._gradient & ThemeStyle.GRADIENT_VERTICAL and style & NC_RIGHT and expanded) or \ + (tabstyle._gradient & ThemeStyle.GRADIENT_HORIZONTAL and style & NC_RIGHT and not expanded) + if swapcolors: + firstcolor, secondcolor = (secondcolor, firstcolor) + if tabstyle._gradient & ThemeStyle.GRADIENT_VERTICAL: + othergradient = (tabstyle._gradient ^ ThemeStyle.GRADIENT_VERTICAL) | ThemeStyle.GRADIENT_HORIZONTAL + else: + othergradient = (tabstyle._gradient ^ ThemeStyle.GRADIENT_HORIZONTAL) | ThemeStyle.GRADIENT_VERTICAL + alternatestyle.EnableGradientStyle(True, othergradient) + alternatestyle.SetFirstGradientColour(firstcolor) + alternatestyle.SetSecondGradientColour(secondcolor) + if tabstyle._gradient & ThemeStyle.DIFFERENT_GRADIENT_FOR_SELECTED: + firstcolor = tabstyle.GetFirstGradientColour(True) + secondcolor = tabstyle.GetSecondGradientColour(True) + if swapcolors: + firstcolor, secondcolor = (secondcolor, firstcolor) + alternatestyle.SetFirstGradientColourSelected(firstcolor) + alternatestyle.SetSecondGradientColourSelected(secondcolor) + if expanded: + self._expandedtabstyle = tabstyle + self._contractedtabstyle = alternatestyle + else: + self._contractedtabstyle = tabstyle + self._expandedtabstyle = alternatestyle + else: + self._expandedtabstyle = tabstyle + self._contractedtabstyle = tabstyle + + def _OnStyleChange(self): + if self._style & NC_TOP or self._style & NC_BOTTOM: + self.SetBestSize((-1, newheight)) + else: + self.SetBestSize((self._CalcBestWidth(wx.ClientDC(self)), -1)) + self._parent.GetSizer().Layout() + self._somethingchanged = True + self._firsttime = True + self.Refresh() + + def _ShadowTabEdge(self, dc, paintTools, tabrect): + dc.SetPen(paintTools.ShadowPen) + if self._style & NC_BOTTOM: + dc.DrawLine((tabrect.x + tabrect.width), tabrect.y+1, + (tabrect.x+tabrect.width), tabrect.y + tabrect.height-4) + elif self._style & NC_TOP: + dc.DrawLine(tabrect.x + tabrect.width, tabrect.y+3, + tabrect.x+tabrect.width, tabrect.y+tabrect.height-4) + + def OnPaint(self, event): + """ Handles The wx.EVT_PAINT Event For TabCtrl. """ + + dc = wx.BufferedPaintDC(self) + + if self.GetPageCount() == 0: + event.Skip() + return + + pt = self._GetPaintTools() + + dc.BeginDrawing() + + self._DrawBackground(dc, pt) + + self._CalcMaxTextHeight(dc) + + posx = self._firsttabpos.x + posy = self._firsttabpos.y + + if self._style & NC_LEFT: + _ = 1 + + if self._firsttime: + if not hasattr(self, "_initrect"): + self._initrect = [] + if self.HasSpinButton() and self._fromdnd: + self._firstvisible = self._spinbutton.GetValue() + self._firsttime = False + self._fromdnd = False + else: + self._initrect = [] + self._firstvisible = 0 + else: + if self.HasSpinButton(): + self._firstvisible = self._spinbutton.GetValue() + else: + self._firstvisible = 0 + + lastvisible = self.GetPageCount() + + #and tabs + oncount = -1 + + self._tabvisible = [1]*self.GetPageCount() + + tabrect = [] + # some theme style rendering routines expect this to exist, so + # set it now: + self._tabrect = tabrect + Xrect = [] + + for ii in xrange(self._firstvisible, lastvisible): + if not self._enablehiding or not self._pages[ii]._ishidden: + + oncount = oncount + 1 + + self._tabvisible[ii] = 1 + + newwidth = self._CalcTabTextWidth(dc, ii) + + bmpWidth, bmpHeight = self._CalcTabBitmapSize(ii) + + tabrect.append(self._CalcTabRect(ii, posx, posy, + newwidth, bmpWidth, bmpHeight)) + + self._DrawTab(dc, pt, tabrect[-1], ii) + + self._DrawTabText(dc, ii, *self._CalcTabTextPosition(ii, + tabrect[-1], self._CalcTabBitmapSpace(bmpWidth, bmpHeight))) + + if bmpWidth: + self._DrawTabBitmap(dc, ii, *self._CalcTabBitmapPosition(ii, + bmpWidth, bmpHeight, tabrect[-1])) + + if self.GetSelection() in [ii, ii - 1]: + # Handle this special case on the selected tab and + # on the tab that follows it (if there is one), to ensure + # proper rendering of the selected tab's right edge + self._EnhanceSelectedTab(dc, pt, tabrect[self.GetSelection() - self._firstvisible]) + + if self.GetDrawX()[0]: + Xrect.append(self._CalcXRect(tabrect[-1])) + self._DrawX(dc, tabrect[-1], Xrect[-1], + self._GetTabTextColour(ii)) + + if ii in self._selectedtabs: + self._EnhanceMultiSelectedTab(dc, ii, tabrect[-1]) + + if self._style & NC_TOP or self._style & NC_BOTTOM: + # horizontally positioned tabs along top or bottom + posx = posx + tabrect[-1].width + else: + # vertically stacked tabs along side + posy = posy + tabrect[-1].height + + if self._firsttime: + self._initrect.append(tabrect[oncount]) + + else: + + self._tabvisible[ii] = 0 + + self._xrect = Xrect + + if self._firsttime: + self._firsttime = False + + self.UpdateMenuButton(self.HasMenuButton()) + self.UpdateSpinButton() + + if self._enabledragging: + if self._isdragging and not self._isleaving: + self.DrawInsertionMark(dc, self._olddragpos) + + dc.EndDrawing() + + +# ---------------------------------------------------------------------------- # +# Class NotebookCtrl +# This Is The Main Class Implementation +# ---------------------------------------------------------------------------- # + +class NotebookCtrl(wx.Panel): + """ + Display one or more windows in a notebook. + + B{Events}: + - B{EVT_NOTEBOOKCTRL_PAGE_CHANGING}: sent when the active + page in the notebook is changing + - B{EVT_NOTEBOOKCTRL_PAGE_CHANGED}: sent when the active + page in the notebook has changed + - B{EVT_NOTEBOOKCTRL_PAGE_CLOSING}: sent when a page in the + notebook is closing + - B{EVT_NOTEBOOKCTRL_PAGE_DND}: sent when a page has been + dropped onto the notebook in a drag-drop operation + - B{EVT_NOTEBOOKCTRL_PAGE_DCLICK}: sent when the user + double-clicks a tab in the notebook + - B{EVT_NOTEBOOKCTRL_PAGE_RIGHT}: sent when the user + clicks a tab in the notebook with the right mouse + button + - B{EVT_NOTEBOOKCTRL_PAGE_MIDDLE}: sent when the user + clicks a tab in the notebook with the middle mouse + button + """ + + def __init__(self, parent, id, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=NC_DEFAULT_STYLE, sizer=wx.HORIZONTAL, margin=2, name="NotebookCtrl"): + """ + Default Class Constructor. + + @param style: Style For The NotebookCtrl, Which May Be: + a) NC_TOP: NotebookCtrl Placed On Top (Default); + b) NC_BOTTOM: NotebookCtrl Placed At The Bottom; + c) NC_LEFT: NotebookCtrl Placed At The Left; + d) NC_RIGHT: NotebookCtrl Placed At The Right; + e) NC_FIXED_WIDTH: All Tabs Have The Same Width; + f) wx.NO_BORDER: Shows No Border For The Control (Default, Looks Better); + g) wx.STATIC_BORDER: Shows A Static Border On The Control. + + @param sizer: The Sizer Orientation For The Sizer That Holds + All The Panels: Changing This Style Is Only Useful When You + Use The Tile Method. In This Case, If sizer=wx.HORIZONTAL, + All The Panels Will Be Shown In Columns, While If + sizer=wx.VERTICAL All The Panels Will Be Shown In Rows. + + @param margin: An Integer Number Of Pixels That Add Space + Above TabCtrl If style=NC_TOP, Or Below It If + style=NC_BOTTOM + """ + + wx.Panel.__init__(self, parent, -1, style=wx.NO_FULL_REPAINT_ON_RESIZE | + wx.CLIP_CHILDREN, name=name) + + self.nb = TabCtrl(self, -1, pos, size, style) + + self._notebookpages = [] + + if style & NC_TOP == 0 and style & NC_BOTTOM == 0 \ + and style & NC_LEFT == 0 and style & NC_RIGHT == 0: + style = style | NC_TOP + + if style & wx.NO_BORDER == 0 and \ + style & wx.STATIC_BORDER == 0: + style = style | wx.NO_BORDER + + self._style = style + self._showcolumns = False + self._showtabs = True + self._sizerstyle = sizer + self._custompanel = None + self._focusswitch = False + self._oldfocus = None + + if style & NC_TOP or style & NC_BOTTOM: + self.sizer = wx.BoxSizer(wx.VERTICAL) + self.tabsizer = wx.BoxSizer(wx.VERTICAL) + else: + self.sizer = wx.BoxSizer(wx.HORIZONTAL) + self.tabsizer = wx.BoxSizer(wx.HORIZONTAL) + + self.bsizer = wx.BoxSizer(sizer) + + if style & NC_TOP or style & NC_BOTTOM: + tabBorderFlag = wx.LEFT | wx.RIGHT + else: + tabBorderFlag = wx.TOP | wx.BOTTOM + + if style & NC_TOP or style & NC_LEFT: + self.sizer.Add(self.tabsizer, 0, wx.EXPAND | tabBorderFlag, 2) + self._AddMargin(style, margin) + self.tabsizer.Add(self.nb, 0, wx.EXPAND) + self.sizer.Add(self.bsizer, 1, wx.EXPAND) + elif style & NC_BOTTOM or style & NC_RIGHT: + self.sizer.Add(self.bsizer, 1, wx.EXPAND) + self.sizer.Add(self.tabsizer, 0, wx.EXPAND | tabBorderFlag, 2) + self.tabsizer.Add(self.nb, 0, wx.EXPAND) + self._AddMargin(style, margin) + + self.SetSizer(self.sizer) + + self.tabsizer.Show(self.nb, False) + + self.sizer.Layout() + self.Bind(wx.EVT_MOTION, self.OnMouseMotion) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + + def OnKeyDown(self, event): + """ + Handles The wx.EVT_KEY_DOWN Event For NotebookCtrl. This Is Only Processed + If The User Navigate Through Tabs With Ctrl-Tab Keyboard Navigation. + """ + + if event.GetKeyCode == wx.WXK_TAB: + if event.ControlDown(): + sel = self.GetSelection() + if sel == self.GetPageCount() - 1: + sel = 0 + else: + sel = sel + 1 + + while not self.IsPageEnabled(sel): + sel = sel + 1 + if sel == self.GetPageCount() - 1: + sel = 0 + + self.SetSelection(sel) + + event.Skip() + + + def OnMouseMotion(self, event): + """ Handles The wx.EVT_MOTION Event For NotebookCtrl. """ + + if self.nb._enabledragging: + + if event.Dragging() and not event.RightIsDown() and not event.MiddleIsDown(): + + tolerance = 2 + pt = event.GetPosition() + dx = abs(pt.x - self.nb._dragstartpos.x) + dy = abs(pt.y - self.nb._dragstartpos.y) + if dx <= tolerance and dy <= tolerance: + self.SetCursor(wx.STANDARD_CURSOR) + return + + self.SetCursor(self.nb._dragcursor) + self.nb._isdragging = True + + else: + + self.nb._isdragging = False + self.SetCursor(wx.STANDARD_CURSOR) + + if self.nb._showtooltip: + if self.nb._istooltipshown: + pt = event.GetPosition() + self.nb._insidetab = self.nb.GetInsideTab(pt) + if self.nb._insidetab < 0: + try: + self.nb._tipwindow.Destroy() + self.nb._istooltipshown = False + except: + self.nb._istooltipshown = False + + self.nb.Refresh() + + event.Skip() + + + def EnableChildFocus(self, enable=True): + """ Enables/Disables Sending EVT_NOTEBOOKCTRL_PAGE_CHANGING When In Tile Mode. """ + + self._focusswitch = enable + + + def FindFocusedPage(self, obj): + """ Find Which NotebookCtrl Page Has The Focus Based On Its Child Focus. """ + + while 1: + if obj in self._notebookpages: + return obj + + try: + obj = obj.GetParent() + except: + return None + + return None + + + def OnFocus(self, event): + """ Handles The wx.EVT_CHILD_FOCUS Event For NotebookCtrl. """ + + if not self._focusswitch: + event.Skip() + return + + newfocus = self.FindFocusedPage(event.GetEventObject()) + + if newfocus == self._oldfocus or newfocus is None: + event.Skip() + return + + self._oldfocus = newfocus + + eventOut = NotebookCtrlEvent(wxEVT_NOTEBOOKCTRL_PAGE_CHANGING, self.GetId()) + + nPage = self._notebookpages.index(newfocus) + eventOut.SetSelection(nPage) + eventOut.SetOldSelection(self.GetSelection()) + eventOut.SetEventObject(self) + + if not self.GetEventHandler().ProcessEvent(eventOut): + + # Program Allows The Page Change + self.nb._selection = nPage + eventOut.SetEventType(wxEVT_NOTEBOOKCTRL_PAGE_CHANGED) + eventOut.SetOldSelection(self.nb._selection) + self.GetEventHandler().ProcessEvent(eventOut) + + event.Skip() + + + def AddPage(self, page, text, select=False, img=-1, hidden=False): + """ + Add A Page To The Notebook. + + @param page: Specifies The New Page; + @param text: The Tab Text; + @param select: Whether The Page Should Be Selected Or Not; + @param img: Specifies The Optional Image Index For The New Page. + """ + + self.Freeze() + + oldselection = self.nb.GetSelection() + + if self.GetPageCount() == 0: + if self.GetCustomPage() is not None: + self.bsizer.Detach(self._custompanel) + self._custompanel.Show(False) + self.bsizer.Layout() + + self.bsizer.Add(page, 1, wx.EXPAND | wx.ALL, 2) + + self.nb.AddPage(text, select, img, hidden) + self._notebookpages.append(page) + + page.Bind(wx.EVT_CHILD_FOCUS, self.OnFocus) + + if select: + if oldselection >= 0: + self.bsizer.Show(self.GetPage(oldselection), False) + + self.nb.SetSelection(self.GetPageCount()-1) + self.bsizer.Layout() + else: + if oldselection >= 0: + self.bsizer.Show(page, False) + else: + self.bsizer.Show(page, True) + self.nb.SetSelection(self.GetPageCount()-1) + self.bsizer.Layout() + + if self.GetPageCount() == 1: + + self.bsizer.Show(page, True) + + if self.nb._hideonsingletab: + + self._ShowTabCtrl(False) + + else: + self.nb.Show(True) + self._ShowTabCtrl(True) + + else: + + self.nb.Show(True) + self._ShowTabCtrl(True) + + self.bsizer.Layout() + self.sizer.Layout() + + self.Thaw() + + self.Tile(self._showcolumns) + self.ShowTabs(self._showtabs) + + + def InsertPage(self, nPage, page, text, select=False, img=-1, hidden=False): + """ + Insert A Page Into The Notebook. + + @param page: Specifies The New Page; + @param nPage: Specifies The Position For The New Page; + @param text: The Tab Text; + @param select: Whether The Page Should Be Selected Or Not; + @param img: Specifies The Optional Image Index For The New Page. + @param hidden: C{True} to hide the page; C{False} to display it + """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In InsertPage: (" + str(nPage) + ")" + + self.Freeze() + + oldselection = self.nb.GetSelection() + + if self.GetPageCount() == 0: + if self.GetCustomPage() is not None: + self.bsizer.Detach(self._custompanel) + self._custompanel.Show(False) + + if oldselection >= 0: + self.bsizer.Show(oldselection, False) + self.bsizer.Layout() + + if oldselection >= nPage: + oldselection = oldselection + 1 + + self.nb.InsertPage(nPage, text, select, img, hidden) + self.bsizer.Insert(nPage, page, 1, wx.EXPAND | wx.ALL, 2) + self._notebookpages.insert(nPage, page) + self.bsizer.Layout() + + page.Bind(wx.EVT_CHILD_FOCUS, self.OnFocus) + + for ii in xrange(self.GetPageCount()): + self.bsizer.Show(ii, False) + + self.bsizer.Layout() + + if select: + self.bsizer.Show(nPage, True) + self.bsizer.Layout() + else: + if oldselection >= 0: + self.bsizer.Show(oldselection, True) + self.bsizer.Layout() + else: + self.bsizer.Show(nPage, True) + + self.bsizer.Layout() + + if self.GetPageCount() == 1: + + if self.nb._hideonsingletab: + + self._ShowTabCtrl(False) + + else: + + self.nb.Show(True) + self._ShowTabCtrl(True) + + else: + + self.nb.Show(True) + self._ShowTabCtrl(True) + + self.sizer.Layout() + + self.Thaw() + + self.Tile(self._showcolumns) + self.ShowTabs(self._showtabs) + + + def GetPage(self, nPage): + """ Returns The Window At The Given Position nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPage: (" + str(nPage) + ")" + + return self._notebookpages[nPage] + + + def DeleteAllPages(self): + """ Deletes All NotebookCtrl Pages. """ + + self.Freeze() + + counter = self.GetPageCount() - 1 + + for ii in xrange(self.GetPageCount()): + self.bsizer.Detach(counter-ii) + panels = self.GetPage(counter-ii) + panels.Destroy() + + self.nb.DeleteAllPages() + self._notebookpages = [] + self.nb._selection = -1 + + self.nb.Show(False) + + custom = self.GetCustomPage() + + if custom is not None: + self.SetCustomPage(custom) + custom.Show(True) + + self.bsizer.Layout() + + self._ShowTabCtrl(False) + + self.sizer.Layout() + + self.Thaw() + + + def DeletePage(self, nPage): + """ Deletes The Page nPage, And The Associated Window. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In DeletePage: (" + str(nPage) + ")" + + oldselection = self.GetSelection() + + self.Freeze() + + panel = self.GetPage(nPage) + self.bsizer.Detach(nPage) + + self.bsizer.Layout() + + self._notebookpages.pop(nPage) + self.nb.DeletePage(nPage) + + panel.Destroy() + + if self.GetPageCount() > 0: + if oldselection == nPage: + if self.GetSelection() > 0: + self.SetSelection(self.GetSelection()-1) + else: + self.SetSelection(self.GetSelection()) + self.bsizer.Show(self.GetSelection()) + self.bsizer.Layout() + + if self.GetPageCount() == 0: + self.nb.Show(False) + self._ShowTabCtrl(False) + + custom = self.GetCustomPage() + + if custom is not None: + self.bsizer.Add(custom, 1, wx.EXPAND | wx.ALL, 2) + custom.Show(True) + + self.bsizer.Layout() + self.sizer.Layout() + self.Thaw() + return + + if self.GetPageCount() == 1: + + if self.nb._hideonsingletab: + + self._ShowTabCtrl(False) + + else: + + self.nb.Show(True) + self._ShowTabCtrl(True) + + else: + + self.nb.Show(True) + self._ShowTabCtrl(True) + + self.sizer.Layout() + + self.Thaw() + + self.Tile(self._showcolumns) + self.ShowTabs(self._showtabs) + + + def SetSelection(self, nPage): + """ + Sets The Current Tab Selection To The Given nPage. This Call Generates The + EVT_NOTEBOOKCTRL_PAGE_CHANGING Event. + """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetSelection: (" + str(nPage) + ")" + + oldselection = self.GetSelection() + + if oldselection == nPage: + return + + self.nb.SetSelection(nPage) + + self.Tile(self._showcolumns) + self.ShowTabs(self._showtabs) + + + def GetPageCount(self): + """ Returns The Number Of Pages In NotebookCtrl. """ + + return self.nb.GetPageCount() + + + def GetSelection(self): + """ Returns The Current Selection. """ + + return self.nb.GetSelection() + + + def GetImageList(self): + """ Returns The Image List Associated With The NotebookCtrl. """ + + return self.nb.GetImageList() + + + def SetImageList(self, imagelist): + """ Associate An Image List To NotebookCtrl. """ + + self.nb.SetImageList(imagelist) + + + def AssignImageList(self, imagelist): + """ Associate An Image List To NotebookCtrl. """ + + self.nb.AssignImageList(imagelist) + + + def GetPadding(self): + """ Returns The (Horizontal, Vertical) Padding Of The Text Inside Tabs. """ + + return self.nb.GetPadding() + + + def SetPadding(self, padding): + """ Sets The (Horizontal, Vertical) Padding Of The Text Inside Tabs. """ + + self.nb.SetPadding(padding) + + + def SetUseFocusIndicator(self, focus=True): + """ Globally Enables/Disables Tab Focus Indicator. """ + + self.nb.SetUseFocusIndicator(focus) + + + def GetUseFocusIndicator(self): + """ Returns Globally Enable/Disable State For Tab Focus Indicator. """ + + return self.nb.GetUseFocusIndicator() + + + def EnablePage(self, nPage, enable=True): + """ Enable/Disable The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In EnablePage: (" + str(nPage) + ")" + + self.nb.EnablePage(nPage, enable) + + + def IsPageEnabled(self, nPage): + """ Returns Whether A Page Is Enabled Or Not. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In IsPageEnabled: (" + str(nPage) + ")" + + return self.nb.IsPageEnabled(nPage) + + + def SetHighlightSelection(self, highlight=True): + """ Globally Enables/Disables Tab Highlighting On Tab Selection. """ + + self.nb.SetHighlightSelection(highlight) + + + def GetHighlightSelection(self): + """ Returns Globally Enable/Disable State For Tab Highlighting On Tab Selection. """ + + return self.nb.GetHighlightSelection() + + + def SetAnimationImages(self, nPage, imgarray): + """ + Sets An Animation List Associated To The Given Page nPage. + + @param nPage: The Given Page; + @param imgarray: A List Of Image Indexes Of Images Inside The + ImageList Associated To NotebookCtrl. + """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetAnimationImages: (" + str(nPage) + ")" + + if not imgarray: + raise "\nERROR: Invalid Image Array In SetAnimationImages: (" + repr(imgarray) + ")" + + if min(imgarray) < 0: + raise "\nERROR: Invalid Image Array In SetAnimationImages: (Min(ImgArray) = " + \ + str(min(imgarray)) + " < 0)" + + if max(imgarray) > self.GetImageList().GetImageCount() - 1: + raise "\nERROR: Invalid Image Array In SetAnimationImages: (Max(ImgArray) = " + \ + str(max(imgarray)) + " > " + str(self.GetImageList().GetImageCount()-1) + ")" + + self.nb.SetAnimationImages(nPage, imgarray) + + + def GetAnimationImages(self, nPage): + """ Returns The Animation Images List Associated To The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetAnimationImages: (" + str(nPage) + ")" + + return self.nb.GetAnimationImages(nPage) + + + def StartAnimation(self, nPage, timer=500): + """ Starts The Animation On The Given Page nPage, With Refreshing Time Rate "timer". """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In StartAnimation: (" + str(nPage) + ")" + + self.nb.StartAnimation(nPage, timer) + + + def StopAnimation(self, nPage): + """ Stops The Animation On The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In StopAnimation: (" + str(nPage) + ")" + + self.nb.StopAnimation(nPage) + + + def EnableDragAndDrop(self, enable=True): + """ Globall Enables/Disables Tabs Drag And Drop. """ + + self.nb.EnableDragAndDrop(enable) + + + def EnableHiding(self, enable=True): + """ Globally Enables/Disables Hiding On Tabs In Runtime. """ + + self.nb.EnableHiding(enable) + + + def SetDrawX(self, drawx=True, style=1, image1=None, image2=None): + """ + Globally Enables/Disables The Drawing Of A Closing "X" In The Tab. + + @param drawx: C{True} to enable drawing a closing "X"; C{False} to + disable it + @param style: the style of the X to draw when C{drawx} is C{True}; + possible values are: + - C{1}: Small "X" At The Top-Right Of The Tab; + - C{2}: Bigger "X" In The Middle Vertical Of The Tab (Like Opera Notebook); + - C{3}: Custom "X" Image Is Drawn On Tabs. + @param image1: if C{style} is C{3}, the image to use when drawing + the X on an unhighlighted tab + @param image2: if C{style} is C{3}, the image to use when drawing + the X on a highlighted tab + """ + + self.nb.SetDrawX(drawx, style, image1, image2) + + + def GetDrawX(self): + """ + Returns The Enable/Disable State Of Drawing Of A Small "X" At The Top-Right Of + Every Page. + """ + + return self.nb.GetDrawX() + + + def SetImageToCloseButton(self, convert=True): + """ Set Whether The Tab Icon Should Be Converted To The Close Button Or Not. """ + + self.nb.SetImageToCloseButton(convert) + + + def GetImageToCloseButton(self): + """ Get Whether The Tab Icon Should Be Converted To The Close Button Or Not. """ + + return self.nb._convertimage + + + def HideOnSingleTab(self, hide=True): + """ Hides The TabCtrl When There Is Only One Tab In NotebookCtrl. """ + + self.nb.HideOnSingleTab(hide) + + if self.GetPageCount() == 1: + if hide: + self._ShowTabCtrl(False) + else: + self._ShowTabCtrl(True) + + self.sizer.Layout() + + + def SetPagePopupMenu(self, nPage, menu): + """ Sets A Popup Menu Specific To A Single Tab. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPagePopupMenu: (" + str(nPage) + ")" + + self.nb.SetPagePopupMenu(nPage, menu) + + + def GetPagePopupMenu(self, nPage): + """ Returns The Popup Menu Associated To A Single Tab. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPagePopupMenu: (" + str(nPage) + ")" + + return self.nb.GetPagePopupMenu(nPage) + + + def SetPageToolTip(self, nPage, tooltip="", timer=500, winsize=400): + """ + Sets A ToolTip For The Given Page nPage. + + @param nPage: The Given Page; + @param tooltip: The ToolTip String; + @param timer: The Timer After Which The Tip Window Is Popped Up; + @param winsize: The Maximum Width Of The Tip Window. + """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageToolTip: (" + str(nPage) + ")" + + self.nb.SetPageToolTip(nPage, tooltip, timer, winsize) + + + def GetPageToolTip(self, nPage): + """ Returns A Tuple With All Page ToolTip Parameters. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageToolTip: (" + str(nPage) + ")" + + return self.nb.GetPageToolTip(nPage) + + + def EnableToolTip(self, show=True): + """ Globally Enables/Disables Tab ToolTips. """ + + self.nb.EnableToolTip(show) + + + def GetToolTipBackgroundColour(self): + """ Returns The ToolTip Window Background Colour. """ + + return self.nb.GetToolTipBackgroundColour() + + + def SetToolTipBackgroundColour(self, colour=None): + """ Sets The ToolTip Window Background Colour. """ + + if colour is None: + colour = wx.Colour(255, 255, 230) + + self.nb.SetToolTipBackgroundColour(colour) + + + def EnableTabGradients(self, enable=True): + """ Globally Enables/Disables Drawing Of Gradient Coloured Tabs For Each Tab. """ + + self.nb.EnableTabGradients(enable) + + + def SetPageFirstGradientColour(self, nPage, colour=None): + """ Sets The Single Tab First Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageFirstGradientColour: (" + str(nPage) + ")" + + if colour is None: + colour = wx.WHITE + + self.nb.SetPageFirstGradientColour(nPage, colour) + + + def SetPageSecondGradientColour(self, nPage, colour=None): + """ Sets The Single Tab Second Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageSecondGradientColour: (" + str(nPage) + ")" + + self.nb.SetPageSecondGradientColour(nPage, colour) + + + def GetPageFirstGradientColour(self, nPage): + """ Returns The Single Tab First Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageFirstGradientColour: (" + str(nPage) + ")" + + return self.nb.GetPageFirstGradientColour(nPage) + + + def GetPageSecondGradientColour(self, nPage): + """ Returns The Single Tab Second Gradient Colour. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageSecondGradientColour: (" + str(nPage) + ")" + + return self.nb.GetPageSecondGradientColour(nPage) + + + def CancelTip(self): + """ Destroys The Tip Window (Probably You Won't Need This One. """ + + self.nb.CancelTip() + + + def AdvanceSelection(self, forward=True): + """ + Cycles Through The Tabs. The Call To This Function Generates The + EVT_NOTEBOOKCTRL_PAGE_CHANGING Event. + """ + + self.nb.AdvanceSelection(forward) + + + def SetDefaultPage(self, defaultpage=-1): + """ + Sets The Default Page That Will Be Selected When An Active And Selected + Tab Is Made Inactive. + """ + + if defaultpage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetDefaultPage: (" + str(defaultpage) + ")" + + self.nb.SetDefaultPage(defaultpage) + + + def GetDefaultPage(self): + """ Returns The Default Page. """ + + return self.nb.GetDefaultPage() + + + def GetPageText(self, nPage): + """ Returns The String For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageText: (" + str(nPage) + ")" + + return self.nb.GetPageText(nPage) + + + def SetPageText(self, nPage, text): + """ Sets The String For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageText: (" + str(nPage) + ")" + + self.nb.SetPageText(nPage, text) + + + def GetPageImage(self, nPage): + """ Returns The Image Index For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageImage: (" + str(nPage) + ")" + + return self.nb.GetPageImage(nPage) + + + def SetPageImage(self, nPage, img): + """ Sets The Image Index For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageImage: (" + str(nPage) + ")" + + self.nb.SetPageImage(nPage, img) + + + def SetPageTextFont(self, nPage, font=None): + """ Sets The Primary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageTextFont: (" + str(nPage) + ")" + + if font is None: + font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + self.nb.SetPageTextFont(nPage, font) + + + def GetPageTextFont(self, nPage): + """ Returns The Primary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageTextFont: (" + str(nPage) + ")" + + return self.nb.GetPageTextFont(nPage) + + + def SetPageTextSecondaryFont(self, nPage, font=None): + """ Sets The Secondary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageTextSecondaryFont: (" + str(nPage) + ")" + + if font is None: + font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + self.nb.SetPageTextSecondaryFont(nPage, font) + + + def GetPageTextSecondaryFont(self, nPage): + """ Returns The Secondary Font For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageTextSecondaryFont: (" + str(nPage) + ")" + + return self.nb.GetPageTextSecondaryFont(nPage) + + + def SetPageTextColour(self, nPage, colour=None): + """ Sets The Text Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageTextColour: (" + str(nPage) + ")" + + if colour is None: + colour = wx.BLACK + + self.nb.SetPageTextColour(nPage, colour) + + + def GetPageTextColour(self, nPage): + """ Returns The Text Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageTextColour: (" + str(nPage) + ")" + + return self.nb.GetPageTextColour(nPage) + + + def SetPageColour(self, nPage, colour=None): + """ Sets The Tab Background Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In SetPageColour: (" + str(nPage) + ")" + + if colour is None: + colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + + self.nb.SetPageColour(nPage, colour) + + + def GetPageColour(self, nPage): + """ Returns The Tab Background Colour For The Given Page nPage. """ + + if nPage < 0 or nPage >= self.GetPageCount(): + raise "\nERROR: Invalid Notebook Page In GetPageColour: (" + str(nPage) + ")" + + return self.nb.GetPageColour(nPage) + + + def SetTabHeight(self, height=28): + """ Sets The Tabs Height. """ + + if height <= 0: + raise "\nERROR: Impossible To Set An Height <= 0. " + + self.nb.SetTabHeight(height) + + + def SetControlBackgroundColour(self, colour=None): + """ Sets The TabCtrl Background Colour (Behind The Tabs). """ + + if colour is None: + colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) + + self.nb.SetBackgroundColour(colour) + + + def ApplyTabTheme(self, theme=None): + """ Apply A Particular Theme To Be Drawn On Tabs. """ + + if theme is None: + theme = ThemeStyle() + + self.nb.ApplyTabTheme(theme) + + + def SetSelectionColour(self, colour=None): + """ Sets The Tab Selection Colour (Thin Line Above The Selected Tab). """ + + if colour is None: + colour = wx.Colour(255, 180, 0) + + self.nb.SetSelectionColour(colour) + + + def SetContourLineColour(self, colour=None): + """ Sets The Contour Line Colour (Controur Line Around Tabs). """ + + self.nb.SetContourLineColour(colour) + + def Tile(self, show=True, orient=None): + """ Shows Pages In Column/Row Mode (One Panel After The Other In Columns/Rows). """ + + if self._GetTabCtrlWindow().IsShown() == show and orient is None: + return + + self.Freeze() + + if orient is not None and show: + if orient == wx.VERTICAL: + norient = wx.HORIZONTAL + else: + norient = wx.VERTICAL + + if orient is not None and show: + origorient = self.bsizer.GetOrientation() + if origorient != norient: + for ii in xrange(self.GetPageCount()-1, -1, -1): + self.bsizer.Detach(ii) + + self.sizer.Detach(self.bsizer) + self.bsizer.Destroy() + + self.bsizer = wx.BoxSizer(norient) + + for ii in xrange(self.GetPageCount()): + self.bsizer.Add(self._notebookpages[ii], 1, wx.EXPAND | wx.ALL, 2) + + if self._style & NC_TOP: + self.sizer.Add(self.bsizer, 1, wx.EXPAND) + else: + self.sizer.Insert(0, self.bsizer, 1, wx.EXPAND) + + self.bsizer.Layout() + self.sizer.Layout() + + selection = self.GetSelection() + + if show: + self._ShowTabCtrl(False) + if self._style & NC_TOP or self._style & NC_LEFT: + if len(self.nb._selectedtabs) > 0: + for ii in xrange(self.GetPageCount()): + if ii in self.nb._selectedtabs: + self.bsizer.Show(ii, True) + else: + self.bsizer.Show(ii, False) + else: + for ii in xrange(self.GetPageCount()): + if self.IsPageEnabled(ii): + if not self.nb._enablehiding or not self.nb._pages[ii]._ishidden: + self.bsizer.Show(ii, True) + else: + self.bsizer.Show(ii, False) + else: + self.bsizer.Show(ii, False) + else: + if len(self.nb._selectedtabs) > 0: + for ii in xrange(self.GetPageCount()): + if ii in self.nb._selectedtabs: + self.bsizer.Show(ii, True) + else: + for ii in xrange(self.GetPageCount()): + if self.IsPageEnabled(ii): + if not self.nb._enablehiding or not self.nb._pages[ii]._ishidden: + self.bsizer.Show(ii, True) + else: + self.bsizer.Show(ii, False) + else: + self.bsizer.Show(ii, False) + else: + self._ShowTabCtrl(True) + if self._style & NC_TOP or self._style & NC_LEFT: + for ii in xrange(self.GetPageCount()): + self.bsizer.Show(ii, False) + else: + for ii in xrange(self.GetPageCount()): + self.bsizer.Show(ii, False) + + if selection < 0: + self.bsizer.Layout() + self.sizer.Layout() + return + else: + self.bsizer.Show(selection, True) + self.bsizer.Layout() + + self._showcolumns = show + + self.bsizer.Layout() + self.sizer.Layout() + + self.Thaw() + + + + def ShowTabs(self, show=True): + """ Shows/Hides Tabs On Request. """ + + if self._GetTabCtrlWindow().IsShown() == show: + return + + if self.GetPageCount() == 0: + return + + self.Freeze() + + self._ShowTabCtrl(show) + + self._showtabs = show + + self.sizer.Layout() + + self.Thaw() + + + def GetIndex(self, page): + """ Returns The Page Index (Position) Based On The NotebookCtrl Page Passed. """ + + if page in self._notebookpages: + return self._notebookpages.index(page) + + return -1 + + + def ReparentPage(self, nPage, newParent): + """ Reparents The NotebookCtrl Page nPage To A New Parent. """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In ReparentPage: (" + str(nPage) + ")" + + page = self.GetPage(nPage) + page.Reparent(newParent) + + + def ReparentToFrame(self, nPage, createNotebook=False): + """ Reparents The NotebookCtrl Page nPage To A New Frame. """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In ReparentToFrame: (" + str(nPage) + ")" + + self.Freeze() + + infos = self.GetPageInfo(nPage) + panel = self.GetPage(nPage) + text = infos["text"] + oldparent = panel.GetParent() + + frame = NCFrame(None, -1, text, nb=self, infos=infos, panel=panel, oldparent=oldparent) + + if createNotebook: + nb = NotebookCtrl(frame, -1, style=self._style, sizer=self._sizerstyle) + nb.SetImageList(infos["imagelist"]) + self.ReparentToNotebook(nPage, nb) + else: + self.ReparentPage(nPage, frame) + + self.nb.DeletePage(nPage, False) + + self.bsizer.Detach(nPage) + self.bsizer.Layout() + self.sizer.Layout() + + self._notebookpages.pop(nPage) + + self.AdvanceSelection() + + if self.GetPageCount() == 0: + self._ShowTabCtrl(False) + + self.sizer.Layout() + + custom = self.GetCustomPage() + if custom is not None: + self.SetCustomPage(custom) + + self.Thaw() + + frame.Show() + + + def ReparentToNotebook(self, nPage, notebook, newPage=None): + """ Reparents The NotebookCtrl Page nPage To A New NotebookCtrl. """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In ReparentToNotebook: (" + str(nPage) + ")" + + if newPage is not None and newPage >= notebook.GetPageCount(): + raise "\nERROR: Invalid Notebook New Page In ReparentToNotebook: (" + str(nPage) + ")" + + self.Freeze() + + infos = self.GetPageInfo(nPage) + panel = self.GetPage(nPage) + + self.ReparentPage(nPage, notebook) + + if newPage is None: + notebook.AddPage(panel, infos["text"], False, infos["image"]) + notebook.SetPageInfo(0, infos) + + for attr in attrs: + setattr(notebook, attr, getattr(self.nb, attr)) + + self.nb.DeletePage(nPage, False) + + self.bsizer.Detach(nPage) + self.bsizer.Layout() + self.sizer.Layout() + + self._notebookpages.pop(nPage) + + self.AdvanceSelection() + + if self.GetPageCount() == 0: + self._ShowTabCtrl(False) + + self.sizer.Layout() + + self.Thaw() + + + def GetPageInfo(self, nPage): + """ Returns All The Style Information For A Given Page. """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In GetPageInfo: (" + str(nPage) + ")" + + text = self.GetPageText(nPage) + image = self.GetPageImage(nPage) + font1 = self.GetPageTextFont(nPage) + font2 = self.GetPageTextSecondaryFont(nPage) + fontcolour = self.GetPageTextColour(nPage) + pagecolour = self.GetPageColour(nPage) + enabled = self.IsPageEnabled(nPage) + tooltip, ontime, winsize = self.GetPageToolTip(nPage) + menu = self.GetPagePopupMenu(nPage) + firstcol = self.GetPageFirstGradientColour(nPage) + secondcol = self.GetPageSecondGradientColour(nPage) + ishidden = self.nb._pages[nPage]._ishidden + + isanimated = 0 + timer = None + + if self.nb._timers[nPage].IsRunning(): + isanimated = 1 + timer = self.nb._timers[nPage].GetInterval() + + self.StopAnimation(nPage) + animatedimages = self.GetAnimationImages(nPage) + + infos = {"text": text, "image": image, "font1": font1, "font2": font2, + "fontcolour": fontcolour, "pagecolour": pagecolour, "enabled": enabled, + "tooltip": tooltip, "ontime": ontime, "winsize": winsize, + "menu": menu, "isanimated": isanimated, "timer": timer, + "animatedimages": animatedimages, "imagelist": self.nb._imglist, + "firstcol": firstcol, "secondcol": secondcol, "ishidden": ishidden} + + return infos + + + def SetPageInfo(self, nPage, infos): + """ Sets All The Style Information For A Given Page. """ + + if nPage < 0 or (self.GetSelection() >= 0 and nPage >= self.GetPageCount()): + raise "\nERROR: Invalid Notebook Page In SetPageInfo: (" + str(nPage) + ")" + + self.SetPageTextFont(nPage, infos["font1"]) + self.SetPageTextSecondaryFont(nPage, infos["font2"]) + self.SetPageTextColour(nPage, infos["fontcolour"]) + self.SetPageColour(nPage, infos["pagecolour"]) + self.EnablePage(nPage, infos["enabled"]) + self.SetPageToolTip(nPage, infos["tooltip"], infos["ontime"], infos["winsize"]) + self.SetPagePopupMenu(nPage, infos["menu"]) + self.SetPageFirstGradientColour(nPage, infos["firstcol"]) + self.SetPageSecondGradientColour(nPage, infos["secondcol"]) + self.nb._pages[nPage]._ishidden = infos["ishidden"] + + if infos["isanimated"] and len(infos["animatedimages"]) > 1: + self.SetAnimationImages(nPage, infos["animatedimages"]) + self.StartAnimation(nPage, infos["timer"]) + + + def SetCustomPage(self, panel): + """ Sets A Custom Panel To Show When There Are No Pages Left In NotebookCtrl. """ + + self.Freeze() + + if panel is None: + if self._custompanel is not None: + self.bsizer.Detach(self._custompanel) + self._custompanel.Show(False) + + if self.GetPageCount() == 0: + self._ShowTabCtrl(False) + else: + if self.GetPageCount() == 0: + if self._custompanel is not None: + self.bsizer.Detach(self._custompanel) + self._custompanel.Show(False) + + self.bsizer.Add(panel, 1, wx.EXPAND | wx.ALL, 2) + panel.Show(True) + self._ShowTabCtrl(False) + else: + panel.Show(False) + + self._custompanel = panel + + self.bsizer.Layout() + self.sizer.Layout() + self.Thaw() + + + def GetCustomPage(self): + """ Gets A Custom Panel To Show When There Are No Pages Left In NotebookCtrl. """ + + return self._custompanel + + + def HideTab(self, nPage, hide=True): + """ Hides A Tab In The NotebookCtrl. """ + + self.nb.HideTab(nPage, hide) + + + def HitTest(self, point, flags=0): + """ + Standard NotebookCtrl HitTest() Method. If Called With 2 Outputs, It + Returns The Page Clicked (If Any) And One Of These Flags: + + NC_HITTEST_NOWHERE = 0 ==> Hit Not On Tab + NC_HITTEST_ONICON = 1 ==> Hit On Icon + NC_HITTEST_ONLABEL = 2 ==> Hit On Label + NC_HITTEST_ONITEM = 4 ==> Hit Generic, On Item + NC_HITTEST_ONX = 8 ==> Hit On Closing "X" On Every Page + """ + + return self.nb.HitTest(point, flags) + + def _AddMargin(self, style, margin): + if style & NC_TOP or style & NC_BOTTOM: + self.tabsizer.Add((0, margin), 0) + elif style & NC_LEFT or style & NC_RIGHT: + self.tabsizer.Add((margin, 0), 0) + + def _GetTabCtrlWindow(self): + if self._style & NC_TOP or self._style & NC_LEFT: + return self.tabsizer.GetItem(1) + else: + return self.tabsizer.GetItem(0) + + def _ShowTabCtrl(self, show): + if self._style & NC_TOP: + self.sizer.Show(0, show) + else: + self.sizer.Show(1, show) + + +# ---------------------------------------------------------------------------- # +# Class TransientTipWindow +# Auxiliary Help Class. Used To Build The Tip Window. +# ---------------------------------------------------------------------------- # + +class _PopupWindow: + + def _Fill(self, tip, winsize): + + panel = wx.Panel(self, -1) + colour = self.GetParent().GetToolTipBackgroundColour() + + panel.SetBackgroundColour(colour) + + # border from sides and top to text (in pixels) + border = 5 + # how much space between text lines + textPadding = 2 + max_len = len(tip) + tw = winsize + + mylines = tip.split("\n") + + sts = wx.StaticText(panel, -1, "\n".join(mylines)) + sx, sy = sts.GetBestSize() + sts.SetPosition((2, 2)) + + panel.SetSize((sx+6, sy+6)) + self.SetSize(panel.GetSize()) + + +class TransientTipWindow(_PopupWindow, wx.PopupWindow): + + def __init__(self, parent, tip, winsize): + + wx.PopupWindow.__init__(self, parent, flags=wx.SIMPLE_BORDER) + self._Fill(tip,winsize) + + + def ProcessLeftDown(self, evt): + + return False + + + def OnDismiss(self): + + return False + + +class macPopupWindow(wx.Frame): + + def __init__(self, parent, flags): + + wx.Frame.__init__(self, parent, id=-1, style=flags|wx.FRAME_NO_TASKBAR|wx.STAY_ON_TOP) + self._hideOnActivate = False + #Get the parent frame: could be improved maybe? + self._parentFrame = parent + + while True: + + parent = self._parentFrame.GetParent() + + if parent: + self._parentFrame = parent + else: + break + + self.Bind(wx.EVT_ACTIVATE, self.OnActivate) + + + def Show(self, show=True): + + wx.Frame.Show(self,show) + + if show: + self._parentFrame.Raise() + self._hideOnActivate = True + + + def OnActivate(self, evt): + """ + Let The User Hide The Tooltip By Clicking On It. + NotebookCtrl Will Destroy It Later. + """ + + if self._hideOnActivate: + wx.Frame.Show(self,False) + + +class macTransientTipWindow(_PopupWindow, macPopupWindow): + + def __init__(self, parent, tip, winsize): + + macPopupWindow.__init__(self, parent, flags=wx.SIMPLE_BORDER) + self._Fill(tip, winsize) + + +class NCFrame(wx.Frame): + + def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, nb=None, + panel=None, infos=None, oldparent=None): + + wx.Frame.__init__(self, parent, id, title, pos, size, style) + + self._infos = infos + self._nb = nb + self._panel = panel + self._oldparent = oldparent + + self.Bind(wx.EVT_CLOSE, self.OnClose) + + + def OnClose(self, event): + + try: + infos = self._infos + self._panel.Reparent(self._oldparent) + self._nb.AddPage(self._panel, infos["text"], False, infos["image"]) + + id = self._nb.GetPageCount() - 1 + + self._nb.SetPageTextFont(id, infos["font1"]) + self._nb.SetPageTextSecondaryFont(id, infos["font2"]) + self._nb.SetPageTextColour(id, infos["fontcolour"]) + self._nb.SetPageColour(id, infos["pagecolour"]) + self._nb.EnablePage(id, infos["enabled"]) + self._nb.SetPageToolTip(id, infos["tooltip"], infos["ontime"], infos["winsize"]) + self._nb.SetPagePopupMenu(id, infos["menu"]) + self._nb.SetPageFirstGradientColour(id, infos["firstcol"]) + self._nb.SetPageSecondGradientColour(id, infos["secondcol"]) + self._nb._pages[id]._ishidden = infos["ishidden"] + + if infos["isanimated"] and len(infos["animatedimages"]) > 1: + self._nb.SetAnimationImages(id, infos["animatedimages"]) + self._nb.StartAnimation(id, infos["timer"]) + + except: + self.Destroy() + event.Skip() + return + + self.Destroy() + + event.Skip() + +class NotebookCtrlWindowHandler(xrc.XmlResourceHandler): + """ + Create L{NotebookCtrl} windows defined in Xrc resources. + + Below is an example of a resource definition:: + <?xml version="1.0" encoding="ISO-8859-1"?> + <resource> + <object class="wxPanel" name="appPanel"> + <object class="wxBoxSizer"> + <orient>wxVERTICAL</orient> + <object class="sizeritem"> + <option>1</option> + <flag>wxEXPAND</flag> + <object class="NotebookCtrl" name="notebook"> + <style>wxNO_BORDER | NC_RIGHT | NC_ROTATE | NC_EXPANDABLE </style> + <focus>0</focus> + <highlight>1</highlight> + <tabstyle>NC_GRADIENT_HORIZONTAL | NC_GRADIENT_SELECTION</tabstyle> + <color1>#DCDCDC</color1> + <color2>#F5F5F5</color2> + <selectedcolor1>#C4DADB</selectedcolor1> + <selectedcolor2>#FFFFFF</selectedcolor2> + <custompagecolor>#C0C0C0</custompagecolor> + </object> + </object> + </object> + </object> + </resource> + + @undocumented: CanHandle, DoCreateResource, SetupWindow + """ + def __init__(self): + """ + Create a NotebookCtrlWindowHandler instance. + """ + xrc.XmlResourceHandler.__init__(self) + # Specify the window styles recognized by objects of this type + self.AddStyle("wxNO_BORDER", wx.NO_BORDER) + self.AddStyle("wxTAB_TRAVERSAL", wx.TAB_TRAVERSAL) + self.AddStyle("NC_TOP", NC_TOP) + self.AddStyle("NC_BOTTOM", NC_BOTTOM) + self.AddStyle("NC_LEFT", NC_LEFT) + self.AddStyle("NC_RIGHT", NC_RIGHT) + self.AddStyle("NC_FIXED_WIDTH", NC_FIXED_WIDTH) + self.AddStyle("NC_ROTATE", NC_ROTATE) + self.AddStyle("NC_EXPANDABLE", NC_EXPANDABLE) + # More styles, used in the tabstyle parameter + self.AddStyle("NC_AQUA_LIGHT", NC_AQUA_LIGHT) + self.AddStyle("NC_AQUA_DARK", NC_AQUA_DARK) + self.AddStyle("NC_AQUA", NC_AQUA) + self.AddStyle("NC_METAL", NC_METAL) + self.AddStyle("NC_SILVER", NC_SILVER) + self.AddStyle("NC_KDE", NC_KDE) + self.AddStyle("NC_GRADIENT_VERTICAL", NC_GRADIENT_VERTICAL) + self.AddStyle("NC_GRADIENT_HORIZONTAL", NC_GRADIENT_HORIZONTAL) + self.AddStyle("NC_GRADIENT_SELECTION", NC_GRADIENT_SELECTION) + + self.AddWindowStyles() + + def _CreateResourceInstance(self, parent, id, position, size, style, name): + window = NotebookCtrl(parent, id, position, size=size, style=style, name=name) + return window + + def _GetColorParamValue(self, paramName, defaultValue=wx.WHITE): + paramValue = self.GetParamValue(paramName) + if paramValue: + return self.GetColour(paramName) + else: + return defaultValue + + def _GetCustomPage(self, window): + customPage = wx.Window(window, -1, style = wx.STATIC_BORDER) + customPage.SetBackgroundColour(self._GetColorParamValue('custompagecolor')) + return customPage + + def _GetIntParamValue(self, paramName, defaultValue=0): + paramValue = self.GetParamValue(paramName) + if paramValue: + return int(paramValue) + else: + return defaultValue + + def _GetTabTheme(self): + tabstyle = self.GetStyle("tabstyle") + + if tabstyle: + result = ThemeStyle() + if tabstyle & NC_GRADIENT_VERTICAL or tabstyle & NC_GRADIENT_HORIZONTAL: + result.EnableGradientStyle(True, tabstyle) + result.SetFirstGradientColour(self._GetColorParamValue('color1')) + result.SetSecondGradientColour(self._GetColorParamValue('color2')) + result.SetFirstGradientColourSelected(self._GetColorParamValue('selectedcolor1')) + result.SetSecondGradientColourSelected(self._GetColorParamValue('selectedcolor2')) + elif tabstyle & NC_AQUA_LIGHT or tabstyle & NC_AQUA_DARK: + result.EnableAquaTheme(True, tabstyle & NC_AQUA_LIGHT and 2 or 1) + elif tabstyle & NC_METAL: + result.EnableMetalTheme(True) + elif tabstyle & NC_KDE: + result.EnableKDETheme(True) + elif tabstyle & NC_SILVER: + result.EnableSilverTheme(True) + else: + result = GetDefaultTabStyle() + return result + + # This method and the next one are required for XmlResourceHandlers + def CanHandle(self, node): + return self.IsOfClass(node, "NotebookCtrl") + + def DoCreateResource(self): + # NOTE: wxWindows can be created in either a single-phase or + # in a two-phase way. Single phase is what you normally do, + # and two-phase creates the instnace first, and then later + # creates the actual window when the Create method is called. + # (In wxPython the first phase is done using the wxPre* + # function, for example, wxPreFrame, wxPrePanel, etc.) + # + # wxXmlResource supports either method, a premade instance can + # be created and populated by xrc using the appropriate + # LoadOn* method (such as LoadOnPanel) or xrc can create the + # instance too, using the Load* method. However this makes + # the handlers a bit more complex. If you can be sure that a + # particular class will never be loaded using a pre-existing + # instance, then you can make the handle much simpler. I'll + # show both methods below. + + # The simple method assumes that there is no existing + # instance. Be sure of that with an assert. + assert self.GetInstance() is None + + # Now create the object + window = self._CreateResourceInstance(self.GetParentAsWindow(), + self.GetID(), + self.GetPosition(), + self.GetSize(), + self.GetStyle("style", NC_DEFAULT_STYLE), + self.GetName()) + + # Set standard window attributes + self.SetupWindow(window) + # Create any child windows of this node + self.CreateChildren(window) + + return window + + def SetupWindow(self, window): + super(NotebookCtrlWindowHandler, self).SetupWindow(window) + window.ApplyTabTheme(self._GetTabTheme()) + window.SetHighlightSelection(self._GetIntParamValue("highlight", 0) != 0) + window.SetUseFocusIndicator(self._GetIntParamValue("focus", 1) != 0) + window.SetCustomPage(self._GetCustomPage(window))