view orpg/tools/NotebookCtrl.py @ 248:1df5912db00c beta tip

Traipse Beta 'OpenRPG' {101205-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Closed) New Features: New to Map, can re-order Grid, Miniatures, and Whiteboard layer draw order New to Server GUI, can now clear log New Earthdawn Dieroller New IronClaw roller, sheet, and image New ShapeShifter PC Sheet Updates: Update to Warhammer PC Sheet. Rollers set as macros. Should work with little maintanence. Update to Browser Server window. Display rooms with ' " & cleaner Update to Server. Handles ' " & cleaner Update to Dieroller. Cleaner, more effecient expression system Update to Hidden Die plugin, allows for non standard dice rolls Update to location.py, allows for more portable references when starting Traipse Update to the Features node Fixes: Fix to InterParse that was causing an Infernal Loop with Namespace Internal Fix to XML data, removed old Minidom and switched to Element Tree Fix to Server that was causing eternal attempt to find a Server ID, in Register Rooms thread Fix to Server, removing wxPython dependencies where not needed Fix to metaservers.xml file not being created Fix to Single and Double quotes in Whiteboard text Fix to Background images not showing when using the Image Server Fix to Duplicate chat names appearing Fix to Server GUI's logging output Fix to FNB.COLORFUL_TABS bug Fix to Gametree for XSLT Sheets Fix to Gametree for locating gametree files Fix to Send to Chat from Gametree Fix to Gametree, renaming and remapping operates correctly Fix to aliaslib, prevents error caused when SafeHTML is sent None
author sirebral
date Sun, 05 Dec 2010 10:53:30 -0600
parents 4385a7d0efd1
children
line wrap: on
line source

# --------------------------------------------------------------------------- #
# 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))