remove numpy + matrix
[iramuteq] / aui / auibook.py
diff --git a/aui/auibook.py b/aui/auibook.py
new file mode 100644 (file)
index 0000000..62ae00c
--- /dev/null
@@ -0,0 +1,5737 @@
+"""
+auibook contains a notebook control which implements many features common in
+applications with dockable panes. Specifically, L{AuiNotebook} implements functionality
+which allows the user to rearrange tab order via drag-and-drop, split the tab window
+into many different splitter configurations, and toggle through different themes to
+customize the control's look and feel.
+
+An effort has been made to try to maintain an API as similar to that of `wx.Notebook`.
+
+The default theme that is used is L{AuiDefaultTabArt}, which provides a modern, glossy
+look and feel. The theme can be changed by calling L{AuiNotebook.SetArtProvider}.
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+import types
+import datetime
+
+from wx.lib.expando import ExpandoTextCtrl
+
+import framemanager
+import tabart as TA
+
+from aui_utilities import LightColour, MakeDisabledBitmap, TabDragImage
+from aui_utilities import TakeScreenShot, RescaleScreenShot
+
+from aui_constants import *
+
+# AuiNotebook events
+wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BUTTON = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_END_DRAG = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK = wx.NewEventType()
+
+# Define a new event for a drag cancelled
+wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG = wx.NewEventType()
+
+# Define events for editing a tab label
+wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT = wx.NewEventType()
+wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT = wx.NewEventType()
+
+# Create event binders
+EVT_AUINOTEBOOK_PAGE_CLOSE = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 1)
+""" A tab in `AuiNotebook` is being closed. Can be vetoed by calling `Veto()`. """
+EVT_AUINOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 1)
+""" A tab in `AuiNotebook` has been closed. """
+EVT_AUINOTEBOOK_PAGE_CHANGED = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 1)
+""" The page selection was changed. """
+EVT_AUINOTEBOOK_PAGE_CHANGING = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 1)
+""" The page selection is being changed. """
+EVT_AUINOTEBOOK_BUTTON = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 1)
+""" The user clicked on a button in the `AuiNotebook` tab area. """
+EVT_AUINOTEBOOK_BEGIN_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 1)
+""" A drag-and-drop operation on a notebook tab has started. """
+EVT_AUINOTEBOOK_END_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 1)
+""" A drag-and-drop operation on a notebook tab has finished. """
+EVT_AUINOTEBOOK_DRAG_MOTION = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 1)
+""" A drag-and-drop operation on a notebook tab is ongoing. """
+EVT_AUINOTEBOOK_ALLOW_DND = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 1)
+""" Fires an event asking if it is OK to drag and drop a tab. """
+EVT_AUINOTEBOOK_DRAG_DONE = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 1)
+""" A drag-and-drop operation on a notebook tab has finished. """
+EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 1)
+""" The user clicked with the middle mouse button on a tab. """
+EVT_AUINOTEBOOK_TAB_MIDDLE_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 1)
+""" The user clicked with the middle mouse button on a tab. """
+EVT_AUINOTEBOOK_TAB_RIGHT_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 1)
+""" The user clicked with the right mouse button on a tab. """
+EVT_AUINOTEBOOK_TAB_RIGHT_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 1)
+""" The user clicked with the right mouse button on a tab. """
+EVT_AUINOTEBOOK_BG_MIDDLE_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN, 1)
+""" The user middle-clicked in the tab area but not over a tab or a button. """
+EVT_AUINOTEBOOK_BG_MIDDLE_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP, 1)
+""" The user middle-clicked in the tab area but not over a tab or a button. """
+EVT_AUINOTEBOOK_BG_RIGHT_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN, 1)
+""" The user right-clicked in the tab area but not over a tab or a button. """
+EVT_AUINOTEBOOK_BG_RIGHT_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP, 1)
+""" The user right-clicked in the tab area but not over a tab or a button. """
+EVT_AUINOTEBOOK_BG_DCLICK = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 1)
+""" The user left-clicked on the tab area not occupied by `AuiNotebook` tabs. """
+EVT_AUINOTEBOOK_CANCEL_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG, 1)
+""" A drag and drop operation has been cancelled. """
+EVT_AUINOTEBOOK_TAB_DCLICK = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, 1)
+""" The user double-clicked with the left mouse button on a tab. """
+EVT_AUINOTEBOOK_BEGIN_LABEL_EDIT = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT, 1)
+""" The user double-clicked with the left mouse button on a tab which text is editable. """
+EVT_AUINOTEBOOK_END_LABEL_EDIT = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, 1)
+""" The user finished editing a tab label. """
+
+
+# -----------------------------------------------------------------------------
+# Auxiliary class: TabTextCtrl
+# This is the temporary ExpandoTextCtrl created when you edit the text of a tab
+# -----------------------------------------------------------------------------
+
+class TabTextCtrl(ExpandoTextCtrl):
+    """ Control used for in-place edit. """
+
+    def __init__(self, owner, tab, page_index):
+        """
+        Default class constructor.
+        For internal use: do not call it in your code!
+
+        :param `owner`: the L{AuiTabCtrl} owning the tab;
+        :param `tab`: the actual L{AuiNotebookPage} tab;
+        :param `page_index`: the L{AuiNotebook} page index for the tab.
+        """
+
+        self._owner = owner
+        self._tabEdited = tab
+        self._pageIndex = page_index
+        self._startValue = tab.caption
+        self._finished = False
+        self._aboutToFinish = False
+        self._currentValue = self._startValue
+
+        x, y, w, h = self._tabEdited.rect
+
+        wnd = self._tabEdited.control
+        if wnd:
+            x += wnd.GetSize()[0] + 2
+            h = 0
+
+        image_h = 0
+        image_w = 0
+
+        image = tab.bitmap
+
+        if image.IsOk():
+            image_w, image_h = image.GetWidth(), image.GetHeight()
+            image_w += 6
+
+        dc = wx.ClientDC(self._owner)
+        h = max(image_h, dc.GetMultiLineTextExtent(tab.caption)[1])
+        h = h + 2
+
+        # FIXME: what are all these hardcoded 4, 8 and 11s really?
+        x += image_w
+        w -= image_w + 4
+
+        y = (self._tabEdited.rect.height - h)/2 + 1
+
+        expandoStyle = wx.WANTS_CHARS
+        if wx.Platform in ["__WXGTK__", "__WXMAC__"]:
+            expandoStyle |= wx.SIMPLE_BORDER
+            xSize, ySize = w + 2, h
+        else:
+            expandoStyle |= wx.SUNKEN_BORDER
+            xSize, ySize = w + 2, h+2
+
+        ExpandoTextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue,
+                                 wx.Point(x, y), wx.Size(xSize, ySize),
+                                 expandoStyle)
+
+        if wx.Platform == "__WXMAC__":
+            self.SetFont(owner.GetFont())
+            bs = self.GetBestSize()
+            self.SetSize((-1, bs.height))
+
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
+
+
+    def AcceptChanges(self):
+        """ Accepts/refuses the changes made by the user. """
+
+        value = self.GetValue()
+        notebook = self._owner.GetParent()
+
+        if value == self._startValue:
+            # nothing changed, always accept
+            # when an item remains unchanged, the owner
+            # needs to be notified that the user decided
+            # not to change the tree item label, and that
+            # the edit has been cancelled
+            notebook.OnRenameCancelled(self._pageIndex)
+            return True
+
+        if not notebook.OnRenameAccept(self._pageIndex, value):
+            # vetoed by the user
+            return False
+
+        # accepted, do rename the item
+        notebook.SetPageText(self._pageIndex, value)
+
+        return True
+
+
+    def Finish(self):
+        """ Finish editing. """
+
+        if not self._finished:
+
+            notebook = self._owner.GetParent()
+
+            self._finished = True
+            self._owner.SetFocus()
+            notebook.ResetTextControl()
+
+
+    def OnChar(self, event):
+        """
+        Handles the ``wx.EVT_CHAR`` event for L{TabTextCtrl}.
+
+        :param `event`: a `wx.KeyEvent` event to be processed.
+        """
+
+        keycode = event.GetKeyCode()
+        shiftDown = event.ShiftDown()
+
+        if keycode == wx.WXK_RETURN:
+            if shiftDown and self._tabEdited.IsMultiline():
+                event.Skip()
+            else:
+                self._aboutToFinish = True
+                self.SetValue(self._currentValue)
+                # Notify the owner about the changes
+                self.AcceptChanges()
+                # Even if vetoed, close the control (consistent with MSW)
+                wx.CallAfter(self.Finish)
+
+        elif keycode == wx.WXK_ESCAPE:
+            self.StopEditing()
+
+        else:
+            event.Skip()
+
+
+    def OnKeyUp(self, event):
+        """
+        Handles the ``wx.EVT_KEY_UP`` event for L{TabTextCtrl}.
+
+        :param `event`: a `wx.KeyEvent` event to be processed.
+        """
+
+        if not self._finished:
+
+            # auto-grow the textctrl:
+            mySize = self.GetSize()
+
+            dc = wx.ClientDC(self)
+            sx, sy, dummy = dc.GetMultiLineTextExtent(self.GetValue() + "M")
+
+            self.SetSize((sx, -1))
+            self._currentValue = self.GetValue()
+
+        event.Skip()
+
+
+    def OnKillFocus(self, event):
+        """
+        Handles the ``wx.EVT_KILL_FOCUS`` event for L{TabTextCtrl}.
+
+        :param `event`: a `wx.FocusEvent` event to be processed.
+        """
+
+        if not self._finished and not self._aboutToFinish:
+
+            # We must finish regardless of success, otherwise we'll get
+            # focus problems:
+            if not self.AcceptChanges():
+                self._owner.GetParent().OnRenameCancelled(self._pageIndex)
+
+        # We must let the native text control handle focus, too, otherwise
+        # it could have problems with the cursor (e.g., in wxGTK).
+        event.Skip()
+        wx.CallAfter(self._owner.GetParent().ResetTextControl)
+
+
+    def StopEditing(self):
+        """ Suddenly stops the editing. """
+
+        self._owner.GetParent().OnRenameCancelled(self._pageIndex)
+        self.Finish()
+
+
+    def item(self):
+        """ Returns the item currently edited. """
+
+        return self._tabEdited
+
+
+# ----------------------------------------------------------------------
+
+class AuiNotebookPage(object):
+    """
+    A simple class which holds information about tab captions, bitmaps and
+    colours.
+    """
+
+    def __init__(self):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+        """
+
+        self.window = None              # page's associated window
+        self.caption = ""               # caption displayed on the tab
+        self.bitmap = wx.NullBitmap     # tab's bitmap
+        self.dis_bitmap = wx.NullBitmap # tab's disabled bitmap
+        self.rect = wx.Rect()           # tab's hit rectangle
+        self.active = False             # True if the page is currently active
+        self.enabled = True             # True if the page is currently enabled
+        self.hasCloseButton = True      # True if the page has a close button using the style
+                                        # AUI_NB_CLOSE_ON_ALL_TABS
+        self.control = None             # A control can now be inside a tab
+        self.renamable = False          # If True, a tab can be renamed by a left double-click
+
+        self.text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT)
+
+        self.access_time = datetime.datetime.now() # Last time this page was selected
+
+
+    def IsMultiline(self):
+        """ Returns whether the tab contains multiline text. """
+
+        return "\n" in self.caption
+
+
+# ----------------------------------------------------------------------
+
+class AuiTabContainerButton(object):
+    """
+    A simple class which holds information about tab buttons and their state.
+    """
+
+    def __init__(self):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+        """
+
+        self.id = -1                                      # button's id
+        self.cur_state = AUI_BUTTON_STATE_NORMAL          # current state (normal, hover, pressed, etc.)
+        self.location = wx.LEFT                           # buttons location (wxLEFT, wxRIGHT, or wxCENTER)
+        self.bitmap = wx.NullBitmap                       # button's hover bitmap
+        self.dis_bitmap = wx.NullBitmap                   # button's disabled bitmap
+        self.rect = wx.Rect()                             # button's hit rectangle
+
+
+# ----------------------------------------------------------------------
+
+class CommandNotebookEvent(wx.PyCommandEvent):
+    """ A specialized command event class for events sent by L{AuiNotebook} . """
+
+    def __init__(self, command_type=None, win_id=0):
+        """
+        Default class constructor.
+
+        :param `command_type`: the event kind or an instance of `wx.PyCommandEvent`.
+        :param `win_id`: the window identification number.
+        """
+
+        if type(command_type) == types.IntType:
+            wx.PyCommandEvent.__init__(self, command_type, win_id)
+        else:
+            wx.PyCommandEvent.__init__(self, command_type.GetEventType(), command_type.GetId())
+
+        self.old_selection = -1
+        self.selection = -1
+        self.drag_source = None
+        self.dispatched = 0
+        self.label = ""
+        self.editCancelled = False
+
+
+    def SetSelection(self, s):
+        """
+        Sets the selection member variable.
+
+        :param `s`: the new selection.
+        """
+
+        self.selection = s
+        self._commandInt = s
+
+
+    def GetSelection(self):
+        """ Returns the currently selected page, or -1 if none was selected. """
+
+        return self.selection
+
+
+    def SetOldSelection(self, s):
+        """
+        Sets the id of the page selected before the change.
+
+        :param `s`: the old selection.
+        """
+
+        self.old_selection = s
+
+
+    def GetOldSelection(self):
+        """
+        Returns the page that was selected before the change, or -1 if none was
+        selected.
+        """
+
+        return self.old_selection
+
+
+    def SetDragSource(self, s):
+        """
+        Sets the drag and drop source.
+
+        :param `s`: the drag source.
+        """
+
+        self.drag_source = s
+
+
+    def GetDragSource(self):
+        """ Returns the drag and drop source. """
+
+        return self.drag_source
+
+
+    def SetDispatched(self, b):
+        """
+        Sets the event as dispatched (used for automatic L{AuiNotebook} ).
+
+        :param `b`: whether the event was dispatched or not.
+        """
+
+        self.dispatched = b
+
+
+    def GetDispatched(self):
+        """ Returns whether the event was dispatched (used for automatic L{AuiNotebook} ). """
+
+        return self.dispatched
+
+
+    def IsEditCancelled(self):
+        """ Returns the edit cancel flag (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only)."""
+
+        return self.editCancelled
+
+
+    def SetEditCanceled(self, editCancelled):
+        """
+        Sets the edit cancel flag (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only).
+
+        :param `editCancelled`: whether the editing action has been cancelled or not.
+        """
+
+        self.editCancelled = editCancelled
+
+
+    def GetLabel(self):
+        """Returns the label-itemtext (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only)."""
+
+        return self.label
+
+
+    def SetLabel(self, label):
+        """
+        Sets the label. Useful only for ``EVT_AUINOTEBOOK_END_LABEL_EDIT``.
+
+        :param `label`: the new label.
+        """
+
+        self.label = label
+
+
+# ----------------------------------------------------------------------
+
+class AuiNotebookEvent(CommandNotebookEvent):
+    """ A specialized command event class for events sent by L{AuiNotebook}. """
+
+    def __init__(self, command_type=None, win_id=0):
+        """
+        Default class constructor.
+
+        :param `command_type`: the event kind or an instance of `wx.PyCommandEvent`.
+        :param `win_id`: the window identification number.
+        """
+
+        CommandNotebookEvent.__init__(self, command_type, win_id)
+
+        if type(command_type) == types.IntType:
+            self.notify = wx.NotifyEvent(command_type, win_id)
+        else:
+            self.notify = wx.NotifyEvent(command_type.GetEventType(), command_type.GetId())
+
+
+    def GetNotifyEvent(self):
+        """ Returns the actual `wx.NotifyEvent`. """
+
+        return self.notify
+
+
+    def IsAllowed(self):
+        """ Returns whether the event is allowed or not. """
+
+        return self.notify.IsAllowed()
+
+
+    def Veto(self):
+        """
+        Prevents the change announced by this event from happening.
+
+        It is in general a good idea to notify the user about the reasons for
+        vetoing the change because otherwise the applications behaviour (which
+        just refuses to do what the user wants) might be quite surprising.
+        """
+
+        self.notify.Veto()
+
+
+    def Allow(self):
+        """
+        This is the opposite of L{Veto}: it explicitly allows the event to be
+        processed. For most events it is not necessary to call this method as the
+        events are allowed anyhow but some are forbidden by default (this will
+        be mentioned in the corresponding event description).
+        """
+
+        self.notify.Allow()
+
+
+# ---------------------------------------------------------------------------- #
+# Class TabNavigatorWindow
+# ---------------------------------------------------------------------------- #
+
+class TabNavigatorWindow(wx.Dialog):
+    """
+    This class is used to create a modal dialog that enables "Smart Tabbing",
+    similar to what you would get by hitting ``Alt`` + ``Tab`` on Windows.
+    """
+
+    def __init__(self, parent=None, icon=None):
+        """
+        Default class constructor. Used internally.
+
+        :param `parent`: the L{TabNavigatorWindow} parent;
+        :param `icon`: the L{TabNavigatorWindow} icon.
+        """
+
+        wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)
+
+        self._selectedItem = -1
+        self._indexMap = []
+
+        if icon is None:
+            self._bmp = Mondrian.GetBitmap()
+        else:
+            self._bmp = icon
+
+        if self._bmp.GetSize() != (16, 16):
+            img = self._bmp.ConvertToImage()
+            img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
+            self._bmp = wx.BitmapFromImage(img)
+
+        sz = wx.BoxSizer(wx.VERTICAL)
+
+        self._listBox = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, 150), [], wx.LB_SINGLE | wx.NO_BORDER)
+
+        mem_dc = wx.MemoryDC()
+        mem_dc.SelectObject(wx.EmptyBitmap(1,1))
+        font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        font.SetWeight(wx.BOLD)
+        mem_dc.SetFont(font)
+
+        panelHeight = mem_dc.GetCharHeight()
+        panelHeight += 4 # Place a spacer of 2 pixels
+
+        # Out signpost bitmap is 24 pixels
+        if panelHeight < 24:
+            panelHeight = 24
+
+        self._panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, panelHeight))
+
+        sz.Add(self._panel)
+        sz.Add(self._listBox, 1, wx.EXPAND)
+
+        self.SetSizer(sz)
+
+        # Connect events to the list box
+        self._listBox.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+        self._listBox.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
+        self._listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnItemSelected)
+
+        # Connect paint event to the panel
+        self._panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)
+        self._panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnPanelEraseBg)
+
+        self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
+        self._listBox.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
+        self.PopulateListControl(parent)
+
+        self.GetSizer().Fit(self)
+        self.GetSizer().SetSizeHints(self)
+        self.GetSizer().Layout()
+        self.Centre()
+
+        # Set focus on the list box to avoid having to click on it to change
+        # the tab selection under GTK.
+        self._listBox.SetFocus()
+
+
+    def OnKeyUp(self, event):
+        """
+        Handles the ``wx.EVT_KEY_UP`` for the L{TabNavigatorWindow}.
+
+        :param `event`: a `wx.KeyEvent` event to be processed.
+        """
+
+        if event.GetKeyCode() == wx.WXK_CONTROL:
+            self.CloseDialog()
+
+
+    def OnNavigationKey(self, event):
+        """
+        Handles the ``wx.EVT_NAVIGATION_KEY`` for the L{TabNavigatorWindow}.
+
+        :param `event`: a `wx.NavigationKeyEvent` event to be processed.
+        """
+
+        selected = self._listBox.GetSelection()
+        bk = self.GetParent()
+        maxItems = bk.GetPageCount()
+
+        if event.GetDirection():
+
+            # Select next page
+            if selected == maxItems - 1:
+                itemToSelect = 0
+            else:
+                itemToSelect = selected + 1
+
+        else:
+
+            # Previous page
+            if selected == 0:
+                itemToSelect = maxItems - 1
+            else:
+                itemToSelect = selected - 1
+
+        self._listBox.SetSelection(itemToSelect)
+
+
+    def PopulateListControl(self, book):
+        """
+        Populates the L{TabNavigatorWindow} listbox with a list of tabs.
+
+        :param `book`: the actual L{AuiNotebook}.
+        """
+        # Index of currently selected page
+        selection = book.GetSelection()
+        # Total number of pages
+        count = book.GetPageCount()
+        # List of (index, AuiNotebookPage)
+        pages = list(enumerate(book.GetTabContainer().GetPages()))
+        if book.GetAGWWindowStyleFlag() & AUI_NB_ORDER_BY_ACCESS:
+            # Sort pages using last access time. Most recently used is the
+            # first in line
+            pages.sort(
+                key = lambda element: element[1].access_time,
+                reverse = True
+            )
+        else:
+            # Manually add the current selection as first item
+            # Remaining ones are added in the next loop
+            del pages[selection]
+            self._listBox.Append(book.GetPageText(selection))
+            self._indexMap.append(selection)
+
+        for (index, page) in pages:
+            self._listBox.Append(book.GetPageText(index))
+            self._indexMap.append(index)
+
+        # Select the next entry after the current selection
+        self._listBox.SetSelection(0)
+        dummy = wx.NavigationKeyEvent()
+        dummy.SetDirection(True)
+        self.OnNavigationKey(dummy)
+
+
+    def OnItemSelected(self, event):
+        """
+        Handles the ``wx.EVT_LISTBOX_DCLICK`` event for the wx.ListBox inside L{TabNavigatorWindow}.
+
+        :param `event`: a `wx.ListEvent` event to be processed.
+        """
+
+        self.CloseDialog()
+
+
+    def CloseDialog(self):
+        """ Closes the L{TabNavigatorWindow} dialog, setting selection in L{AuiNotebook}. """
+
+        bk = self.GetParent()
+        self._selectedItem = self._listBox.GetSelection()
+        self.EndModal(wx.ID_OK)
+
+
+    def GetSelectedPage(self):
+        """ Gets the page index that was selected when the dialog was closed. """
+
+        return self._indexMap[self._selectedItem]
+
+
+    def OnPanelPaint(self, event):
+        """
+        Handles the ``wx.EVT_PAINT`` event for L{TabNavigatorWindow} top panel.
+
+        :param `event`: a `wx.PaintEvent` event to be processed.
+        """
+
+        dc = wx.PaintDC(self._panel)
+        rect = self._panel.GetClientRect()
+
+        bmp = wx.EmptyBitmap(rect.width, rect.height)
+
+        mem_dc = wx.MemoryDC()
+        mem_dc.SelectObject(bmp)
+
+        endColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
+        startColour = LightColour(endColour, 50)
+        mem_dc.GradientFillLinear(rect, startColour, endColour, wx.SOUTH)
+
+        # Draw the caption title and place the bitmap
+        # get the bitmap optimal position, and draw it
+        bmpPt, txtPt = wx.Point(), wx.Point()
+        bmpPt.y = (rect.height - self._bmp.GetHeight())/2
+        bmpPt.x = 3
+        mem_dc.DrawBitmap(self._bmp, bmpPt.x, bmpPt.y, True)
+
+        # get the text position, and draw it
+        font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        font.SetWeight(wx.BOLD)
+        mem_dc.SetFont(font)
+        fontHeight = mem_dc.GetCharHeight()
+
+        txtPt.x = bmpPt.x + self._bmp.GetWidth() + 4
+        txtPt.y = (rect.height - fontHeight)/2
+        mem_dc.SetTextForeground(wx.WHITE)
+        mem_dc.DrawText("Opened tabs:", txtPt.x, txtPt.y)
+        mem_dc.SelectObject(wx.NullBitmap)
+
+        dc.DrawBitmap(bmp, 0, 0)
+
+
+    def OnPanelEraseBg(self, event):
+        """
+        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{TabNavigatorWindow} top panel.
+
+        :param `event`: a `wx.EraseEvent` event to be processed.
+
+        :note: This is intentionally empty, to reduce flicker.
+        """
+
+        pass
+
+
+# ----------------------------------------------------------------------
+# -- AuiTabContainer class implementation --
+
+class AuiTabContainer(object):
+    """
+    AuiTabContainer is a class which contains information about each
+    tab. It also can render an entire tab control to a specified DC.
+    It's not a window class itself, because this code will be used by
+    the L{AuiManager}, where it is disadvantageous to have separate
+    windows for each tab control in the case of "docked tabs".
+
+    A derived class, L{AuiTabCtrl}, is an actual `wx.Window`-derived window
+    which can be used as a tab control in the normal sense.
+    """
+
+    def __init__(self, auiNotebook):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+
+        :param `auiNotebook`: the parent L{AuiNotebook} window.        
+        """
+
+        self._tab_offset = 0
+        self._agwFlags = 0
+        self._art = TA.AuiDefaultTabArt()
+
+        self._buttons = []
+        self._pages = []
+        self._tab_close_buttons = []
+
+        self._rect = wx.Rect()
+        self._auiNotebook = auiNotebook
+
+        self.AddButton(AUI_BUTTON_LEFT, wx.LEFT)
+        self.AddButton(AUI_BUTTON_RIGHT, wx.RIGHT)
+        self.AddButton(AUI_BUTTON_WINDOWLIST, wx.RIGHT)
+        self.AddButton(AUI_BUTTON_CLOSE, wx.RIGHT)
+
+
+    def SetArtProvider(self, art):
+        """
+        Instructs L{AuiTabContainer} to use art provider specified by parameter `art`
+        for all drawing calls. This allows plugable look-and-feel features.
+
+        :param `art`: an art provider.
+
+        :note: The previous art provider object, if any, will be deleted by L{AuiTabContainer}.
+        """
+
+        del self._art
+        self._art = art
+
+        if self._art:
+            self._art.SetAGWFlags(self._agwFlags)
+
+
+    def GetArtProvider(self):
+        """ Returns the current art provider being used. """
+
+        return self._art
+
+
+    def SetAGWFlags(self, agwFlags):
+        """
+        Sets the tab art flags.
+
+        :param `agwFlags`: a combination of the following values:
+
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
+         ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet
+         ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet
+         ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
+         ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
+         ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
+         ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
+         ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
+         ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
+         ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
+         ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
+         ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
+         ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
+         ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close L{AuiNotebook} tabs by mouse middle button click
+         ``AUI_NB_SUB_NOTEBOOK``              This style is used by L{AuiManager} to create automatic AuiNotebooks
+         ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
+         ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
+         ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
+         ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
+         ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
+         ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
+         ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
+         ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
+         ==================================== ==================================
+
+        :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
+
+        """
+
+        self._agwFlags = agwFlags
+
+        # check for new close button settings
+        self.RemoveButton(AUI_BUTTON_LEFT)
+        self.RemoveButton(AUI_BUTTON_RIGHT)
+        self.RemoveButton(AUI_BUTTON_WINDOWLIST)
+        self.RemoveButton(AUI_BUTTON_CLOSE)
+
+        if agwFlags & AUI_NB_SCROLL_BUTTONS:
+            self.AddButton(AUI_BUTTON_LEFT, wx.LEFT)
+            self.AddButton(AUI_BUTTON_RIGHT, wx.RIGHT)
+
+        if agwFlags & AUI_NB_WINDOWLIST_BUTTON:
+            self.AddButton(AUI_BUTTON_WINDOWLIST, wx.RIGHT)
+
+        if agwFlags & AUI_NB_CLOSE_BUTTON:
+            self.AddButton(AUI_BUTTON_CLOSE, wx.RIGHT)
+
+        if self._art:
+            self._art.SetAGWFlags(self._agwFlags)
+
+
+    def GetAGWFlags(self):
+        """
+        Returns the tab art flags.
+
+        See L{SetAGWFlags} for a list of possible return values.
+
+        :see: L{SetAGWFlags}
+        """
+
+        return self._agwFlags
+
+
+    def SetNormalFont(self, font):
+        """
+        Sets the normal font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._art.SetNormalFont(font)
+
+
+    def SetSelectedFont(self, font):
+        """
+        Sets the selected tab font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._art.SetSelectedFont(font)
+
+
+    def SetMeasuringFont(self, font):
+        """
+        Sets the font for calculating text measurements.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._art.SetMeasuringFont(font)
+
+
+    def SetTabRect(self, rect):
+        """
+        Sets the tab area rectangle.
+
+        :param `rect`: an instance of `wx.Rect`, specifying the available area for L{AuiTabContainer}.
+        """
+
+        self._rect = rect
+
+        if self._art:
+            minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
+            self._art.SetSizingInfo(rect.GetSize(), len(self._pages), minMaxTabWidth)
+
+
+    def AddPage(self, page, info):
+        """
+        Adds a page to the tab control.
+
+        :param `page`: the window associated with this tab;
+        :param `info`: an instance of L{AuiNotebookPage}.
+        """
+
+        page_info = info
+        page_info.window = page
+
+        self._pages.append(page_info)
+
+        # let the art provider know how many pages we have
+        if self._art:
+            minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
+            self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
+
+        return True
+
+
+    def InsertPage(self, page, info, idx):
+        """
+        Inserts a page in the tab control in the position specified by `idx`.
+
+        :param `page`: the window associated with this tab;
+        :param `info`: an instance of L{AuiNotebookPage};
+        :param `idx`: the page insertion index.
+        """
+
+        page_info = info
+        page_info.window = page
+
+        if idx >= len(self._pages):
+            self._pages.append(page_info)
+        else:
+            self._pages.insert(idx, page_info)
+
+        # let the art provider know how many pages we have
+        if self._art:
+            minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
+            self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
+
+        return True
+
+
+    def MovePage(self, page, new_idx):
+        """
+        Moves a page in a new position specified by `new_idx`.
+
+        :param `page`: the window associated with this tab;
+        :param `new_idx`: the new page position.
+        """
+
+        idx = self.GetIdxFromWindow(page)
+        if idx == -1:
+            return False
+
+        # get page entry, make a copy of it
+        p = self.GetPage(idx)
+
+        # remove old page entry
+        self.RemovePage(page)
+
+        # insert page where it should be
+        self.InsertPage(page, p, new_idx)
+
+        return True
+
+
+    def RemovePage(self, wnd):
+        """
+        Removes a page from the tab control.
+
+        :param `wnd`: an instance of `wx.Window`, a window associated with this tab.
+        """
+
+        minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
+
+        for page in self._pages:
+            if page.window == wnd:
+                self._pages.remove(page)
+                self._tab_offset = min(self._tab_offset, len(self._pages) - 1)
+
+                # let the art provider know how many pages we have
+                if self._art:
+                    self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
+
+                return True
+
+        return False
+
+
+    def SetActivePage(self, wndOrInt):
+        """
+        Sets the L{AuiTabContainer} active page.
+
+        :param `wndOrInt`: an instance of `wx.Window` or an integer specifying a tab index.
+        """
+
+        if type(wndOrInt) == types.IntType:
+
+            if wndOrInt >= len(self._pages):
+                return False
+
+            wnd = self._pages[wndOrInt].window
+
+        else:
+            wnd = wndOrInt
+
+        found = False
+
+        for indx, page in enumerate(self._pages):
+            if page.window == wnd:
+                page.active = True
+                found = True
+            else:
+                page.active = False
+
+        return found
+
+
+    def SetNoneActive(self):
+        """ Sets all the tabs as inactive (non-selected). """
+
+        for page in self._pages:
+            page.active = False
+
+
+    def GetActivePage(self):
+        """ Returns the current selected tab or ``wx.NOT_FOUND`` if none is selected. """
+
+        for indx, page in enumerate(self._pages):
+            if page.active:
+                return indx
+
+        return wx.NOT_FOUND
+
+
+    def GetWindowFromIdx(self, idx):
+        """
+        Returns the window associated with the tab with index `idx`.
+
+        :param `idx`: the tab index.
+        """
+
+        if idx >= len(self._pages):
+            return None
+
+        return self._pages[idx].window
+
+
+    def GetIdxFromWindow(self, wnd):
+        """
+        Returns the tab index based on the window `wnd` associated with it.
+
+        :param `wnd`: an instance of `wx.Window`.
+        """
+
+        for indx, page in enumerate(self._pages):
+            if page.window == wnd:
+                return indx
+
+        return wx.NOT_FOUND
+
+
+    def GetPage(self, idx):
+        """
+        Returns the page specified by the given index.
+
+        :param `idx`: the tab index.
+        """
+
+        if idx < 0 or idx >= len(self._pages):
+            raise Exception("Invalid Page index")
+
+        return self._pages[idx]
+
+
+    def GetPages(self):
+        """ Returns a list of all the pages in this L{AuiTabContainer}. """
+
+        return self._pages
+
+
+    def GetPageCount(self):
+        """ Returns the number of pages in the L{AuiTabContainer}. """
+
+        return len(self._pages)
+
+
+    def GetEnabled(self, idx):
+        """
+        Returns whether a tab is enabled or not.
+
+        :param `idx`: the tab index.
+        """
+
+        if idx < 0 or idx >= len(self._pages):
+            return False
+
+        return self._pages[idx].enabled
+
+
+    def EnableTab(self, idx, enable=True):
+        """
+        Enables/disables a tab in the L{AuiTabContainer}.
+
+        :param `idx`: the tab index;
+        :param `enable`: ``True`` to enable a tab, ``False`` to disable it.
+        """
+
+        if idx < 0 or idx >= len(self._pages):
+            raise Exception("Invalid Page index")
+
+        self._pages[idx].enabled = enable
+        wnd = self.GetWindowFromIdx(idx)
+        wnd.Enable(enable)
+
+
+    def AddButton(self, id, location, normal_bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap):
+        """
+        Adds a button in the tab area.
+
+        :param `id`: the button identifier. This can be one of the following:
+
+         ==============================  =================================
+         Button Identifier               Description
+         ==============================  =================================
+         ``AUI_BUTTON_CLOSE``            Shows a close button on the tab area
+         ``AUI_BUTTON_WINDOWLIST``       Shows a window list button on the tab area
+         ``AUI_BUTTON_LEFT``             Shows a left button on the tab area
+         ``AUI_BUTTON_RIGHT``            Shows a right button on the tab area
+         ==============================  =================================
+
+        :param `location`: the button location. Can be ``wx.LEFT`` or ``wx.RIGHT``;
+        :param `normal_bitmap`: the bitmap for an enabled tab;
+        :param `disabled_bitmap`: the bitmap for a disabled tab.
+        """
+
+        button = AuiTabContainerButton()
+        button.id = id
+        button.bitmap = normal_bitmap
+        button.dis_bitmap = disabled_bitmap
+        button.location = location
+        button.cur_state = AUI_BUTTON_STATE_NORMAL
+
+        self._buttons.append(button)
+
+
+    def RemoveButton(self, id):
+        """
+        Removes a button from the tab area.
+
+        :param `id`: the button identifier. See L{AddButton} for a list of button identifiers.
+
+        :see: L{AddButton}
+        """
+
+        for button in self._buttons:
+            if button.id == id:
+                self._buttons.remove(button)
+                return
+
+
+    def GetTabOffset(self):
+        """ Returns the tab offset. """
+
+        return self._tab_offset
+
+
+    def SetTabOffset(self, offset):
+        """
+        Sets the tab offset.
+
+        :param `offset`: the tab offset.
+        """
+
+        self._tab_offset = offset
+
+
+    def MinimizeTabOffset(self, dc, wnd, max_width):
+        """
+        Minimize `self._tab_offset` to fit as many tabs as possible in the available space.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: an instance of `wx.Window`;
+        :param `max_width`: the maximum available width for the tabs.
+        """
+
+        total_width = 0
+
+        for i, page in reversed(list(enumerate(self._pages))):
+
+            tab_button = self._tab_close_buttons[i]
+            (tab_width, tab_height), x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active, tab_button.cur_state, page.control)
+            total_width += tab_width
+
+            if total_width > max_width:
+
+                tab_offset = i + 1
+
+                if tab_offset < self._tab_offset and tab_offset < len(self._pages):
+                    self._tab_offset = tab_offset
+
+                break
+
+        if i == 0:
+            self._tab_offset = 0
+
+
+    def Render(self, raw_dc, wnd):
+        """
+        Renders the tab catalog to the specified `wx.DC`.
+
+        It is a virtual function and can be overridden to provide custom drawing
+        capabilities.
+
+        :param `raw_dc`: a `wx.DC` device context;
+        :param `wnd`: an instance of `wx.Window`.
+        """
+
+        if not raw_dc or not raw_dc.IsOk():
+            return
+
+        dc = wx.MemoryDC()
+
+        # use the same layout direction as the window DC uses to ensure that the
+        # text is rendered correctly
+        dc.SetLayoutDirection(raw_dc.GetLayoutDirection())
+
+        page_count = len(self._pages)
+        button_count = len(self._buttons)
+
+        # create off-screen bitmap
+        bmp = wx.EmptyBitmap(self._rect.GetWidth(), self._rect.GetHeight())
+        dc.SelectObject(bmp)
+
+        if not dc.IsOk():
+            return
+
+        # find out if size of tabs is larger than can be
+        # afforded on screen
+        total_width = visible_width = 0
+
+        for i in xrange(page_count):
+            page = self._pages[i]
+
+            # determine if a close button is on this tab
+            close_button = False
+            if (self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS and page.hasCloseButton) or \
+               (self._agwFlags & AUI_NB_CLOSE_ON_ACTIVE_TAB and page.active and page.hasCloseButton):
+
+                close_button = True
+
+            control = page.control
+            if control:
+                try:
+                    control.GetSize()
+                except wx.PyDeadObjectError:
+                    page.control = None
+
+            size, x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
+                                                  (close_button and [AUI_BUTTON_STATE_NORMAL] or \
+                                                   [AUI_BUTTON_STATE_HIDDEN])[0], page.control)
+
+            if i+1 < page_count:
+                total_width += x_extent
+            else:
+                total_width += size[0]
+
+            if i >= self._tab_offset:
+                if i+1 < page_count:
+                    visible_width += x_extent
+                else:
+                    visible_width += size[0]
+
+        if total_width > self._rect.GetWidth() or self._tab_offset != 0:
+
+            # show left/right buttons
+            for button in self._buttons:
+                if button.id == AUI_BUTTON_LEFT or \
+                   button.id == AUI_BUTTON_RIGHT:
+
+                    button.cur_state &= ~AUI_BUTTON_STATE_HIDDEN
+
+        else:
+
+            # hide left/right buttons
+            for button in self._buttons:
+                if button.id == AUI_BUTTON_LEFT or \
+                   button.id == AUI_BUTTON_RIGHT:
+
+                    button.cur_state |= AUI_BUTTON_STATE_HIDDEN
+
+        # determine whether left button should be enabled
+        for button in self._buttons:
+            if button.id == AUI_BUTTON_LEFT:
+                if self._tab_offset == 0:
+                    button.cur_state |= AUI_BUTTON_STATE_DISABLED
+                else:
+                    button.cur_state &= ~AUI_BUTTON_STATE_DISABLED
+
+            if button.id == AUI_BUTTON_RIGHT:
+                if visible_width < self._rect.GetWidth() - 16*button_count:
+                    button.cur_state |= AUI_BUTTON_STATE_DISABLED
+                else:
+                    button.cur_state &= ~AUI_BUTTON_STATE_DISABLED
+
+        # draw background
+        self._art.DrawBackground(dc, wnd, self._rect)
+
+        # draw buttons
+        left_buttons_width = 0
+        right_buttons_width = 0
+
+        # draw the buttons on the right side
+        offset = self._rect.x + self._rect.width
+
+        for i in xrange(button_count):
+            button = self._buttons[button_count - i - 1]
+
+            if button.location != wx.RIGHT:
+                continue
+            if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
+                continue
+
+            button_rect = wx.Rect(*self._rect)
+            button_rect.SetY(1)
+            button_rect.SetWidth(offset)
+
+            button.rect = self._art.DrawButton(dc, wnd, button_rect, button, wx.RIGHT)
+
+            offset -= button.rect.GetWidth()
+            right_buttons_width += button.rect.GetWidth()
+
+        offset = 0
+
+        # draw the buttons on the left side
+        for i in xrange(button_count):
+            button = self._buttons[button_count - i - 1]
+
+            if button.location != wx.LEFT:
+                continue
+            if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
+                continue
+
+            button_rect = wx.Rect(offset, 1, 1000, self._rect.height)
+
+            button.rect = self._art.DrawButton(dc, wnd, button_rect, button, wx.LEFT)
+
+            offset += button.rect.GetWidth()
+            left_buttons_width += button.rect.GetWidth()
+
+        offset = left_buttons_width
+
+        if offset == 0:
+            offset += self._art.GetIndentSize()
+
+        # prepare the tab-close-button array
+        # make sure tab button entries which aren't used are marked as hidden
+        for i in xrange(page_count, len(self._tab_close_buttons)):
+            self._tab_close_buttons[i].cur_state = AUI_BUTTON_STATE_HIDDEN
+
+        # make sure there are enough tab button entries to accommodate all tabs
+        while len(self._tab_close_buttons) < page_count:
+            tempbtn = AuiTabContainerButton()
+            tempbtn.id = AUI_BUTTON_CLOSE
+            tempbtn.location = wx.CENTER
+            tempbtn.cur_state = AUI_BUTTON_STATE_HIDDEN
+            self._tab_close_buttons.append(tempbtn)
+
+        # buttons before the tab offset must be set to hidden
+        for i in xrange(self._tab_offset):
+            self._tab_close_buttons[i].cur_state = AUI_BUTTON_STATE_HIDDEN
+            if self._pages[i].control:
+                if self._pages[i].control.IsShown():
+                    self._pages[i].control.Hide()
+
+        self.MinimizeTabOffset(dc, wnd, self._rect.GetWidth() - right_buttons_width - offset - 2)
+
+        # draw the tabs
+        active = 999
+        active_offset = 0
+
+        rect = wx.Rect(*self._rect)
+        rect.y = 0
+        rect.height = self._rect.height
+
+        for i in xrange(self._tab_offset, page_count):
+
+            page = self._pages[i]
+            tab_button = self._tab_close_buttons[i]
+
+            # determine if a close button is on this tab
+            if (self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS and page.hasCloseButton) or \
+               (self._agwFlags & AUI_NB_CLOSE_ON_ACTIVE_TAB and page.active and page.hasCloseButton):
+
+                if tab_button.cur_state == AUI_BUTTON_STATE_HIDDEN:
+
+                    tab_button.id = AUI_BUTTON_CLOSE
+                    tab_button.cur_state = AUI_BUTTON_STATE_NORMAL
+                    tab_button.location = wx.CENTER
+
+            else:
+
+                tab_button.cur_state = AUI_BUTTON_STATE_HIDDEN
+
+            rect.x = offset
+            rect.width = self._rect.width - right_buttons_width - offset - 2
+
+            if rect.width <= 0:
+                break
+
+            page.rect, tab_button.rect, x_extent = self._art.DrawTab(dc, wnd, page, rect, tab_button.cur_state)
+
+            if page.active:
+                active = i
+                active_offset = offset
+                active_rect = wx.Rect(*rect)
+
+            offset += x_extent
+
+        lenPages = len(self._pages)
+        # make sure to deactivate buttons which are off the screen to the right
+        for j in xrange(i+1, len(self._tab_close_buttons)):
+            self._tab_close_buttons[j].cur_state = AUI_BUTTON_STATE_HIDDEN
+            if j > 0 and j <= lenPages:
+                if self._pages[j-1].control:
+                    if self._pages[j-1].control.IsShown():
+                        self._pages[j-1].control.Hide()
+
+        # draw the active tab again so it stands in the foreground
+        if active >= self._tab_offset and active < len(self._pages):
+
+            page = self._pages[active]
+            tab_button = self._tab_close_buttons[active]
+
+            rect.x = active_offset
+            dummy = self._art.DrawTab(dc, wnd, page, active_rect, tab_button.cur_state)
+
+        raw_dc.Blit(self._rect.x, self._rect.y, self._rect.GetWidth(), self._rect.GetHeight(), dc, 0, 0)
+
+
+    def IsTabVisible(self, tabPage, tabOffset, dc, wnd):
+        """
+        Returns whether a tab is visible or not.
+
+        :param `tabPage`: the tab index;
+        :param `tabOffset`: the tab offset;
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: an instance of `wx.Window` derived window.
+        """
+
+        if not dc or not dc.IsOk():
+            return False
+
+        page_count = len(self._pages)
+        button_count = len(self._buttons)
+        self.Render(dc, wnd)
+
+        # Hasn't been rendered yet assume it's visible
+        if len(self._tab_close_buttons) < page_count:
+            return True
+
+        if self._agwFlags & AUI_NB_SCROLL_BUTTONS:
+            # First check if both buttons are disabled - if so, there's no need to
+            # check further for visibility.
+            arrowButtonVisibleCount = 0
+            for i in xrange(button_count):
+
+                button = self._buttons[i]
+                if button.id == AUI_BUTTON_LEFT or \
+                   button.id == AUI_BUTTON_RIGHT:
+
+                    if button.cur_state & AUI_BUTTON_STATE_HIDDEN == 0:
+                        arrowButtonVisibleCount += 1
+
+            # Tab must be visible
+            if arrowButtonVisibleCount == 0:
+                return True
+
+        # If tab is less than the given offset, it must be invisible by definition
+        if tabPage < tabOffset:
+            return False
+
+        # draw buttons
+        left_buttons_width = 0
+        right_buttons_width = 0
+
+        offset = 0
+
+        # calculate size of the buttons on the right side
+        offset = self._rect.x + self._rect.width
+
+        for i in xrange(button_count):
+            button = self._buttons[button_count - i - 1]
+
+            if button.location != wx.RIGHT:
+                continue
+            if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
+                continue
+
+            offset -= button.rect.GetWidth()
+            right_buttons_width += button.rect.GetWidth()
+
+        offset = 0
+
+        # calculate size of the buttons on the left side
+        for i in xrange(button_count):
+            button = self._buttons[button_count - i - 1]
+
+            if button.location != wx.LEFT:
+                continue
+            if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
+                continue
+
+            offset += button.rect.GetWidth()
+            left_buttons_width += button.rect.GetWidth()
+
+        offset = left_buttons_width
+
+        if offset == 0:
+            offset += self._art.GetIndentSize()
+
+        rect = wx.Rect(*self._rect)
+        rect.y = 0
+        rect.height = self._rect.height
+
+        # See if the given page is visible at the given tab offset (effectively scroll position)
+        for i in xrange(tabOffset, page_count):
+
+            page = self._pages[i]
+            tab_button = self._tab_close_buttons[i]
+
+            rect.x = offset
+            rect.width = self._rect.width - right_buttons_width - offset - 2
+
+            if rect.width <= 0:
+                return False # haven't found the tab, and we've run out of space, so return False
+
+            size, x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active, tab_button.cur_state, page.control)
+            offset += x_extent
+
+            if i == tabPage:
+
+                # If not all of the tab is visible, and supposing there's space to display it all,
+                # we could do better so we return False.
+                if (self._rect.width - right_buttons_width - offset - 2) <= 0 and (self._rect.width - right_buttons_width - left_buttons_width) > x_extent:
+                    return False
+                else:
+                    return True
+
+        # Shouldn't really get here, but if it does, assume the tab is visible to prevent
+        # further looping in calling code.
+        return True
+
+
+    def MakeTabVisible(self, tabPage, win):
+        """
+        Make the tab visible if it wasn't already.
+
+        :param `tabPage`: the tab index;
+        :param `win`: an instance of `wx.Window` derived window.
+        """
+
+        dc = wx.ClientDC(win)
+
+        if not self.IsTabVisible(tabPage, self.GetTabOffset(), dc, win):
+            for i in xrange(len(self._pages)):
+                if self.IsTabVisible(tabPage, i, dc, win):
+                    self.SetTabOffset(i)
+                    win.Refresh()
+                    return
+
+
+    def TabHitTest(self, x, y):
+        """
+        TabHitTest() tests if a tab was hit, passing the window pointer
+        back if that condition was fulfilled.
+
+        :param `x`: the mouse `x` position;
+        :param `y`: the mouse `y` position.
+        """
+
+        if not self._rect.Contains((x,y)):
+            return None
+
+        btn = self.ButtonHitTest(x, y)
+        if btn:
+            if btn in self._buttons:
+                return None
+
+        for i in xrange(self._tab_offset, len(self._pages)):
+            page = self._pages[i]
+            if page.rect.Contains((x,y)):
+                return page.window
+
+        return None
+
+
+    def ButtonHitTest(self, x, y):
+        """
+        Tests if a button was hit.
+
+        :param `x`: the mouse `x` position;
+        :param `y`: the mouse `y` position.
+
+        :returns: and instance of L{AuiTabContainerButton} if a button was hit, ``None`` otherwise.
+        """
+
+        if not self._rect.Contains((x,y)):
+            return None
+
+        for button in self._buttons:
+            if button.rect.Contains((x,y)) and \
+               (button.cur_state & (AUI_BUTTON_STATE_HIDDEN|AUI_BUTTON_STATE_DISABLED)) == 0:
+                return button
+
+        for button in self._tab_close_buttons:
+            if button.rect.Contains((x,y)) and \
+               (button.cur_state & (AUI_BUTTON_STATE_HIDDEN|AUI_BUTTON_STATE_DISABLED)) == 0:
+                return button
+
+        return None
+
+
+    def DoShowHide(self):
+        """
+        This function shows the active window, then hides all of the other windows
+        (in that order).
+        """
+
+        pages = self.GetPages()
+
+        # show new active page first
+        for page in pages:
+            if page.active:
+                page.window.Show(True)
+                break
+
+        # hide all other pages
+        for page in pages:
+            if not page.active:
+                page.window.Show(False)
+
+
+# ----------------------------------------------------------------------
+# -- AuiTabCtrl class implementation --
+
+class AuiTabCtrl(wx.PyControl, AuiTabContainer):
+    """
+    This is an actual `wx.Window`-derived window which can be used as a tab
+    control in the normal sense.
+    """
+
+    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=wx.NO_BORDER|wx.WANTS_CHARS|wx.TAB_TRAVERSAL):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiTabCtrl} parent;
+        :param `id`: an identifier for the control: a value of -1 is taken to mean a default;
+        :param `pos`: the control position. A value of (-1, -1) indicates a default position,
+         chosen by either the windowing system or wxPython, depending on platform;
+        :param `size`: the control size. A value of (-1, -1) indicates a default size,
+         chosen by either the windowing system or wxPython, depending on platform;
+        :param `style`: the window style.
+        """
+
+        wx.PyControl.__init__(self, parent, id, pos, size, style, name="AuiTabCtrl")
+        AuiTabContainer.__init__(self, parent)
+
+        self._click_pt = wx.Point(-1, -1)
+        self._is_dragging = False
+        self._hover_button = None
+        self._pressed_button = None
+        self._drag_image = None
+        self._drag_img_offset = (0, 0)
+        self._on_button = False
+
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
+        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+        self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
+        self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
+        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
+        self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
+        self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost)
+        self.Bind(wx.EVT_MOTION, self.OnMotion)
+        self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
+        self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnButton)
+
+
+    def IsDragging(self):
+        """ Returns whether the user is dragging a tab with the mouse or not. """
+
+        return self._is_dragging
+
+
+    def GetDefaultBorder(self):
+        """ Returns the default border style for L{AuiTabCtrl}. """
+
+        return wx.BORDER_NONE
+
+
+    def OnPaint(self, event):
+        """
+        Handles the ``wx.EVT_PAINT`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.PaintEvent` event to be processed.
+        """
+
+        dc = wx.PaintDC(self)
+        dc.SetFont(self.GetFont())
+
+        if self.GetPageCount() > 0:
+            self.Render(dc, self)
+
+
+    def OnEraseBackground(self, event):
+        """
+        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.EraseEvent` event to be processed.
+
+        :note: This is intentionally empty, to reduce flicker.
+        """
+
+        pass
+
+
+    def DoGetBestSize(self):
+        """
+        Gets the size which best suits the window: for a control, it would be the
+        minimal size which doesn't truncate the control, for a panel - the same
+        size as it would have after a call to `Fit()`.
+
+        :note: Overridden from `wx.PyControl`.
+        """
+
+        return wx.Size(self._rect.width, self._rect.height)
+
+
+    def OnSize(self, event):
+        """
+        Handles the ``wx.EVT_SIZE`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.SizeEvent` event to be processed.
+        """
+
+        s = event.GetSize()
+        self.SetTabRect(wx.Rect(0, 0, s.GetWidth(), s.GetHeight()))
+
+
+    def OnLeftDown(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_DOWN`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        self.CaptureMouse()
+        self._click_pt = wx.Point(-1, -1)
+        self._is_dragging = False
+        self._click_tab = None
+        self._pressed_button = None
+
+        wnd = self.TabHitTest(event.GetX(), event.GetY())
+
+        if wnd is not None:
+            new_selection = self.GetIdxFromWindow(wnd)
+
+            # AuiNotebooks always want to receive this event
+            # even if the tab is already active, because they may
+            # have multiple tab controls
+            if new_selection != self.GetActivePage() or isinstance(self.GetParent(), AuiNotebook):
+
+                e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
+                e.SetSelection(new_selection)
+                e.SetOldSelection(self.GetActivePage())
+                e.SetEventObject(self)
+                self.GetEventHandler().ProcessEvent(e)
+
+            self._click_pt.x = event.GetX()
+            self._click_pt.y = event.GetY()
+            self._click_tab = wnd
+        else:
+            page_index = self.GetActivePage()
+            if page_index != wx.NOT_FOUND:
+                self.GetWindowFromIdx(page_index).SetFocus()
+
+        if self._hover_button:
+            self._pressed_button = self._hover_button
+            self._pressed_button.cur_state = AUI_BUTTON_STATE_PRESSED
+            self._on_button = True
+            self.Refresh()
+            self.Update()
+
+
+    def OnCaptureLost(self, event):
+        """
+        Handles the ``wx.EVT_MOUSE_CAPTURE_LOST`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseCaptureLostEvent` event to be processed.
+        """
+
+        if self._is_dragging:
+            self._is_dragging = False
+            self._on_button = False
+
+            if self._drag_image:
+                self._drag_image.EndDrag()
+                del self._drag_image
+                self._drag_image = None
+
+            event = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG, self.GetId())
+            event.SetSelection(self.GetIdxFromWindow(self._click_tab))
+            event.SetOldSelection(event.GetSelection())
+            event.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(event)
+
+
+    def OnLeftUp(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_UP`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        self._on_button = False
+
+        if self._is_dragging:
+
+            if self.HasCapture():
+                self.ReleaseMouse()
+
+            self._is_dragging = False
+            if self._drag_image:
+                self._drag_image.EndDrag()
+                del self._drag_image
+                self._drag_image = None
+                self.GetParent().Refresh()
+
+            evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, self.GetId())
+            evt.SetSelection(self.GetIdxFromWindow(self._click_tab))
+            evt.SetOldSelection(evt.GetSelection())
+            evt.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(evt)
+
+            return
+
+        self.GetParent()._mgr.HideHint()
+
+        if self.HasCapture():
+            self.ReleaseMouse()
+
+        if self._hover_button:
+            self._pressed_button = self._hover_button
+
+        if self._pressed_button:
+
+            # make sure we're still clicking the button
+            button = self.ButtonHitTest(event.GetX(), event.GetY())
+
+            if button is None:
+                return
+
+            if button != self._pressed_button:
+                self._pressed_button = None
+                return
+
+            self.Refresh()
+            self.Update()
+
+            if self._pressed_button.cur_state & AUI_BUTTON_STATE_DISABLED == 0:
+
+                evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, self.GetId())
+                evt.SetSelection(self.GetIdxFromWindow(self._click_tab))
+                evt.SetInt(self._pressed_button.id)
+                evt.SetEventObject(self)
+                eventHandler = self.GetEventHandler()
+
+                if eventHandler is not None:
+                    eventHandler.ProcessEvent(evt)
+
+            self._pressed_button = None
+
+        self._click_pt = wx.Point(-1, -1)
+        self._is_dragging = False
+        self._click_tab = None
+
+
+    def OnMiddleUp(self, event):
+        """
+        Handles the ``wx.EVT_MIDDLE_UP`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        eventHandler = self.GetEventHandler()
+        if not isinstance(eventHandler, AuiTabCtrl):
+            event.Skip()
+            return
+
+        x, y = event.GetX(), event.GetY()
+        wnd = self.TabHitTest(x, y)
+
+        if wnd:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, self.GetId())
+            e.SetEventObject(self)
+            e.SetSelection(self.GetIdxFromWindow(wnd))
+            self.GetEventHandler().ProcessEvent(e)
+        elif not self.ButtonHitTest(x, y):
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP, self.GetId())
+            e.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnMiddleDown(self, event):
+        """
+        Handles the ``wx.EVT_MIDDLE_DOWN`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        eventHandler = self.GetEventHandler()
+        if not isinstance(eventHandler, AuiTabCtrl):
+            event.Skip()
+            return
+        
+        x, y = event.GetX(), event.GetY()
+        wnd = self.TabHitTest(x, y)
+
+        if wnd:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.GetId())
+            e.SetEventObject(self)
+            e.SetSelection(self.GetIdxFromWindow(wnd))
+            self.GetEventHandler().ProcessEvent(e)
+        elif not self.ButtonHitTest(x, y):
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN, self.GetId())
+            e.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnRightUp(self, event):
+        """
+        Handles the ``wx.EVT_RIGHT_UP`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        x, y = event.GetX(), event.GetY()
+        wnd = self.TabHitTest(x, y)
+
+        if wnd:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, self.GetId())
+            e.SetEventObject(self)
+            e.SetSelection(self.GetIdxFromWindow(wnd))
+            self.GetEventHandler().ProcessEvent(e)
+        elif not self.ButtonHitTest(x, y):
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP, self.GetId())
+            e.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnRightDown(self, event):
+        """
+        Handles the ``wx.EVT_RIGHT_DOWN`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        x, y = event.GetX(), event.GetY()
+        wnd = self.TabHitTest(x, y)
+
+        if wnd:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, self.GetId())
+            e.SetEventObject(self)
+            e.SetSelection(self.GetIdxFromWindow(wnd))
+            self.GetEventHandler().ProcessEvent(e)
+        elif not self.ButtonHitTest(x, y):
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN, self.GetId())
+            e.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnLeftDClick(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_DCLICK`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        x, y = event.GetX(), event.GetY()
+        wnd = self.TabHitTest(x, y)
+
+        if wnd:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, self.GetId())
+            e.SetEventObject(self)
+            e.SetSelection(self.GetIdxFromWindow(wnd))
+            self.GetEventHandler().ProcessEvent(e)
+        elif not self.ButtonHitTest(x, y):
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, self.GetId())
+            e.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnMotion(self, event):
+        """
+        Handles the ``wx.EVT_MOTION`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        pos = event.GetPosition()
+
+        # check if the mouse is hovering above a button
+
+        button = self.ButtonHitTest(pos.x, pos.y)
+        wnd = self.TabHitTest(pos.x, pos.y)
+
+        if wnd is not None:
+            mouse_tab = self.GetIdxFromWindow(wnd)
+            if not self._pages[mouse_tab].enabled:
+                self._hover_button = None
+                return
+
+        if self._on_button:
+            return
+
+        if button:
+
+            if self._hover_button and button != self._hover_button:
+                self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
+                self._hover_button = None
+                self.Refresh()
+                self.Update()
+
+            if button.cur_state != AUI_BUTTON_STATE_HOVER:
+                button.cur_state = AUI_BUTTON_STATE_HOVER
+                self.Refresh()
+                self.Update()
+                self._hover_button = button
+                return
+
+        else:
+
+            if self._hover_button:
+                self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
+                self._hover_button = None
+                self.Refresh()
+                self.Update()
+
+        if not event.LeftIsDown() or self._click_pt == wx.Point(-1, -1):
+            return
+
+        if not self.HasCapture():
+            return
+
+        wnd = self.TabHitTest(pos.x, pos.y)
+
+        if not self._is_dragging:
+
+            drag_x_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_X)
+            drag_y_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_Y)
+
+            if abs(pos.x - self._click_pt.x) > drag_x_threshold or \
+               abs(pos.y - self._click_pt.y) > drag_y_threshold:
+
+                self._is_dragging = True
+
+                if self._drag_image:
+                    self._drag_image.EndDrag()
+                    del self._drag_image
+                    self._drag_image = None
+
+                if self._agwFlags & AUI_NB_DRAW_DND_TAB:
+                    # Create the custom draw image from the icons and the text of the item
+                    mouse_tab = self.GetIdxFromWindow(wnd)
+                    page = self._pages[mouse_tab]
+                    tab_button = self._tab_close_buttons[mouse_tab]
+                    self._drag_image = TabDragImage(self, page, tab_button.cur_state, self._art)
+
+                    if self._agwFlags & AUI_NB_TAB_FLOAT:
+                        self._drag_image.BeginDrag(wx.Point(0,0), self, fullScreen=True)
+                    else:
+                        self._drag_image.BeginDragBounded(wx.Point(0,0), self, self.GetParent())
+
+                    # Capture the mouse cursor position offset relative to
+                    # The tab image location
+                    self._drag_img_offset = (pos[0] - page.rect.x,
+                                             pos[1] - page.rect.y)
+
+                    self._drag_image.Show()
+
+        if not wnd:
+            evt2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, self.GetId())
+            evt2.SetSelection(self.GetIdxFromWindow(self._click_tab))
+            evt2.SetOldSelection(evt2.GetSelection())
+            evt2.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(evt2)
+            if evt2.GetDispatched():
+                return
+
+        evt3 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, self.GetId())
+        evt3.SetSelection(self.GetIdxFromWindow(self._click_tab))
+        evt3.SetOldSelection(evt3.GetSelection())
+        evt3.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(evt3)
+
+        if self._drag_image:
+            # Apply the drag images offset
+            pos -= self._drag_img_offset
+            self._drag_image.Move(pos)
+
+
+    def OnLeaveWindow(self, event):
+        """
+        Handles the ``wx.EVT_LEAVE_WINDOW`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.
+        """
+
+        if self._hover_button:
+            self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
+            self._hover_button = None
+            self.Refresh()
+            self.Update()
+
+
+    def OnButton(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_BUTTON`` event for L{AuiTabCtrl}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        button = event.GetInt()
+
+        if button == AUI_BUTTON_LEFT or button == AUI_BUTTON_RIGHT:
+            if button == AUI_BUTTON_LEFT:
+                if self.GetTabOffset() > 0:
+
+                    self.SetTabOffset(self.GetTabOffset()-1)
+                    self.Refresh()
+                    self.Update()
+            else:
+                self.SetTabOffset(self.GetTabOffset()+1)
+                self.Refresh()
+                self.Update()
+
+        elif button == AUI_BUTTON_WINDOWLIST:
+            idx = self.GetArtProvider().ShowDropDown(self, self._pages, self.GetActivePage())
+
+            if idx != -1:
+
+                e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
+                e.SetSelection(idx)
+                e.SetOldSelection(self.GetActivePage())
+                e.SetEventObject(self)
+                self.GetEventHandler().ProcessEvent(e)
+
+        else:
+            event.Skip()
+
+
+    def OnSetFocus(self, event):
+        """
+        Handles the ``wx.EVT_SET_FOCUS`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.FocusEvent` event to be processed.
+        """
+
+        self.Refresh()
+
+
+    def OnKillFocus(self, event):
+        """
+        Handles the ``wx.EVT_KILL_FOCUS`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.FocusEvent` event to be processed.
+        """
+
+        self.Refresh()
+
+
+    def OnKeyDown(self, event):
+        """
+        Handles the ``wx.EVT_KEY_DOWN`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.KeyEvent` event to be processed.
+        """
+
+        key = event.GetKeyCode()
+        nb = self.GetParent()
+
+        if key == wx.WXK_LEFT:
+            nb.AdvanceSelection(False)
+            self.SetFocus()
+
+        elif key == wx.WXK_RIGHT:
+            nb.AdvanceSelection(True)
+            self.SetFocus()
+
+        elif key == wx.WXK_HOME:
+            newPage = 0
+            nb.SetSelection(newPage)
+            self.SetFocus()
+
+        elif key == wx.WXK_END:
+            newPage = nb.GetPageCount() - 1
+            nb.SetSelection(newPage)
+            self.SetFocus()
+
+        elif key == wx.WXK_TAB:
+            if not event.ControlDown():
+                flags = 0
+                if not event.ShiftDown(): flags |= wx.NavigationKeyEvent.IsForward
+                if event.CmdDown():       flags |= wx.NavigationKeyEvent.WinChange
+                self.Navigate(flags)
+            else:
+
+                if not nb or not isinstance(nb, AuiNotebook):
+                    event.Skip()
+                    return
+
+                bForward = bWindowChange = 0
+                if not event.ShiftDown(): bForward |= wx.NavigationKeyEvent.IsForward
+                if event.CmdDown():       bWindowChange |= wx.NavigationKeyEvent.WinChange
+
+                keyEvent = wx.NavigationKeyEvent()
+                keyEvent.SetDirection(bForward)
+                keyEvent.SetWindowChange(bWindowChange)
+                keyEvent.SetFromTab(True)
+                keyEvent.SetEventObject(nb)
+
+                if not nb.GetEventHandler().ProcessEvent(keyEvent):
+
+                    # Not processed? Do an explicit tab into the page.
+                    win = self.GetWindowFromIdx(self.GetActivePage())
+                    if win:
+                        win.SetFocus()
+
+                self.SetFocus()
+
+                return
+
+        else:
+            event.Skip()
+
+
+    def OnKeyDown2(self, event):
+        """
+        Deprecated.
+
+        Handles the ``wx.EVT_KEY_DOWN`` event for L{AuiTabCtrl}.
+
+        :param `event`: a `wx.KeyEvent` event to be processed.
+
+        :warning: This method implementation is now deprecated. Refer to L{OnKeyDown}
+         for the correct one.
+        """
+
+        if self.GetActivePage() == -1:
+            event.Skip()
+            return
+
+        # We can't leave tab processing to the system on Windows, tabs and keys
+        # get eaten by the system and not processed properly if we specify both
+        # wxTAB_TRAVERSAL and wxWANTS_CHARS. And if we specify just wxTAB_TRAVERSAL,
+        # we don't key arrow key events.
+
+        key = event.GetKeyCode()
+
+        if key == wx.WXK_NUMPAD_PAGEUP:
+            key = wx.WXK_PAGEUP
+        if key == wx.WXK_NUMPAD_PAGEDOWN:
+            key = wx.WXK_PAGEDOWN
+        if key == wx.WXK_NUMPAD_HOME:
+            key = wx.WXK_HOME
+        if key == wx.WXK_NUMPAD_END:
+            key = wx.WXK_END
+        if key == wx.WXK_NUMPAD_LEFT:
+            key = wx.WXK_LEFT
+        if key == wx.WXK_NUMPAD_RIGHT:
+            key = wx.WXK_RIGHT
+
+        if key == wx.WXK_TAB or key == wx.WXK_PAGEUP or key == wx.WXK_PAGEDOWN:
+
+            bCtrlDown = event.ControlDown()
+            bShiftDown = event.ShiftDown()
+
+            bForward = (key == wx.WXK_TAB and not bShiftDown) or (key == wx.WXK_PAGEDOWN)
+            bWindowChange = (key == wx.WXK_PAGEUP) or (key == wx.WXK_PAGEDOWN) or bCtrlDown
+            bFromTab = (key == wx.WXK_TAB)
+
+            nb = self.GetParent()
+            if not nb or not isinstance(nb, AuiNotebook):
+                event.Skip()
+                return
+
+            keyEvent = wx.NavigationKeyEvent()
+            keyEvent.SetDirection(bForward)
+            keyEvent.SetWindowChange(bWindowChange)
+            keyEvent.SetFromTab(bFromTab)
+            keyEvent.SetEventObject(nb)
+
+            if not nb.GetEventHandler().ProcessEvent(keyEvent):
+
+                # Not processed? Do an explicit tab into the page.
+                win = self.GetWindowFromIdx(self.GetActivePage())
+                if win:
+                    win.SetFocus()
+
+            return
+
+        if len(self._pages) < 2:
+            event.Skip()
+            return
+
+        newPage = -1
+
+        if self.GetLayoutDirection() == wx.Layout_RightToLeft:
+            forwardKey = wx.WXK_LEFT
+            backwardKey = wx.WXK_RIGHT
+        else:
+            forwardKey = wx.WXK_RIGHT
+            backwardKey = wx.WXK_LEFT
+
+        if key == forwardKey:
+            if self.GetActivePage() == -1:
+                newPage = 0
+            elif self.GetActivePage() < len(self._pages) - 1:
+                newPage = self.GetActivePage() + 1
+
+        elif key == backwardKey:
+            if self.GetActivePage() == -1:
+                newPage = len(self._pages) - 1
+            elif self.GetActivePage() > 0:
+                newPage = self.GetActivePage() - 1
+
+        elif key == wx.WXK_HOME:
+            newPage = 0
+
+        elif key == wx.WXK_END:
+            newPage = len(self._pages) - 1
+
+        else:
+            event.Skip()
+
+        if newPage != -1:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
+            e.SetSelection(newPage)
+            e.SetOldSelection(newPage)
+            e.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(e)
+
+        else:
+            event.Skip()
+
+
+# ----------------------------------------------------------------------
+
+class TabFrame(wx.PyWindow):
+    """
+    TabFrame is an interesting case. It's important that all child pages
+    of the multi-notebook control are all actually children of that control
+    (and not grandchildren). TabFrame facilitates this. There is one
+    instance of TabFrame for each tab control inside the multi-notebook.
+
+    It's important to know that TabFrame is not a real window, but it merely
+    used to capture the dimensions/positioning of the internal tab control and
+    it's managed page windows.
+    """
+
+    def __init__(self, parent):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+        """
+
+        pre = wx.PrePyWindow()
+
+        self._tabs = None
+        self._rect = wx.Rect(0, 0, 200, 200)
+        self._tab_ctrl_height = 20
+        self._tab_rect = wx.Rect()
+        self._parent = parent
+
+        self.PostCreate(pre)
+
+
+    def SetTabCtrlHeight(self, h):
+        """
+        Sets the tab control height.
+
+        :param `h`: the tab area height.
+        """
+
+        self._tab_ctrl_height = h
+
+
+    def DoSetSize(self, x, y, width, height, flags=wx.SIZE_AUTO):
+        """
+        Sets the position and size of the window in pixels. The `flags`
+        parameter indicates the interpretation of the other params if they are
+        equal to -1.
+
+        :param `x`: the window `x` position;
+        :param `y`: the window `y` position;
+        :param `width`: the window width;
+        :param `height`: the window height;
+        :param `flags`: may have one of this bit set:
+
+         ===================================  ======================================
+         Size Flags                           Description
+         ===================================  ======================================
+         ``wx.SIZE_AUTO``                     A -1 indicates that a class-specific default should be used.
+         ``wx.SIZE_AUTO_WIDTH``               A -1 indicates that a class-specific default should be used for the width.
+         ``wx.SIZE_AUTO_HEIGHT``              A -1 indicates that a class-specific default should be used for the height.
+         ``wx.SIZE_USE_EXISTING``             Existing dimensions should be used if -1 values are supplied.
+         ``wx.SIZE_ALLOW_MINUS_ONE``          Allow dimensions of -1 and less to be interpreted as real dimensions, not default values.
+         ``wx.SIZE_FORCE``                    Normally, if the position and the size of the window are already the same as the parameters of this function, nothing is done. but with this flag a window resize may be forced even in this case (supported in wx 2.6.2 and later and only implemented for MSW and ignored elsewhere currently)
+         ===================================  ======================================
+
+        :note: Overridden from `wx.PyControl`.
+        """
+
+        self._rect = wx.Rect(x, y, max(1, width), max(1, height))
+        self.DoSizing()
+
+
+    def DoGetSize(self):
+        """
+        Returns the window size.
+
+        :note: Overridden from `wx.PyControl`.
+        """
+
+        return self._rect.width, self._rect.height
+
+
+    def DoGetClientSize(self):
+        """
+        Returns the window client size.
+
+        :note: Overridden from `wx.PyControl`.
+        """
+
+        return self._rect.width, self._rect.height
+
+
+    def Show(self, show=True):
+        """
+        Shows/hides the window.
+
+        :param `show`: ``True`` to show the window, ``False`` otherwise.
+
+        :note: Overridden from `wx.PyControl`, this method always returns ``False`` as
+         L{TabFrame} should never be phisically shown on screen.
+        """
+
+        return False
+
+
+    def DoSizing(self):
+        """ Does the actual sizing of the tab control. """
+
+        if not self._tabs:
+            return
+
+        hideOnSingle = ((self._tabs.GetAGWFlags() & AUI_NB_HIDE_ON_SINGLE_TAB) and \
+                        self._tabs.GetPageCount() <= 1)
+
+        if not hideOnSingle and not self._parent._hide_tabs:
+            tab_height = self._tab_ctrl_height
+
+            self._tab_rect = wx.Rect(self._rect.x, self._rect.y, self._rect.width, self._tab_ctrl_height)
+
+            if self._tabs.GetAGWFlags() & AUI_NB_BOTTOM:
+                self._tab_rect = wx.Rect(self._rect.x, self._rect.y + self._rect.height - tab_height,
+                                         self._rect.width, tab_height)
+                self._tabs.SetDimensions(self._rect.x, self._rect.y + self._rect.height - tab_height,
+                                         self._rect.width, tab_height)
+                self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
+
+            else:
+
+                self._tab_rect = wx.Rect(self._rect.x, self._rect.y, self._rect.width, tab_height)
+                self._tabs.SetDimensions(self._rect.x, self._rect.y, self._rect.width, tab_height)
+                self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
+
+            # TODO: elif (GetAGWFlags() & AUI_NB_LEFT)
+            # TODO: elif (GetAGWFlags() & AUI_NB_RIGHT)
+
+            self._tabs.Refresh()
+            self._tabs.Update()
+
+        else:
+
+            tab_height = 0
+            self._tabs.SetDimensions(self._rect.x, self._rect.y, self._rect.width, tab_height)
+            self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
+
+        pages = self._tabs.GetPages()
+
+        for page in pages:
+
+            height = self._rect.height - tab_height
+
+            if height < 0:
+                # avoid passing negative height to wx.Window.SetSize(), this
+                # results in assert failures/GTK+ warnings
+                height = 0
+
+            if self._tabs.GetAGWFlags() & AUI_NB_BOTTOM:
+                page.window.SetDimensions(self._rect.x, self._rect.y, self._rect.width, height)
+
+            else:
+                page.window.SetDimensions(self._rect.x, self._rect.y + tab_height,
+                                          self._rect.width, height)
+
+            # TODO: elif (GetAGWFlags() & AUI_NB_LEFT)
+            # TODO: elif (GetAGWFlags() & AUI_NB_RIGHT)
+
+            if repr(page.window.__class__).find("AuiMDIChildFrame") >= 0:
+                page.window.ApplyMDIChildFrameRect()
+
+
+    def Update(self):
+        """
+        Calling this method immediately repaints the invalidated area of the window
+        and all of its children recursively while this would usually only happen when
+        the flow of control returns to the event loop.
+
+        :note: Notice that this function doesn't invalidate any area of the window so
+         nothing happens if nothing has been invalidated (i.e. marked as requiring a redraw).
+         Use `Refresh` first if you want to immediately redraw the window unconditionally.
+
+        :note: Overridden from `wx.PyControl`.
+        """
+
+        # does nothing
+        pass
+
+
+# ----------------------------------------------------------------------
+# -- AuiNotebook class implementation --
+
+class AuiNotebook(wx.PyPanel):
+    """
+    AuiNotebook is a notebook control which implements many features common in
+    applications with dockable panes. Specifically, AuiNotebook implements functionality
+    which allows the user to rearrange tab order via drag-and-drop, split the tab window
+    into many different splitter configurations, and toggle through different themes to
+    customize the control's look and feel.
+
+    An effort has been made to try to maintain an API as similar to that of `wx.Notebook`.
+
+    The default theme that is used is L{AuiDefaultTabArt}, which provides a modern, glossy
+    look and feel. The theme can be changed by calling L{AuiNotebook.SetArtProvider}.
+    """
+
+    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=0, agwStyle=AUI_NB_DEFAULT_STYLE):
+        """
+        Default class constructor.
+
+        :param `parent`: the L{AuiNotebook} parent;
+        :param `id`: an identifier for the control: a value of -1 is taken to mean a default;
+        :param `pos`: the control position. A value of (-1, -1) indicates a default position,
+         chosen by either the windowing system or wxPython, depending on platform;
+        :param `size`: the control size. A value of (-1, -1) indicates a default size,
+         chosen by either the windowing system or wxPython, depending on platform;
+        :param `style`: the underlying `wx.PyPanel` window style;
+        :param `agwStyle`: the AGW-specific window style. This can be a combination of the following bits:
+
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
+         ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet.
+         ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet.
+         ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
+         ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
+         ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
+         ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
+         ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
+         ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
+         ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
+         ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
+         ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
+         ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
+         ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close L{AuiNotebook} tabs by mouse middle button click
+         ``AUI_NB_SUB_NOTEBOOK``              This style is used by L{AuiManager} to create automatic AuiNotebooks
+         ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
+         ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
+         ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
+         ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
+         ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
+         ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
+         ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
+         ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
+         ==================================== ==================================
+
+         Default value for `agwStyle` is:
+         ``AUI_NB_DEFAULT_STYLE`` = ``AUI_NB_TOP`` | ``AUI_NB_TAB_SPLIT`` | ``AUI_NB_TAB_MOVE`` | ``AUI_NB_SCROLL_BUTTONS`` | ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` | ``AUI_NB_MIDDLE_CLICK_CLOSE`` | ``AUI_NB_DRAW_DND_TAB``
+
+        """
+
+        self._curpage = -1
+        self._tab_id_counter = AuiBaseTabCtrlId
+        self._dummy_wnd = None
+        self._hide_tabs = False
+        self._sash_dclick_unsplit = False
+        self._tab_ctrl_height = 20
+        self._requested_bmp_size = wx.Size(-1, -1)
+        self._requested_tabctrl_height = -1
+        self._textCtrl = None
+        self._tabBounds = (-1, -1)
+        self._click_tab = None
+
+        wx.PyPanel.__init__(self, parent, id, pos, size, style|wx.BORDER_NONE|wx.TAB_TRAVERSAL)
+        self._mgr = framemanager.AuiManager()
+        self._tabs = AuiTabContainer(self)
+
+        self.InitNotebook(agwStyle)
+
+
+    def GetTabContainer(self):
+        """ Returns the instance of L{AuiTabContainer}. """
+
+        return self._tabs
+
+
+    def InitNotebook(self, agwStyle):
+        """
+        Contains common initialization code called by all constructors.
+
+        :param `agwStyle`: the notebook style.
+
+        :see: L{__init__}
+        """
+
+        self.SetName("AuiNotebook")
+        self._agwFlags = agwStyle
+
+        self._popupWin = None
+        self._naviIcon = None
+        self._imageList = None
+        self._last_drag_x = 0
+
+        self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        self._selected_font.SetWeight(wx.BOLD)
+
+        self.SetArtProvider(TA.AuiDefaultTabArt())
+
+        self._dummy_wnd = wx.Window(self, wx.ID_ANY, wx.Point(0, 0), wx.Size(0, 0))
+        self._dummy_wnd.SetSize((200, 200))
+        self._dummy_wnd.Show(False)
+
+        self._mgr.SetManagedWindow(self)
+        self._mgr.SetAGWFlags(AUI_MGR_DEFAULT)
+        self._mgr.SetDockSizeConstraint(1.0, 1.0) # no dock size constraint
+
+        self._mgr.AddPane(self._dummy_wnd, framemanager.AuiPaneInfo().Name("dummy").Bottom().CaptionVisible(False).Show(False))
+        self._mgr.Update()
+
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocusNotebook)
+        self.Bind(EVT_AUINOTEBOOK_PAGE_CHANGING, self.OnTabClicked,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnTabBeginDrag,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_END_DRAG, self.OnTabEndDrag,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_DRAG_MOTION, self.OnTabDragMotion,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_CANCEL_DRAG, self.OnTabCancelDrag,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnTabButton,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.OnTabMiddleDown,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_UP, self.OnTabMiddleUp,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.OnTabRightDown,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_UP, self.OnTabRightUp,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_BG_DCLICK, self.OnTabBgDClick,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+        self.Bind(EVT_AUINOTEBOOK_TAB_DCLICK, self.OnTabDClick,
+                  id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
+
+        self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKeyNotebook)
+
+
+    def SetArtProvider(self, art):
+        """
+        Sets the art provider to be used by the notebook.
+
+        :param `art`: an art provider.
+        """
+
+        self._tabs.SetArtProvider(art)
+        self.UpdateTabCtrlHeight(force=True)
+
+
+    def SavePerspective(self):
+        """
+        Saves the entire user interface layout into an encoded string, which can then
+        be stored by the application (probably using `wx.Config`). When a perspective
+        is restored using L{LoadPerspective}, the entire user interface will return
+        to the state it was when the perspective was saved.
+        """
+
+        # Build list of panes/tabs
+        tabs = ""
+        all_panes = self._mgr.GetAllPanes()
+
+        for pane in all_panes:
+
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+
+            if tabs:
+                tabs += "|"
+
+            tabs += pane.name + "="
+
+            # add tab id's
+            page_count = tabframe._tabs.GetPageCount()
+
+            for p in xrange(page_count):
+
+                page = tabframe._tabs.GetPage(p)
+                page_idx = self._tabs.GetIdxFromWindow(page.window)
+
+                if p:
+                    tabs += ","
+
+                if p == tabframe._tabs.GetActivePage():
+                    tabs += "+"
+                elif page_idx == self._curpage:
+                    tabs += "*"
+
+                tabs += "%u"%page_idx
+
+        tabs += "@"
+
+        # Add frame perspective
+        tabs += self._mgr.SavePerspective()
+
+        return tabs
+
+
+    def LoadPerspective(self, layout):
+        """
+        Loads a layout which was saved with L{SavePerspective}.
+
+        :param `layout`: a string which contains a saved L{AuiNotebook} layout.
+        """
+
+        # Remove all tab ctrls (but still keep them in main index)
+        tab_count = self._tabs.GetPageCount()
+        for i in xrange(tab_count):
+            wnd = self._tabs.GetWindowFromIdx(i)
+
+            # find out which onscreen tab ctrl owns this tab
+            ctrl, ctrl_idx = self.FindTab(wnd)
+            if not ctrl:
+                return False
+
+            # remove the tab from ctrl
+            if not ctrl.RemovePage(wnd):
+                return False
+
+        self.RemoveEmptyTabFrames()
+
+        sel_page = 0
+        tabs = layout[0:layout.index("@")]
+        to_break1 = False
+
+        while 1:
+
+            if "|" not in tabs:
+                to_break1 = True
+                tab_part = tabs
+            else:
+                tab_part = tabs[0:tabs.index('|')]
+
+            if "=" not in tab_part:
+                # No pages in this perspective...
+                return False
+
+            # Get pane name
+            pane_name = tab_part[0:tab_part.index("=")]
+
+            # create a new tab frame
+            new_tabs = TabFrame(self)
+            self._tab_id_counter += 1
+            new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
+            new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
+            new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
+            new_tabs._tabs.SetAGWFlags(self._agwFlags)
+            dest_tabs = new_tabs._tabs
+
+            # create a pane info structure with the information
+            # about where the pane should be added
+            pane_info = framemanager.AuiPaneInfo().Name(pane_name).Bottom().CaptionVisible(False)
+            self._mgr.AddPane(new_tabs, pane_info)
+
+            # Get list of tab id's and move them to pane
+            tab_list = tab_part[tab_part.index("=")+1:]
+            to_break2, active_found = False, False
+
+            while 1:
+                if "," not in tab_list:
+                    to_break2 = True
+                    tab = tab_list
+                else:
+                    tab = tab_list[0:tab_list.index(",")]
+                    tab_list = tab_list[tab_list.index(",")+1:]
+
+                # Check if this page has an 'active' marker
+                c = tab[0]
+                if c in ['+', '*']:
+                    tab = tab[1:]
+
+                tab_idx = int(tab)
+                if tab_idx >= self.GetPageCount():
+                    to_break1 = True
+                    break
+
+                # Move tab to pane
+                page = self._tabs.GetPage(tab_idx)
+                newpage_idx = dest_tabs.GetPageCount()
+                dest_tabs.InsertPage(page.window, page, newpage_idx)
+
+                if c == '+':
+                    dest_tabs.SetActivePage(newpage_idx)
+                    active_found = True
+                elif c == '*':
+                    sel_page = tab_idx
+
+                if to_break2:
+                    break
+
+            if not active_found:
+                dest_tabs.SetActivePage(0)
+
+            new_tabs.DoSizing()
+            dest_tabs.DoShowHide()
+            dest_tabs.Refresh()
+
+            if to_break1:
+                break
+
+            tabs = tabs[tabs.index('|')+1:]
+
+        # Load the frame perspective
+        frames = layout[layout.index('@')+1:]
+        self._mgr.LoadPerspective(frames)
+
+        # Force refresh of selection
+        self._curpage = -1
+        self.SetSelection(sel_page)
+
+        return True
+
+
+    def SetTabCtrlHeight(self, height):
+        """
+        Sets the tab height.
+
+        By default, the tab control height is calculated by measuring the text
+        height and bitmap sizes on the tab captions.
+
+        Calling this method will override that calculation and set the tab control
+        to the specified height parameter. A call to this method will override
+        any call to L{SetUniformBitmapSize}. Specifying -1 as the height will
+        return the control to its default auto-sizing behaviour.
+
+        :param `height`: the tab control area height.
+        """
+
+        self._requested_tabctrl_height = height
+
+        # if window is already initialized, recalculate the tab height
+        if self._dummy_wnd:
+            self.UpdateTabCtrlHeight()
+
+
+    def SetUniformBitmapSize(self, size):
+        """
+        Ensures that all tabs will have the same height, even if some tabs
+        don't have bitmaps. Passing ``wx.DefaultSize`` to this
+        function will instruct the control to use dynamic tab height, which is
+        the default behaviour. Under the default behaviour, when a tab with a
+        large bitmap is added, the tab control's height will automatically
+        increase to accommodate the larger bitmap.
+
+        :param `size`: an instance of `wx.Size` specifying the tab bitmap size.
+        """
+
+        self._requested_bmp_size = wx.Size(*size)
+
+        # if window is already initialized, recalculate the tab height
+        if self._dummy_wnd:
+            self.UpdateTabCtrlHeight()
+
+
+    def UpdateTabCtrlHeight(self, force=False):
+        """
+        UpdateTabCtrlHeight() does the actual tab resizing. It's meant
+        to be used interally.
+
+        :param `force`: ``True`` to force the tab art to repaint.
+        """
+
+        # get the tab ctrl height we will use
+        height = self.CalculateTabCtrlHeight()
+
+        # if the tab control height needs to change, update
+        # all of our tab controls with the new height
+        if self._tab_ctrl_height != height or force:
+            art = self._tabs.GetArtProvider()
+
+            self._tab_ctrl_height = height
+
+            all_panes = self._mgr.GetAllPanes()
+            for pane in all_panes:
+
+                if pane.name == "dummy":
+                    continue
+
+                tab_frame = pane.window
+                tabctrl = tab_frame._tabs
+                tab_frame.SetTabCtrlHeight(self._tab_ctrl_height)
+                tabctrl.SetArtProvider(art.Clone())
+                tab_frame.DoSizing()
+
+
+    def UpdateHintWindowSize(self):
+        """ Updates the L{AuiManager} hint window size. """
+
+        size = self.CalculateNewSplitSize()
+
+        # the placeholder hint window should be set to this size
+        info = self._mgr.GetPane("dummy")
+
+        if info.IsOk():
+            info.MinSize(size)
+            info.BestSize(size)
+            self._dummy_wnd.SetSize(size)
+
+
+    def CalculateNewSplitSize(self):
+        """ Calculates the size of the new split. """
+
+        # count number of tab controls
+        tab_ctrl_count = 0
+        all_panes = self._mgr.GetAllPanes()
+
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tab_ctrl_count += 1
+
+        # if there is only one tab control, the first split
+        # should happen around the middle
+        if tab_ctrl_count < 2:
+            new_split_size = self.GetClientSize()
+            new_split_size.x /= 2
+            new_split_size.y /= 2
+
+        else:
+
+            # this is in place of a more complicated calculation
+            # that needs to be implemented
+            new_split_size = wx.Size(180, 180)
+
+        return new_split_size
+
+
+    def CalculateTabCtrlHeight(self):
+        """ Calculates the tab control area height. """
+
+        # if a fixed tab ctrl height is specified,
+        # just return that instead of calculating a
+        # tab height
+        if self._requested_tabctrl_height != -1:
+            return self._requested_tabctrl_height
+
+        # find out new best tab height
+        art = self._tabs.GetArtProvider()
+
+        return art.GetBestTabCtrlSize(self, self._tabs.GetPages(), self._requested_bmp_size)
+
+
+    def GetArtProvider(self):
+        """ Returns the associated art provider. """
+
+        return self._tabs.GetArtProvider()
+
+
+    def SetAGWWindowStyleFlag(self, agwStyle):
+        """
+        Sets the AGW-specific style of the window.
+
+        :param `agwStyle`: the new window style. This can be a combination of the following bits:
+
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
+         ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet.
+         ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet.
+         ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
+         ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
+         ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
+         ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
+         ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
+         ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
+         ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
+         ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
+         ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
+         ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
+         ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close L{AuiNotebook} tabs by mouse middle button click
+         ``AUI_NB_SUB_NOTEBOOK``              This style is used by L{AuiManager} to create automatic AuiNotebooks
+         ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
+         ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
+         ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
+         ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
+         ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
+         ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
+         ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
+         ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
+         ==================================== ==================================
+
+        :note: Please note that some styles cannot be changed after the window
+         creation and that `Refresh` might need to be be called after changing the
+         others for the change to take place immediately.
+
+        :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
+        """
+
+        self._agwFlags = agwStyle
+
+        # if the control is already initialized
+        if self._mgr.GetManagedWindow() == self:
+
+            # let all of the tab children know about the new style
+
+            all_panes = self._mgr.GetAllPanes()
+            for pane in all_panes:
+                if pane.name == "dummy":
+                    continue
+
+                tabframe = pane.window
+                tabctrl = tabframe._tabs
+                tabctrl.SetAGWFlags(self._agwFlags)
+                tabframe.DoSizing()
+                tabctrl.Refresh()
+                tabctrl.Update()
+
+
+    def GetAGWWindowStyleFlag(self):
+        """
+        Returns the AGW-specific style of the window.
+
+        :see: L{SetAGWWindowStyleFlag} for a list of possible AGW-specific window styles.
+        """
+
+        return self._agwFlags
+
+
+    def AddPage(self, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap, control=None):
+        """
+        Adds a page. If the `select` parameter is ``True``, calling this will generate a
+        page change event.
+
+        :param `page`: the page to be added;
+        :param `caption`: specifies the text for the new page;
+        :param `select`: specifies whether the page should be selected;
+        :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
+        :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+
+        return self.InsertPage(self.GetPageCount(), page, caption, select, bitmap, disabled_bitmap, control)
+
+
+    def InsertPage(self, page_idx, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap,
+                   control=None):
+        """
+        This is similar to L{AddPage}, but allows the ability to specify the insert location.
+
+        :param `page_idx`: specifies the position for the new page;
+        :param `page`: the page to be added;
+        :param `caption`: specifies the text for the new page;
+        :param `select`: specifies whether the page should be selected;
+        :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
+        :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+
+        if not page:
+            return False
+
+        page.Reparent(self)
+        info = AuiNotebookPage()
+        info.window = page
+        info.caption = caption
+        info.bitmap = bitmap
+        info.active = False
+        info.control = control
+
+        originalPaneMgr = framemanager.GetManager(page)
+        if originalPaneMgr:
+            originalPane = originalPaneMgr.GetPane(page)
+
+            if originalPane:
+                info.hasCloseButton = originalPane.HasCloseButton()
+
+        if bitmap.IsOk() and not disabled_bitmap.IsOk():
+            disabled_bitmap = MakeDisabledBitmap(bitmap)
+            info.dis_bitmap = disabled_bitmap
+
+        # if there are currently no tabs, the first added
+        # tab must be active
+        if self._tabs.GetPageCount() == 0:
+            info.active = True
+
+        self._tabs.InsertPage(page, info, page_idx)
+
+        # if that was the first page added, even if
+        # select is False, it must become the "current page"
+        # (though no select events will be fired)
+        if not select and self._tabs.GetPageCount() == 1:
+            select = True
+
+        active_tabctrl = self.GetActiveTabCtrl()
+        if page_idx >= active_tabctrl.GetPageCount():
+            active_tabctrl.AddPage(page, info)
+        else:
+            active_tabctrl.InsertPage(page, info, page_idx)
+
+        force = False
+        if control:
+            force = True
+            control.Reparent(active_tabctrl)
+            control.Show()
+
+        self.UpdateTabCtrlHeight(force=force)
+        self.DoSizing()
+        active_tabctrl.DoShowHide()
+
+        # adjust selected index
+        if self._curpage >= page_idx:
+            self._curpage += 1
+
+        if select:
+            self.SetSelectionToWindow(page)
+
+        return True
+
+
+    def DeletePage(self, page_idx):
+        """
+        Deletes a page at the given index. Calling this method will generate a page
+        change event.
+
+        :param `page_idx`: the page index to be deleted.
+
+        :note: L{DeletePage} removes a tab from the multi-notebook, and destroys the window as well.
+
+        :see: L{RemovePage}
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        wnd = self._tabs.GetWindowFromIdx(page_idx)
+        # hide the window in advance, as this will
+        # prevent flicker
+        wnd.Show(False)
+
+        self.RemoveControlFromPage(page_idx)
+
+        if not self.RemovePage(page_idx):
+            return False
+
+        wnd.Destroy()
+
+        return True
+
+
+    def RemovePage(self, page_idx):
+        """
+        Removes a page, without deleting the window pointer.
+
+        :param `page_idx`: the page index to be removed.
+
+        :note: L{RemovePage} removes a tab from the multi-notebook, but does not destroy the window.
+
+        :see: L{DeletePage}
+        """
+
+        # save active window pointer
+        active_wnd = None
+        if self._curpage >= 0:
+            active_wnd = self._tabs.GetWindowFromIdx(self._curpage)
+
+        # save pointer of window being deleted
+        wnd = self._tabs.GetWindowFromIdx(page_idx)
+        new_active = None
+
+        # make sure we found the page
+        if not wnd:
+            return False
+
+        # find out which onscreen tab ctrl owns this tab
+        ctrl, ctrl_idx = self.FindTab(wnd)
+        if not ctrl:
+            return False
+
+        currentPage = ctrl.GetPage(ctrl_idx)
+        is_curpage = (self._curpage == page_idx)
+        is_active_in_split = currentPage.active
+
+        # remove the tab from main catalog
+        if not self._tabs.RemovePage(wnd):
+            return False
+
+        # remove the tab from the onscreen tab ctrl
+        ctrl.RemovePage(wnd)
+
+        if is_active_in_split:
+
+            ctrl_new_page_count = ctrl.GetPageCount()
+
+            if ctrl_idx >= ctrl_new_page_count:
+                ctrl_idx = ctrl_new_page_count - 1
+
+            if ctrl_idx >= 0 and ctrl_idx < ctrl.GetPageCount():
+
+                ctrl_idx = self.FindNextActiveTab(ctrl_idx, ctrl)
+
+                # set new page as active in the tab split
+                ctrl.SetActivePage(ctrl_idx)
+
+                # if the page deleted was the current page for the
+                # entire tab control, then record the window
+                # pointer of the new active page for activation
+                if is_curpage:
+                    new_active = ctrl.GetWindowFromIdx(ctrl_idx)
+
+        else:
+
+            # we are not deleting the active page, so keep it the same
+            new_active = active_wnd
+
+        if not new_active:
+
+            # we haven't yet found a new page to active,
+            # so select the next page from the main tab
+            # catalogue
+
+            if 0 <= page_idx < self._tabs.GetPageCount():
+                new_active = self._tabs.GetPage(page_idx).window
+            if not new_active and self._tabs.GetPageCount() > 0:
+                new_active = self._tabs.GetPage(0).window
+
+        self.RemoveEmptyTabFrames()
+
+        # set new active pane
+        if new_active:
+            if not self.IsBeingDeleted():
+                self._curpage = -1
+                self.SetSelectionToWindow(new_active)
+        else:
+            self._curpage = -1
+            self._tabs.SetNoneActive()
+
+        return True
+
+
+    def FindNextActiveTab(self, ctrl_idx, ctrl):
+        """
+        Finds the next active tab (used mainly when L{AuiNotebook} has inactive/disabled
+        tabs in it).
+
+        :param `ctrl_idx`: the index of the first (most obvious) tab to check for active status;
+        :param `ctrl`: an instance of L{AuiTabCtrl}.
+        """
+
+        if self.GetEnabled(ctrl_idx):
+            return ctrl_idx
+
+        for indx in xrange(ctrl_idx, ctrl.GetPageCount()):
+            if self.GetEnabled(indx):
+                return indx
+
+        for indx in xrange(ctrl_idx, -1, -1):
+            if self.GetEnabled(indx):
+                return indx
+
+        return 0
+
+
+    def HideAllTabs(self, hidden=True):
+        """
+        Hides all tabs on the L{AuiNotebook} control.
+
+        :param `hidden`: if ``True`` hides all tabs.
+        """
+
+        self._hide_tabs = hidden
+
+
+    def SetSashDClickUnsplit(self, unsplit=True):
+        """
+        Sets whether to unsplit a splitted L{AuiNotebook} when double-clicking on a sash.
+
+        :param `unsplit`: ``True`` to unsplit on sash double-clicking, ``False`` otherwise.
+        """
+
+        self._sash_dclick_unsplit = unsplit
+
+
+    def GetSashDClickUnsplit(self):
+        """
+        Returns whether a splitted L{AuiNotebook} can be unsplitted by double-clicking
+        on the splitter sash.
+        """
+
+        return self._sash_dclick_unsplit
+
+
+    def SetMinMaxTabWidth(self, minTabWidth, maxTabWidth):
+        """
+        Sets the minimum and/or the maximum tab widths for L{AuiNotebook} when the
+        ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
+
+        Pass -1 to either `minTabWidth` or `maxTabWidth` to reset to the default tab
+        width behaviour for L{AuiNotebook}.
+
+        :param `minTabWidth`: the minimum allowed tab width, in pixels;
+        :param `maxTabWidth`: the maximum allowed tab width, in pixels.
+
+        :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
+         style is present.
+        """
+
+        if minTabWidth > maxTabWidth:
+            raise Exception("Minimum tab width must be less or equal than maximum tab width")
+
+        self._tabBounds = (minTabWidth, maxTabWidth)
+        self.SetAGWWindowStyleFlag(self._agwFlags)
+
+
+    def GetMinMaxTabWidth(self):
+        """
+        Returns the minimum and the maximum tab widths for L{AuiNotebook} when the
+        ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
+
+        :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
+         style is present.
+
+        :see: L{SetMinMaxTabWidth} for more information.
+        """
+
+        return self._tabBounds
+
+
+    def GetPageIndex(self, page_wnd):
+        """
+        Returns the page index for the specified window. If the window is not
+        found in the notebook, ``wx.NOT_FOUND`` is returned.
+        """
+
+        return self._tabs.GetIdxFromWindow(page_wnd)
+
+
+    def SetPageText(self, page_idx, text):
+        """
+        Sets the tab label for the page.
+
+        :param `page_idx`: the page index;
+        :param `text`: the new tab label.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        should_refresh = page_info.caption != text
+        page_info.caption = text
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        info = ctrl.GetPage(ctrl_idx)
+        should_refresh = should_refresh or info.caption != text
+        info.caption = text
+
+        if should_refresh:
+            ctrl.Refresh()
+            ctrl.Update()
+
+        self.UpdateTabCtrlHeight(force=True)
+
+        return True
+
+
+    def GetPageText(self, page_idx):
+        """
+        Returns the tab label for the page.
+
+        :param `page_idx`: the page index.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return ""
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        return page_info.caption
+
+
+    def SetPageBitmap(self, page_idx, bitmap):
+        """
+        Sets the tab bitmap for the page.
+
+        :param `page_idx`: the page index;
+        :param `bitmap`: an instance of `wx.Bitmap`.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        should_refresh = page_info.bitmap is not bitmap
+        page_info.bitmap = bitmap
+        if bitmap.IsOk() and not page_info.dis_bitmap.IsOk():
+            page_info.dis_bitmap = MakeDisabledBitmap(bitmap)
+
+        # tab height might have changed
+        self.UpdateTabCtrlHeight()
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        info = ctrl.GetPage(ctrl_idx)
+        should_refresh = should_refresh or info.bitmap is not bitmap
+        info.bitmap = bitmap
+        info.dis_bitmap = page_info.dis_bitmap
+        if should_refresh:
+            ctrl.Refresh()
+            ctrl.Update()
+
+        return True
+
+
+    def GetPageBitmap(self, page_idx):
+        """
+        Returns the tab bitmap for the page.
+
+        :param `page_idx`: the page index.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return wx.NullBitmap
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        return page_info.bitmap
+
+
+    def SetImageList(self, imageList):
+        """
+        Sets the image list for the L{AuiNotebook} control.
+
+        :param `imageList`: an instance of `wx.ImageList`.
+        """
+
+        self._imageList = imageList
+
+
+    def AssignImageList(self, imageList):
+        """
+        Sets the image list for the L{AuiNotebook} control.
+
+        :param `imageList`: an instance of `wx.ImageList`.
+        """
+
+        self.SetImageList(imageList)
+
+
+    def GetImageList(self):
+        """ Returns the associated image list (if any). """
+
+        return self._imageList
+
+
+    def SetPageImage(self, page, image):
+        """
+        Sets the image index for the given page.
+
+        :param `page`: the page index;
+        :param `image`: an index into the image list which was set with L{SetImageList}.
+        """
+
+        if page >= self._tabs.GetPageCount():
+            return False
+
+        if not isinstance(image, types.IntType):
+            raise Exception("The image parameter must be an integer, you passed " \
+                            "%s"%repr(image))
+
+        if not self._imageList:
+            raise Exception("To use SetPageImage you need to associate an image list " \
+                            "Using SetImageList or AssignImageList")
+
+        if image >= self._imageList.GetImageCount():
+            raise Exception("Invalid image index (%d), the image list contains only" \
+                            " (%d) bitmaps"%(image, self._imageList.GetImageCount()))
+
+        if image == -1:
+            self.SetPageBitmap(page, wx.NullBitmap)
+            return
+
+        bitmap = self._imageList.GetBitmap(image)
+        self.SetPageBitmap(page, bitmap)
+
+
+    def GetPageImage(self, page):
+        """
+        Returns the image index for the given page.
+
+        :param `page`: the given page for which to retrieve the image index.
+        """
+
+        if page >= self._tabs.GetPageCount():
+            return False
+
+        bitmap = self.GetPageBitmap(page)
+        for indx in xrange(self._imageList.GetImageCount()):
+            imgListBmp = self._imageList.GetBitmap(indx)
+            if imgListBmp == bitmap:
+                return indx
+
+        return wx.NOT_FOUND
+
+
+    def SetPageTextColour(self, page_idx, colour):
+        """
+        Sets the tab text colour for the page.
+
+        :param `page_idx`: the page index;
+        :param `colour`: an instance of `wx.Colour`.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        should_refresh = page_info.text_colour != colour
+        page_info.text_colour = colour
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        info = ctrl.GetPage(ctrl_idx)
+        should_refresh = should_refresh or info.text_colour != colour
+        info.text_colour = page_info.text_colour
+
+        if should_refresh:
+            ctrl.Refresh()
+            ctrl.Update()
+
+        return True
+
+
+    def GetPageTextColour(self, page_idx):
+        """
+        Returns the tab text colour for the page.
+
+        :param `page_idx`: the page index.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return wx.NullColour
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        return page_info.text_colour
+
+
+    def AddControlToPage(self, page_idx, control):
+        """
+        Adds a control inside a tab (not in the tab area).
+
+        :param `page_idx`: the page index;
+        :param `control`: an instance of `wx.Window`.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        page_info.control = control
+
+        # tab height might have changed
+        self.UpdateTabCtrlHeight(force=True)
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        control.Reparent(ctrl)
+
+        info = ctrl.GetPage(ctrl_idx)
+        info.control = control
+        ctrl.Refresh()
+        ctrl.Update()
+
+        return True
+
+
+    def RemoveControlFromPage(self, page_idx):
+        """
+        Removes a control from a tab (not from the tab area).
+
+        :param `page_idx`: the page index.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        page_info = self._tabs.GetPage(page_idx)
+        if page_info.control is None:
+            return False
+
+        page_info.control.Destroy()
+        page_info.control = None
+
+        # tab height might have changed
+        self.UpdateTabCtrlHeight(force=True)
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        info = ctrl.GetPage(ctrl_idx)
+        info.control = None
+        ctrl.Refresh()
+        ctrl.Update()
+
+        return True
+
+
+    def SetCloseButton(self, page_idx, hasCloseButton):
+        """
+        Sets whether a tab should display a close button or not.
+
+        :param `page_idx`: the page index;
+        :param `hasCloseButton`: ``True`` if the page displays a close button.
+
+        :note: This can only be called if ``AUI_NB_CLOSE_ON_ALL_TABS`` is specified.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        if self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS == 0:
+            raise Exception("SetCloseButton can only be used with AUI_NB_CLOSE_ON_ALL_TABS style.")
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        page_info.hasCloseButton = hasCloseButton
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        info = ctrl.GetPage(ctrl_idx)
+        info.hasCloseButton = page_info.hasCloseButton
+        ctrl.Refresh()
+        ctrl.Update()
+
+        return True
+
+
+    def HasCloseButton(self, page_idx):
+        """
+        Returns whether a tab displays a close button or not.
+
+        :param `page_idx`: the page index.
+
+        :note: This can only be called if ``AUI_NB_CLOSE_ON_ALL_TABS`` is specified.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        page_info = self._tabs.GetPage(page_idx)
+        return page_info.hasCloseButton
+
+
+    def GetSelection(self):
+        """ Returns the index of the currently active page, or -1 if none was selected. """
+
+        return self._curpage
+
+
+    def GetCurrentPage(self):
+        """ Returns the currently active page (not the index), or ``None`` if none was selected. """
+
+        if self._curpage >= 0 and self._curpage < self._tabs.GetPageCount():
+            return self.GetPage(self._curpage)
+
+        return None
+
+
+    def EnsureVisible(self, indx):
+        """
+        Ensures the input page index `indx` is visible.
+
+        :param `indx`: the page index.
+        """
+
+        self._tabs.MakeTabVisible(indx, self)
+
+
+    def SetSelection(self, new_page, force=False):
+        """
+        Sets the page selection. Calling this method will generate a page change event.
+
+        :param `new_page`: the index of the new selection;
+        :param `force`: whether to force the selection or not.
+        """
+        wnd = self._tabs.GetWindowFromIdx(new_page)
+
+        #Update page access time
+        self._tabs.GetPages()[new_page].access_time = datetime.datetime.now()
+
+        if not wnd or not self.GetEnabled(new_page):
+            return self._curpage
+
+        # don't change the page unless necessary
+        # however, clicking again on a tab should give it the focus.
+        if new_page == self._curpage and not force:
+
+            ctrl, ctrl_idx = self.FindTab(wnd)
+            if wx.Window.FindFocus() != ctrl:
+                ctrl.SetFocus()
+
+            return self._curpage
+
+        evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
+        evt.SetSelection(new_page)
+        evt.SetOldSelection(self._curpage)
+        evt.SetEventObject(self)
+
+        if not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed():
+
+            old_curpage = self._curpage
+            self._curpage = new_page
+
+            # program allows the page change
+            evt.SetEventType(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED)
+            self.GetEventHandler().ProcessEvent(evt)
+
+            if not evt.IsAllowed(): # event is no longer allowed after handler
+                return self._curpage
+
+            ctrl, ctrl_idx = self.FindTab(wnd)
+
+            if ctrl:
+                self._tabs.SetActivePage(wnd)
+                ctrl.SetActivePage(ctrl_idx)
+                self.DoSizing()
+                ctrl.DoShowHide()
+                ctrl.MakeTabVisible(ctrl_idx, ctrl)
+
+                # set fonts
+                all_panes = self._mgr.GetAllPanes()
+                for pane in all_panes:
+                    if pane.name == "dummy":
+                        continue
+
+                    tabctrl = pane.window._tabs
+                    if tabctrl != ctrl:
+                        tabctrl.SetSelectedFont(self._normal_font)
+                    else:
+                        tabctrl.SetSelectedFont(self._selected_font)
+
+                    tabctrl.Refresh()
+                    tabctrl.Update()
+
+                # Set the focus to the page if we're not currently focused on the tab.
+                # This is Firefox-like behaviour.
+                if wnd.IsShownOnScreen() and wx.Window.FindFocus() != ctrl:
+                    wnd.SetFocus()
+
+                return old_curpage
+
+        return self._curpage
+
+
+    def SetSelectionToWindow(self, win):
+        """
+        Sets the selection based on the input window `win`.
+
+        :param `win`: a `wx.Window` derived window.
+        """
+
+        idx = self._tabs.GetIdxFromWindow(win)
+
+        if idx == wx.NOT_FOUND:
+            raise Exception("invalid notebook page")
+
+        if not self.GetEnabled(idx):
+            return
+
+        # since a tab was clicked, let the parent know that we received
+        # the focus, even if we will assign that focus immediately
+        # to the child tab in the SetSelection call below
+        # (the child focus event will also let AuiManager, if any,
+        # know that the notebook control has been activated)
+
+        parent = self.GetParent()
+        if parent:
+            eventFocus = wx.ChildFocusEvent(self)
+            parent.GetEventHandler().ProcessEvent(eventFocus)
+
+        self.SetSelection(idx)
+
+
+    def SetSelectionToPage(self, page):
+        """
+        Sets the selection based on the input page.
+
+        :param `page`: an instance of L{AuiNotebookPage}.
+        """
+
+        self.SetSelectionToWindow(page.window)
+
+
+    def GetPageCount(self):
+        """ Returns the number of pages in the notebook. """
+
+        return self._tabs.GetPageCount()
+
+
+    def GetPage(self, page_idx):
+        """
+        Returns the page specified by the given index.
+
+        :param `page_idx`: the page index.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            raise Exception("invalid notebook page")
+
+        return self._tabs.GetWindowFromIdx(page_idx)
+
+
+    def GetPageInfo(self, page_idx):
+        """
+        Returns the L{AuiNotebookPage} info structure specified by the given index.
+
+        :param `page_idx`: the page index.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            raise Exception("invalid notebook page")
+
+        return self._tabs.GetPage(page_idx)
+
+
+    def GetEnabled(self, page_idx):
+        """
+        Returns whether the page specified by the index `page_idx` is enabled.
+
+        :param `page_idx`: the page index.
+        """
+
+        return self._tabs.GetEnabled(page_idx)
+
+
+    def EnableTab(self, page_idx, enable=True):
+        """
+        Enables/disables a page in the notebook.
+
+        :param `page_idx`: the page index;
+        :param `enable`: ``True`` to enable the page, ``False`` to disable it.
+        """
+
+        self._tabs.EnableTab(page_idx, enable)
+        self.Refresh()
+
+
+    def DoSizing(self):
+        """ Performs all sizing operations in each tab control. """
+
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+            tabframe.DoSizing()
+
+
+    def GetAuiManager(self):
+        """ Returns the associated L{AuiManager}. """
+
+        return self._mgr
+
+
+    def GetActiveTabCtrl(self):
+        """
+        Returns the active tab control. It is called to determine which control
+        gets new windows being added.
+        """
+
+        if self._curpage >= 0 and self._curpage < self._tabs.GetPageCount():
+
+            # find the tab ctrl with the current page
+            ctrl, idx = self.FindTab(self._tabs.GetPage(self._curpage).window)
+            if ctrl:
+                return ctrl
+
+        # no current page, just find the first tab ctrl
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+            return tabframe._tabs
+
+        # If there is no tabframe at all, create one
+        tabframe = TabFrame(self)
+        tabframe.SetTabCtrlHeight(self._tab_ctrl_height)
+        self._tab_id_counter += 1
+        tabframe._tabs = AuiTabCtrl(self, self._tab_id_counter)
+
+        tabframe._tabs.SetAGWFlags(self._agwFlags)
+        tabframe._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
+        self._mgr.AddPane(tabframe, framemanager.AuiPaneInfo().Center().CaptionVisible(False).
+                          PaneBorder((self._agwFlags & AUI_NB_SUB_NOTEBOOK) == 0))
+
+        self._mgr.Update()
+
+        return tabframe._tabs
+
+
+    def FindTab(self, page):
+        """
+        Finds the tab control that currently contains the window as well
+        as the index of the window in the tab control. It returns ``True`` if the
+        window was found, otherwise ``False``.
+
+        :param `page`: an instance of L{AuiNotebookPage}.
+        """
+
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+
+            page_idx = tabframe._tabs.GetIdxFromWindow(page)
+
+            if page_idx != -1:
+
+                ctrl = tabframe._tabs
+                idx = page_idx
+                return ctrl, idx
+
+        return None, wx.NOT_FOUND
+
+
+    def Split(self, page, direction):
+        """
+        Performs a split operation programmatically.
+
+        :param `page`: indicates the page that will be split off. This page will also become
+         the active page after the split.
+        :param `direction`: specifies where the pane should go, it should be one of the
+         following: ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, or ``wx.RIGHT``.
+        """
+
+        cli_size = self.GetClientSize()
+
+        # get the page's window pointer
+        wnd = self.GetPage(page)
+        if not wnd:
+            return
+
+        # notebooks with 1 or less pages can't be split
+        if self.GetPageCount() < 2:
+            return
+
+        # find out which tab control the page currently belongs to
+
+        src_tabs, src_idx = self.FindTab(wnd)
+        if not src_tabs:
+            return
+
+        # choose a split size
+        if self.GetPageCount() > 2:
+            split_size = self.CalculateNewSplitSize()
+        else:
+            # because there are two panes, always split them
+            # equally
+            split_size = self.GetClientSize()
+            split_size.x /= 2
+            split_size.y /= 2
+
+        # create a new tab frame
+        new_tabs = TabFrame(self)
+        new_tabs._rect = wx.RectPS(wx.Point(0, 0), split_size)
+        new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
+        self._tab_id_counter += 1
+        new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
+
+        new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
+        new_tabs._tabs.SetAGWFlags(self._agwFlags)
+        dest_tabs = new_tabs._tabs
+
+        page_info = src_tabs.GetPage(src_idx)
+        if page_info.control:
+            self.ReparentControl(page_info.control, dest_tabs)
+
+        # create a pane info structure with the information
+        # about where the pane should be added
+        pane_info = framemanager.AuiPaneInfo().Bottom().CaptionVisible(False)
+
+        if direction == wx.LEFT:
+
+            pane_info.Left()
+            mouse_pt = wx.Point(0, cli_size.y/2)
+
+        elif direction == wx.RIGHT:
+
+            pane_info.Right()
+            mouse_pt = wx.Point(cli_size.x, cli_size.y/2)
+
+        elif direction == wx.TOP:
+
+            pane_info.Top()
+            mouse_pt = wx.Point(cli_size.x/2, 0)
+
+        elif direction == wx.BOTTOM:
+
+            pane_info.Bottom()
+            mouse_pt = wx.Point(cli_size.x/2, cli_size.y)
+
+        self._mgr.AddPane(new_tabs, pane_info, mouse_pt)
+        self._mgr.Update()
+
+        # remove the page from the source tabs
+        page_info.active = False
+
+        src_tabs.RemovePage(page_info.window)
+
+        if src_tabs.GetPageCount() > 0:
+            src_tabs.SetActivePage(0)
+            src_tabs.DoShowHide()
+            src_tabs.Refresh()
+
+        # add the page to the destination tabs
+        dest_tabs.InsertPage(page_info.window, page_info, 0)
+
+        if src_tabs.GetPageCount() == 0:
+            self.RemoveEmptyTabFrames()
+
+        self.DoSizing()
+        dest_tabs.DoShowHide()
+        dest_tabs.Refresh()
+
+        # force the set selection function reset the selection
+        self._curpage = -1
+
+        # set the active page to the one we just split off
+        self.SetSelectionToPage(page_info)
+
+        self.UpdateHintWindowSize()
+
+
+    def UnSplit(self):
+        """ Restores original view after a tab split. """
+
+        self.Freeze()
+
+        # remember the tab now selected
+        nowSelected = self.GetSelection()
+        # select first tab as destination
+        self.SetSelection(0)
+        # iterate all other tabs
+        for idx in xrange(1, self.GetPageCount()):
+            # get win reference
+            win = self.GetPage(idx)
+            # get tab title
+            title = self.GetPageText(idx)
+            # get page bitmap
+            bmp = self.GetPageBitmap(idx)
+            # remove from notebook
+            self.RemovePage(idx)
+            # re-add in the same position so it will tab
+            self.InsertPage(idx, win, title, False, bmp)
+        # restore orignial selected tab
+        self.SetSelection(nowSelected)
+
+        self.Thaw()
+
+
+    def ReparentControl(self, control, dest_tabs):
+        """
+        Reparents a control added inside a tab.
+
+        :param `control`: an instance of `wx.Window`;
+        :param `dest_tabs`: the destination L{AuiTabCtrl}.
+        """
+
+        control.Hide()
+        control.Reparent(dest_tabs)
+
+
+    def UnsplitDClick(self, part, sash_size, pos):
+        """
+        Unsplit the L{AuiNotebook} on sash double-click.
+
+        :param `part`: an UI part representing the sash;
+        :param `sash_size`: the sash size;
+        :param `pos`: the double-click mouse position.
+
+        :warning: Due to a bug on MSW, for disabled pages `wx.FindWindowAtPoint`
+         returns the wrong window. See http://trac.wxwidgets.org/ticket/2942
+        """
+
+        if not self._sash_dclick_unsplit:
+            # Unsplit not allowed
+            return
+
+        pos1 = wx.Point(*pos)
+        pos2 = wx.Point(*pos)
+        if part.orientation == wx.HORIZONTAL:
+            pos1.y -= 2*sash_size
+            pos2.y += 2*sash_size + self.GetTabCtrlHeight()
+        elif part.orientation == wx.VERTICAL:
+            pos1.x -= 2*sash_size
+            pos2.x += 2*sash_size
+        else:
+            raise Exception("Invalid UI part orientation")
+
+        pos1, pos2 = self.ClientToScreen(pos1), self.ClientToScreen(pos2)
+        win1, win2 = wx.FindWindowAtPoint(pos1), wx.FindWindowAtPoint(pos2)
+
+        if isinstance(win1, wx.ScrollBar):
+            # Hopefully it will work
+            pos1 = wx.Point(*pos)
+            shift = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + 2*(sash_size+1)
+            if part.orientation == wx.HORIZONTAL:
+                pos1.y -= shift
+            else:
+                pos1.x -= shift
+
+            pos1 = self.ClientToScreen(pos1)
+            win1 = wx.FindWindowAtPoint(pos1)
+
+        if isinstance(win2, wx.ScrollBar):
+            pos2 = wx.Point(*pos)
+            shift = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + 2*(sash_size+1)
+            if part.orientation == wx.HORIZONTAL:
+                pos2.y += shift
+            else:
+                pos2.x += shift
+
+            pos2 = self.ClientToScreen(pos2)
+            win2 = wx.FindWindowAtPoint(pos2)
+
+        if not win1 or not win2:
+            # How did we get here?
+            return
+
+        if isinstance(win1, AuiNotebook) or isinstance(win2, AuiNotebook):
+            # This is a bug on MSW, for disabled pages wx.FindWindowAtPoint
+            # returns the wrong window.
+            # See http://trac.wxwidgets.org/ticket/2942
+            return
+
+        tab_frame1, tab_frame2 = self.GetTabFrameFromWindow(win1), self.GetTabFrameFromWindow(win2)
+
+        if not tab_frame1 or not tab_frame2:
+            return
+
+        tab_ctrl_1, tab_ctrl_2 = tab_frame1._tabs, tab_frame2._tabs
+
+        if tab_ctrl_1.GetPageCount() > tab_ctrl_2.GetPageCount():
+            src_tabs = tab_ctrl_2
+            dest_tabs = tab_ctrl_1
+        else:
+            src_tabs = tab_ctrl_1
+            dest_tabs = tab_ctrl_2
+
+        selection = -1
+        page_count = dest_tabs.GetPageCount()
+
+        for page in xrange(src_tabs.GetPageCount()-1, -1, -1):
+            # remove the page from the source tabs
+            page_info = src_tabs.GetPage(page)
+            if page_info.active:
+                selection = page_count + page
+            src_tabs.RemovePage(page_info.window)
+
+            # add the page to the destination tabs
+            dest_tabs.AddPage(page_info.window, page_info)
+            if page_info.control:
+                self.ReparentControl(page_info.control, dest_tabs)
+
+        self.RemoveEmptyTabFrames()
+
+        dest_tabs.DoShowHide()
+        self.DoSizing()
+        dest_tabs.Refresh()
+        self._mgr.Update()
+        if selection > 0:
+            wx.CallAfter(dest_tabs.MakeTabVisible, selection, self)
+
+
+    def OnSize(self, event):
+        """
+        Handles the ``wx.EVT_SIZE`` event for L{AuiNotebook}.
+
+        :param `event`: a `wx.SizeEvent` event to be processed.
+        """
+
+        self.UpdateHintWindowSize()
+        event.Skip()
+
+
+    def OnTabClicked(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_PAGE_CHANGING`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        if self._textCtrl is not None:
+            self._textCtrl.StopEditing()
+
+        ctrl = event.GetEventObject()
+        assert ctrl != None
+
+        wnd = ctrl.GetWindowFromIdx(event.GetSelection())
+        assert wnd != None
+
+        self.SetSelectionToWindow(wnd)
+
+
+    def OnTabBgDClick(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_BG_DCLICK`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        if self._textCtrl is not None:
+            self._textCtrl.StopEditing()
+
+        # notify owner that the tabbar background has been double-clicked
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, self.GetId())
+        e.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnTabDClick(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_TAB_DCLICK`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        # notify owner that the tabbar background has been double-clicked
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, self.GetId())
+        e.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(e)
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        if not self.IsRenamable(event.GetSelection()):
+            return
+
+        self.EditTab(event.GetSelection())
+
+
+    def OnTabBeginDrag(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_BEGIN_DRAG`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        self._last_drag_x = 0
+
+
+    def OnTabDragMotion(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_DRAG_MOTION`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        if self._textCtrl is not None:
+            self._textCtrl.StopEditing()
+
+        screen_pt = wx.GetMousePosition()
+        client_pt = self.ScreenToClient(screen_pt)
+        zero = wx.Point(0, 0)
+
+        src_tabs = event.GetEventObject()
+        dest_tabs = self.GetTabCtrlFromPoint(client_pt)
+
+        if dest_tabs == src_tabs:
+
+            # always hide the hint for inner-tabctrl drag
+            self._mgr.HideHint()
+
+            # if tab moving is not allowed, leave
+            if not self._agwFlags & AUI_NB_TAB_MOVE:
+                return
+
+            pt = dest_tabs.ScreenToClient(screen_pt)
+
+            # this is an inner-tab drag/reposition
+            dest_location_tab = dest_tabs.TabHitTest(pt.x, pt.y)
+
+            if dest_location_tab:
+
+                src_idx = event.GetSelection()
+                dest_idx = dest_tabs.GetIdxFromWindow(dest_location_tab)
+
+                # prevent jumpy drag
+                if (src_idx == dest_idx) or dest_idx == -1 or \
+                   (src_idx > dest_idx and self._last_drag_x <= pt.x) or \
+                   (src_idx < dest_idx and self._last_drag_x >= pt.x):
+
+                    self._last_drag_x = pt.x
+                    return
+
+                src_tab = dest_tabs.GetWindowFromIdx(src_idx)
+                dest_tabs.MovePage(src_tab, dest_idx)
+                self._tabs.MovePage(self._tabs.GetPage(src_idx).window, dest_idx)
+                dest_tabs.SetActivePage(dest_idx)
+                dest_tabs.DoShowHide()
+                dest_tabs.Refresh()
+                self._last_drag_x = pt.x
+
+            return
+
+        # if external drag is allowed, check if the tab is being dragged
+        # over a different AuiNotebook control
+        if self._agwFlags & AUI_NB_TAB_EXTERNAL_MOVE:
+
+            tab_ctrl = wx.FindWindowAtPoint(screen_pt)
+
+            # if we aren't over any window, stop here
+            if not tab_ctrl:
+                if self._agwFlags & AUI_NB_TAB_FLOAT:
+                    if self.IsMouseWellOutsideWindow():
+                        hintRect = wx.RectPS(screen_pt, (400, 300))
+                        # Use CallAfter so we overwrite the hint that might be
+                        # shown by our superclass:
+                        wx.CallAfter(self._mgr.ShowHint, hintRect)
+                return
+
+            # make sure we are not over the hint window
+            if not isinstance(tab_ctrl, wx.Frame):
+                while tab_ctrl:
+                    if isinstance(tab_ctrl, AuiTabCtrl):
+                        break
+
+                    tab_ctrl = tab_ctrl.GetParent()
+
+                if tab_ctrl:
+                    nb = tab_ctrl.GetParent()
+
+                    if nb != self:
+
+                        hint_rect = tab_ctrl.GetClientRect()
+                        hint_rect.x, hint_rect.y = tab_ctrl.ClientToScreenXY(hint_rect.x, hint_rect.y)
+                        self._mgr.ShowHint(hint_rect)
+                        return
+
+            else:
+
+                if not dest_tabs:
+                    # we are either over a hint window, or not over a tab
+                    # window, and there is no where to drag to, so exit
+                    return
+
+        if self._agwFlags & AUI_NB_TAB_FLOAT:
+            if self.IsMouseWellOutsideWindow():
+                hintRect = wx.RectPS(screen_pt, (400, 300))
+                # Use CallAfter so we overwrite the hint that might be
+                # shown by our superclass:
+                wx.CallAfter(self._mgr.ShowHint, hintRect)
+                return
+
+        # if there are less than two panes, split can't happen, so leave
+        if self._tabs.GetPageCount() < 2:
+            return
+
+        # if tab moving is not allowed, leave
+        if not self._agwFlags & AUI_NB_TAB_SPLIT:
+            return
+
+        if dest_tabs:
+
+            hint_rect = dest_tabs.GetRect()
+            hint_rect.x, hint_rect.y = self.ClientToScreenXY(hint_rect.x, hint_rect.y)
+            self._mgr.ShowHint(hint_rect)
+
+        else:
+            rect = self._mgr.CalculateHintRect(self._dummy_wnd, client_pt, zero)
+            if rect.IsEmpty():
+                self._mgr.HideHint()
+                return
+
+            hit_wnd = wx.FindWindowAtPoint(screen_pt)
+            if hit_wnd and not isinstance(hit_wnd, AuiNotebook):
+                tab_frame = self.GetTabFrameFromWindow(hit_wnd)
+                if tab_frame:
+                    hint_rect = wx.Rect(*tab_frame._rect)
+                    hint_rect.x, hint_rect.y = self.ClientToScreenXY(hint_rect.x, hint_rect.y)
+                    rect.Intersect(hint_rect)
+                    self._mgr.ShowHint(rect)
+                else:
+                    self._mgr.DrawHintRect(self._dummy_wnd, client_pt, zero)
+            else:
+                self._mgr.DrawHintRect(self._dummy_wnd, client_pt, zero)
+
+
+    def OnTabEndDrag(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_END_DRAG`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        self._mgr.HideHint()
+
+        src_tabs = event.GetEventObject()
+        if not src_tabs:
+            raise Exception("no source object?")
+
+        # get the mouse position, which will be used to determine the drop point
+        mouse_screen_pt = wx.GetMousePosition()
+        mouse_client_pt = self.ScreenToClient(mouse_screen_pt)
+
+        # check for an external move
+        if self._agwFlags & AUI_NB_TAB_EXTERNAL_MOVE:
+            tab_ctrl = wx.FindWindowAtPoint(mouse_screen_pt)
+
+            while tab_ctrl:
+
+                if isinstance(tab_ctrl, AuiTabCtrl):
+                    break
+
+                tab_ctrl = tab_ctrl.GetParent()
+
+            if tab_ctrl:
+
+                nb = tab_ctrl.GetParent()
+
+                if nb != self:
+
+                    # find out from the destination control
+                    # if it's ok to drop this tab here
+                    e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, self.GetId())
+                    e.SetSelection(event.GetSelection())
+                    e.SetOldSelection(event.GetSelection())
+                    e.SetEventObject(self)
+                    e.SetDragSource(self)
+                    e.Veto() # dropping must be explicitly approved by control owner
+
+                    nb.GetEventHandler().ProcessEvent(e)
+
+                    if not e.IsAllowed():
+
+                        # no answer or negative answer
+                        self._mgr.HideHint()
+                        return
+
+                    # drop was allowed
+                    src_idx = event.GetSelection()
+                    src_page = src_tabs.GetWindowFromIdx(src_idx)
+
+                    # Check that it's not an impossible parent relationship
+                    p = nb
+                    while p and not p.IsTopLevel():
+                        if p == src_page:
+                            return
+
+                        p = p.GetParent()
+
+                    # get main index of the page
+                    main_idx = self._tabs.GetIdxFromWindow(src_page)
+                    if main_idx == wx.NOT_FOUND:
+                        raise Exception("no source page?")
+
+                    # make a copy of the page info
+                    page_info = self._tabs.GetPage(main_idx)
+
+                    # remove the page from the source notebook
+                    self.RemovePage(main_idx)
+
+                    # reparent the page
+                    src_page.Reparent(nb)
+
+                    # Reparent the control in a tab (if any)
+                    if page_info.control:
+                        self.ReparentControl(page_info.control, tab_ctrl)
+
+                    # find out the insert idx
+                    dest_tabs = tab_ctrl
+                    pt = dest_tabs.ScreenToClient(mouse_screen_pt)
+
+                    target = dest_tabs.TabHitTest(pt.x, pt.y)
+                    insert_idx = -1
+                    if target:
+                        insert_idx = dest_tabs.GetIdxFromWindow(target)
+
+                    # add the page to the new notebook
+                    if insert_idx == -1:
+                        insert_idx = dest_tabs.GetPageCount()
+
+                    dest_tabs.InsertPage(page_info.window, page_info, insert_idx)
+                    nb._tabs.AddPage(page_info.window, page_info)
+
+                    nb.DoSizing()
+                    dest_tabs.DoShowHide()
+                    dest_tabs.Refresh()
+
+                    # set the selection in the destination tab control
+                    nb.SetSelectionToPage(page_info)
+
+                    # notify owner that the tab has been dragged
+                    e2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, self.GetId())
+                    e2.SetSelection(event.GetSelection())
+                    e2.SetOldSelection(event.GetSelection())
+                    e2.SetEventObject(self)
+                    self.GetEventHandler().ProcessEvent(e2)
+
+                    # notify the target notebook that the tab has been dragged
+                    e3 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, nb.GetId())
+                    e3.SetSelection(insert_idx)
+                    e3.SetOldSelection(insert_idx)
+                    e3.SetEventObject(nb)
+                    nb.GetEventHandler().ProcessEvent(e3)
+
+                    return
+
+        if self._agwFlags & AUI_NB_TAB_FLOAT:
+            self._mgr.HideHint()
+            if self.IsMouseWellOutsideWindow():
+                # Use CallAfter so we our superclass can deal with the event first
+                wx.CallAfter(self.FloatPage, self.GetSelection())
+                event.Skip()
+                return
+
+        # only perform a tab split if it's allowed
+        dest_tabs = None
+
+        if self._agwFlags & AUI_NB_TAB_SPLIT and self._tabs.GetPageCount() >= 2:
+
+            # If the pointer is in an existing tab frame, do a tab insert
+            hit_wnd = wx.FindWindowAtPoint(mouse_screen_pt)
+            tab_frame = self.GetTabFrameFromTabCtrl(hit_wnd)
+            insert_idx = -1
+
+            if tab_frame:
+
+                dest_tabs = tab_frame._tabs
+
+                if dest_tabs == src_tabs:
+                    return
+
+                pt = dest_tabs.ScreenToClient(mouse_screen_pt)
+                target = dest_tabs.TabHitTest(pt.x, pt.y)
+
+                if target:
+                    insert_idx = dest_tabs.GetIdxFromWindow(target)
+
+            else:
+
+                zero = wx.Point(0, 0)
+                rect = self._mgr.CalculateHintRect(self._dummy_wnd, mouse_client_pt, zero)
+
+                if rect.IsEmpty():
+                    # there is no suitable drop location here, exit out
+                    return
+
+                # If there is no tabframe at all, create one
+                new_tabs = TabFrame(self)
+                new_tabs._rect = wx.RectPS(wx.Point(0, 0), self.CalculateNewSplitSize())
+                new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
+                self._tab_id_counter += 1
+                new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
+                new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
+                new_tabs._tabs.SetAGWFlags(self._agwFlags)
+
+                self._mgr.AddPane(new_tabs, framemanager.AuiPaneInfo().Bottom().CaptionVisible(False), mouse_client_pt)
+                self._mgr.Update()
+                dest_tabs = new_tabs._tabs
+
+            # remove the page from the source tabs
+            page_info = src_tabs.GetPage(event.GetSelection())
+
+            if page_info.control:
+                self.ReparentControl(page_info.control, dest_tabs)
+
+            page_info.active = False
+            src_tabs.RemovePage(page_info.window)
+
+            if src_tabs.GetPageCount() > 0:
+                src_tabs.SetActivePage(0)
+                src_tabs.DoShowHide()
+                src_tabs.Refresh()
+
+            # add the page to the destination tabs
+            if insert_idx == -1:
+                insert_idx = dest_tabs.GetPageCount()
+
+            dest_tabs.InsertPage(page_info.window, page_info, insert_idx)
+
+            if src_tabs.GetPageCount() == 0:
+                self.RemoveEmptyTabFrames()
+
+            self.DoSizing()
+            dest_tabs.DoShowHide()
+            dest_tabs.Refresh()
+
+            # force the set selection function reset the selection
+            self._curpage = -1
+
+            # set the active page to the one we just split off
+            self.SetSelectionToPage(page_info)
+
+            self.UpdateHintWindowSize()
+
+        # notify owner that the tab has been dragged
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, self.GetId())
+        e.SetSelection(event.GetSelection())
+        e.SetOldSelection(event.GetSelection())
+        e.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnTabCancelDrag(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_CANCEL_DRAG`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        self._mgr.HideHint()
+
+        src_tabs = event.GetEventObject()
+        if not src_tabs:
+            raise Exception("no source object?")
+
+
+    def IsMouseWellOutsideWindow(self):
+        """ Returns whether the mouse is well outside the L{AuiNotebook} screen rectangle. """
+
+        screen_rect = self.GetScreenRect()
+        screen_rect.Inflate(50, 50)
+
+        return not screen_rect.Contains(wx.GetMousePosition())
+
+
+    def FloatPage(self, page_index):
+        """
+        Float the page in `page_index` by reparenting it to a floating frame.
+
+        :param `page_index`: the index of the page to be floated.
+
+        :warning: When the notebook is more or less full screen, tabs cannot be dragged far
+         enough outside of the notebook to become floating pages.
+        """
+
+        root_manager = framemanager.GetManager(self)
+        page_title = self.GetPageText(page_index)
+        page_contents = self.GetPage(page_index)
+        page_bitmap = self.GetPageBitmap(page_index)
+        text_colour = self.GetPageTextColour(page_index)
+        info = self.GetPageInfo(page_index)
+
+        if root_manager and root_manager != self._mgr:
+            root_manager = framemanager.GetManager(self)
+
+            if hasattr(page_contents, "__floating_size__"):
+                floating_size = wx.Size(*page_contents.__floating_size__)
+            else:
+                floating_size = page_contents.GetBestSize()
+                if floating_size == wx.DefaultSize:
+                    floating_size = wx.Size(300, 200)
+
+            page_contents.__page_index__ = page_index
+            page_contents.__aui_notebook__ = self
+            page_contents.__text_colour__ = text_colour
+            page_contents.__control__ = info.control
+
+            if info.control:
+                info.control.Reparent(page_contents)
+                info.control.Hide()
+                info.control = None
+
+            self.RemovePage(page_index)
+            self.RemoveEmptyTabFrames()
+
+            pane_info = framemanager.AuiPaneInfo().Float().FloatingPosition(wx.GetMousePosition()). \
+                        FloatingSize(floating_size).BestSize(floating_size).Name("__floating__%s"%page_title). \
+                        Caption(page_title).Icon(page_bitmap)
+            root_manager.AddPane(page_contents, pane_info)
+            root_manager.Bind(framemanager.EVT_AUI_PANE_CLOSE, self.OnCloseFloatingPage)
+            self.GetActiveTabCtrl().DoShowHide()
+            self.DoSizing()
+            root_manager.Update()
+
+        else:
+            frame = wx.Frame(self, title=page_title,
+                             style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_TOOL_WINDOW|
+                                   wx.FRAME_FLOAT_ON_PARENT | wx.FRAME_NO_TASKBAR)
+
+            if info.control:
+                info.control.Reparent(frame)
+                info.control.Hide()
+
+            frame.bitmap = page_bitmap
+            frame.page_index = page_index
+            frame.text_colour = text_colour
+            frame.control = info.control
+            page_contents.Reparent(frame)
+            frame.Bind(wx.EVT_CLOSE, self.OnCloseFloatingPage)
+            frame.Move(wx.GetMousePosition())
+            frame.Show()
+            self.RemovePage(page_index)
+
+            self.RemoveEmptyTabFrames()
+
+        wx.CallAfter(self.RemoveEmptyTabFrames)
+
+
+    def OnCloseFloatingPage(self, event):
+        """
+        Handles the ``wx.EVT_CLOSE`` event for a floating page in L{AuiNotebook}.
+
+        :param `event`: a `wx.CloseEvent` event to be processed.
+        """
+
+        root_manager = framemanager.GetManager(self)
+        if root_manager and root_manager != self._mgr:
+            pane = event.pane
+            if pane.name.startswith("__floating__"):
+                self.ReDockPage(pane)
+                return
+
+            event.Skip()
+        else:
+            event.Skip()
+            frame = event.GetEventObject()
+            page_title = frame.GetTitle()
+            page_contents = list(frame.GetChildren())[-1]
+            page_contents.Reparent(self)
+            self.InsertPage(frame.page_index, page_contents, page_title, select=True, bitmap=frame.bitmap, control=frame.control)
+
+            if frame.control:
+                src_tabs, idx = self.FindTab(page_contents)
+                frame.control.Reparent(src_tabs)
+                frame.control.Hide()
+                frame.control = None
+
+            self.SetPageTextColour(frame.page_index, frame.text_colour)
+
+
+    def ReDockPage(self, pane):
+        """
+        Re-docks a floating L{AuiNotebook} tab in the original position, when possible.
+
+        :param `pane`: an instance of L{framemanager.AuiPaneInfo}.
+        """
+
+        root_manager = framemanager.GetManager(self)
+
+        pane.window.__floating_size__ = wx.Size(*pane.floating_size)
+        page_index = pane.window.__page_index__
+        text_colour = pane.window.__text_colour__
+        control = pane.window.__control__
+
+        root_manager.DetachPane(pane.window)
+        self.InsertPage(page_index, pane.window, pane.caption, True, pane.icon, control=control)
+
+        self.SetPageTextColour(page_index, text_colour)
+        self.GetActiveTabCtrl().DoShowHide()
+        self.DoSizing()
+        if control:
+            self.UpdateTabCtrlHeight(force=True)
+
+        self._mgr.Update()
+        root_manager.Update()
+
+
+    def GetTabCtrlFromPoint(self, pt):
+        """
+        Returns the tab control at the specified point.
+
+        :param `pt`: a `wx.Point` object.
+        """
+
+        # if we've just removed the last tab from the source
+        # tab set, the remove the tab control completely
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+            if tabframe._tab_rect.Contains(pt):
+                return tabframe._tabs
+
+        return None
+
+
+    def GetTabFrameFromTabCtrl(self, tab_ctrl):
+        """
+        Returns the tab frame associated with a tab control.
+
+        :param `tab_ctrl`: an instance of L{AuiTabCtrl}.
+        """
+
+        # if we've just removed the last tab from the source
+        # tab set, the remove the tab control completely
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+            if tabframe._tabs == tab_ctrl:
+                return tabframe
+
+        return None
+
+
+    def GetTabFrameFromWindow(self, wnd):
+        """
+        Returns the tab frame associated with a window.
+
+        :param `wnd`: an instance of `wx.Window`.
+        """
+
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            tabframe = pane.window
+            for page in tabframe._tabs.GetPages():
+                if wnd == page.window:
+                    return tabframe
+
+        return None
+
+
+    def RemoveEmptyTabFrames(self):
+        """ Removes all the empty tab frames. """
+
+        # if we've just removed the last tab from the source
+        # tab set, the remove the tab control completely
+        all_panes = self._mgr.GetAllPanes()
+
+        for indx in xrange(len(all_panes)-1, -1, -1):
+            pane = all_panes[indx]
+            if pane.name == "dummy":
+                continue
+
+            tab_frame = pane.window
+            if tab_frame._tabs.GetPageCount() == 0:
+                self._mgr.DetachPane(tab_frame)
+                tab_frame._tabs.Destroy()
+                tab_frame._tabs = None
+                del tab_frame
+
+        # check to see if there is still a center pane
+        # if there isn't, make a frame the center pane
+        first_good = None
+        center_found = False
+
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+
+            if pane.dock_direction == AUI_DOCK_CENTRE:
+                center_found = True
+            if not first_good:
+                first_good = pane.window
+
+        if not center_found and first_good:
+            self._mgr.GetPane(first_good).Centre()
+
+        if not self.IsBeingDeleted():
+            self._mgr.Update()
+
+
+    def OnChildFocusNotebook(self, event):
+        """
+        Handles the ``wx.EVT_CHILD_FOCUS`` event for L{AuiNotebook}.
+
+        :param `event`: a `wx.ChildFocusEvent` event to be processed.
+        """
+
+        # if we're dragging a tab, don't change the current selection.
+        # This code prevents a bug that used to happen when the hint window
+        # was hidden.  In the bug, the focus would return to the notebook
+        # child, which would then enter this handler and call
+        # SetSelection, which is not desired turn tab dragging.
+
+        event.Skip()
+
+        all_panes = self._mgr.GetAllPanes()
+        for pane in all_panes:
+            if pane.name == "dummy":
+                continue
+            tabframe = pane.window
+            if tabframe._tabs.IsDragging():
+                return
+
+##        # change the tab selection to the child
+##        # which was focused
+##        idx = self._tabs.GetIdxFromWindow(event.GetWindow())
+##        if idx != -1 and idx != self._curpage:
+##            self.SetSelection(idx)
+
+
+    def SetNavigatorIcon(self, bmp):
+        """
+        Sets the icon used by the L{TabNavigatorWindow}.
+
+        :param `bmp`: an instance of `wx.Bitmap`.
+        """
+
+        if isinstance(bmp, wx.Bitmap) and bmp.IsOk():
+            # Make sure image is proper size
+            if bmp.GetSize() != (16, 16):
+                img = bmp.ConvertToImage()
+                img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
+                bmp = wx.BitmapFromImage(img)
+            self._naviIcon = bmp
+        else:
+            raise TypeError, "SetNavigatorIcon requires a valid bitmap"
+
+
+    def OnNavigationKeyNotebook(self, event):
+        """
+        Handles the ``wx.EVT_NAVIGATION_KEY`` event for L{AuiNotebook}.
+
+        :param `event`: a `wx.NavigationKeyEvent` event to be processed.
+        """
+
+        if event.IsWindowChange():
+            if self._agwFlags & AUI_NB_SMART_TABS:
+                if not self._popupWin:
+                    self._popupWin = TabNavigatorWindow(self, self._naviIcon)
+                    self._popupWin.SetReturnCode(wx.ID_OK)
+                    self._popupWin.ShowModal()
+                    idx = self._popupWin.GetSelectedPage()
+                    self._popupWin.Destroy()
+                    self._popupWin = None
+                    # Need to do CallAfter so that the selection and its
+                    # associated events get processed outside the context of
+                    # this key event. Not doing so causes odd issues with the
+                    # window focus under certain use cases on Windows.
+                    wx.CallAfter(self.SetSelection, idx, True)
+                else:
+                    # a dialog is already opened
+                    self._popupWin.OnNavigationKey(event)
+                    return
+            else:
+                # change pages
+                # FIXME: the problem with this is that if we have a split notebook,
+                # we selection may go all over the place.
+                self.AdvanceSelection(event.GetDirection())
+
+        else:
+            # we get this event in 3 cases
+            #
+            # a) one of our pages might have generated it because the user TABbed
+            # out from it in which case we should propagate the event upwards and
+            # our parent will take care of setting the focus to prev/next sibling
+            #
+            # or
+            #
+            # b) the parent panel wants to give the focus to us so that we
+            # forward it to our selected page. We can't deal with this in
+            # OnSetFocus() because we don't know which direction the focus came
+            # from in this case and so can't choose between setting the focus to
+            # first or last panel child
+            #
+            # or
+            #
+            # c) we ourselves (see MSWTranslateMessage) generated the event
+            #
+            parent = self.GetParent()
+
+            # the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
+            isFromParent = event.GetEventObject() == parent
+            isFromSelf = event.GetEventObject() == self
+
+            if isFromParent or isFromSelf:
+
+                # no, it doesn't come from child, case (b) or (c): forward to a
+                # page but only if direction is backwards (TAB) or from ourselves,
+                if self.GetSelection() != wx.NOT_FOUND and (not event.GetDirection() or isFromSelf):
+
+                    # so that the page knows that the event comes from it's parent
+                    # and is being propagated downwards
+                    event.SetEventObject(self)
+
+                    page = self.GetPage(self.GetSelection())
+                    if not page.GetEventHandler().ProcessEvent(event):
+                        page.SetFocus()
+
+                    #else: page manages focus inside it itself
+
+                else: # otherwise set the focus to the notebook itself
+
+                    self.SetFocus()
+
+            else:
+
+                # send this event back for the 'wraparound' focus.
+                winFocus = event.GetCurrentFocus()
+
+                if winFocus:
+                    event.SetEventObject(self)
+                    winFocus.GetEventHandler().ProcessEvent(event)
+
+
+    def OnTabButton(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_BUTTON`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        button_id = event.GetInt()
+
+        if button_id == AUI_BUTTON_CLOSE:
+
+            selection = event.GetSelection()
+
+            if selection == -1:
+
+                # if the close button is to the right, use the active
+                # page selection to determine which page to close
+                selection = tabs.GetActivePage()
+
+            if selection == -1 or not tabs.GetEnabled(selection):
+                return
+
+            if selection != -1:
+
+                close_wnd = tabs.GetWindowFromIdx(selection)
+
+                if close_wnd.GetName() == "__fake__page__":
+                    # This is a notebook preview
+                    previous_active, page_status = close_wnd.__previousStatus
+                    for page, status in zip(tabs.GetPages(), page_status):
+                        page.enabled = status
+
+                    main_idx = self._tabs.GetIdxFromWindow(close_wnd)
+                    self.DeletePage(main_idx)
+
+                    if previous_active >= 0:
+                        tabs.SetActivePage(previous_active)
+                        page_count = tabs.GetPageCount()
+                        selection = -1
+
+                        for page in xrange(page_count):
+                            # remove the page from the source tabs
+                            page_info = tabs.GetPage(page)
+                            if page_info.active:
+                                selection = page
+                                break
+
+                        tabs.DoShowHide()
+                        self.DoSizing()
+                        tabs.Refresh()
+
+                        if selection >= 0:
+                            wx.CallAfter(tabs.MakeTabVisible, selection, self)
+
+                    # Don't fire the event
+                    return
+
+                # ask owner if it's ok to close the tab
+                e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, self.GetId())
+                idx = self._tabs.GetIdxFromWindow(close_wnd)
+                e.SetSelection(idx)
+                e.SetOldSelection(event.GetSelection())
+                e.SetEventObject(self)
+                self.GetEventHandler().ProcessEvent(e)
+                if not e.IsAllowed():
+                    return
+
+                if repr(close_wnd.__class__).find("AuiMDIChildFrame") >= 0:
+                    close_wnd.Close()
+
+                else:
+                    main_idx = self._tabs.GetIdxFromWindow(close_wnd)
+                    self.DeletePage(main_idx)
+
+                # notify owner that the tab has been closed
+                e2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, self.GetId())
+                e2.SetSelection(idx)
+                e2.SetEventObject(self)
+                self.GetEventHandler().ProcessEvent(e2)
+
+                if self.GetPageCount() == 0:
+                    mgr = self.GetAuiManager()
+                    win = mgr.GetManagedWindow()
+                    win.SendSizeEvent()
+
+
+    def OnTabMiddleDown(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        # patch event through to owner
+        wnd = tabs.GetWindowFromIdx(event.GetSelection())
+
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.GetId())
+        e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
+        e.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnTabMiddleUp(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_TAB_MIDDLE_UP`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        # if the AUI_NB_MIDDLE_CLICK_CLOSE is specified, middle
+        # click should act like a tab close action.  However, first
+        # give the owner an opportunity to handle the middle up event
+        # for custom action
+
+        wnd = tabs.GetWindowFromIdx(event.GetSelection())
+
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, self.GetId())
+        e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
+        e.SetEventObject(self)
+        if self.GetEventHandler().ProcessEvent(e):
+            return
+        if not e.IsAllowed():
+            return
+
+        # check if we are supposed to close on middle-up
+        if self._agwFlags & AUI_NB_MIDDLE_CLICK_CLOSE == 0:
+            return
+
+        # simulate the user pressing the close button on the tab
+        event.SetInt(AUI_BUTTON_CLOSE)
+        self.OnTabButton(event)
+
+
+    def OnTabRightDown(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_TAB_RIGHT_DOWN`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        # patch event through to owner
+        wnd = tabs.GetWindowFromIdx(event.GetSelection())
+
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, self.GetId())
+        e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
+        e.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(e)
+
+
+    def OnTabRightUp(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_TAB_RIGHT_UP`` event for L{AuiNotebook}.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        tabs = event.GetEventObject()
+        if not tabs.GetEnabled(event.GetSelection()):
+            return
+
+        # patch event through to owner
+        wnd = tabs.GetWindowFromIdx(event.GetSelection())
+
+        e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, self.GetId())
+        e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
+        e.SetEventObject(self)
+        self.GetEventHandler().ProcessEvent(e)
+
+
+    def SetNormalFont(self, font):
+        """
+        Sets the normal font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._normal_font = font
+        self.GetArtProvider().SetNormalFont(font)
+
+
+    def SetSelectedFont(self, font):
+        """
+        Sets the selected tab font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._selected_font = font
+        self.GetArtProvider().SetSelectedFont(font)
+
+
+    def SetMeasuringFont(self, font):
+        """
+        Sets the font for calculating text measurements.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self.GetArtProvider().SetMeasuringFont(font)
+
+
+    def SetFont(self, font):
+        """
+        Sets the tab font.
+
+        :param `font`: a `wx.Font` object.
+
+        :note: Overridden from `wx.PyPanel`.
+        """
+
+        wx.PyPanel.SetFont(self, font)
+
+        selectedFont = wx.Font(font.GetPointSize(), font.GetFamily(),
+                               font.GetStyle(), wx.BOLD, font.GetUnderlined(),
+                               font.GetFaceName(), font.GetEncoding())
+
+        self.SetNormalFont(font)
+        self.SetSelectedFont(selectedFont)
+        self.SetMeasuringFont(selectedFont)
+
+        # Recalculate tab container size based on new font
+        self.UpdateTabCtrlHeight(force=False)
+        self.DoSizing()
+
+        return True
+
+
+    def GetTabCtrlHeight(self):
+        """ Returns the tab control height. """
+
+        return self._tab_ctrl_height
+
+
+    def GetHeightForPageHeight(self, pageHeight):
+        """
+        Gets the height of the notebook for a given page height.
+
+        :param `pageHeight`: the given page height.
+        """
+
+        self.UpdateTabCtrlHeight()
+
+        tabCtrlHeight = self.GetTabCtrlHeight()
+        decorHeight = 2
+        return tabCtrlHeight + pageHeight + decorHeight
+
+
+    def AdvanceSelection(self, forward=True, wrap=True):
+        """
+        Cycles through the tabs.
+
+        :param `forward`: whether to advance forward or backward;
+        :param `wrap`: ``True`` to return to the first tab if we reach the last tab.
+
+        :note: The call to this function generates the page changing events.
+        """
+
+        tabCtrl = self.GetActiveTabCtrl()
+        newPage = -1
+
+        focusWin = tabCtrl.FindFocus()
+        activePage = tabCtrl.GetActivePage()
+        lenPages = len(tabCtrl.GetPages())
+
+        if lenPages == 1:
+            return False
+
+        if forward:
+            if lenPages > 1:
+
+                if activePage == -1 or activePage == lenPages - 1:
+                    if not wrap:
+                        return False
+
+                    newPage = 0
+
+                elif activePage < lenPages - 1:
+                    newPage = activePage + 1
+
+        else:
+
+            if lenPages > 1:
+                if activePage == -1 or activePage == 0:
+                    if not wrap:
+                        return False
+
+                    newPage = lenPages - 1
+
+                elif activePage > 0:
+                    newPage = activePage - 1
+
+
+        if newPage != -1:
+            if not self.GetEnabled(newPage):
+                return False
+
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, tabCtrl.GetId())
+            e.SetSelection(newPage)
+            e.SetOldSelection(activePage)
+            e.SetEventObject(tabCtrl)
+            self.GetEventHandler().ProcessEvent(e)
+
+##        if focusWin:
+##            focusWin.SetFocus()
+
+        return True
+
+
+    def ShowWindowMenu(self):
+        """
+        Shows the window menu for the active tab control associated with this
+        notebook, and returns ``True`` if a selection was made.
+        """
+
+        tabCtrl = self.GetActiveTabCtrl()
+        idx = tabCtrl.GetArtProvider().ShowDropDown(tabCtrl, tabCtrl.GetPages(), tabCtrl.GetActivePage())
+
+        if not self.GetEnabled(idx):
+            return False
+
+        if idx != -1:
+            e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, tabCtrl.GetId())
+            e.SetSelection(idx)
+            e.SetOldSelection(tabCtrl.GetActivePage())
+            e.SetEventObject(tabCtrl)
+            self.GetEventHandler().ProcessEvent(e)
+
+            return True
+
+        else:
+
+            return False
+
+
+    def AddTabAreaButton(self, id, location, normal_bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap):
+        """
+        Adds a button in the tab area.
+
+        :param `id`: the button identifier. This can be one of the following:
+
+         ==============================  =================================
+         Button Identifier               Description
+         ==============================  =================================
+         ``AUI_BUTTON_CLOSE``            Shows a close button on the tab area
+         ``AUI_BUTTON_WINDOWLIST``       Shows a window list button on the tab area
+         ``AUI_BUTTON_LEFT``             Shows a left button on the tab area
+         ``AUI_BUTTON_RIGHT``            Shows a right button on the tab area
+         ==============================  =================================
+
+        :param `location`: the button location. Can be ``wx.LEFT`` or ``wx.RIGHT``;
+        :param `normal_bitmap`: the bitmap for an enabled tab;
+        :param `disabled_bitmap`: the bitmap for a disabled tab.
+        """
+
+        active_tabctrl = self.GetActiveTabCtrl()
+        active_tabctrl.AddButton(id, location, normal_bitmap, disabled_bitmap)
+
+
+    def RemoveTabAreaButton(self, id):
+        """
+        Removes a button from the tab area.
+
+        :param `id`: the button identifier. See L{AddTabAreaButton} for a list of button identifiers.
+
+        :see: L{AddTabAreaButton}
+        """
+
+        active_tabctrl = self.GetActiveTabCtrl()
+        active_tabctrl.RemoveButton(id)
+
+
+    def HasMultiplePages(self):
+        """
+        This method should be overridden to return ``True`` if this window has multiple pages. All
+        standard class with multiple pages such as `wx.Notebook`, `wx.Listbook` and `wx.Treebook`
+        already override it to return ``True`` and user-defined classes with similar behaviour
+        should do it as well to allow the library to handle such windows appropriately.
+
+        :note: Overridden from `wx.PyPanel`.
+        """
+
+        return True
+
+
+    def GetDefaultBorder(self):
+        """ Returns the default border style for L{AuiNotebook}. """
+
+        return wx.BORDER_NONE
+
+
+    def NotebookPreview(self, thumbnail_size=200):
+        """
+        Generates a preview of all the pages in the notebook (MSW and GTK only).
+
+        :param `thumbnail_size`: the maximum size of every page thumbnail.
+
+        :note: this functionality is currently unavailable on wxMac.
+        """
+
+        if wx.Platform == "__WXMAC__":
+            return False
+
+        tabCtrl = self.GetActiveTabCtrl()
+        activePage = tabCtrl.GetActivePage()
+        pages = tabCtrl.GetPages()
+
+        pageStatus, pageText = [], []
+
+        for indx, page in enumerate(pages):
+
+            pageStatus.append(page.enabled)
+
+            if not page.enabled:
+                continue
+
+            self.SetSelectionToPage(page)
+            pageText.append(page.caption)
+
+            rect = page.window.GetScreenRect()
+            bmp = RescaleScreenShot(TakeScreenShot(rect), thumbnail_size)
+
+            page.enabled = False
+            if indx == 0:
+                il = wx.ImageList(bmp.GetWidth(), bmp.GetHeight(), True)
+
+            il.Add(bmp)
+
+        # create the list control
+        listCtrl = wx.ListCtrl(self, style=wx.LC_ICON|wx.LC_AUTOARRANGE|wx.LC_HRULES|wx.LC_VRULES,
+                               name="__fake__page__")
+
+        # assign the image list to it
+        listCtrl.AssignImageList(il, wx.IMAGE_LIST_NORMAL)
+        listCtrl.__previousStatus = [activePage, pageStatus]
+
+        # create some items for the list
+        for indx, text in enumerate(pageText):
+            listCtrl.InsertImageStringItem(10000, text, indx)
+
+        self.AddPage(listCtrl, "AuiNotebook Preview", True, bitmap=auinotebook_preview.GetBitmap(), disabled_bitmap=wx.NullBitmap)
+        return True
+
+
+    def SetRenamable(self, page_idx, renamable):
+        """
+        Sets whether a tab can be renamed via a left double-click or not.
+
+        :param `page_idx`: the page index;
+        :param `renamable`: ``True`` if the page can be renamed.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        # update our own tab catalog
+        page_info = self._tabs.GetPage(page_idx)
+        page_info.renamable = renamable
+
+        # update what's on screen
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        info = ctrl.GetPage(ctrl_idx)
+        info.renamable = page_info.renamable
+
+        return True
+
+
+    def IsRenamable(self, page_idx):
+        """
+        Returns whether a tab can be renamed or not.
+
+        :param `page_idx`: the page index.
+
+        :returns: ``True`` is a page can be renamed, ``False`` otherwise.
+        """
+
+        if page_idx >= self._tabs.GetPageCount():
+            return False
+
+        page_info = self._tabs.GetPage(page_idx)
+        return page_info.renamable
+
+
+    def OnRenameCancelled(self, page_index):
+        """
+        Called by L{TabTextCtrl}, to cancel the changes and to send the
+        `EVT_AUINOTEBOOK_END_LABEL_EDIT` event.
+
+        :param `page_index`: the page index in the notebook.
+        """
+
+        # let owner know that the edit was cancelled
+        evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, self.GetId())
+
+        evt.SetSelection(page_index)
+        evt.SetEventObject(self)
+        evt.SetLabel("")
+        evt.SetEditCanceled(True)
+        self.GetEventHandler().ProcessEvent(evt)
+
+
+    def OnRenameAccept(self, page_index, value):
+        """
+        Called by L{TabTextCtrl}, to accept the changes and to send the
+        `EVT_AUINOTEBOOK_END_LABEL_EDIT` event.
+
+        :param `page_index`: the page index in the notebook;
+        :param `value`: the new label for the tab.
+        """
+
+        evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, self.GetId())
+        evt.SetSelection(page_index)
+        evt.SetEventObject(self)
+        evt.SetLabel(value)
+        evt.SetEditCanceled(False)
+
+        return not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed()
+
+
+    def ResetTextControl(self):
+        """ Called by L{TabTextCtrl} when it marks itself for deletion. """
+
+        if not self._textCtrl:
+            return
+
+        self._textCtrl.Destroy()
+        self._textCtrl = None
+
+        # tab height might have changed
+        self.UpdateTabCtrlHeight(force=True)
+
+
+    def EditTab(self, page_index):
+        """
+        Starts the editing of an item label, sending a `EVT_AUINOTEBOOK_BEGIN_LABEL_EDIT` event.
+
+        :param `page_index`: the page index we want to edit.
+        """
+
+        if page_index >= self._tabs.GetPageCount():
+            return False
+
+        if not self.IsRenamable(page_index):
+            return False
+
+        page_info = self._tabs.GetPage(page_index)
+        ctrl, ctrl_idx = self.FindTab(page_info.window)
+        if not ctrl:
+            return False
+
+        evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT, self.GetId())
+        evt.SetSelection(page_index)
+        evt.SetEventObject(self)
+        if self.GetEventHandler().ProcessEvent(evt) and not evt.IsAllowed():
+            # vetoed by user
+            return False
+
+        if self._textCtrl is not None and page_info != self._textCtrl.item():
+            self._textCtrl.StopEditing()
+
+        self._textCtrl = TabTextCtrl(ctrl, page_info, page_index)
+        self._textCtrl.SetFocus()
+
+        return True