remove numpy + matrix
authorPierre <ratinaud@univ-tlse2.fr>
Sat, 12 Jan 2013 23:23:12 +0000 (00:23 +0100)
committerPierre <ratinaud@univ-tlse2.fr>
Sat, 12 Jan 2013 23:23:12 +0000 (00:23 +0100)
35 files changed:
OptionAlceste.py
PrintRScript.py
ProfList.py
Rscripts/chdfunct.R
analysematrix.py [new file with mode: 0644]
analysetxt.py
aui/__init__.py [new file with mode: 0644]
aui/aui_constants.py [new file with mode: 0644]
aui/aui_switcherdialog.py [new file with mode: 0644]
aui/aui_utilities.py [new file with mode: 0644]
aui/auibar.py [new file with mode: 0644]
aui/auibook.py [new file with mode: 0644]
aui/dockart.py [new file with mode: 0644]
aui/framemanager.py [new file with mode: 0644]
aui/tabart.py [new file with mode: 0644]
aui/tabmdi.py [new file with mode: 0644]
chemins.py
corpus.py
functions.py
iramuteq.py
layout.py
listlex.py
openanalyse.py
profile_segment.py
tabchdalc.py
tabchddist.py
tableau.py
tabsimi.py
tabstudent.py
textaslexico.py
textsimi.py
textstat.py
tools.py [new file with mode: 0644]
tree.py
ttparser.py

index 6465e29..49ff0f1 100755 (executable)
@@ -21,8 +21,8 @@ class OptionAlc(wx.Dialog):
         self.AlcesteConf = parametres
         self.choose = False
         
         self.AlcesteConf = parametres
         self.choose = False
         
-        self.label_1 = wx.StaticText(self, -1, u"Lemmatisation")
-        self.radio_1 = wx.RadioBox(self, -1, u"", choices=['oui', 'non'], majorDimension=0, style=wx.RA_SPECIFY_ROWS)
+        #self.label_1 = wx.StaticText(self, -1, u"Lemmatisation")
+        #self.radio_1 = wx.RadioBox(self, -1, u"", choices=['oui', 'non'], majorDimension=0, style=wx.RA_SPECIFY_ROWS)
 
         self.label_12 = wx.StaticText(self, -1, u"Classification")
         self.radio_box_2 = wx.RadioBox(self, -1, u"", choices=[u"double sur UC", u"simple sur UCE", u"simple sur UCI"], majorDimension=0, style=wx.RA_SPECIFY_ROWS) #, u"simple sur UCE (non implemente)"
 
         self.label_12 = wx.StaticText(self, -1, u"Classification")
         self.radio_box_2 = wx.RadioBox(self, -1, u"", choices=[u"double sur UC", u"simple sur UCE", u"simple sur UCI"], majorDimension=0, style=wx.RA_SPECIFY_ROWS) #, u"simple sur UCE (non implemente)"
@@ -42,8 +42,8 @@ analysée (2 = automatique)"""
         self.spin_ctrl_5 = wx.SpinCtrl(self, -1, "",size = (100,30), min=2, max=1000)
         self.label_max_actives =  wx.StaticText(self, -1, u"Nombre maximum de formes analysées")
         self.spin_max_actives = wx.SpinCtrl(self, -1, "",size = (100,30), min=20, max=10000)
         self.spin_ctrl_5 = wx.SpinCtrl(self, -1, "",size = (100,30), min=2, max=1000)
         self.label_max_actives =  wx.StaticText(self, -1, u"Nombre maximum de formes analysées")
         self.spin_max_actives = wx.SpinCtrl(self, -1, "",size = (100,30), min=20, max=10000)
-        self.label_4 = wx.StaticText(self, -1, u"Configuration \ndes clés d'analyse")
-        self.button_5 = wx.Button(self, wx.ID_PREFERENCES, "")
+        #self.label_4 = wx.StaticText(self, -1, u"Configuration \ndes clés d'analyse")
+        #self.button_5 = wx.Button(self, wx.ID_PREFERENCES, "")
         self.button_1 = wx.Button(self, wx.ID_CANCEL, "")
         self.button_2 = wx.Button(self, wx.ID_DEFAULT, u"Valeurs par défaut")
         self.button_4 = wx.Button(self, wx.ID_OK, "")
         self.button_1 = wx.Button(self, wx.ID_CANCEL, "")
         self.button_2 = wx.Button(self, wx.ID_DEFAULT, u"Valeurs par défaut")
         self.button_4 = wx.Button(self, wx.ID_OK, "")
@@ -52,18 +52,18 @@ analysée (2 = automatique)"""
         self.__set_properties()
         self.__do_layout()
 
         self.__set_properties()
         self.__do_layout()
 
-        self.Bind(wx.EVT_BUTTON, self.OnKeyPref, self.button_5)
+        #self.Bind(wx.EVT_BUTTON, self.OnKeyPref, self.button_5)
         self.Bind(wx.EVT_BUTTON, self.OnDef, self.button_2)
         
     def __set_properties(self):
         self.SetTitle("Options")
         #lang = self.AlcesteConf.get('ALCESTE', 'lang')
         #self.choice_dict.SetSelection(self.langues.index(lang))
         self.Bind(wx.EVT_BUTTON, self.OnDef, self.button_2)
         
     def __set_properties(self):
         self.SetTitle("Options")
         #lang = self.AlcesteConf.get('ALCESTE', 'lang')
         #self.choice_dict.SetSelection(self.langues.index(lang))
-        DefaultLem = self.parametres['lem']
-        if DefaultLem :
-            self.radio_1.SetSelection(0)
-        else:
-            self.radio_1.SetSelection(1)
+        #DefaultLem = self.parametres['lem']
+        #if DefaultLem :
+        #    self.radio_1.SetSelection(0)
+        #else:
+        #    self.radio_1.SetSelection(1)
         self.radio_box_2.SetSelection(int(self.parametres['classif_mode']))
         self.spin_ctrl_1.SetValue(int(self.parametres['tailleuc1']))
         self.spin_ctrl_2.SetValue(int(self.parametres['tailleuc2']))
         self.radio_box_2.SetSelection(int(self.parametres['classif_mode']))
         self.spin_ctrl_1.SetValue(int(self.parametres['tailleuc1']))
         self.spin_ctrl_2.SetValue(int(self.parametres['tailleuc2']))
@@ -82,10 +82,10 @@ analysée (2 = automatique)"""
         #grid_sizer2.Add(self.label_dict, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
         #grid_sizer2.Add(self.choice_dict, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
 
         #grid_sizer2.Add(self.label_dict, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
         #grid_sizer2.Add(self.choice_dict, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
 
-        grid_sizer2.Add(self.label_1, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
-        grid_sizer2.Add(self.radio_1, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
-        grid_sizer2.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
-        grid_sizer2.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 1)
+        #grid_sizer2.Add(self.label_1, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
+        #grid_sizer2.Add(self.radio_1, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
+        #grid_sizer2.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
+        #grid_sizer2.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 1)
 
         grid_sizer2.Add(self.label_12, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
         grid_sizer2.Add(self.radio_box_2, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
 
         grid_sizer2.Add(self.label_12, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
         grid_sizer2.Add(self.radio_box_2, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
@@ -122,10 +122,10 @@ analysée (2 = automatique)"""
         grid_sizer2.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
         grid_sizer2.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 1)
        
         grid_sizer2.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
         grid_sizer2.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 1)
        
-        grid_sizer2.Add(self.label_4, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
-        grid_sizer2.Add(self.button_5, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
-        grid_sizer2.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
-        grid_sizer2.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 1)
+        #grid_sizer2.Add(self.label_4, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
+        #grid_sizer2.Add(self.button_5, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 0)
+        #grid_sizer2.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
+        #grid_sizer2.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 1)
         
         grid_button.Add(self.button_1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
         grid_button.Add(self.button_2, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
         
         grid_button.Add(self.button_1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
         grid_button.Add(self.button_2, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
index db2eeac..9a92096 100644 (file)
@@ -275,7 +275,7 @@ def RchdQuest(DicoPath, RscriptPath, nbcl = 10, mincl = 10):
     chd.result<-Rchdquest("%s","%s","%s", nbt = nbt, mincl = mincl)
     n1 <- chd.result$n1
     classeuce1 <- chd.result$cuce1
     chd.result<-Rchdquest("%s","%s","%s", nbt = nbt, mincl = mincl)
     n1 <- chd.result$n1
     classeuce1 <- chd.result$cuce1
-    """ % (DicoPath['Act01'], DicoPath['listeuce1'], DicoPath['uce'])
+    """ % (DicoPath['mat01'], DicoPath['listeuce1'], DicoPath['uce'])
     
     txt += """
     tree_tot1 <- make_tree_tot(chd.result$chd)
     
     txt += """
     tree_tot1 <- make_tree_tot(chd.result$chd)
index db822cf..600dbcf 100644 (file)
@@ -49,13 +49,17 @@ class ProfListctrlPanel(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.Col
             self.lenact = profclasse.index([u'*****', u'*', u'*', u'*', u'*', u'*', '', ''])
             profclasse.pop(self.lenact)
         except ValueError:
             self.lenact = profclasse.index([u'*****', u'*', u'*', u'*', u'*', u'*', '', ''])
             profclasse.pop(self.lenact)
         except ValueError:
-            self.lenact = len(profclasse)
+            try :
+                self.lenact = profclasse.index([u'*', u'*', u'*', u'*', u'*', u'*', '', ''])
+                profclasse.pop(self.lenact)
+            except ValueError:
+                self.lenact = len(profclasse)
         try :
             self.lensup = profclasse.index([u'*', u'*', u'*', u'*', u'*', u'*', '', ''])
             self.lensup = self.lensup - self.lenact
             profclasse.pop(self.lensup)
         except ValueError: 
         try :
             self.lensup = profclasse.index([u'*', u'*', u'*', u'*', u'*', u'*', '', ''])
             self.lensup = self.lensup - self.lenact
             profclasse.pop(self.lensup)
         except ValueError: 
-            self.lensup = 0
+            self.lensup = len(profclasse) - self.lenact
         self.lenet = len(profclasse) - (self.lenact + self.lensup)
 #        print self.lenact, self.lensup, self.lenet
         for i,  line in enumerate(classen) :
         self.lenet = len(profclasse) - (self.lenact + self.lensup)
 #        print self.lenact, self.lensup, self.lenet
         for i,  line in enumerate(classen) :
index dd86dc1..a2dc502 100644 (file)
@@ -293,95 +293,12 @@ AsLexico2<- function(mat, chip = FALSE) {
     out
 }
 
     out
 }
 
-
-##from textometrieR
-##http://txm.sourceforge.net/doc/R/textometrieR-package.html
-##Sylvain Loiseau
-#specificites.probabilities <- function (lexicaltable, types = NULL, parts = NULL) 
-#{
-#    rowMargin <- rowSums(lexicaltable)
-#    colMargin <- colSums(lexicaltable)
-#    F <- sum(lexicaltable)
-#    if (!is.null(types)) {
-#        if (is.character(types)) {
-#            if (is.null(rownames(lexicaltable))) 
-#                stop("The lexical table has no row names and the \"types\" argument is a character vector.")
-#            if (!all(types %in% rownames(lexicaltable))) 
-#                stop(paste("Some requested types are not known in the lexical table: ", 
-#                  paste(types[!(types %in% rownames(lexicaltable))], 
-#                    collapse = " ")))
-#        }
-#        else {
-#            if (any(types < 1)) 
-#                stop("The row index must be greater than 0.")
-#            if (max(types) > nrow(lexicaltable)) 
-#                stop("Row index must be smaller than the number of rows.")
-#        }
-#        lexicaltable <- lexicaltable[types, , drop = FALSE]
-#        rowMargin <- rowMargin[types]
-#    }
-#    if (!is.null(parts)) {
-#        if (is.character(parts)) {
-#            if (is.null(colnames(lexicaltable))) 
-#                stop("The lexical table has no col names and the \"parts\" argument is a character vector.")
-#            if (!all(parts %in% colnames(lexicaltable))) 
-#                stop(paste("Some requested parts are not known in the lexical table: ", 
-#                  paste(parts[!(parts %in% colnames(lexicaltable))], 
-#                    collapse = " ")))
-#        }
-#        else {
-#            if (max(parts) > ncol(lexicaltable)) 
-#                stop("Column index must be smaller than the number of cols.")
-#            if (any(parts < 1)) 
-#                stop("The col index must be greater than 0.")
-#        }
-#        lexicaltable <- lexicaltable[, parts, drop = FALSE]
-#        colMargin <- colMargin[parts]
-#    }
-#    if (nrow(lexicaltable) == 0 | ncol(lexicaltable) == 0) {
-#        stop("The lexical table must contains at least one row and one column.")
-#    }
-#    specif <- matrix(0, nrow = nrow(lexicaltable), ncol = ncol(lexicaltable))
-#    for (i in 1:ncol(lexicaltable)) {
-#        whiteDrawn <- lexicaltable[, i]
-#        white <- rowMargin
-#        black <- F - white
-#        drawn <- colMargin[i]
-#        independance <- (white * drawn)/F
-#        specif_negative <- whiteDrawn < independance
-#        specif_positive <- whiteDrawn >= independance
-#        specif[specif_negative, i] <- phyper(whiteDrawn[specif_negative], 
-#            white[specif_negative], black[specif_negative], drawn)
-#        specif[specif_positive, i] <- phyper(whiteDrawn[specif_positive] - 
-#            1, white[specif_positive], black[specif_positive], 
-#            drawn)
-#    }
-#    dimnames(specif) <- dimnames(lexicaltable)
-#    return(specif)
-#}
-#
-##from textometrieR
-##http://txm.sourceforge.net/doc/R/textometrieR-package.html
-##Sylvain Loiseau
-#specificites <- function (lexicaltable, types = NULL, parts = NULL) 
-#{
-#    spe <- specificites.probabilities(lexicaltable, types, parts)
-#    spelog <- matrix(0, nrow = nrow(spe), ncol = ncol(spe))
-#    spelog[spe < 0.5] <- log10(spe[spe < 0.5])
-#    spelog[spe > 0.5] <- abs(log10(1 - spe[spe > 0.5]))
-#    spelog[spe == 0.5] <- 0
-#    spelog[is.infinite(spe)] <- 0
-#    spelog <- round(spelog, digits = 4)
-#    rownames(spelog) <- rownames(spe)
-#    colnames(spelog) <- colnames(spe)
-#    return(spelog)
-#}
-
 make.spec.hypergeo <- function(mat) {
     library(textometrieR)
     spec <- specificites(mat)
        sumcol<-colSums(mat)
     eff_relatif<-round(t(apply(mat,1,function(x) {(x/t(as.matrix(sumcol))*1000)})),2)
 make.spec.hypergeo <- function(mat) {
     library(textometrieR)
     spec <- specificites(mat)
        sumcol<-colSums(mat)
     eff_relatif<-round(t(apply(mat,1,function(x) {(x/t(as.matrix(sumcol))*1000)})),2)
+    colnames(eff_relatif) <- colnames(mat)
     out <-list()
     out[[1]]<-spec
     out[[3]]<-eff_relatif
     out <-list()
     out[[1]]<-spec
     out[[3]]<-eff_relatif
diff --git a/analysematrix.py b/analysematrix.py
new file mode 100644 (file)
index 0000000..1a14c4a
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/env python
+# -*- coding: utf-8 -*-
+#Author: Pierre Ratinaud
+#Copyright (c) 2013 Pierre Ratinaud
+#Lisense: GNU GPL
+
+
+
+import logging
+import os
+from time import time
+from uuid import uuid4
+
+
+from chemins import PathOut
+from functions import exec_rcode, check_Rresult, DoConf
+from time import time, sleep
+from openanalyse import OpenAnalyse
+
+class AnalyseMatrix :
+    def __init__(self, ira, tableau, parametres = None, dlg = False) :
+        self.tableau = tableau
+        self.ira = ira
+        self.parent = ira
+        self.dlg = dlg
+        self.parametres = parametres
+        self.val = False
+        if not 'pathout' in self.parametres :
+            self.pathout = PathOut(tableau.parametres['filename'], analyse_type = parametres['type'], dirout = parametres['pathout'])
+        else :
+            self.pathout = PathOut(filename = tableau.parametres['filename'], dirout = self.parametres['pathout'], analyse_type = self.parametres['type'])
+
+        self.parametres['pathout'] = self.pathout.dirout
+        self.parametres['uuid'] = str(uuid4())
+        self.parametres['name'] = os.path.split(self.parametres['pathout'])[1]
+        self.parametres['encoding'] = self.ira.syscoding
+
+        self.t1 = time()
+        result_analyse = self.doanalyse()
+        if result_analyse is None :
+            self.time = time() - self.t1
+            minutes, seconds = divmod(self.time, 60)
+            hours, minutes = divmod(minutes, 60)            
+            self.parametres['time'] = '%.0fh %.0fm %.0fs' % (hours, minutes, seconds)
+            self.parametres['ira'] = self.pathout['Analyse.ira']
+            DoConf().makeoptions([self.parametres['type']], [self.parametres], self.pathout['Analyse.ira'])
+            self.ira.history.addMatrix(self.parametres)
+            if dlg :
+                dlg.Destroy()
+                OpenAnalyse(self.parent, self.parametres['ira'])
+                #self.ira.tree.AddAnalyse(self.parametres)
+                self.val = 5100
+        else :
+            self.val = False
+            if dlg :
+                dlg.Destroy()
+    def doanalyse(self) :
+        pass
+        
index 3edf0a9..db69d01 100644 (file)
@@ -13,6 +13,7 @@ from PrintRScript import RchdTxt, AlcesteTxtProf
 from OptionAlceste import OptionAlc 
 from layout import PrintRapport
 from openanalyse import OpenAnalyse
 from OptionAlceste import OptionAlc 
 from layout import PrintRapport
 from openanalyse import OpenAnalyse
+from dialog import StatDialog
 from time import time
 
 log = logging.getLogger('iramuteq.analyse')
 from time import time
 
 log = logging.getLogger('iramuteq.analyse')
@@ -30,15 +31,14 @@ class AnalyseText :
             self.pathout = PathOut(corpus.parametres['originalpath'], analyse_type = parametres['type'], dirout = corpus.parametres['pathout'])
         else :
             self.pathout = PathOut(filename = corpus.parametres['originalpath'], dirout = self.parametres['pathout'], analyse_type = self.parametres['name'])
             self.pathout = PathOut(corpus.parametres['originalpath'], analyse_type = parametres['type'], dirout = corpus.parametres['pathout'])
         else :
             self.pathout = PathOut(filename = corpus.parametres['originalpath'], dirout = self.parametres['pathout'], analyse_type = self.parametres['name'])
-        self.parametres = self.make_config(parametres)
+        self.parametres = self.lemparam()
+        if self.parametres is not None :
+            self.parametres = self.make_config(parametres)
         log.info(self.pathout.dirout)
         if self.parametres is not None :
             self.keys = DoConf(self.ira.ConfigPath['key']).getoptions()
             gramact = [k for k in keys if keys[k] == 1]
             gramsup = [k for k in keys if keys[k] == 2]
         log.info(self.pathout.dirout)
         if self.parametres is not None :
             self.keys = DoConf(self.ira.ConfigPath['key']).getoptions()
             gramact = [k for k in keys if keys[k] == 1]
             gramsup = [k for k in keys if keys[k] == 2]
-            #FIXME
-            if not 'lem' in self.parametres :
-                self.parametres['lem'] = 1
             self.parametres['pathout'] = self.pathout.mkdirout()
             self.pathout = PathOut(dirout = self.parametres['pathout'])
             self.pathout.createdir(self.parametres['pathout'])
             self.parametres['pathout'] = self.pathout.mkdirout()
             self.pathout = PathOut(dirout = self.parametres['pathout'])
             self.pathout.createdir(self.parametres['pathout'])
@@ -76,18 +76,39 @@ class AnalyseText :
     def doanalyse(self) :
         pass
 
     def doanalyse(self) :
         pass
 
+    def lemparam(self) :
+        if self.dlg :
+            dial = StatDialog(self, self.parent)
+            dial.CenterOnParent()
+            val = dial.ShowModal()
+            if val == 5100 :
+                if dial.radio_lem.GetSelection() == 0 :
+                    lem = 1
+                else :
+                    lem = 0            
+                self.parametres['lem'] = lem
+                dial.Destroy()
+                return self.parametres
+            else :
+                dial.Destroy()
+                return None        
+        else :
+            return self.parametres
+
     def make_config(self, config) :
         if config is not None :
             if not self.dlg : 
                 return config
             else :
                 return self.preferences()
     def make_config(self, config) :
         if config is not None :
             if not self.dlg : 
                 return config
             else :
                 return self.preferences()
+        else :
+            return None
 
     def readconfig(self, config) :
         return config
 
     def preferences(self) :
 
     def readconfig(self, config) :
         return config
 
     def preferences(self) :
-        return {}
+        return self.parametres
 
     def printRscript(self) :
         pass
 
     def printRscript(self) :
         pass
@@ -159,8 +180,6 @@ class Alceste(AnalyseText) :
             parametres['max_actives'] = self.dial.spin_max_actives.GetValue()
             parametres['corpus'] = ''
             parametres['pathout'] = self.pathout.dirout
             parametres['max_actives'] = self.dial.spin_max_actives.GetValue()
             parametres['corpus'] = ''
             parametres['pathout'] = self.pathout.dirout
-            for val in parametres :
-                print val, parametres[val]
             DoConf(self.parent.ConfigPath['alceste']).makeoptions(['ALCESTE'], [parametres])
             self.dial.Destroy()
             return parametres
             DoConf(self.parent.ConfigPath['alceste']).makeoptions(['ALCESTE'], [parametres])
             self.dial.Destroy()
             return parametres
diff --git a/aui/__init__.py b/aui/__init__.py
new file mode 100644 (file)
index 0000000..ba3b51d
--- /dev/null
@@ -0,0 +1,290 @@
+"""
+AUI is an Advanced User Interface library that aims to implement "cutting-edge"
+interface usability and design features so developers can quickly and easily create
+beautiful and usable application interfaces.
+
+
+Vision and Design Principles
+============================
+
+AUI attempts to encapsulate the following aspects of the user interface:
+
+* **Frame Management**: Frame management provides the means to open, move and hide common
+  controls that are needed to interact with the document, and allow these configurations
+  to be saved into different perspectives and loaded at a later time. 
+
+* **Toolbars**: Toolbars are a specialized subset of the frame management system and should
+  behave similarly to other docked components. However, they also require additional
+  functionality, such as "spring-loaded" rebar support, "chevron" buttons and end-user
+  customizability. 
+
+* **Modeless Controls**: Modeless controls expose a tool palette or set of options that
+  float above the application content while allowing it to be accessed. Usually accessed
+  by the toolbar, these controls disappear when an option is selected, but may also be
+  "torn off" the toolbar into a floating frame of their own. 
+
+* **Look and Feel**: Look and feel encompasses the way controls are drawn, both when shown
+  statically as well as when they are being moved. This aspect of user interface design
+  incorporates "special effects" such as transparent window dragging as well as frame animation. 
+
+AUI adheres to the following principles:
+
+- Use native floating frames to obtain a native look and feel for all platforms;
+- Use existing wxPython code where possible, such as sizer implementation for frame management; 
+- Use standard wxPython coding conventions.
+
+
+Usage
+=====
+
+The following example shows a simple implementation that uses L{AuiManager} to manage
+three text controls in a frame window::
+
+    class MyFrame(wx.Frame):
+
+        def __init__(self, parent, id=-1, title="AUI Test", pos=wx.DefaultPosition,
+                     size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
+
+            wx.Frame.__init__(self, parent, id, title, pos, size, style)
+
+            self._mgr = aui.AuiManager()
+            
+            # notify AUI which frame to use
+            self._mgr.SetManagedWindow(self)
+
+            # create several text controls
+            text1 = wx.TextCtrl(self, -1, "Pane 1 - sample text",
+                                wx.DefaultPosition, wx.Size(200,150),
+                                wx.NO_BORDER | wx.TE_MULTILINE)
+                                               
+            text2 = wx.TextCtrl(self, -1, "Pane 2 - sample text",
+                                wx.DefaultPosition, wx.Size(200,150),
+                                wx.NO_BORDER | wx.TE_MULTILINE)
+                                               
+            text3 = wx.TextCtrl(self, -1, "Main content window",
+                                wx.DefaultPosition, wx.Size(200,150),
+                                wx.NO_BORDER | wx.TE_MULTILINE)
+            
+            # add the panes to the manager
+            self._mgr.AddPane(text1, AuiPaneInfo().Left().Caption("Pane Number One"))
+            self._mgr.AddPane(text2, AuiPaneInfo().Bottom().Caption("Pane Number Two"))
+            self._mgr.AddPane(text3, AuiPaneInfo().CenterPane())
+                                  
+            # tell the manager to "commit" all the changes just made
+            self._mgr.Update()
+
+            self.Bind(wx.EVT_CLOSE, self.OnClose)
+
+
+        def OnClose(self, event):
+
+            # deinitialize the frame manager
+            self._mgr.UnInit()
+
+            self.Destroy()        
+            event.Skip()        
+
+
+    # our normal wxApp-derived class, as usual
+
+    app = wx.PySimpleApp()
+
+    frame = MyFrame(None)
+    app.SetTopWindow(frame)
+    frame.Show()
+
+    app.MainLoop()
+
+
+What's New
+==========
+
+Current wxAUI Version Tracked: wxWidgets 2.9.0 (SVN HEAD)
+
+The wxPython AUI version fixes the following bugs or implement the following
+missing features (the list is not exhaustive):
+
+- Visual Studio 2005 style docking: http://www.kirix.com/forums/viewtopic.php?f=16&t=596
+- Dock and Pane Resizing: http://www.kirix.com/forums/viewtopic.php?f=16&t=582 
+- Patch concerning dock resizing: http://www.kirix.com/forums/viewtopic.php?f=16&t=610 
+- Patch to effect wxAuiToolBar orientation switch: http://www.kirix.com/forums/viewtopic.php?f=16&t=641 
+- AUI: Core dump when loading a perspective in wxGTK (MSW OK): http://www.kirix.com/forums/viewtopic.php?f=15&t=627 
+- wxAuiNotebook reordered AdvanceSelection(): http://www.kirix.com/forums/viewtopic.php?f=16&t=617 
+- Vertical Toolbar Docking Issue: http://www.kirix.com/forums/viewtopic.php?f=16&t=181 
+- Patch to show the resize hint on mouse-down in aui: http://trac.wxwidgets.org/ticket/9612 
+- The Left/Right and Top/Bottom Docks over draw each other: http://trac.wxwidgets.org/ticket/3516 
+- MinSize() not honoured: http://trac.wxwidgets.org/ticket/3562 
+- Layout problem with wxAUI: http://trac.wxwidgets.org/ticket/3597 
+- Resizing children ignores current window size: http://trac.wxwidgets.org/ticket/3908 
+- Resizing panes under Vista does not repaint background: http://trac.wxwidgets.org/ticket/4325 
+- Resize sash resizes in response to click: http://trac.wxwidgets.org/ticket/4547 
+- "Illegal" resizing of the AuiPane? (wxPython): http://trac.wxwidgets.org/ticket/4599 
+- Floating wxAUIPane Resize Event doesn't update its position: http://trac.wxwidgets.org/ticket/9773
+- Don't hide floating panels when we maximize some other panel: http://trac.wxwidgets.org/ticket/4066 
+- wxAUINotebook incorrect ALLOW_ACTIVE_PANE handling: http://trac.wxwidgets.org/ticket/4361 
+- Page changing veto doesn't work, (patch supplied): http://trac.wxwidgets.org/ticket/4518 
+- Show and DoShow are mixed around in wxAuiMDIChildFrame: http://trac.wxwidgets.org/ticket/4567 
+- wxAuiManager & wxToolBar - ToolBar Of Size Zero: http://trac.wxwidgets.org/ticket/9724 
+- wxAuiNotebook doesn't behave properly like a container as far as...: http://trac.wxwidgets.org/ticket/9911
+- Serious layout bugs in wxAUI: http://trac.wxwidgets.org/ticket/10620
+- wAuiDefaultTabArt::Clone() should just use copy contructor: http://trac.wxwidgets.org/ticket/11388
+- Drop down button for check tool on wxAuiToolbar: http://trac.wxwidgets.org/ticket/11139
+
+Plus the following features:
+
+- AuiManager:
+
+  (a) Implementation of a simple minimize pane system: Clicking on this minimize button causes a new
+      AuiToolBar to be created and added to the frame manager, (currently the implementation is such
+      that panes at West will have a toolbar at the right, panes at South will have toolbars at the
+      bottom etc...) and the pane is hidden in the manager.
+      Clicking on the restore button on the newly created toolbar will result in the toolbar being
+      removed and the original pane being restored;
+  (b) Panes can be docked on top of each other to form `AuiNotebooks`; `AuiNotebooks` tabs can be torn
+      off to create floating panes;
+  (c) On Windows XP, use the nice sash drawing provided by XP while dragging the sash;
+  (d) Possibility to set an icon on docked panes;
+  (e) Possibility to draw a sash visual grip, for enhanced visualization of sashes;
+  (f) Implementation of a native docking art (`ModernDockArt`). Windows XP only, **requires** Mark Hammond's
+      pywin32 package (winxptheme);
+  (g) Possibility to set a transparency for floating panes (a la Paint .NET);
+  (h) Snapping the main frame to the screen in any positin specified by horizontal and vertical
+      alignments;
+  (i) Snapping floating panes on left/right/top/bottom or any combination of directions, a la Winamp;
+  (j) "Fly-out" floating panes, i.e. panes which show themselves only when the mouse hover them;
+  (k) Ability to set custom bitmaps for pane buttons (close, maximize, etc...);
+  (l) Implementation of the style ``AUI_MGR_ANIMATE_FRAMES``, which fade-out floating panes when
+      they are closed (all platforms which support frames transparency) and show a moving rectangle
+      when they are docked and minimized (Windows < Vista and GTK only);
+  (m) A pane switcher dialog is available to cycle through existing AUI panes;
+  (n) Some flags which allow to choose the orientation and the position of the minimized panes;
+  (o) The functions [Get]MinimizeMode() in `AuiPaneInfo` which allow to set/get the flags described above;
+  (p) Events like ``EVT_AUI_PANE_DOCKING``, ``EVT_AUI_PANE_DOCKED``, ``EVT_AUI_PANE_FLOATING`` and ``EVT_AUI_PANE_FLOATED`` are
+      available for all panes *except* toolbar panes;
+  (q) Implementation of the RequestUserAttention method for panes;
+  (r) Ability to show the caption bar of docked panes on the left instead of on the top (with caption
+      text rotated by 90 degrees then). This is similar to what `wxDockIt` did. To enable this feature on any
+      given pane, simply call `CaptionVisible(True, left=True)`;
+  (s) New Aero-style docking guides: you can enable them by using the `AuiManager` style ``AUI_MGR_AERO_DOCKING_GUIDES``;
+  (t) A slide-in/slide-out preview of minimized panes can be seen by enabling the `AuiManager` style
+      ``AUI_MGR_PREVIEW_MINIMIZED_PANES`` and by hovering with the mouse on the minimized pane toolbar tool;
+  (u) New Whidbey-style docking guides: you can enable them by using the `AuiManager` style ``AUI_MGR_WHIDBEY_DOCKING_GUIDES``;
+  (v) Native of custom-drawn mini frames can be used as floating panes, depending on the ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style;
+  (w) A "smooth docking effect" can be obtained by using the ``AUI_MGR_SMOOTH_DOCKING`` style (similar to PyQT docking style).
+  
+|
+
+- AuiNotebook:
+
+  (a) Implementation of the style ``AUI_NB_HIDE_ON_SINGLE_TAB``, a la `wx.lib.agw.flatnotebook`;
+  (b) Implementation of the style ``AUI_NB_SMART_TABS``, a la `wx.lib.agw.flatnotebook`;
+  (c) Implementation of the style ``AUI_NB_USE_IMAGES_DROPDOWN``, which allows to show tab images
+      on the tab dropdown menu instead of bare check menu items (a la `wx.lib.agw.flatnotebook`);
+  (d) 6 different tab arts are available, namely:
+  
+      (1) Default "glossy" theme (as in `wx.aui.AuiNotebook`)
+      (2) Simple theme (as in `wx.aui.AuiNotebook`)
+      (3) Firefox 2 theme
+      (4) Visual Studio 2003 theme (VC71)
+      (5) Visual Studio 2005 theme (VC81)
+      (6) Google Chrome theme
+
+  (e) Enabling/disabling tabs;
+  (f) Setting the colour of the tab's text;
+  (g) Implementation of the style ``AUI_NB_CLOSE_ON_TAB_LEFT``, which draws the tab close button on
+      the left instead of on the right (a la Camino browser);
+  (h) Ability to save and load perspectives in `AuiNotebook` (experimental);
+  (i) Possibility to add custom buttons in the `AuiNotebook` tab area;
+  (j) Implementation of the style ``AUI_NB_TAB_FLOAT``, which 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;
+  (k) Implementation of the style ``AUI_NB_DRAW_DND_TAB`` (on by default), which draws an image
+      representation of a tab while dragging;
+  (l) Implementation of the `AuiNotebook` unsplit functionality, which unsplit a splitted AuiNotebook
+      when double-clicking on a sash;
+  (m) Possibility to hide all the tabs by calling `HideAllTAbs`;
+  (n) wxPython controls can now be added inside page tabs by calling `AddControlToPage`, and they can be
+      removed by calling `RemoveControlFromPage`;
+  (o) Possibility to preview all the pages in a `AuiNotebook` (as thumbnails) by using the `NotebookPreview`
+      method of `AuiNotebook`;
+  (p) Tab labels can be edited by calling the `SetRenamable` method on a `AuiNotebook` page;
+  (q) Support for multi-lines tab labels in `AuiNotebook`;
+  (r) Support for setting minimum and maximum tab widths for fixed width tabs;
+  (s) Implementation of the style ``AUI_NB_ORDER_BY_ACCESS``, which orders the tabs by last access time
+      inside the Tab Navigator dialog;
+  (t) Implementation of the style ``AUI_NB_NO_TAB_FOCUS``, allowing the developer not to draw the tab
+      focus rectangle on tne `AuiNotebook` tabs.
+
+|
+
+- AuiToolBar:
+
+  (a) ``AUI_TB_PLAIN_BACKGROUND`` style that allows to easy setup a plain background to the AUI toolbar,
+      without the need to override drawing methods. This style contrasts with the default behaviour
+      of the `wx.aui.AuiToolBar` that draws a background gradient and this break the window design when
+      putting it within a control that has margin between the borders and the toolbar (example: put
+      `wx.aui.AuiToolBar` within a `wx.StaticBoxSizer` that has a plain background);
+  (b) `AuiToolBar` allow item alignment: http://trac.wxwidgets.org/ticket/10174;
+  (c) `AUIToolBar` `DrawButton()` improvement: http://trac.wxwidgets.org/ticket/10303;
+  (d) `AuiToolBar` automatically assign new id for tools: http://trac.wxwidgets.org/ticket/10173;
+  (e) `AuiToolBar` Allow right-click on any kind of button: http://trac.wxwidgets.org/ticket/10079;
+  (f) `AuiToolBar` idle update only when visible: http://trac.wxwidgets.org/ticket/10075;
+  (g) Ability of creating `AuiToolBar` tools with [counter]clockwise rotation. This allows to propose a
+      variant of the minimizing functionality with a rotated button which keeps the caption of the pane
+      as label;
+  (h) Allow setting the alignment of all tools in a toolbar that is expanded.
+
+
+TODOs
+=====
+
+- Documentation, documentation and documentation;
+- Fix `tabmdi.AuiMDIParentFrame` and friends, they do not work correctly at present;
+- Allow specification of `CaptionLeft()` to `AuiPaneInfo` to show the caption bar of docked panes
+  on the left instead of on the top (with caption text rotated by 90 degrees then). This is
+  similar to what `wxDockIt` did - DONE;
+- Make developer-created `AuiNotebooks` and automatic (framemanager-created) `AuiNotebooks` behave
+  the same way (undocking of tabs) - DONE, to some extent;
+- Find a way to dock panes in already floating panes (`AuiFloatingFrames`), as they already have
+  their own `AuiManager`;
+- Add more gripper styles (see, i.e., PlusDock 4.0);
+- Add an "AutoHide" feature to docked panes, similar to fly-out floating panes (see, i.e., PlusDock 4.0);
+- Add events for panes when they are about to float or to be docked (something like
+  ``EVT_AUI_PANE_FLOATING/ED`` and ``EVT_AUI_PANE_DOCKING/ED``) - DONE, to some extent;
+- Implement the 4-ways splitter behaviour for horizontal and vertical sashes if they intersect;
+- Extend `tabart.py` with more aui tab arts;
+- Implement ``AUI_NB_LEFT`` and ``AUI_NB_RIGHT`` tab locations in `AuiNotebook`;
+- Move `AuiDefaultToolBarArt` into a separate module (as with `tabart.py` and `dockart.py`) and
+  provide more arts for toolbars (maybe from `wx.lib.agw.flatmenu`?)
+- Support multiple-rows/multiple columns toolbars;
+- Integrate as much as possible with `wx.lib.agw.flatmenu`, from dropdown menus in `AuiNotebook` to
+  toolbars and menu positioning;
+- Possibly handle minimization of panes in a different way (or provide an option to switch to
+  another way of minimizing panes);
+- Clean up/speed up the code, especially time-consuming for-loops;
+- Possibly integrate `wxPyRibbon` (still on development), at least on Windows.
+
+
+License And Version
+===================
+
+AUI library is distributed under the wxPython license. 
+
+Latest revision: Andrea Gavana @ 21 Jun 2011, 22.00 GMT
+
+Version 1.3. 
+
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+from aui_constants import *
+from aui_utilities import *
+from auibar import *
+from auibook import *
+from tabart import *
+from dockart import *
+from framemanager import *
+from tabmdi import *
diff --git a/aui/aui_constants.py b/aui/aui_constants.py
new file mode 100644 (file)
index 0000000..38f183d
--- /dev/null
@@ -0,0 +1,2588 @@
+"""
+This module contains all the constants used by wxPython-AUI.
+
+Especially important and meaningful are constants for AuiManager, AuiDockArt and
+AuiNotebook.
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+from wx.lib.embeddedimage import PyEmbeddedImage
+
+# ------------------------- #
+# - AuiNotebook Constants - #
+# ------------------------- #
+
+# For tabart
+# --------------
+
+vertical_border_padding = 4
+""" Border padding used in drawing tabs. """
+
+if wx.Platform == "__WXMAC__":
+    nb_close_bits = "\xFF\xFF\xFF\xFF\x0F\xFE\x03\xF8\x01\xF0\x19\xF3" \
+                    "\xB8\xE3\xF0\xE1\xE0\xE0\xF0\xE1\xB8\xE3\x19\xF3" \
+                    "\x01\xF0\x03\xF8\x0F\xFE\xFF\xFF"
+    """ AuiNotebook close button image on wxMAC. """
+    
+elif wx.Platform == "__WXGTK__":
+    nb_close_bits = "\xff\xff\xff\xff\x07\xf0\xfb\xef\xdb\xed\x8b\xe8" \
+                    "\x1b\xec\x3b\xee\x1b\xec\x8b\xe8\xdb\xed\xfb\xef" \
+                    "\x07\xf0\xff\xff\xff\xff\xff\xff"
+    """ AuiNotebook close button image on wxGTK. """
+    
+else:
+    nb_close_bits = "\xff\xff\xff\xff\xff\xff\xff\xff\xe7\xf3\xcf\xf9" \
+                    "\x9f\xfc\x3f\xfe\x3f\xfe\x9f\xfc\xcf\xf9\xe7\xf3" \
+                    "\xff\xff\xff\xff\xff\xff\xff\xff"
+    """ AuiNotebook close button image on wxMSW. """
+
+nb_left_bits = "\xff\xff\xff\xff\xff\xff\xff\xfe\x7f\xfe\x3f\xfe\x1f" \
+               "\xfe\x0f\xfe\x1f\xfe\x3f\xfe\x7f\xfe\xff\xfe\xff\xff" \
+               "\xff\xff\xff\xff\xff\xff"
+""" AuiNotebook left button image. """
+
+nb_right_bits = "\xff\xff\xff\xff\xff\xff\xdf\xff\x9f\xff\x1f\xff\x1f" \
+                "\xfe\x1f\xfc\x1f\xfe\x1f\xff\x9f\xff\xdf\xff\xff\xff" \
+                "\xff\xff\xff\xff\xff\xff"
+""" AuiNotebook right button image. """
+
+nb_list_bits = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x0f" \
+               "\xf8\xff\xff\x0f\xf8\x1f\xfc\x3f\xfe\x7f\xff\xff\xff" \
+               "\xff\xff\xff\xff\xff\xff"
+""" AuiNotebook windows list button image. """
+
+
+#----------------------------------------------------------------------
+tab_active_center = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAbCAYAAAC9WOV0AAAABHNCSVQICAgIfAhkiAAAADNJ"
+    "REFUCJltzMEJwDAUw9DHX6OLdP/Bop4KDc3F2EIYrsFtrZow8GnH6OD1zvRTajvY2QMHIhNx"
+    "jUhuAgAAAABJRU5ErkJggg==")
+""" Center active tab image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_active_left = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAbCAYAAACjkdXHAAAABHNCSVQICAgIfAhkiAAAAglJ"
+    "REFUOI2Nkk9rE0EYh5/J7mpW06xE2iSmeFHxEoqIAc/FQ5CKgn4DP4KlIQG/QVsQbBEKgop+"
+    "Anvy4rV4bLT2JCGJPVXqwaZJd+f1kN26WTfJDrzszDLPPL/5o0jeFGAC54A0YKmEYAo4DzjA"
+    "LHAZmElqtIGrhmEsvtzcfPNtb6/V6524SWALKBiGsfhxe/uzFhGth5XEmgVubWxsvA1Az68k"
+    "1nngYbPZ7ASg69c06wxwe3V9/b3reVqHwGmwCZRs2370fX//wIuA0+CLwEKj0XilZTSu602G"
+    "FcP7vLe7+7XlRaCgPw62gGv5fP6p63raiwFdLWKOgdNArl6vV1UqpQgcYdcYbwooAPfb7c7h"
+    "mTWmUjGwCWTL5fL1K6VSLiqQyMTYyLVa/UEwe9IC0chFYKnb/XnkeiIDV+Q0UsG/qNkCnEql"
+    "crNQLDpaxpskJnYayD1bXl4S/xrDoPLHKjQOmsHwlCuHv44+ZJ2sLTrGGqzg7zEc+VK1Wl1w"
+    "HMcG0DFxw6sFsRVwAZhdWak9FoRJ+w2HCKzzwN3jXv+daVmGDkdWoMKb9fumHz0DFFfX1p5Y"
+    "lmXo6N0G48jzVEDOt97pdA9ezOXzGU+PzBmN6VuDqyoDN3Z2vjyfKxQynhYkJuJ/L02Ara3X"
+    "n3602r8HrpaTUy3HAy1/+hNq8O+r+q4WETirmFMNBwm3v+gdmytKNIUpAAAAAElFTkSuQmCC")
+""" Left active tab image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_active_right = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAbCAYAAACjkdXHAAAABHNCSVQICAgIfAhkiAAAAkpJ"
+    "REFUOI2NlM1rU0EUxX9zZ5KaWq3GKKnGutC0FEWCWAWLRUOxBetK/wdp6Re6F6TFXXGhuFdw"
+    "b7dCQUUpiFt1XbB2q7Uf1iTvunjzkpe0afNgmLnDnHvOPe/OWCALtAFC+Cktfha4CRwBDnhg"
+    "BQhaSrK19bf89dv35WfPX7y01haBbiAFmH3BlUA1Gm8WFt75BFkg0TK4VAl0Y3NL5+efvgIK"
+    "wOH92EVjxRljGBi4VgTOeLDbk7kcqEZju1TWX7/Xgtm5J6+BS8ChvdilLhAhkUya4eFbxVQq"
+    "1e3ZbUtgg8GKJd/Tk70/NjYCHCPsgX1kV8K5VA70z8amfvy0tAwMAcebSRfijikY8ez5/OlM"
+    "JrOncbIjp4K1lmRb0sw8eDgCpAm7rwlz46YIzjpGb48WveyDNPhDfCOuHmNwzpHL5dK9fX3n"
+    "mkmvaxJiayOCWMvM1PSdZtJrhiloLJMYIeESDFwf7Acyu0mXGLYmX0PpYi3ZbFdnoVDoBTpp"
+    "uCxCjFob1tYKzlnGJyZHd5Mu6uVGkqvMCmCwzjE4eOMqcALoINauUic37hjhLXPWcTSdThWL"
+    "QxcJX5yqdGk4H/cP9a4755iYnLpL+M/b8e0qjafrekb9TUskuNx/5TzQ5Y1zO9yOZEd1R7OI"
+    "JdXebh/Pzt3zCToAMZv/AjU1orDWWKAGVJVSqcTqysp6X+/ZaeAL8KNac9wsVQ8yNeOsdZw8"
+    "let4/2HpEdAPXDAb20HLj7xqeHT158ra4uLbz2bdg03krmetxrH9KDAmHP8Bn0j1t/01UV0A"
+    "AAAASUVORK5CYII=")
+""" Right active tab image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_close = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAI9J"
+    "REFUKJG90MEKAWEUxfEfM4rxAFIommzZzNb7v4BsLJTsiGQlYjHfME3flrO75/xvnXv5p/qY"
+    "R/wcWTUktWCKFbrYB6/AAhecmwunAI/RwQAjbLGpoFakwjLATxzqMLQjC68A3/FohkljLkKN"
+    "Ha4YKg8+VkBag3Pll9a1GikmuPk+4qMMs0jFMXoR/0d6A9JRFV/jxY+iAAAAAElFTkSuQmCC")
+""" Normal close button image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_close_h = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAOlJ"
+    "REFUKJGVkiFuw0AQRd849hUS7iPUwGEllhyjYJ+gaK9Q4CsY9QTFIY4shQQucI8Q7l6h3Z0S"
+    "r7UgjdrPZvVm52k0wpJLWe4y51qgVpECQFQnYPzabN4ra2cAAbgWxZMmyavAkTtROIn33fM0"
+    "fcilLHep92+/wXHTd5K8JJlzbYD3w8C2aVZo2zTsh4FF5Zg516ZAHYBb35MbszbkxnDr+3hQ"
+    "napIIUv1eT6vYPggvAGoSJE88r6XVFQnRA7BOdYIk8IUUZ1SYAQOsXOskRsT1+P/11pZO4v3"
+    "ncLpESzed5W1c1jQn0/jBzPfck1qdmfjAAAAAElFTkSuQmCC")
+""" Hover close button image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_close_p = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAASxJ"
+    "REFUKJF9kbFLQlEYxX/nvbs55OAkiJAE7k7Nibo9xf+hrTlyr3Boipb+BCGq0bApJEQcG0Ms"
+    "aQ0Lmq5+Dc+nDtbZ7uHce37fd8VSlWwh50PfRKqClWJXI8y6bu5uHj5e3wEEcJDP75txLBSx"
+    "RYbdS7QfJ5PnsJIt5BbB4hQjkrQtjxlFILOXyvQDH/qmUCSJznDAYetkFTxsndAZDggkhCIf"
+    "+qaLmWP1bu8oN+qrC+VGnd7t3bpKqrp4wBjl+ux8FUweSLwlXCnYCv2PHGgE1BLmTYykad2i"
+    "kcOsi1TbZN7EKDfq67NZV5VsIeedvzQjCv5YK8R/4bw7Cl+/P7920+kJkBEq/hWWaPem45cQ"
+    "YDybTfdSmf5CizckwHaAH9ATZldu7i560/ELwC+6RXdU6KzezAAAAABJRU5ErkJggg==")
+""" Pressed close button image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_inactive_center = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAbCAYAAAC9WOV0AAAABHNCSVQICAgIfAhkiAAAAElJ"
+    "REFUCJlVyiEOgDAUBNHp3qmX5iYkyMpqBAaFILRdDGn4qybZB98yy3ZZrRu1PpABAQiDSLN+"
+    "h4NLEU8CBAfoPHZUywr3M/wCTz8c3/qQrUcAAAAASUVORK5CYII=")
+""" Center inactive tab image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_inactive_left = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAbCAYAAACjkdXHAAAABHNCSVQICAgIfAhkiAAAAf5J"
+    "REFUOI2llE1rE1EUhp8bZwyhaZomk5DaD40hSWPQVkTd6KIIEUWlLqTEhTaLulBQ6sfKjeBC"
+    "ECULXQku/Alx7d6/U1EQae45LjJpJ5NOnOKBgYG5z33Px3sG/iPMIc87QAmYBZKHgdOu69a2"
+    "3/W2yrVGK5vPLTlxFV3Xrb3+8v1Ntd5oiSpWBmnEidKT972tar3R6ovSt4qoxoIdoFipNlpW"
+    "B6AVRYFEHNWn3a8dz/PK1rIHEgN2UpnMseVTK7fUGBME48CFe88+3sh5+SXr1xmMSbABvJXz"
+    "l9siYAVGWJ0Mu/OVZr5Q8CpWfFWzD2Imj2qu/fhtG4wRVUIZg0bDBsgtn15dt6qIKKBDQZ81"
+    "kWmnzly6OZ+ZzhSt7jfK6CBjFMwEk5TWOy82AVQGhzVUb5RJEkC2fLK6JgIiPhioeZJJUhev"
+    "3j2RTqdzooqge2ojCxwxqrnrG4/uq4Ida3HgAjMOJ4CZSq1+RVBUzCgQinDDstfa282jyeTU"
+    "rhUGF4CJgMPKhbXbmw9VFfG7fBA4LCao7AAzi8cXz1kF0dENMqH38KgWnnd7nSMJxxE5wI4+"
+    "MHyCaeeAYvPshQ0RJby3wVSDHxxgAVh99elb9/evndmfP3boW2FsqGNhMMCdBy8/fJ5KZ6at"
+    "qL+3Q1dEzFkNGMX82ZWh18e0/vVT/wuFmdYVv/ruKgAAAABJRU5ErkJggg==")
+""" Left inactive tab image for the Chrome tab art. """
+
+#----------------------------------------------------------------------
+tab_inactive_right = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAbCAYAAACjkdXHAAAABHNCSVQICAgIfAhkiAAAAhBJ"
+    "REFUOI2llM9rE1EQxz8zb1dSTKNuYtW01kQDRoKFWi9FEEq1IooUUWoPokWCtVqkR69KsSBU"
+    "8OJRPOhBxZNe/At6FBER/HFUPEq1IGn3ecgm2ZjdJODCHPY9vvP9fufNDPzHZ4DDQBrYBKwB"
+    "ftfoJys/Kw9ef/1y8/6rh67rHgKS3WLl6cqqtcCGD58+vn+zdPXorUql8g5Y7wTWdd+y4Vus"
+    "teQK+yfKi8/KwM5umBXAAgioCIP54gTQBzgdwTbsQZR0JpOfXXw+0w27hn9EBGMcyRcPnulJ"
+    "pbKd2JvACKgKnpcePH99+TSwvT3YEphusKsqB4ZHp4FMNWUn5loSEVSFbZ63b8eeUhpwu5Md"
+    "JBFRjHHk7LXb08CuNuAaZTgEEaFQHJoEvDjpakOYmnURUFWSvam+0ujJfqAnmlnABhG2jlTZ"
+    "j19YuEzMm7dUu34hihrDQG7vGLCViPq0VruuvdquyWSvN3xsKhclvbXaoUQiihFlfLJ8iYiq"
+    "O/EtUC2xGGF3vjAObAnI6stCsZbYCLwnEonNY+dulALvHWSH2YN2PXLq4hz/9HpjnmOs18DZ"
+    "bP9IIL0+afV5juqzRgLFcV1n9u6LGWAgWnaMBFHBOIbi0MgU1S3jAcjyyw9xqpvzWou1Pj++"
+    "f/t8b/7EAvBW5u48agU37abWs99rv1YfL81fkT8V34YxbZ696d4CfwEszZSZx6Z26wAAAABJ"
+    "RU5ErkJggg==")
+""" Right inactive tab image for the Chrome tab art. """
+
+# For auibook
+# -----------
+
+AuiBaseTabCtrlId = 5380
+""" Base window identifier for AuiTabCtrl. """
+
+AUI_NB_TOP                 = 1 << 0
+""" With this style, tabs are drawn along the top of the notebook. """
+AUI_NB_LEFT                = 1 << 1  # not implemented yet
+""" With this style, tabs are drawn along the left of the notebook.
+Not implemented yet. """
+AUI_NB_RIGHT               = 1 << 2  # not implemented yet
+""" With this style, tabs are drawn along the right of the notebook.
+Not implemented yet. """
+AUI_NB_BOTTOM              = 1 << 3
+""" With this style, tabs are drawn along the bottom of the notebook. """
+AUI_NB_TAB_SPLIT           = 1 << 4
+""" Allows the tab control to be split by dragging a tab. """
+AUI_NB_TAB_MOVE            = 1 << 5
+""" Allows a tab to be moved horizontally by dragging. """
+AUI_NB_TAB_EXTERNAL_MOVE   = 1 << 6
+""" Allows a tab to be moved to another tab control. """
+AUI_NB_TAB_FIXED_WIDTH     = 1 << 7
+""" With this style, all tabs have the same width. """
+AUI_NB_SCROLL_BUTTONS      = 1 << 8
+""" With this style, left and right scroll buttons are displayed. """
+AUI_NB_WINDOWLIST_BUTTON   = 1 << 9
+""" With this style, a drop-down list of windows is available. """
+AUI_NB_CLOSE_BUTTON        = 1 << 10
+""" With this style, a close button is available on the tab bar. """
+AUI_NB_CLOSE_ON_ACTIVE_TAB = 1 << 11
+""" With this style, a close button is available on the active tab. """
+AUI_NB_CLOSE_ON_ALL_TABS   = 1 << 12
+""" With this style, a close button is available on all tabs. """
+AUI_NB_MIDDLE_CLICK_CLOSE  = 1 << 13
+""" Allows to close `AuiNotebook` tabs by mouse middle button click. """
+AUI_NB_SUB_NOTEBOOK        = 1 << 14
+""" This style is used by `AuiManager` to create automatic `AuiNotebooks`. """
+AUI_NB_HIDE_ON_SINGLE_TAB  = 1 << 15
+""" Hides the tab window if only one tab is present. """
+AUI_NB_SMART_TABS          = 1 << 16
+""" Use `Smart Tabbing`, like ``Alt`` + ``Tab`` on Windows. """
+AUI_NB_USE_IMAGES_DROPDOWN = 1 << 17
+""" Uses images on dropdown window list menu instead of check items. """
+AUI_NB_CLOSE_ON_TAB_LEFT   = 1 << 18
+""" Draws the tab close button on the left instead of on the right
+(a la Camino browser). """
+AUI_NB_TAB_FLOAT           = 1 << 19
+""" 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        = 1 << 20
+""" Draws an image representation of a tab while dragging. """
+AUI_NB_ORDER_BY_ACCESS     = 1 << 21
+""" Tab navigation order by last access time. """
+AUI_NB_NO_TAB_FOCUS        = 1 << 22
+""" Don't draw tab focus rectangle. """
+
+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
+""" Default `AuiNotebook` style. """
+
+#----------------------------------------------------------------------
+Mondrian = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAHFJ"
+    "REFUWIXt1jsKgDAQRdF7xY25cpcWC60kioI6Fm/ahHBCMh+BRmGMnAgEWnvPpzK8dvrFCCCA"
+    "coD8og4c5Lr6WB3Q3l1TBwLYPuF3YS1gn1HphgEEEABcKERrGy0E3B0HFJg7C1N/f/kTBBBA"
+    "+Vi+AMkgFEvBPD17AAAAAElFTkSuQmCC")
+""" Default icon for the Smart Tabbing dialog. """
+
+# -------------------------- #
+# - FrameManager Constants - #
+# -------------------------- #
+
+# Docking Styles
+AUI_DOCK_NONE = 0
+""" No docking direction. """
+AUI_DOCK_TOP = 1
+""" Top docking direction. """
+AUI_DOCK_RIGHT = 2
+""" Right docking direction. """
+AUI_DOCK_BOTTOM = 3
+""" Bottom docking direction. """
+AUI_DOCK_LEFT = 4
+""" Left docking direction. """
+AUI_DOCK_CENTER = 5
+""" Center docking direction. """
+AUI_DOCK_CENTRE = AUI_DOCK_CENTER
+""" Centre docking direction. """
+AUI_DOCK_NOTEBOOK_PAGE = 6
+""" Automatic AuiNotebooks docking style. """
+
+# Floating/Dragging Styles
+AUI_MGR_ALLOW_FLOATING           = 1 << 0
+""" Allow floating of panes. """
+AUI_MGR_ALLOW_ACTIVE_PANE        = 1 << 1
+""" If a pane becomes active, "highlight" it in the interface. """
+AUI_MGR_TRANSPARENT_DRAG         = 1 << 2
+""" If the platform supports it, set transparency on a floating pane
+while it is dragged by the user. """
+AUI_MGR_TRANSPARENT_HINT         = 1 << 3
+""" If the platform supports it, show a transparent hint window when
+the user is about to dock a floating pane. """
+AUI_MGR_VENETIAN_BLINDS_HINT     = 1 << 4
+""" Show a "venetian blind" effect when the user is about to dock a
+floating pane. """
+AUI_MGR_RECTANGLE_HINT           = 1 << 5
+""" Show a rectangle hint effect when the user is about to dock a
+floating pane. """
+AUI_MGR_HINT_FADE                = 1 << 6
+""" If the platform supports it, the hint window will fade in and out. """
+AUI_MGR_NO_VENETIAN_BLINDS_FADE  = 1 << 7
+""" Disables the "venetian blind" fade in and out. """
+AUI_MGR_LIVE_RESIZE              = 1 << 8
+""" Live resize when the user drag a sash. """
+AUI_MGR_ANIMATE_FRAMES           = 1 << 9
+""" Fade-out floating panes when they are closed (all platforms which support
+frames transparency) and show a moving rectangle when they are docked
+(Windows < Vista and GTK only). """
+AUI_MGR_AERO_DOCKING_GUIDES      = 1 << 10
+""" Use the new Aero-style bitmaps as docking guides. """
+AUI_MGR_PREVIEW_MINIMIZED_PANES  = 1 << 11
+""" Slide in and out minimized panes to preview them. """
+AUI_MGR_WHIDBEY_DOCKING_GUIDES   = 1 << 12
+""" Use the new Whidbey-style bitmaps as docking guides. """
+AUI_MGR_SMOOTH_DOCKING           = 1 << 13
+""" Performs a "smooth" docking of panes (a la PyQT). """
+AUI_MGR_USE_NATIVE_MINIFRAMES    = 1 << 14
+""" Use miniframes with native caption bar as floating panes instead or custom
+drawn caption bars (forced on wxMac). """
+AUI_MGR_AUTONB_NO_CAPTION        = 1 << 15
+""" Panes that merge into an automatic notebook will not have the pane
+caption visible. """
+
+
+AUI_MGR_DEFAULT = AUI_MGR_ALLOW_FLOATING | AUI_MGR_TRANSPARENT_HINT | \
+                  AUI_MGR_HINT_FADE | AUI_MGR_NO_VENETIAN_BLINDS_FADE
+""" Default `AuiManager` style. """
+
+# Panes Customization
+AUI_DOCKART_SASH_SIZE = 0
+""" Customizes the sash size. """
+AUI_DOCKART_CAPTION_SIZE = 1
+""" Customizes the caption size. """
+AUI_DOCKART_GRIPPER_SIZE = 2
+""" Customizes the gripper size. """
+AUI_DOCKART_PANE_BORDER_SIZE = 3
+""" Customizes the pane border size. """
+AUI_DOCKART_PANE_BUTTON_SIZE = 4
+""" Customizes the pane button size. """
+AUI_DOCKART_BACKGROUND_COLOUR = 5
+""" Customizes the background colour. """
+AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR = 6
+""" Customizes the background gradient colour. """
+AUI_DOCKART_SASH_COLOUR = 7
+""" Customizes the sash colour. """
+AUI_DOCKART_ACTIVE_CAPTION_COLOUR = 8
+""" Customizes the active caption colour. """
+AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR = 9
+""" Customizes the active caption gradient colour. """
+AUI_DOCKART_INACTIVE_CAPTION_COLOUR = 10
+""" Customizes the inactive caption colour. """
+AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR = 11
+""" Customizes the inactive gradient caption colour. """
+AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR = 12
+""" Customizes the active caption text colour. """
+AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR = 13
+""" Customizes the inactive caption text colour. """
+AUI_DOCKART_BORDER_COLOUR = 14
+""" Customizes the border colour. """
+AUI_DOCKART_GRIPPER_COLOUR = 15
+""" Customizes the gripper colour. """
+AUI_DOCKART_CAPTION_FONT = 16
+""" Customizes the caption font. """
+AUI_DOCKART_GRADIENT_TYPE = 17
+""" Customizes the gradient type (no gradient, vertical or horizontal). """
+AUI_DOCKART_DRAW_SASH_GRIP = 18
+""" Draw a sash grip on the sash. """
+
+# Caption Gradient Type
+AUI_GRADIENT_NONE = 0
+""" No gradient on the captions. """
+AUI_GRADIENT_VERTICAL = 1
+""" Vertical gradient on the captions. """
+AUI_GRADIENT_HORIZONTAL = 2
+""" Horizontal gradient on the captions. """
+
+# Pane Button State
+AUI_BUTTON_STATE_NORMAL = 0
+""" Normal button state. """
+AUI_BUTTON_STATE_HOVER = 1 << 1
+""" Hovered button state. """
+AUI_BUTTON_STATE_PRESSED = 1 << 2
+""" Pressed button state. """
+AUI_BUTTON_STATE_DISABLED = 1 << 3
+""" Disabled button state. """
+AUI_BUTTON_STATE_HIDDEN   = 1 << 4
+""" Hidden button state. """
+AUI_BUTTON_STATE_CHECKED  = 1 << 5
+""" Checked button state. """
+
+# Pane minimize mode
+AUI_MINIMIZE_POS_SMART    = 0x01
+""" Minimizes the pane on the closest tool bar. """
+AUI_MINIMIZE_POS_TOP      = 0x02
+""" Minimizes the pane on the top tool bar. """
+AUI_MINIMIZE_POS_LEFT     = 0x03
+""" Minimizes the pane on its left tool bar. """
+AUI_MINIMIZE_POS_RIGHT    = 0x04
+""" Minimizes the pane on its right tool bar. """
+AUI_MINIMIZE_POS_BOTTOM   = 0x05
+""" Minimizes the pane on its bottom tool bar. """
+AUI_MINIMIZE_POS_MASK     = 0x07
+""" Mask to filter the position flags. """
+AUI_MINIMIZE_CAPT_HIDE    = 0
+""" Hides the caption of the minimized pane. """
+AUI_MINIMIZE_CAPT_SMART   = 0x08
+""" Displays the caption in the best rotation (horz or clockwise). """
+AUI_MINIMIZE_CAPT_HORZ    = 0x10
+""" Displays the caption horizontally. """
+AUI_MINIMIZE_CAPT_MASK    = 0x18
+""" Mask to filter the caption flags. """
+
+# Button kind
+AUI_BUTTON_CLOSE = 101
+""" Shows a close button on the pane. """
+AUI_BUTTON_MAXIMIZE_RESTORE = 102
+""" Shows a maximize/restore button on the pane. """
+AUI_BUTTON_MINIMIZE = 103
+""" Shows a minimize button on the pane. """
+AUI_BUTTON_PIN = 104
+""" Shows a pin button on the pane. """
+AUI_BUTTON_OPTIONS = 105
+""" Shows an option button on the pane (not implemented). """
+AUI_BUTTON_WINDOWLIST = 106
+""" Shows a window list button on the pane (for AuiNotebook). """
+AUI_BUTTON_LEFT = 107
+""" Shows a left button on the pane (for AuiNotebook). """
+AUI_BUTTON_RIGHT = 108
+""" Shows a right button on the pane (for AuiNotebook). """
+AUI_BUTTON_UP = 109
+""" Shows an up button on the pane (not implemented). """
+AUI_BUTTON_DOWN = 110
+""" Shows a down button on the pane (not implemented). """
+AUI_BUTTON_CUSTOM1 = 201
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM2 = 202
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM3 = 203
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM4 = 204
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM5 = 205
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM6 = 206
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM7 = 207
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM8 = 208
+""" Shows a custom button on the pane. """
+AUI_BUTTON_CUSTOM9 = 209
+""" Shows a custom button on the pane. """
+
+# Pane Insert Level
+AUI_INSERT_PANE = 0
+""" Level for inserting a pane. """
+AUI_INSERT_ROW = 1
+""" Level for inserting a row. """
+AUI_INSERT_DOCK = 2
+""" Level for inserting a dock. """
+
+# Action constants
+actionNone = 0
+""" No current action. """
+actionResize = 1
+""" Resize action. """
+actionClickButton = 2
+""" Click on a pane button action. """
+actionClickCaption = 3
+""" Click on a pane caption action. """
+actionDragToolbarPane = 4
+""" Drag a floating toolbar action. """
+actionDragFloatingPane = 5
+""" Drag a floating pane action. """
+
+# Drop/Float constants
+auiInsertRowPixels = 10
+""" Number of pixels between rows. """
+auiNewRowPixels = 40
+""" Number of pixels for a new inserted row. """
+auiLayerInsertPixels = 40
+""" Number of pixels between layers. """
+auiLayerInsertOffset = 5
+""" Number of offset pixels between layers. """
+auiToolBarLayer = 10
+""" AUI layer for a toolbar. """
+
+# some built in bitmaps
+
+if wx.Platform == "__WXMAC__":
+
+    close_bits = "\xFF\xFF\xFF\xFF\x0F\xFE\x03\xF8\x01\xF0\x19\xF3\xB8\xE3\xF0" \
+                 "\xE1\xE0\xE0\xF0\xE1\xB8\xE3\x19\xF3\x01\xF0\x03\xF8\x0F\xFE\xFF\xFF"
+    """ Close button bitmap for a pane on wxMAC. """
+
+elif wx.Platform == "__WXGTK__":
+
+    close_bits = "\xff\xff\xff\xff\x07\xf0\xfb\xef\xdb\xed\x8b\xe8\x1b\xec\x3b\xee" \
+                 "\x1b\xec\x8b\xe8\xdb\xed\xfb\xef\x07\xf0\xff\xff\xff\xff\xff\xff"
+    """ Close button bitmap for a pane on wxGTK. """
+
+else:
+
+    close_bits = "\xff\xff\xff\xff\xff\xff\xff\xff\xcf\xf3\x9f\xf9\x3f\xfc\x7f\xfe" \
+                 "\x3f\xfc\x9f\xf9\xcf\xf3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+    """ Close button bitmap for a pane on wxMSW. """
+
+pin_bits     = '\xff\xff\xff\xff\xff\xff\x1f\xfc\xdf\xfc\xdf\xfc\xdf\xfc\xdf\xfc' \
+               '\xdf\xfc\x0f\xf8\x7f\xff\x7f\xff\x7f\xff\xff\xff\xff\xff\xff\xff'
+""" Pin button bitmap for a pane. """
+
+max_bits     = '\xff\xff\xff\xff\xff\xff\x07\xf0\xf7\xf7\x07\xf0\xf7\xf7\xf7\xf7' \
+               '\xf7\xf7\xf7\xf7\xf7\xf7\x07\xf0\xff\xff\xff\xff\xff\xff\xff\xff'
+""" Maximize button bitmap for a pane. """
+
+restore_bits = '\xff\xff\xff\xff\xff\xff\x1f\xf0\x1f\xf0\xdf\xf7\x07\xf4\x07\xf4' \
+               '\xf7\xf5\xf7\xf1\xf7\xfd\xf7\xfd\x07\xfc\xff\xff\xff\xff\xff\xff'
+""" Restore/maximize button bitmap for a pane. """
+
+minimize_bits = '\xff\xff\xff\xff\xff\xff\x07\xf0\xf7\xf7\x07\xf0\xff\xff\xff\xff' \
+                '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
+""" Minimize button bitmap for a pane. """
+
+restore_xpm = ["16 15 3 1",
+               "       c None",
+               ".      c #000000",
+               "+      c #FFFFFF",
+               "                ",
+               "     .......... ",
+               "     .++++++++. ",
+               "     .......... ",
+               "     .++++++++. ",
+               " ..........+++. ",
+               " .++++++++.+++. ",
+               " ..........+++. ",
+               " .++++++++..... ",
+               " .++++++++.     ",
+               " .++++++++.     ",
+               " .++++++++.     ",
+               " .++++++++.     ",
+               " ..........     ",
+               "                "]
+""" Restore/minimize button bitmap for a pane. """
+
+#----------------------------------------------------------------------
+
+down_focus_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACaUlE"
+    "QVRIib2WvWsUQRjGn5mdnWxyTaqz9q+QlLnGToSgWAYDNjbpNCAGDGIvaRPbNJGQyiAEbK+w"
+    "sAo2qexyEhbxsvt+jMXc3u3liPfhmWeXnWVm9vc+vO/M7prVzTb+gxyA7Ye/nXPWWmvtXKBb"
+    "B9YBcM5lWZam6by4QNcBsNamaeq9d87NmWutdc59+NgGoKIizCwsxMTMFI8oZmZilzomZiFm"
+    "FWERaXbv7eyueO+TJEHM79LSkvfeWnv2qftgex2ASGDmkrUkKUspiIuCy5IL4qKQgnghdQVx"
+    "ScKsxCKiaH8lIu99NOwAEFGsG4Dv5xeiQYOKBBYVUWJlFhIVVmIlEZGQJKVIYBbWoKqqwQN5"
+    "nqdpuri42OMys6rGOG/X78yW0bXWNyLqcyyAEEIIYcYK3aB5Lazb4o5fsPc3ToFaloxBwMle"
+    "6+9Pjfd7stda6HR85+dCPC86Y6ETcQEcHz32eZ7meZrnx0ePJnlk0vwenm70r/PkTgWdjjuV"
+    "rnPPfvxaa+3NcL3GMaub7XdPtNFoZFn24tmX1/trAOLuM6aaFQwQYExAMPWNaUw1FW+eHj5/"
+    "dbfZbDYajY33F7e1L4gUA5uo3fd8AWbQH70bjGqEyxLq3LoMYhKCgakCIWZoLLdkMRE43Iy0"
+    "tWi9QOP8xoIFAyBUjF7dgOizb9iMhLmByxIAHbAGKYigUPX3hqog47hSvfCHfYRaDcNg3IzO"
+    "7GmydRaGi37zMujrut/9l58nijROQ9yd3ZXLy8urq6vZWFmW9f+Yhrje++XlZR2keDpZa4f+"
+    "H/pKkiR+/f9dDsDWgQW6QHcuxKg/ZbVtCjjzINkAAAAASUVORK5CYII=")
+""" VS2005 focused docking guide window down bitmap. """
+
+#----------------------------------------------------------------------
+down_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACY0lE"
+    "QVRIib2WwWrUUBSG/3tzc5s2m0JhXPsU0u1s3Lkpui4W3PgAuhAFi2/QbesTVEphwCIU3Hbh"
+    "wk2LG1fujJQgtMk55x4Xd2aS6VAzM479JyQhufnOz3/uzcQMBgP8BzkAeZ4756y11tqlQIui"
+    "cACcc1mWpWm6ZK61Nk1T771zbilcxBxiAs659x/OAAQJIswsLMTEzBR/UczMxC51TMxCzEGE"
+    "RaR39WB3b9N7nyTJkLu2tua9t9ZefLx69GYbgIgyc82hJqlrqYiriuuaK+Kqkop4JXUVcU3C"
+    "HIhFJODsCxF57xu/RBT7BuDb958SNGgQUZYgEogDs5AE4UAcSEREk6QWUWbhoCGEENQDZVmm"
+    "abq6ujrkMnMIIdZ5t31vsUC3+l+JaMyxAFRVVRds0C1azsS6O273hH24cwq0UjIGipP9/t+f"
+    "6vZ7st9fKQpf/FqJ28+iEzoTF8Dx0RNflmlZpmV5fPR4lkdmzffwdGe8XyZ3Luh83Ll0k3vx"
+    "4/dWf3+B/Q2OGQwGGxsbeZ5nWfbi2efXB1sA4uozZjRKDaAwRqGmvTCNGQ3F26eHz1/d7/V6"
+    "eZ6fn5/f1bogCmhsonU+9AWY5nr0bjCtKS6LtrltGcQQ1MCMCiEm1MmtWUwETh6mjq1qw0Jd"
+    "fmPD1ADQEWPYNyD6HBs2U2Vu4bIoEBpWE0EE6ej68NaoSBdXRi/8SR/a6qE29830yKFmm2c6"
+    "2fTbp8FYN/0evPw0U6UuTXB39zYvLy+vr68XY2VZNv5imuB679fX10MT8Xyy1k58P4yVJEn8"
+    "9/93OQBFURRFsRTcWH8An5lwqISXsWUAAAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window down bitmap. """
+
+#----------------------------------------------------------------------
+left_focus_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACVElE"
+    "QVRIibWWvW8TQRDF3+7Ors8ShSsaSpo0dEgoFcINVChSBFRUkajpIKKgiPgP0pqGJiAhITqE"
+    "FIk2BQUVHT2VK+y7ndmhWN/5Ixcbh8tYWvtO8vvdm5mdPXPv+RmuMgjA670/RGSttdZ2q354"
+    "YgkAERVF4b3vHABMCIC11nsfQiCiqwJYa4noxbNvOw/6AJIk62ySJMLMwhI5MnPMnxzMzJHJ"
+    "E0dmicxJhEXk+uTO0fFuCME5h1yDxbh5+zEz93q+LGOv50WUmStOVZSqkjJyWXJVcRm5LKWM"
+    "3PNURq6iMKfIIpJw9n08Hg8Gg36/3wL4+eu3iHpykcWTS5pElCWJpMiJWaIk4RQ5RRERda4S"
+    "UWbhpCmllDQA0+k0pZQFVwF3bzEAZ5N3jgje+0COnPVknbUAdm5cW5/1/eGPxcuL2saoAczC"
+    "DQWAV0/fr1c/HxcBFNC8QGEMMu3NuyddAfIjG9QLjKJTB3NIHV050EZuoQI6+93q4P7B6TYA"
+    "A2gW1xlC61K0OXi492HZ6EbAnGFqEmBmhlYc7A9HutRq/wgA5plSwDT9tORgfzgCNsmv2QfQ"
+    "OvEwps7BooOPpwebxFsB83wazdWdl321BjOGWWejrciZ0+wBMwef76LPnx6trXFrivIfVOsl"
+    "P2V7FwH4MhpuCTBLX7mjckU628naTImlrdDdLDJ59OT+XDDU8SwyTX+Y2bC7hIPVA+fty6/b"
+    "SmwBODreHY/H0+n0P0WLomjegJYAIYTBYNAcp5cOa20IoQXgnMuvAh0GATg8scAEmHQrneMv"
+    "3LAo6X/e0vAAAAAASUVORK5CYII=")
+""" VS2005 focused docking guide window left bitmap. """
+
+#----------------------------------------------------------------------
+left_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACTklE"
+    "QVRIibWWPWsUURSG3/u5u8RiKxtLmzR2gqQSttFKhKBWqQL+BQXL4D9IGxsrBUGEFCIEbC0s"
+    "rOzshYHt3Jl7PizuzsduJhs3Ts7C3Z2BfZ95zzn33DGnp6e4zvAAdnZ2vPfWWmvtsOpFUXgA"
+    "3vvxeBxCuC6AtTaEEGP03g8LQE5RTo73/sXzr7sPJwCExTorLMxExMSJEhGl/MlBRJTIB0+J"
+    "iBORMBMz3/xz7+h4L8bonFsCunH77lMiGo1CWabRKDArEVUkVeKq4jJRWVJVUZmoLLlMNAq+"
+    "TFQlJpJEzCz49n0+n0+n08lk0gP4+es3swbvEnHwTlSYlViYJZEQcWJhkkSSmJnVuYpZiZhE"
+    "RUREI7BYLESkTVE37t8hAM5KcM57hBCid97Z4K2zFsDurRubk74/+9G9vKhtjBrAdG4oALw6"
+    "eLdZ/XxcBFBA8wKFMci012+fDQXIj2xQLzCKQR20kDqGcqCNXKcCuvzd6+DB4dk2AANoFtcl"
+    "QutS9Dl49Pj9qtFLAS3D1CTALA2tOdifnehKq/0jAGgzpYBp+mnFwf7sBLhMfsM+gNaJhzF1"
+    "DroOPpwdXibeC2jzaTRXty37eg2WDLPJRl+RM6fZA6YFn++iTx+fbKxxb4ryH1TrJT9lfxcB"
+    "+Hwy2xJgVr5yR+WKDLaTtZkSK1thuFlk8ujJ/dkxNPAsMk1/mOWwu4KD9QPnzcsv20psATg6"
+    "3pvP54vF4j9Fx+Nx8wa0AogxTqfT5ji9clhrY4w9AOdcfhUYMDyAoiiKohhWt4m/9Qss43IB"
+    "CBMAAAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window left bitmap. """
+
+#----------------------------------------------------------------------
+right_focus_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACWElE"
+    "QVRIibWWv2/TQBTHv3e+uyRbJwZWFv4AJNSRLjChSkhlYqrEzFZVDAwVC3PXsrAUISTExlKJ"
+    "tQMSWzcmFqaqQqT2+8VwtuMkbiBp+mzF0pPz/dzX7z373IMXp7jJCABebf8JIXjvvffrVd8/"
+    "9gFACGE4HMYY1w4AxgGA9z7GmFIKIdwUwHsfQth7/vXuoxEAFfWFV1ERZhYWYmJmykcOZmbi"
+    "EAMTsxCzirCI3BrfPzjcTCkVRYFcg27cubfDzINBLEsaDKKIMXPFWpFUlZTEZclVxSVxWUpJ"
+    "PIihJK5ImJVYRBSn387Pzzc2NkajUQ/g7McvEYuhIJYYCjUVMRYVUWJlFhIVVmIlERErikrE"
+    "mIXVVFXVEnB5eamqWXAW8Gb39uKHevbzNwARZVFirUSIlFkqEVUD8Pb71P1Lt83LZ+8BAA7O"
+    "AYABMAPcFfcvDXj97ikA5wxmHVVrf64LyA7Mau1so770uVjRQa1lzaKtSc2ZWAR4uHsyn2xq"
+    "YBnjbFp4zsRCBw6Ptz/M5GoHgLla15AfUV8F/gEwA/Bk66jPgXNwMNhkyf199F816DIaB5bx"
+    "yB2aO2qFLsp/+Xiy22YmczA1Cq4hLQlwsK56xwHgumLWln0pgPv8aWcmNdVF7TKujkWAL0db"
+    "88nagXWb0xYgVn4XWf0CymdzWQNgapJzWC7HCnPQF5M5aBhXzthqgMkcoF57Zxx6YvaDMzO3"
+    "148pwMHhJhFdXFwQ0XVEh8NhuwOaAqSUUkoxxvaLulp471NKPYC80ci7gXVFALB/7IExMF6j"
+    "bht/AXIQRaTUgkiHAAAAAElFTkSuQmCC")
+""" VS2005 focused docking guide window right bitmap. """
+
+#----------------------------------------------------------------------
+right_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACVElE"
+    "QVRIibWWv2sUQRTHvzM7M3dHmlQWtjb+AYKkTaOVCEKsrAL+CxaWwcY6bWysRAQRUtgEbC0E"
+    "u3RWNsJCCILZfb8sZvdu925zej/yveMWHnvvM9837+2OOz09xU0qANjZ2QkheO+999vNXpZl"
+    "ABBCGI/HMcabAnjvY4wppRDCdgHIJcrFCSG8eP7l7sMJABX1hVdREWYWFmJiZsqfLGZm4hAD"
+    "E7MQs4qwiNz6c//oeC+lVBRFA+jqzr0DZh6NYlXRaBRFjJlr1pqkrqUiriqua66Iq0oq4lEM"
+    "FXFNwqzEIqL4+u3i4mJ3d3cymQwAzn/8ErEYCmKJoVBTEWNRESVWZiFRYSVWEhGxoqhFjFlY"
+    "TVVVLQFXV1eqOitRV68Pby+v6fnP3wBElEWJtRYhUmapRVQNwJvvvftXbpuXz94BABycAwAD"
+    "YAa4a+5fGfDq7VMAzhnMOllt+rMpIDswa3JnG81lyMWaDppc1i7a2tCCiWWAB4dni8F2Dyxj"
+    "nPUTL5hY6sDh0eP3c7HGAWCuyWvIJRragX8AzAA82T8ZcuAcHAw2W/JwH/3XHnQZrQPLeOQO"
+    "zR21Rhflv3w4O5xGZnPQGwXXklYEOFg3e8cB4LrJbLrtKwHcp48Hc6FeF02Xcb2WAT6f7C8G"
+    "GwfWbU5bglj7WWTNAyh/28sWAL1JzrK8HWvMwZBmc9Ayrp2x9QCzOUCz9s44DGj+hTM3t5ur"
+    "Bzg63iOiy8tLItok6Xg8np6AeoCUUkopxjh9o64n731KaQCQDxr5NLAtBQBlWZZlucWkXf0F"
+    "imtJnvbT2psAAAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window right bitmap. """
+
+#----------------------------------------------------------------------
+tab_focus_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAIAAADZ8fBYAAAAA3NCSVQICAjb4U/gAAAC10lE"
+    "QVRIidWWT0gUcRTH387+ZveXZgzsbmvSsmqEfzBJSYz+gUsHCYJgISPytCQKFhJdpIN0qIUO"
+    "ezIUaU/roQ5eEzp46ZT/DhG4haCXdSUPK+Wuzvze+02HgdFmFqMtD76Bx8yb3/v8vr/3Zn4z"
+    "np6ReTgCYwAwdqfEGFMURVGU/wIdfaswAGCMcc5VVf1fXIBdBgCKoqiq+nxkobn3BABIkgBA"
+    "hIiEJFAgorAOyxARBTKVoUAkgSiJkIhO73a/nLjGOd/nWkrPXbqLiH6/CgBEJiIaKA1BhkG6"
+    "QF1Hw0BdoK6TLtCvMl2gIQhRCiQiCfPLm5ubtbW1YNXXtuzadyJTZV4AkKYkMpEkkRQoEUmQ"
+    "JJQCpSAiMr1eg8hEJJSmlFJK0wdQLBYR0cl9laj7l6LGY5/tc2ejsrmdgeGJbG5nYHgym9uJ"
+    "x9KHeGuMNd7B8fSMzCfvyerq6rHHn2bmEgPDE09G+/9WaSqZmRofisfSiadnotHoozclp94K"
+    "oGWznNxn/e8q4LqznNwXmb4KuO6s4643lZyugOvOcj8PDyrgurOOe30r05tKZv7ALavXmszt"
+    "rXZZL7EjhTmuU8lpRxNSyemZuUEAmJlLOPzU+CAAuKFluO7OWpF4LO1OPsTcejOOTcRepqXR"
+    "tngs7Y6U4bbcqNrIF6bGh6yt0prAgm7kC6E2fSNfWF9b2d7e1jStvqGlbMSmeRsuP7zZZvp8"
+    "PvCoW1s/a2qq7vddD57y3b7VZfmNfGFxadUQBgqztbWps7Pdy04uLq0WSyVJnoMRgUY45NM0"
+    "bXZZ7OvtaA8vLOdeT85mP+4eXN35K/6W5nBjxFz5tv7+w8LWF3+oTW+IBpsavStf1+xIfTTY"
+    "cNbknDPGfqsD5/xCa6AuDFe791xtEJyHIhHedTGw17tnj49EeFdH8GAkEAhwzgF+7HMZY5qm"
+    "cc6tD6rDGGOMMUS075aN2Ho9R/R/9gsXZ7dKHM+ODQAAAABJRU5ErkJggg==")
+""" VS2005 focused docking guide window center bitmap. """
+
+#----------------------------------------------------------------------
+tab_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAIAAADZ8fBYAAAAA3NCSVQICAjb4U/gAAAC1klE"
+    "QVRIidWVTWgTQRTHJ5vZZEyMLKSx1RbSImJbioeiCKIechLBU8CCtadAaaGIFC/iQTxowENO"
+    "hUohp/Tiocdce/Gk/TiIpTm0JCnmA+OaFJs2u/PerIcp27IbKsZ66Ft47P5n3m/evLc768lm"
+    "s+Q/GCWEBINBSqmiKIqinApU13VKCKGUMsZUVT1lrqIoqqq+frYyeP8cIUSgIIQgAgACcuAA"
+    "wOUlDQCAA1UpcADkAAIREPHiwa2383cYY0TWwa7AlRuPAMDvVwkhiBYAmCBMjqaJBgfDANME"
+    "g4NhoMHBr1KDg8kRQHBAREE+r1er1Z6enkOubbn8d0RLpV5CiLAEogUoEAUHAYAcBYLgIDgi"
+    "ouX1mogWAIKwhBBCWD5Cms0mADi57xKX/6Ws8dgX+97ZqFxpb3JmPlfam5x5nyvtxWPpE7yc"
+    "I+c7OJ5sNhsOh4PB4Kunn5aWE5Mz87MvJv4201QyszA3HY+lE88vRaPRYrHozLcDaNsoJ/fl"
+    "xIcOuO4oJ/dNZqwDrjvqrOebSi52wHVHud+HJx1w3VFnvb6d5ZtKZv7AbZuvXMztZbvkR+wI"
+    "oY7nVHLR0YRUcnFpeYoQsrSccPiFuSlCiBvahuvurFTisbQ7+ARz55txHCL2NmWOtsVjabfS"
+    "hjt0L1Cu1BfmpuVRKReQ0HKlHhkxypV6Ib/ZaDQ0TesfGGqr2DTv+Ph4IBDw+XzEo9Zqv0Kh"
+    "wOOxu10XfA8f3JS+XKmvrm2Z3ARuDQ9fGx297qXnV9e2mvv7Aj3HFQ5md8Snadru7u7Rua6q"
+    "6sp6aTNXzX08OL67q7f9Q4PdTP1ZKCn5Qq321R8ZMQaiXf19VuGbJ1/8IZX+aNdAnxWJRHp7"
+    "e7e3t4+4oVCo0Wjout5qtdx9YIwxxlqtlj3aVgmHw5qmbWxsHNWXUqppGmNM/lCd/aWUUgoA"
+    "9mhbhTFGKT3sm67ruq7v7Oy4cR3bb5uW079be13FAAAAAElFTkSuQmCC")
+""" VS2005 unfocused docking guide window center bitmap. """
+
+#----------------------------------------------------------------------
+up_focus_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACTUlE"
+    "QVRIic2WP28TMRjGH/85c1miqEF8CnbUgSFdukVRmZGQ+gW6Vgwd+hW60Yq1gMQMQzpHGZAg"
+    "C6JS+QIMmUju/L5+Ge6ulzoRTcMh5TmdZfv8/vT4Pcu26h2N8R9kAZwMfltrtdZa60agx5fa"
+    "ArDWpmmaJElTXGBmAWitkyRxzllrG+Zqra21bz+OAQQOzETExJ48EfniKURE5MkmljwRe6LA"
+    "TMz8ZPbs9GzXOWeMQZHfW33/NOufvALALESUU8g95zlnnrKM8pwyT1nGmadHic085Z6Jgidm"
+    "Dhh/mU6nnU6n1WrFXAA/fv7iIEECsxAH5uApELHnwBQ8Bc/MLMbkzELEFCSEEII4YD6fhxAK"
+    "Tsx9/tQDEIgqOzRggAQQQEEBguIFgKoNqDdfvy1yYq41emG4QKkSpDQAiNQfFQClpBoZcaK2"
+    "s0awEHzXVVyri1gxN7FaFuILu6qwtAyokqWWwEvcxNTTKsIK95Cqs4JJzV02vMJvHS/1cFFQ"
+    "UGV+K3tSzWlZq/5bOWGllIio0mzpX+pZSJXdVRmOuabcItRC+ZfKcn+pFRvN65fvNihj9Y7G"
+    "o9FoMplcX18f9M5lUx30zofD4WQyubm56R2Nm9oYY20B98XeRfPcAro+ei1uf/DBt9u+3c7b"
+    "7f7gfTPc/cOr7HE36+5k3Z28u5N1u/uHV/dG3X+gfb7YW8dgpC1YD1vBjfP7oEW6Lvf0bHc6"
+    "nc7n881YaZre3pjucJ1znU7n9qx+qLTWzrkVXGNMcav4d1kAx5camAGzRoiF/gCKPmudbgYP"
+    "HQAAAABJRU5ErkJggg==")
+""" VS2005 focused docking guide window up bitmap. """
+
+#----------------------------------------------------------------------
+up_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACTklE"
+    "QVRIic2WP4vUQBjGn/mTMUtgWS6yvb29XGGzzXXHwdWCcGBzH8BCweK+wnWyWKtot6DNge0V"
+    "gjYnNn4BA9vdJvO+81ok2ewmi7d3RtgnZEgm8/545skwiZrNZvgPsgCSJLHWaq211r1Asyyz"
+    "AKy1cRxHUdQzV2sdRZFzzlrbCxdlDmUC1to3Hy8BBA7MRMTEnjwR+fIoRUTkyUaWPBF7osBM"
+    "zDy+fnR2vu+cM8ZU3KV+fLo+fPUUALMQUUGh8FwUnHvKcyoKyj3lOeee7kU291R4JgqemDng"
+    "8ut8Ph+NRoPBoM0F8PPXbw4SJDALcWAOngIRew5MwVPwzMxiTMEsRExBQgghiAMWi0UIoclh"
+    "VY8fegACUVWHBgwQAQIoKEBQngBQ3wPq9bfv7XzX7o1eGS5QqgIpDQAizUMFQCmpR3bf26qc"
+    "NYKV4nVX7aumaavNjayWlfrSriotdQF1WKoD7nAj00yrLCvdQ+rOGiYNt2t4g9+mXprhoqCg"
+    "qnxre1LPqatN762asFJKRFRltvIvzSykTndTwm2uqbYItdL+5aLbX2nDRvPiyds7tC2p2WyW"
+    "pmmSJHEcP3/25cPFSXfQNjqeTE9fPhiPx0mSXF1d9bMxdrUD3OPJtH9uCd0evRX38Oi9Hw79"
+    "cFgMh4dH7/rhHpxc5PfTPN3L070i3cvT9ODk4saqmz9on6eTbQy2tAPrYSe47XxvtUi35Z6d"
+    "78/n88VicTdWHMfLP6Y1rnNuNBotv9W3ldbaObeBa4wp/yr+XRZAlmVZlvWCW+oP2FUt8NYb"
+    "g5wAAAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window up bitmap. """
+
+#----------------------------------------------------------------------
+down = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACFUlE"
+    "QVRIidWVPWvbUBSG3+tv8KKpnYtH/4DuJtCti2nntBm7depQWih0zT9w/AtSQsDQ0JIfkKFD"
+    "wRC61Iu3uBgRiKXz1eFalizb8QfxkFdCurofz3l1zhVyg8EAe1BhH9BHyC3NWkTU/XYFQEVF"
+    "mFlYiImZyR9ezMzEpXKJiVmIWUVYRJ7cPT/uHizhFgqF6+93Lz8fAhAxZo5ZY5I4log4ijiO"
+    "OSKOIomIq+VSRByTMCuxiCiufo3H4yAI8txisQjgz98bUVNTEWNRESVWZiFRYSVWEhGxYjEW"
+    "MWZhNVVVtQoQhuESrtfXw6e7JbTd+k1E6dv3+/3dQPeo3+8/3n22Si+OLgFLn52D4aLTun/V"
+    "er8XnVZ1NKqM/lX9eTNaC92IC+D87HUlDMthWA7D87NXmyzZNL+nl0ez60Nyt4Jux91Kee71"
+    "8Lbd6uxwzXFcr9drNpv+4f2bn59O2gDMAMC5ZJY5wOCcwZxlV7tkKr68PX338Vmj0cBev7f8"
+    "d0GkSG0i0576Alza7707LGqBy2JZblYOPgnm4JJA8Blay41ZnAfO3xbumWjTQOv8+oKZA2AJ"
+    "Y1o3wPucGXYLYVZwWQzQlJWmwIMs6Z8OJUHWcUU1aWZ9WKaGlo67xZlTbbbPbL7oq7fBTHm/"
+    "Jx9+bBRpnea4x92D4XA4mUx2Y9VqteVcAEEQ1Ov13bhZ5fP7IFB4v/v41f8HFQ1ap0nfm7YA"
+    "AAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window down bitmap. """
+
+#----------------------------------------------------------------------
+down_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACaUlE"
+    "QVRIib2WvWsUQRjGn5mdnWxyTaqz9q+QlLnGToSgWAYDNjbpNCAGDGIvaRPbNJGQyiAEbK+w"
+    "sAo2qexyEhbxsvt+jMXc3u3liPfhmWeXnWVm9vc+vO/M7prVzTb+gxyA7Ye/nXPWWmvtXKBb"
+    "B9YBcM5lWZam6by4QNcBsNamaeq9d87NmWutdc59+NgGoKIizCwsxMTMFI8oZmZilzomZiFm"
+    "FWERaXbv7eyueO+TJEHM79LSkvfeWnv2qftgex2ASGDmkrUkKUspiIuCy5IL4qKQgnghdQVx"
+    "ScKsxCKiaH8lIu99NOwAEFGsG4Dv5xeiQYOKBBYVUWJlFhIVVmIlEZGQJKVIYBbWoKqqwQN5"
+    "nqdpuri42OMys6rGOG/X78yW0bXWNyLqcyyAEEIIYcYK3aB5Lazb4o5fsPc3ToFaloxBwMle"
+    "6+9Pjfd7stda6HR85+dCPC86Y6ETcQEcHz32eZ7meZrnx0ePJnlk0vwenm70r/PkTgWdjjuV"
+    "rnPPfvxaa+3NcL3GMaub7XdPtNFoZFn24tmX1/trAOLuM6aaFQwQYExAMPWNaUw1FW+eHj5/"
+    "dbfZbDYajY33F7e1L4gUA5uo3fd8AWbQH70bjGqEyxLq3LoMYhKCgakCIWZoLLdkMRE43Iy0"
+    "tWi9QOP8xoIFAyBUjF7dgOizb9iMhLmByxIAHbAGKYigUPX3hqog47hSvfCHfYRaDcNg3IzO"
+    "7GmydRaGi37zMujrut/9l58nijROQ9yd3ZXLy8urq6vZWFmW9f+Yhrje++XlZR2keDpZa4f+"
+    "H/pKkiR+/f9dDsDWgQW6QHcuxKg/ZbVtCjjzINkAAAAASUVORK5CYII=")
+""" VS2005 focused docking guide window down bitmap. """
+
+#----------------------------------------------------------------------
+left = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACMElE"
+    "QVRIib2WPYsTURSG3zv3ThKJRSqblDYLwU6wFMKCViIEtbKQ/QdWgrXt/oO4hZWCIEIKUfYH"
+    "WFgIATuraewHM3PPh8XNZCaTSWI2oydwMx/kfeY959wzMbPZDC3FaDTavOi23Wgron8n/Z8A"
+    "rnry/NmXk/vXAAhLZCNhYSYiJvbkiciHTwgiIk8uduSJ2BMJMzHzjd93zi9OmwEAbt5+TETd"
+    "bpxlvtuNmZWIcpLcc55z5inLKM8p85RlnHnqxi7zlHsmEk/MLPj6LUmS4XDYDPjx8xezxs56"
+    "4thZUWFWYmEWT0LEnoVJPIlnZlZrc2YlYhIVERHtAIvFYquDu7cIgI0kttY5xHHccdbZKHaR"
+    "jSIAJ8Pru5M+GX+vnm4rslEDmMoFBYCXT9/uVt+MbQAFNCxQGINAe/XmSVuA8MgGxQKjaNVB"
+    "CSmiLQe6kqtUQJfHjQ7unV0eAjCABnFdIrQoRZODBw/frRvdCygZpiABZmmo5mAynupaq/0l"
+    "ACgzpYBZ9dOag8l4CuyT37EPoEXiYUyRg6qD95dn+8QbAWU+jYbqlmWv12DJMLtsNBU5cFZ7"
+    "wJTgzS76+OHRzho3pij8QLVYwlM2dxGAT9PxgQCz9hU6KlSktZ2sqymxthXam0UmjJ7QnxVD"
+    "Lc8is+oPsxx2V3BQf+G8fvH5UIkDAOcXp0mSVF94V4ter5emab/frwMADAaDcOOYSNO00+mE"
+    "4zrgePWaiAMwn8+PF932//MPv0Uk8OspzrYAAAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window left bitmap. """
+
+#----------------------------------------------------------------------
+left_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACVElE"
+    "QVRIibWWvW8TQRDF3+7Ors8ShSsaSpo0dEgoFcINVChSBFRUkajpIKKgiPgP0pqGJiAhITqE"
+    "FIk2BQUVHT2VK+y7ndmhWN/5Ixcbh8tYWvtO8vvdm5mdPXPv+RmuMgjA670/RGSttdZ2q354"
+    "YgkAERVF4b3vHABMCIC11nsfQiCiqwJYa4noxbNvOw/6AJIk62ySJMLMwhI5MnPMnxzMzJHJ"
+    "E0dmicxJhEXk+uTO0fFuCME5h1yDxbh5+zEz93q+LGOv50WUmStOVZSqkjJyWXJVcRm5LKWM"
+    "3PNURq6iMKfIIpJw9n08Hg8Gg36/3wL4+eu3iHpykcWTS5pElCWJpMiJWaIk4RQ5RRERda4S"
+    "UWbhpCmllDQA0+k0pZQFVwF3bzEAZ5N3jgje+0COnPVknbUAdm5cW5/1/eGPxcuL2saoAczC"
+    "DQWAV0/fr1c/HxcBFNC8QGEMMu3NuyddAfIjG9QLjKJTB3NIHV050EZuoQI6+93q4P7B6TYA"
+    "A2gW1xlC61K0OXi492HZ6EbAnGFqEmBmhlYc7A9HutRq/wgA5plSwDT9tORgfzgCNsmv2QfQ"
+    "OvEwps7BooOPpwebxFsB83wazdWdl321BjOGWWejrciZ0+wBMwef76LPnx6trXFrivIfVOsl"
+    "P2V7FwH4MhpuCTBLX7mjckU628naTImlrdDdLDJ59OT+XDDU8SwyTX+Y2bC7hIPVA+fty6/b"
+    "SmwBODreHY/H0+n0P0WLomjegJYAIYTBYNAcp5cOa20IoQXgnMuvAh0GATg8scAEmHQrneMv"
+    "3LAo6X/e0vAAAAAASUVORK5CYII=")
+""" VS2005 focused docking guide window left bitmap. """
+
+#----------------------------------------------------------------------
+right = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACMklE"
+    "QVRIibWWvY7TUBCFz83vojSuKNiShgdAol8hQYWQkJaKAuUNtqWmoeANFgoqhJAQHRLaB6BA"
+    "orC0HWnSUEURK2LPmRmKayeO4wTysydWbI+c+e7xzDgOo9EIK0rTdDW4m0Ij4FBK07R1fdmj"
+    "rh3QqZ6cPf965+ENAKbWardMTZWkUoVCUuIniiSFnW6HQqqQpkpVvfnn3uu395sBAG7fPSXZ"
+    "73ezTPr9rqqTzGm5aJ5rJswy5jkzYZZpJux3O5kwFyVNqKqGb9/H4/Hx8XEz4PLnL1XvdtpC"
+    "7Xba5qbqVFM1oZEqakoTmqiqerudqzqpNDczM+8Bs9lsrYNXw1ub7+nl+DcAVaOa0HJVESM1"
+    "VzVzAG9+LF2/dZFfPHsPAAgIAQAcgDsQ1ly/NeDlu6cAQnC4V7L6/GtfQHTgXuSONopdk4sd"
+    "HRS5vFy0l6EVE5sAD4YXq8GyBh4xwZcTr5jY6CDg0eMPtVjhAPBQ5HXEW9RUgX8A3AE8OTlv"
+    "chACAhy+WHJzH/1XDaqM0oFHPGKHxo7aoYviTz5eDOeRxRwsjUIoSVsCAryaveIACNVkPi/7"
+    "VoDw+dNpLbTURfNlrNcmwJfzk9Vg4cCrzekbEDs/i7x4AMWt3B0AsDTJUR7LscMcNGkxByVj"
+    "7YztBljMAYq1V8ahQfU/nNrc7q/6e9FkMplOpyKyT9Kjo6MkSQaDQZqmdQdJkiRJsk92AFdX"
+    "V71eLx7XAQfRYDCYH68FHOr19C8Ad0k9S0aHzwAAAABJRU5ErkJggg==")
+""" VS2005 unfocused docking guide window right bitmap. """
+
+#----------------------------------------------------------------------
+right_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAAA3NCSVQICAjb4U/gAAACWElE"
+    "QVRIibWWv2/TQBTHv3e+uyRbJwZWFv4AJNSRLjChSkhlYqrEzFZVDAwVC3PXsrAUISTExlKJ"
+    "tQMSWzcmFqaqQqT2+8VwtuMkbiBp+mzF0pPz/dzX7z373IMXp7jJCABebf8JIXjvvffrVd8/"
+    "9gFACGE4HMYY1w4AxgGA9z7GmFIKIdwUwHsfQth7/vXuoxEAFfWFV1ERZhYWYmJmykcOZmbi"
+    "EAMTsxCzirCI3BrfPzjcTCkVRYFcg27cubfDzINBLEsaDKKIMXPFWpFUlZTEZclVxSVxWUpJ"
+    "PIihJK5ImJVYRBSn387Pzzc2NkajUQ/g7McvEYuhIJYYCjUVMRYVUWJlFhIVVmIlERErikrE"
+    "mIXVVFXVEnB5eamqWXAW8Gb39uKHevbzNwARZVFirUSIlFkqEVUD8Pb71P1Lt83LZ+8BAA7O"
+    "AYABMAPcFfcvDXj97ikA5wxmHVVrf64LyA7Mau1so770uVjRQa1lzaKtSc2ZWAR4uHsyn2xq"
+    "YBnjbFp4zsRCBw6Ptz/M5GoHgLla15AfUV8F/gEwA/Bk66jPgXNwMNhkyf199F816DIaB5bx"
+    "yB2aO2qFLsp/+Xiy22YmczA1Cq4hLQlwsK56xwHgumLWln0pgPv8aWcmNdVF7TKujkWAL0db"
+    "88nagXWb0xYgVn4XWf0CymdzWQNgapJzWC7HCnPQF5M5aBhXzthqgMkcoF57Zxx6YvaDMzO3"
+    "148pwMHhJhFdXFwQ0XVEh8NhuwOaAqSUUkoxxvaLulp471NKPYC80ci7gXVFALB/7IExMF6j"
+    "bht/AXIQRaTUgkiHAAAAAElFTkSuQmCC")
+""" VS2005 focused docking guide window right bitmap. """
+
+#----------------------------------------------------------------------
+tab = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAIAAADZ8fBYAAAAA3NCSVQICAjb4U/gAAACq0lE"
+    "QVRIidWWTWgTQRTHJ+3ETEugERrtF6QhFhKsIlgQRITm5LkBvdiDhBZKc5DiRXryoAEPPRUi"
+    "pTnVi4cee5NePAitFtFoVxSyheYDa02KCd3deW/Gw2Cy7MZaIz307TK7/Gfeb9587Nvx6LpO"
+    "TsA6TgJ6glyqHgcHB4/ub0ZvdRFCBApCCCIAICAHDgBcXcoAADhQLwUOgBxAIAIinju89iRz"
+    "gzHW5Pb09BBCImO3AcDn8xJCECUAWCAsjpaFJgfTBMsCk4NposnB56UmB4sjgOCAiIJsbJXL"
+    "5b6+PsYYtQev5b8hSi/tJIQIKRAloEAUHAQAchQIgoPgiIiys9NClAAIQgohhJBnCKnX6wDQ"
+    "jFfZ0+TA/8xpIv6+8e5cN61Qm05ltEJtOvVMK9QS8ewRpWqj2js4nsb+nbv3cnU9OZ3KzD2c"
+    "/NdIF9IrS4sziXg2+aA/FAr5/X5nvG1AW3o5ufOTL9rgur2c3Mcrd9rgur1Oe7wL6edtcN1e"
+    "7v1wtw2u2+u0z2978S6kV/7CbRmv6sxdquVSH7HTR/9tE+PLUsqp2cz27k/7PTWbkcezifHl"
+    "tbW1XC6n6zp1dONeWaUk4tnjTwtx5F81KEcSaQxzdT1p1xPxrFtpwY3d7C6WKkuLMypVqg4U"
+    "tFiqBEfNYqmi57er1WogEBgOx1oqDVoz/77e2Onu6hq7emGg/6w9imKp8ubt149a/mI0rGqV"
+    "8u7DlyuXRuzKp8/5yzG/yr9NrmEYm1uFba2svTq0c0eu+2LR88z7Qy905PW9vZwvOGqGQ73D"
+    "Q1Lf9eR3vitlONQbHpLBYHBwcJAx5rGfd6rV6v7+vmEY7nVgjDHGDMNo1LZUIpGIc34ppYFA"
+    "gDGmfqgOo5RSSgGgUdtSUSUANLmqWp0q/mTK82hFcX4Bm24GMv+uL+EAAAAASUVORK5CYII=")
+""" VS2005 unfocused docking guide window center bitmap. """
+
+#----------------------------------------------------------------------
+tab_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAIAAADZ8fBYAAAAA3NCSVQICAjb4U/gAAAC10lE"
+    "QVRIidWWT0gUcRTH387+ZveXZgzsbmvSsmqEfzBJSYz+gUsHCYJgISPytCQKFhJdpIN0qIUO"
+    "ezIUaU/roQ5eEzp46ZT/DhG4haCXdSUPK+Wuzvze+02HgdFmFqMtD76Bx8yb3/v8vr/3Zn4z"
+    "np6ReTgCYwAwdqfEGFMURVGU/wIdfaswAGCMcc5VVf1fXIBdBgCKoqiq+nxkobn3BABIkgBA"
+    "hIiEJFAgorAOyxARBTKVoUAkgSiJkIhO73a/nLjGOd/nWkrPXbqLiH6/CgBEJiIaKA1BhkG6"
+    "QF1Hw0BdoK6TLtCvMl2gIQhRCiQiCfPLm5ubtbW1YNXXtuzadyJTZV4AkKYkMpEkkRQoEUmQ"
+    "JJQCpSAiMr1eg8hEJJSmlFJK0wdQLBYR0cl9laj7l6LGY5/tc2ejsrmdgeGJbG5nYHgym9uJ"
+    "x9KHeGuMNd7B8fSMzCfvyerq6rHHn2bmEgPDE09G+/9WaSqZmRofisfSiadnotHoozclp94K"
+    "oGWznNxn/e8q4LqznNwXmb4KuO6s4643lZyugOvOcj8PDyrgurOOe30r05tKZv7ALavXmszt"
+    "rXZZL7EjhTmuU8lpRxNSyemZuUEAmJlLOPzU+CAAuKFluO7OWpF4LO1OPsTcejOOTcRepqXR"
+    "tngs7Y6U4bbcqNrIF6bGh6yt0prAgm7kC6E2fSNfWF9b2d7e1jStvqGlbMSmeRsuP7zZZvp8"
+    "PvCoW1s/a2qq7vddD57y3b7VZfmNfGFxadUQBgqztbWps7Pdy04uLq0WSyVJnoMRgUY45NM0"
+    "bXZZ7OvtaA8vLOdeT85mP+4eXN35K/6W5nBjxFz5tv7+w8LWF3+oTW+IBpsavStf1+xIfTTY"
+    "cNbknDPGfqsD5/xCa6AuDFe791xtEJyHIhHedTGw17tnj49EeFdH8GAkEAhwzgF+7HMZY5qm"
+    "cc6tD6rDGGOMMUS075aN2Ho9R/R/9gsXZ7dKHM+ODQAAAABJRU5ErkJggg==")
+""" VS2005 focused docking guide window center bitmap. """
+
+#----------------------------------------------------------------------
+up = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACHUlE"
+    "QVRIidWWP4vUQBjGn8mfcyGQYiM2W8mWsRcLm22uWw6uFpQt7WwVLPwKVsp+gFMsAwqynY2F"
+    "oLAgNu4HsEiz3E7mfee1yN9Lcueu7oo+IcPMZN4fz7yZzEQlSYIDyAMQx/F+ocvl0tkvsdKh"
+    "uF6z8eLsAwDLlpmImNiQISKTX7mIiAx5vkeGiA2RZSZmvnF++9nzO0EQ9HC/vj2fPr0PgFmI"
+    "KCObGc4y1oa0piwjbUhr1oau+Z42lBkmsoaY2eLjpzRN+7kAvn3/wVasWGYhtszWkCViw5bJ"
+    "GrKGmVlcN2MWIiYr1lpr5QjYbDb9eQBw95YBIBBVdDiAC/iAAAoKEOQ3AJRtQL38/OXS/ALw"
+    "XKcxXKBUAVIOAIjUDxUApaQcecV7A3DkuYJG8EVX7VpdtNXm+p4jjfjcrsotdQFlslQH3OH6"
+    "bj2tPCx3Dyk7S5jU3K7hHr91vNTDRUFBFfkt7Uk5p6763lsxYaWUiKjCbOFf6llImd2+DLe5"
+    "ruM0UlA5uazS7S/Usz88vnf2G2VLKkmSap989OD9m8WsO2gbnU7mD5/cHI/H+C/3yR24p5P5"
+    "/rk5dHv0VtzpyWsThiYMszCcnrzaD/d4ttDXIx0NdTTMoqGOouPZ4pdR7e+iq3fzyTYGWzrY"
+    "etj7zwOAOI7/yjmPHRfpFVKr1apqrNfrNE2bx+pOGgwGo9Eor1/wGwRB9QPwh/oH9oed9BPW"
+    "YyQlBOJt4AAAAABJRU5ErkJggg==")
+""" VS2005 unfocused docking guide window up bitmap. """
+#----------------------------------------------------------------------
+up_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAgCAIAAABhFeQrAAAAA3NCSVQICAjb4U/gAAACTUlE"
+    "QVRIic2WP28TMRjGH/85c1miqEF8CnbUgSFdukVRmZGQ+gW6Vgwd+hW60Yq1gMQMQzpHGZAg"
+    "C6JS+QIMmUju/L5+Ge6ulzoRTcMh5TmdZfv8/vT4Pcu26h2N8R9kAZwMfltrtdZa60agx5fa"
+    "ArDWpmmaJElTXGBmAWitkyRxzllrG+Zqra21bz+OAQQOzETExJ48EfniKURE5MkmljwRe6LA"
+    "TMz8ZPbs9GzXOWeMQZHfW33/NOufvALALESUU8g95zlnnrKM8pwyT1nGmadHic085Z6Jgidm"
+    "Dhh/mU6nnU6n1WrFXAA/fv7iIEECsxAH5uApELHnwBQ8Bc/MLMbkzELEFCSEEII4YD6fhxAK"
+    "Tsx9/tQDEIgqOzRggAQQQEEBguIFgKoNqDdfvy1yYq41emG4QKkSpDQAiNQfFQClpBoZcaK2"
+    "s0awEHzXVVyri1gxN7FaFuILu6qwtAyokqWWwEvcxNTTKsIK95Cqs4JJzV02vMJvHS/1cFFQ"
+    "UGV+K3tSzWlZq/5bOWGllIio0mzpX+pZSJXdVRmOuabcItRC+ZfKcn+pFRvN65fvNihj9Y7G"
+    "o9FoMplcX18f9M5lUx30zofD4WQyubm56R2Nm9oYY20B98XeRfPcAro+ei1uf/DBt9u+3c7b"
+    "7f7gfTPc/cOr7HE36+5k3Z28u5N1u/uHV/dG3X+gfb7YW8dgpC1YD1vBjfP7oEW6Lvf0bHc6"
+    "nc7n881YaZre3pjucJ1znU7n9qx+qLTWzrkVXGNMcav4d1kAx5camAGzRoiF/gCKPmudbgYP"
+    "HQAAAABJRU5ErkJggg==")
+""" VS2005 focused docking guide window up bitmap. """
+
+#----------------------------------------------------------------------
+aero_dock_pane = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAGcAAABlCAMAAABnVw3AAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAb3WKdHqPdnyRd3+deYGge4OifISifoallZaWgIy1g463hZC5hZG6iJO8o6Sk"
+    "pKSkpKWlpaampqenqKmpqaqqqqurq6ysrKysra6urq+vr7CwsLGxsbKysrOzs7S0tLS0tLW1"
+    "tba2tre3t7i4uLm5uru7vLy8vL29vr6+iZfLjJrNjpzPjpzQkZ7PkJ/SlKHSkaHek6Pgk6Th"
+    "labjmKjlnqzhnqzjoa/npbPov8DAwMDAwMHBwsLCwsPDw8TExMXFxsbGycnJycrKysvLzMzM"
+    "zM3Nzc7Ozs/Pz9DQ0NDQ0NHR0dLS0tPT09TU1NTU1tbW1tfX0tTY19jY1tjd2NjY2dnZ2dra"
+    "2tvb29zc29zf3Nzc3N3d3d7e3t/fxs7szNPt0NXo0dfu1djk09js2tzk3d/k3d/m2Nzv3N/r"
+    "1Nr02N713OH13OL23+X43+X54ODg4eHh4eLi4+Pj4uPm4uPn4+Tk5OTk5OXl5ubm5+fn4eLo"
+    "4uTs5eXp5efv5+jo6Ojo6enp6urq6+vr6Onv7Ozs7O3t7e7u7u/v4Ob55Oj16Ov37e/26+/9"
+    "7/D28PDw8fHx8vLy8/Pz8/P09PT09fX19vb29vf38vT89ff9+Pj4+Pj5+vr6+vr7+/v8/Pz8"
+    "/f39/v7+////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAsPpcmgAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAf+SURBVGhD7Zr7d9tEGoa9UKBZCiapneBQN3bUQlgo13DbhXBrnRQnS0LJNhDbUO7s"
+    "ErCtZWWZm2Rj2YHdbWQsCSQL2/yjZkaypBlpdKH14XA4vL8kkuebx3PR6HtnHBtHkfxyZoGs"
+    "zMtR4sfjWJRi0mJRHZGlFhej1BCNs1XyocDbpa0ooEjtSfcDOHp6apyFAMzop4XfF2f0K7Vn"
+    "qpyhr0bT5AwHvhpOkzPQ/TVNjt73lT6c4jzQNV/1p8npq77SBlNsj6b4Sr1Ozmtr99xpvQpm"
+    "6LKvquyMVezOe9Ze81uDfNbRHLUtiPaqNi8KvrqqztvlRGGbypFJRM7mMoOtnPNHDV8JisOB"
+    "QczyJolE4qy/MMAX6Pl23VdNGeeMBi+sE0AEzsYl92tgXqj5ipNcnNHo0oYX5OVsnfe8beab"
+    "VVQcelHveTij895XrJdD6SGc4b+HCIglcHTK0yAPJ4dPAYOJtUd/78b3dAdE4owYz6zzcM4Q"
+    "3tEop/v18ePHv+7aICJndMbdIDdn93Iwp/vh7VAf2iAy5/KuC+TmPN0J5HQ+eNDUB51Ji8ic"
+    "zjMhnAwpIbT7TfjXU4+ZeuqfbRNE5miZEE5qGNSe2g//s/RDLYgzSoVwiKlammMmvVTjLE0w"
+    "NH+UJmV37peFe3yInM3NZp0liz/azk+NM0ptt3tkdXZSxGQ1vD3EDOpiep6s9EVSeW+y5e03"
+    "/1Qt+ie/Fseb1HnaE5AS+ieL7k+IHMwTzqDDcDofkB+6P8qfRkPtpAEkD9Bbxsa4J+RHyCgo"
+    "hZRRm/xi9sxZH61smMBUQUEiRzwyC6G3jLk8IY8PdvElWMdf/6745om9DQP0UhELxDjQW8Zc"
+    "ntDF6Z8CdXSoID/XuxtyTmlBHD0dcy0ALs4oCRLrdiBHpmDuncT7wdWenxZ+I5xhEgxMWHvg"
+    "2AW3Z+RpD4c/ChMO6WVhjZlMmRwscMjhQwo5WM9+hT8WgyQwCgIVtOLIFPQSSdyKDb7CpwXg"
+    "DLGa2b49g/W+1jc4LSrgcR1IJgdYpL5tk/o6i4YMYL/hX6TmYD76XtN0k9MPAE044Pt9/5ED"
+    "qmERQ8DBPWHNMlT6O08eaqqeBManRWn+vrEvUdAbJTVVO3zyHX0SrtXQCOAtAQd71BnVdFQ/"
+    "fvnQ6qGq9BOyLDcp1d83aj0KFJETqqIerj705Y9muMqgEcBbxhZwT1iVJSjt/dvOrR4q8oQj"
+    "+/tGtWtyFEk5XD132/saDJeVKhYxABwNFrNVMV7P2he3nDi3+oksaQkQxVMSVga7ABxYc0Lu"
+    "yZ+snjtxyxcarECqoIVUHXBUowGWSl1RFKVXbrj5xP2rH0tdNQGCeKqLlcEuZJGCFSckUfp4"
+    "9f4TN9/wigRq6JXQQjLkiC1U2xx0VOKnN91636MHQqN7EnxIL7exMthFm1+G1yeFunDw6H23"
+    "3vSpCOK5xjZaqKMCTodHtVVjgGryu5DT5ETIqWabWBnsojnhNJkm5Lwrwwpq9S20UEsBnDaW"
+    "mOWrhrOuaJ8/8MgBx1ydA8a0nOV9sjdwm6tnoXed48vcwSMPfK5VjHAmj0bwEuC0aFTrpaIp"
+    "4coTB2y1Mwe+VznLYmWwC5bNwq8+xxbZgyeuCGZwqbKOFuIgB/eEueL+RI03vq2U27Ogt4vZ"
+    "SZKLuUcrE65l4YjO1vYr377RmMQWyjm0LNsL4BQblf2yMAvaXwjhwC4CnP1Kw/qSETiFPUSA"
+    "A4x2IRPcHujFZxk0br8U2h43B8ye/RAOnKHXxykJd4Dx3AvmZOCQ33Fd7Zlwlpx+oxnXjgX3"
+    "Hyoap4XNohzWb6VWHHx62eHQbMt2WuY/teeegxXEw9qT5i2vZvAwzn6VT0CO02+McDpt6PSp"
+    "U+bf9N+MuEQF/YL4PICeL7a12UK9Wr5SclTlN1YMjv2cgqfSeHHIWm4H/lX0s/8wOCvnmbIT"
+    "WMbWA+D5NmNj3KuVOsgCyF2cMyq5i7NutjfOmtsyytkj46+wWDb7fW6DdSKFoxJi/zqXUvBc"
+    "Jr+EHCHNzMUdJWBrgJ5fNDsL6O61lFF6MWsGUevW8K4knMDZBOoXlvLecybXMmRVUrBEWnq8"
+    "98BCE+br8eUuWrWeUr95zr1JZNjQf5P3kpp8re05uXPks4F9tHOSALpGzsqu/z65sjuZlCju"
+    "GjkJMYDThUuGS+GcVIP2hsVtzP+/sfRf654Kl0CX6t2w/SqwxARx3nr8YVOPvxnAofl22P7b"
+    "0wXCu9Npj3LlL6auOD3pbQ/dpMP2E3fXsd1ps21xxc5ilbf/DPU2csfLqXXyYfuj42zL23Fx"
+    "M7k3pHx27NixzxT7WlY8HJqXlsP2e8e5Hd4DiqPJ8nev/unV75AbHg7NdvbC96/HSy3PVIhL"
+    "6Caf/LqMXbrbwzRF9ywg/S5gc80DiveABXAELYWtroRzaIbvPus9miGcl6xfEOp417k4GFTE"
+    "OTTb7G4QDmaI5z9rnQbL0A4rLl71Fcqh6RrfEZ+NeP4zHm8uXT4SeM5O+We7ARwZpsaG6nzr"
+    "qrSXiXyeBebkhcyFCmuPdkLq+qpnWD5DEkfnMhd+wfkcLArOG+20Yaaw66s92kkFfvl5I/6l"
+    "FgLPAad4TtsJOgecIifgHBB4wun9PgT3lphvrHtSKCI20u9qfJI6Yy33vjr/4Fg98Ee/hZ8H"
+    "k2bLAu4tsYRomvPN5S1RDvSEU3tOXd4S2yICnnBqHJe3RLMD6AmjKNJ64PKW6C8VoSeMop8B"
+    "XFekjMjBOHUAAAAASUVORK5CYII=")
+
+#----------------------------------------------------------------------
+aero_dock_pane_denied = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAGcAAABlCAYAAABQif3yAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "ABh0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjM2qefiJQAAELhJREFUeF7tnfmPHNURx81v"
+    "hEsCIQQCxCnu60d+AH5BCCGBAAkEP/AHABKKkEhCCErCHcA4WGAIYJwYbOMrGHzgE9vYxvje"
+    "9drYa+99etd7zHov766PSn3avFFvu3v6dU9Pz2Q9K5Vmtt9VVd9X76zqOWtSCf4NDw9LfX39"
+    "pMbGxklHR0YKxuHvzj570tVXXz3ptttuO6tgjUykioeGhuTbRYukrr5eRkZG5OTJkwUj6qcd"
+    "2ptIOiyYLFV79khDQ0PBAPEDm/Zot2BCTZSKFy9eLGNjY6mCc+zYMaHdiaLDgsmxYOHCVIHB"
+    "kvij3YIJNVEqLoNTwkgWAxysp2w5Fp2iDI6FkoqVxYBz4sQJSYPM6q1sORaIoyRAOX78eCpE"
+    "W+VhzQIYsgAOwLC8TYvK4EQAB1DY66RBtIX1lIc1C4BQEgobHR1NhegAZXAsgDHDGgrj3CsN"
+    "ohMwjJYtxwIglITCjh49mgrRAc5YcGpqa2Xjxo2yZs0aWaiKR/m5aNonn0hLS4vodUEq1NTc"
+    "LO3t7UK7YbzBP3IgD3JZ9LXSzLJj505Zvny57Nu3T7p7eqS/v9/qzGzBggVO3h4tkwb19fU5"
+    "wyft2lxPGN6QC/mQszQR8OGqqqpKlv3wg7S2tloJ61UISjpy5IgcPnw4FaIDMITaguPlFzmR"
+    "F7lLGqRdu3bJ1q1bnTHcphf65UFJvb29cujQoVSoq6tLuHmNCw4yIC9yI39JAlRRUSH7q6tj"
+    "g5I9RlFw6M1tbW2pUEdHh3D7mg84hnfkRw8lBRC3iNu3b88bGGenruDQm5t1oo5KKDpqGSx0"
+    "cHAwEXDgHz2U1K0qEyMbx7hDmbtcXHDYSDa3tDsbyigAsVJLEhz0gD5KwnpYrcSd/IPmnKiW"
+    "MzZ2TOoaWuWPr051PvnfFqCkwUEm9FESq7jlK1YkYjHuOScKOAMDA9LRmZG/vj07S/zPcxuA"
+    "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"
+    "VvwtWIicAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_dock_pane_bottom = PyEmbeddedImage(
+    "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")
+
+#----------------------------------------------------------------------
+aero_dock_pane_center = PyEmbeddedImage(
+    "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"
+    "AAAASUVORK5CYII=")
+
+#----------------------------------------------------------------------
+aero_dock_pane_left = PyEmbeddedImage(
+    "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")
+
+#----------------------------------------------------------------------
+aero_dock_pane_right = PyEmbeddedImage(
+    "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"
+    "SBhbDwAAAABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_dock_pane_top = PyEmbeddedImage(
+    "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")
+
+#----------------------------------------------------------------------
+aero_down = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAgCAYAAADqgqNBAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo1MTo0MyArMDEwMMndnrAAAAAHdElNRQfZAxkQNALaVrQp"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAhJJREFUeNrtl01v2kAQ"
+    "ht81FgECjUQUyeKjBrfi0EOJktxzg0uOSaWe0lNP/Wk99lBy7A+oSiO1ORE+BChSTjGfBuPs"
+    "rATCNSkmyHtpX2ll2czOs+OdWTMMXIPBwKnVaqjX6xiORghK0UgEuVwOhmEgFosx1u/3ncrV"
+    "FYrFItKpFMLhcGBwy7LQ7nRQrVZRLpXAfl5fO4l4HLquBwb9U41GA2avB6V+e4t0Oi0NTMpk"
+    "MiCuOuavQlXVtRNGFs+NobPWLhphiO783YZ4gut3tc3OEN9/9fDQt5+0ebEbwttCHG9eR335"
+    "9A0v5LhDx0LlW+tJm6OTLAr59WDHcTaDiwXk96AqNj5/+eH57fzsEIa+t4k7KMur8TMMPYkP"
+    "744xMO8Wg+7puZ/5HrhfMI3ZbIb9ZByX708xHt6LK93Tc78+XHu+alXrlNdT+PTxgpepBtu2"
+    "fc8nu1AotB18Op1C0w7EdRMxxrwJtyn8uXOW9ezIt5ErctlwT+SUrTKkKIo7cvrUTSYTKXDK"
+    "dEo6V+RULrLkipxeuSy4p9QILmvP5yfhAi4z4VbCZZXaMkfZws/W+g//R+Hz4046fIe3R3S6"
+    "0YEftChIOtuJR1zRLvHiQ4r3afRxCbLeCRzhzWK73Rb/gNRXvGP8WqmILkLTtEAbRQqu0+3i"
+    "980NyuUyxGabpila5GarhVGALTJF/TKbFS1yIpFgj6VqglrJraorAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_down_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAgCAYAAADqgqNBAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo1NDo1OSArMDEwMEcuCiQAAAAHdElNRQfZAxkQNxVyqGIt"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAABENJREFUeNrtV02IHEUU"
+    "/qq6529nFnezLtGNEEk0O5NZI4GoQaLxoCJ4NgjBgAe9K/5c9SToQbx6UaJgQLyJXgIRDUkk"
+    "t+iS1YDgwSWHMGOyO7Mz0z/l97qqZ3vGnd0ZYb1oDY9XXT39vve+eu9VtwJHo9H4+ufrK8+f"
+    "XQ6xuh5jt8ZCReNM3Ue9uvjl3NzcKUXgH06fvX7C1B7H08cNDpQUSvxjXlGofWqPWo8JYCiR"
+    "CCchdZe6Q71OudE2uPy9gv7tEj57qXpeXbh4yXzQfAzvnTQoGwWPKL4WMM4JrERgZdwhDgh/"
+    "sRExCMURLhjOu1rjrW8M3tz3I/QnPwW4/4RCPrIPjRpaWad2Er2Nl+JELoox+xwguP6tDYOD"
+    "RYW4sz1wnjzeaW3nnh2VKQXDvYpH/FUYWShpLBPXH4fGPCNaudHC+ctNNG6HI/+35y4fTx6b"
+    "wZGlaYTRznb91BszQmR0aKhWrUDo+fSrX0YaO/XsImqHp9HK+GcyOq2jOM6CG0kGByhabmo7"
+    "ly1kHqJNg/X6PF7xAnz48Xd/A37t1adwqDqP9cAM2jIZ+0NbkVSQgAUuI6VE0lLpZ2xs9Z2e"
+    "waHFBbz7xjNYa/7eF7mWdbkfG/QlGpqL/SDeZEDbyPlQbKx2YuhRMocTt367F+PevTN45+0X"
+    "0F5bTbRcy3r/WQzZyNplJIO0x7YMPIp2ks2XpM4zJXQr9FCvHcBH77+OBw7ux83OYJmmVKcM"
+    "JlE7SbekD57SnaUs21iGu5s8vLoBzNy3P9HDI7vnm83GYqg0w7MJF2QiV8YhO52oLep2Y0Q5"
+    "DQA6VgO332IvNNnIaSSIbB/3XaR9qmN3PUF/jV3kUZrA8WbCqeFSs3tiqEXY043tUHG6O2rr"
+    "yEcNM1QtiV1hwLW9NNtt5KH1Ktl7bR9Mki6lHtv37K0iz+5zWr6h29KByM81DPaS9z0Fhemc"
+    "QpnHWdGzbTWn7enmTQCenmI9onepW1xYC4Amy1Hx9wVtnUzBO4x8nTcKSicLXpKuPCA8m7GT"
+    "gKeUC3iX4NKaNwjeZka3erbWgyDajLwb2PY5xasC1xl8QnNKtSRiPCG4ZHaPtrpOpDIEQ4KJ"
+    "ogztkuotulrmYpEbnReqo836NhOCJ22UKD2JPpLorbRDY50bAA8t9V1Kj1T3tHt9cm8xySEz"
+    "IXjax/sMOIxkDIDDlQV2Pl7HAd9KUtvZMe574a6M/8H/q+D63/Mii+Pfzc+jMmugwka+2w4U"
+    "WdjlnMY054Lrv/xQDheuedj3RIQSi7/C7lLJASV2mIJnDxb/H/T2gC2xyN5e4PM5HpF5vqnM"
+    "c71M0LlvFc4c9eEfXTq8Ur96pfr58nHUHjV4cEpjhqAVgvHDgocNHYDteOOCy6nGMwT8KEGL"
+    "rP5JadCjX/mu98dFoBpfwbGHl2z3bDab5uq1ZZxbiXGztXufyPeUNV6sajxypI7Z2Vn1F7X+"
+    "m7ZM/KBNAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_down_focus_single = aero_down_focus
+
+#----------------------------------------------------------------------
+aero_down_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAgCAYAAADqgqNBAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo1MTo0MyArMDEwMMndnrAAAAAHdElNRQfZAxkQNALaVrQp"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAhJJREFUeNrtl01v2kAQ"
+    "ht81FgECjUQUyeKjBrfi0EOJktxzg0uOSaWe0lNP/Wk99lBy7A+oSiO1ORE+BChSTjGfBuPs"
+    "rATCNSkmyHtpX2ll2czOs+OdWTMMXIPBwKnVaqjX6xiORghK0UgEuVwOhmEgFosx1u/3ncrV"
+    "FYrFItKpFMLhcGBwy7LQ7nRQrVZRLpXAfl5fO4l4HLquBwb9U41GA2avB6V+e4t0Oi0NTMpk"
+    "MiCuOuavQlXVtRNGFs+NobPWLhphiO783YZ4gut3tc3OEN9/9fDQt5+0ebEbwttCHG9eR335"
+    "9A0v5LhDx0LlW+tJm6OTLAr59WDHcTaDiwXk96AqNj5/+eH57fzsEIa+t4k7KMur8TMMPYkP"
+    "744xMO8Wg+7puZ/5HrhfMI3ZbIb9ZByX708xHt6LK93Tc78+XHu+alXrlNdT+PTxgpepBtu2"
+    "fc8nu1AotB18Op1C0w7EdRMxxrwJtyn8uXOW9ezIt5ErctlwT+SUrTKkKIo7cvrUTSYTKXDK"
+    "dEo6V+RULrLkipxeuSy4p9QILmvP5yfhAi4z4VbCZZXaMkfZws/W+g//R+Hz4046fIe3R3S6"
+    "0YEftChIOtuJR1zRLvHiQ4r3afRxCbLeCRzhzWK73Rb/gNRXvGP8WqmILkLTtEAbRQqu0+3i"
+    "980NyuUyxGabpila5GarhVGALTJF/TKbFS1yIpFgj6VqglrJraorAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_left = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo0NDo0MCArMDEwMN+SkKkAAAAHdElNRQfZAxkQMBKjjWFJ"
+    "AAAACXBIWXMAAAsRAAALEQF/ZF+RAAAABGdBTUEAALGPC/xhBQAAAkJJREFUeNrtV02P0lAU"
+    "PS2lUEIlkxlHpCAEZ89Ol27M8A/czMK4duO/caeJiSZu3SgTN25M3DhBFkMmDhQJEK0apnwM"
+    "0wK1t+Z1YOqyr2w8yWuT95qcc9+97/UeAS6m06nTarWg6zrOZzPwhJJMolQqoVwuI5VKCcJk"
+    "MnFqh4eoVCrQcjnIssxVgGVZ6PX7qNfrqO7vQ/jSaDhqOo1isciV+Co6nQ5G4zFEvd2GpmmR"
+    "khPy+TyIW7xwt0SSpMgFECdxi5EzX8HGBDiOs1kBDH7ymaKoIAjCpQAiXy6XXAlFUfR4aBA5"
+    "EyBGET2R630LM+tvxpmQNQG8BpE3T8/w8cjEb3MREMe1BuLxOL4bY3z4NIAob/nzq1x+DYQt"
+    "IBaLodM18PpNA0p6Fwn5kpzqjXZmTUCYRUiR//x1hucv3yOl3lhbY4Gyd+g7QJGffG3jxau3"
+    "SCg7gXUWLH3nC6BfJI2wUC4V8OTxAZ4+qwXWiGexWPgp8J40EeYwTRM72xk8OrgfEMC+CRzD"
+    "sEUYhoFi4ToePriDuBTzBdD20zqrOWl1Mmz0ej1kMhncu3sTn5u2XwORHEOG4XAIbfcazm0F"
+    "aUXExdQO3oS8MXZbrz1t6ZL/gG3ba2uRtEKUYirMf2Hj/cB/AZ4A1hxsREDCdUKrVyNvULD0"
+    "HyBO4vackXsokXNtGR0R3t0RkSddf0iX1Hw+h3TbNYnvajXPKGSzWe7ekILsDwY4bjZRrVbh"
+    "JX80Gnnu+Fu3ixlnd0zR3yoUPHesqqrwB18A5ik1mQXQAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_left_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo0NjozNCArMDEwMCXtbZ4AAAAHdElNRQfZAxkQLw561jOY"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAABAVJREFUeNrtV02IHEUY"
+    "fVXTO70/M+yOExNd4oIRkgljDIiyQZR4FD0rQlDw6E1RDwkoIqiHXLzlKK4Iwb2peFDJHgxx"
+    "dS+LcTMhhw0qJkvG7Jjsz8xOd1f5qqu6p9eNMB2Y9WLBm6+nf+p79X2vvqoSYFtdXf3ql8bl"
+    "52aWQlxbVxhkmyxJvFz3UK8dmq1Wqy8IOv/+xEzjSV17AsePaTw0KjDCF4cFUKQtEB6vJSz6"
+    "bWYYERFqoEvbpW3TXm1rfDsvIBsX8OlLte/E3PkL+vTqNN4/rlGCQEE6hzGEta5TkYOATsCf"
+    "SBtCGqGy9wJ2+vo5jbcqP0J+fDHA/qcEisqyHnSL6MTjz+GnBYxv+SdDso/x1rvgPBudvb6A"
+    "8Z0nrQNpXsJIZaxBNt/a/c8bJf0vME3pDAGlbG4ieikYLUgrPOV6MULU4i5EqK0j7XyYazMz"
+    "RGYkXsImhbAvpIJ0Q8/jHM55MmLl+out6hFMCUR8OyCksiMX0kYj7gV26CInCZVxbvofKQhs"
+    "0Jq6IPkkISKTFKQRcB9plWGtt+ujH8QRcBgfEmj+3sYe1hVTY0J3PyUQhYiLhHkQa8Hlalvo"
+    "7hITRYErl5r44txNrNyMUGQkEpKZFLBUKiu2AjKhduKLLXKKkB9MjQn89kcLs18vo+Dvi++b"
+    "gQZRTwtecjMk3UiaHAlb97VlGidfO2306dy8N+krNK5cx+kz51GamMKo77TB3Ea6Nw1lEoFA"
+    "WSKR00SaBpU/Dff4EteuN/HOB5/dUZyh01hKwITEpMDAEAl0EpV/6KIPDDPHlxYX8eprH94x"
+    "Oqb/bmT7TFMw09SodgJUWJ/HKZoSpWqmjU9BDMWrI1dJt0L20w7Wj+LMRyfxxtuf7Hi2fFvh"
+    "V+45vuTYp9IUBMAaZ8IGmW0SbaKjLLZULzr9ovFXhMn778V7p07sIGD62wyxvQ6ABDZC+6BD"
+    "bEUWXYdA5UOH33yzAhw++ADeffMZ+MVCSsD013YzIUMgIgHFkWt+TFBJW6TYpQ2IxOaB6efz"
+    "qwHWS+N4/tkDGC95lgCfbdJXkNUA1E7RKbcoZStknqacKBeaIer7q5jurGGC+8Em05Msfr0I"
+    "DLgttRRGDpQxe4Ob3s3tQ/F2g4BJ7Q90fivYuaP4z3dE/xOQye9uMjG+ksrg7RkRGKMwy0W5"
+    "KyR8yr7M+r5GPRrf3itHhjC3WODhJILPyVv27FowSgy7tWAo51pgmqkBoVtlhyPEffmePX+U"
+    "6bg8h/iM6D16pH6jvjC/9+zSMTw4rXGIZ8MJeivTMS/hmzOiMCTypSl2Trulbeldp71NIsu8"
+    "cfEnoHZrHo8dfdjuMVqtll74eQlnLyusbAz2gHbfmMSLNYnHH6mjUqmIvwGdqbciWIcx6wAA"
+    "AABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_left_focus_single = aero_left_focus
+
+#----------------------------------------------------------------------
+aero_left_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo0NDo0MCArMDEwMN+SkKkAAAAHdElNRQfZAxkQMBKjjWFJ"
+    "AAAACXBIWXMAAAsRAAALEQF/ZF+RAAAABGdBTUEAALGPC/xhBQAAAkJJREFUeNrtV02P0lAU"
+    "PS2lUEIlkxlHpCAEZ89Ol27M8A/czMK4duO/caeJiSZu3SgTN25M3DhBFkMmDhQJEK0apnwM"
+    "0wK1t+Z1YOqyr2w8yWuT95qcc9+97/UeAS6m06nTarWg6zrOZzPwhJJMolQqoVwuI5VKCcJk"
+    "MnFqh4eoVCrQcjnIssxVgGVZ6PX7qNfrqO7vQ/jSaDhqOo1isciV+Co6nQ5G4zFEvd2GpmmR"
+    "khPy+TyIW7xwt0SSpMgFECdxi5EzX8HGBDiOs1kBDH7ymaKoIAjCpQAiXy6XXAlFUfR4aBA5"
+    "EyBGET2R630LM+tvxpmQNQG8BpE3T8/w8cjEb3MREMe1BuLxOL4bY3z4NIAob/nzq1x+DYQt"
+    "IBaLodM18PpNA0p6Fwn5kpzqjXZmTUCYRUiR//x1hucv3yOl3lhbY4Gyd+g7QJGffG3jxau3"
+    "SCg7gXUWLH3nC6BfJI2wUC4V8OTxAZ4+qwXWiGexWPgp8J40EeYwTRM72xk8OrgfEMC+CRzD"
+    "sEUYhoFi4ToePriDuBTzBdD20zqrOWl1Mmz0ej1kMhncu3sTn5u2XwORHEOG4XAIbfcazm0F"
+    "aUXExdQO3oS8MXZbrz1t6ZL/gG3ba2uRtEKUYirMf2Hj/cB/AZ4A1hxsREDCdUKrVyNvULD0"
+    "HyBO4vackXsokXNtGR0R3t0RkSddf0iX1Hw+h3TbNYnvajXPKGSzWe7ekILsDwY4bjZRrVbh"
+    "JX80Gnnu+Fu3ixlnd0zR3yoUPHesqqrwB18A5ik1mQXQAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_right = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo0NToxOCArMDEwMEtfu5QAAAAHdElNRQfZAxkQLyM/CW/t"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAkpJREFUeNrtVz2PEkEY"
+    "fnbZQ+AgGD+R5YSAxVkRLfwF5vgH11xlbeNPsbE2Ftf4C+RiY2OijeGu8GIusEjgiJ4Ej689"
+    "YHedd8zgsBgt3Fkan+TNZnY3eZ55v2ZeDQzj8dir1+uwLAsT24ZKxGMxFAoFFItFJBIJTRuN"
+    "Rl714ADlchlmNotoNKpUwHQ6RbvTQa1WQ2VnB9rh0ZGXSiaRz+eVEvvRbDYxGA6hW40GTNMM"
+    "lZyQy+VA3PoFc4lhGKELIE7i1kNn9mFtAjzPW68AgUXwhaKwoGnaLwFE7rru4gOZWKsiFwJ0"
+    "/+7tqQ6rw7JTVxcd4hOchvyCVPXOHbz9cA57soHtUhqO4yjxgMBSDogPo4mDN+/OcCVt4Ob1"
+    "JGazmTJvLEIgu4Uwmzt48fI9mq2vPBzyP/9qlF9LZeh/KeP5/mucffuOSCTC/wnCBOcfPSDj"
+    "6bN9fDppBOYJucJ4DtARSfbzKI6vCHjyeA+X05sYstMrCJA3RZVxAZTpwvx4tPcQ166m0ev1"
+    "Ak2+lTIkctk1G0YEu7sPeBV0u91AyQmCyxALEiBUbcYjuH/vFnsC7XY7cHK5yS01Iqr3ZELH"
+    "3VIc5g0X/X4/cHKx4d+ehiTgYvwFd0w3sIT7G1auQiRCZefzY+33gf8CuAD5eAxdwCXWfqkH"
+    "qLyAyKDNUismTuLmkxErSmTZWEbZr/puSOQxNh9Sg5vP5zBKbEh8Va3yQSGTySifDWmTndNT"
+    "fDw+RqVSAQ/+YDDg0/HnVgu24umYdn97a4tPx6lUSvsBjEDOU65zEi4AAAAASUVORK5CYII=")
+
+#----------------------------------------------------------------------
+aero_right_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo0ODo0NiArMDEwMKZ+RR0AAAAHdElNRQfZAxkQMQU5RdXP"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAA/ZJREFUeNrtV09oVEcY"
+    "/83sy27UfZhtNLWxKNaDK6sNlJZIabHgpdB7KYiCR28W7EEPIkLbgz1481hIaSt4a6UUWkyh"
+    "Imm9hKbpSg7pIUVjotnGJG5235uZft+bmc1bk8A+NTn1W743b98bvt9vvn9vRoBkbm7uxp/V"
+    "ux8Mjce4t6ixkdJflDhZCVApH7je29v7oSDwX48PVd8x5bdx9IjB/q0CW2hitwDyNOZIA7qX"
+    "sNqp8DIUaWyAJo1NGus0/l03+GlEQFZv46sT5Z/F8K3b5vLcID49alCEQE46wESFHZ1RkYGA"
+    "8UoXZZiQQazts4iMfnzT4JPSb5BfjkV49V2BvLasN1oUgQR0OfieAGPLh+SSl8nfZhPA097p"
+    "KwgwdpawbogEnpFOjazpeBv3P6uXzDrKok2KgNY2NopQcpwL0iaedlY4EY14hiQ0Fsg4DL7n"
+    "yhCplQSeTUuFndBKSLf0LOBw4H7F2tlLRr1CsEVA0eyIVNLLAi19K9VhnWvHOOc7UllI6BQ4"
+    "21euHLkvSHrjiUgfAp7I9d9H/n44VUdPl1hhbdrzoxM1ZrV6W7FfmyegYiRNIp8TmH6k8N3N"
+    "R5j4axalvGwj8TyqUupJrhDgdqktCZb5xRjXf5jEvw9q2EutudXNMgKyPT96jVgV2kNgX1Jc"
+    "Ur2w0VS4+MWPqE5Mob+giAS9z6j84/jrVC7ERrcW0+aByJXi03Lhs69x7/4sXqLszOIBg7Xd"
+    "7xPRpD3ALklCsE6nOX3mc1RHR9FNOaLWMLiWxs5e7O4jF+amsuoXm5Th0KxB73JEEwMMyNXF"
+    "dvXKOQQ7dmFiXnVchp5IRC5oENgy/VmiZJ+n7zKtA9/T2ve0+kAELNDLZY1VH/1L54+j/5Wd"
+    "+GVGZWrF3tVRAm71CWEsKQuq0x4AEWB2DdV6gkI+h7Nn38ee3SXcmIptvDJ0Ik8gcbu2thMi"
+    "ZIr3HJFqI6CIgKaJtvNtLwY4duw1LBS349vJJgJC9puULASiBNwkMW/QAw4Dd1jpynGFgKvR"
+    "Wfo+9+yUGBwIgd0hRmbipDXL9LehQ1mvCTEOL0alq8DLP080vpmOkd8XYqzWecI9j6zakEzX"
+    "NW49iFGPs6TcCySw2fI/Aemvm8mEsXLuPtixRWAblURI3/7NIFGgwg+7JBYoxxk7OHW4C8Oj"
+    "OTqcKBSoaEMq0iIpb8u6iSbNRRedkHLP0Ih8K+6mimZbhcCeP0ICDoeRnBGDNw5XZip3Rvqu"
+    "jR/BvkGDA7QB6SG0kPeGBFjgM6JgEtnClIDT2OAzIZFYpPExEZmkB2O/A+X5Ebw5cMjuM2u1"
+    "mrnzxziu3dWYXtrYA9qubRIflSXeer2CUqkk/gNN/sDRnOMoBAAAAABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_right_focus_single = aero_right_focus
+
+#----------------------------------------------------------------------
+aero_right_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAYAAACGVs+MAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo0NToxOCArMDEwMEtfu5QAAAAHdElNRQfZAxkQLyM/CW/t"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAkpJREFUeNrtVz2PEkEY"
+    "fnbZQ+AgGD+R5YSAxVkRLfwF5vgH11xlbeNPsbE2Ftf4C+RiY2OijeGu8GIusEjgiJ4Ej689"
+    "YHedd8zgsBgt3Fkan+TNZnY3eZ55v2ZeDQzj8dir1+uwLAsT24ZKxGMxFAoFFItFJBIJTRuN"
+    "Rl714ADlchlmNotoNKpUwHQ6RbvTQa1WQ2VnB9rh0ZGXSiaRz+eVEvvRbDYxGA6hW40GTNMM"
+    "lZyQy+VA3PoFc4lhGKELIE7i1kNn9mFtAjzPW68AgUXwhaKwoGnaLwFE7rru4gOZWKsiFwJ0"
+    "/+7tqQ6rw7JTVxcd4hOchvyCVPXOHbz9cA57soHtUhqO4yjxgMBSDogPo4mDN+/OcCVt4Ob1"
+    "JGazmTJvLEIgu4Uwmzt48fI9mq2vPBzyP/9qlF9LZeh/KeP5/mucffuOSCTC/wnCBOcfPSDj"
+    "6bN9fDppBOYJucJ4DtARSfbzKI6vCHjyeA+X05sYstMrCJA3RZVxAZTpwvx4tPcQ166m0ev1"
+    "Ak2+lTIkctk1G0YEu7sPeBV0u91AyQmCyxALEiBUbcYjuH/vFnsC7XY7cHK5yS01Iqr3ZELH"
+    "3VIc5g0X/X4/cHKx4d+ehiTgYvwFd0w3sIT7G1auQiRCZefzY+33gf8CuAD5eAxdwCXWfqkH"
+    "qLyAyKDNUismTuLmkxErSmTZWEbZr/puSOQxNh9Sg5vP5zBKbEh8Va3yQSGTySifDWmTndNT"
+    "fDw+RqVSAQ/+YDDg0/HnVgu24umYdn97a4tPx6lUSvsBjEDOU65zEi4AAAAASUVORK5CYII=")
+
+#----------------------------------------------------------------------
+aero_tab = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAMAAACxiD++AAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFq6ysra6urq+vr7CwsLGxsbKysrOzs7S0tLS0tLW1tba2tre3t7i4uLm5uru7vL29"
+    "v8DAwMDAwsPDxMXFxsbGycrKzc3Nzc7Ozs/P09TU19fX2dnZ2tra29vb3Nzc3N3d3t7e4ODg"
+    "4uLi4+Pj4+Tk5OTk5OXl5ubm5+fn6Ojo6enp6+vr7Ozs7e3t7e7u7+/v8fHx8vLy8/Pz9PT0"
+    "9fX19vb29vf3+Pj4+fn5+vr6+/v7/Pz8/f39/v7+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAA0PbvAwAAABh0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjM2"
+    "qefiJQAAAP9JREFUOE+t0tlSwjAUgOFjZQuxuOBSFURSSOyq0qYNEd//tUzCaIeLk9743Zyb"
+    "f5LOSUH3AC3vyEWIuIw06KvHRmGqKIJ3+u1RzaD0BpL+S3DwfIO5oqAHDxPk1LOpr9oGe0/h"
+    "ArXHmSCjLbpIpSobyBa3o5DSWqJaF+xqlPykEE/LHFWmU2AkS1GZCdYkFajkjZhAxCghCLwS"
+    "zlCcE1j1BpOeE176gqU/mMBy3F0Rb/mpZENh0QWxyJNTfD6HRXfFthgNndFgcJzDa2aCv0WZ"
+    "tUj7blLdP9nZ6PCZATtPft+qjELt/vAm/HCzCNYmuA2O5xqzmzOwAuIG0AfGfgDFvqY+8bKe"
+    "lgAAAABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_tab_focus = PyEmbeddedImage(
+    "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")
+
+#----------------------------------------------------------------------
+aero_tab_focus_single = aero_tab_focus
+
+#----------------------------------------------------------------------
+aero_tab_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAfCAMAAACxiD++AAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFq6ysra6urq+vr7CwsLGxsbKysrOzs7S0tLS0tLW1tba2tre3t7i4uLm5uru7vL29"
+    "v8DAwMDAwsPDxMXFxsbGycrKzc3Nzc7Ozs/P09TU19fX2dnZ2tra29vb3Nzc3N3d3t7e4ODg"
+    "4uLi4+Pj4+Tk5OTk5OXl5ubm5+fn6Ojo6enp6+vr7Ozs7e3t7e7u7+/v8fHx8vLy8/Pz9PT0"
+    "9fX19vb29vf3+Pj4+fn5+vr6+/v7/Pz8/f39/v7+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAA0PbvAwAAABh0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjM2"
+    "qefiJQAAAP9JREFUOE+t0tlSwjAUgOFjZQuxuOBSFURSSOyq0qYNEd//tUzCaIeLk9743Zyb"
+    "f5LOSUH3AC3vyEWIuIw06KvHRmGqKIJ3+u1RzaD0BpL+S3DwfIO5oqAHDxPk1LOpr9oGe0/h"
+    "ArXHmSCjLbpIpSobyBa3o5DSWqJaF+xqlPykEE/LHFWmU2AkS1GZCdYkFajkjZhAxCghCLwS"
+    "zlCcE1j1BpOeE176gqU/mMBy3F0Rb/mpZENh0QWxyJNTfD6HRXfFthgNndFgcJzDa2aCv0WZ"
+    "tUj7blLdP9nZ6PCZATtPft+qjELt/vAm/HCzCNYmuA2O5xqzmzOwAuIG0AfGfgDFvqY+8bKe"
+    "lgAAAABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_up = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAgCAYAAADqgqNBAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo1MToxNCArMDEwMESarloAAAAHdElNRQfZAxkQMyBAd2MK"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAh9JREFUeNrFl0tv2kAU"
+    "hY+NBXbAElS0QTwColKX4Q+gbvmDXXXVriq160olq7b7Rk2zICsejWyUqkmQgEIMxvWxZAti"
+    "0eCoHo40ssZj+5vrO3fmXgmuut2u4zb0+33MZjPEJU3TUKvVUK/X2SRlOp067ZMTNBoNNJtN"
+    "JJPJ2OCWZcEwTXw/OwO50o/zc0fPZFCtVmOD3tdgMMB4MoHc7/VQKpWEgalyuQxy5Tv3VyiK"
+    "IhROHrmyUOo97QXuOM7+4L4CZ/uziVuSJG3CCRYFX5+AcDg5iURiP3Ba7bOE+3xde/H5huWi"
+    "4SHLV6uVEKAsy5uW86hbLBZC4FzpoVCzbVsI3OcFcP5yUfBQqBEuyufkhFb7Y+BMubheHguX"
+    "fXiURjH7sSw7yIKivh/Ao4g+KxQK6A1MvHr9wbuyv35a7arI8Gw2i+ubCd6++4KUlveu7PN+"
+    "rPBcLoer33d48/4bDvTDoLHP+xyPop0zR13XYfxaov3VhJZ5Fhr/+NlE62UFh090jMfj/wdP"
+    "p9O4uk3itDNB6iC/9bnTjoXjFxmUnu42AQ/+0GKZz+c4KuaQz6kPflBTJYxuRrtZnnJjlbsb"
+    "N/xtsc7x2+thkIH8S6M/9tbdkkbyGxwn1yuX3OBDsVj0Dpc4j1aCVVWFYRhYLpdQnrsV46d2"
+    "26siGK9xFoo0zhwO0bm4QKvVgudsd3F4JfLPy0vPv3GJVh9VKl6J7EaP9Be4+2JJRD7+lAAA"
+    "AABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_up_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAgCAYAAADqgqNBAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo1NTo1MyArMDEwMAycPlQAAAAHdElNRQfZAxkQOBMcU9vX"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAABFhJREFUeNq1V82LHEUU"
+    "/1VNz863yWyUyCSaEPzYuGtkwY8QYwLiQfTgwYsgBPMXeDJXvYmo+Qc8KEFIULyJHhQWNcSE"
+    "3KLrLgqLoMYVkplIdmZ2PrrL36uqnulOsu50YB68ed3V3fV771fvvZpSoDSbzS9/Xll9+czy"
+    "EFc3IkxLGlWNE/MBjj17RBljPlQE/uH1MytHzcEjeOGwwYGSQokvzigqbUCbo9UTAhhqKMqL"
+    "IW2PdpN2g/pbx+DH7xX02gWcPblwWi2dv2Debz2Dd48bVIxCjiiBFjBeE1iJwumkIg4If5ER"
+    "NRiKIxxgtOhpjVNfGby15xL0xz8NsP+owkzoPpqmiBP5MEL9RUBw9bWuwe6iwvRWOi2C0yhp"
+    "CO6kSzkVCWJvzBYqopBtScwdruM8sHj+wkYuiWGMBxTrHxrvQdZcMCat0S33qcgFbMBBTau0"
+    "y3CL6K0yfmxCibzDoYkz3pdelGbA0U53Ir4RqfHLmq9FcHVmSy1D+GbEphnNF5cdLFYuAR65"
+    "MsgJqNcwMZkFzxB5THXcbGzUXk1iGS240BGmPEw3lqwlkcyfcbNxGHbOKEW7W/M4cpVYb7Em"
+    "I+0pQM/qIHIq8w1NMnLyMwhdHw98pCOqI39/NwnnGY2tMKwwLjUL7tbE0Iqyp1Ot5/HqqOwJ"
+    "FyYot/MKA5EZMTOOfOi8smuv3Yc26RLdRWeMPLnOoU++oV/SVOTnmuzv5H22oFDLK1S4nRVZ"
+    "DTN0JK/d7pbLAB7vYn2i92jbHLg5AFp9KV6Fs5zreAy+ycg3+KDADiMDOZuuCibnMjYLeEy5"
+    "gPcIvsmQuwTvMKPbfWNrfTAIx5H36FWHDpR5V+A4g7c0x1RLIkYZwSWz+5yr57UbOgwJJgwT"
+    "tEuqt+lqhYNFLvSMUB2O69tkBB/adm1IuwBL9E47ZMA6lwIfOup71D6p7mv/98m3VvHC3AIu"
+    "tzuYI//2zG27WJy8cW1bBjyGFQ8+al5RQs02Ksvxyr4Ae9t/WqvV9t8k548l858JYeOlhsLy"
+    "yhrePHXaWrnPUg13DX5oNsDf/9zAO+99jnKtYa3cy/hUwRd3Bbj5xzre/uAb1Or7Rir3Mi7P"
+    "s8jEbz+yI4fm79fxyRdrqO588LbnH322hjdezePRvbP4pRVONOdE4PtrGr2rHXx7qWup3krk"
+    "+bGwjYceqNCB4bbzOnD9//yv86Tx/IEKGrvL205YLSssXds68iROcC+PRxXmf5WNfCsHpEF8"
+    "xwlLE/DUve7a6Z2kyO8reY0arwU3OPl4HktXctjzHCenw1V2l2oeKLF2Cjm3scQNZzjByUKO"
+    "WvfwoDdgSyyytxf4fZ5b5Az/qdxHnyoE3fW1wonFAMHiwmOr85cvzn26fBgHnzZ4uKyxk6BV"
+    "gvFgwc2GDsB1vEnEdjj+cA8BDyVo0+Eb1CbZ+5Unxr/OA3PRRTz5xILrnq1Wy1y+soxzqxHW"
+    "29M7ON1f0XhtTuOpQ/Oo1+vqPxxtdiUOpmR7AAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+aero_up_focus_single = aero_up_focus
+
+#----------------------------------------------------------------------
+aero_up_single = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB8AAAAgCAYAAADqgqNBAAAALHRFWHRDcmVhdGlvbiBUaW1l"
+    "AG1lciAyNSBtYXIgMjAwOSAxNzo1MToxNCArMDEwMESarloAAAAHdElNRQfZAxkQMyBAd2MK"
+    "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAh9JREFUeNrFl0tv2kAU"
+    "hY+NBXbAElS0QTwColKX4Q+gbvmDXXXVriq160olq7b7Rk2zICsejWyUqkmQgEIMxvWxZAti"
+    "0eCoHo40ssZj+5vrO3fmXgmuut2u4zb0+33MZjPEJU3TUKvVUK/X2SRlOp067ZMTNBoNNJtN"
+    "JJPJ2OCWZcEwTXw/OwO50o/zc0fPZFCtVmOD3tdgMMB4MoHc7/VQKpWEgalyuQxy5Tv3VyiK"
+    "IhROHrmyUOo97QXuOM7+4L4CZ/uziVuSJG3CCRYFX5+AcDg5iURiP3Ba7bOE+3xde/H5huWi"
+    "4SHLV6uVEKAsy5uW86hbLBZC4FzpoVCzbVsI3OcFcP5yUfBQqBEuyufkhFb7Y+BMubheHguX"
+    "fXiURjH7sSw7yIKivh/Ao4g+KxQK6A1MvHr9wbuyv35a7arI8Gw2i+ubCd6++4KUlveu7PN+"
+    "rPBcLoer33d48/4bDvTDoLHP+xyPop0zR13XYfxaov3VhJZ5Fhr/+NlE62UFh090jMfj/wdP"
+    "p9O4uk3itDNB6iC/9bnTjoXjFxmUnu42AQ/+0GKZz+c4KuaQz6kPflBTJYxuRrtZnnJjlbsb"
+    "N/xtsc7x2+thkIH8S6M/9tbdkkbyGxwn1yuX3OBDsVj0Dpc4j1aCVVWFYRhYLpdQnrsV46d2"
+    "26siGK9xFoo0zhwO0bm4QKvVgudsd3F4JfLPy0vPv3GJVh9VKl6J7EaP9Be4+2JJRD7+lAAA"
+    "AABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+aero_denied = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAADxklEQVQ4jbWUzWuUVxTGn3Pe"
+    "+955k8nMJE5m0vqRZCaUYomG0uJGjDR0UXBRUj+6Kqg7kSy6SBf5A7ropiB07aK4qIsWIYsu"
+    "ioXBQoRKqWhJ1eq0sWqSmWQyk8y8H/e+p4t8oJ2YuPHA2dzD+fHwnHMuiQheR/BroQJQOxWv"
+    "Z7N9cRB8nAA+dIEBAIiAvwPgJ04kro1Vq/Mv66XtrCgVi9osLEx1K/V57+HD6a5Dh8B9fSDH"
+    "ga1WsXr7Niq3btVrUfS1yue/HH34MNwV/HM+n1HN5vd7+/vHsmfPQu3bt60i8+wZli5fxr/l"
+    "8nXT2fnJBwsLK8/XX/B4ZnhYqWbzyv5icaz34kXwnj2IrYUoBXge4HkQpRBbC85kkL1wAfuH"
+    "hsZUs3llZnhYvRQclMvnsqnUidTp04DWoEwGnE6DEwmQ44AcB5xIgNNpUCYDaI3UqVPIplIn"
+    "gnL53LbgmULB9YCp9LFjcHM5KM8D+z6o0QD+l9RogH0fyvPg5nLIHD8OD5iaKRTcTd6W/KhS"
+    "OZJOJgc7R0ag4ngd8gqhAHSOjMArlQbrlcoRAL+8oJhFjiaLRWhmrN29ixYzfNdF6LqIXBdG"
+    "a1itEWsNaA24LoQZ5s4duAC6hobAIkfbFDOQ78jlQPU6zNIS3jh/Hqz1jmolDFE7eRIqk4GX"
+    "zYKBfDtYJGDfB9VqkNXVV7IBAOT+fYjrrs9DJGgDE/Ns+OABkExCBQHmL10CM4OMAVkLAkAb"
+    "3hEAIgJZC7YW9OgRwnodzDy7xds8kF97et70iMrFfF6T1gAzEEWAMburBvBwcTH0RQbfX15+"
+    "Cjw3vIOl0tMgiq7VVlbA9Tq4VgOvrYGDYNes1esIoujawVLpaZtiAPitu/sta8yt/o6OVI/a"
+    "8X/aimVj8E+r1XCUeu/dWu3+tmAA+D2dHvetvdqtlDrgOEgQbQsMRDBnLWrGGM9xzozU6z88"
+    "X28D29VV/LF370d+HH/bFOntIkKGCJuLFwJYEcGqCDqJKh7zZ+88efKj09WFHcFbtuTzOW61"
+    "vghEPo1EDtiNdweASzSnib6Tjo6vum/eXCwUCm39dO/ePUxMTDizs7Oq0WjoMAy1tVbHceyK"
+    "iJsFOs4wv32AqF8AzMXx3FWRP5cAn4hCZo6YOdRah8lkMhwYGDCTk5NGzc/PY21tDcYYiuOY"
+    "RIQ30gGgKiL4xpi/AJSxvsIWAIjIAaBEJBYRx1rLxhhqtVqoVqu0rRXT09N048YNPH78mKrV"
+    "KjWbTfi+TwDgeZ4kEgn09vZKX1+fjI6OYnx8vA3yHxWIwp50Lj49AAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+auinotebook_preview = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0"
+    "RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJXSURBVHjahFJRSBNxGP/d/+52"
+    "W66ildPKJB9EMSwjFSNoUkS+GYg9pi9R9tJeopcgC3yKYvRUMHA+VBJUmpQPQSySKINqEGSB"
+    "Np3aNjs3N8+7285d/7sxa9yw3/G777v/932/+76PP6PrOgw8D876dYImWJAEZPpW8l89nYea"
+    "i8KGgMHRYPitvgkCw0G9qaNXD4x80Qs1BknRnzaBEfX1e+G758PQaEgvnP8VULA5lCS8/T7T"
+    "NUQK4ApO4j/1l3s6N/zA8MiGTxiGgUGowGwkhqkfc5Bl1SKwlM6i90y75dzsoKX9fNPLF2Mt"
+    "sZVGuMt3YPDpK7jsDnj7uiEIfH6ClAjF5rAKHD1xcW99a927C5e6hP3luwCGwONpRjwm4tqA"
+    "H7du9pmJDG9HrsSeSE3dvmdnz512tFZVQaGjaCyBjedRubscxzva8PrNJzNR0xmsKsQq4KzY"
+    "fqDW7UY8o4KjxRzLmpYlBLUNNZj48BVRMQlWA7I5yTqCvcyuq+s0qmkQiC1/QeiTo1ajNpWS"
+    "oMoMVJVyXbZ2sDj9S5sMh2l7CjJUJGswq0HNZBCNLmEhPA0xsQpJUSHJJUZILCbuxuZF7fv8"
+    "ApbTaaysrSEpSVgURdy//RDJxAp2RvzgMgkQzbpEziVEbgQfBeuXPY1dqcMS4W08cnIWY0Pj"
+    "iIXjuNI2A0dsDmXyT3yUTtESZ5EAU3CuDvhD7ydDB1mWg6zQWelSj2ydwbE9v1Fd4TQZmHLj"
+    "yTfBzP88PsgUCZTCne4qFycID7Zt4TuqK50TFaTmZMP1x5l/c/4IMABbKBvEcRELXgAAAABJ"
+    "RU5ErkJggg==")
+
+#----------------------------------------------------------------------
+whidbey_down = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACsAAAAeCAMAAACCNBfsAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAaHWVZ3egZ3e1cYCncYa0dpC4cYXGcpDBc5fWd6Dld6Pwi5OzgJbUkJjQkqHC"
+    "hafggqfwh7Dwk6Xgl7fwoKjWp7fXsbbWoLfnoLjwpMLwscHnsMfxtdD0wMHg5eTo5/D49/f3"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAverH2wAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAF4SURBVDhPbZTpYoIwEITRGqAQ1ChqOFrx/R8y3SObq8yvsHwZhglaOdH7HZbJIp1W"
+    "YW7t7w68LOcwDaxd5+lhC3q18zwOMhTWIjtZ+3wGfFmWFYZjgJl9vWC/1x30AI2zzExzIcqz"
+    "a2RxC2Ak2n4zJmEx1TgaY8R7AtHsdgMS1IivJRZHyN5lA6PMEgwZ2JVYs23bxwuWG3PGdF3X"
+    "ImvtBO/KqAl38ytgO+WqMz1SDDz7KXaCbatU5YYARt+SbUHg61zT9/iMHsQZKHBI9A1S6njk"
+    "fpuCRTjmJVQBymfxlfuCc5IX0cMhnhvCMYO8KCdCV0L9GbMz8Fhqous1ugbWw/32k2oDtFUn"
+    "do2sz1ywqWvCElz6pq4pS3CRVxooMvjqsI2+v5LwXCVrljfrGclhqH2v5e+Nr6VnYltgBct7"
+    "iDDExm+grvm0ouL/QwrjN1CHXvczYAz0xF5Pp9w168zfAhjeH9gSle8hnWutldL6H7rHOq2P"
+    "agd1f/M7VhKuYPh3AAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_down_single = whidbey_down
+
+#----------------------------------------------------------------------
+whidbey_down_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACsAAAAeCAMAAACCNBfsAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagcHiYZ3egcHilUHjRQXLwR4DwU4Pw"
+    "YIfTcYXGcIjVc5fWZpHxcJj3d6PwgJbUkJjAkJjQkqHChafggqfwh7Dgh7Dwl7fwoKjAqLDI"
+    "sLDIl8D4pMLw4Of35/D4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAQS37pgAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAGeSURBVDhPfdOLUsIwEAVQCCixFSggVfBFQWpVjKJo+f8fi3ezSZq2jDsDhHB62Wyh"
+    "o319Vctg9V2tO34p5H0bHw5D4Xe9FULK5bKhb6UcDs/cprNdMYiprhee30khaStx2NpudwCb"
+    "JHG8WAhbEkU0cZgtUge0iYrjCEUuilheOWysS2U9GpluGJJ1mKyg1LHZn6NYNNYUCWuozSjr"
+    "FVxnLGQUjZPJlLJKCk7piar0Nk2zS90RAhINVDZN5yUeNZuhbjq6BzmZomYoICr/Yr5mtcry"
+    "fL2mfnvjSd2anvkSsqBZUazN2XS/YVNLrc2Qut3yHAj/10OWFwVRtrpPvZo5cL+28A7fnxfv"
+    "G6Ps74GwsfXCsXKkMnWWko39DKukAbjUygKfskFqYLnnKvcHdQT9sA0EPdgD1ts9Vr2G/fpp"
+    "pDMeAg3gWe2fgn9V9d+0yW5edCylQurnwJejZ/7VPJpZ7V+C1PBsFrPFXAul6rSRi2S2uFmq"
+    "kdrK1frcWOq1kXrC6osHulnq963Wa3Nm9kOySrVpq1/yr5vNbtdK1foPGIxy6qmqIg0AAAAA"
+    "SUVORK5CYII=")
+
+#----------------------------------------------------------------------
+whidbey_down_focus_single = whidbey_down_focus
+
+#----------------------------------------------------------------------
+whidbey_dock_pane = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAF0AAABdCAMAAADwr5rxAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAaHWVcHiYZ3egZ3e1cHilcYCncYa0dpC4ZobHcYXGcpDBc5fWcZjgd6Dld6Pw"
+    "i5Ozl6e3q62xgIjQgpXEgJbUkJjAkJjQgJjggKDXkqHCk6TWhafggqfwh7Dwk6Xgk7Tkl7fw"
+    "oKjAoKjWqLDIp7fXsLDIsbbWoLfnoLjwsLjjl8D4pMLwscHnsMfxtdD0wMHg6tTN4Njk5eTo"
+    "4Of35/D48Ofh9/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAA1jVbdgAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAdFSURBVGhDtZpxe6I4EMbtbQVXrHJuaY8KWq/XW3Sr196u8v0/GTfzThICRIh9nssf"
+    "u4rw42UyM5kJHVVXjDQah/EV51ejK05+SKMwjK/BX0Gfzu9ms2kY3PgL8qd/nc/vCB+GN/54"
+    "b/p0sVjMaRA+9Mb70hku9GvUe9IBV3TC+9rejy5wTfdX70VneJ7LHRaLu7uIPNPL9j50hp+a"
+    "w9NzPOhQfvrHHqeZn/phOmzeofvZfpA+zTFa2rNsFk2GPWeIruB5y+5En00mg1E7QNfw/CSP"
+    "oEaZ51kWDavvpxu4phOWB+hJMqi+l17DFb0sBa+eJB3KOX30xLIFeAQXvKYTvtf2PfTVbrtd"
+    "6xsonrKMoafhJAwu5/vL9NWO6Rp/iZ5OJsFlfI/2onh7oxvQYKOsaeALjTV944fK2DFn489o"
+    "ryqmA69NDrtjWPSwZyHs9RlWr+h5Qzt9UdqzPvhATTBMn/Uu4QOxut9r24vFdzSUvUh+lk37"
+    "6wMXfVNf8sF0mdrt9nGF8YbB3ppNl/WpK8eNHPTNxuCXHx/a9q+vZz3exZvW6+m5NPhi58B3"
+    "6Yfj2zYRHcuSgOI5Bi0fDgeiT/nT8oxTi2K3U1dZz9ChH5gu+GXJ9HNRvP/dgoOfyEGox/Tn"
+    "nVlo04kNm34DHPRfv7psxuujywqm4rBo41t0Vs4uvl4/PXHUNLj7/f6VRvte5RJTjxBo4Zv0"
+    "A3sDxw2d+EeTcnzd4qnIdR43zZ/EbSUjNfEN+oHppFsiEZOmxn5/ZJOBvt1uNivrNyvk2nib"
+    "fmA6W0W0Z6FGkL0sNvLYt0L/aKeLtnqLTvCD2FzRNR53VelSZ8l1IfhfdqqDbRaWcWo6nWZs"
+    "zrk1SZLVvww40oBeZTG9oDwbOm6pDy8svKHzA9Y2Z3oUgU5WOSIXdOjAv/Nv1hLJ5aZRr+lQ"
+    "XhNYevSEZye6ZJo2PZNpd9ANXtFFuYtOM0p50UnP4Dm2VeQZavVCZ6cqudziCOL/szSKYkh7"
+    "hReu62UJv/MyS8+HM2AZuUwNgwcdTlVSkasrrppOgSL0Rg2ME9l6uD8/cqmWd643T0Y90xEO"
+    "azku48RzCu0vau1z0lNEhJu++Epkoks4dOhjPPaL+Hl+gf4upmlplzaIPGdUIbewXls7OTvo"
+    "xktbdK6auNyAWz266fOHaoQ4ZKNpOl940vRXHQN99BU9XMkzqylo3+7u0nTEdkHkKzoKRUNH"
+    "tccxeoEOwxs6XQqK0GdRNFoVKkqEDo+8lk4Syq2UsDWd2ubxKHtm5dzVid1Fe5bOjCx4kUt7"
+    "mohf8QPCMg3ttGERjp4z1Yk27O5HV15r6MbuVF1S2xbHo+pRNblNf88SaN9sVIpyaSfDYhnU"
+    "dEQK7A54ELO/f6VJqC2joknF4bmfPgadw4UtY+hmo4Vj9ZanWNtd07PsRfmDuYqTgc43nAei"
+    "e8TEu4OuWh7kGcb70kViKfR7phdvVA808gw34thHkBzJ+EZHSquH5NfzN5p1ZU+S/kM3fZxE"
+    "owBnCF2lWFAivcWi8jvUYydDBujAZ0JX7XD5Q0yTMT0EnVf6emHh9FC3anptgu3nc5ueppC2"
+    "0nfT//OdqRcOA5HeoVsbFGZdFbxFJ3/7C3ShyTPoz/C4PwHnhau+rLmxVdcEwNunRdEYNcFK"
+    "8Wv6jO4cBL8DfuC1xbqssbVi1TOMN6chHoSu1Ws6ByLZBfDzObEX8yRqNvd2LaZsLzt3k8lY"
+    "1g8aDw+Ukc3gfatAzMJjapTnOdEb2yqNOtLgSdwkMHBWGEIxxmQS/iYTqvAyGSmN9rZEswZW"
+    "eJkaC0Af4/ieBrPHY6kWWniCh+0NoVb9DjzbPLhZNhH07f4+CIK4heYbT0V7d0ui3XvcSvLk"
+    "QEbXROPnz859zFGOLGptZhRDru2UTt9EeB3ISj31TQ784fBIRwVeVTQtrq2gbs93G0Vfvtg9"
+    "3/Ft92KqdXWfI1cpVCspOOMnjg3QLr0aRwpOxuGOj+i7orD6JarnqVugEE2sfnXs2khx0CsD"
+    "r6rv33U3w/3MZvNCQ/dP3EI+1c2pa5fGRbfa2e/SApohXbw6RmFk0a2rzMd+ujRclAI0XnYh"
+    "pBRH1ujfhuil6yZX6LTEmlEnl158H91uFVVtr+t7ne0o9iKXSdSxHjpvLJieqLmXar5xaPfs"
+    "Ll2mP8IMWqRzTy/PSTplzIvqe7Qndit3gc4581Paq4p23lHpYP+dH6K5lyoZSce1U3+vz0xb"
+    "dMZLgcD/AB5aode9Qb+/S5VmtEsDUNMH334MxKqpcxw+4/HWbIAuNSbXgc3RLIo+4zNyjeBR"
+    "ZdbjRKuFKec+F03qqrpCtuieb/uGLGNXyDbdR7mugXsezlUhn06D3iJED+2qvkcDpCpUvzdl"
+    "nnTVnSg6vavxfT/spb1Wz9qpnPN7Q+mrXTsmF1I+78j0PHpq13jOLR7v966mV6jS0BX0vAJq"
+    "OZ+3dlLP8UnDH+7nkUpRHMf/399CVFUcf7nu7zj+A8yummsi9EdGAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_dock_pane_bottom = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAF0AAABdCAMAAADwr5rxAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagaHWVcHiYZ3egZ3e1cHilUHjRQXLw"
+    "cYCncYa0dpC4R4DwU4PwZobHYIfTcYXGcIjVcpDBc5fWZpHxcZjgcJj3d6Dld6Pwi5Ozl6e3"
+    "q62xgIjQgpXEgJbUkJjAkJjQgJjggKDXkqHCk6TWhafggqfwh7Dgh7Dwk6Xgk7Tkl7fwoKjA"
+    "oKjWqLDIp7fXsLDIsbbWoLfnoLjwsLjjl8D4pMLwscHnsMfxtdD0wMHg6tTN4Njk5eTo4Of3"
+    "5/D48Ofh9/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAaHenigAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAejSURBVGhDpZkLe9o2FIYpTVeWrG3Srmy9stk0LTPJMKMhmYM9KOuSERqydAX8//+H"
+    "dy6SLF+QRXeepwtg+9XnT+fotlqyRQRey+1vcX9S2+Lm08Bz3f42+C3ond5xt9txnRf2guzp"
+    "b3u9Y8C77gt7vDW94/t+DwLwrjXelo5wpm+j3pJOcEEHvK33dnSGS7q9eis6wqOIW/D942MP"
+    "MtPKexs6wlfZsMwcCzopX/2jx6prp76aTp4X6HbeV9I7EUVOexh2vXZ15lTRBTzK+Q70brtd"
+    "WbUVdAmPVvwKIuIoCkOvWr2ZruCSDlgMog+HleqN9BQu6HHMePEmQdWYY6IPNS+IB3DGSzrg"
+    "jd4b6KOr6XQsGxA84YyiB27bdTaP95vpoyukS/wmetBuO5vxBu2z2fU1NACBpowh6AvEGL7h"
+    "S4WYmN3W12hPEqQTXlpOvlNodNcwERpzBtULepTRDl+E9tAEr1gTVNO7xim8olbnc+k9O34F"
+    "IfwC+WHYMa8PyuiT9JHPSOeunU7PRhTXFJitYWeQ3joqaaiEPpko/ODzZ+n9xcVaxi1n03jc"
+    "WccKP7sqwRfpi5vr6ZB1DGIAcuYoNH9YLIDewU+DNd06m11diae0dyjQF0hn/CBG+no2u73M"
+    "wYk/5B9JPXV/VOiFPB3Y5Ol7ghN9uSyyES9/HSRkFZZFHp+jo3JM8fH4/ByrJsOdz+cXEPm2"
+    "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")
+
+#----------------------------------------------------------------------
+whidbey_dock_pane_center = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAF0AAABdCAMAAADwr5rxAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAGDeAJUqQIEOkIkewJFKlJlayMlSiMle1K2G0LmnSNnDgR2OkQ2O1UGagUHCo"
+    "VXSzaHWVZ3egZ3e1cHilQ2TAQHDAQnLSVnfFQXfgcYCncYa0dpC4VYTTRIDjVYbgZobHYIfT"
+    "ZpPXcYXGcpDBc5fWYIjgYZHgcZjgd6Dld6Pwi5Ozq62xgIjQgJbUkJjAkJjQgJjggKDXkqHC"
+    "k6TWlrDXhafggqfwh7Dwk6Xgk7Tkl7fwoKjWqLDIp7fXsLDIsbbWoLfnoLjwsLjjpMLwscHn"
+    "sMfxtdD0wMHg6tTN4Njk5eTo4Of35/D48Ofh9/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAXehG6QAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAdqSURBVGhDtZoNW9pIEMc5emrB651VUDRUpEkqb5d6SBOUXq9Q8dprke//aXLzsrvZ"
+    "hGWz+NzN87RCzP74M5mZzGyspDvYOGh4wx3OTys7nPx+HHjecBf8DnS/3wtD37t47S7InX7V"
+    "7/cA73mv3fHOdH8wGPTBAO85413pCGf6Luod6QQXdMC7+t6NznBJd1fvREd4kvAnDAa9XgCR"
+    "6eR7FzrCV3lzjBwHOilf/a3bKnRTX04nn2/Q3XxfSvcTsoL2OA6DdnnklNEFPCn4Hehhu12a"
+    "tSV0CU9W/BWErZMkjoNy9Xa6gks6YNGIHkWl6q30DC7o6zXjxTcZl9UcGz3SfEE8gDNe0gFv"
+    "9b2FPlnMZlP5AYInPKPoY6/tXWyv99vpkwXSJX4bfdxuX2zHW7TP5w8P8AFg6JQpGL0Bm8I7"
+    "/FIxBmbYeI72NEU64aXLye9kGt2z3AitMYPqBT3JaYc3Qntsg5f0BOX00HoLL8nVL1+k79nj"
+    "CzDhL5Afx769PzDR77Il35DOl3Y2u5mQPZBhtMb+KDt1YvggA/3uTuFH375J33/69CTtK0fT"
+    "dOo/rRV+vjDgN+nLx4dZxDpGawBy5Cg0v1guge7jq9ETnTqfLxZilfYdNuhLpDN+tEb603z+"
+    "9a8CnPgRHyT1dPmTjatQpAObfPo7wYn+48cmG/Hy6CglV2FaFPEFOirHEJ9Ob28xazRuFIYt"
+    "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")
+
+#----------------------------------------------------------------------
+whidbey_dock_pane_left = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAF0AAABdCAMAAADwr5rxAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagaHWVcHiYZ3egZ3e1cHilUHjRQXLw"
+    "cYCncYa0dpC4R4DwU4PwZobHYIfTcYXGcIjVcpDBc5fWZpHxcZjgcJj3d6Dld6Pwi5Ozl6e3"
+    "q62xgIjQgpXEgJbUkJjAkJjQgKDXkqHCk6TWhafggqfwh7Dgh7Dwk6Xgk7Tkl7fwoKjAoKjW"
+    "qLDIp7fXsLDIsbbWoLfnoLjwsLjjl8D4pMLwscHnsMfxtdD0wMHg6tTN4Njk5eTo4Of35/D4"
+    "8Ofh9/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAA+wCLtAAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAeDSURBVGhDtZr/f9JGGMdprZPJnHSKm9OKI4DG0K5kWAptGIw5FGqpdQL5//+Q7Ply"
+    "d7kkR3L4ms8PWmjyzifP3T33PM+1FO1gQ6/u+DtcH5V2uPh86DmOvwt+B7p7etLpuE7jmb0g"
+    "e/qr09MTwDvOM3u8Nd3tdrunYIB3rPG2dIQzfRf1lnSCCzrgbX1vR2e4pNurt6IjfDTiJ3S7"
+    "JycezEwr39vQEb5OmuXMsaCT8vUn3dYdO/XFdPJ5hm7n+0K6OyJLaQ+CjtcsnjlFdAEfpfwO"
+    "9E6zWbhqC+gSPlrzKwgLR6Mg8IrV59MVXNIBi0b0fr9QfS49hgt6GDJevMmwKObk0fuaL4gH"
+    "cMZLOuBzfZ9Dv1xMp2P5AMETnlH0odN0Gtvj/Xb65QLpEr+NPmw2G9vxOdrn8+treAAYOmUM"
+    "Rh/AxvAJXyrAidmpf432KEI64aXLye9kGt3J2Qhz5wyqF/RRQjt8ENqDPHhBTlBM7+Ru4QVr"
+    "9epK+p49vgAT/gL5QeDm5wcm+n58y2ek89BOp4NLsmsynK2B24svvTQ8yEC/s6/wvc+fpe9n"
+    "s420W55N47G7CRV+vjDgs/Q7D+/fE/heCECeOQrNPyyXQHfxp96GNM/ni0U/oz5DP3iIdML3"
+    "QqRv5vPb9yk48fv8Jamn4R9lRiFNPzg8rCJ+j+BEX62ybMTLb3sRuQqXRRqfogP88BDo9/b2"
+    "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"
+    "RK5CYII=")
+
+#----------------------------------------------------------------------
+whidbey_dock_pane_right = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAF0AAABdCAMAAADwr5rxAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagaHWVcHiYZ3egZ3e1cHilUHjRQXLw"
+    "cYCncYa0dpC4R4DwU4PwZobHYIfTcYXGcIjVcpDBc5fWZpHxcZjgcJj3d6Dld6Pwi5Ozl6e3"
+    "q62xgIjQgpXEgJbUkJjAkJjQgJjggKDXkqHCk6TWhafggqfwh7Dgh7Dwk6Xgk7Tkl7fwoKjA"
+    "oKjWqLDIp7fXsLDIsbbWoLfnoLjwsLjjl8D4pMLwscHnsMfxtdD0wMHg6tTN4Njk5eTo4Of3"
+    "5/D48Ofh9/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAaHenigAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAe+SURBVGhDtZr/W9pWFMYtW1en67RzunWdpUvQlgWdMCoyJFkotbaRCrMryP//f2Tn"
+    "yz25N19ILn2enV8Qkn7y5s2555570414jRh4dbezxvnxxhonXww81+2sg1+D3jw7bbWarvPM"
+    "XpA9/eXZ2SngXfeZPd6a3my322cQgHet8bZ0hDN9HfWWdIIrOuBtvbejM1zo9uqt6AgPAr5C"
+    "u3166kFmWnlvQ0f4Ih2WmWNBJ+WLf8xYtOzUV9PJ8xzdzvtKejOgyGj3/ZbXqM6cKrqCBxnf"
+    "gd5qNCpHbQVd4MGCb0HFMgh836tWX05P4EIHLAbRe71K9aV0DVf05ZLx6k4GVTWnjN4zvCAe"
+    "wBkvdMCXel9CH96Mx6FcQPGUMwl94DZcZ3W9X00f3iBd8Kvog0bDWY0v0R5Ft7dwAQg0JYSg"
+    "LxAhfMOb8jExW/Uv0R7HSCe8WE6+Uxh0t2QiLM0ZVK/oQUo7fFHa/TJ4RU9QTW+VTuEVY3Uy"
+    "Ee/Z8RsI5RfI9/1meX9QRB/pf/IJ6fxox+P+kOKWArPVb3b1qT8XXKiAPhol+O6nT+L99fW9"
+    "xB1nUxg275cJ/rvjV3l8nj6d3Y57fGJ3CUDOnATNf0ynQG/iX917OjU6Pn6Vx+foU6QzvrtE"
+    "+n0U3b3PwInf4x9JfbR/cHzSzj3iLB3Y5OlrghN9Ps+zES+/duO7h/tMz+IzdFSOKR6Gl5c4"
+    "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"
+    "AABJRU5ErkJggg==")
+
+#----------------------------------------------------------------------
+whidbey_dock_pane_top = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAF0AAABdCAMAAADwr5rxAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagaHWVcHiYZ3egZ3e1cHilUHjRQXLw"
+    "cYCncYa0dpC4R4DwU4PwZobHYIfTcYXGcIjVcpDBc5fWZpHxcZjgcJj3d6Dld6Pwi5Ozl6e3"
+    "q62xgIjQgpXEgJbUkJjAkJjQgJjggKDXkqHCk6TWhafggqfwh7Dgh7Dwk6Xgk7Tkl7fwoKjA"
+    "oKjWqLDIp7fXsLDIsbbWoLfnoLjwsLjjl8D4pMLwscHnsMfxtdD0wMHg6tTN4Njk5eTo4Of3"
+    "5/D48Ofh9/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAaHenigAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAe1SURBVGhDpZkNexJHEMcxrTVNao3G0PoSaXmJ4l1soBhCCUdBmkaPCDFWCN//e1z/"
+    "M7O7t/fC3aHzPMqFu/3t/2ZnZmeTUrCBnZ83m2cbPB+UNni4NxweHx/9tsGIDei92bBF9B+L"
+    "44vTu6T8AFb+oTC+ML07gfIjppe/L4ovSofy1nGjIfT9oviC9O6kBZ83flf0ovhi9NccLY2y"
+    "sv39gvhC9NeT4R/HR5q+Xwa9GL4IvQmvLBcRK4gvQG/+DZcvP8JG2hYF1efTSflxY2HIdLHY"
+    "gxVwTi696VGCptLz8Xl0Ud4oR90u2vOXNodOyjlYFjoa+VPT89Rn010ol+xXdP1Bq/oI9jA7"
+    "azPprguX23T4R7QbejY+i/6q1YJXpLYwlZ0foz/4LqOkZdBfvaJgaZQzPbP3IAu/nv7rEYxi"
+    "g+xgIfVrgX+44FV99HAP7J2dra216jO0/3RULkfpPAP9p+lg37u3Hp65r/oHByE9GvDy/e4u"
+    "2Bnw7F3bD+kQbGbS1w/y4Dk9wd29vcePtc5dtp9/1rOQ8jt3MjfBnFwlPLtgd2dnG7YD291V"
+    "88Et2fBU7eNQz2fgiU3kLWVPnhBflHfCRwcpb5GifTw2+M5n4Mkf21vbh7fanvzCPoLy25XB"
+    "+9MUfJI+v55NuqKjswLw7v3729vPDVounj6ld6Grzi0/6vvTqRplvUOCPie64Dsrot9u3X//"
+    "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"
+    "gg==")
+
+#----------------------------------------------------------------------
+whidbey_left = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB4AAAArCAMAAABYWciOAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAaHWVZ3egZ3e1cHilcYCncYa0dpC4ZobHcYXGcpDBc5fWd6Dld6Pwi5OzgIjQ"
+    "gJbUkJjQgJjggKDXkqHCk6TWhafggqfwh7Dwk6Xgk7Tkl7fwoKjWqLDIoLfnpMLwscHnsMfx"
+    "tdD04Njk5/D49/f3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAQ3JLMwAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAF4SURBVDhPfdTbdoIwEAVQL6htAi0tElBaoVb+/xPTM5M7wc6LLvc6GTIJbnRel/DT"
+    "ZkUvwXOefsahdqGMJ2LvSwaO4P7d5BdMWejQ9+dzzhOiY4/quu4z44kYSebudbH4REwLm7Q6"
+    "Jr2hk+lrGR4e7Rb17ZRSdV23v55vxL4vsRCBOesXRrquBbZm0yb7jIHD/EDNKPpUjRBvbmOU"
+    "Heb7/f6g/aISJhz6R8J4MJvmbM6FGSrPmRaM09i0YZ4zDdHxTN8909I8RMtzyu3NztgwbyxK"
+    "qytlq6pK0qqROC/0vipGzx0v7ll/MAY2Y1G1SWv9UpXlCvNlpiPZlyjX2w4VJ/rlZk7+D3Oe"
+    "DsoXwqoNV5HzKHtidJlUC3eXifuXZcxNE91U4xFLIb6jm8oeWIKLcFPN/jxLKY/HhM3+TUl5"
+    "OhU48eQN9Y6VTwe6D+kLbJ0W3m5X3m926ntgXb7+eg/z2ZzJhcuusN4Lsds9/WfSuhBes94U"
+    "C6r/AM3yZVcU56/qAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_left_single = whidbey_left
+
+#----------------------------------------------------------------------
+whidbey_left_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB4AAAArCAMAAABYWciOAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagcHiYZ3egcHilUHjRQXLwR4DwU4Pw"
+    "YIfTcYXGcIjVc5fWZpHxcJj3d6PwgJbUkJjAkJjQkqHChafggqfwh7Dgh7Dwl7fwoKjAqLDI"
+    "sLDIl8D4pMLw4Of35/D4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAQS37pgAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAGNSURBVDhPfdRrW8IgFAdwLyuXZLIUy67D6dK0pd3w+38xOuewAYOMN3ue/fbnwHZG"
+    "R8ej5251Yu33nMfcHw8H1iNOxsiNh5xkGUfvmqIBg2YZ8KBrvM3J5BoGH7NBnW9xMjHMGTjl"
+    "fQYlxvKYh/V5jErc5MEdJxMOAx/BKxZI0w/LkI340aZx5jZzdmdrU92TjKr8IbB2szHKqv1+"
+    "r2Y4hFDINzWf082A2Xxu+II0TDNU4KcnAcNPz4TiV0P6pp1XKduM9R0XZdlipXBpnNXpgGl3"
+    "Hu+qdSnzZ1sb0libpab2rqrKMs/d0tqstz+QhwfUt/dabFrrbYXzy1OsNwf0Fgs2TB+ad77B"
+    "6SNOba9B/uV49D6ZqIvX3bJp1m++mEBO710rLg8wv8wNmVZj6ZdrxeWatmcZW9FjXbxj3ufR"
+    "6NPr8wLyHnM28vtc6+INX08+w9LTKednwS9I+TwnJg3+UMoTGw3/7wKndxqyXq3gAdg9ZaO0"
+    "1oab8yo+mRYLKe1p9se5tpCX/7G+dUfhL8vucupsrDz0AAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_left_focus_single = whidbey_left_focus
+
+#----------------------------------------------------------------------
+whidbey_right = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB4AAAArCAMAAABYWciOAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAaHWVcHilcYCncYa0dpC4ZobHcYXGcpDBc5fWcZjgd6Dld6Pwi5OzgJbUkJjA"
+    "kJjQgKDXk6TWhafggqfwh7Dwl7fwp7fXoLfnoLjwsLjjpMLwscHntdD0wMHg4Of35/D49/f3"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAWZqHkAAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAFySURBVDhPddPbeoIwDABglDqoFDcVGBVw8P4PyXJoQ0sxN+L3m0NDzVaJx/YoT5k8"
+    "9fbAhfve2luS77kfhq5rir077pkTZ34Ng7Vt2yRO/ELuUPeOTIWxdOrA3Fc46p/9AVobsgnm"
+    "Z0b1xRsTeLa+EV1f+jCBQ+8DlnzgsDBX2fLxYFR8WeYtxJF/u65tF95KM0/TNEv+ZzZfkElL"
+    "TbKhuDEVnJ/4Z1+cufpmfsBwC47newNV1fV6v8cMTqMx67Jkhs0s3YIRsNbqHDCePczWhVIx"
+    "S28NoVRdRyxrMaR5zZPjdcDJha+opxOf+33ACthtrR/glkY7LzmXs5npjbn3VqqcFHmE2i0E"
+    "934+fd9PjKXdvylbR7yn/q7FuVB8HOF9uMJUOsjF3retb9PcysuFZ+aA0QrJJXYzC6/Fk+IO"
+    "Eee628IOquJcx5wP6nYV9cYvGpYBKucNRqNHpfW+r9+580uS63vjD855vvXcF4fvB7r+A9+i"
+    "Xf4K/oDaAAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_right_single = whidbey_right
+
+#----------------------------------------------------------------------
+whidbey_right_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAB4AAAArCAMAAABYWciOAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagcHiYZ3egcHilUHjRQXLwR4DwU4Pw"
+    "YIfTcYXGcIjVc5fWZpHxcJj3d6PwgJbUkJjAkJjQkqHChafggqfwh7Dgh7Dwl7fwoKjAqLDI"
+    "sLDIl8D4pMLw4Of35/D4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAQS37pgAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAGXSURBVDhPdZTZVoNADEDpaC2CFSqC1hVaxWoVqdv0/39szDIraF7gcHuTTCankbJx"
+    "6V7tW2TfTprVmDvcNKsxt7ismnbzOPQ1npaMh5zxNMdo4Afr0CfMNK8Bv4UcMdBzwshDHzBS"
+    "wlWN6QM/UmKecu68hBj40ed8nmrOuN28u/qR+op9XNfANw+mf8asow31ge8Mh9au4zhlRIF+"
+    "1z2zjwcTiKWL/f6p2zFHHMdJWkpty77/lpCffcQ3IwzHY5+GitkDG8fTddv/MB2v+9n6dlVJ"
+    "aBxq9/Dk/l+95IDgu8b3eD0GJ1ibTmYwzqFt12wTLn07xKc51XW16XqaF20D1jPVtRHf3XHn"
+    "Sxyqm1ovC5r+MZ97OcJEj/TULuA+B3ZRFIdm5njd/o1JaSgmvzK7Bh8LXAt8kku1/8KaAr61"
+    "u+ZsQ1X0Aauks1tsKdhCzGb4gzMKr66uTTzLFwuNnctjmUycb3t2m6om7JPtu3qZyE+yBURI"
+    "+UogvwAM5QfUYOw/ybIhtVghPuBUXrg/LiHG1NmwcSNXqV+4tHLqnJPo+QAAAABJRU5ErkJg"
+    "gg==")
+
+#----------------------------------------------------------------------
+whidbey_right_focus_single = whidbey_right_focus
+
+#----------------------------------------------------------------------
+whidbey_up = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACsAAAAeCAMAAACCNBfsAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAaHWVcHilcYCncYa0dpC4ZobHcYXGcpDBc5fWcZjgd6Dld6Pwi5OzgJbUkJjA"
+    "kJjQgKDXk6TWhafggqfwh7Dwl7fwp7fXoLfnoLjwsLjjpMLwscHntdD0wMHg4Of35/D49/f3"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAWZqHkAAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAFmSURBVDhPhdTtdoMgDAZgW3GKWjsL2i/X2vu/SJaEBBXcWf6Jz3l5i9bMpdNXR3Xa"
+    "Wc/StXNfKXXawaktm1rrUuWHJCWxX01TA1bqkODYlm3bNjCAVYwji9TbneStJcoWcNR5Yz0V"
+    "mySvLVJrvW/buq7g7NadVxbpvJ3taSyWUuef9cx6kxwsdU3sprPY0tJEucboqginwZapjfqC"
+    "1UUhT9BboXb28Twfa42pQjLZQMUCwiHbdZKMdqFsPx+PeZee3w2w3WpXugvUY7GAsXPmLvdx"
+    "HITzXe4QbK8Klbvsckcr+C/bF0WeZ+52ez6Bw+D2AwxdwAxwhRsaPDp9hA4OLWGpSn1pVlZh"
+    "X8Cg2dpNLlxwrgFKFpP/sRqZf26Ph3T2Te8w3AyijSlJ8fuA1v/Acfy+0Dxp8DyZig2dr9fw"
+    "WXj5ExoGnxpy5TSi78c0gRUacvE0XjvfsGnqwuryH3q/d6hz07L6CxOEXf5LAPv7AAAAAElF"
+    "TkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_up_single = whidbey_up
+
+#----------------------------------------------------------------------
+whidbey_up_focus = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAACsAAAAeCAMAAACCNBfsAAAAAXNSR0IArs4c6QAAAARnQU1B"
+    "AACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA"
+    "AwBQTFRFAAAAECBXFC9vECiQBS2oIDKMIDigC0CVJUqQIkewJFKlJlayMEiwMlSiK2G0JFTR"
+    "LmnSKGHmKHDoInDwMGjgNnDgNXLwSFeKQFizR2OkUGagcHiYZ3egcHilUHjRQXLwR4DwU4Pw"
+    "YIfTcYXGcIjVc5fWZpHxcJj3d6PwgJbUkJjAkJjQkqHChafggqfwh7Dgh7Dwl7fwoKjAqLDI"
+    "sLDIl8D4pMLw4Of35/D4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+    "AAAAAAAAAAAAAAAAAAAAAAAAQS37pgAAAQB0Uk5T////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////////////////////////////////////////////////////////"
+    "////////////////////AFP3ByUAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn"
+    "4iUAAAGTSURBVDhPfdQNV4IwFAZgpEyCVBRZaR8OUcI0yrLw//8xuvfuy22e7jkKZz68uxtg"
+    "0Pm135fl24XxwB/bNU1VFS/+D77d/TY12lsPe3aLqTkUu3Gxa7cHSC3IsmsHOxZS64pzYTMH"
+    "23Z7qKFXvpTWwZZd0w5wJivLbHxu14fmtSqUzRhYC5/ZEuY/tVbZ2NjyA1o9/UB9qmrtZG0x"
+    "teKtdnjSplCmDWXLd7xZF63G0opUzux2Ra5eoLCYShvQqv2io7IymewGUsV9lVYdcG1TqAnd"
+    "QbSbDbR6bqETkastYbCruob5xTNAhpp27PgK7WqFG8DZvz2kY8DBQwGF68XKW/HUtPCBE1rb"
+    "dJKCjOMwDLq7gjHbkscvZUEOBiGtLc+NtTdYjCcJyFDsQ2cshOnr1PlYUmH7aTqbqYyEajRS"
+    "12Bqr6f2V2CaLInjCCqGShJ5NTRAVOQSRokulDWfozap2gLGmaMwetIv7/yeulGpxnb94TCK"
+    "Hp23fLHAedSgeS/C4fHo/y89R5qqfhF9+xJGvszoH5Xccuo6pVT3AAAAAElFTkSuQmCC")
+
+#----------------------------------------------------------------------
+whidbey_up_focus_single = whidbey_up_focus
+
+#----------------------------------------------------------------------
+
+whidbey_denied = aero_denied
+
+#----------------------------------------------------------------------
+
+# ------------------------ #
+# - AuiToolBar Constants - #
+# ------------------------ #
+
+ITEM_CONTROL = wx.ITEM_MAX
+""" The item in the AuiToolBar is a control. """
+ITEM_LABEL = ITEM_CONTROL + 1
+""" The item in the AuiToolBar is a text label. """
+ITEM_SPACER = ITEM_CONTROL + 2
+""" The item in the AuiToolBar is a spacer. """
+ITEM_SEPARATOR = wx.ITEM_SEPARATOR
+""" The item in the AuiToolBar is a separator. """
+ITEM_CHECK = wx.ITEM_CHECK
+""" The item in the AuiToolBar is a toolbar check item. """
+ITEM_NORMAL = wx.ITEM_NORMAL
+""" The item in the AuiToolBar is a standard toolbar item. """
+ITEM_RADIO = wx.ITEM_RADIO
+""" The item in the AuiToolBar is a toolbar radio item. """
+ID_RESTORE_FRAME = wx.ID_HIGHEST + 10000
+""" Identifier for restoring a minimized pane. """
+
+BUTTON_DROPDOWN_WIDTH = 10
+""" Width of the drop-down button in AuiToolBar. """
+
+DISABLED_TEXT_GREY_HUE = 153.0
+""" Hue text colour for the disabled text in AuiToolBar. """
+DISABLED_TEXT_COLOUR = wx.Colour(DISABLED_TEXT_GREY_HUE,
+                                 DISABLED_TEXT_GREY_HUE,
+                                 DISABLED_TEXT_GREY_HUE)
+""" Text colour for the disabled text in AuiToolBar. """
+
+AUI_TB_TEXT             = 1 << 0
+""" Shows the text in the toolbar buttons; by default only icons are shown. """
+AUI_TB_NO_TOOLTIPS      = 1 << 1
+""" Don't show tooltips on `AuiToolBar` items. """
+AUI_TB_NO_AUTORESIZE    = 1 << 2
+""" Do not auto-resize the `AuiToolBar`. """
+AUI_TB_GRIPPER          = 1 << 3
+""" Shows a gripper on the `AuiToolBar`. """
+AUI_TB_OVERFLOW         = 1 << 4
+""" The `AuiToolBar` can contain overflow items. """
+AUI_TB_VERTICAL         = 1 << 5
+""" The `AuiToolBar` is vertical. """
+AUI_TB_HORZ_LAYOUT      = 1 << 6
+""" Shows the text and the icons alongside, not vertically stacked.
+This style must be used with ``AUI_TB_TEXT``. """
+AUI_TB_PLAIN_BACKGROUND = 1 << 7
+""" Don't draw a gradient background on the toolbar. """
+AUI_TB_CLOCKWISE        = 1 << 8
+AUI_TB_COUNTERCLOCKWISE = 1 << 9
+
+AUI_TB_HORZ_TEXT        = AUI_TB_HORZ_LAYOUT | AUI_TB_TEXT
+""" Combination of ``AUI_TB_HORZ_LAYOUT`` and ``AUI_TB_TEXT``. """
+AUI_TB_VERT_TEXT        = AUI_TB_VERTICAL | AUI_TB_CLOCKWISE | AUI_TB_TEXT
+
+AUI_TB_DEFAULT_STYLE    = 0
+""" `AuiToolBar` default style. """
+
+# AuiToolBar settings
+AUI_TBART_SEPARATOR_SIZE = 0
+""" Separator size in AuiToolBar. """
+AUI_TBART_GRIPPER_SIZE = 1
+""" Gripper size in AuiToolBar. """
+AUI_TBART_OVERFLOW_SIZE = 2
+""" Overflow button size in AuiToolBar. """
+
+# AuiToolBar text orientation
+AUI_TBTOOL_TEXT_LEFT = 0     # unused/unimplemented
+""" Text in AuiToolBar items is aligned left. """
+AUI_TBTOOL_TEXT_RIGHT = 1
+""" Text in AuiToolBar items is aligned right. """
+AUI_TBTOOL_TEXT_TOP = 2      # unused/unimplemented
+""" Text in AuiToolBar items is aligned top. """
+AUI_TBTOOL_TEXT_BOTTOM = 3
+""" Text in AuiToolBar items is aligned bottom. """
+
+# AuiToolBar tool orientation
+AUI_TBTOOL_HORIZONTAL = 0             # standard
+AUI_TBTOOL_VERT_CLOCKWISE = 1         # rotation of 90 on the right
+AUI_TBTOOL_VERT_COUNTERCLOCKWISE = 2  # rotation of 90 on the left
+
+
+# --------------------- #
+# - AuiMDI* Constants - #
+# --------------------- #
+
+wxWINDOWCLOSE = 4001
+""" Identifier for the AuiMDI "close window" menu. """
+wxWINDOWCLOSEALL = 4002
+""" Identifier for the AuiMDI "close all windows" menu. """
+wxWINDOWNEXT = 4003
+""" Identifier for the AuiMDI "next window" menu. """
+wxWINDOWPREV = 4004
+""" Identifier for the AuiMDI "previous window" menu. """
+
+# ----------------------------- #
+# - AuiDockingGuide Constants - #
+# ----------------------------- #
+
+colourTargetBorder = wx.Colour(180, 180, 180)
+colourTargetShade = wx.Colour(206, 206, 206)
+colourTargetBackground = wx.Colour(224, 224, 224)
+colourIconBorder = wx.Colour(82, 65, 156)
+colourIconBackground = wx.Colour(255, 255, 255)
+colourIconDockingPart1 = wx.Colour(215, 228, 243)
+colourIconDockingPart2 = wx.Colour(180, 201, 225)
+colourIconShadow = wx.Colour(198, 198, 198)
+colourIconArrow = wx.Colour(77, 79, 170)
+colourHintBackground = wx.Colour(0, 64, 255)
+guideSizeX, guideSizeY = 29, 32
+aeroguideSizeX, aeroguideSizeY = 31, 32
+whidbeySizeX, whidbeySizeY = 43, 30
+
+# ------------------------------- #
+# - AuiSwitcherDialog Constants - #
+# ------------------------------- #
+
+SWITCHER_TEXT_MARGIN_X = 4
+SWITCHER_TEXT_MARGIN_Y = 1
diff --git a/aui/aui_switcherdialog.py b/aui/aui_switcherdialog.py
new file mode 100644 (file)
index 0000000..552cb84
--- /dev/null
@@ -0,0 +1,1216 @@
+"""
+Description
+===========
+
+The idea of `SwitcherDialog` is to make it easier to implement keyboard
+navigation in AUI and other applications that have multiple panes and
+tabs.
+
+A key combination with a modifier (such as ``Ctrl`` + ``Tab``) shows the
+dialog, and the user holds down the modifier whilst navigating with
+``Tab`` and arrow keys before releasing the modifier to dismiss the dialog
+and activate the selected pane.
+
+The switcher dialog is a multi-column menu with no scrolling, implemented
+by the `MultiColumnListCtrl` class. You can have headings for your items
+for logical grouping, and you can force a column break if you need to.
+
+The modifier used for invoking and dismissing the dialog can be customised,
+as can the colours, number of rows, and the key used for cycling through
+the items. So you can use different keys on different platforms if
+required (especially since ``Ctrl`` + ``Tab`` is reserved on some platforms).
+
+Items are shown as names and optional 16x16 images.
+
+
+Base Functionalities
+====================
+
+To use the dialog, you set up the items in a `SwitcherItems` object,
+before passing this to the `SwitcherDialog` instance.
+
+Call L{SwitcherItems.AddItem} and optionally L{SwitcherItems.AddGroup} to add items and headings. These
+functions take a label (to be displayed to the user), an identifying name,
+an integer id, and a bitmap. The name and id are purely for application-defined
+identification. You may also set a description to be displayed when each
+item is selected; and you can set a window pointer for convenience when
+activating the desired window after the dialog returns.
+
+Have created the dialog, you call `ShowModal()`, and if the return value is
+``wx.ID_OK``, retrieve the selection from the dialog and activate the pane.
+
+The sample code below shows a generic method of finding panes and notebook
+tabs within the current L{AuiManager}, and using the pane name or notebook
+tab position to display the pane.
+
+The only other code to add is a menu item with the desired accelerator,
+whose modifier matches the one you pass to L{SwitcherDialog.SetModifierKey} 
+(the default being ``wx.WXK_CONTROL``).
+
+
+Usage
+=====
+
+Menu item::
+
+    if wx.Platform == "__WXMAC__":
+        switcherAccel = "Alt+Tab"
+    elif wx.Platform == "__WXGTK__":
+        switcherAccel = "Ctrl+/"
+    else:
+        switcherAccel = "Ctrl+Tab"
+
+    view_menu.Append(ID_SwitchPane, _("S&witch Window...") + "\t" + switcherAccel)
+
+
+Event handler::
+
+    def OnSwitchPane(self, event):
+
+        items = SwitcherItems()
+        items.SetRowCount(12)
+
+        # Add the main windows and toolbars, in two separate columns
+        # We'll use the item 'id' to store the notebook selection, or -1 if not a page
+
+        for k in xrange(2):
+            if k == 0:
+                items.AddGroup(_("Main Windows"), "mainwindows")
+            else:
+                items.AddGroup(_("Toolbars"), "toolbars").BreakColumn()
+
+            for pane in self._mgr.GetAllPanes():
+                name = pane.name
+                caption = pane.caption
+
+                toolbar = isinstance(info.window, wx.ToolBar) or isinstance(info.window, aui.AuiToolBar)
+                if caption and (toolBar  and k == 1) or (not toolBar and k == 0):
+                    items.AddItem(caption, name, -1).SetWindow(pane.window)
+
+        # Now add the wxAuiNotebook pages
+
+        items.AddGroup(_("Notebook Pages"), "pages").BreakColumn()
+
+        for pane in self._mgr.GetAllPanes():
+            nb = pane.window
+            if isinstance(nb, aui.AuiNotebook):
+                for j in xrange(nb.GetPageCount()):
+
+                    name = nb.GetPageText(j)
+                    win = nb.GetPage(j)
+
+                    items.AddItem(name, name, j, nb.GetPageBitmap(j)).SetWindow(win)
+
+        # Select the focused window
+
+        idx = items.GetIndexForFocus()
+        if idx != wx.NOT_FOUND:
+            items.SetSelection(idx)
+
+        if wx.Platform == "__WXMAC__":
+            items.SetBackgroundColour(wx.WHITE)
+        
+        # Show the switcher dialog
+
+        dlg = SwitcherDialog(items, wx.GetApp().GetTopWindow())
+
+        # In GTK+ we can't use Ctrl+Tab; we use Ctrl+/ instead and tell the switcher
+        # to treat / in the same was as tab (i.e. cycle through the names)
+
+        if wx.Platform == "__WXGTK__":
+            dlg.SetExtraNavigationKey(wxT('/'))
+
+        if wx.Platform == "__WXMAC__":
+            dlg.SetBackgroundColour(wx.WHITE)
+            dlg.SetModifierKey(wx.WXK_ALT)
+
+        ans = dlg.ShowModal()
+
+        if ans == wx.ID_OK and dlg.GetSelection() != -1:
+            item = items.GetItem(dlg.GetSelection())
+
+            if item.GetId() == -1:
+                info = self._mgr.GetPane(item.GetName())
+                info.Show()
+                self._mgr.Update()
+                info.window.SetFocus()
+
+            else:
+                nb = item.GetWindow().GetParent()
+                win = item.GetWindow();
+                if isinstance(nb, aui.AuiNotebook):
+                    nb.SetSelection(item.GetId())
+                    win.SetFocus()
+
+
+"""
+
+import wx
+
+import auibook
+from aui_utilities import FindFocusDescendant
+from aui_constants import SWITCHER_TEXT_MARGIN_X, SWITCHER_TEXT_MARGIN_Y
+
+
+# Define a translation function
+_ = wx.GetTranslation
+
+    
+class SwitcherItem(object):
+    """ An object containing information about one item. """
+    
+    def __init__(self, item=None):
+        """ Default class constructor. """
+
+        self._id = 0
+        self._isGroup = False
+        self._breakColumn = False
+        self._rowPos = 0
+        self._colPos = 0
+        self._window = None
+        self._description = ""
+
+        self._textColour = wx.NullColour
+        self._bitmap = wx.NullBitmap
+        self._font = wx.NullFont
+        
+        if item:
+            self.Copy(item)
+
+
+    def Copy(self, item):
+        """
+        Copy operator between 2 L{SwitcherItem} instances.
+
+        :param `item`: another instance of L{SwitcherItem}.
+        """
+
+        self._id = item._id
+        self._name = item._name
+        self._title = item._title
+        self._isGroup = item._isGroup
+        self._breakColumn = item._breakColumn
+        self._rect = item._rect
+        self._font = item._font
+        self._textColour = item._textColour
+        self._bitmap = item._bitmap
+        self._description = item._description
+        self._rowPos = item._rowPos
+        self._colPos = item._colPos
+        self._window = item._window
+
+
+    def SetTitle(self, title):
+
+        self._title = title
+        return self
+    
+
+    def GetTitle(self):
+        
+        return self._title
+
+
+    def SetName(self, name):
+
+        self._name = name
+        return self
+
+    
+    def GetName(self):
+
+        return self._name
+
+
+    def SetDescription(self, descr):
+
+        self._description = descr
+        return self
+
+
+    def GetDescription(self):
+
+        return self._description
+    
+
+    def SetId(self, id):
+
+        self._id = id
+        return self
+
+    
+    def GetId(self):
+
+        return self._id
+
+
+    def SetIsGroup(self, isGroup):
+
+        self._isGroup = isGroup
+        return self
+
+    
+    def GetIsGroup(self):
+
+        return self._isGroup
+    
+
+    def BreakColumn(self, breakCol=True):
+
+        self._breakColumn = breakCol
+        return self
+
+    
+    def GetBreakColumn(self):
+
+        return self._breakColumn
+
+
+    def SetRect(self, rect):
+
+        self._rect = rect
+        return self
+
+    
+    def GetRect(self):
+        
+        return self._rect
+
+
+    def SetTextColour(self, colour):
+
+        self._textColour = colour
+        return self
+
+    
+    def GetTextColour(self):
+
+        return self._textColour
+    
+
+    def SetFont(self, font):
+
+        self._font = font
+        return self
+
+    
+    def GetFont(self):
+
+        return self._font
+    
+
+    def SetBitmap(self, bitmap):
+
+        self._bitmap = bitmap
+        return self
+
+    
+    def GetBitmap(self):
+
+        return self._bitmap
+
+
+    def SetRowPos(self, pos):
+
+        self._rowPos = pos
+        return self
+
+    
+    def GetRowPos(self):
+
+        return self._rowPos
+    
+
+    def SetColPos(self, pos):
+
+        self._colPos = pos
+        return self
+
+    
+    def GetColPos(self):
+
+        return self._colPos
+    
+
+    def SetWindow(self, win):
+
+        self._window = win
+        return self
+    
+
+    def GetWindow(self):
+
+        return self._window
+
+    
+class SwitcherItems(object):
+    """ An object containing switcher items. """
+
+    def __init__(self, items=None):
+        """ Default class constructor. """
+
+        self._selection = -1
+        self._rowCount = 10
+        self._columnCount = 0
+
+        self._backgroundColour = wx.NullColour
+        self._textColour = wx.NullColour
+        self._selectionColour = wx.NullColour
+        self._selectionOutlineColour = wx.NullColour
+        self._itemFont = wx.NullFont
+
+        self._items = []        
+        
+        if wx.Platform == "__WXMSW__":
+            # If on Windows XP/Vista, use more appropriate colours
+            self.SetSelectionOutlineColour(wx.Colour(49, 106, 197))
+            self.SetSelectionColour(wx.Colour(193, 210, 238))
+
+        if items:
+            self.Copy(items)
+            
+
+    def Copy(self, items):
+        """
+        Copy operator between 2 L{SwitcherItems}.
+
+        :param `items`: another instance of L{SwitcherItems}.
+        """
+        
+        self.Clear()
+
+        for item in items._items:
+            self._items.append(item)
+        
+        self._selection = items._selection
+        self._rowCount = items._rowCount
+        self._columnCount = items._columnCount
+
+        self._backgroundColour = items._backgroundColour
+        self._textColour = items._textColour
+        self._selectionColour = items._selectionColour
+        self._selectionOutlineColour = items._selectionOutlineColour
+        self._itemFont = items._itemFont
+
+
+    def AddItem(self, titleOrItem, name=None, id=0, bitmap=wx.NullBitmap):
+
+        if isinstance(titleOrItem, SwitcherItem):
+            self._items.append(titleOrItem)
+            return self._items[-1]
+        
+        item = SwitcherItem()
+        item.SetTitle(titleOrItem)
+        item.SetName(name)
+        item.SetId(id)
+        item.SetBitmap(bitmap)
+
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def AddGroup(self, title, name, id=0, bitmap=wx.NullBitmap):
+
+        item = self.AddItem(title, name, id, bitmap)
+        item.SetIsGroup(True)
+
+        return item
+
+
+    def Clear(self):
+
+        self._items = []
+
+
+    def FindItemByName(self, name):
+
+        for i in xrange(len(self._items)):
+            if self._items[i].GetName() == name:
+                return i
+        
+        return wx.NOT_FOUND
+
+
+    def FindItemById(self, id):
+
+        for i in xrange(len(self._items)):
+            if self._items[i].GetId() == id:
+                return i
+        
+        return wx.NOT_FOUND
+
+
+    def SetSelection(self, sel):
+
+        self._selection = sel
+
+
+    def SetSelectionByName(self, name):
+
+        idx = self.FindItemByName(name)
+        if idx != wx.NOT_FOUND:
+            self.SetSelection(idx)
+
+
+    def GetSelection(self):
+
+        return self._selection
+    
+
+    def GetItem(self, i):
+
+        return self._items[i]
+
+
+    def GetItemCount(self):
+
+        return len(self._items)
+    
+
+    def SetRowCount(self, rows):
+
+        self._rowCount = rows
+
+        
+    def GetRowCount(self):
+
+        return self._rowCount
+
+
+    def SetColumnCount(self, cols):
+
+        self._columnCount = cols
+
+        
+    def GetColumnCount(self):
+
+        return self._columnCount
+    
+
+    def SetBackgroundColour(self, colour):
+
+        self._backgroundColour = colour
+
+        
+    def GetBackgroundColour(self):
+
+        return self._backgroundColour
+    
+
+    def SetTextColour(self, colour):
+
+        self._textColour = colour
+
+        
+    def GetTextColour(self):
+
+        return self._textColour
+    
+
+    def SetSelectionColour(self, colour):
+
+        self._selectionColour = colour
+
+        
+    def GetSelectionColour(self):
+
+        return self._selectionColour
+    
+
+    def SetSelectionOutlineColour(self, colour):
+
+        self._selectionOutlineColour = colour
+
+        
+    def GetSelectionOutlineColour(self):
+
+        return self._selectionOutlineColour
+    
+
+    def SetItemFont(self, font):
+
+        self._itemFont = font
+
+        
+    def GetItemFont(self):
+
+        return self._itemFont 
+    
+
+    def PaintItems(self, dc, win):
+
+        backgroundColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
+        standardTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
+        selectionColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+        selectionOutlineColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
+        standardFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        groupFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        groupFont.SetWeight(wx.BOLD)
+
+        if self.GetBackgroundColour().IsOk():
+            backgroundColour = self.GetBackgroundColour()
+
+        if self.GetTextColour().IsOk():
+            standardTextColour = self.GetTextColour()
+
+        if self.GetSelectionColour().IsOk():
+            selectionColour = self.GetSelectionColour()
+
+        if self.GetSelectionOutlineColour().IsOk():
+            selectionOutlineColour = self.GetSelectionOutlineColour()
+
+        if self.GetItemFont().IsOk():
+        
+            standardFont = self.GetItemFont()   
+            groupFont = wx.Font(standardFont.GetPointSize(), standardFont.GetFamily(), standardFont.GetStyle(),
+                                wx.BOLD, standardFont.GetUnderlined(), standardFont.GetFaceName())
+        
+        textMarginX = SWITCHER_TEXT_MARGIN_X
+
+        dc.SetLogicalFunction(wx.COPY)
+        dc.SetBrush(wx.Brush(backgroundColour))
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        dc.DrawRectangleRect(win.GetClientRect())
+        dc.SetBackgroundMode(wx.TRANSPARENT)
+
+        for i in xrange(len(self._items)):
+            item = self._items[i]
+            if i == self._selection:
+                dc.SetPen(wx.Pen(selectionOutlineColour))
+                dc.SetBrush(wx.Brush(selectionColour))
+                dc.DrawRectangleRect(item.GetRect())
+            
+            clippingRect = wx.Rect(*item.GetRect())
+            clippingRect.Deflate(1, 1)
+
+            dc.SetClippingRect(clippingRect)
+
+            if item.GetTextColour().IsOk():
+                dc.SetTextForeground(item.GetTextColour())
+            else:
+                dc.SetTextForeground(standardTextColour)
+            
+            if item.GetFont().IsOk():
+                dc.SetFont(item.GetFont())
+            else:
+                if item.GetIsGroup():
+                    dc.SetFont(groupFont)
+                else:
+                    dc.SetFont(standardFont)
+            
+            w, h = dc.GetTextExtent(item.GetTitle())
+            x = item.GetRect().x
+
+            x += textMarginX
+
+            if not item.GetIsGroup():
+                if item.GetBitmap().IsOk() and item.GetBitmap().GetWidth() <= 16 \
+                   and item.GetBitmap().GetHeight() <= 16:
+                    x -= textMarginX
+                    dc.DrawBitmap(item.GetBitmap(), x, item.GetRect().y + \
+                                  (item.GetRect().height - item.GetBitmap().GetHeight())/2,
+                                  True)
+                    x += 16 + textMarginX
+                #x += textMarginX
+            
+            y = item.GetRect().y + (item.GetRect().height - h)/2
+            dc.DrawText(item.GetTitle(), x, y)
+            dc.DestroyClippingRegion()
+    
+
+    def CalculateItemSize(self, dc):
+
+        # Start off allowing for an icon
+        sz = wx.Size(150, 16)
+        standardFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        groupFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+        groupFont.SetWeight(wx.BOLD)
+
+        textMarginX = SWITCHER_TEXT_MARGIN_X
+        textMarginY = SWITCHER_TEXT_MARGIN_Y
+        maxWidth = 300
+        maxHeight = 40
+
+        if self.GetItemFont().IsOk():
+            standardFont = self.GetItemFont()   
+
+        for item in self._items:
+            if item.GetFont().IsOk():
+                dc.SetFont(item.GetFont())
+            else:
+                if item.GetIsGroup():
+                    dc.SetFont(groupFont)
+                else:
+                    dc.SetFont(standardFont)
+
+            w, h = dc.GetTextExtent(item.GetTitle())
+            w += 16 + 2*textMarginX
+
+            if w > sz.x:
+                sz.x = min(w, maxWidth)
+            if h > sz.y:
+                sz.y = min(h, maxHeight)
+        
+        if sz == wx.Size(16, 16):
+            sz = wx.Size(100, 25)
+        else:
+            sz.x += textMarginX*2
+            sz.y += textMarginY*2
+        
+        return sz
+
+
+    def GetIndexForFocus(self):
+
+        for i, item in enumerate(self._items):        
+            if item.GetWindow():
+            
+                if FindFocusDescendant(item.GetWindow()):
+                    return i
+            
+        return wx.NOT_FOUND
+
+
+class MultiColumnListCtrl(wx.PyControl):
+    """ A control for displaying several columns (not scrollable). """
+
+    def __init__(self, parent, aui_manager, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=0, validator=wx.DefaultValidator, name="MultiColumnListCtrl"):
+
+        wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
+
+        self._overallSize = wx.Size(200, 100)
+        self._modifierKey = wx.WXK_CONTROL
+        self._extraNavigationKey = 0
+        self._aui_manager = aui_manager
+        
+        self.SetInitialSize(size)
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
+        self.Bind(wx.EVT_KEY_UP, self.OnKey)
+
+
+    def __del__(self):
+
+        self._aui_manager.HideHint()
+
+        
+    def DoGetBestSize(self):
+
+        return self._overallSize
+
+
+    def OnEraseBackground(self, event):
+        
+        pass
+
+
+    def OnPaint(self, event):
+
+        dc = wx.AutoBufferedPaintDC(self)
+        rect = self.GetClientRect()
+
+        if self._items.GetColumnCount() == 0:
+            self.CalculateLayout(dc)
+
+        if self._items.GetColumnCount() == 0:
+            return
+
+        self._items.PaintItems(dc, self)
+
+
+    def OnMouseEvent(self, event):
+
+        if event.LeftDown():
+            self.SetFocus()
+    
+
+    def OnChar(self, event):
+
+        event.Skip()        
+
+
+    def OnKey(self, event):
+
+        if event.GetEventType() == wx.wxEVT_KEY_UP:
+            if event.GetKeyCode() == self.GetModifierKey():
+                topLevel = wx.GetTopLevelParent(self)
+                closeEvent = wx.CloseEvent(wx.wxEVT_CLOSE_WINDOW, topLevel.GetId())
+                closeEvent.SetEventObject(topLevel)
+                closeEvent.SetCanVeto(False)
+                
+                topLevel.GetEventHandler().ProcessEvent(closeEvent)
+                return
+                
+            event.Skip()
+            return
+
+        keyCode = event.GetKeyCode()
+        
+        if keyCode in [wx.WXK_ESCAPE, wx.WXK_RETURN]:
+            if keyCode == wx.WXK_ESCAPE:
+                self._items.SetSelection(-1)
+
+            topLevel = wx.GetTopLevelParent(self)
+            closeEvent = wx.CloseEvent(wx.wxEVT_CLOSE_WINDOW, topLevel.GetId())
+            closeEvent.SetEventObject(topLevel)
+            closeEvent.SetCanVeto(False)
+            
+            topLevel.GetEventHandler().ProcessEvent(closeEvent)
+            return
+        
+        elif keyCode in [wx.WXK_TAB, self.GetExtraNavigationKey()]:
+            if event.ShiftDown():
+            
+                self._items.SetSelection(self._items.GetSelection() - 1)
+                if self._items.GetSelection() < 0:
+                    self._items.SetSelection(self._items.GetItemCount() - 1)
+
+                self.AdvanceToNextSelectableItem(-1)
+            
+            else:
+            
+                self._items.SetSelection(self._items.GetSelection() + 1)
+                if self._items.GetSelection() >= self._items.GetItemCount():
+                    self._items.SetSelection(0)
+
+                self.AdvanceToNextSelectableItem(1)
+            
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        elif keyCode in [wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN]:
+            self._items.SetSelection(self._items.GetSelection() + 1)
+            if self._items.GetSelection() >= self._items.GetItemCount():
+                self._items.SetSelection(0)
+            
+            self.AdvanceToNextSelectableItem(1)
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        elif keyCode in [wx.WXK_UP, wx.WXK_NUMPAD_UP]:
+            self._items.SetSelection(self._items.GetSelection() - 1)
+            if self._items.GetSelection() < 0:
+                self._items.SetSelection(self._items.GetItemCount() - 1)
+            
+            self.AdvanceToNextSelectableItem(-1)
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        elif keyCode in [wx.WXK_HOME, wx.WXK_NUMPAD_HOME]:
+            self._items.SetSelection(0)
+            self.AdvanceToNextSelectableItem(1)
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        elif keyCode in [wx.WXK_END, wx.WXK_NUMPAD_END]:
+            self._items.SetSelection(self._items.GetItemCount() - 1)
+            self.AdvanceToNextSelectableItem(-1)
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        elif keyCode in [wx.WXK_LEFT, wx.WXK_NUMPAD_LEFT]:
+            item = self._items.GetItem(self._items.GetSelection())
+
+            row = item.GetRowPos()
+            newCol = item.GetColPos() - 1
+            if newCol < 0:
+                newCol = self._items.GetColumnCount() - 1
+
+            # Find the first item from the end whose row matches and whose column is equal or lower
+            for i in xrange(self._items.GetItemCount()-1, -1, -1):
+                item2 = self._items.GetItem(i)
+                if item2.GetColPos() == newCol and item2.GetRowPos() <= row:
+                    self._items.SetSelection(i)
+                    break
+
+            self.AdvanceToNextSelectableItem(-1)
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        elif keyCode in [wx.WXK_RIGHT, wx.WXK_NUMPAD_RIGHT]:
+            item = self._items.GetItem(self._items.GetSelection())
+
+            row = item.GetRowPos()
+            newCol = item.GetColPos() + 1
+            if newCol >= self._items.GetColumnCount():
+                newCol = 0
+
+            # Find the first item from the end whose row matches and whose column is equal or lower
+            for i in xrange(self._items.GetItemCount()-1, -1, -1):
+                item2 = self._items.GetItem(i)
+                if item2.GetColPos() == newCol and item2.GetRowPos() <= row:
+                    self._items.SetSelection(i)
+                    break
+
+            self.AdvanceToNextSelectableItem(1)
+            self.GenerateSelectionEvent()
+            self.Refresh()
+        
+        else:
+            event.Skip()
+
+
+    def AdvanceToNextSelectableItem(self, direction):
+
+        if self._items.GetItemCount() < 2:
+            return
+
+        if self._items.GetSelection() == -1:
+            self._items.SetSelection(0)
+
+        oldSel = self._items.GetSelection()
+
+        while 1:
+        
+            if self._items.GetItem(self._items.GetSelection()).GetIsGroup():
+            
+                self._items.SetSelection(self._items.GetSelection() + direction)
+                if self._items.GetSelection() == -1:
+                    self._items.SetSelection(self._items.GetItemCount()-1)
+                elif self._items.GetSelection() == self._items.GetItemCount():
+                    self._items.SetSelection(0)
+                if self._items.GetSelection() == oldSel:
+                    break
+            
+            else:
+                break
+
+        self.SetTransparency()
+        selection = self._items.GetItem(self._items.GetSelection()).GetWindow()
+        pane = self._aui_manager.GetPane(selection)
+
+        if not pane.IsOk():
+            if isinstance(selection.GetParent(), auibook.AuiNotebook):
+                self.SetTransparency(selection)
+                self._aui_manager.ShowHint(selection.GetScreenRect())
+                wx.CallAfter(self.SetFocus)
+                self.SetFocus()
+                return
+            else:
+                self._aui_manager.HideHint()
+                return
+        if not pane.IsShown():
+            self._aui_manager.HideHint()
+            return
+
+        self.SetTransparency(selection)
+        self._aui_manager.ShowHint(selection.GetScreenRect())
+        # NOTE: this is odd but it is the only way for the focus to
+        #       work correctly on wxMac...
+        wx.CallAfter(self.SetFocus)
+        self.SetFocus()        
+    
+
+    def SetTransparency(self, selection=None):
+
+        if not self.GetParent().CanSetTransparent():
+            return
+        
+        if selection is not None:
+            intersects = False
+            if selection.GetScreenRect().Intersects(self.GetParent().GetScreenRect()):
+                intersects = True
+                self.GetParent().SetTransparent(200)
+                return
+
+        self.GetParent().SetTransparent(255)
+
+
+    def GenerateSelectionEvent(self):
+
+        event = wx.CommandEvent(wx.wxEVT_COMMAND_LISTBOX_SELECTED, self.GetId())
+        event.SetEventObject(self)
+        event.SetInt(self._items.GetSelection())
+        self.GetEventHandler().ProcessEvent(event)
+
+
+    def CalculateLayout(self, dc=None):
+
+        if dc is None:
+            dc = wx.ClientDC(self)
+
+        if self._items.GetSelection() == -1:
+            self._items.SetSelection(0)
+
+        columnCount = 1
+
+        # Spacing between edge of window or between columns
+        xMargin = 4
+        yMargin = 4
+
+        # Inter-row spacing
+        rowSpacing = 2
+
+        itemSize = self._items.CalculateItemSize(dc)
+        self._overallSize = wx.Size(350, 200)
+
+        currentRow = 0
+        x = xMargin
+        y = yMargin
+
+        breaking = False
+        i = 0
+        
+        while 1:
+        
+            oldOverallSize = self._overallSize
+            item = self._items.GetItem(i)
+            
+            item.SetRect(wx.Rect(x, y, itemSize.x, itemSize.y))
+            item.SetColPos(columnCount-1)
+            item.SetRowPos(currentRow)
+
+            if item.GetRect().GetBottom() > self._overallSize.y:
+                self._overallSize.y = item.GetRect().GetBottom() + yMargin
+
+            if item.GetRect().GetRight() > self._overallSize.x:
+                self._overallSize.x = item.GetRect().GetRight() + xMargin
+
+            currentRow += 1
+
+            y += rowSpacing + itemSize.y
+            stopBreaking = breaking
+
+            if currentRow > self._items.GetRowCount() or (item.GetBreakColumn() and not breaking and currentRow != 1):
+                currentRow = 0
+                columnCount += 1
+                x += xMargin + itemSize.x
+                y = yMargin
+
+                # Make sure we don't orphan a group
+                if item.GetIsGroup() or (item.GetBreakColumn() and not breaking):
+                    self._overallSize = oldOverallSize
+
+                    if item.GetBreakColumn():
+                        breaking = True
+
+                    # Repeat the last item, in the next column
+                    i -= 1
+                
+            if stopBreaking:
+                breaking = False
+
+            i += 1
+            
+            if i >= self._items.GetItemCount():
+                break
+            
+        self._items.SetColumnCount(columnCount)
+        self.InvalidateBestSize()
+
+
+    def SetItems(self, items):
+        
+        self._items = items
+
+        
+    def GetItems(self):
+
+        return self._items
+
+    def SetExtraNavigationKey(self, keyCode):
+        """
+        Set an extra key that can be used to cycle through items,
+        in case not using the ``Ctrl`` + ``Tab`` combination.
+        """
+
+        self._extraNavigationKey = keyCode
+
+
+    def GetExtraNavigationKey(self):
+
+        return self._extraNavigationKey
+
+
+    def SetModifierKey(self, modifierKey):
+        """
+        Set the modifier used to invoke the dialog, and therefore to test for
+        release.
+        """
+
+        self._modifierKey = modifierKey
+
+        
+    def GetModifierKey(self):
+
+        return self._modifierKey
+
+    
+
+class SwitcherDialog(wx.Dialog):
+    """
+    SwitcherDialog shows a L{MultiColumnListCtrl} with a list of panes
+    and tabs for the user to choose. ``Ctrl`` + ``Tab`` cycles through them.
+    """
+
+    def __init__(self, items, parent, aui_manager, id=wx.ID_ANY, title=_("Pane Switcher"), pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=wx.STAY_ON_TOP|wx.DIALOG_NO_PARENT|wx.BORDER_SIMPLE):
+        """ Default class constructor. """
+        
+        self._switcherBorderStyle = (style & wx.BORDER_MASK)
+        if self._switcherBorderStyle == wx.BORDER_NONE:
+            self._switcherBorderStyle = wx.BORDER_SIMPLE
+
+        style &= wx.BORDER_MASK
+        style |= wx.BORDER_NONE
+
+        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+
+        self._listCtrl = MultiColumnListCtrl(self, aui_manager,
+                                             style=wx.WANTS_CHARS|wx.NO_BORDER)
+        self._listCtrl.SetItems(items)
+        self._listCtrl.CalculateLayout()
+
+        self._descriptionCtrl = wx.html.HtmlWindow(self, size=(-1, 100), style=wx.BORDER_NONE)
+        self._descriptionCtrl.SetBackgroundColour(self.GetBackgroundColour())
+
+        if wx.Platform == "__WXGTK__":
+            fontSize = 11
+            self._descriptionCtrl.SetStandardFonts(fontSize)
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        self.SetSizer(sizer)
+        sizer.Add(self._listCtrl, 1, wx.ALL|wx.EXPAND, 10)
+        sizer.Add(self._descriptionCtrl, 0, wx.ALL|wx.EXPAND, 10)
+        sizer.SetSizeHints(self)
+
+        self._listCtrl.SetFocus()
+
+        self.Centre(wx.BOTH)
+
+        if self._listCtrl.GetItems().GetSelection() == -1:
+            self._listCtrl.GetItems().SetSelection(0)
+
+        self._listCtrl.AdvanceToNextSelectableItem(1)
+
+        self.ShowDescription(self._listCtrl.GetItems().GetSelection())
+
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
+        self.Bind(wx.EVT_LISTBOX, self.OnSelectItem)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+        # Attributes
+        self._closing = False
+        if wx.Platform == "__WXMSW__":
+            self._borderColour = wx.Colour(49, 106, 197)
+        else:
+            self._borderColour = wx.BLACK
+
+        self._aui_manager = aui_manager
+        
+
+    def OnCloseWindow(self, event):
+
+        if self._closing:
+            return
+
+        if self.IsModal():
+            self._closing = True
+
+            if self.GetSelection() == -1:
+                self.EndModal(wx.ID_CANCEL)
+            else:
+                self.EndModal(wx.ID_OK)
+    
+        self._aui_manager.HideHint()
+
+
+    def GetSelection(self):
+
+        return self._listCtrl.GetItems().GetSelection()
+
+
+    def OnActivate(self, event):
+
+        if not event.GetActive():
+            if not self._closing:
+                self._closing = True
+                self.EndModal(wx.ID_CANCEL)
+            
+
+    def OnPaint(self, event):
+
+        dc = wx.PaintDC(self)
+
+        if self._switcherBorderStyle == wx.BORDER_SIMPLE:
+        
+            dc.SetPen(wx.Pen(self._borderColour))
+            dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+            rect = self.GetClientRect()
+            dc.DrawRectangleRect(rect)
+
+            # Draw border around the HTML control
+            rect = wx.Rect(*self._descriptionCtrl.GetRect())
+            rect.Inflate(1, 1)
+            dc.DrawRectangleRect(rect)
+
+    
+    def OnSelectItem(self, event):
+
+        self.ShowDescription(event.GetSelection())
+
+
+# Convert a colour to a 6-digit hex string
+    def ColourToHexString(self, col):
+
+        hx = '%02x%02x%02x' % tuple([int(c) for c in col])
+        return hx
+
+
+    def ShowDescription(self, i):
+
+        item = self._listCtrl.GetItems().GetItem(i)
+        colour = self._listCtrl.GetItems().GetBackgroundColour()
+        
+        if not colour.IsOk():
+            colour = self.GetBackgroundColour()
+
+        backgroundColourHex = self.ColourToHexString(colour)
+        html = _("<body bgcolor=\"#") + backgroundColourHex + _("\"><b>") + item.GetTitle() + _("</b>")
+
+        if item.GetDescription():
+            html += _("<p>")
+            html += item.GetDescription()
+        
+        html += _("</body>")
+        self._descriptionCtrl.SetPage(html)
+
+
+    def SetExtraNavigationKey(self, keyCode):
+
+        self._extraNavigationKey = keyCode
+        if self._listCtrl:
+            self._listCtrl.SetExtraNavigationKey(keyCode)
+
+
+    def GetExtraNavigationKey(self):
+
+        return self._extraNavigationKey
+    
+        
+    def SetModifierKey(self, modifierKey):
+
+        self._modifierKey = modifierKey
+        if self._listCtrl:
+            self._listCtrl.SetModifierKey(modifierKey)
+
+
+    def GetModifierKey(self):
+
+        return self._modifierKey        
+
+
+    def SetBorderColour(self, colour):
+
+        self._borderColour = colour
+
+        
\ No newline at end of file
diff --git a/aui/aui_utilities.py b/aui/aui_utilities.py
new file mode 100644 (file)
index 0000000..d6c701a
--- /dev/null
@@ -0,0 +1,678 @@
+"""
+This module contains some common functions used by wxPython-AUI to
+manipulate colours, bitmaps, text, gradient shadings and custom
+dragging images for AuiNotebook tabs.
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+
+from aui_constants import *
+
+
+if wx.Platform == "__WXMAC__":
+    import Carbon.Appearance
+    
+    
+def BlendColour(fg, bg, alpha):
+    """
+    Blends the two colour component `fg` and `bg` into one colour component, adding
+    an optional alpha channel.
+
+    :param `fg`: the first colour component;
+    :param `bg`: the second colour component;
+    :param `alpha`: an optional transparency value.
+    """
+    
+    result = bg + (alpha*(fg - bg))
+    
+    if result < 0.0:
+        result = 0.0
+    if result > 255:
+        result = 255
+        
+    return result
+
+
+def StepColour(c, ialpha):
+    """
+    Darken/lighten the input colour `c`.
+
+    :param `c`: a colour to darken/lighten;
+    :param `ialpha`: a transparency value.
+    """
+    
+    if ialpha == 100:
+        return c
+        
+    r, g, b = c.Red(), c.Green(), c.Blue()
+
+    # ialpha is 0..200 where 0 is completely black
+    # and 200 is completely white and 100 is the same
+    # convert that to normal alpha 0.0 - 1.0
+    ialpha = min(ialpha, 200)
+    ialpha = max(ialpha, 0)
+    alpha = (ialpha - 100.0)/100.0
+
+    if ialpha > 100:
+    
+        # blend with white
+        bg = 255
+        alpha = 1.0 - alpha  # 0 = transparent fg 1 = opaque fg
+    
+    else:
+    
+        # blend with black
+        bg = 0
+        alpha = 1.0 + alpha  # 0 = transparent fg 1 = opaque fg
+    
+    r = BlendColour(r, bg, alpha)
+    g = BlendColour(g, bg, alpha)
+    b = BlendColour(b, bg, alpha)
+
+    return wx.Colour(r, g, b)
+
+
+def LightContrastColour(c):
+    """
+    Creates a new, lighter colour based on the input colour `c`.
+
+    :param `c`: the input colour to analyze.
+    """
+
+    amount = 120
+
+    # if the colour is especially dark, then
+    # make the contrast even lighter
+    if c.Red() < 128 and c.Green() < 128 and c.Blue() < 128:
+        amount = 160
+
+    return StepColour(c, amount)
+
+
+def ChopText(dc, text, max_size):
+    """
+    Chops the input `text` if its size does not fit in `max_size`, by cutting the
+    text and adding ellipsis at the end.
+
+    :param `dc`: a `wx.DC` device context;
+    :param `text`: the text to chop;
+    :param `max_size`: the maximum size in which the text should fit.
+    """
+    
+    # first check if the text fits with no problems
+    x, y, dummy = dc.GetMultiLineTextExtent(text)
+    
+    if x <= max_size:
+        return text
+
+    textLen = len(text)
+    last_good_length = 0
+    
+    for i in xrange(textLen, -1, -1):
+        s = text[0:i]
+        s += "..."
+
+        x, y = dc.GetTextExtent(s)
+        last_good_length = i
+        
+        if x < max_size:
+            break
+
+    ret = text[0:last_good_length] + "..."    
+    return ret
+
+
+def BitmapFromBits(bits, w, h, colour):
+    """
+    BitmapFromBits() is a utility function that creates a
+    masked bitmap from raw bits (XBM format).
+
+    :param `bits`: a string containing the raw bits of the bitmap;
+    :param `w`: the bitmap width;
+    :param `h`: the bitmap height;
+    :param `colour`: the colour which will replace all white pixels in the
+     raw bitmap.
+    """
+
+    img = wx.BitmapFromBits(bits, w, h).ConvertToImage()
+    img.Replace(0, 0, 0, 123, 123, 123)
+    img.Replace(255, 255, 255, colour.Red(), colour.Green(), colour.Blue())
+    img.SetMaskColour(123, 123, 123)
+    return wx.BitmapFromImage(img)
+
+
+def IndentPressedBitmap(rect, button_state):
+    """
+    Indents the input rectangle `rect` based on the value of `button_state`.
+
+    :param `rect`: an instance of wx.Rect;
+    :param `button_state`: an L{AuiNotebook} button state.
+    """
+
+    if button_state == AUI_BUTTON_STATE_PRESSED:
+        rect.x += 1
+        rect.y += 1
+
+    return rect
+
+
+def GetBaseColour():
+    """
+    Returns the face shading colour on push buttons/backgrounds, mimicking as closely
+    as possible the platform UI colours.
+    """
+
+    if wx.Platform == "__WXMAC__":
+
+        if hasattr(wx, 'MacThemeColour'):
+            base_colour = wx.MacThemeColour(Carbon.Appearance.kThemeBrushToolbarBackground)
+        else:
+            brush = wx.Brush(wx.BLACK)
+            brush.MacSetTheme(Carbon.Appearance.kThemeBrushToolbarBackground)
+            base_colour = brush.GetColour()
+
+    else:
+        
+        base_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
+
+    # the base_colour is too pale to use as our base colour,
+    # so darken it a bit
+    if ((255-base_colour.Red()) +
+        (255-base_colour.Green()) +
+        (255-base_colour.Blue()) < 60):
+    
+        base_colour = StepColour(base_colour, 92)
+    
+    return base_colour
+
+
+def MakeDisabledBitmap(bitmap):
+    """
+    Convert the given image (in place) to a grayed-out version,
+    appropriate for a 'disabled' appearance.
+
+    :param `bitmap`: the bitmap to gray-out.
+    """
+
+    anImage = bitmap.ConvertToImage()    
+    factor = 0.7        # 0 < f < 1.  Higher Is Grayer
+    
+    if anImage.HasMask():
+        maskColour = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue())
+    else:
+        maskColour = None
+        
+    data = map(ord, list(anImage.GetData()))
+
+    for i in range(0, len(data), 3):
+        
+        pixel = (data[i], data[i+1], data[i+2])
+        pixel = MakeGray(pixel, factor, maskColour)
+
+        for x in range(3):
+            data[i+x] = pixel[x]
+
+    anImage.SetData(''.join(map(chr, data)))
+    
+    return anImage.ConvertToBitmap()
+
+
+def MakeGray(rgbTuple, factor, maskColour):
+    """
+    Make a pixel grayed-out. If the pixel matches the `maskColour`, it won't be
+    changed.
+
+    :param `rgbTuple`: a tuple representing a pixel colour;
+    :param `factor`: a graying-out factor;
+    :param `maskColour`: a colour mask.
+    """
+
+    if rgbTuple != maskColour:
+        r, g, b = rgbTuple
+        return map(lambda x: int((230 - x) * factor) + x, (r, g, b))
+    else:
+        return rgbTuple
+
+
+def Clip(a, b, c):
+    """
+    Clips the value in `a` based on the extremes `b` and `c`.
+
+    :param `a`: the value to analyze;
+    :param `b`: a minimum value;
+    :param `c`: a maximum value.
+    """
+
+    return ((a < b and [b]) or [(a > c and [c] or [a])[0]])[0]
+
+
+def LightColour(colour, percent):
+    """
+    Brighten input `colour` by `percent`.
+
+    :param `colour`: the colour to be brightened;
+    :param `percent`: brightening percentage.
+    """
+    
+    end_colour = wx.WHITE
+    
+    rd = end_colour.Red() - colour.Red()
+    gd = end_colour.Green() - colour.Green()
+    bd = end_colour.Blue() - colour.Blue()
+
+    high = 100
+
+    # We take the percent way of the colour from colour -. white
+    i = percent
+    r = colour.Red() + ((i*rd*100)/high)/100
+    g = colour.Green() + ((i*gd*100)/high)/100
+    b = colour.Blue() + ((i*bd*100)/high)/100
+    return wx.Colour(r, g, b)
+
+
+def PaneCreateStippleBitmap():
+    """
+    Creates a stipple bitmap to be used in a `wx.Brush`.
+    This is used to draw sash resize hints.
+    """
+
+    data = [0, 0, 0, 192, 192, 192, 192, 192, 192, 0, 0, 0]
+    img = wx.EmptyImage(2, 2)
+    counter = 0
+    
+    for ii in xrange(2):
+        for jj in xrange(2):
+            img.SetRGB(ii, jj, data[counter], data[counter+1], data[counter+2])
+            counter = counter + 3
+    
+    return img.ConvertToBitmap()
+
+
+def DrawMACCloseButton(colour, backColour=None):
+    """
+    Draws the wxMAC tab close button using `wx.GraphicsContext`.
+
+    :param `colour`: the colour to use to draw the circle;
+    :param `backColour`: the optional background colour for the circle.
+    """
+
+    bmp = wx.EmptyBitmapRGBA(16, 16)
+    dc = wx.MemoryDC()
+    dc.SelectObject(bmp)
+
+    gc = wx.GraphicsContext.Create(dc)    
+    gc.SetBrush(wx.Brush(colour))
+    path = gc.CreatePath()
+    path.AddCircle(6.5, 7, 6.5)
+    path.CloseSubpath()
+    gc.FillPath(path)
+    
+    path = gc.CreatePath()
+    if backColour is not None:
+        pen = wx.Pen(backColour, 2)
+    else:
+        pen = wx.Pen("white", 2)
+        
+    pen.SetCap(wx.CAP_BUTT)
+    pen.SetJoin(wx.JOIN_BEVEL)
+    gc.SetPen(pen)
+    path.MoveToPoint(3.5, 4)
+    path.AddLineToPoint(9.5, 10)
+    path.MoveToPoint(3.5, 10)
+    path.AddLineToPoint(9.5, 4)
+    path.CloseSubpath()
+    gc.DrawPath(path)
+
+    dc.SelectObject(wx.NullBitmap)
+    return bmp
+
+
+def DarkenBitmap(bmp, caption_colour, new_colour):
+    """
+    Darkens the input bitmap on wxMAC using the input colour.
+    
+    :param `bmp`: the bitmap to be manipulated;
+    :param `caption_colour`: the colour of the pane caption;
+    :param `new_colour`: the colour used to darken the bitmap.
+    """
+
+    image = bmp.ConvertToImage()
+    red = caption_colour.Red()/float(new_colour.Red())
+    green = caption_colour.Green()/float(new_colour.Green())
+    blue = caption_colour.Blue()/float(new_colour.Blue())
+    image = image.AdjustChannels(red, green, blue)
+    return image.ConvertToBitmap()
+
+    
+def DrawGradientRectangle(dc, rect, start_colour, end_colour, direction, offset=0, length=0):
+    """
+    Draws a gradient-shaded rectangle.
+
+    :param `dc`: a `wx.DC` device context;
+    :param `rect`: the rectangle in which to draw the gradient;
+    :param `start_colour`: the first colour of the gradient;
+    :param `end_colour`: the second colour of the gradient;
+    :param `direction`: the gradient direction (horizontal or vertical).
+    """
+    
+    if direction == AUI_GRADIENT_VERTICAL:
+        dc.GradientFillLinear(rect, start_colour, end_colour, wx.SOUTH)
+    else:
+        dc.GradientFillLinear(rect, start_colour, end_colour, wx.EAST)
+        
+
+def FindFocusDescendant(ancestor):
+    """
+    Find a window with the focus, that is also a descendant of the given window.
+    This is used to determine the window to initially send commands to.
+
+    :param `ancestor`: the window to check for ancestry.    
+    """
+
+    # Process events starting with the window with the focus, if any.
+    focusWin = wx.Window.FindFocus()
+    win = focusWin
+
+    # Check if this is a descendant of this frame.
+    # If not, win will be set to NULL.
+    while win:
+        if win == ancestor:
+            break
+        else:
+            win = win.GetParent()
+
+    if win is None:
+        focusWin = None
+
+    return focusWin
+
+
+def GetLabelSize(dc, label, vertical):
+    """
+    Returns the L{AuiToolBar} item label size.
+
+    :param `label`: the toolbar tool label;
+    :param `vertical`: whether the toolbar tool orientation is vertical or not.
+    """
+
+    text_width = text_height = 0
+
+    # get the text height
+    dummy, text_height = dc.GetTextExtent("ABCDHgj")
+    # get the text width
+    if label.strip():
+        text_width, dummy = dc.GetTextExtent(label)
+
+    if vertical:
+        tmp = text_height
+        text_height = text_width
+        text_width = tmp
+
+    return wx.Size(text_width, text_height)
+
+
+#---------------------------------------------------------------------------
+# TabDragImage implementation
+# This class handles the creation of a custom image when dragging
+# AuiNotebook tabs
+#---------------------------------------------------------------------------
+
+class TabDragImage(wx.DragImage):
+    """
+    This class handles the creation of a custom image in case of drag and
+    drop of a notebook tab.
+    """
+
+    def __init__(self, notebook, page, button_state, tabArt):
+        """
+        Default class constructor.
+        
+        For internal use: do not call it in your code!
+
+        :param `notebook`: an instance of L{AuiNotebook};
+        :param `page`: the dragged L{AuiNotebook} page;
+        :param `button_state`: the state of the close button on the tab;
+        :param `tabArt`: an instance of L{AuiDefaultTabArt} or one of its derivations.
+        """
+
+        self._backgroundColour = wx.NamedColour("pink")        
+        self._bitmap = self.CreateBitmap(notebook, page, button_state, tabArt)
+        wx.DragImage.__init__(self, self._bitmap)
+
+
+    def CreateBitmap(self, notebook, page, button_state, tabArt):
+        """
+        Actually creates the drag and drop bitmap.
+
+        :param `notebook`: an instance of L{AuiNotebook};
+        :param `page`: the dragged L{AuiNotebook} page;
+        :param `button_state`: the state of the close button on the tab;
+        :param `tabArt`: an instance of L{AuiDefaultTabArt} or one of its derivations.
+        """
+
+        control = page.control
+        memory = wx.MemoryDC(wx.EmptyBitmap(1, 1))
+
+        tab_size, x_extent = tabArt.GetTabSize(memory, notebook, page.caption, page.bitmap, page.active,
+                                               button_state, control)
+            
+        tab_width, tab_height = tab_size
+        rect = wx.Rect(0, 0, tab_width, tab_height)
+
+        bitmap = wx.EmptyBitmap(tab_width+1, tab_height+1)
+        memory.SelectObject(bitmap)
+
+        if wx.Platform == "__WXMAC__":
+            memory.SetBackground(wx.TRANSPARENT_BRUSH)
+        else:
+            memory.SetBackground(wx.Brush(self._backgroundColour))
+            
+        memory.SetBackgroundMode(wx.TRANSPARENT)
+        memory.Clear()
+
+        paint_control = wx.Platform != "__WXMAC__"
+        tabArt.DrawTab(memory, notebook, page, rect, button_state, paint_control=paint_control)
+        
+        memory.SetBrush(wx.TRANSPARENT_BRUSH)
+        memory.SetPen(wx.BLACK_PEN)
+        memory.DrawRoundedRectangle(0, 0, tab_width+1, tab_height+1, 2)
+
+        memory.SelectObject(wx.NullBitmap)
+        
+        # Gtk and Windows unfortunatly don't do so well with transparent
+        # drawing so this hack corrects the image to have a transparent
+        # background.
+        if wx.Platform != '__WXMAC__':
+            timg = bitmap.ConvertToImage()
+            if not timg.HasAlpha():
+                timg.InitAlpha()
+            for y in xrange(timg.GetHeight()):
+                for x in xrange(timg.GetWidth()):
+                    pix = wx.Colour(timg.GetRed(x, y),
+                                    timg.GetGreen(x, y),
+                                    timg.GetBlue(x, y))
+                    if pix == self._backgroundColour:
+                        timg.SetAlpha(x, y, 0)
+            bitmap = timg.ConvertToBitmap()
+        return bitmap        
+
+
+def GetDockingImage(direction, useAero, center):
+    """
+    Returns the correct name of the docking bitmap depending on the input parameters.
+
+    :param `useAero`: whether L{AuiManager} is using Aero-style or Whidbey-style docking
+     images or not;
+    :param `center`: whether we are looking for the center diamond-shaped bitmap or not. 
+    """
+
+    suffix = (center and [""] or ["_single"])[0]
+    prefix = ""
+    if useAero == 2:
+        # Whidbey docking guides
+        prefix = "whidbey_"
+    elif useAero == 1:
+        # Aero docking style
+        prefix = "aero_"
+        
+    if direction == wx.TOP:
+        bmp_unfocus = eval("%sup%s"%(prefix, suffix)).GetBitmap()
+        bmp_focus = eval("%sup_focus%s"%(prefix, suffix)).GetBitmap()
+    elif direction == wx.BOTTOM:
+        bmp_unfocus = eval("%sdown%s"%(prefix, suffix)).GetBitmap()
+        bmp_focus = eval("%sdown_focus%s"%(prefix, suffix)).GetBitmap()
+    elif direction == wx.LEFT:
+        bmp_unfocus = eval("%sleft%s"%(prefix, suffix)).GetBitmap()
+        bmp_focus = eval("%sleft_focus%s"%(prefix, suffix)).GetBitmap()
+    elif direction == wx.RIGHT:
+        bmp_unfocus = eval("%sright%s"%(prefix, suffix)).GetBitmap()
+        bmp_focus = eval("%sright_focus%s"%(prefix, suffix)).GetBitmap()
+    else:
+        bmp_unfocus = eval("%stab%s"%(prefix, suffix)).GetBitmap()
+        bmp_focus = eval("%stab_focus%s"%(prefix, suffix)).GetBitmap()
+
+    return bmp_unfocus, bmp_focus
+
+
+def TakeScreenShot(rect):
+    """
+    Takes a screenshot of the screen at given position and size (rect).
+
+    :param `rect`: the screen rectangle for which we want to take a screenshot.
+    """
+
+    # Create a DC for the whole screen area
+    dcScreen = wx.ScreenDC()
+
+    # Create a Bitmap that will later on hold the screenshot image
+    # Note that the Bitmap must have a size big enough to hold the screenshot
+    # -1 means using the current default colour depth
+    bmp = wx.EmptyBitmap(rect.width, rect.height)
+
+    # Create a memory DC that will be used for actually taking the screenshot
+    memDC = wx.MemoryDC()
+
+    # Tell the memory DC to use our Bitmap
+    # all drawing action on the memory DC will go to the Bitmap now
+    memDC.SelectObject(bmp)
+
+    # Blit (in this case copy) the actual screen on the memory DC
+    # and thus the Bitmap
+    memDC.Blit( 0,            # Copy to this X coordinate
+                0,            # Copy to this Y coordinate
+                rect.width,   # Copy this width
+                rect.height,  # Copy this height
+                dcScreen,     # From where do we copy?
+                rect.x,       # What's the X offset in the original DC?
+                rect.y        # What's the Y offset in the original DC?
+                )
+
+    # Select the Bitmap out of the memory DC by selecting a new
+    # uninitialized Bitmap
+    memDC.SelectObject(wx.NullBitmap)
+
+    return bmp
+
+
+def RescaleScreenShot(bmp, thumbnail_size=200):
+    """
+    Rescales a bitmap to be 300 pixels wide (or tall) at maximum.
+
+    :param `bmp`: the bitmap to rescale;
+    :param `thumbnail_size`: the maximum size of every page thumbnail.
+    """
+
+    bmpW, bmpH = bmp.GetWidth(), bmp.GetHeight()
+    img = bmp.ConvertToImage()
+
+    newW, newH = bmpW, bmpH
+    
+    if bmpW > bmpH:
+        if bmpW > thumbnail_size:
+            ratio = bmpW/float(thumbnail_size)
+            newW, newH = int(bmpW/ratio), int(bmpH/ratio)
+            img.Rescale(newW, newH, wx.IMAGE_QUALITY_HIGH)
+    else:
+        if bmpH > thumbnail_size:
+            ratio = bmpH/float(thumbnail_size)
+            newW, newH = int(bmpW/ratio), int(bmpH/ratio)
+            img.Rescale(newW, newH, wx.IMAGE_QUALITY_HIGH)
+
+    newBmp = img.ConvertToBitmap()
+    otherBmp = wx.EmptyBitmap(newW+5, newH+5)    
+
+    memDC = wx.MemoryDC()
+    memDC.SelectObject(otherBmp)
+    memDC.SetBackground(wx.WHITE_BRUSH)
+    memDC.Clear()
+    
+    memDC.SetPen(wx.TRANSPARENT_PEN)
+
+    pos = 0
+    for i in xrange(5, 0, -1):
+        brush = wx.Brush(wx.Colour(50*i, 50*i, 50*i))
+        memDC.SetBrush(brush)
+        memDC.DrawRoundedRectangle(0, 0, newW+5-pos, newH+5-pos, 2)
+        pos += 1
+
+    memDC.DrawBitmap(newBmp, 0, 0, True)
+     
+    # Select the Bitmap out of the memory DC by selecting a new
+    # uninitialized Bitmap
+    memDC.SelectObject(wx.NullBitmap)
+
+    return otherBmp
+
+
+def GetSlidingPoints(rect, size, direction):
+    """
+    Returns the point at which the sliding in and out of a minimized pane begins.
+
+    :param `rect`: the L{AuiToolBar} tool screen rectangle;
+    :param `size`: the pane window size;
+    :param `direction`: the pane docking direction.
+    """
+
+    if direction == AUI_DOCK_LEFT:
+        startX, startY = rect.x + rect.width + 2, rect.y
+    elif direction == AUI_DOCK_TOP:
+        startX, startY = rect.x, rect.y + rect.height + 2
+    elif direction == AUI_DOCK_RIGHT:
+        startX, startY = rect.x - size.x - 2, rect.y
+    elif direction == AUI_DOCK_BOTTOM:
+        startX, startY = rect.x, rect.y - size.y - 2
+    else:
+        raise Exception("How did we get here?")
+
+    caption_height = wx.SystemSettings.GetMetric(wx.SYS_CAPTION_Y)
+    frame_border_x = wx.SystemSettings.GetMetric(wx.SYS_FRAMESIZE_X)
+    frame_border_y = wx.SystemSettings.GetMetric(wx.SYS_FRAMESIZE_Y)
+    
+    stopX = size.x + caption_height + frame_border_x
+    stopY = size.x + frame_border_y
+    
+    return startX, startY, stopX, stopY
+
+
+def CopyAttributes(newArt, oldArt):
+    """
+    Copies pens, brushes, colours and fonts from the old tab art to the new one.
+
+    :param `newArt`: the new instance of L{AuiDefaultTabArt};
+    :param `oldArt`: the old instance of L{AuiDefaultTabArt}.
+    """    
+    
+    attrs = dir(oldArt)
+
+    for attr in attrs:
+        if attr.startswith("_") and (attr.endswith("_colour") or attr.endswith("_font") or \
+                                     attr.endswith("_font") or attr.endswith("_brush") or \
+                                     attr.endswith("Pen") or attr.endswith("_pen")):
+            setattr(newArt, attr, getattr(oldArt, attr))
+
+    return newArt            
+
diff --git a/aui/auibar.py b/aui/auibar.py
new file mode 100644 (file)
index 0000000..88bb509
--- /dev/null
@@ -0,0 +1,3926 @@
+"""
+auibar contains an implementation of L{AuiToolBar}, which is a completely owner-drawn
+toolbar perfectly integrated with the AUI layout system. This allows drag and drop of
+toolbars, docking/floating behaviour and the possibility to define "overflow" items
+in the toolbar itself.
+
+The default theme that is used is L{AuiDefaultToolBarArt}, which provides a modern,
+glossy look and feel. The theme can be changed by calling L{AuiToolBar.SetArtProvider}.
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+import types
+
+from aui_utilities import BitmapFromBits, StepColour, GetLabelSize
+from aui_utilities import GetBaseColour, MakeDisabledBitmap
+
+import framemanager
+from aui_constants import *
+
+# wxPython version string
+_VERSION_STRING = wx.VERSION_STRING
+
+# AuiToolBar events
+wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN = wx.NewEventType()
+wxEVT_COMMAND_AUITOOLBAR_OVERFLOW_CLICK = wx.NewEventType()
+wxEVT_COMMAND_AUITOOLBAR_RIGHT_CLICK = wx.NewEventType()
+wxEVT_COMMAND_AUITOOLBAR_MIDDLE_CLICK = wx.NewEventType()
+wxEVT_COMMAND_AUITOOLBAR_BEGIN_DRAG = wx.NewEventType()
+
+EVT_AUITOOLBAR_TOOL_DROPDOWN = wx.PyEventBinder(wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN, 1)
+""" A dropdown `AuiToolBarItem` is being shown. """
+EVT_AUITOOLBAR_OVERFLOW_CLICK = wx.PyEventBinder(wxEVT_COMMAND_AUITOOLBAR_OVERFLOW_CLICK, 1)
+""" The user left-clicked on the overflow button in `AuiToolBar`. """
+EVT_AUITOOLBAR_RIGHT_CLICK = wx.PyEventBinder(wxEVT_COMMAND_AUITOOLBAR_RIGHT_CLICK, 1)
+""" Fires an event when the user right-clicks on a `AuiToolBarItem`. """
+EVT_AUITOOLBAR_MIDDLE_CLICK = wx.PyEventBinder(wxEVT_COMMAND_AUITOOLBAR_MIDDLE_CLICK, 1)
+""" Fires an event when the user middle-clicks on a `AuiToolBarItem`. """
+EVT_AUITOOLBAR_BEGIN_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUITOOLBAR_BEGIN_DRAG, 1)
+""" A drag operation involving a toolbar item has started. """
+
+# ----------------------------------------------------------------------
+
+class CommandToolBarEvent(wx.PyCommandEvent):
+    """ A specialized command event class for events sent by L{AuiToolBar}. """
+    
+    def __init__(self, command_type, win_id):
+        """
+        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.is_dropdown_clicked = False
+        self.click_pt = wx.Point(-1, -1)
+        self.rect = wx.Rect(-1, -1, 0, 0)
+        self.tool_id = -1
+
+
+    def IsDropDownClicked(self):
+        """ Returns whether the drop down menu has been clicked. """
+
+        return self.is_dropdown_clicked
+    
+
+    def SetDropDownClicked(self, c):
+        """
+        Sets whether the drop down menu has been clicked.
+
+        :param `c`: ``True`` to set the drop down as clicked, ``False`` otherwise.
+        """
+
+        self.is_dropdown_clicked = c    
+
+
+    def GetClickPoint(self):
+        """ Returns the point where the user clicked with the mouse. """
+
+        return self.click_pt
+
+    
+    def SetClickPoint(self, p):
+        """
+        Sets the clicking point.
+
+        :param `p`: a `wx.Point` object.
+        """
+        
+        self.click_pt = p    
+
+
+    def GetItemRect(self):
+        """ Returns the L{AuiToolBarItem} rectangle. """
+
+        return self.rect
+
+    
+    def SetItemRect(self, r):
+        """
+        Sets the L{AuiToolBarItem} rectangle.
+
+        :param `r`: an instance of `wx.Rect`.
+        """
+
+        self.rect = r    
+
+
+    def GetToolId(self):
+        """ Returns the L{AuiToolBarItem} identifier. """
+
+        return self.tool_id
+
+    
+    def SetToolId(self, id):
+        """
+        Sets the L{AuiToolBarItem} identifier.
+
+        :param `id`: the toolbar item identifier.
+        """
+
+        self.tool_id = id   
+
+
+# ----------------------------------------------------------------------
+
+class AuiToolBarEvent(CommandToolBarEvent):
+    """ A specialized command event class for events sent by L{AuiToolBar}. """
+    
+    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.
+        """
+
+        CommandToolBarEvent.__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 ToolbarCommandCapture(wx.PyEvtHandler):
+    """ A class to handle the dropdown window menu. """
+    
+    def __init__(self):
+        """ Default class constructor. """
+        
+        wx.PyEvtHandler.__init__(self)
+        self._last_id = 0
+
+
+    def GetCommandId(self):
+        """ Returns the event command identifier. """
+        
+        return self._last_id 
+
+
+    def ProcessEvent(self, event):
+        """
+        Processes an event, searching event tables and calling zero or more suitable
+        event handler function(s).
+
+        :param `event`: the event to process.
+
+        :note: Normally, your application would not call this function: it is called
+         in the wxPython implementation to dispatch incoming user interface events
+         to the framework (and application).
+         However, you might need to call it if implementing new functionality (such as
+         a new control) where you define new event types, as opposed to allowing the
+         user to override functions.
+
+         An instance where you might actually override the L{ProcessEvent} function is where
+         you want to direct event processing to event handlers not normally noticed by
+         wxPython. For example, in the document/view architecture, documents and views
+         are potential event handlers. When an event reaches a frame, L{ProcessEvent} will
+         need to be called on the associated document and view in case event handler
+         functions are associated with these objects. 
+
+         The normal order of event table searching is as follows:
+
+         1. If the object is disabled (via a call to `SetEvtHandlerEnabled`) the function
+            skips to step (6).
+         2. If the object is a `wx.Window`, L{ProcessEvent} is recursively called on the window's 
+            `wx.Validator`. If this returns ``True``, the function exits.
+         3. wxWidgets `SearchEventTable` is called for this event handler. If this fails, the
+            base class table is tried, and so on until no more tables exist or an appropriate
+            function was found, in which case the function exits.
+         4. The search is applied down the entire chain of event handlers (usually the chain
+            has a length of one). If this succeeds, the function exits.
+         5. If the object is a `wx.Window` and the event is a `wx.CommandEvent`, L{ProcessEvent} is
+            recursively applied to the parent window's event handler. If this returns ``True``,
+            the function exits.
+         6. Finally, L{ProcessEvent} is called on the `wx.App` object.
+        """
+        
+        if event.GetEventType() == wx.wxEVT_COMMAND_MENU_SELECTED:
+            self._last_id = event.GetId()
+            return True
+        
+        if self.GetNextHandler():
+            return self.GetNextHandler().ProcessEvent(event)
+
+        return False
+
+
+# ----------------------------------------------------------------------
+
+class AuiToolBarItem(object):
+    """
+    AuiToolBarItem is a toolbar element.
+    
+    It has a unique id (except for the separators which always have id = -1), the
+    style (telling whether it is a normal button, separator or a control), the
+    state (toggled or not, enabled or not) and short and long help strings. The
+    default implementations use the short help string for the tooltip text which
+    is popped up when the mouse pointer enters the tool and the long help string
+    for the applications status bar.
+    """
+
+    def __init__(self, item=None):
+        """
+        Default class constructor.
+
+        :param `item`: another instance of L{AuiToolBarItem}.
+        """
+
+        if item:
+            self.Assign(item)
+            return
+        
+        self.window = None
+        self.clockwisebmp = wx.NullBitmap
+        self.counterclockwisebmp = wx.NullBitmap
+        self.clockwisedisbmp = wx.NullBitmap
+        self.counterclockwisedisbmp = wx.NullBitmap
+        self.sizer_item = None
+        self.spacer_pixels = 0
+        self.id = 0
+        self.kind = ITEM_NORMAL
+        self.state = 0   # normal, enabled
+        self.proportion = 0
+        self.active = True
+        self.dropdown = True
+        self.sticky = True
+        self.user_data = 0
+
+        self.label = ""
+        self.bitmap = wx.NullBitmap
+        self.disabled_bitmap = wx.NullBitmap
+        self.hover_bitmap = wx.NullBitmap
+        self.short_help = ""
+        self.long_help = ""
+        self.min_size = wx.Size(-1, -1)
+        self.alignment = wx.ALIGN_CENTER
+        self.orientation = AUI_TBTOOL_HORIZONTAL
+        
+
+    def Assign(self, c):
+        """
+        Assigns the properties of the L{AuiToolBarItem} `c` to `self`.
+
+        :param `c`: another instance of L{AuiToolBarItem}.
+        """
+
+        self.window = c.window
+        self.label = c.label
+        self.bitmap = c.bitmap
+        self.disabled_bitmap = c.disabled_bitmap
+        self.hover_bitmap = c.hover_bitmap
+        self.short_help = c.short_help
+        self.long_help = c.long_help
+        self.sizer_item = c.sizer_item
+        self.min_size = c.min_size
+        self.spacer_pixels = c.spacer_pixels
+        self.id = c.id
+        self.kind = c.kind
+        self.state = c.state
+        self.proportion = c.proportion
+        self.active = c.active
+        self.dropdown = c.dropdown
+        self.sticky = c.sticky
+        self.user_data = c.user_data
+        self.alignment = c.alignment
+        self.orientation = c.orientation
+
+
+    def SetWindow(self, w):
+        """
+        Assigns a window to the toolbar item.
+
+        :param `w`: an instance of `wx.Window`.
+        """
+
+        self.window = w
+
+        
+    def GetWindow(self):
+        """ Returns window associated to the toolbar item. """
+
+        return self.window        
+
+
+    def SetId(self, new_id):
+        """
+        Sets the toolbar item identifier.
+
+        :param `new_id`: the new tool id.
+        """
+
+        self.id = new_id
+
+        
+    def GetId(self):
+        """ Returns the toolbar item identifier. """
+
+        return self.id 
+
+
+    def SetKind(self, new_kind):
+        """
+        Sets the L{AuiToolBarItem} kind.
+
+        :param `new_kind`: can be one of the following items:
+
+         ========================  =============================
+         Item Kind                 Description
+         ========================  =============================
+         ``ITEM_CONTROL``          The item in the `AuiToolBar` is a control
+         ``ITEM_LABEL``            The item in the `AuiToolBar` is a text label
+         ``ITEM_SPACER``           The item in the `AuiToolBar` is a spacer
+         ``ITEM_SEPARATOR``        The item in the `AuiToolBar` is a separator
+         ``ITEM_CHECK``            The item in the `AuiToolBar` is a toolbar check item
+         ``ITEM_NORMAL``           The item in the `AuiToolBar` is a standard toolbar item
+         ``ITEM_RADIO``            The item in the `AuiToolBar` is a toolbar radio item
+         ========================  =============================
+        """
+
+        self.kind = new_kind
+
+
+    def GetKind(self):
+        """ Returns the toolbar item kind. See L{SetKind} for more details. """
+
+        return self.kind
+        
+
+    def SetState(self, new_state):
+        """
+        Sets the toolbar item state.
+
+        :param `new_state`: can be one of the following states:
+
+         ============================================  ======================================
+         Button State Constant                         Description     
+         ============================================  ======================================
+         ``AUI_BUTTON_STATE_NORMAL``                   Normal button state
+         ``AUI_BUTTON_STATE_HOVER``                    Hovered button state
+         ``AUI_BUTTON_STATE_PRESSED``                  Pressed button state
+         ``AUI_BUTTON_STATE_DISABLED``                 Disabled button state
+         ``AUI_BUTTON_STATE_HIDDEN``                   Hidden button state
+         ``AUI_BUTTON_STATE_CHECKED``                  Checked button state
+         ============================================  ======================================
+    
+        """
+
+        self.state = new_state
+
+        
+    def GetState(self):
+        """
+        Returns the toolbar item state. See L{SetState} for more details.
+
+        :see: L{SetState}
+        """
+        
+        return self.state 
+
+
+    def SetSizerItem(self, s):
+        """
+        Associates a sizer item to this toolbar item.
+
+        :param `s`: an instance of `wx.SizerItem`.
+        """
+
+        self.sizer_item = s
+
+        
+    def GetSizerItem(self):
+        """ Returns the associated sizer item. """
+
+        return self.sizer_item 
+
+
+    def SetLabel(self, s):
+        """
+        Sets the toolbar item label.
+
+        :param `s`: a string specifying the toolbar item label.
+        """
+
+        self.label = s
+
+        
+    def GetLabel(self):
+        """ Returns the toolbar item label. """
+
+        return self.label 
+
+
+    def SetBitmap(self, bmp):
+        """
+        Sets the toolbar item bitmap.
+
+        :param `bmp`: an instance of `wx.Bitmap`.
+        """
+        
+        self.bitmap = bmp
+
+        
+    def GetBitmap(self):
+        """ Returns the toolbar item bitmap. """
+
+        return self.GetRotatedBitmap(False)
+
+
+    def SetDisabledBitmap(self, bmp):
+        """
+        Sets the toolbar item disabled bitmap.
+
+        :param `bmp`: an instance of `wx.Bitmap`.
+        """
+        
+        self.disabled_bitmap = bmp
+
+        
+    def GetDisabledBitmap(self):
+        """ Returns the toolbar item disabled bitmap. """
+        
+        return self.GetRotatedBitmap(True)
+
+
+    def SetHoverBitmap(self, bmp):
+        """
+        Sets the toolbar item hover bitmap.
+
+        :param `bmp`: an instance of `wx.Bitmap`.
+        """
+        
+        self.hover_bitmap = bmp
+
+
+    def SetOrientation(self, a):
+        """
+        Sets the toolbar tool orientation.
+
+        :param `a`: one of ``AUI_TBTOOL_HORIZONTAL``, ``AUI_TBTOOL_VERT_CLOCKWISE`` or
+         ``AUI_TBTOOL_VERT_COUNTERCLOCKWISE``.
+        """
+
+        self.orientation = a
+
+
+    def GetOrientation(self):
+        """ Returns the toolbar tool orientation. """
+
+        return self.orientation
+    
+        
+    def GetHoverBitmap(self):
+        """ Returns the toolbar item hover bitmap. """
+        
+        return self.hover_bitmap 
+
+
+    def GetRotatedBitmap(self, disabled):
+        """
+        Returns the correct bitmap depending on the tool orientation.
+
+        :param `disabled`: whether to return the disabled bitmap or not.
+        """
+        
+        bitmap_to_rotate = (disabled and [self.disabled_bitmap] or [self.bitmap])[0]
+        if not bitmap_to_rotate.IsOk() or self.orientation == AUI_TBTOOL_HORIZONTAL:
+            return bitmap_to_rotate
+
+        rotated_bitmap = wx.NullBitmap
+        clockwise = True
+        if self.orientation == AUI_TBTOOL_VERT_CLOCKWISE:
+            rotated_bitmap = (disabled and [self.clockwisedisbmp] or [self.clockwisebmp])[0]
+
+        elif self.orientation == AUI_TBTOOL_VERT_COUNTERCLOCKWISE:
+            rotated_bitmap = (disabled and [self.counterclockwisedisbmp] or [self.counterclockwisebmp])[0]
+            clockwise = False
+
+        if not rotated_bitmap.IsOk():
+            rotated_bitmap = wx.BitmapFromImage(bitmap_to_rotate.ConvertToImage().Rotate90(clockwise))
+
+        return rotated_bitmap
+
+
+    def SetShortHelp(self, s):
+        """
+        Sets the short help string for the L{AuiToolBarItem}, to be displayed in a
+        `wx.ToolTip` when the mouse hover over the toolbar item.
+
+        :param `s`: the tool short help string.
+        """
+
+        self.short_help = s
+
+        
+    def GetShortHelp(self):
+        """ Returns the short help string for the L{AuiToolBarItem}. """
+
+        return self.short_help 
+
+
+    def SetLongHelp(self, s):
+        """
+        Sets the long help string for the toolbar item. This string is shown in the
+        statusbar (if any) of the parent frame when the mouse pointer is inside the
+        tool.
+
+        :param `s`: the tool long help string.
+        """
+
+        self.long_help = s
+
+        
+    def GetLongHelp(self):
+        """ Returns the long help string for the L{AuiToolBarItem}. """
+
+        return self.long_help 
+
+
+    def SetMinSize(self, s):
+        """
+        Sets the toolbar item minimum size.
+
+        :param `s`: an instance of `wx.Size`.
+        """
+
+        self.min_size = wx.Size(*s)
+
+        
+    def GetMinSize(self):
+        """ Returns the toolbar item minimum size. """
+
+        return self.min_size 
+
+
+    def SetSpacerPixels(self, s):
+        """
+        Sets the number of pixels for a toolbar item with kind = ``ITEM_SEPARATOR``.
+
+        :param `s`: number of pixels.
+        """
+
+        self.spacer_pixels = s
+
+        
+    def GetSpacerPixels(self):
+        """ Returns the number of pixels for a toolbar item with kind = ``ITEM_SEPARATOR``. """
+
+        return self.spacer_pixels 
+
+
+    def SetProportion(self, p):
+        """
+        Sets the L{AuiToolBarItem} proportion in the toolbar.
+
+        :param `p`: the item proportion.
+        """
+
+        self.proportion = p
+
+        
+    def GetProportion(self):
+        """ Returns the L{AuiToolBarItem} proportion in the toolbar. """
+
+        return self.proportion 
+
+
+    def SetActive(self, b):
+        """
+        Activates/deactivates the toolbar item.
+
+        :param `b`: ``True`` to activate the item, ``False`` to deactivate it.
+        """
+
+        self.active = b
+
+        
+    def IsActive(self):
+        """ Returns whether the toolbar item is active or not. """
+
+        return self.active
+    
+
+    def SetHasDropDown(self, b):
+        """
+        Sets whether the toolbar item has an associated dropdown menu.
+
+        :param `b`: ``True`` to set a dropdown menu, ``False`` otherwise.
+        """
+
+        self.dropdown = b
+
+        
+    def HasDropDown(self):
+        """ Returns whether the toolbar item has an associated dropdown menu or not. """
+
+        return self.dropdown 
+
+
+    def SetSticky(self, b):
+        """
+        Sets whether the toolbar item is sticky (permanent highlight after mouse enter)
+        or not.
+
+        :param `b`: ``True`` to set the item as sticky, ``False`` otherwise.
+        """
+
+        self.sticky = b
+
+        
+    def IsSticky(self):
+        """ Returns whether the toolbar item has a sticky behaviour or not. """
+
+        return self.sticky 
+
+
+    def SetUserData(self, l):
+        """
+        Associates some kind of user data to the toolbar item.
+        
+        :param `l`: a Python object.
+
+        :note: The user data can be any Python object.
+        """
+
+        self.user_data = l
+
+        
+    def GetUserData(self):
+        """ Returns the associated user data. """
+
+        return self.user_data
+    
+
+    def SetAlignment(self, l):
+        """
+        Sets the toolbar item alignment.
+
+        :param `l`: the item alignment, which can be one of the available `wx.Sizer`
+         alignments.
+        """
+
+        self.alignment = l
+
+        
+    def GetAlignment(self):
+        """ Returns the toolbar item alignment. """
+
+        return self.alignment        
+
+
+# ----------------------------------------------------------------------
+
+class AuiDefaultToolBarArt(object):
+    """
+    Toolbar art provider code - a tab provider provides all drawing functionality to
+    the L{AuiToolBar}. This allows the L{AuiToolBar} to have a plugable look-and-feel.
+
+    By default, a L{AuiToolBar} uses an instance of this class called L{AuiDefaultToolBarArt}
+    which provides bitmap art and a colour scheme that is adapted to the major platforms'
+    look. You can either derive from that class to alter its behaviour or write a
+    completely new tab art class. Call L{AuiToolBar.SetArtProvider} to make use this
+    new tab art.
+    """
+
+    def __init__(self):
+        """ Default class constructor. """
+        
+        self._base_colour = GetBaseColour()
+
+        self._agwFlags = 0
+        self._text_orientation = AUI_TBTOOL_TEXT_BOTTOM
+        self._highlight_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+
+        self._separator_size = 7
+        self._orientation = AUI_TBTOOL_HORIZONTAL
+        self._gripper_size = 7
+        self._overflow_size = 16
+
+        darker1_colour = StepColour(self._base_colour, 85)
+        darker2_colour = StepColour(self._base_colour, 75)
+        darker3_colour = StepColour(self._base_colour, 60)
+        darker4_colour = StepColour(self._base_colour, 50)
+        darker5_colour = StepColour(self._base_colour, 40)
+
+        self._gripper_pen1 = wx.Pen(darker5_colour)
+        self._gripper_pen2 = wx.Pen(darker3_colour)
+        self._gripper_pen3 = wx.WHITE_PEN
+
+        button_dropdown_bits = "\xe0\xf1\xfb"
+        overflow_bits = "\x80\xff\x80\xc1\xe3\xf7"
+
+        self._button_dropdown_bmp = BitmapFromBits(button_dropdown_bits, 5, 3, wx.BLACK)
+        self._disabled_button_dropdown_bmp = BitmapFromBits(button_dropdown_bits, 5, 3,
+                                                            wx.Colour(128, 128, 128))
+        self._overflow_bmp = BitmapFromBits(overflow_bits, 7, 6, wx.BLACK)
+        self._disabled_overflow_bmp = BitmapFromBits(overflow_bits, 7, 6, wx.Colour(128, 128, 128))
+
+        self._font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+
+
+    def Clone(self):
+        """ Clones the L{AuiToolBar} art. """
+
+        return AuiDefaultToolBarArt()
+
+
+    def SetAGWFlags(self, agwFlags):
+        """
+        Sets the toolbar art flags.
+
+        :param `agwFlags`: a combination of the following values:
+
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_TB_TEXT``                      Shows the text in the toolbar buttons; by default only icons are shown
+         ``AUI_TB_NO_TOOLTIPS``               Don't show tooltips on `AuiToolBar` items
+         ``AUI_TB_NO_AUTORESIZE``             Do not auto-resize the `AuiToolBar`
+         ``AUI_TB_GRIPPER``                   Shows a gripper on the `AuiToolBar`
+         ``AUI_TB_OVERFLOW``                  The `AuiToolBar` can contain overflow items
+         ``AUI_TB_VERTICAL``                  The `AuiToolBar` is vertical
+         ``AUI_TB_HORZ_LAYOUT``               Shows the text and the icons alongside, not vertically stacked. This style must be used with ``AUI_TB_TEXT``
+         ``AUI_TB_PLAIN_BACKGROUND``          Don't draw a gradient background on the toolbar
+         ``AUI_TB_HORZ_TEXT``                 Combination of ``AUI_TB_HORZ_LAYOUT`` and ``AUI_TB_TEXT``
+         ==================================== ==================================
+        
+        """
+        
+        self._agwFlags = agwFlags
+
+
+    def GetAGWFlags(self):
+        """
+        Returns the L{AuiDefaultToolBarArt} flags. See L{SetAGWFlags} for more
+        details.
+
+        :see: L{SetAGWFlags}
+        """
+
+        return self._agwFlags
+
+
+    def SetFont(self, font):
+        """
+        Sets the L{AuiDefaultToolBarArt} font.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._font = font
+
+
+    def SetTextOrientation(self, orientation):
+        """
+        Sets the text orientation.
+
+        :param `orientation`: can be one of the following constants:
+
+         ==================================== ==================================
+         Orientation Switches                 Description
+         ==================================== ==================================
+         ``AUI_TBTOOL_TEXT_LEFT``             Text in `AuiToolBar` items is aligned left
+         ``AUI_TBTOOL_TEXT_RIGHT``            Text in `AuiToolBar` items is aligned right
+         ``AUI_TBTOOL_TEXT_TOP``              Text in `AuiToolBar` items is aligned top
+         ``AUI_TBTOOL_TEXT_BOTTOM``           Text in `AuiToolBar` items is aligned bottom
+         ==================================== ==================================
+        
+        """
+
+        self._text_orientation = orientation
+
+
+    def GetFont(self):
+        """ Returns the L{AuiDefaultToolBarArt} font. """
+
+        return self._font
+
+
+    def GetTextOrientation(self):
+        """
+        Returns the L{AuiDefaultToolBarArt} text orientation. See
+        L{SetTextOrientation} for more details.
+
+        :see: L{SetTextOrientation}
+        """
+
+        return self._text_orientation
+
+
+    def SetOrientation(self, orientation):
+        """
+        Sets the toolbar tool orientation.
+
+        :param `orientation`: one of ``AUI_TBTOOL_HORIZONTAL``, ``AUI_TBTOOL_VERT_CLOCKWISE`` or
+         ``AUI_TBTOOL_VERT_COUNTERCLOCKWISE``.
+        """
+
+        self._orientation = orientation
+
+
+    def GetOrientation(self):
+        """ Returns the toolbar orientation. """
+
+        return self._orientation        
+
+
+    def DrawBackground(self, dc, wnd, _rect, horizontal=True):
+        """
+        Draws a toolbar background with a gradient shading.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `_rect`: the L{AuiToolBar} rectangle;
+        :param `horizontal`: ``True`` if the toolbar is horizontal, ``False`` if it is vertical.
+        """
+
+        rect = wx.Rect(*_rect)
+
+        start_colour = StepColour(self._base_colour, 180)
+        end_colour = StepColour(self._base_colour, 85)
+        reflex_colour = StepColour(self._base_colour, 95)
+        
+        dc.GradientFillLinear(rect, start_colour, end_colour,
+                              (horizontal and [wx.SOUTH] or [wx.EAST])[0])
+
+        left = rect.GetLeft()
+        right = rect.GetRight()
+        top = rect.GetTop()
+        bottom = rect.GetBottom()
+
+        dc.SetPen(wx.Pen(reflex_colour))
+        if horizontal:
+            dc.DrawLine(left, bottom, right+1, bottom)
+        else:
+            dc.DrawLine(right, top, right, bottom+1)
+            
+
+    def DrawPlainBackground(self, dc, wnd, _rect):
+        """
+        Draws a toolbar background with a plain colour.
+
+        This method contrasts with the default behaviour of the L{AuiToolBar} that
+        draws a background gradient and this break the window design when putting
+        it within a control that has margin between the borders and the toolbar
+        (example: put L{AuiToolBar} within a `wx.StaticBoxSizer` that has a plain background).
+      
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `_rect`: the L{AuiToolBar} rectangle.
+        """
+        
+        rect = wx.Rect(*_rect)
+        rect.height += 1
+
+        dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
+        dc.DrawRectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 1)
+
+
+    def DrawLabel(self, dc, wnd, item, rect):
+        """
+        Draws a toolbar item label.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `item`: an instance of L{AuiToolBarItem};
+        :param `rect`: the L{AuiToolBarItem} rectangle.
+        """
+        
+        dc.SetFont(self._font)
+        dc.SetTextForeground(wx.BLACK)
+        orient = item.GetOrientation()
+
+        horizontal = orient == AUI_TBTOOL_HORIZONTAL
+        # we only care about the text height here since the text
+        # will get cropped based on the width of the item
+        label_size = GetLabelSize(dc, item.GetLabel(), not horizontal)
+        text_width = label_size.GetWidth()
+        text_height = label_size.GetHeight()
+
+        if orient == AUI_TBTOOL_HORIZONTAL:
+            text_x = rect.x
+            text_y = rect.y + (rect.height-text_height)/2
+            dc.DrawText(item.GetLabel(), text_x, text_y)
+
+        elif orient == AUI_TBTOOL_VERT_CLOCKWISE:
+            text_x = rect.x + (rect.width+text_width)/2
+            text_y = rect.y
+            dc.DrawRotatedText(item.GetLabel(), text_x, text_y, 270)
+
+        elif AUI_TBTOOL_VERT_COUNTERCLOCKWISE:
+            text_x = rect.x + (rect.width-text_width)/2
+            text_y = rect.y + text_height
+            dc.DrawRotatedText(item.GetLabel(), text_x, text_y, 90)
+
+
+    def DrawButton(self, dc, wnd, item, rect):
+        """
+        Draws a toolbar item button.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `item`: an instance of L{AuiToolBarItem};
+        :param `rect`: the L{AuiToolBarItem} rectangle.
+        """
+
+        bmp_rect, text_rect = self.GetToolsPosition(dc, item, rect)
+        
+        if not item.GetState() & AUI_BUTTON_STATE_DISABLED:
+        
+            if item.GetState() & AUI_BUTTON_STATE_PRESSED:
+            
+                dc.SetPen(wx.Pen(self._highlight_colour))
+                dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 150)))
+                dc.DrawRectangleRect(rect)
+            
+            elif item.GetState() & AUI_BUTTON_STATE_HOVER or item.IsSticky():
+            
+                dc.SetPen(wx.Pen(self._highlight_colour))
+                dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 170)))
+
+                # draw an even lighter background for checked item hovers (since
+                # the hover background is the same colour as the check background)
+                if item.GetState() & AUI_BUTTON_STATE_CHECKED:
+                    dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 180)))
+
+                dc.DrawRectangleRect(rect)
+            
+            elif item.GetState() & AUI_BUTTON_STATE_CHECKED:
+            
+                # it's important to put this code in an else statment after the
+                # hover, otherwise hovers won't draw properly for checked items
+                dc.SetPen(wx.Pen(self._highlight_colour))
+                dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 170)))
+                dc.DrawRectangleRect(rect)
+            
+        if item.GetState() & AUI_BUTTON_STATE_DISABLED:
+            bmp = item.GetDisabledBitmap()
+        else:
+            bmp = item.GetBitmap()
+
+        if bmp.IsOk():
+            dc.DrawBitmap(bmp, bmp_rect.x, bmp_rect.y, True)
+
+        # set the item's text colour based on if it is disabled
+        dc.SetTextForeground(wx.BLACK)
+        if item.GetState() & AUI_BUTTON_STATE_DISABLED:
+            dc.SetTextForeground(DISABLED_TEXT_COLOUR)
+
+        if self._agwFlags & AUI_TB_TEXT and item.GetLabel() != "":
+            self.DrawLabel(dc, wnd, item, text_rect)
+        
+
+    def DrawDropDownButton(self, dc, wnd, item, rect):
+        """
+        Draws a toolbar dropdown button.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `item`: an instance of L{AuiToolBarItem};
+        :param `rect`: the L{AuiToolBarItem} rectangle.
+        """
+        
+        dropbmp_x = dropbmp_y = 0
+
+        button_rect = wx.Rect(rect.x, rect.y, rect.width-BUTTON_DROPDOWN_WIDTH, rect.height)
+        dropdown_rect = wx.Rect(rect.x+rect.width-BUTTON_DROPDOWN_WIDTH-1, rect.y, BUTTON_DROPDOWN_WIDTH+1, rect.height)
+
+        horizontal = item.GetOrientation() == AUI_TBTOOL_HORIZONTAL
+        
+        if horizontal:
+            button_rect = wx.Rect(rect.x, rect.y, rect.width-BUTTON_DROPDOWN_WIDTH, rect.height)
+            dropdown_rect = wx.Rect(rect.x+rect.width-BUTTON_DROPDOWN_WIDTH-1, rect.y, BUTTON_DROPDOWN_WIDTH+1, rect.height)
+        else:
+            button_rect = wx.Rect(rect.x, rect.y, rect.width, rect.height-BUTTON_DROPDOWN_WIDTH)
+            dropdown_rect = wx.Rect(rect.x, rect.y+rect.height-BUTTON_DROPDOWN_WIDTH-1, rect.width, BUTTON_DROPDOWN_WIDTH+1)
+
+        dropbmp_width = self._button_dropdown_bmp.GetWidth()
+        dropbmp_height = self._button_dropdown_bmp.GetHeight()
+        if not horizontal:
+            tmp = dropbmp_width
+            dropbmp_width = dropbmp_height
+            dropbmp_height = tmp
+
+        dropbmp_x = dropdown_rect.x + (dropdown_rect.width/2) - dropbmp_width/2
+        dropbmp_y = dropdown_rect.y + (dropdown_rect.height/2) - dropbmp_height/2
+
+        bmp_rect, text_rect = self.GetToolsPosition(dc, item, button_rect)
+        
+        if item.GetState() & AUI_BUTTON_STATE_PRESSED:
+        
+            dc.SetPen(wx.Pen(self._highlight_colour))
+            dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 140)))
+            dc.DrawRectangleRect(button_rect)
+            dc.DrawRectangleRect(dropdown_rect)
+        
+        elif item.GetState() & AUI_BUTTON_STATE_HOVER or item.IsSticky():
+        
+            dc.SetPen(wx.Pen(self._highlight_colour))
+            dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 170)))
+            dc.DrawRectangleRect(button_rect)
+            dc.DrawRectangleRect(dropdown_rect)
+
+        elif item.GetState() & AUI_BUTTON_STATE_CHECKED:
+            # it's important to put this code in an else statment after the 
+            # hover, otherwise hovers won't draw properly for checked items 
+            dc.SetPen(wx.Pen(self._highlight_colour))
+            dc.SetBrush(wx.Brush(StepColour(self._highlight_colour, 170)))
+            dc.DrawRectangle(button_rect)
+            dc.DrawRectangle(dropdown_rect)
+            
+        if item.GetState() & AUI_BUTTON_STATE_DISABLED:
+        
+            bmp = item.GetDisabledBitmap()
+            dropbmp = self._disabled_button_dropdown_bmp
+        
+        else:
+        
+            bmp = item.GetBitmap()
+            dropbmp = self._button_dropdown_bmp
+        
+        if not bmp.IsOk():
+            return
+
+        dc.DrawBitmap(bmp, bmp_rect.x, bmp_rect.y, True)
+        if horizontal:
+            dc.DrawBitmap(dropbmp, dropbmp_x, dropbmp_y, True)
+        else:
+            dc.DrawBitmap(wx.BitmapFromImage(dropbmp.ConvertToImage().Rotate90(item.GetOrientation() == AUI_TBTOOL_VERT_CLOCKWISE)),
+                          dropbmp_x, dropbmp_y, True)
+            
+        # set the item's text colour based on if it is disabled
+        dc.SetTextForeground(wx.BLACK)
+        if item.GetState() & AUI_BUTTON_STATE_DISABLED:
+            dc.SetTextForeground(DISABLED_TEXT_COLOUR)
+
+        if self._agwFlags & AUI_TB_TEXT and item.GetLabel() != "":  
+            self.DrawLabel(dc, wnd, item, text_rect)
+        
+
+    def DrawControlLabel(self, dc, wnd, item, rect):
+        """
+        Draws a label for a toolbar control.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `item`: an instance of L{AuiToolBarItem};
+        :param `rect`: the L{AuiToolBarItem} rectangle.
+        """
+
+        label_size = GetLabelSize(dc, item.GetLabel(), item.GetOrientation() != AUI_TBTOOL_HORIZONTAL)
+        text_height = label_size.GetHeight()
+        text_width = label_size.GetWidth()
+
+        dc.SetFont(self._font)
+
+        if self._agwFlags & AUI_TB_TEXT:
+        
+            tx, text_height = dc.GetTextExtent("ABCDHgj")        
+
+        text_width, ty = dc.GetTextExtent(item.GetLabel())
+
+        # don't draw the label if it is wider than the item width
+        if text_width > rect.width:
+            return
+
+        # set the label's text colour
+        dc.SetTextForeground(wx.BLACK)
+
+        text_x = rect.x + (rect.width/2) - (text_width/2) + 1
+        text_y = rect.y + rect.height - text_height - 1
+
+        if self._agwFlags & AUI_TB_TEXT and item.GetLabel() != "": 
+            dc.DrawText(item.GetLabel(), text_x, text_y)
+    
+
+    def GetLabelSize(self, dc, wnd, item):
+        """
+        Returns the label size for a toolbar item.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `item`: an instance of L{AuiToolBarItem}.
+        """
+
+        dc.SetFont(self._font)
+        label_size = GetLabelSize(dc, item.GetLabel(), self._orientation != AUI_TBTOOL_HORIZONTAL)
+
+        return wx.Size(item.GetMinSize().GetWidth(), label_size.GetHeight())
+
+
+    def GetToolSize(self, dc, wnd, item):
+        """
+        Returns the toolbar item size.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `item`: an instance of L{AuiToolBarItem}.
+        """
+        
+        if not item.GetBitmap().IsOk() and not self._agwFlags & AUI_TB_TEXT:
+            return wx.Size(16, 16)
+
+        width = item.GetBitmap().GetWidth()
+        height = item.GetBitmap().GetHeight()
+
+        if self._agwFlags & AUI_TB_TEXT:
+        
+            dc.SetFont(self._font)
+            label_size = GetLabelSize(dc, item.GetLabel(), self.GetOrientation() != AUI_TBTOOL_HORIZONTAL)
+            padding = 6
+            
+            if self._text_orientation == AUI_TBTOOL_TEXT_BOTTOM:
+            
+                if self.GetOrientation() != AUI_TBTOOL_HORIZONTAL:
+                    height += 3   # space between top border and bitmap
+                    height += 3   # space between bitmap and text
+                    padding = 0
+
+                height += label_size.GetHeight()
+            
+                if item.GetLabel() != "":
+                    width = max(width, label_size.GetWidth()+padding)
+                
+            elif self._text_orientation == AUI_TBTOOL_TEXT_RIGHT and item.GetLabel() != "":
+            
+                if self.GetOrientation() == AUI_TBTOOL_HORIZONTAL:
+                    
+                    width += 3  # space between left border and bitmap
+                    width += 3  # space between bitmap and text
+                    padding = 0
+
+                width += label_size.GetWidth()
+                height = max(height, label_size.GetHeight()+padding)
+                
+        # if the tool has a dropdown button, add it to the width
+        if item.HasDropDown():
+            if item.GetOrientation() == AUI_TBTOOL_HORIZONTAL:
+                width += BUTTON_DROPDOWN_WIDTH+4
+            else:
+                height += BUTTON_DROPDOWN_WIDTH+4
+
+        return wx.Size(width, height)
+
+
+    def DrawSeparator(self, dc, wnd, _rect):
+        """
+        Draws a toolbar separator.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `_rect`: the L{AuiToolBarItem} rectangle.
+        """
+        
+        horizontal = True
+        if self._agwFlags & AUI_TB_VERTICAL:
+            horizontal = False
+
+        rect = wx.Rect(*_rect)
+
+        if horizontal:
+        
+            rect.x += (rect.width/2)
+            rect.width = 1
+            new_height = (rect.height*3)/4
+            rect.y += (rect.height/2) - (new_height/2)
+            rect.height = new_height
+        
+        else:
+        
+            rect.y += (rect.height/2)
+            rect.height = 1
+            new_width = (rect.width*3)/4
+            rect.x += (rect.width/2) - (new_width/2)
+            rect.width = new_width
+        
+        start_colour = StepColour(self._base_colour, 80)
+        end_colour = StepColour(self._base_colour, 80)
+        dc.GradientFillLinear(rect, start_colour, end_colour, (horizontal and [wx.SOUTH] or [wx.EAST])[0])
+
+
+    def DrawGripper(self, dc, wnd, rect):
+        """
+        Draws the toolbar gripper.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `rect`: the L{AuiToolBar} rectangle.
+        """
+        
+        i = 0
+        while 1:
+        
+            if self._agwFlags & AUI_TB_VERTICAL:
+            
+                x = rect.x + (i*4) + 4
+                y = rect.y + 3
+                if x > rect.GetWidth() - 4:
+                    break
+            
+            else:
+            
+                x = rect.x + 3
+                y = rect.y + (i*4) + 4
+                if y > rect.GetHeight() - 4:
+                    break
+            
+            dc.SetPen(self._gripper_pen1)
+            dc.DrawPoint(x, y)
+            dc.SetPen(self._gripper_pen2)
+            dc.DrawPoint(x, y+1)
+            dc.DrawPoint(x+1, y)
+            dc.SetPen(self._gripper_pen3)
+            dc.DrawPoint(x+2, y+1)
+            dc.DrawPoint(x+2, y+2)
+            dc.DrawPoint(x+1, y+2)
+
+            i += 1
+
+
+    def DrawOverflowButton(self, dc, wnd, rect, state):
+        """
+        Draws the overflow button for the L{AuiToolBar}.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` derived window;
+        :param `rect`: the L{AuiToolBar} rectangle;
+        :param `state`: the overflow button state.
+        """
+        
+        if state & AUI_BUTTON_STATE_HOVER or  state & AUI_BUTTON_STATE_PRESSED:
+        
+            cli_rect = wnd.GetClientRect()
+            light_gray_bg = StepColour(self._highlight_colour, 170)
+
+            if self._agwFlags & AUI_TB_VERTICAL:
+            
+                dc.SetPen(wx.Pen(self._highlight_colour))
+                dc.DrawLine(rect.x, rect.y, rect.x+rect.width, rect.y)
+                dc.SetPen(wx.Pen(light_gray_bg))
+                dc.SetBrush(wx.Brush(light_gray_bg))
+                dc.DrawRectangle(rect.x, rect.y+1, rect.width, rect.height)
+            
+            else:
+            
+                dc.SetPen(wx.Pen(self._highlight_colour))
+                dc.DrawLine(rect.x, rect.y, rect.x, rect.y+rect.height)
+                dc.SetPen(wx.Pen(light_gray_bg))
+                dc.SetBrush(wx.Brush(light_gray_bg))
+                dc.DrawRectangle(rect.x+1, rect.y, rect.width, rect.height)
+            
+        x = rect.x + 1 + (rect.width-self._overflow_bmp.GetWidth())/2
+        y = rect.y + 1 + (rect.height-self._overflow_bmp.GetHeight())/2
+        dc.DrawBitmap(self._overflow_bmp, x, y, True)
+
+
+    def GetElementSize(self, element_id):
+        """
+        Returns the size of a UI element in the L{AuiToolBar}.
+
+        :param `element_id`: can be one of the following:
+
+         ==================================== ==================================
+         Element Identifier                   Description
+         ==================================== ==================================
+         ``AUI_TBART_SEPARATOR_SIZE``         Separator size in `AuiToolBar`
+         ``AUI_TBART_GRIPPER_SIZE``           Gripper size in `AuiToolBar`
+         ``AUI_TBART_OVERFLOW_SIZE``          Overflow button size in `AuiToolBar`
+         ==================================== ==================================        
+        """
+        
+        if element_id == AUI_TBART_SEPARATOR_SIZE:
+            return self._separator_size
+        elif element_id == AUI_TBART_GRIPPER_SIZE:
+            return self._gripper_size
+        elif element_id == AUI_TBART_OVERFLOW_SIZE:
+            return self._overflow_size
+
+        return 0
+
+
+    def SetElementSize(self, element_id, size):
+        """
+        Sets the size of a UI element in the L{AuiToolBar}.
+
+        :param `element_id`: can be one of the following:
+
+         ==================================== ==================================
+         Element Identifier                   Description
+         ==================================== ==================================
+         ``AUI_TBART_SEPARATOR_SIZE``         Separator size in `AuiToolBar`
+         ``AUI_TBART_GRIPPER_SIZE``           Gripper size in `AuiToolBar`
+         ``AUI_TBART_OVERFLOW_SIZE``          Overflow button size in `AuiToolBar`
+         ==================================== ==================================
+
+        :param `size`: the new size of the UI element.        
+        """
+        
+        if element_id == AUI_TBART_SEPARATOR_SIZE:
+            self._separator_size = size
+        elif element_id == AUI_TBART_GRIPPER_SIZE:
+            self._gripper_size = size
+        elif element_id == AUI_TBART_OVERFLOW_SIZE:
+            self._overflow_size = size
+
+
+    def ShowDropDown(self, wnd, items):
+        """
+        Shows the drop down window menu for overflow items.
+
+        :param `wnd`: an instance of `wx.Window`;
+        :param `items`: the overflow toolbar items (a Python list).
+        """
+
+        menuPopup = wx.Menu()
+        items_added = 0
+
+        for item in items:
+
+            if item.GetKind() not in [ITEM_SEPARATOR, ITEM_SPACER, ITEM_CONTROL]:
+            
+                text = item.GetShortHelp()
+                if text == "":
+                    text = item.GetLabel()
+                if text == "":
+                    text = " "
+
+                kind = item.GetKind()
+                m = wx.MenuItem(menuPopup, item.GetId(), text, item.GetShortHelp(), kind)
+                orientation = item.GetOrientation()
+                item.SetOrientation(AUI_TBTOOL_HORIZONTAL)
+                
+                if kind not in [ITEM_CHECK, ITEM_RADIO]:
+                    m.SetBitmap(item.GetBitmap())
+
+                item.SetOrientation(orientation)                    
+                    
+                menuPopup.AppendItem(m)
+                if kind in [ITEM_CHECK, ITEM_RADIO]:            
+                    state = (item.state & AUI_BUTTON_STATE_CHECKED and [True] or [False])[0]
+                    m.Check(state)
+
+                items_added += 1
+            
+            else:
+            
+                if items_added > 0 and item.GetKind() == ITEM_SEPARATOR:
+                    menuPopup.AppendSeparator()
+            
+        # find out where to put the popup menu of window items
+        pt = wx.GetMousePosition()
+        pt = wnd.ScreenToClient(pt)
+
+        # find out the screen coordinate at the bottom of the tab ctrl
+        cli_rect = wnd.GetClientRect()
+        pt.y = cli_rect.y + cli_rect.height
+
+        cc = ToolbarCommandCapture()
+        wnd.PushEventHandler(cc)
+
+        # Adjustments to get slightly better menu placement
+        if wx.Platform == "__WXMAC__":
+            pt.y += 5
+            pt.x -= 5
+
+        wnd.PopupMenu(menuPopup, pt)
+        command = cc.GetCommandId()
+        wnd.PopEventHandler(True)
+
+        return command
+
+
+    def GetToolsPosition(self, dc, item, rect):
+        """
+        Returns the bitmap and text rectangles for a toolbar item.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `item`: an instance of L{AuiToolBarItem};
+        :param `rect`: the tool rect.
+        """
+        
+        text_width = text_height = 0
+        horizontal = self._orientation == AUI_TBTOOL_HORIZONTAL
+        text_bottom = self._text_orientation == AUI_TBTOOL_TEXT_BOTTOM
+        text_right = self._text_orientation == AUI_TBTOOL_TEXT_RIGHT
+        bmp_width = item.GetBitmap().GetWidth()
+        bmp_height = item.GetBitmap().GetHeight()
+     
+        if self._agwFlags & AUI_TB_TEXT:        
+            dc.SetFont(self._font)
+            label_size = GetLabelSize(dc, item.GetLabel(), not horizontal)
+            text_height = label_size.GetHeight()
+            text_width = label_size.GetWidth()
+        
+        bmp_x = bmp_y = text_x = text_y = 0
+
+        if horizontal and text_bottom:
+            bmp_x = rect.x + (rect.width/2) - (bmp_width/2)
+            bmp_y = rect.y + 3
+            text_x = rect.x + (rect.width/2) - (text_width/2)
+            text_y = rect.y + ((bmp_y - rect.y) * 2) + bmp_height
+        
+        elif horizontal and text_right:
+            bmp_x = rect.x + 3
+            bmp_y = rect.y + (rect.height/2) - (bmp_height / 2)
+            text_x = rect.x + ((bmp_x - rect.x) * 2) + bmp_width
+            text_y = rect.y + (rect.height/2) - (text_height/2)
+        
+        elif not horizontal and text_bottom:
+            bmp_x = rect.x + (rect.width / 2) - (bmp_width / 2)
+            bmp_y = rect.y + 3
+            text_x = rect.x + (rect.width / 2) - (text_width / 2)
+            text_y = rect.y + ((bmp_y - rect.y) * 2) + bmp_height
+        
+        bmp_rect = wx.Rect(bmp_x, bmp_y, bmp_width, bmp_height)
+        text_rect = wx.Rect(text_x, text_y, text_width, text_height)
+
+        return bmp_rect, text_rect
+
+    
+class AuiToolBar(wx.PyControl):
+    """
+    AuiToolBar is a completely owner-drawn toolbar perfectly integrated with the
+    AUI layout system. This allows drag and drop of toolbars, docking/floating
+    behaviour and the possibility to define "overflow" items in the toolbar itself.
+
+    The default theme that is used is L{AuiDefaultToolBarArt}, which provides a modern,
+    glossy look and feel. The theme can be changed by calling L{AuiToolBar.SetArtProvider}.
+    """
+
+    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=0, agwStyle=AUI_TB_DEFAULT_STYLE):
+        """
+        Default class constructor.
+
+        :param `parent`: the L{AuiToolBar} 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 control window style;
+        :param `agwStyle`: the AGW-specific window style. This can be a combination of the
+         following bits:
+        
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_TB_TEXT``                      Shows the text in the toolbar buttons; by default only icons are shown
+         ``AUI_TB_NO_TOOLTIPS``               Don't show tooltips on `AuiToolBar` items
+         ``AUI_TB_NO_AUTORESIZE``             Do not auto-resize the `AuiToolBar`
+         ``AUI_TB_GRIPPER``                   Shows a gripper on the `AuiToolBar`
+         ``AUI_TB_OVERFLOW``                  The `AuiToolBar` can contain overflow items
+         ``AUI_TB_VERTICAL``                  The `AuiToolBar` is vertical
+         ``AUI_TB_HORZ_LAYOUT``               Shows the text and the icons alongside, not vertically stacked. This style must be used with ``AUI_TB_TEXT``
+         ``AUI_TB_PLAIN_BACKGROUND``          Don't draw a gradient background on the toolbar
+         ``AUI_TB_HORZ_TEXT``                 Combination of ``AUI_TB_HORZ_LAYOUT`` and ``AUI_TB_TEXT``
+         ==================================== ==================================
+
+         The default value for `agwStyle` is: ``AUI_TB_DEFAULT_STYLE`` = 0
+
+        """
+        
+        wx.PyControl.__init__(self, parent, id, pos, size, style|wx.BORDER_NONE)
+
+        self._sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.SetSizer(self._sizer)
+        self._button_width = -1
+        self._button_height = -1
+        self._sizer_element_count = 0
+        self._action_pos = wx.Point(-1, -1)
+        self._action_item = None
+        self._tip_item = None
+        self._art = AuiDefaultToolBarArt()
+        self._tool_packing = 2
+        self._tool_border_padding = 3
+        self._tool_text_orientation = AUI_TBTOOL_TEXT_BOTTOM
+        self._tool_orientation = AUI_TBTOOL_HORIZONTAL
+        self._tool_alignment = wx.EXPAND
+        self._gripper_sizer_item = None
+        self._overflow_sizer_item = None
+        self._dragging = False
+
+        self._agwStyle = self._originalStyle = agwStyle
+
+        self._gripper_visible = (self._agwStyle & AUI_TB_GRIPPER and [True] or [False])[0]
+        self._overflow_visible = (self._agwStyle & AUI_TB_OVERFLOW and [True] or [False])[0]
+        self._overflow_state = 0
+        self._custom_overflow_prepend = []
+        self._custom_overflow_append = []
+
+        self._items = []
+        
+        self.SetMargins(5, 5, 2, 2)
+        self.SetFont(wx.NORMAL_FONT)
+        self._art.SetAGWFlags(self._agwStyle)
+        self.SetExtraStyle(wx.WS_EX_PROCESS_IDLE)
+        
+        if agwStyle & AUI_TB_HORZ_LAYOUT:
+            self.SetToolTextOrientation(AUI_TBTOOL_TEXT_RIGHT)
+        elif agwStyle & AUI_TB_VERTICAL:
+            if agwStyle & AUI_TB_CLOCKWISE:
+                self.SetToolOrientation(AUI_TBTOOL_VERT_CLOCKWISE)
+            elif agwStyle & AUI_TB_COUNTERCLOCKWISE:
+                self.SetToolOrientation(AUI_TBTOOL_VERT_COUNTERCLOCKWISE)
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_IDLE, self.OnIdle)
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
+        self.Bind(wx.EVT_RIGHT_DCLICK, self.OnRightDown)
+        self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
+        self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
+        self.Bind(wx.EVT_MIDDLE_DCLICK, self.OnMiddleDown)
+        self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
+        self.Bind(wx.EVT_MOTION, self.OnMotion)
+        self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
+        self.Bind(wx.EVT_SET_CURSOR, self.OnSetCursor)
+
+
+    def SetWindowStyleFlag(self, style):
+        """
+        Sets the style of the window.
+        
+        :param `style`: the new window style. 
+
+        :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.
+
+        :note: Overridden from `wx.PyControl`.
+        """
+
+        wx.PyControl.SetWindowStyleFlag(self, style|wx.BORDER_NONE)
+        
+
+    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_TB_TEXT``                      Shows the text in the toolbar buttons; by default only icons are shown
+         ``AUI_TB_NO_TOOLTIPS``               Don't show tooltips on `AuiToolBar` items
+         ``AUI_TB_NO_AUTORESIZE``             Do not auto-resize the `AuiToolBar`
+         ``AUI_TB_GRIPPER``                   Shows a gripper on the `AuiToolBar`
+         ``AUI_TB_OVERFLOW``                  The `AuiToolBar` can contain overflow items
+         ``AUI_TB_VERTICAL``                  The `AuiToolBar` is vertical
+         ``AUI_TB_HORZ_LAYOUT``               Shows the text and the icons alongside, not vertically stacked. This style must be used with ``AUI_TB_TEXT``
+         ``AUI_TB_PLAIN_BACKGROUND``          Don't draw a gradient background on the toolbar
+         ``AUI_TB_HORZ_TEXT``                 Combination of ``AUI_TB_HORZ_LAYOUT`` and ``AUI_TB_TEXT``
+         ==================================== ==================================
+
+        :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.
+        """
+        
+        self._agwStyle = self._originalStyle = agwStyle
+
+        if self._art:
+            self._art.SetAGWFlags(self._agwStyle)
+        
+        if agwStyle & AUI_TB_GRIPPER:
+            self._gripper_visible = True
+        else:
+            self._gripper_visible = False
+
+        if agwStyle & AUI_TB_OVERFLOW:
+            self._overflow_visible = True
+        else:
+            self._overflow_visible = False
+
+        if agwStyle & AUI_TB_HORZ_LAYOUT:
+            self.SetToolTextOrientation(AUI_TBTOOL_TEXT_RIGHT)
+        else:
+            self.SetToolTextOrientation(AUI_TBTOOL_TEXT_BOTTOM)
+
+        if agwStyle & AUI_TB_VERTICAL:
+            if agwStyle & AUI_TB_CLOCKWISE:
+                self.SetToolOrientation(AUI_TBTOOL_VERT_CLOCKWISE)
+            elif agwStyle & AUI_TB_COUNTERCLOCKWISE:
+                self.SetToolOrientation(AUI_TBTOOL_VERT_COUNTERCLOCKWISE)
+
+                
+    def GetAGWWindowStyleFlag(self):
+        """
+        Returns the AGW-specific window style flag.
+
+        :see: L{SetAGWWindowStyleFlag} for an explanation of various AGW-specific style.
+        """
+
+        return self._agwStyle
+    
+
+    def SetArtProvider(self, art):
+        """
+        Instructs L{AuiToolBar} 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{AuiToolBar}.
+        """
+        
+        del self._art
+        self._art = art
+
+        if self._art:
+            self._art.SetAGWFlags(self._agwStyle)
+            self._art.SetTextOrientation(self._tool_text_orientation)
+            self._art.SetOrientation(self._tool_orientation)
+        
+
+    def GetArtProvider(self):
+        """ Returns the current art provider being used. """
+
+        return self._art
+
+
+    def AddSimpleTool(self, tool_id, label, bitmap, short_help_string="", kind=ITEM_NORMAL):
+        """
+        Adds a tool to the toolbar. This is the simplest method you can use to
+        ass an item to the L{AuiToolBar}.
+
+        :param `tool_id`: an integer by which the tool may be identified in subsequent operations;
+        :param `label`: the toolbar tool label;
+        :param `bitmap`: the primary tool bitmap;
+        :param `short_help_string`: this string is used for the tools tooltip;
+        :param `kind`: the item kind. Can be one of the following:
+
+         ========================  =============================
+         Item Kind                 Description
+         ========================  =============================
+         ``ITEM_CONTROL``          The item in the `AuiToolBar` is a control
+         ``ITEM_LABEL``            The item in the `AuiToolBar` is a text label
+         ``ITEM_SPACER``           The item in the `AuiToolBar` is a spacer
+         ``ITEM_SEPARATOR``        The item in the `AuiToolBar` is a separator
+         ``ITEM_CHECK``            The item in the `AuiToolBar` is a toolbar check item
+         ``ITEM_NORMAL``           The item in the `AuiToolBar` is a standard toolbar item
+         ``ITEM_RADIO``            The item in the `AuiToolBar` is a toolbar radio item
+         ========================  =============================
+        """
+        
+        return self.AddTool(tool_id, label, bitmap, wx.NullBitmap, kind, short_help_string, "", None)
+
+
+    def AddToggleTool(self, tool_id, bitmap, disabled_bitmap, toggle=False, client_data=None, short_help_string="", long_help_string=""):
+        """
+        Adds a toggle tool to the toolbar. 
+
+        :param `tool_id`: an integer by which the tool may be identified in subsequent operations;
+        :param `bitmap`: the primary tool bitmap;
+        :param `disabled_bitmap`: the bitmap to use when the tool is disabled. If it is equal to
+         `wx.NullBitmap`, the disabled bitmap is automatically generated by greing the normal one;
+        :param `client_data`: whatever Python object to associate with the toolbar item;
+        :param `short_help_string`: this string is used for the tools tooltip;
+        :param `long_help_string`: this string is shown in the statusbar (if any) of the parent
+         frame when the mouse pointer is inside the tool.
+        """
+
+        kind = (toggle and [ITEM_CHECK] or [ITEM_NORMAL])[0]
+        return self.AddTool(tool_id, "", bitmap, disabled_bitmap, kind, short_help_string, long_help_string, client_data)
+
+
+    def AddTool(self, tool_id, label, bitmap, disabled_bitmap, kind, short_help_string, long_help_string, client_data):
+        """
+        Adds a tool to the toolbar. This is the full feature version of L{AddTool}.
+
+        :param `tool_id`: an integer by which the tool may be identified in subsequent operations;
+        :param `label`: the toolbar tool label;
+        :param `bitmap`: the primary tool bitmap;
+        :param `disabled_bitmap`: the bitmap to use when the tool is disabled. If it is equal to
+         `wx.NullBitmap`, the disabled bitmap is automatically generated by greing the normal one;
+        :param `kind`: the item kind. Can be one of the following:
+
+         ========================  =============================
+         Item Kind                 Description
+         ========================  =============================
+         ``ITEM_CONTROL``          The item in the `AuiToolBar` is a control
+         ``ITEM_LABEL``            The item in the `AuiToolBar` is a text label
+         ``ITEM_SPACER``           The item in the `AuiToolBar` is a spacer
+         ``ITEM_SEPARATOR``        The item in the `AuiToolBar` is a separator
+         ``ITEM_CHECK``            The item in the `AuiToolBar` is a toolbar check item
+         ``ITEM_NORMAL``           The item in the `AuiToolBar` is a standard toolbar item
+         ``ITEM_RADIO``            The item in the `AuiToolBar` is a toolbar radio item
+         ========================  =============================
+
+        :param `short_help_string`: this string is used for the tools tooltip;
+        :param `long_help_string`: this string is shown in the statusbar (if any) of the parent
+         frame when the mouse pointer is inside the tool.
+        :param `client_data`: whatever Python object to associate with the toolbar item.
+        """
+        
+        item = AuiToolBarItem()
+        item.window = None
+        item.label = label
+        item.bitmap = bitmap
+        item.disabled_bitmap = disabled_bitmap
+        item.short_help = short_help_string
+        item.long_help = long_help_string
+        item.active = True
+        item.dropdown = False
+        item.spacer_pixels = 0
+
+        if tool_id == wx.ID_ANY:
+            tool_id = wx.NewId()
+            
+        item.id = tool_id
+        item.state = 0
+        item.proportion = 0
+        item.kind = kind
+        item.sizer_item = None
+        item.min_size = wx.Size(-1, -1)
+        item.user_data = 0
+        item.sticky = False
+        item.orientation = self._tool_orientation
+
+        if not item.disabled_bitmap.IsOk():
+            # no disabled bitmap specified, we need to make one
+            if item.bitmap.IsOk():
+                item.disabled_bitmap = MakeDisabledBitmap(item.bitmap)
+        
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def AddCheckTool(self, tool_id, label, bitmap, disabled_bitmap, short_help_string="", long_help_string="", client_data=None):
+        """
+        Adds a new check (or toggle) tool to the L{AuiToolBar}.
+        
+        :see: L{AddTool}.
+        """
+
+        return self.AddTool(tool_id, label, bitmap, disabled_bitmap, ITEM_CHECK, short_help_string, long_help_string, client_data) 
+
+
+    def AddRadioTool(self, tool_id, label, bitmap, disabled_bitmap, short_help_string="", long_help_string="", client_data=None):
+        """
+        Adds a new radio tool to the toolbar.
+
+        Consecutive radio tools form a radio group such that exactly one button
+        in the group is pressed at any moment, in other words whenever a button
+        in the group is pressed the previously pressed button is automatically
+        released. You should avoid having the radio groups of only one element
+        as it would be impossible for the user to use such button.
+
+        :note: By default, the first button in the radio group is initially pressed,
+         the others are not.
+
+        :see: L{AddTool}.
+        """
+
+        return self.AddTool(tool_id, label, bitmap, disabled_bitmap, ITEM_RADIO, short_help_string, long_help_string, client_data)
+
+    
+    def AddControl(self, control, label=""):
+        """
+        Adds any control to the toolbar, typically e.g. a combobox.
+
+        :param `control`: the control to be added;
+        :param `label`: the label which appears if the control goes into the
+         overflow items in the toolbar.
+        """
+
+        item = AuiToolBarItem()
+        item.window = control
+        item.label = label
+        item.bitmap = wx.NullBitmap
+        item.disabled_bitmap = wx.NullBitmap
+        item.active = True
+        item.dropdown = False
+        item.spacer_pixels = 0
+        item.id = control.GetId()
+        item.state = 0
+        item.proportion = 0
+        item.kind = ITEM_CONTROL
+        item.sizer_item = None
+        item.min_size = control.GetEffectiveMinSize()
+        item.user_data = 0
+        item.sticky = False
+        item.orientation = self._tool_orientation
+
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def AddLabel(self, tool_id, label="", width=0):
+        """
+        Adds a label tool to the L{AuiToolBar}.
+
+        :param `tool_id`: an integer by which the tool may be identified in subsequent operations;
+        :param `label`: the toolbar tool label;
+        :param `width`: the tool width.
+        """
+
+        min_size = wx.Size(-1, -1)
+        
+        if width != -1:
+            min_size.x = width
+
+        item = AuiToolBarItem()
+        item.window = None
+        item.label = label
+        item.bitmap = wx.NullBitmap
+        item.disabled_bitmap = wx.NullBitmap
+        item.active = True
+        item.dropdown = False
+        item.spacer_pixels = 0
+
+        if tool_id == wx.ID_ANY:
+            tool_id = wx.NewId()
+        
+        item.id = tool_id
+        item.state = 0
+        item.proportion = 0
+        item.kind = ITEM_LABEL
+        item.sizer_item = None
+        item.min_size = min_size
+        item.user_data = 0
+        item.sticky = False
+        item.orientation = self._tool_orientation
+
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def AddSeparator(self):
+        """ Adds a separator for spacing groups of tools. """
+        
+        item = AuiToolBarItem()
+        item.window = None
+        item.label = ""
+        item.bitmap = wx.NullBitmap
+        item.disabled_bitmap = wx.NullBitmap
+        item.active = True
+        item.dropdown = False
+        item.id = -1
+        item.state = 0
+        item.proportion = 0
+        item.kind = ITEM_SEPARATOR
+        item.sizer_item = None
+        item.min_size = wx.Size(-1, -1)
+        item.user_data = 0
+        item.sticky = False
+        item.orientation = self._tool_orientation
+
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def AddSpacer(self, pixels):
+        """
+        Adds a spacer for spacing groups of tools.
+
+        :param `pixels`: the width of the spacer.
+        """
+
+        item = AuiToolBarItem()
+        item.window = None
+        item.label = ""
+        item.bitmap = wx.NullBitmap
+        item.disabled_bitmap = wx.NullBitmap
+        item.active = True
+        item.dropdown = False
+        item.spacer_pixels = pixels
+        item.id = -1
+        item.state = 0
+        item.proportion = 0
+        item.kind = ITEM_SPACER
+        item.sizer_item = None
+        item.min_size = wx.Size(-1, -1)
+        item.user_data = 0
+        item.sticky = False
+        item.orientation = self._tool_orientation
+
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def AddStretchSpacer(self, proportion=1):
+        """
+        Adds a stretchable spacer for spacing groups of tools.
+
+        :param `proportion`: the stretchable spacer proportion.
+        """
+        
+        item = AuiToolBarItem()
+        item.window = None
+        item.label = ""
+        item.bitmap = wx.NullBitmap
+        item.disabled_bitmap = wx.NullBitmap
+        item.active = True
+        item.dropdown = False
+        item.spacer_pixels = 0
+        item.id = -1
+        item.state = 0
+        item.proportion = proportion
+        item.kind = ITEM_SPACER
+        item.sizer_item = None
+        item.min_size = wx.Size(-1, -1)
+        item.user_data = 0
+        item.sticky = False
+        item.orientation = self._tool_orientation
+
+        self._items.append(item)
+        return self._items[-1]
+
+
+    def Clear(self):
+        """ Deletes all the tools in the L{AuiToolBar}. """
+
+        self._items = []
+        self._sizer_element_count = 0
+
+
+    def ClearTools(self):
+        """ Deletes all the tools in the L{AuiToolBar}. """
+
+        self.Clear()
+        
+
+    def DeleteTool(self, tool_id):
+        """
+        Removes the specified tool from the toolbar and deletes it.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier.
+
+        :returns: ``True`` if the tool was deleted, ``False`` otherwise.
+        
+        :note: Note that it is unnecessary to call L{Realize} for the change to
+         take place, it will happen immediately.
+        """
+
+        idx = self.GetToolIndex(tool_id)
+        
+        if idx >= 0 and idx < len(self._items):
+            self._items.pop(idx)
+            self.Realize()
+            return True
+        
+        return False
+
+
+    def DeleteToolByPos(self, pos):
+        """
+        This function behaves like L{DeleteTool} but it deletes the tool at the
+        specified position and not the one with the given id.
+
+        :param `pos`: the tool position.
+
+        :see: L{DeleteTool}        
+        """
+        
+        if pos >= 0 and pos < len(self._items):
+            
+            self._items.pop(pos)
+            self.Realize()
+            return True
+
+        return False
+
+
+    def FindControl(self, id):
+        """
+        Returns a pointer to the control identified by `id` or ``None`` if no corresponding
+        control is found.
+
+        :param `id`: the control identifier.        
+        """
+        
+        wnd = self.FindWindow(id)
+        return wnd
+
+
+    def FindTool(self, tool_id):
+        """
+        Finds a tool for the given tool id.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier.
+        """
+        
+        for item in self._items:
+            if item.id == tool_id:
+                return item
+    
+        return None
+
+
+    def FindToolForPosition(self, x, y):
+        """
+        Finds a tool for the given mouse position.
+
+        :param `x`: mouse `x` position;
+        :param `y`: mouse `y` position.
+
+        :returns: a pointer to a L{AuiToolBarItem} if a tool is found, or ``None`` otherwise.
+        """
+
+        for i, item in enumerate(self._items):
+            if not item.sizer_item:
+                continue
+
+            rect = item.sizer_item.GetRect()
+            if rect.Contains((x,y)):
+            
+                # if the item doesn't fit on the toolbar, return None
+                if not self.GetToolFitsByIndex(i):
+                    return None
+
+                return item
+            
+        return None
+
+
+    def FindToolForPositionWithPacking(self, x, y):
+        """
+        Finds a tool for the given mouse position, taking into account also the
+        tool packing.
+
+        :param `x`: mouse `x` position;
+        :param `y`: mouse `y` position.
+
+        :returns: a pointer to a L{AuiToolBarItem} if a tool is found, or ``None`` otherwise.
+        """
+        
+        count = len(self._items)
+        
+        for i, item in enumerate(self._items):
+            if not item.sizer_item:
+                continue
+
+            rect = item.sizer_item.GetRect()
+
+            # apply tool packing
+            if i+1 < count:
+                rect.width += self._tool_packing
+
+            if rect.Contains((x,y)):
+            
+                # if the item doesn't fit on the toolbar, return None
+                if not self.GetToolFitsByIndex(i):
+                    return None
+
+                return item
+
+        return None
+
+
+    def FindToolByIndex(self, pos):
+        """
+        Finds a tool for the given tool position in the L{AuiToolBar}.
+
+        :param `pos`: the tool position in the toolbar.
+
+        :returns: a pointer to a L{AuiToolBarItem} if a tool is found, or ``None`` otherwise.        
+        """
+        
+        if pos < 0 or pos >= len(self._items):
+            return None
+
+        return self._items[pos]
+
+
+    def SetToolBitmapSize(self, size):
+        """
+        Sets the default size of each tool bitmap. The default bitmap size is
+        16 by 15 pixels.
+
+        :param `size`: the size of the bitmaps in the toolbar.
+
+        :note: This should be called to tell the toolbar what the tool bitmap
+         size is. Call it before you add tools.
+
+        :note: Note that this is the size of the bitmap you pass to L{AddTool},
+         and not the eventual size of the tool button.
+
+        :todo: Add `wx.ToolBar` compatibility, actually implementing this method.
+        """
+
+        # TODO: wx.ToolBar compatibility
+        pass
+
+
+    def GetToolBitmapSize(self):
+        """
+        Returns the size of bitmap that the toolbar expects to have. The default
+        bitmap size is 16 by 15 pixels.
+
+        :note: Note that this is the size of the bitmap you pass to L{AddTool},
+         and not the eventual size of the tool button.
+
+        :todo: Add `wx.ToolBar` compatibility, actually implementing this method.
+        """
+        
+        # TODO: wx.ToolBar compatibility
+        return wx.Size(16, 15)
+
+
+    def SetToolProportion(self, tool_id, proportion):
+        """
+        Sets the tool proportion in the toolbar.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier;
+        :param `proportion`: the tool proportion in the toolbar.
+        """
+
+        item = self.FindTool(tool_id)
+        if not item:
+            return
+
+        item.proportion = proportion
+
+
+    def GetToolProportion(self, tool_id):
+        """
+        Returns the tool proportion in the toolbar.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier.
+        """
+
+        item = self.FindTool(tool_id)
+        if not item:
+            return
+
+        return item.proportion
+
+
+    def SetToolSeparation(self, separation):
+        """
+        Sets the separator size for the toolbar.
+
+        :param `separation`: the separator size in pixels.
+        """
+
+        if self._art:
+            self._art.SetElementSize(AUI_TBART_SEPARATOR_SIZE, separation)
+
+
+    def GetToolSeparation(self):
+        """ Returns the separator size for the toolbar, in pixels. """
+        
+        if self._art:
+            return self._art.GetElementSize(AUI_TBART_SEPARATOR_SIZE)
+
+        return 5
+
+
+    def SetToolDropDown(self, tool_id, dropdown):
+        """
+        Assigns a drop down window menu to the toolbar item.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier;
+        :param `dropdown`: whether to assign a drop down menu or not.
+        """
+
+        item = self.FindTool(tool_id)
+        if not item:
+            return
+
+        item.dropdown = dropdown
+
+
+    def GetToolDropDown(self, tool_id):
+        """
+        Returns whether the toolbar item identified by `tool_id` has an associated
+        drop down window menu or not.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier.
+        """
+
+        item = self.FindTool(tool_id)
+        if not item:
+            return
+
+        return item.dropdown
+
+
+    def SetToolSticky(self, tool_id, sticky):
+        """
+        Sets the toolbar item as sticky or non-sticky.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier;
+        :param `sticky`: whether the tool should be sticky or not.
+        """
+
+        # ignore separators
+        if tool_id == -1:
+            return
+
+        item = self.FindTool(tool_id)
+        if not item:
+            return
+
+        if item.sticky == sticky:
+            return
+
+        item.sticky = sticky
+
+        self.Refresh(False)
+        self.Update()
+
+
+    def GetToolSticky(self, tool_id):
+        """
+        Returns whether the toolbar item identified by `tool_id` has a sticky
+        behaviour or not.
+
+        :param `tool_id`: the L{AuiToolBarItem} identifier.
+        """
+
+        item = self.FindTool(tool_id)
+        if not item:
+            return
+
+        return item.sticky
+
+
+    def SetToolBorderPadding(self, padding):
+        """
+        Sets the padding between the tool border and the label.
+
+        :param `padding`: the padding in pixels.
+        """
+
+        self._tool_border_padding = padding
+
+
+    def GetToolBorderPadding(self):
+        """ Returns the padding between the tool border and the label, in pixels. """
+
+        return self._tool_border_padding
+
+
+    def SetToolTextOrientation(self, orientation):
+        """
+        Sets the label orientation for the toolbar items.
+
+        :param `orientation`: the L{AuiToolBarItem} label orientation.
+        """
+
+        self._tool_text_orientation = orientation
+
+        if self._art:
+            self._art.SetTextOrientation(orientation)
+    
+
+    def GetToolTextOrientation(self):
+        """ Returns the label orientation for the toolbar items. """
+
+        return self._tool_text_orientation
+
+
+    def SetToolOrientation(self, orientation):
+        """
+        Sets the tool orientation for the toolbar items.
+
+        :param `orientation`: the L{AuiToolBarItem} orientation.
+        """
+
+        self._tool_orientation = orientation
+        if self._art:
+            self._art.SetOrientation(orientation)
+
+
+    def GetToolOrientation(self):
+        """ Returns the orientation for the toolbar items. """
+
+        return self._tool_orientation        
+
+
+    def SetToolPacking(self, packing):
+        """
+        Sets the value used for spacing tools. The default value is 1 pixel.
+
+        :param `packing`: the value for packing.
+        """
+
+        self._tool_packing = packing
+
+
+    def GetToolPacking(self):
+        """ Returns the value used for spacing tools. The default value is 1 pixel. """
+
+        return self._tool_packing
+
+
+    def SetOrientation(self, orientation):
+        """
+        Sets the toolbar orientation.
+
+        :param `orientation`: either ``wx.VERTICAL`` or ``wx.HORIZONTAL``.
+
+        :note: This can be temporarily overridden by L{AuiManager} when floating and
+         docking a L{AuiToolBar}.
+        """
+
+        pass
+    
+
+    def SetMargins(self, left=-1, right=-1, top=-1, bottom=-1):
+        """
+        Set the values to be used as margins for the toolbar.
+
+        :param `left`: the left toolbar margin;
+        :param `right`: the right toolbar margin;
+        :param `top`: the top toolbar margin;
+        :param `bottom`: the bottom toolbar margin.
+        """
+
+        if left != -1:
+            self._left_padding = left
+        if right != -1:
+            self._right_padding = right
+        if top != -1:
+            self._top_padding = top
+        if bottom != -1:
+            self._bottom_padding = bottom
+
+
+    def SetMarginsSize(self, size):
+        """
+        Set the values to be used as margins for the toolbar.
+
+        :param `size`: the margin size (an instance of `wx.Size`).
+        """
+        
+        self.SetMargins(size.x, size.x, size.y, size.y)
+
+
+    def SetMarginsXY(self, x, y):
+        """
+        Set the values to be used as margins for the toolbar.
+        
+        :param `x`: left margin, right margin and inter-tool separation value;
+        :param `y`: top margin, bottom margin and inter-tool separation value.
+        """
+        
+        self.SetMargins(x, x, y, y)        
+
+            
+    def GetGripperVisible(self):
+        """ Returns whether the toolbar gripper is visible or not. """
+
+        return self._gripper_visible
+
+
+    def SetGripperVisible(self, visible):
+        """
+        Sets whether the toolbar gripper is visible or not.
+
+        :param `visible`: ``True`` for a visible gripper, ``False`` otherwise.
+        """
+
+        self._gripper_visible = visible
+        if visible:
+            self._agwStyle |= AUI_TB_GRIPPER
+        else:
+            self._agwStyle &= ~AUI_TB_GRIPPER
+            
+        self.Realize()
+        self.Refresh(False)
+
+
+    def GetOverflowVisible(self):
+        """ Returns whether the overflow button is visible or not. """
+
+        return self._overflow_visible
+
+
+    def SetOverflowVisible(self, visible):
+        """
+        Sets whether the overflow button is visible or not.
+
+        :param `visible`: ``True`` for a visible overflow button, ``False`` otherwise.
+        """
+
+        self._overflow_visible = visible
+        if visible:
+            self._agwStyle |= AUI_TB_OVERFLOW
+        else:
+            self._agwStyle &= ~AUI_TB_OVERFLOW
+
+        self.Refresh(False)
+
+
+    def SetFont(self, font):
+        """
+        Sets the L{AuiToolBar} font.
+
+        :param `font`: a `wx.Font` object.
+
+        :note: Overridden from `wx.PyControl`.
+        """        
+
+        res = wx.PyControl.SetFont(self, font)
+
+        if self._art:
+            self._art.SetFont(font)
+    
+        return res
+
+
+    def SetHoverItem(self, pitem):
+        """
+        Sets a toolbar item to be currently hovered by the mouse.
+
+        :param `pitem`: an instance of L{AuiToolBarItem}.
+        """
+
+        former_hover = None
+
+        for item in self._items:
+        
+            if item.state & AUI_BUTTON_STATE_HOVER:
+                former_hover = item
+                
+            item.state &= ~AUI_BUTTON_STATE_HOVER
+
+        if pitem:
+            pitem.state |= AUI_BUTTON_STATE_HOVER
+        
+        if former_hover != pitem:
+            self.Refresh(False)
+            self.Update()
+        
+
+    def SetPressedItem(self, pitem):
+        """
+        Sets a toolbar item to be currently in a "pressed" state.
+
+        :param `pitem`: an instance of L{AuiToolBarItem}.
+        """
+
+        former_item = None
+
+        for item in self._items:
+        
+            if item.state & AUI_BUTTON_STATE_PRESSED:
+                former_item = item
+                
+            item.state &= ~AUI_BUTTON_STATE_PRESSED
+        
+        if pitem:
+            pitem.state &= ~AUI_BUTTON_STATE_HOVER
+            pitem.state |= AUI_BUTTON_STATE_PRESSED
+        
+        if former_item != pitem:
+            self.Refresh(False)
+            self.Update()
+    
+
+    def RefreshOverflowState(self):
+        """ Refreshes the overflow button. """
+
+        if not self._overflow_sizer_item:
+            self._overflow_state = 0
+            return
+        
+        overflow_state = 0
+        overflow_rect = self.GetOverflowRect()
+
+        # find out the mouse's current position
+        pt = wx.GetMousePosition()
+        pt = self.ScreenToClient(pt)
+
+        # find out if the mouse cursor is inside the dropdown rectangle
+        if overflow_rect.Contains((pt.x, pt.y)):
+
+            if _VERSION_STRING < "2.9":
+                leftDown = wx.GetMouseState().LeftDown()
+            else:
+                leftDown = wx.GetMouseState().LeftIsDown()
+        
+            if leftDown:
+                overflow_state = AUI_BUTTON_STATE_PRESSED
+            else:
+                overflow_state = AUI_BUTTON_STATE_HOVER
+        
+        if overflow_state != self._overflow_state:
+            self._overflow_state = overflow_state
+            self.Refresh(False)
+            self.Update()
+        
+        self._overflow_state = overflow_state
+
+
+    def ToggleTool(self, tool_id, state):
+        """
+        Toggles a tool on or off. This does not cause any event to get emitted.
+
+        :param `tool_id`: tool in question.
+        :param `state`: if ``True``, toggles the tool on, otherwise toggles it off.
+
+        :note: This only applies to a tool that has been specified as a toggle tool.
+        """
+        
+        tool = self.FindTool(tool_id)
+
+        if tool:
+            if tool.kind not in [ITEM_CHECK, ITEM_RADIO]:
+                return
+
+            if tool.kind == ITEM_RADIO:
+                idx = self.GetToolIndex(tool_id)
+                if idx >= 0 and idx < len(self._items):
+                    for i in xrange(idx, len(self._items)):
+                        tool = self.FindToolByIndex(i)
+                        if tool.kind != ITEM_RADIO:
+                            break
+                        tool.state &= ~AUI_BUTTON_STATE_CHECKED
+
+                    for i in xrange(idx, -1, -1):
+                        tool = self.FindToolByIndex(i)
+                        if tool.kind != ITEM_RADIO:
+                            break
+                        tool.state &= ~AUI_BUTTON_STATE_CHECKED 
+
+                    tool = self.FindTool(tool_id)
+                    tool.state |= AUI_BUTTON_STATE_CHECKED
+            else:
+                if state == True:
+                    tool.state |= AUI_BUTTON_STATE_CHECKED
+                else:
+                    tool.state &= ~AUI_BUTTON_STATE_CHECKED 
+
+
+    def GetToolToggled(self, tool_id):
+        """
+        Returns whether a tool is toggled or not.
+
+        :param `tool_id`: the toolbar item identifier.
+
+        :note: This only applies to a tool that has been specified as a toggle tool.
+        """        
+
+        tool = self.FindTool(tool_id)
+
+        if tool:
+            if tool.kind not in [ITEM_CHECK, ITEM_RADIO]:
+                return False
+
+            return (tool.state & AUI_BUTTON_STATE_CHECKED and [True] or [False])[0]
+        
+        return False
+
+
+    def EnableTool(self, tool_id, state):
+        """
+        Enables or disables the tool.
+
+        :param `tool_id`: identifier for the tool to enable or disable.
+        :param `state`: if ``True``, enables the tool, otherwise disables it.
+        """
+
+        tool = self.FindTool(tool_id)
+
+        if tool:
+        
+            if state == True:
+                tool.state &= ~AUI_BUTTON_STATE_DISABLED
+            else:
+                tool.state |= AUI_BUTTON_STATE_DISABLED
+        
+
+    def GetToolEnabled(self, tool_id):
+        """
+        Returns whether the tool identified by `tool_id` is enabled or not.
+
+        :param `tool_id`: the tool identifier.
+        """
+
+        tool = self.FindTool(tool_id)
+
+        if tool:
+            return (tool.state & AUI_BUTTON_STATE_DISABLED and [False] or [True])[0]
+
+        return False
+
+
+    def GetToolLabel(self, tool_id):
+        """
+        Returns the tool label for the tool identified by `tool_id`.
+
+        :param `tool_id`: the tool identifier.
+        """
+
+        tool = self.FindTool(tool_id)
+        if not tool:
+            return ""
+        
+        return tool.label
+
+
+    def SetToolLabel(self, tool_id, label):
+        """
+        Sets the tool label for the tool identified by `tool_id`.
+
+        :param `tool_id`: the tool identifier;
+        :param `label`: the new toolbar item label.
+        """
+        
+        tool = self.FindTool(tool_id)
+        if tool:    
+            tool.label = label
+    
+
+    def GetToolBitmap(self, tool_id):
+        """
+        Returns the tool bitmap for the tool identified by `tool_id`.
+
+        :param `tool_id`: the tool identifier.
+        """
+        
+        tool = self.FindTool(tool_id)
+        if not tool:
+            return wx.NullBitmap
+
+        return tool.bitmap
+
+
+    def SetToolBitmap(self, tool_id, bitmap):
+        """
+        Sets the tool bitmap for the tool identified by `tool_id`.
+
+        :param `tool_id`: the tool identifier;
+        :param `bitmap`: the new bitmap for the toolbar item (an instance of `wx.Bitmap`).
+        """
+        
+        tool = self.FindTool(tool_id)
+        if tool:
+            tool.bitmap = bitmap
+
+
+    def SetToolNormalBitmap(self, tool_id, bitmap):
+        """
+        Sets the tool bitmap for the tool identified by `tool_id`.
+
+        :param `tool_id`: the tool identifier;
+        :param `bitmap`: the new bitmap for the toolbar item (an instance of `wx.Bitmap`).
+        """
+        
+        self.SetToolBitmap(tool_id, bitmap)
+
+
+    def SetToolDisabledBitmap(self, tool_id, bitmap):
+        """
+        Sets the tool disabled bitmap for the tool identified by `tool_id`.
+
+        :param `tool_id`: the tool identifier;
+        :param `bitmap`: the new disabled bitmap for the toolbar item (an instance of `wx.Bitmap`).
+        """
+        
+        tool = self.FindTool(tool_id)
+        if tool:
+            tool.disabled_bitmap = bitmap
+
+
+    def GetToolShortHelp(self, tool_id):
+        """
+        Returns the short help for the given tool.
+
+        :param `tool_id`: the tool identifier.
+        """
+
+        tool = self.FindTool(tool_id)
+        if not tool:
+            return ""
+
+        return tool.short_help
+
+
+    def SetToolShortHelp(self, tool_id, help_string):
+        """
+        Sets the short help for the given tool.
+
+        :param `tool_id`: the tool identifier;
+        :param `help_string`: the string for the short help.
+        """
+        
+        tool = self.FindTool(tool_id)
+        if tool:
+            tool.short_help = help_string
+
+
+    def GetToolLongHelp(self, tool_id):
+        """
+        Returns the long help for the given tool.
+
+        :param `tool_id`: the tool identifier.
+        """
+
+        tool = self.FindTool(tool_id)
+        if not tool:
+            return ""
+
+        return tool.long_help
+
+
+    def SetToolAlignment(self, alignment=wx.EXPAND):
+        """
+        This sets the alignment for all of the tools within the
+        toolbar (only has an effect when the toolbar is expanded).
+
+        :param `alignment`: `wx.Sizer` alignment value
+         (``wx.ALIGN_CENTER_HORIZONTAL`` or ``wx.ALIGN_CENTER_VERTICAL``).
+        """
+
+        self._tool_alignment = alignment
+
+
+
+    def SetToolLongHelp(self, tool_id, help_string):
+        """
+        Sets the long help for the given tool.
+
+        :param `tool_id`: the tool identifier;
+        :param `help_string`: the string for the long help.
+        """
+        
+        tool = self.FindTool(tool_id)
+        if tool:
+            tool.long_help = help_string
+    
+
+    def SetCustomOverflowItems(self, prepend, append):
+        """
+        Sets the two lists `prepend` and `append` as custom overflow items.
+
+        :param `prepend`: a list of L{AuiToolBarItem} to be prepended;
+        :param `append`: a list of L{AuiToolBarItem} to be appended.
+        """
+
+        self._custom_overflow_prepend = prepend
+        self._custom_overflow_append = append
+
+
+    def GetToolCount(self):
+        """ Returns the number of tools in the L{AuiToolBar}. """
+
+        return len(self._items)
+
+
+    def GetToolIndex(self, tool_id):
+        """
+        Returns the position of the tool in the toolbar given its identifier.
+
+        :param `tool_id`: the toolbar item identifier.
+        """
+
+        # this will prevent us from returning the index of the
+        # first separator in the toolbar since its id is equal to -1
+        if tool_id == -1:
+            return wx.NOT_FOUND
+
+        for i, item in enumerate(self._items):
+            if item.id == tool_id:
+                return i
+        
+        return wx.NOT_FOUND
+
+
+    def GetToolPos(self, tool_id):
+        """
+        Returns the position of the tool in the toolbar given its identifier.
+
+        :param `tool_id`: the toolbar item identifier.
+        """
+        
+        return self.GetToolIndex(tool_id)
+                                
+
+    def GetToolFitsByIndex(self, tool_id):
+        """
+        Returns whether the tool identified by `tool_id` fits into the toolbar or not.
+
+        :param `tool_id`: the toolbar item identifier.
+        """
+        
+        if tool_id < 0 or tool_id >= len(self._items):
+            return False
+
+        if not self._items[tool_id].sizer_item:
+            return False
+
+        cli_w, cli_h = self.GetClientSize()
+        rect = self._items[tool_id].sizer_item.GetRect()
+
+        if self._agwStyle & AUI_TB_VERTICAL:
+            # take the dropdown size into account
+            if self._overflow_visible:
+                cli_h -= self._overflow_sizer_item.GetSize().y
+
+            if rect.y+rect.height < cli_h:
+                return True
+        
+        else:
+        
+            # take the dropdown size into account
+            if self._overflow_visible:
+                cli_w -= self._overflow_sizer_item.GetSize().x
+
+            if rect.x+rect.width < cli_w:
+                return True
+        
+        return False
+
+
+    def GetToolFits(self, tool_id):
+        """
+        Returns whether the tool identified by `tool_id` fits into the toolbar or not.
+
+        :param `tool_id`: the toolbar item identifier.
+        """
+        
+        return self.GetToolFitsByIndex(self.GetToolIndex(tool_id))
+
+
+    def GetToolRect(self, tool_id):
+        """
+        Returns the toolbar item rectangle
+
+        :param `tool_id`: the toolbar item identifier.
+        """
+
+        tool = self.FindTool(tool_id)
+        if tool and tool.sizer_item:
+            return tool.sizer_item.GetRect()
+
+        return wx.Rect()
+
+
+    def GetToolBarFits(self):
+        """ Returns whether the L{AuiToolBar} size fits in a specified size. """
+
+        if len(self._items) == 0:
+            # empty toolbar always 'fits'
+            return True
+        
+        # entire toolbar content fits if the last tool fits
+        return self.GetToolFitsByIndex(len(self._items) - 1)
+
+
+    def Realize(self):
+        """ Realizes the toolbar. This function should be called after you have added tools. """
+
+        dc = wx.ClientDC(self)
+        
+        if not dc.IsOk():
+            return False
+
+        horizontal = True
+        if self._agwStyle & AUI_TB_VERTICAL:
+            horizontal = False
+
+        # create the new sizer to add toolbar elements to
+        sizer = wx.BoxSizer((horizontal and [wx.HORIZONTAL] or [wx.VERTICAL])[0])
+
+        # add gripper area
+        separator_size = self._art.GetElementSize(AUI_TBART_SEPARATOR_SIZE)
+        gripper_size = self._art.GetElementSize(AUI_TBART_GRIPPER_SIZE)
+        
+        if gripper_size > 0 and self._gripper_visible:        
+            if horizontal:
+                self._gripper_sizer_item = sizer.Add((gripper_size, 1), 0, wx.EXPAND)
+            else:
+                self._gripper_sizer_item = sizer.Add((1, gripper_size), 0, wx.EXPAND)
+        else:
+            self._gripper_sizer_item = None
+        
+        # add "left" padding
+        if self._left_padding > 0:
+            if horizontal:
+                sizer.Add((self._left_padding, 1))
+            else:
+                sizer.Add((1, self._left_padding))
+        
+        count = len(self._items)
+        for i, item in enumerate(self._items):
+        
+            sizer_item = None
+            kind = item.kind
+
+            if kind == ITEM_LABEL:
+                
+                size = self._art.GetLabelSize(dc, self, item)
+                sizer_item = sizer.Add((size.x + (self._tool_border_padding*2),
+                                        size.y + (self._tool_border_padding*2)),
+                                       item.proportion,
+                                       item.alignment)
+                if i+1 < count:
+                    sizer.AddSpacer(self._tool_packing)
+                
+
+            elif kind in [ITEM_CHECK, ITEM_NORMAL, ITEM_RADIO]:
+                
+                size = self._art.GetToolSize(dc, self, item)
+                sizer_item = sizer.Add((size.x + (self._tool_border_padding*2),
+                                        size.y + (self._tool_border_padding*2)),
+                                       0,
+                                       item.alignment)
+                # add tool packing
+                if i+1 < count:
+                    sizer.AddSpacer(self._tool_packing)
+
+            elif kind == ITEM_SEPARATOR:
+                
+                if horizontal:
+                    sizer_item = sizer.Add((separator_size, 1), 0, wx.EXPAND)
+                else:
+                    sizer_item = sizer.Add((1, separator_size), 0, wx.EXPAND)
+
+                # add tool packing
+                if i+1 < count:
+                    sizer.AddSpacer(self._tool_packing)
+
+            elif kind == ITEM_SPACER:
+                
+                if item.proportion > 0:
+                    sizer_item = sizer.AddStretchSpacer(item.proportion)
+                else:
+                    sizer_item = sizer.Add((item.spacer_pixels, 1))
+                    
+            elif kind == ITEM_CONTROL:
+                
+                vert_sizer = wx.BoxSizer(wx.VERTICAL)
+                vert_sizer.AddStretchSpacer(1)
+                ctrl_sizer_item = vert_sizer.Add(item.window, 0, wx.EXPAND)
+                vert_sizer.AddStretchSpacer(1)
+                
+                if self._agwStyle & AUI_TB_TEXT and \
+                    self._tool_text_orientation == AUI_TBTOOL_TEXT_BOTTOM and \
+                    item.GetLabel() != "":
+                
+                    s = self.GetLabelSize(item.GetLabel())
+                    vert_sizer.Add((1, s.y))
+
+                sizer_item = sizer.Add(vert_sizer, item.proportion, wx.EXPAND)
+                min_size = item.min_size
+
+                # proportional items will disappear from the toolbar if
+                # their min width is not set to something really small
+                if item.proportion != 0:
+                    min_size.x = 1
+                
+                if min_size.IsFullySpecified():
+                    sizer.SetItemMinSize(vert_sizer, min_size)
+                    vert_sizer.SetItemMinSize(item.window, min_size)
+                
+                # add tool packing
+                if i+1 < count:
+                    sizer.AddSpacer(self._tool_packing)
+                
+            item.sizer_item = sizer_item
+        
+
+        # add "right" padding
+        if self._right_padding > 0:
+            if horizontal:
+                sizer.Add((self._right_padding, 1))
+            else:
+                sizer.Add((1, self._right_padding))
+        
+        # add drop down area
+        self._overflow_sizer_item = None
+
+        if self._agwStyle & AUI_TB_OVERFLOW:
+        
+            overflow_size = self._art.GetElementSize(AUI_TBART_OVERFLOW_SIZE)
+            if overflow_size > 0 and self._overflow_visible:
+            
+                if horizontal:
+                    self._overflow_sizer_item = sizer.Add((overflow_size, 1), 0, wx.EXPAND)
+                else:
+                    self._overflow_sizer_item = sizer.Add((1, overflow_size), 0, wx.EXPAND)
+            
+            else:
+            
+                self._overflow_sizer_item = None
+            
+        # the outside sizer helps us apply the "top" and "bottom" padding
+        outside_sizer = wx.BoxSizer((horizontal and [wx.VERTICAL] or [wx.HORIZONTAL])[0])
+
+        # add "top" padding
+        if self._top_padding > 0:
+        
+            if horizontal:
+                outside_sizer.Add((1, self._top_padding))
+            else:
+                outside_sizer.Add((self._top_padding, 1))
+        
+        # add the sizer that contains all of the toolbar elements
+        outside_sizer.Add(sizer, 1, self._tool_alignment)
+
+        # add "bottom" padding
+        if self._bottom_padding > 0:
+        
+            if horizontal:
+                outside_sizer.Add((1, self._bottom_padding))
+            else:
+                outside_sizer.Add((self._bottom_padding, 1))
+
+        del self._sizer # remove old sizer
+        self._sizer = outside_sizer
+        self.SetSizer(outside_sizer)
+
+        # calculate the rock-bottom minimum size
+        for item in self._items:
+        
+            if item.sizer_item and item.proportion > 0 and item.min_size.IsFullySpecified():
+                item.sizer_item.SetMinSize((0, 0))
+        
+        self._absolute_min_size = self._sizer.GetMinSize()
+
+        # reset the min sizes to what they were
+        for item in self._items:
+        
+            if item.sizer_item and item.proportion > 0 and item.min_size.IsFullySpecified():
+                item.sizer_item.SetMinSize(item.min_size)
+        
+        # set control size
+        size = self._sizer.GetMinSize()
+        self.SetMinSize(size)
+        self._minWidth = size.x
+        self._minHeight = size.y
+
+        if self._agwStyle & AUI_TB_NO_AUTORESIZE == 0:
+        
+            cur_size = self.GetClientSize()
+            new_size = self.GetMinSize()
+
+            if new_size != cur_size:
+            
+                self.SetClientSize(new_size)
+            
+            else:
+            
+                self._sizer.SetDimension(0, 0, cur_size.x, cur_size.y)
+            
+        else:
+        
+            cur_size = self.GetClientSize()
+            self._sizer.SetDimension(0, 0, cur_size.x, cur_size.y)
+                    
+        self.Refresh(False)
+        return True
+
+
+    def GetOverflowState(self):
+        """ Returns the state of the overflow button. """
+
+        return self._overflow_state
+
+
+    def GetOverflowRect(self):
+        """ Returns the rectangle of the overflow button. """
+
+        cli_rect = wx.RectPS(wx.Point(0, 0), self.GetClientSize())
+        overflow_rect = wx.Rect(*self._overflow_sizer_item.GetRect())
+        overflow_size = self._art.GetElementSize(AUI_TBART_OVERFLOW_SIZE)
+
+        if self._agwStyle & AUI_TB_VERTICAL:
+        
+            overflow_rect.y = cli_rect.height - overflow_size
+            overflow_rect.x = 0
+            overflow_rect.width = cli_rect.width
+            overflow_rect.height = overflow_size
+        
+        else:
+        
+            overflow_rect.x = cli_rect.width - overflow_size
+            overflow_rect.y = 0
+            overflow_rect.width = overflow_size
+            overflow_rect.height = cli_rect.height
+        
+        return overflow_rect
+
+
+    def GetLabelSize(self, label):
+        """
+        Returns the standard size of a toolbar item.
+
+        :param `label`: a test label.
+        """
+
+        dc = wx.ClientDC(self)
+        dc.SetFont(self._font)
+
+        return GetLabelSize(dc, label, self._tool_orientation != AUI_TBTOOL_HORIZONTAL)
+
+
+    def GetAuiManager(self):
+        """ Returns the L{AuiManager} which manages the toolbar. """
+
+        try:
+            return self._auiManager
+        except AttributeError:
+            return False
+
+
+    def SetAuiManager(self, auiManager):
+        """ Sets the L{AuiManager} which manages the toolbar. """
+        
+        self._auiManager = auiManager        
+
+        
+    def DoIdleUpdate(self):
+        """ Updates the toolbar during idle times. """
+
+        handler = self.GetEventHandler()
+        if not handler:
+            return
+        
+        need_refresh = False
+
+        for item in self._items:
+                
+            if item.id == -1:
+                continue
+
+            evt = wx.UpdateUIEvent(item.id)
+            evt.SetEventObject(self)
+
+            if handler.ProcessEvent(evt):
+            
+                if evt.GetSetEnabled():
+                
+                    if item.window:
+                        is_enabled = item.window.IsEnabled()
+                    else:
+                        is_enabled = (item.state & AUI_BUTTON_STATE_DISABLED and [False] or [True])[0]
+
+                    new_enabled = evt.GetEnabled()
+                    if new_enabled != is_enabled:
+                    
+                        if item.window:
+                            item.window.Enable(new_enabled)
+                        else:
+                            if new_enabled:
+                                item.state &= ~AUI_BUTTON_STATE_DISABLED
+                            else:
+                                item.state |= AUI_BUTTON_STATE_DISABLED
+                        
+                        need_refresh = True
+                    
+                if evt.GetSetChecked():
+                
+                    # make sure we aren't checking an item that can't be
+                    if item.kind != ITEM_CHECK and item.kind != ITEM_RADIO:
+                        continue
+
+                    is_checked = (item.state & AUI_BUTTON_STATE_CHECKED and [True] or [False])[0]
+                    new_checked = evt.GetChecked()
+
+                    if new_checked != is_checked:
+                    
+                        if new_checked:
+                            item.state |= AUI_BUTTON_STATE_CHECKED
+                        else:
+                            item.state &= ~AUI_BUTTON_STATE_CHECKED
+
+                        need_refresh = True
+                    
+        if need_refresh:
+            self.Refresh(False)
+
+        
+    def OnSize(self, event):
+        """
+        Handles the ``wx.EVT_SIZE`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.SizeEvent` event to be processed.        
+        """
+        
+        x, y = self.GetClientSize()
+        realize = False
+
+        if x > y:
+            self.SetOrientation(wx.HORIZONTAL)
+        else:
+            self.SetOrientation(wx.VERTICAL)
+
+        if (x >= y and self._absolute_min_size.x > x) or (y > x and self._absolute_min_size.y > y):
+        
+            # hide all flexible items
+            for item in self._items:
+                if item.sizer_item and item.proportion > 0 and item.sizer_item.IsShown():
+                    item.sizer_item.Show(False)
+                    item.sizer_item.SetProportion(0)
+
+            if self._originalStyle & AUI_TB_OVERFLOW:
+                if not self.GetOverflowVisible():
+                    self.SetOverflowVisible(True)
+                    realize = True
+                       
+        else:
+
+            if self._originalStyle & AUI_TB_OVERFLOW and not self._custom_overflow_append and \
+               not self._custom_overflow_prepend:
+                if self.GetOverflowVisible():
+                    self.SetOverflowVisible(False)
+                    realize = True
+
+            # show all flexible items
+            for item in self._items:
+                if item.sizer_item and item.proportion > 0 and not item.sizer_item.IsShown():
+                    item.sizer_item.Show(True)
+                    item.sizer_item.SetProportion(item.proportion)
+                
+        self._sizer.SetDimension(0, 0, x, y)
+
+        if realize:
+            self.Realize()
+        else:
+            self.Refresh(False)
+            
+        self.Update()
+
+        
+    def DoSetSize(self, x, y, width, height, sizeFlags=wx.SIZE_AUTO):
+        """        
+        Sets the position and size of the window in pixels. The `sizeFlags`
+        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 `sizeFlags`: 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`.
+        """
+        
+        parent_size = self.GetParent().GetClientSize()
+        if x + width > parent_size.x:
+            width = max(0, parent_size.x - x)
+        if y + height > parent_size.y:
+            height = max(0, parent_size.y - y)
+
+        wx.PyControl.DoSetSize(self, x, y, width, height, sizeFlags)
+
+
+    def OnIdle(self, event):
+        """
+        Handles the ``wx.EVT_IDLE`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.IdleEvent` event to be processed.        
+        """
+        
+        self.DoIdleUpdate()
+        event.Skip()
+
+
+    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 self._absolute_min_size
+    
+
+    def OnPaint(self, event):
+        """
+        Handles the ``wx.EVT_PAINT`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.PaintEvent` event to be processed.        
+        """
+
+        dc = wx.AutoBufferedPaintDC(self)
+        cli_rect = wx.RectPS(wx.Point(0, 0), self.GetClientSize())
+
+        horizontal = True
+        if self._agwStyle & AUI_TB_VERTICAL:
+            horizontal = False
+
+        if self._agwStyle & AUI_TB_PLAIN_BACKGROUND:
+            self._art.DrawPlainBackground(dc, self, cli_rect)
+        else:
+            self._art.DrawBackground(dc, self, cli_rect, horizontal)
+
+        gripper_size = self._art.GetElementSize(AUI_TBART_GRIPPER_SIZE)
+        dropdown_size = self._art.GetElementSize(AUI_TBART_OVERFLOW_SIZE)
+
+        # paint the gripper
+        if gripper_size > 0 and self._gripper_sizer_item:
+            gripper_rect = wx.Rect(*self._gripper_sizer_item.GetRect())
+            if horizontal:
+                gripper_rect.width = gripper_size
+            else:
+                gripper_rect.height = gripper_size
+                
+            self._art.DrawGripper(dc, self, gripper_rect)
+        
+        # calculated how far we can draw items
+        if horizontal:
+            last_extent = cli_rect.width
+        else:
+            last_extent = cli_rect.height
+            
+        if self._overflow_visible:
+            last_extent -= dropdown_size
+
+        # paint each individual tool
+        for item in self._items:
+
+            if not item.sizer_item:
+                continue
+
+            item_rect = wx.Rect(*item.sizer_item.GetRect())
+
+            if (horizontal and item_rect.x + item_rect.width >= last_extent) or \
+               (not horizontal and item_rect.y + item_rect.height >= last_extent):
+
+                break
+            
+            if item.kind == ITEM_SEPARATOR:
+                # draw a separator
+                self._art.DrawSeparator(dc, self, item_rect)
+            
+            elif item.kind == ITEM_LABEL:
+                # draw a text label only
+                self._art.DrawLabel(dc, self, item, item_rect)
+            
+            elif item.kind == ITEM_NORMAL:
+                # draw a regular button or dropdown button
+                if not item.dropdown:
+                    self._art.DrawButton(dc, self, item, item_rect)
+                else:
+                    self._art.DrawDropDownButton(dc, self, item, item_rect)
+            
+            elif item.kind == ITEM_CHECK:
+                # draw a regular toggle button or a dropdown one
+                if not item.dropdown:
+                    self._art.DrawButton(dc, self, item, item_rect)
+                else:
+                    self._art.DrawDropDownButton(dc, self, item, item_rect)
+
+            elif item.kind == ITEM_RADIO:
+                # draw a toggle button
+                self._art.DrawButton(dc, self, item, item_rect)
+            
+            elif item.kind == ITEM_CONTROL:
+                # draw the control's label
+                self._art.DrawControlLabel(dc, self, item, item_rect)
+            
+            # fire a signal to see if the item wants to be custom-rendered
+            self.OnCustomRender(dc, item, item_rect)
+        
+        # paint the overflow button
+        if dropdown_size > 0 and self._overflow_sizer_item:
+            dropdown_rect = self.GetOverflowRect()
+            self._art.DrawOverflowButton(dc, self, dropdown_rect, self._overflow_state)
+
+        
+    def OnEraseBackground(self, event):
+        """
+        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.EraseEvent` event to be processed.
+
+        :note: This is intentionally empty, to reduce flicker.
+        """
+
+        pass
+    
+
+    def OnLeftDown(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_DOWN`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        cli_rect = wx.RectPS(wx.Point(0, 0), self.GetClientSize())
+        self.StopPreviewTimer()
+
+        if self._gripper_sizer_item:
+        
+            gripper_rect = wx.Rect(*self._gripper_sizer_item.GetRect())
+            if gripper_rect.Contains(event.GetPosition()):
+            
+                # find aui manager
+                manager = self.GetAuiManager()
+                if not manager:
+                    return
+
+                x_drag_offset = event.GetX() - gripper_rect.GetX()
+                y_drag_offset = event.GetY() - gripper_rect.GetY()
+
+                clientPt = wx.Point(*event.GetPosition())
+                screenPt = self.ClientToScreen(clientPt)
+                managedWindow = manager.GetManagedWindow()
+                managerClientPt = managedWindow.ScreenToClient(screenPt)
+
+                # gripper was clicked
+                manager.OnGripperClicked(self, managerClientPt, wx.Point(x_drag_offset, y_drag_offset))            
+                return
+
+        if self._overflow_sizer_item:
+            overflow_rect = self.GetOverflowRect()
+
+            if self._art and self._overflow_visible and overflow_rect.Contains(event.GetPosition()):
+            
+                e = AuiToolBarEvent(wxEVT_COMMAND_AUITOOLBAR_OVERFLOW_CLICK, -1)
+                e.SetEventObject(self)
+                e.SetToolId(-1)
+                e.SetClickPoint(event.GetPosition())
+                processed = self.ProcessEvent(e)
+
+                if processed:
+                    self.DoIdleUpdate()
+                else:                
+                    overflow_items = []
+
+                    # add custom overflow prepend items, if any
+                    count = len(self._custom_overflow_prepend)
+                    for i in xrange(count):
+                        overflow_items.append(self._custom_overflow_prepend[i])
+
+                    # only show items that don't fit in the dropdown
+                    count = len(self._items)
+                    for i in xrange(count):
+                    
+                        if not self.GetToolFitsByIndex(i):
+                            overflow_items.append(self._items[i])
+                    
+                    # add custom overflow append items, if any
+                    count = len(self._custom_overflow_append)
+                    for i in xrange(count):
+                        overflow_items.append(self._custom_overflow_append[i])
+
+                    res = self._art.ShowDropDown(self, overflow_items)
+                    self._overflow_state = 0
+                    self.Refresh(False)
+                    if res != -1:
+                        e = wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, res)
+                        e.SetEventObject(self)
+                        if not self.GetParent().ProcessEvent(e):
+                            tool = self.FindTool(res)
+                            if tool:
+                                state = (tool.state & AUI_BUTTON_STATE_CHECKED and [True] or [False])[0]
+                                self.ToggleTool(res, not state)
+                    
+                return
+            
+        self._dragging = False
+        self._action_pos = wx.Point(*event.GetPosition())
+        self._action_item = self.FindToolForPosition(*event.GetPosition())
+
+        if self._action_item:
+        
+            if self._action_item.state & AUI_BUTTON_STATE_DISABLED:
+            
+                self._action_pos = wx.Point(-1, -1)
+                self._action_item = None
+                return
+            
+            self.SetPressedItem(self._action_item)
+
+            # fire the tool dropdown event
+            e = AuiToolBarEvent(wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN, self._action_item.id)
+            e.SetEventObject(self)
+            e.SetToolId(self._action_item.id)
+            e.SetDropDownClicked(False)
+
+            mouse_x, mouse_y = event.GetX(), event.GetY()
+            rect = wx.Rect(*self._action_item.sizer_item.GetRect())
+
+            if self._action_item.dropdown:
+                if (self._action_item.orientation == AUI_TBTOOL_HORIZONTAL and \
+                    mouse_x >= (rect.x+rect.width-BUTTON_DROPDOWN_WIDTH-1) and \
+                    mouse_x < (rect.x+rect.width)) or \
+                    (self._action_item.orientation != AUI_TBTOOL_HORIZONTAL and \
+                     mouse_y >= (rect.y+rect.height-BUTTON_DROPDOWN_WIDTH-1) and \
+                     mouse_y < (rect.y+rect.height)):
+                    
+                    e.SetDropDownClicked(True)            
+            
+            e.SetClickPoint(event.GetPosition())
+            e.SetItemRect(rect)
+            self.ProcessEvent(e)
+            self.DoIdleUpdate()
+        
+
+    def OnLeftUp(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_UP`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        self.SetPressedItem(None)
+
+        hit_item = self.FindToolForPosition(*event.GetPosition())
+        
+        if hit_item and not hit_item.state & AUI_BUTTON_STATE_DISABLED:
+            self.SetHoverItem(hit_item)
+
+        if self._dragging:
+            # reset drag and drop member variables
+            self._dragging = False
+            self._action_pos = wx.Point(-1, -1)
+            self._action_item = None
+        
+        else:
+
+            if self._action_item and hit_item == self._action_item:
+                self.SetToolTipString("")
+
+                if hit_item.kind in [ITEM_CHECK, ITEM_RADIO]:
+                    toggle = not (self._action_item.state & AUI_BUTTON_STATE_CHECKED)
+                    self.ToggleTool(self._action_item.id, toggle)
+
+                    # repaint immediately
+                    self.Refresh(False)
+                    self.Update()
+                    
+                    e = wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, self._action_item.id)
+                    e.SetEventObject(self)
+                    e.SetInt(toggle)
+                    self._action_pos = wx.Point(-1, -1)
+                    self._action_item = None
+                    
+                    self.ProcessEvent(e)
+                    self.DoIdleUpdate()
+                    
+                else:
+
+                    if self._action_item.id == ID_RESTORE_FRAME:
+                        # find aui manager
+                        manager = self.GetAuiManager()
+
+                        if not manager:
+                            return
+
+                        pane = manager.GetPane(self)
+                        e = framemanager.AuiManagerEvent(framemanager.wxEVT_AUI_PANE_MIN_RESTORE)
+
+                        e.SetManager(manager)
+                        e.SetPane(pane)
+
+                        manager.ProcessEvent(e)
+                        self.DoIdleUpdate()
+
+                    else:
+
+                        e = wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, self._action_item.id)
+                        e.SetEventObject(self)
+                        self.ProcessEvent(e)
+                        self.DoIdleUpdate()
+                
+        # reset drag and drop member variables
+        self._dragging = False
+        self._action_pos = wx.Point(-1, -1)
+        self._action_item = None
+
+
+    def OnRightDown(self, event):
+        """
+        Handles the ``wx.EVT_RIGHT_DOWN`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        cli_rect = wx.RectPS(wx.Point(0, 0), self.GetClientSize())
+
+        if self._gripper_sizer_item:
+            gripper_rect = self._gripper_sizer_item.GetRect()
+            if gripper_rect.Contains(event.GetPosition()):
+                return
+        
+        if self._overflow_sizer_item:
+        
+            dropdown_size = self._art.GetElementSize(AUI_TBART_OVERFLOW_SIZE)
+            if dropdown_size > 0 and event.GetX() > cli_rect.width - dropdown_size and \
+               event.GetY() >= 0 and event.GetY() < cli_rect.height and self._art:
+                return
+            
+        self._action_pos = wx.Point(*event.GetPosition())
+        self._action_item = self.FindToolForPosition(*event.GetPosition())
+
+        if self._action_item:
+            if self._action_item.state & AUI_BUTTON_STATE_DISABLED:
+            
+                self._action_pos = wx.Point(-1, -1)
+                self._action_item = None
+                return
+
+
+    def OnRightUp(self, event):
+        """
+        Handles the ``wx.EVT_RIGHT_UP`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        hit_item = self.FindToolForPosition(*event.GetPosition())
+
+        if self._action_item and hit_item == self._action_item:
+            
+            e = AuiToolBarEvent(wxEVT_COMMAND_AUITOOLBAR_RIGHT_CLICK, self._action_item.id)
+            e.SetEventObject(self)
+            e.SetToolId(self._action_item.id)
+            e.SetClickPoint(self._action_pos)
+            self.ProcessEvent(e)
+            self.DoIdleUpdate()
+            
+        else:
+        
+            # right-clicked on the invalid area of the toolbar
+            e = AuiToolBarEvent(wxEVT_COMMAND_AUITOOLBAR_RIGHT_CLICK, -1)
+            e.SetEventObject(self)
+            e.SetToolId(-1)
+            e.SetClickPoint(self._action_pos)
+            self.ProcessEvent(e)
+            self.DoIdleUpdate()
+        
+        # reset member variables
+        self._action_pos = wx.Point(-1, -1)
+        self._action_item = None
+
+
+    def OnMiddleDown(self, event):
+        """
+        Handles the ``wx.EVT_MIDDLE_DOWN`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        cli_rect = wx.RectPS(wx.Point(0, 0), self.GetClientSize())
+
+        if self._gripper_sizer_item:
+        
+            gripper_rect = self._gripper_sizer_item.GetRect()
+            if gripper_rect.Contains(event.GetPosition()):
+                return
+        
+        if self._overflow_sizer_item:
+        
+            dropdown_size = self._art.GetElementSize(AUI_TBART_OVERFLOW_SIZE)
+            if dropdown_size > 0 and event.GetX() > cli_rect.width - dropdown_size and \
+               event.GetY() >= 0 and event.GetY() < cli_rect.height and self._art:            
+                return
+            
+        self._action_pos = wx.Point(*event.GetPosition())
+        self._action_item = self.FindToolForPosition(*event.GetPosition())
+
+        if self._action_item:
+            if self._action_item.state & AUI_BUTTON_STATE_DISABLED:
+            
+                self._action_pos = wx.Point(-1, -1)
+                self._action_item = None
+                return
+
+
+    def OnMiddleUp(self, event):
+        """
+        Handles the ``wx.EVT_MIDDLE_UP`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        hit_item = self.FindToolForPosition(*event.GetPosition())
+
+        if self._action_item and hit_item == self._action_item:        
+            if hit_item.kind == ITEM_NORMAL:
+            
+                e = AuiToolBarEvent(wxEVT_COMMAND_AUITOOLBAR_MIDDLE_CLICK, self._action_item.id)
+                e.SetEventObject(self)
+                e.SetToolId(self._action_item.id)
+                e.SetClickPoint(self._action_pos)
+                self.ProcessEvent(e)
+                self.DoIdleUpdate()
+            
+        # reset member variables
+        self._action_pos = wx.Point(-1, -1)
+        self._action_item = None
+
+
+    def OnMotion(self, event):
+        """
+        Handles the ``wx.EVT_MOTION`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+        
+        # start a drag event
+        if not self._dragging and self._action_item != None and self._action_pos != wx.Point(-1, -1) and \
+           abs(event.GetX() - self._action_pos.x) + abs(event.GetY() - self._action_pos.y) > 5:
+        
+            self.SetToolTipString("")
+            self._dragging = True
+
+            e = AuiToolBarEvent(wxEVT_COMMAND_AUITOOLBAR_BEGIN_DRAG, self.GetId())
+            e.SetEventObject(self)
+            e.SetToolId(self._action_item.id)
+            self.ProcessEvent(e)
+            self.DoIdleUpdate()
+            return
+        
+        hit_item = self.FindToolForPosition(*event.GetPosition())
+        
+        if hit_item:        
+            if not hit_item.state & AUI_BUTTON_STATE_DISABLED:
+                self.SetHoverItem(hit_item)
+            else:
+                self.SetHoverItem(None)
+        
+        else:        
+            # no hit item, remove any hit item
+            self.SetHoverItem(hit_item)
+        
+        # figure out tooltips
+        packing_hit_item = self.FindToolForPositionWithPacking(*event.GetPosition())
+        
+        if packing_hit_item:
+        
+            if packing_hit_item != self._tip_item:
+                self._tip_item = packing_hit_item
+
+                if packing_hit_item.short_help != "":
+                    self.StartPreviewTimer()
+                    self.SetToolTipString(packing_hit_item.short_help)
+                else:
+                    self.SetToolTipString("")
+                    self.StopPreviewTimer()
+            
+        else:
+        
+            self.SetToolTipString("")
+            self._tip_item = None
+            self.StopPreviewTimer()
+        
+        # if we've pressed down an item and we're hovering
+        # over it, make sure it's state is set to pressed
+        if self._action_item:
+        
+            if self._action_item == hit_item:
+                self.SetPressedItem(self._action_item)
+            else:
+                self.SetPressedItem(None)
+        
+        # figure out the dropdown button state (are we hovering or pressing it?)
+        self.RefreshOverflowState()
+
+
+    def OnLeaveWindow(self, event):
+        """
+        Handles the ``wx.EVT_LEAVE_WINDOW`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.MouseEvent` event to be processed.        
+        """
+
+        self.RefreshOverflowState()
+        self.SetHoverItem(None)
+        self.SetPressedItem(None)
+
+        self._tip_item = None
+        self.StopPreviewTimer()
+
+
+    def OnSetCursor(self, event):
+        """
+        Handles the ``wx.EVT_SET_CURSOR`` event for L{AuiToolBar}.
+
+        :param `event`: a `wx.SetCursorEvent` event to be processed.        
+        """
+        
+        cursor = wx.NullCursor
+
+        if self._gripper_sizer_item:
+        
+            gripper_rect = self._gripper_sizer_item.GetRect()
+            if gripper_rect.Contains((event.GetX(), event.GetY())):
+                cursor = wx.StockCursor(wx.CURSOR_SIZING)
+            
+        event.SetCursor(cursor)
+
+
+    def OnCustomRender(self, dc, item, rect):
+        """
+        Handles custom render for single L{AuiToolBar} items.
+        
+        :param `dc`: a `wx.DC` device context;
+        :param `item`: an instance of L{AuiToolBarItem};
+        :param `rect`: the toolbar item rect.
+
+        :note: This method must be overridden to provide custom rendering of items.
+        """
+        
+        pass
+
+
+    def IsPaneMinimized(self):
+        """ Returns whether this L{AuiToolBar} contains a minimized pane tool. """
+        
+        manager = self.GetAuiManager()
+        if not manager:
+            return False
+        
+        if manager.GetAGWFlags() & AUI_MGR_PREVIEW_MINIMIZED_PANES == 0:
+            # No previews here
+            return False
+
+        self_name = manager.GetPane(self).name
+        
+        if not self_name.endswith("_min"):
+            # Wrong tool name
+            return False
+
+        return self_name[0:-4]
+    
+        
+    def StartPreviewTimer(self):
+        """ Starts a timer in L{AuiManager} to slide-in/slide-out the minimized pane. """
+
+        self_name = self.IsPaneMinimized()
+        if not self_name:
+            return
+
+        manager = self.GetAuiManager()        
+        manager.StartPreviewTimer(self)
+
+
+    def StopPreviewTimer(self):
+        """ Stops a timer in L{AuiManager} to slide-in/slide-out the minimized pane. """
+
+        self_name = self.IsPaneMinimized()
+        if not self_name:
+            return
+
+        manager = self.GetAuiManager()        
+        manager.StopPreviewTimer()
+            
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
diff --git a/aui/dockart.py b/aui/dockart.py
new file mode 100644 (file)
index 0000000..17da477
--- /dev/null
@@ -0,0 +1,1162 @@
+"""
+Dock art provider code - a dock provider provides all drawing functionality to
+the AUI dock manager. This allows the dock manager to have a plugable look-and-feel.
+
+By default, a L{AuiManager} uses an instance of this class called L{AuiDefaultDockArt}
+which provides bitmap art and a colour scheme that is adapted to the major platforms'
+look. You can either derive from that class to alter its behaviour or write a
+completely new dock art class. Call L{AuiManager.SetArtProvider} to make use this
+new dock art.
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+import types
+
+from aui_utilities import BitmapFromBits, StepColour, ChopText, GetBaseColour
+from aui_utilities import DrawGradientRectangle, DrawMACCloseButton
+from aui_utilities import DarkenBitmap, LightContrastColour
+from aui_constants import *
+
+optionActive = 2**14
+
+_ctypes = False
+
+# Try to import winxptheme for ModernDockArt
+if wx.Platform == "__WXMSW__":
+    try:
+        import ctypes
+        import winxptheme
+        _ctypes = True
+    except ImportError:
+        pass
+
+# -- AuiDefaultDockArt class implementation --
+
+class AuiDefaultDockArt(object):
+    """
+    Dock art provider code - a dock provider provides all drawing functionality
+    to the AUI dock manager. This allows the dock manager to have a plugable
+    look-and-feel.
+
+    By default, a L{AuiManager} uses an instance of this class called L{AuiDefaultDockArt}
+    which provides bitmap art and a colour scheme that is adapted to the major
+    platforms' look. You can either derive from that class to alter its behaviour or
+    write a completely new dock art class.
+    
+    Call L{AuiManager.SetArtProvider} to make use this new dock art.
+
+
+    **Metric Ordinals**
+
+    These are the possible pane dock art settings for L{AuiManager}:
+
+    ================================================  ======================================
+    Metric Ordinal Constant                           Description
+    ================================================  ======================================
+    ``AUI_DOCKART_SASH_SIZE``                         Customizes the sash size
+    ``AUI_DOCKART_CAPTION_SIZE``                      Customizes the caption size
+    ``AUI_DOCKART_GRIPPER_SIZE``                      Customizes the gripper size
+    ``AUI_DOCKART_PANE_BORDER_SIZE``                  Customizes the pane border size
+    ``AUI_DOCKART_PANE_BUTTON_SIZE``                  Customizes the pane button size
+    ``AUI_DOCKART_BACKGROUND_COLOUR``                 Customizes the background colour
+    ``AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR``        Customizes the background gradient colour
+    ``AUI_DOCKART_SASH_COLOUR``                       Customizes the sash colour
+    ``AUI_DOCKART_ACTIVE_CAPTION_COLOUR``             Customizes the active caption colour
+    ``AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR``    Customizes the active caption gradient colour
+    ``AUI_DOCKART_INACTIVE_CAPTION_COLOUR``           Customizes the inactive caption colour
+    ``AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR``  Customizes the inactive gradient caption colour
+    ``AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR``        Customizes the active caption text colour
+    ``AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR``      Customizes the inactive caption text colour
+    ``AUI_DOCKART_BORDER_COLOUR``                     Customizes the border colour
+    ``AUI_DOCKART_GRIPPER_COLOUR``                    Customizes the gripper colour
+    ``AUI_DOCKART_CAPTION_FONT``                      Customizes the caption font
+    ``AUI_DOCKART_GRADIENT_TYPE``                     Customizes the gradient type (no gradient, vertical or horizontal)
+    ``AUI_DOCKART_DRAW_SASH_GRIP``                    Draw a sash grip on the sash
+    ================================================  ======================================
+
+
+    **Gradient Types**
+
+    These are the possible gradient dock art settings for L{AuiManager}:
+
+    ============================================  ======================================
+    Gradient Constant                             Description 
+    ============================================  ======================================
+    ``AUI_GRADIENT_NONE``                         No gradient on the captions
+    ``AUI_GRADIENT_VERTICAL``                     Vertical gradient on the captions
+    ``AUI_GRADIENT_HORIZONTAL``                   Horizontal gradient on the captions
+    ============================================  ======================================
+
+
+    **Button States**
+
+    These are the possible pane button / L{AuiNotebook} button / L{AuiToolBar} button states:
+
+    ============================================  ======================================
+    Button State Constant                         Description     
+    ============================================  ======================================
+    ``AUI_BUTTON_STATE_NORMAL``                   Normal button state
+    ``AUI_BUTTON_STATE_HOVER``                    Hovered button state
+    ``AUI_BUTTON_STATE_PRESSED``                  Pressed button state
+    ``AUI_BUTTON_STATE_DISABLED``                 Disabled button state
+    ``AUI_BUTTON_STATE_HIDDEN``                   Hidden button state
+    ``AUI_BUTTON_STATE_CHECKED``                  Checked button state
+    ============================================  ======================================
+
+
+    **Button Identifiers**
+
+    These are the possible pane button / L{AuiNotebook} button / L{AuiToolBar} button identifiers:
+
+    ============================================  ======================================
+    Button Identifier                             Description     
+    ============================================  ======================================
+    ``AUI_BUTTON_CLOSE``                          Shows a close button on the pane
+    ``AUI_BUTTON_MAXIMIZE_RESTORE``               Shows a maximize/restore button on the pane
+    ``AUI_BUTTON_MINIMIZE``                       Shows a minimize button on the pane
+    ``AUI_BUTTON_PIN``                            Shows a pin button on the pane
+    ``AUI_BUTTON_OPTIONS``                        Shows an option button on the pane (not implemented)
+    ``AUI_BUTTON_WINDOWLIST``                     Shows a window list button on the pane (for L{AuiNotebook})
+    ``AUI_BUTTON_LEFT``                           Shows a left button on the pane (for L{AuiNotebook})
+    ``AUI_BUTTON_RIGHT``                          Shows a right button on the pane (for L{AuiNotebook})
+    ``AUI_BUTTON_UP``                             Shows an up button on the pane (not implemented)
+    ``AUI_BUTTON_DOWN``                           Shows a down button on the pane (not implemented)
+    ``AUI_BUTTON_CUSTOM1``                        Shows a custom button on the pane (not implemented)
+    ``AUI_BUTTON_CUSTOM2``                        Shows a custom button on the pane (not implemented)
+    ``AUI_BUTTON_CUSTOM3``                        Shows a custom button on the pane (not implemented)
+    ============================================  ======================================
+    
+    """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        self.Init()
+
+        isMac = wx.Platform == "__WXMAC__"
+        
+        if isMac:
+            self._caption_font = wx.SMALL_FONT
+        else:
+            self._caption_font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False)
+
+        self.SetDefaultPaneBitmaps(isMac)
+        self._restore_bitmap = wx.BitmapFromXPMData(restore_xpm)
+        
+        # default metric values
+        self._sash_size = 4
+
+        if isMac:
+            # This really should be implemented in wx.SystemSettings
+            # There is no way to do this that I am aware outside of using
+            # the cocoa python bindings. 8 pixels looks correct on my system
+            # so hard coding it for now.
+
+            # How do I translate this?!? Not sure of the below implementation...
+            # SInt32 height;
+            # GetThemeMetric( kThemeMetricSmallPaneSplitterHeight , &height );
+            # self._sash_size = height;
+
+            self._sash_size = 8 # Carbon.Appearance.kThemeMetricPaneSplitterHeight            
+            
+        elif wx.Platform == "__WXGTK__":
+            self._sash_size = wx.RendererNative.Get().GetSplitterParams(wx.GetTopLevelWindows()[0]).widthSash
+
+        else:
+            self._sash_size = 4
+        
+        self._caption_size = 19
+        self._border_size = 1
+        self._button_size = 14
+        self._gripper_size = 9
+        self._gradient_type = AUI_GRADIENT_VERTICAL
+        self._draw_sash = False
+        
+
+    def Init(self):
+        """ Initializes the dock art. """
+
+        base_colour = GetBaseColour()
+        darker1_colour = StepColour(base_colour, 85)
+        darker2_colour = StepColour(base_colour, 75)
+        darker3_colour = StepColour(base_colour, 60)
+        darker4_colour = StepColour(base_colour, 40)
+
+        self._background_colour = base_colour
+        self._background_gradient_colour = StepColour(base_colour, 180)
+
+        isMac = wx.Platform == "__WXMAC__"
+
+        if isMac:
+            self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+        else:
+            self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
+
+        self._active_caption_gradient_colour = LightContrastColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT))
+        self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
+        self._inactive_caption_colour = darker1_colour
+        self._inactive_caption_gradient_colour = StepColour(base_colour, 97)
+        self._inactive_caption_text_colour = wx.BLACK
+    
+        self._sash_brush = wx.Brush(base_colour)
+        self._background_brush = wx.Brush(base_colour)
+        self._border_pen = wx.Pen(darker2_colour)
+        self._gripper_brush = wx.Brush(base_colour)
+        self._gripper_pen1 = wx.Pen(darker4_colour)
+        self._gripper_pen2 = wx.Pen(darker3_colour)
+        self._gripper_pen3 = wx.WHITE_PEN
+
+        
+    def GetMetric(self, id):
+        """
+        Gets the value of a certain setting.
+
+        :param `id`: can be one of the size values in `Metric Ordinals`.
+        """
+
+
+        if id == AUI_DOCKART_SASH_SIZE:
+            return self._sash_size
+        elif id == AUI_DOCKART_CAPTION_SIZE:
+            return self._caption_size
+        elif id == AUI_DOCKART_GRIPPER_SIZE:
+            return self._gripper_size
+        elif id == AUI_DOCKART_PANE_BORDER_SIZE:
+            return self._border_size
+        elif id == AUI_DOCKART_PANE_BUTTON_SIZE:
+            return self._button_size
+        elif id == AUI_DOCKART_GRADIENT_TYPE:
+            return self._gradient_type
+        elif id == AUI_DOCKART_DRAW_SASH_GRIP:
+            return self._draw_sash
+        else:
+            raise Exception("Invalid Metric Ordinal.")
+
+
+    def SetMetric(self, id, new_val):
+        """
+        Sets the value of a certain setting using `new_val`
+
+        :param `id`: can be one of the size values in `Metric Ordinals`;
+        :param `new_val`: the new value of the setting.
+        """
+
+        if id == AUI_DOCKART_SASH_SIZE:
+            self._sash_size = new_val
+        elif id == AUI_DOCKART_CAPTION_SIZE:
+            self._caption_size = new_val
+        elif id == AUI_DOCKART_GRIPPER_SIZE:
+            self._gripper_size = new_val
+        elif id == AUI_DOCKART_PANE_BORDER_SIZE:
+            self._border_size = new_val
+        elif id == AUI_DOCKART_PANE_BUTTON_SIZE:
+            self._button_size = new_val
+        elif id == AUI_DOCKART_GRADIENT_TYPE:
+            self._gradient_type = new_val
+        elif id == AUI_DOCKART_DRAW_SASH_GRIP:
+            self._draw_sash = new_val
+        else:
+            raise Exception("Invalid Metric Ordinal.")
+
+
+    def GetColor(self, id):
+        """
+        Gets the colour of a certain setting.
+
+        :param `id`: can be one of the colour values in `Metric Ordinals`.
+        """
+
+        if id == AUI_DOCKART_BACKGROUND_COLOUR:
+            return self._background_brush.GetColour()
+        elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR:
+            return self._background_gradient_colour
+        elif id == AUI_DOCKART_SASH_COLOUR:
+            return self._sash_brush.GetColour()
+        elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR:
+            return self._inactive_caption_colour
+        elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR:
+            return self._inactive_caption_gradient_colour
+        elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR:
+            return self._inactive_caption_text_colour
+        elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR:
+            return self._active_caption_colour
+        elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR:
+            return self._active_caption_gradient_colour
+        elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR:
+            return self._active_caption_text_colour        
+        elif id == AUI_DOCKART_BORDER_COLOUR:
+            return self._border_pen.GetColour()
+        elif id == AUI_DOCKART_GRIPPER_COLOUR:
+            return self._gripper_brush.GetColour()
+        else:
+            raise Exception("Invalid Colour Ordinal.")
+
+
+    def SetColor(self, id, colour):
+        """
+        Sets the colour of a certain setting.
+
+        :param `id`: can be one of the colour values in `Metric Ordinals`;
+        :param `colour`: the new value of the setting.
+        """
+
+        if isinstance(colour, basestring):
+            colour = wx.NamedColour(colour)
+        elif isinstance(colour, types.TupleType):
+            colour = wx.Colour(*colour)
+        elif isinstance(colour, types.IntType):
+            colour = wx.ColourRGB(colour)
+        
+        if id == AUI_DOCKART_BACKGROUND_COLOUR:
+            self._background_brush.SetColour(colour)
+        elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR:
+            self._background_gradient_colour = colour
+        elif id == AUI_DOCKART_SASH_COLOUR:
+            self._sash_brush.SetColour(colour)
+        elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR:
+            self._inactive_caption_colour = colour
+            if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__":
+                # No custom bitmaps for the pane close button
+                # Change the MAC close bitmap colour
+                self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, colour)
+
+        elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR:
+            self._inactive_caption_gradient_colour = colour
+        elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR:
+            self._inactive_caption_text_colour = colour
+        elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR:
+            self._active_caption_colour = colour
+            if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__":
+                # No custom bitmaps for the pane close button
+                # Change the MAC close bitmap colour
+                self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, colour)
+                
+        elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR:
+            self._active_caption_gradient_colour = colour
+        elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR:
+            self._active_caption_text_colour = colour
+        elif id == AUI_DOCKART_BORDER_COLOUR:
+            self._border_pen.SetColour(colour)
+        elif id == AUI_DOCKART_GRIPPER_COLOUR:
+            self._gripper_brush.SetColour(colour)
+            self._gripper_pen1.SetColour(StepColour(colour, 40))
+            self._gripper_pen2.SetColour(StepColour(colour, 60))
+        else:
+            raise Exception("Invalid Colour Ordinal.")
+        
+
+    GetColour = GetColor
+    SetColour = SetColor
+
+    def SetFont(self, id, font):
+        """
+        Sets a font setting.
+        
+        :param `id`: must be ``AUI_DOCKART_CAPTION_FONT``;
+        :param `font`: an instance of `wx.Font`.
+        """
+        
+        if id == AUI_DOCKART_CAPTION_FONT:
+            self._caption_font = font
+
+
+    def GetFont(self, id):
+        """
+        Gets a font setting.
+        
+        :param `id`: must be ``AUI_DOCKART_CAPTION_FONT``, otherwise `wx.NullFont` is returned.
+        """
+        
+        if id == AUI_DOCKART_CAPTION_FONT:
+            return self._caption_font
+        
+        return wx.NullFont
+
+
+    def DrawSash(self, dc, window, orient, rect):
+        """
+        Draws a sash between two windows.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `orient`: the sash orientation;
+        :param `rect`: the sash rectangle.
+        """                
+
+        # AG: How do we make this work?!?
+        # RendererNative does not use the sash_brush chosen by the user
+        # and the rect.GetSize() is ignored as the sash is always drawn
+        # 3 pixel wide
+        # wx.RendererNative.Get().DrawSplitterSash(window, dc, rect.GetSize(), pos, orient)
+
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        dc.SetBrush(self._sash_brush)
+        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)        
+
+        draw_sash = self.GetMetric(AUI_DOCKART_DRAW_SASH_GRIP)
+        if draw_sash:
+            self.DrawSashGripper(dc, orient, rect)
+
+
+    def DrawBackground(self, dc, window, orient, rect):
+        """
+        Draws a background.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `orient`: the gradient (if any) orientation;
+        :param `rect`: the background rectangle.
+        """
+
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        if wx.Platform == "__WXMAC__":
+            # we have to clear first, otherwise we are drawing a light striped pattern
+            # over an already darker striped background
+            dc.SetBrush(wx.WHITE_BRUSH)
+            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
+
+        DrawGradientRectangle(dc, rect, self._background_brush.GetColour(),
+                              self._background_gradient_colour,
+                              AUI_GRADIENT_HORIZONTAL, rect.x, 700)
+
+
+    def DrawBorder(self, dc, window, rect, pane):
+        """
+        Draws the pane border.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `rect`: the border rectangle;
+        :param `pane`: the pane for which the border is drawn.
+        """        
+
+        drect = wx.Rect(*rect)
+        
+        dc.SetPen(self._border_pen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        border_width = self.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
+
+        if pane.IsToolbar():
+        
+            for ii in xrange(0, border_width):
+            
+                dc.SetPen(wx.WHITE_PEN)
+                dc.DrawLine(drect.x, drect.y, drect.x+drect.width, drect.y)
+                dc.DrawLine(drect.x, drect.y, drect.x, drect.y+drect.height)
+                dc.SetPen(self._border_pen)       
+                dc.DrawLine(drect.x, drect.y+drect.height-1,
+                            drect.x+drect.width, drect.y+drect.height-1)
+                dc.DrawLine(drect.x+drect.width-1, drect.y,
+                            drect.x+drect.width-1, drect.y+drect.height)
+                drect.Deflate(1, 1)
+        
+        else:
+        
+            for ii in xrange(0, border_width):
+            
+                dc.DrawRectangle(drect.x, drect.y, drect.width, drect.height)
+                drect.Deflate(1, 1)
+            
+
+    def DrawCaptionBackground(self, dc, rect, pane):
+        """
+        Draws the text caption background in the pane.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `rect`: the text caption rectangle;
+        :param `pane`: the pane for which the text background is drawn.
+        """        
+
+        active = pane.state & optionActive
+        if self._gradient_type == AUI_GRADIENT_NONE:
+            if active:
+                dc.SetBrush(wx.Brush(self._active_caption_colour))
+            else:
+                dc.SetBrush(wx.Brush(self._inactive_caption_colour))
+
+            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)        
+
+        else:
+
+            switch_gradient = pane.HasCaptionLeft()
+            gradient_type = self._gradient_type
+            if switch_gradient:
+                gradient_type = (self._gradient_type == AUI_GRADIENT_HORIZONTAL and [AUI_GRADIENT_VERTICAL] or \
+                                 [AUI_GRADIENT_HORIZONTAL])[0]
+            
+            if active:
+                if wx.Platform == "__WXMAC__":
+                    DrawGradientRectangle(dc, rect, self._active_caption_colour,
+                                          self._active_caption_gradient_colour,
+                                          gradient_type)                    
+                else:
+                    DrawGradientRectangle(dc, rect, self._active_caption_gradient_colour,
+                                          self._active_caption_colour,
+                                          gradient_type)
+            else:
+                if wx.Platform == "__WXMAC__":
+                    DrawGradientRectangle(dc, rect, self._inactive_caption_gradient_colour,
+                                          self._inactive_caption_colour,
+                                          gradient_type)
+                else:
+                    DrawGradientRectangle(dc, rect, self._inactive_caption_colour,
+                                          self._inactive_caption_gradient_colour,
+                                          gradient_type)
+
+
+    def DrawIcon(self, dc, rect, pane):
+        """
+        Draws the icon in the pane caption area.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the icon is drawn.
+        """        
+        
+        # Draw the icon centered vertically 
+        if pane.icon.Ok():
+            if pane.HasCaptionLeft():
+                bmp = wx.ImageFromBitmap(pane.icon).Rotate90(clockwise=False)
+                dc.DrawBitmap(bmp.ConvertToBitmap(), rect.x+(rect.width-pane.icon.GetWidth())/2, rect.y+rect.height-2-pane.icon.GetHeight(), True)
+            else:
+                dc.DrawBitmap(pane.icon, rect.x+2, rect.y+(rect.height-pane.icon.GetHeight())/2, True)
+
+
+    def DrawCaption(self, dc, window, text, rect, pane):
+        """
+        Draws the text in the pane caption.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `text`: the text to be displayed;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the text is drawn.
+        """        
+
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        dc.SetFont(self._caption_font)
+        
+        self.DrawCaptionBackground(dc, rect, pane)
+
+        if pane.state & optionActive:
+            dc.SetTextForeground(self._active_caption_text_colour)
+        else:
+            dc.SetTextForeground(self._inactive_caption_text_colour)
+
+        w, h = dc.GetTextExtent("ABCDEFHXfgkj")
+
+        clip_rect = wx.Rect(*rect)
+        btns = pane.CountButtons()
+
+        captionLeft = pane.HasCaptionLeft()
+        variable = (captionLeft and [rect.height] or [rect.width])[0]
+
+        variable -= 3      # text offset
+        variable -= 2      # button padding
+
+        caption_offset = 0
+        if pane.icon:
+            if captionLeft:
+                caption_offset += pane.icon.GetHeight() + 3
+            else:
+                caption_offset += pane.icon.GetWidth() + 3
+                
+            self.DrawIcon(dc, rect, pane)
+
+        variable -= caption_offset
+        variable -= btns*(self._button_size + self._border_size)
+        draw_text = ChopText(dc, text, variable)
+
+        if captionLeft:
+            dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-1, rect.y+rect.height-3-caption_offset, 90)
+        else:
+            dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-1)
+
+
+    def RequestUserAttention(self, dc, window, text, rect, pane):
+        """
+        Requests the user attention by intermittently highlighting the pane caption.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `text`: the text to be displayed;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the text is drawn.
+        """        
+
+        state = pane.state
+        pane.state &= ~optionActive
+        
+        for indx in xrange(6):
+            active = (indx%2 == 0 and [True] or [False])[0]
+            if active:
+                pane.state |= optionActive
+            else:
+                pane.state &= ~optionActive
+                
+            self.DrawCaptionBackground(dc, rect, pane)
+            self.DrawCaption(dc, window, text, rect, pane)
+            wx.SafeYield()
+            wx.MilliSleep(350)
+
+        pane.state = state
+        
+
+    def DrawGripper(self, dc, window, rect, pane):
+        """
+        Draws a gripper on the pane.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the gripper is drawn.
+        """        
+
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        dc.SetBrush(self._gripper_brush)
+
+        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
+
+        if not pane.HasGripperTop():
+            y = 4
+            while 1:
+                dc.SetPen(self._gripper_pen1)
+                dc.DrawPoint(rect.x+3, rect.y+y) 
+                dc.SetPen(self._gripper_pen2) 
+                dc.DrawPoint(rect.x+3, rect.y+y+1) 
+                dc.DrawPoint(rect.x+4, rect.y+y) 
+                dc.SetPen(self._gripper_pen3) 
+                dc.DrawPoint(rect.x+5, rect.y+y+1) 
+                dc.DrawPoint(rect.x+5, rect.y+y+2) 
+                dc.DrawPoint(rect.x+4, rect.y+y+2) 
+                y = y + 4 
+                if y > rect.GetHeight() - 4:
+                    break
+        else:
+            x = 4
+            while 1:
+                dc.SetPen(self._gripper_pen1) 
+                dc.DrawPoint(rect.x+x, rect.y+3) 
+                dc.SetPen(self._gripper_pen2) 
+                dc.DrawPoint(rect.x+x+1, rect.y+3) 
+                dc.DrawPoint(rect.x+x, rect.y+4) 
+                dc.SetPen(self._gripper_pen3) 
+                dc.DrawPoint(rect.x+x+1, rect.y+5) 
+                dc.DrawPoint(rect.x+x+2, rect.y+5) 
+                dc.DrawPoint(rect.x+x+2, rect.y+4) 
+                x = x + 4 
+                if x > rect.GetWidth() - 4:
+                    break 
+        
+
+    def DrawPaneButton(self, dc, window, button, button_state, _rect, pane):
+        """
+        Draws a pane button in the pane caption area.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `button`: the button to be drawn;
+        :param `button_state`: the pane button state;
+        :param `_rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the button is drawn.
+        """        
+        
+        if not pane:
+            return
+        
+        if button == AUI_BUTTON_CLOSE:
+            if pane.state & optionActive:
+                bmp = self._active_close_bitmap
+            else:
+                bmp = self._inactive_close_bitmap
+
+        elif button == AUI_BUTTON_PIN:
+            if pane.state & optionActive:
+                bmp = self._active_pin_bitmap
+            else:
+                bmp = self._inactive_pin_bitmap
+
+        elif button == AUI_BUTTON_MAXIMIZE_RESTORE:
+            if pane.IsMaximized():
+                if pane.state & optionActive:
+                    bmp = self._active_restore_bitmap
+                else:
+                    bmp = self._inactive_restore_bitmap
+            else:
+                if pane.state & optionActive:
+                    bmp = self._active_maximize_bitmap
+                else:
+                    bmp = self._inactive_maximize_bitmap
+
+        elif button == AUI_BUTTON_MINIMIZE:
+            if pane.state & optionActive:
+                bmp = self._active_minimize_bitmap
+            else:
+                bmp = self._inactive_minimize_bitmap
+
+        isVertical = pane.HasCaptionLeft()
+        
+        rect = wx.Rect(*_rect)
+
+        if isVertical:
+            old_x = rect.x
+            rect.x = rect.x + (rect.width/2) - (bmp.GetWidth()/2)
+            rect.width = old_x + rect.width - rect.x - 1
+        else:
+            old_y = rect.y
+            rect.y = rect.y + (rect.height/2) - (bmp.GetHeight()/2)
+            rect.height = old_y + rect.height - rect.y - 1
+
+        if button_state == AUI_BUTTON_STATE_PRESSED:
+            rect.x += 1
+            rect.y += 1
+
+        if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]:
+
+            if pane.state & optionActive:
+
+                dc.SetBrush(wx.Brush(StepColour(self._active_caption_colour, 120)))
+                dc.SetPen(wx.Pen(StepColour(self._active_caption_colour, 70)))
+
+            else:
+
+                dc.SetBrush(wx.Brush(StepColour(self._inactive_caption_colour, 120)))
+                dc.SetPen(wx.Pen(StepColour(self._inactive_caption_colour, 70)))
+
+            if wx.Platform != "__WXMAC__":
+                # draw the background behind the button
+                dc.DrawRectangle(rect.x, rect.y, 15, 15)
+            else:
+                # Darker the bitmap a bit
+                bmp = DarkenBitmap(bmp, self._active_caption_colour, StepColour(self._active_caption_colour, 110))
+
+        if isVertical:
+            bmp = wx.ImageFromBitmap(bmp).Rotate90(clockwise=False).ConvertToBitmap()
+            
+        # draw the button itself
+        dc.DrawBitmap(bmp, rect.x, rect.y, True)
+
+
+    def DrawSashGripper(self, dc, orient, rect):
+        """
+        Draws a sash gripper on a sash between two windows.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `orient`: the sash orientation;
+        :param `rect`: the sash rectangle.
+        """
+        
+        dc.SetBrush(self._gripper_brush)
+
+        if orient == wx.HORIZONTAL:  # horizontal sash
+            
+            x = rect.x + int((1.0/4.0)*rect.width)
+            xend = rect.x + int((3.0/4.0)*rect.width)
+            y = rect.y + (rect.height/2) - 1
+
+            while 1:
+                dc.SetPen(self._gripper_pen3)
+                dc.DrawRectangle(x, y, 2, 2)
+                dc.SetPen(self._gripper_pen2) 
+                dc.DrawPoint(x+1, y+1)
+                x = x + 5
+
+                if x >= xend:
+                    break
+
+        else:
+
+            y = rect.y + int((1.0/4.0)*rect.height)
+            yend = rect.y + int((3.0/4.0)*rect.height)
+            x = rect.x + (rect.width/2) - 1
+
+            while 1:
+                dc.SetPen(self._gripper_pen3)
+                dc.DrawRectangle(x, y, 2, 2)
+                dc.SetPen(self._gripper_pen2) 
+                dc.DrawPoint(x+1, y+1)
+                y = y + 5
+
+                if y >= yend:
+                    break
+
+
+    def SetDefaultPaneBitmaps(self, isMac):
+        """
+        Assigns the default pane bitmaps.
+
+        :param `isMac`: whether we are on wxMAC or not.
+        """
+
+        if isMac:
+            self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, self._inactive_caption_colour)
+            self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, self._active_caption_colour)
+        else:
+            self._inactive_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._inactive_caption_text_colour)
+            self._active_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._active_caption_text_colour)
+            
+        if isMac:
+            self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE)
+            self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE)
+        else:
+            self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._inactive_caption_text_colour)
+            self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._active_caption_text_colour)
+
+        if isMac:
+            self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE)
+            self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE)
+        else:
+            self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._inactive_caption_text_colour)
+            self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._active_caption_text_colour)
+
+        if isMac:
+            self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE)
+            self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE)
+        else:
+            self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._inactive_caption_text_colour)
+            self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._active_caption_text_colour)
+
+        self._inactive_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._inactive_caption_text_colour)
+        self._active_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._active_caption_text_colour)
+
+        self._custom_pane_bitmaps = False
+        
+        
+    def SetCustomPaneBitmap(self, bmp, button, active, maximize=False):
+        """
+        Sets a custom button bitmap for the pane button.
+
+        :param `bmp`: the actual bitmap to set;
+        :param `button`: the button identifier;
+        :param `active`: whether it is the bitmap for the active button or not;
+        :param `maximize`: used to distinguish between the maximize and restore bitmaps.
+        """
+
+        if bmp.GetWidth() > 16 or bmp.GetHeight() > 16:
+            raise Exception("The input bitmap is too big")
+
+        if button == AUI_BUTTON_CLOSE:
+            if active:
+                self._active_close_bitmap = bmp
+            else:
+                self._inactive_close_bitmap = bmp
+
+            if wx.Platform == "__WXMAC__":
+                self._custom_pane_bitmaps = True                
+
+        elif button == AUI_BUTTON_PIN:
+            if active:
+                self._active_pin_bitmap = bmp
+            else:
+                self._inactive_pin_bitmap = bmp
+
+        elif button == AUI_BUTTON_MAXIMIZE_RESTORE:
+            if maximize:
+                if active:
+                    self._active_maximize_bitmap = bmp
+                else:
+                    self._inactive_maximize_bitmap = bmp
+            else:
+                if active:
+                    self._active_restore_bitmap = bmp
+                else:
+                    self._inactive_restore_bitmap = bmp
+
+        elif button == AUI_BUTTON_MINIMIZE:
+            if active:
+                self._active_minimize_bitmap = bmp
+            else:
+                self._inactive_minimize_bitmap = bmp
+
+
+if _ctypes:
+    class RECT(ctypes.Structure):
+        """ Used to handle L{ModernDockArt} on Windows XP/Vista/7. """
+        _fields_ = [('left', ctypes.c_ulong),('top', ctypes.c_ulong),('right', ctypes.c_ulong),('bottom', ctypes.c_ulong)]
+
+        def dump(self):
+            """ Dumps `self` as a `wx.Rect`. """
+            return map(int, (self.left, self.top, self.right, self.bottom))
+
+
+    class SIZE(ctypes.Structure):
+        """ Used to handle L{ModernDockArt} on Windows XP/Vista/7. """
+        _fields_ = [('x', ctypes.c_long),('y', ctypes.c_long)]
+
+
+class ModernDockArt(AuiDefaultDockArt):
+    """
+    ModernDockArt is a custom `AuiDockArt` class, that implements a look similar to
+    Firefox and other recents applications. 
+
+    Is uses the `winxptheme` module and XP themes whenever possible, so it should
+    look good even if the user has a custom theme.
+
+    :note: This dock art is Windows only and will only work if you have installed
+     Mark Hammond's `pywin32` module (http://sourceforge.net/projects/pywin32/).
+    """
+
+    def __init__(self, win):
+        """
+        Default class constructor. 
+
+        :param `win`: the window managed by L{AuiManager}. 
+        """
+        
+        AuiDefaultDockArt.__init__(self)
+        
+        self.win = win
+
+        # Get the size of a small close button (themed)
+        hwnd = self.win.GetHandle()
+        
+        self.hTheme1 = winxptheme.OpenThemeData(hwnd, "Window")
+        
+        self.usingTheme = True
+        
+        if not self.hTheme1:
+            self.usingTheme = False
+
+        self._button_size = 13
+
+        self._button_border_size = 3
+        self._caption_text_indent = 6
+        self._caption_size = 22
+        
+        # We only highlight the active pane with the caption text being in bold.
+        # So we do not want a special colour for active elements.        
+        self._active_close_bitmap = self._inactive_close_bitmap
+
+        self.Init()
+        
+
+    def Init(self):
+        """ Initializes the dock art. """
+
+        AuiDefaultDockArt.Init(self)
+        
+        self._active_caption_colour = self._inactive_caption_colour
+        self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_CAPTIONTEXT)
+        self._inactive_caption_text_colour = self._active_caption_text_colour
+
+
+    def DrawCaption(self, dc, window, text, rect, pane):
+        """
+        Draws the text in the pane caption.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `text`: the text to be displayed;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the text is drawn.
+        """        
+
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        self.DrawCaptionBackground(dc, rect, pane)
+
+        active = ((pane.state & optionActive) and [True] or [False])[0]
+
+        self._caption_font.SetWeight(wx.FONTWEIGHT_BOLD)
+        dc.SetFont(self._caption_font)
+        
+        if active:
+            dc.SetTextForeground(self._active_caption_text_colour)
+        else:
+            dc.SetTextForeground(self._inactive_caption_text_colour)
+
+        w, h = dc.GetTextExtent("ABCDEFHXfgkj")
+
+        clip_rect = wx.Rect(*rect)
+        btns = pane.CountButtons()
+
+        captionLeft = pane.HasCaptionLeft()
+        variable = (captionLeft and [rect.height] or [rect.width])[0]
+
+        variable -= 3      # text offset
+        variable -= 2      # button padding
+
+        caption_offset = 0
+        if pane.icon:
+            if captionLeft:
+                caption_offset += pane.icon.GetHeight() + 3
+            else:
+                caption_offset += pane.icon.GetWidth() + 3
+                
+            self.DrawIcon(dc, rect, pane)
+
+        diff = -2
+        if self.usingTheme:
+            diff = -1
+
+        variable -= caption_offset
+        variable -= btns*(self._button_size + self._button_border_size)
+        draw_text = ChopText(dc, text, variable)
+
+        if captionLeft:
+            dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-diff, rect.y+rect.height-3-caption_offset, 90)
+        else:
+            dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-diff)
+
+
+    def DrawCaptionBackground(self, dc, rect, pane):
+        """
+        Draws the text caption background in the pane.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `rect`: the text caption rectangle;
+        :param `pane`: the pane for which we are drawing the caption background.
+        """        
+
+        dc.SetBrush(self._background_brush)
+        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
+
+        active = ((pane.state & optionActive) and [True] or [False])[0]
+
+        if self.usingTheme:
+            
+            rectangle = wx.Rect()
+
+            rc = RECT(rectangle.x, rectangle.y, rectangle.width, rectangle.height)
+
+            # If rect x/y values are negative rc.right/bottom values will overflow and winxptheme.DrawThemeBackground
+            # will raise a TypeError. Ensure they are never negative.
+            rect.x = max(0, rect.x)
+            rect.y = max(0, rect.y)
+
+            rc.top = rect.x
+            rc.left = rect.y
+            rc.right = rect.x + rect.width
+            rc.bottom = rect.y + rect.height
+
+            if active:
+                winxptheme.DrawThemeBackground(self.hTheme1, dc.GetHDC(), 5, 1, (rc.top, rc.left, rc.right, rc.bottom), None)
+            else:
+                winxptheme.DrawThemeBackground(self.hTheme1, dc.GetHDC(), 5, 2, (rc.top, rc.left, rc.right, rc.bottom), None)
+            
+        else:
+
+            AuiDefaultDockArt.DrawCaptionBackground(self, dc, rect, pane)
+
+
+    def RequestUserAttention(self, dc, window, text, rect, pane):
+        """
+        Requests the user attention by intermittently highlighting the pane caption.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `text`: the text to be displayed;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the text is drawn.
+        """        
+    
+        state = pane.state
+        pane.state &= ~optionActive
+        
+        for indx in xrange(6):
+            active = (indx%2 == 0 and [True] or [False])[0]
+            if active:
+                pane.state |= optionActive
+            else:
+                pane.state &= ~optionActive
+                
+            self.DrawCaptionBackground(dc, rect, pane)
+            self.DrawCaption(dc, window, text, rect, pane)
+            wx.SafeYield()
+            wx.MilliSleep(350)
+
+        pane.state = state
+
+
+    def DrawPaneButton(self, dc, window, button, button_state, rect, pane):
+        """
+        Draws a pane button in the pane caption area.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `window`: an instance of `wx.Window`;
+        :param `button`: the button to be drawn;
+        :param `button_state`: the pane button state;
+        :param `rect`: the pane caption rectangle;
+        :param `pane`: the pane for which the button is drawn.
+        """        
+
+        if self.usingTheme:
+
+            hTheme = self.hTheme1            
+                    
+            # Get the real button position (compensating for borders)
+            drect = wx.Rect(rect.x, rect.y, self._button_size, self._button_size)
+            
+            # Draw the themed close button
+            rc = RECT(0, 0, 0, 0)
+            if pane.HasCaptionLeft():
+                rc.top = rect.x + self._button_border_size
+                rc.left = int(rect.y + 1.5*self._button_border_size)
+                rc.right = rect.x + self._button_size + self._button_border_size
+                rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size)
+            else:
+                rc.top = rect.x - self._button_border_size
+                rc.left = int(rect.y + 1.5*self._button_border_size)
+                rc.right = rect.x + self._button_size- self._button_border_size
+                rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size)
+
+            if button == AUI_BUTTON_CLOSE:
+                btntype = 19
+                
+            elif button == AUI_BUTTON_PIN:
+                btntype = 23
+
+            elif button == AUI_BUTTON_MAXIMIZE_RESTORE:
+                if not pane.IsMaximized():
+                    btntype = 17
+                else:
+                    btntype = 21
+            else:
+                btntype = 15
+
+            state = 4 # CBS_DISABLED
+            
+            if pane.state & optionActive:
+
+                if button_state == AUI_BUTTON_STATE_NORMAL:
+                    state = 1 # CBS_NORMAL
+
+                elif button_state == AUI_BUTTON_STATE_HOVER:
+                    state = 2 # CBS_HOT
+
+                elif button_state == AUI_BUTTON_STATE_PRESSED:
+                    state = 3 # CBS_PUSHED
+
+                else:
+                    raise Exception("ERROR: Unknown State.")
+
+            else: # inactive pane
+
+                if button_state == AUI_BUTTON_STATE_NORMAL:
+                    state = 5 # CBS_NORMAL
+
+                elif button_state == AUI_BUTTON_STATE_HOVER:
+                    state = 6 # CBS_HOT
+
+                elif button_state == AUI_BUTTON_STATE_PRESSED:
+                    state = 7 # CBS_PUSHED
+
+                else:
+                    raise Exception("ERROR: Unknown State.")
+
+            try:
+                winxptheme.DrawThemeBackground(hTheme, dc.GetHDC(), btntype, state, (rc.top, rc.left, rc.right, rc.bottom), None)
+            except TypeError:
+                return
+
+        else:
+
+            # Fallback to default closebutton if themes are not enabled
+            rect2 = wx.Rect(rect.x-4, rect.y+2, rect.width, rect.height)
+            AuiDefaultDockArt.DrawPaneButton(self, dc, window, button, button_state, rect2, pane)
+
diff --git a/aui/framemanager.py b/aui/framemanager.py
new file mode 100644 (file)
index 0000000..9b277bf
--- /dev/null
@@ -0,0 +1,10385 @@
+# --------------------------------------------------------------------------- #
+# AUI Library wxPython IMPLEMENTATION
+#
+# Original C++ Code From Kirix (wxAUI). You Can Find It At:
+#
+#    License: wxWidgets license
+#
+# http:#www.kirix.com/en/community/opensource/wxaui/about_wxaui.html
+#
+# Current wxAUI Version Tracked: wxWidgets 2.9.0 SVN HEAD
+#
+#
+# Python Code By:
+#
+# Andrea Gavana, @ 23 Dec 2005
+# Latest Revision: 10 Mar 2011, 15.00 GMT
+#
+# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
+# Write To Me At:
+#
+# andrea.gavana@gmail.com
+# gavana@kpo.kz
+#
+# Or, Obviously, To The wxPython Mailing List!!!
+#
+# End Of Comments
+# --------------------------------------------------------------------------- #
+
+"""
+Description
+===========
+
+framemanager is the central module of the AUI class framework.
+
+L{AuiManager} manages the panes associated with it for a particular `wx.Frame`, using
+a pane's L{AuiPaneInfo} information to determine each pane's docking and floating
+behavior. AuiManager uses wxPython' sizer mechanism to plan the layout of each frame.
+It uses a replaceable dock art class to do all drawing, so all drawing is localized
+in one area, and may be customized depending on an application's specific needs.
+
+AuiManager works as follows: the programmer adds panes to the class, or makes
+changes to existing pane properties (dock position, floating state, show state, etc...).
+To apply these changes, AuiManager's L{AuiManager.Update} function is called. This batch
+processing can be used to avoid flicker, by modifying more than one pane at a time,
+and then "committing" all of the changes at once by calling `Update()`.
+
+Panes can be added quite easily::
+
+    text1 = wx.TextCtrl(self, -1)
+    text2 = wx.TextCtrl(self, -1)
+    self._mgr.AddPane(text1, AuiPaneInfo().Left().Caption("Pane Number One"))
+    self._mgr.AddPane(text2, AuiPaneInfo().Bottom().Caption("Pane Number Two"))
+
+    self._mgr.Update()
+
+
+Later on, the positions can be modified easily. The following will float an
+existing pane in a tool window::
+
+    self._mgr.GetPane(text1).Float()
+
+
+Layers, Rows and Directions, Positions
+======================================
+
+Inside AUI, the docking layout is figured out by checking several pane parameters.
+Four of these are important for determining where a pane will end up.
+
+**Direction** - Each docked pane has a direction, `Top`, `Bottom`, `Left`, `Right`, or `Center`.
+This is fairly self-explanatory. The pane will be placed in the location specified
+by this variable.
+
+**Position** - More than one pane can be placed inside of a "dock". Imagine two panes
+being docked on the left side of a window. One pane can be placed over another.
+In proportionally managed docks, the pane position indicates it's sequential position,
+starting with zero. So, in our scenario with two panes docked on the left side, the
+top pane in the dock would have position 0, and the second one would occupy position 1. 
+
+**Row** - A row can allow for two docks to be placed next to each other. One of the most
+common places for this to happen is in the toolbar. Multiple toolbar rows are allowed,
+the first row being in row 0, and the second in row 1. Rows can also be used on
+vertically docked panes. 
+
+**Layer** - A layer is akin to an onion. Layer 0 is the very center of the managed pane.
+Thus, if a pane is in layer 0, it will be closest to the center window (also sometimes
+known as the "content window"). Increasing layers "swallow up" all layers of a lower
+value. This can look very similar to multiple rows, but is different because all panes
+in a lower level yield to panes in higher levels. The best way to understand layers
+is by running the AUI sample (`AUI.py`).
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+import time
+import types
+import warnings
+
+import auibar
+import auibook
+import tabmdi
+import dockart
+import tabart
+
+from aui_utilities import Clip, PaneCreateStippleBitmap, GetDockingImage, GetSlidingPoints
+
+from aui_constants import *
+
+# Define this as a translation function
+_ = wx.GetTranslation
+
+_winxptheme = False
+if wx.Platform == "__WXMSW__":
+    try:
+        import winxptheme
+        _winxptheme = True
+    except ImportError:
+        pass
+
+# wxPython version string
+_VERSION_STRING = wx.VERSION_STRING
+
+# AUI Events
+wxEVT_AUI_PANE_BUTTON = wx.NewEventType()
+wxEVT_AUI_PANE_CLOSE = wx.NewEventType()
+wxEVT_AUI_PANE_MAXIMIZE = wx.NewEventType()
+wxEVT_AUI_PANE_RESTORE = wx.NewEventType()
+wxEVT_AUI_RENDER = wx.NewEventType()
+wxEVT_AUI_FIND_MANAGER = wx.NewEventType()
+wxEVT_AUI_PANE_MINIMIZE = wx.NewEventType()
+wxEVT_AUI_PANE_MIN_RESTORE = wx.NewEventType()
+wxEVT_AUI_PANE_FLOATING = wx.NewEventType()
+wxEVT_AUI_PANE_FLOATED = wx.NewEventType()
+wxEVT_AUI_PANE_DOCKING = wx.NewEventType()
+wxEVT_AUI_PANE_DOCKED = wx.NewEventType()
+wxEVT_AUI_PANE_ACTIVATED = wx.NewEventType()
+wxEVT_AUI_PERSPECTIVE_CHANGED = wx.NewEventType()
+
+EVT_AUI_PANE_BUTTON = wx.PyEventBinder(wxEVT_AUI_PANE_BUTTON, 0)
+""" Fires an event when the user left-clicks on a pane button. """
+EVT_AUI_PANE_CLOSE = wx.PyEventBinder(wxEVT_AUI_PANE_CLOSE, 0)
+""" A pane in `AuiManager` has been closed. """
+EVT_AUI_PANE_MAXIMIZE = wx.PyEventBinder(wxEVT_AUI_PANE_MAXIMIZE, 0)
+""" A pane in `AuiManager` has been maximized. """
+EVT_AUI_PANE_RESTORE = wx.PyEventBinder(wxEVT_AUI_PANE_RESTORE, 0)
+""" A pane in `AuiManager` has been restored from a maximized state. """
+EVT_AUI_RENDER = wx.PyEventBinder(wxEVT_AUI_RENDER, 0)
+""" Fires an event every time the AUI frame is being repainted. """
+EVT_AUI_FIND_MANAGER = wx.PyEventBinder(wxEVT_AUI_FIND_MANAGER, 0)
+""" Used to find which AUI manager is controlling a certain pane. """
+EVT_AUI_PANE_MINIMIZE = wx.PyEventBinder(wxEVT_AUI_PANE_MINIMIZE, 0)
+""" A pane in `AuiManager` has been minimized. """
+EVT_AUI_PANE_MIN_RESTORE = wx.PyEventBinder(wxEVT_AUI_PANE_MIN_RESTORE, 0)
+""" A pane in `AuiManager` has been restored from a minimized state. """
+EVT_AUI_PANE_FLOATING = wx.PyEventBinder(wxEVT_AUI_PANE_FLOATING, 0)
+""" A pane in `AuiManager` is about to be floated. """
+EVT_AUI_PANE_FLOATED = wx.PyEventBinder(wxEVT_AUI_PANE_FLOATED, 0)
+""" A pane in `AuiManager` has been floated. """
+EVT_AUI_PANE_DOCKING = wx.PyEventBinder(wxEVT_AUI_PANE_DOCKING, 0)
+""" A pane in `AuiManager` is about to be docked. """
+EVT_AUI_PANE_DOCKED = wx.PyEventBinder(wxEVT_AUI_PANE_DOCKED, 0)
+""" A pane in `AuiManager` has been docked. """
+EVT_AUI_PANE_ACTIVATED = wx.PyEventBinder(wxEVT_AUI_PANE_ACTIVATED, 0)
+""" A pane in `AuiManager` has been activated. """
+EVT_AUI_PERSPECTIVE_CHANGED = wx.PyEventBinder(wxEVT_AUI_PERSPECTIVE_CHANGED, 0)
+""" The layout in `AuiManager` has been changed. """
+
+# ---------------------------------------------------------------------------- #
+
+class AuiDockInfo(object):
+    """ A class to store all properties of a dock. """
+
+    def __init__(self):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+        """
+
+        object.__init__(self)
+        
+        self.dock_direction = 0
+        self.dock_layer = 0
+        self.dock_row = 0
+        self.size = 0
+        self.min_size = 0
+        self.resizable = True
+        self.fixed = False
+        self.toolbar = False
+        self.rect = wx.Rect()
+        self.panes = []
+
+
+    def IsOk(self):
+        """
+        Returns whether a dock is valid or not.
+
+        In order to be valid, a dock needs to have a non-zero `dock_direction`.
+        """
+
+        return self.dock_direction != 0
+
+    
+    def IsHorizontal(self):
+        """ Returns whether the dock is horizontal or not. """
+
+        return self.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]
+
+
+    def IsVertical(self):
+        """ Returns whether the dock is vertical or not. """
+
+        return self.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT, AUI_DOCK_CENTER]
+    
+
+# ---------------------------------------------------------------------------- #
+
+class AuiDockingGuideInfo(object):
+    """ A class which holds information about VS2005 docking guide windows. """
+
+    def __init__(self, other=None):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+
+        :param `other`: another instance of L{AuiDockingGuideInfo}.
+        """
+
+        if other:
+            self.Assign(other)
+        else:
+            # window representing the docking target
+            self.host = None
+            # dock direction (top, bottom, left, right, center)
+            self.dock_direction = AUI_DOCK_NONE
+
+
+    def Assign(self, other):
+        """
+        Assigns the properties of the `other` L{AuiDockingGuideInfo} to `self`.
+
+        :param `other`: another instance of L{AuiDockingGuideInfo}.
+        """
+
+        self.host = other.host
+        self.dock_direction = other.dock_direction
+
+
+    def Host(self, h):
+        """
+        Hosts a docking guide window.
+
+        :param `h`: an instance of L{AuiSingleDockingGuide} or L{AuiCenterDockingGuide}.
+        """
+
+        self.host = h
+        return self
+    
+
+    def Left(self):
+        """ Sets the guide window to left docking. """
+
+        self.dock_direction = AUI_DOCK_LEFT
+        return self
+
+    
+    def Right(self):
+        """ Sets the guide window to right docking. """
+
+        self.dock_direction = AUI_DOCK_RIGHT
+        return self 
+
+
+    def Top(self):
+        """ Sets the guide window to top docking. """
+
+        self.dock_direction = AUI_DOCK_TOP
+        return self 
+
+
+    def Bottom(self):
+        """ Sets the guide window to bottom docking. """
+
+        self.dock_direction = AUI_DOCK_BOTTOM
+        return self 
+
+
+    def Center(self):
+        """ Sets the guide window to center docking. """
+
+        self.dock_direction = AUI_DOCK_CENTER
+        return self 
+
+
+    def Centre(self):
+        """ Sets the guide window to centre docking. """
+        
+        self.dock_direction = AUI_DOCK_CENTRE
+        return self
+
+
+# ---------------------------------------------------------------------------- #
+
+class AuiDockUIPart(object):
+    """ A class which holds attributes for a UI part in the interface. """
+    
+    typeCaption = 0
+    typeGripper = 1
+    typeDock = 2
+    typeDockSizer = 3
+    typePane = 4
+    typePaneSizer = 5
+    typeBackground = 6
+    typePaneBorder = 7
+    typePaneButton = 8
+
+    def __init__(self):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+        """
+        
+        self.orientation = wx.VERTICAL
+        self.type = 0
+        self.rect = wx.Rect()
+
+
+# ---------------------------------------------------------------------------- #
+
+class AuiPaneButton(object):
+    """ A simple class which describes the caption pane button attributes. """
+
+    def __init__(self, button_id):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+
+        :param `button_id`: the pane button identifier.
+        """
+
+        self.button_id = button_id
+
+
+# ---------------------------------------------------------------------------- #
+
+# event declarations/classes
+
+class AuiManagerEvent(wx.PyCommandEvent):
+    """ A specialized command event class for events sent by L{AuiManager}. """
+
+    def __init__(self, eventType, id=1):
+        """
+        Default class constructor.
+
+        :param `eventType`: the event kind;
+        :param `id`: the event identification number.
+        """
+
+        wx.PyCommandEvent.__init__(self, eventType, id)
+
+        self.manager = None
+        self.pane = None
+        self.button = 0
+        self.veto_flag = False
+        self.canveto_flag = True
+        self.dc = None
+
+
+    def SetManager(self, mgr):
+        """
+        Associates a L{AuiManager} to the current event.
+
+        :param `mgr`: an instance of L{AuiManager}.
+        """
+
+        self.manager = mgr
+
+
+    def SetDC(self, pdc):
+        """
+        Associates a `wx.DC` device context to this event.
+
+        :param `pdc`: a `wx.DC` device context object. 
+        """
+
+        self.dc = pdc
+
+
+    def SetPane(self, p):
+        """
+        Associates a L{AuiPaneInfo} instance to this event.
+
+        :param `p`: a L{AuiPaneInfo} instance.
+        """
+        
+        self.pane = p
+
+        
+    def SetButton(self, b):
+        """
+        Associates a L{AuiPaneButton} instance to this event.
+
+        :param `b`: a L{AuiPaneButton} instance.
+        """
+        
+        self.button = b
+
+        
+    def GetManager(self):
+        """ Returns the associated L{AuiManager} (if any). """
+
+        return self.manager
+
+
+    def GetDC(self):
+        """ Returns the associated `wx.DC` device context (if any). """
+
+        return self.dc
+    
+
+    def GetPane(self):
+        """ Returns the associated L{AuiPaneInfo} structure (if any). """
+        
+        return self.pane
+
+
+    def GetButton(self):
+        """ Returns the associated L{AuiPaneButton} instance (if any). """
+
+        return self.button
+
+
+    def Veto(self, veto=True):
+        """
+        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.
+
+        :param `veto`: ``True`` to veto the event, ``False`` otherwise.
+        """
+
+        self.veto_flag = veto
+
+        
+    def GetVeto(self):
+        """ Returns whether the event has been vetoed or not. """
+
+        return self.veto_flag
+
+    
+    def SetCanVeto(self, can_veto):
+        """
+        Sets whether the event can be vetoed or not.
+
+        :param `can_veto`: a bool flag. ``True`` if the event can be vetoed, ``False`` otherwise.
+        """
+
+        self.canveto_flag = can_veto
+
+        
+    def CanVeto(self):
+        """ Returns whether the event can be vetoed and has been vetoed. """
+
+        return  self.canveto_flag and self.veto_flag
+
+
+# ---------------------------------------------------------------------------- #
+
+class AuiPaneInfo(object):
+    """
+    AuiPaneInfo specifies all the parameters for a pane. These parameters specify where
+    the pane is on the screen, whether it is docked or floating, or hidden. In addition,
+    these parameters specify the pane's docked position, floating position, preferred
+    size, minimum size, caption text among many other parameters.
+    """
+    
+    optionFloating         = 2**0
+    optionHidden           = 2**1
+    optionLeftDockable     = 2**2
+    optionRightDockable    = 2**3
+    optionTopDockable      = 2**4
+    optionBottomDockable   = 2**5
+    optionFloatable        = 2**6
+    optionMovable          = 2**7
+    optionResizable        = 2**8
+    optionPaneBorder       = 2**9
+    optionCaption          = 2**10
+    optionGripper          = 2**11
+    optionDestroyOnClose   = 2**12
+    optionToolbar          = 2**13
+    optionActive           = 2**14
+    optionGripperTop       = 2**15
+    optionMaximized        = 2**16
+    optionDockFixed        = 2**17
+    optionNotebookDockable = 2**18
+    optionMinimized        = 2**19
+    optionLeftSnapped      = 2**20
+    optionRightSnapped     = 2**21
+    optionTopSnapped       = 2**22
+    optionBottomSnapped    = 2**23
+    optionFlyOut           = 2**24
+    optionCaptionLeft      = 2**25
+
+    buttonClose            = 2**26
+    buttonMaximize         = 2**27
+    buttonMinimize         = 2**28
+    buttonPin              = 2**29
+    
+    buttonCustom1          = 2**30
+    buttonCustom2          = 2**31
+    buttonCustom3          = 2**32
+
+    savedHiddenState       = 2**33    # used internally
+    actionPane             = 2**34    # used internally
+    wasMaximized           = 2**35    # used internally
+    needsRestore           = 2**36    # used internally
+
+
+    def __init__(self):
+        """ Default class constructor. """
+        
+        self.window = None
+        self.frame = None
+        self.state = 0
+        self.dock_direction = AUI_DOCK_LEFT
+        self.dock_layer = 0
+        self.dock_row = 0
+        self.dock_pos = 0
+        self.minimize_mode = AUI_MINIMIZE_POS_SMART
+        self.floating_pos = wx.Point(-1, -1)
+        self.floating_size = wx.Size(-1, -1)
+        self.best_size = wx.Size(-1, -1)
+        self.min_size = wx.Size(-1, -1)
+        self.max_size = wx.Size(-1, -1)
+        self.dock_proportion = 0
+        self.caption = ""
+        self.buttons = []
+        self.name = ""
+        self.icon = wx.NullIcon
+        self.rect = wx.Rect()
+        self.notebook_id = -1
+        self.transparent = 255
+        self.needsTransparency = False
+        self.previousDockPos = None
+        self.previousDockSize = 0
+        self.snapped = 0
+        
+        self.DefaultPane()
+        
+    
+    def dock_direction_get(self):
+        """
+        Getter for the `dock_direction`.
+
+        :see: L{dock_direction_set} for a set of valid docking directions.
+        """
+        
+        if self.IsMaximized():
+            return AUI_DOCK_CENTER
+        else:
+            return self._dock_direction
+
+
+    def dock_direction_set(self, value):
+        """
+        Setter for the `dock_direction`.
+
+        :param `value`: the docking direction. This cab ne one of the following bits:
+
+        ============================ ======= =============================================
+        Dock Flag                     Value  Description
+        ============================ ======= =============================================
+        ``AUI_DOCK_NONE``                  0 No docking direction.
+        ``AUI_DOCK_TOP``                   1 Top docking direction.
+        ``AUI_DOCK_RIGHT``                 2 Right docking direction.
+        ``AUI_DOCK_BOTTOM``                3 Bottom docking direction.
+        ``AUI_DOCK_LEFT``                  4 Left docking direction.
+        ``AUI_DOCK_CENTER``                5 Center docking direction.
+        ``AUI_DOCK_CENTRE``                5 Centre docking direction.
+        ``AUI_DOCK_NOTEBOOK_PAGE``         6 Automatic AuiNotebooks docking style.
+        ============================ ======= =============================================
+        
+        """
+        
+        self._dock_direction = value
+        
+    dock_direction = property(dock_direction_get, dock_direction_set)
+
+    def IsOk(self):
+        """
+        Returns ``True`` if the L{AuiPaneInfo} structure is valid.
+
+        :note: A pane structure is valid if it has an associated window.
+        """
+        
+        return self.window != None
+
+
+    def IsMaximized(self):
+        """ Returns ``True`` if the pane is maximized. """
+        
+        return self.HasFlag(self.optionMaximized)
+
+
+    def IsMinimized(self):
+        """ Returns ``True`` if the pane is minimized. """
+
+        return self.HasFlag(self.optionMinimized)
+
+
+    def IsFixed(self):
+        """ Returns ``True`` if the pane cannot be resized. """
+        
+        return not self.HasFlag(self.optionResizable)
+
+    
+    def IsResizeable(self):
+        """ Returns ``True`` if the pane can be resized. """
+        
+        return self.HasFlag(self.optionResizable)
+
+    
+    def IsShown(self):
+        """ Returns ``True`` if the pane is currently shown. """
+        
+        return not self.HasFlag(self.optionHidden)
+
+    
+    def IsFloating(self):
+        """ Returns ``True`` if the pane is floating. """
+
+        return self.HasFlag(self.optionFloating)
+
+    
+    def IsDocked(self):
+        """ Returns ``True`` if the pane is docked. """
+        
+        return not self.HasFlag(self.optionFloating)
+
+    
+    def IsToolbar(self):
+        """ Returns ``True`` if the pane contains a toolbar. """
+
+        return self.HasFlag(self.optionToolbar)
+
+    
+    def IsTopDockable(self):
+        """
+        Returns ``True`` if the pane can be docked at the top
+        of the managed frame.
+        """
+        
+        return self.HasFlag(self.optionTopDockable)
+
+    
+    def IsBottomDockable(self):
+        """
+        Returns ``True`` if the pane can be docked at the bottom
+        of the managed frame.
+        """
+        
+        return self.HasFlag(self.optionBottomDockable)
+
+    
+    def IsLeftDockable(self):
+        """
+        Returns ``True`` if the pane can be docked at the left
+        of the managed frame.
+        """
+        
+        return self.HasFlag(self.optionLeftDockable) 
+
+
+    def IsRightDockable(self):
+        """
+        Returns ``True`` if the pane can be docked at the right
+        of the managed frame.
+        """
+        
+        return self.HasFlag(self.optionRightDockable)
+
+
+    def IsDockable(self):
+        """ Returns ``True`` if the pane can be docked. """
+        
+        return self.IsTopDockable() or self.IsBottomDockable() or self.IsLeftDockable() or \
+               self.IsRightDockable() or self.IsNotebookDockable()
+    
+    
+    def IsFloatable(self):
+        """
+        Returns ``True`` if the pane can be undocked and displayed as a
+        floating window.
+        """
+
+        return self.HasFlag(self.optionFloatable)
+
+    
+    def IsMovable(self):
+        """
+        Returns ``True`` if the docked frame can be undocked or moved to
+        another dock position.
+        """
+        
+        return self.HasFlag(self.optionMovable)
+
+
+    def IsDestroyOnClose(self):
+        """
+        Returns ``True`` if the pane should be destroyed when it is closed.
+        
+        Normally a pane is simply hidden when the close button is clicked. Calling L{DestroyOnClose}
+        with a ``True`` input parameter will cause the window to be destroyed when the user clicks
+        the pane's close button.
+        """
+        
+        return self.HasFlag(self.optionDestroyOnClose)
+    
+
+    def IsNotebookDockable(self):
+        """
+        Returns ``True`` if a pane can be docked on top to another to create a
+        L{AuiNotebook}.
+        """
+
+        return self.HasFlag(self.optionNotebookDockable)
+    
+
+    def IsTopSnappable(self):
+        """ Returns ``True`` if the pane can be snapped at the top of the managed frame. """
+        
+        return self.HasFlag(self.optionTopSnapped)
+
+    
+    def IsBottomSnappable(self):
+        """ Returns ``True`` if the pane can be snapped at the bottom of the managed frame. """
+        
+        return self.HasFlag(self.optionBottomSnapped)
+
+    
+    def IsLeftSnappable(self):
+        """ Returns ``True`` if the pane can be snapped on the left of the managed frame. """
+        
+        return self.HasFlag(self.optionLeftSnapped) 
+
+
+    def IsRightSnappable(self):
+        """ Returns ``True`` if the pane can be snapped on the right of the managed frame. """
+        
+        return self.HasFlag(self.optionRightSnapped)
+
+
+    def IsSnappable(self):
+        """ Returns ``True`` if the pane can be snapped. """
+        
+        return self.IsTopSnappable() or self.IsBottomSnappable() or self.IsLeftSnappable() or \
+               self.IsRightSnappable()
+
+
+    def IsFlyOut(self):
+        """ Returns ``True`` if the floating pane has a "fly-out" effect. """
+
+        return self.HasFlag(self.optionFlyOut)        
+            
+
+    def HasCaption(self):
+        """ Returns ``True`` if the pane displays a caption. """
+        
+        return self.HasFlag(self.optionCaption)
+
+    
+    def HasCaptionLeft(self):
+        """ Returns ``True`` if the pane displays a caption on the left (rotated by 90 degrees). """
+        
+        return self.HasFlag(self.optionCaptionLeft)
+
+
+    def HasGripper(self):
+        """ Returns ``True`` if the pane displays a gripper. """
+        
+        return self.HasFlag(self.optionGripper) 
+
+
+    def HasBorder(self):
+        """ Returns ``True`` if the pane displays a border. """
+        
+        return self.HasFlag(self.optionPaneBorder)
+
+    
+    def HasCloseButton(self):
+        """ Returns ``True`` if the pane displays a button to close the pane. """
+
+        return self.HasFlag(self.buttonClose) 
+
+
+    def HasMaximizeButton(self):
+        """ Returns ``True`` if the pane displays a button to maximize the pane. """
+        
+        return self.HasFlag(self.buttonMaximize)
+
+    
+    def HasMinimizeButton(self):
+        """ Returns ``True`` if the pane displays a button to minimize the pane. """
+        
+        return self.HasFlag(self.buttonMinimize) 
+
+
+    def GetMinimizeMode(self):
+        """
+        Returns the minimization style for this pane.
+
+        Possible return values are:
+
+        ============================== ========= ==============================
+        Minimize Mode Flag             Hex Value Description
+        ============================== ========= ==============================
+        ``AUI_MINIMIZE_POS_SMART``          0x01 Minimizes the pane on the closest tool bar
+        ``AUI_MINIMIZE_POS_TOP``            0x02 Minimizes the pane on the top tool bar
+        ``AUI_MINIMIZE_POS_LEFT``           0x03 Minimizes the pane on its left tool bar
+        ``AUI_MINIMIZE_POS_RIGHT``          0x04 Minimizes the pane on its right tool bar
+        ``AUI_MINIMIZE_POS_BOTTOM``         0x05 Minimizes the pane on its bottom tool bar
+        ``AUI_MINIMIZE_POS_MASK``           0x07 Mask to filter the position flags
+        ``AUI_MINIMIZE_CAPT_HIDE``           0x0 Hides the caption of the minimized pane
+        ``AUI_MINIMIZE_CAPT_SMART``         0x08 Displays the caption in the best rotation (horizontal or clockwise)
+        ``AUI_MINIMIZE_CAPT_HORZ``          0x10 Displays the caption horizontally
+        ``AUI_MINIMIZE_CAPT_MASK``          0x18 Mask to filter the caption flags
+        ============================== ========= ==============================
+
+        The flags can be filtered with the following masks:
+
+        ============================== ========= ==============================
+        Minimize Mask Flag             Hex Value Description
+        ============================== ========= ==============================        
+        ``AUI_MINIMIZE_POS_MASK``           0x07 Filters the position flags
+        ``AUI_MINIMIZE_CAPT_MASK``          0x18 Filters the caption flags
+        ============================== ========= ==============================
+
+        """
+        
+        return self.minimize_mode
+    
+
+    def HasPinButton(self):
+        """ Returns ``True`` if the pane displays a button to float the pane. """
+        
+        return self.HasFlag(self.buttonPin) 
+
+
+    def HasGripperTop(self):
+        """ Returns ``True`` if the pane displays a gripper at the top. """
+
+        return self.HasFlag(self.optionGripperTop)
+
+
+    def Window(self, w):
+        """
+        Associate a `wx.Window` derived window to this pane.
+
+        This normally does not need to be specified, as the window pointer is
+        automatically assigned to the L{AuiPaneInfo} structure as soon as it is
+        added to the manager.
+
+        :param `w`: a `wx.Window` derived window.
+        """
+
+        self.window = w
+        return self
+
+    
+    def Name(self, name):
+        """
+        Sets the name of the pane so it can be referenced in lookup functions.
+
+        If a name is not specified by the user, a random name is assigned to the pane
+        when it is added to the manager.
+
+        :param `name`: a string specifying the pane name.
+
+        :warning: If you are using L{AuiManager.SavePerspective} and L{AuiManager.LoadPerspective}, you will have
+         to specify a name for your pane using L{Name}, as randomly generated names can
+         not be properly restored.
+        """
+
+        self.name = name
+        return self
+
+    
+    def Caption(self, caption):
+        """
+        Sets the caption of the pane.
+
+        :param `caption`: a string specifying the pane caption.
+        """
+        
+        self.caption = caption
+        return self
+
+    
+    def Left(self):
+        """ 
+        Sets the pane dock position to the left side of the frame.
+
+        :note: This is the same thing as calling L{Direction} with ``AUI_DOCK_LEFT`` as
+         parameter.
+        """
+        
+        self.dock_direction = AUI_DOCK_LEFT
+        return self
+
+    
+    def Right(self):
+        """
+        Sets the pane dock position to the right side of the frame.
+
+        :note: This is the same thing as calling L{Direction} with ``AUI_DOCK_RIGHT`` as
+         parameter.
+        """
+        
+        self.dock_direction = AUI_DOCK_RIGHT
+        return self
+
+    
+    def Top(self):
+        """
+        Sets the pane dock position to the top of the frame.
+
+        :note: This is the same thing as calling L{Direction} with ``AUI_DOCK_TOP`` as
+         parameter.
+        """
+
+        self.dock_direction = AUI_DOCK_TOP
+        return self
+
+    
+    def Bottom(self):
+        """
+        Sets the pane dock position to the bottom of the frame.
+
+        :note: This is the same thing as calling L{Direction} with ``AUI_DOCK_BOTTOM`` as
+         parameter.
+        """
+
+        self.dock_direction = AUI_DOCK_BOTTOM
+        return self
+
+    
+    def Center(self):
+        """
+        Sets the pane to the center position of the frame.
+
+        The centre pane is the space in the middle after all border panes (left, top,
+        right, bottom) are subtracted from the layout.
+
+        :note: This is the same thing as calling L{Direction} with ``AUI_DOCK_CENTER`` as
+         parameter.
+        """
+        
+        self.dock_direction = AUI_DOCK_CENTER
+        return self
+
+        
+    def Centre(self):
+        """
+        Sets the pane to the center position of the frame.
+
+        The centre pane is the space in the middle after all border panes (left, top,
+        right, bottom) are subtracted from the layout.
+
+        :note: This is the same thing as calling L{Direction} with ``AUI_DOCK_CENTRE`` as
+         parameter.
+        """
+        
+        self.dock_direction = AUI_DOCK_CENTRE
+        return self
+
+    
+    def Direction(self, direction):
+        """
+        Determines the direction of the docked pane. It is functionally the
+        same as calling L{Left}, L{Right}, L{Top} or L{Bottom}, except that docking direction
+        may be specified programmatically via the parameter `direction`.
+
+        :param `direction`: the direction of the docked pane.
+
+        :see: L{dock_direction_set} for a list of valid docking directions.        
+        """
+        
+        self.dock_direction = direction
+        return self
+
+    
+    def Layer(self, layer):
+        """
+        Determines the layer of the docked pane.
+
+        The dock layer is similar to an onion, the inner-most layer being layer 0. Each
+        shell moving in the outward direction has a higher layer number. This allows for
+        more complex docking layout formation.
+
+        :param `layer`: the layer of the docked pane.
+        """
+        
+        self.dock_layer = layer
+        return self
+
+    
+    def Row(self, row):
+        """
+        Determines the row of the docked pane.
+
+        :param `row`: the row of the docked pane.
+        """
+        
+        self.dock_row = row
+        return self
+
+    
+    def Position(self, pos):
+        """
+        Determines the position of the docked pane.
+
+        :param `pos`: the position of the docked pane.
+        """
+
+        self.dock_pos = pos
+        return self
+
+
+    def MinSize(self, arg1=None, arg2=None):
+        """
+        Sets the minimum size of the pane.
+
+        This method is split in 2 versions depending on the input type. If `arg1` is
+        a `wx.Size` object, then L{MinSize1} is called. Otherwise, L{MinSize2} is called.
+
+        :param `arg1`: a `wx.Size` object, a (x, y) tuple or or a `x` coordinate.
+        :param `arg2`: a `y` coordinate (only if `arg1` is a `x` coordinate, otherwise unused).
+        """
+        
+        if isinstance(arg1, wx.Size):
+            ret = self.MinSize1(arg1)
+        elif isinstance(arg1, types.TupleType):
+            ret = self.MinSize1(wx.Size(*arg1))
+        else:
+            ret = self.MinSize2(arg1, arg2)
+
+        return ret
+
+    
+    def MinSize1(self, size):
+        """
+        Sets the minimum size of the pane.
+
+        :see: L{MinSize} for an explanation of input parameters.
+        """
+        self.min_size = size
+        return self
+
+
+    def MinSize2(self, x, y):
+        """
+        Sets the minimum size of the pane.
+
+        :see: L{MinSize} for an explanation of input parameters.
+        """
+
+        self.min_size = wx.Size(x, y)
+        return self
+
+
+    def MaxSize(self, arg1=None, arg2=None):
+        """
+        Sets the maximum size of the pane.
+
+        This method is split in 2 versions depending on the input type. If `arg1` is
+        a `wx.Size` object, then L{MaxSize1} is called. Otherwise, L{MaxSize2} is called.
+
+        :param `arg1`: a `wx.Size` object, a (x, y) tuple or a `x` coordinate.
+        :param `arg2`: a `y` coordinate (only if `arg1` is a `x` coordinate, otherwise unused).
+        """
+        
+        if isinstance(arg1, wx.Size):
+            ret = self.MaxSize1(arg1)
+        elif isinstance(arg1, types.TupleType):
+            ret = self.MaxSize1(wx.Size(*arg1))
+        else:
+            ret = self.MaxSize2(arg1, arg2)
+
+        return ret
+    
+    
+    def MaxSize1(self, size):
+        """
+        Sets the maximum size of the pane.
+
+        :see: L{MaxSize} for an explanation of input parameters.
+        """
+
+        self.max_size = size
+        return self
+
+
+    def MaxSize2(self, x, y):
+        """
+        Sets the maximum size of the pane.
+
+        :see: L{MaxSize} for an explanation of input parameters.
+        """
+
+        self.max_size.Set(x,y)
+        return self
+
+
+    def BestSize(self, arg1=None, arg2=None):
+        """
+        Sets the ideal size for the pane. The docking manager will attempt to use
+        this size as much as possible when docking or floating the pane.
+
+        This method is split in 2 versions depending on the input type. If `arg1` is
+        a `wx.Size` object, then L{BestSize1} is called. Otherwise, L{BestSize2} is called.
+
+        :param `arg1`: a `wx.Size` object, a (x, y) tuple or a `x` coordinate.
+        :param `arg2`: a `y` coordinate (only if `arg1` is a `x` coordinate, otherwise unused).
+        """
+        
+        if isinstance(arg1, wx.Size):
+            ret = self.BestSize1(arg1)
+        elif isinstance(arg1, types.TupleType):
+            ret = self.BestSize1(wx.Size(*arg1))
+        else:
+            ret = self.BestSize2(arg1, arg2)
+
+        return ret
+    
+            
+    def BestSize1(self, size):
+        """
+        Sets the best size of the pane.
+
+        :see: L{BestSize} for an explanation of input parameters.
+        """
+
+        self.best_size = size
+        return self
+
+    
+    def BestSize2(self, x, y):
+        """
+        Sets the best size of the pane.
+
+        :see: L{BestSize} for an explanation of input parameters.
+        """
+
+        self.best_size.Set(x,y)
+        return self
+    
+    
+    def FloatingPosition(self, pos):
+        """
+        Sets the position of the floating pane.
+
+        :param `pos`: a `wx.Point` or a tuple indicating the pane floating position.
+        """
+        
+        self.floating_pos = wx.Point(*pos)
+        return self
+
+    
+    def FloatingSize(self, size):
+        """
+        Sets the size of the floating pane.
+
+        :param `size`: a `wx.Size` or a tuple indicating the pane floating size.
+        """
+        
+        self.floating_size = wx.Size(*size)
+        return self
+
+
+    def Maximize(self):
+        """ Makes the pane take up the full area."""
+
+        return self.SetFlag(self.optionMaximized, True)
+
+
+    def Minimize(self):
+        """
+        Makes the pane minimized in a L{AuiToolBar}.
+
+        Clicking on the minimize button causes a new L{AuiToolBar} to be created
+        and added to the frame manager, (currently the implementation is such that
+        panes at West will have a toolbar at the right, panes at South will have
+        toolbars at the bottom etc...) and the pane is hidden in the manager.
+        
+        Clicking on the restore button on the newly created toolbar will result in the
+        toolbar being removed and the original pane being restored.
+        """
+        
+        return self.SetFlag(self.optionMinimized, True)
+
+
+    def MinimizeMode(self, mode):
+        """
+        Sets the expected minimized mode if the MinimizeButton() is visible.
+
+        The minimized pane can have a specific position in the work space:
+
+        ============================== ========= ==============================
+        Minimize Mode Flag             Hex Value Description
+        ============================== ========= ==============================
+        ``AUI_MINIMIZE_POS_SMART``          0x01 Minimizes the pane on the closest tool bar
+        ``AUI_MINIMIZE_POS_TOP``            0x02 Minimizes the pane on the top tool bar
+        ``AUI_MINIMIZE_POS_LEFT``           0x03 Minimizes the pane on its left tool bar
+        ``AUI_MINIMIZE_POS_RIGHT``          0x04 Minimizes the pane on its right tool bar
+        ``AUI_MINIMIZE_POS_BOTTOM``         0x05 Minimizes the pane on its bottom tool bar
+        ============================== ========= ==============================
+
+        The caption of the minimized pane can be displayed in different modes:
+
+        ============================== ========= ==============================
+        Caption Mode Flag              Hex Value Description
+        ============================== ========= ==============================
+        ``AUI_MINIMIZE_CAPT_HIDE``           0x0 Hides the caption of the minimized pane
+        ``AUI_MINIMIZE_CAPT_SMART``         0x08 Displays the caption in the best rotation (horizontal in the top and in the bottom tool bar or clockwise in the right and in the left tool bar)
+        ``AUI_MINIMIZE_CAPT_HORZ``          0x10 Displays the caption horizontally
+        ============================== ========= ==============================
+        
+        """
+        
+        self.minimize_mode = mode
+        return self
+    
+
+    def Restore(self):
+        """ Is the reverse of L{Maximize} and L{Minimize}."""
+        
+        return self.SetFlag(self.optionMaximized or self.optionMinimized, False)
+
+    
+    def Fixed(self):
+        """
+        Forces a pane to be fixed size so that it cannot be resized.
+        After calling L{Fixed}, L{IsFixed} will return ``True``.
+        """
+        
+        return self.SetFlag(self.optionResizable, False)
+
+    
+    def Resizable(self, resizable=True):
+        """
+        Allows a pane to be resizable if `resizable` is ``True``, and forces
+        it to be a fixed size if `resizeable` is ``False``.
+
+        If `resizable` is ``False``, this is simply an antonym for L{Fixed}.
+
+        :param `resizable`: whether the pane will be resizeable or not.
+        """
+        
+        return self.SetFlag(self.optionResizable, resizable)
+
+
+    def Transparent(self, alpha):
+        """
+        Makes the pane transparent when floating.
+
+        :param `alpha`: an integer value between 0 and 255 for pane transparency.
+        """
+
+        if alpha < 0 or alpha > 255:
+            raise Exception("Invalid transparency value (%s)"%repr(alpha))
+                            
+        self.transparent = alpha
+        self.needsTransparency = True
+
+    
+    def Dock(self):
+        """
+        Indicates that a pane should be docked. It is the opposite of L{Float}.
+        """
+
+        if self.IsNotebookPage():
+            self.notebook_id = -1
+            self.dock_direction = AUI_DOCK_NONE
+        
+        return self.SetFlag(self.optionFloating, False)
+
+    
+    def Float(self):
+        """
+        Indicates that a pane should be floated. It is the opposite of L{Dock}.
+        """
+
+        if self.IsNotebookPage():
+            self.notebook_id = -1
+            self.dock_direction = AUI_DOCK_NONE
+
+        return self.SetFlag(self.optionFloating, True)
+
+    
+    def Hide(self):
+        """
+        Indicates that a pane should be hidden.
+
+        Calling L{Show} (``False``) achieve the same effect.
+        """
+        
+        return self.SetFlag(self.optionHidden, True)
+
+    
+    def Show(self, show=True):
+        """
+        Indicates that a pane should be shown.
+
+        :param `show`: whether the pane should be shown or not.
+        """
+        
+        return self.SetFlag(self.optionHidden, not show)
+    
+
+    # By defaulting to 1000, the tab will get placed at the end
+    def NotebookPage(self, id, tab_position=1000):
+        """
+        Forces a pane to be a notebook page, so that the pane can be
+        docked on top to another to create a L{AuiNotebook}.
+
+        :param `id`: the notebook id;
+        :param `tab_position`: the tab number of the pane once docked in a notebook.
+        """
+        
+        # Remove any floating frame
+        self.Dock()
+        self.notebook_id = id
+        self.dock_pos = tab_position
+        self.dock_row = 0
+        self.dock_layer = 0
+        self.dock_direction = AUI_DOCK_NOTEBOOK_PAGE
+
+        return self
+
+
+    def NotebookControl(self, id):
+        """
+        Forces a pane to be a notebook control (L{AuiNotebook}).
+
+        :param `id`: the notebook id.
+        """
+
+        self.notebook_id = id
+        self.window = None
+        self.buttons = []
+        
+        if self.dock_direction == AUI_DOCK_NOTEBOOK_PAGE:
+            self.dock_direction = AUI_DOCK_NONE
+            
+        return self
+    
+
+    def HasNotebook(self):
+        """ Returns whether a pane has a L{AuiNotebook} or not. """
+
+        return self.notebook_id >= 0
+
+
+    def IsNotebookPage(self):
+        """ Returns whether the pane is a notebook page in a L{AuiNotebook}. """
+
+        return self.notebook_id >= 0 and self.dock_direction == AUI_DOCK_NOTEBOOK_PAGE
+
+
+    def IsNotebookControl(self):
+        """ Returns whether the pane is a notebook control (L{AuiNotebook}). """
+
+        return not self.IsNotebookPage() and self.HasNotebook()
+
+
+    def SetNameFromNotebookId(self):
+        """ Sets the pane name once docked in a L{AuiNotebook} using the notebook id. """
+
+        if self.notebook_id >= 0:
+            self.name = "__notebook_%d"%self.notebook_id
+            
+        return self
+
+
+    def CaptionVisible(self, visible=True, left=False):
+        """
+        Indicates that a pane caption should be visible. If `visible` is ``False``, no pane
+        caption is drawn.
+
+        :param `visible`: whether the caption should be visible or not;
+        :param `left`: whether the caption should be drawn on the left (rotated by 90 degrees) or not.
+        """
+
+        if left:
+            self.SetFlag(self.optionCaption, False)
+            return self.SetFlag(self.optionCaptionLeft, visible)
+
+        self.SetFlag(self.optionCaptionLeft, False)
+        return self.SetFlag(self.optionCaption, visible)
+
+    
+    def PaneBorder(self, visible=True):
+        """
+        Indicates that a border should be drawn for the pane.
+
+        :param `visible`: whether the pane border should be visible or not.
+        """
+        
+        return self.SetFlag(self.optionPaneBorder, visible)
+
+    
+    def Gripper(self, visible=True):
+        """
+        Indicates that a gripper should be drawn for the pane.
+
+        :param `visible`: whether the gripper should be visible or not.
+        """
+        
+        return self.SetFlag(self.optionGripper, visible)
+
+
+    def GripperTop(self, attop=True):
+        """
+        Indicates that a gripper should be drawn at the top of the pane.
+
+        :param `attop`: whether the gripper should be drawn at the top or not.
+        """
+        
+        return self.SetFlag(self.optionGripperTop, attop)
+
+    
+    def CloseButton(self, visible=True):
+        """
+        Indicates that a close button should be drawn for the pane.
+
+        :param `visible`: whether the close button should be visible or not.
+        """
+        
+        return self.SetFlag(self.buttonClose, visible)
+
+    
+    def MaximizeButton(self, visible=True):
+        """
+        Indicates that a maximize button should be drawn for the pane.
+
+        :param `visible`: whether the maximize button should be visible or not.
+        """
+        
+        return self.SetFlag(self.buttonMaximize, visible)
+
+    
+    def MinimizeButton(self, visible=True):
+        """
+        Indicates that a minimize button should be drawn for the pane.
+
+        :param `visible`: whether the minimize button should be visible or not.
+        """
+
+        return self.SetFlag(self.buttonMinimize, visible)
+
+    
+    def PinButton(self, visible=True):
+        """
+        Indicates that a pin button should be drawn for the pane.
+
+        :param `visible`: whether the pin button should be visible or not.
+        """
+        
+        return self.SetFlag(self.buttonPin, visible)
+
+    
+    def DestroyOnClose(self, b=True):
+        """
+        Indicates whether a pane should be destroyed when it is closed.
+
+        Normally a pane is simply hidden when the close button is clicked. Setting
+        `b` to ``True`` will cause the window to be destroyed when the user clicks
+        the pane's close button.
+
+        :param `b`: whether the pane should be destroyed when it is closed or not.
+        """
+        
+        return self.SetFlag(self.optionDestroyOnClose, b)
+
+    
+    def TopDockable(self, b=True):
+        """
+        Indicates whether a pane can be docked at the top of the frame.
+
+        :param `b`: whether the pane can be docked at the top or not.
+        """
+        
+        return self.SetFlag(self.optionTopDockable, b)
+
+    
+    def BottomDockable(self, b=True):
+        """
+        Indicates whether a pane can be docked at the bottom of the frame.
+
+        :param `b`: whether the pane can be docked at the bottom or not.
+        """
+        
+        return self.SetFlag(self.optionBottomDockable, b)
+
+    
+    def LeftDockable(self, b=True):
+        """
+        Indicates whether a pane can be docked on the left of the frame.
+
+        :param `b`: whether the pane can be docked at the left or not.
+        """
+
+        return self.SetFlag(self.optionLeftDockable, b)
+
+    
+    def RightDockable(self, b=True):
+        """
+        Indicates whether a pane can be docked on the right of the frame.
+
+        :param `b`: whether the pane can be docked at the right or not.
+        """
+        
+        return self.SetFlag(self.optionRightDockable, b)
+
+    
+    def Floatable(self, b=True):
+        """
+        Sets whether the user will be able to undock a pane and turn it
+        into a floating window.
+
+        :param `b`: whether the pane can be floated or not.
+        """
+        
+        return self.SetFlag(self.optionFloatable, b)
+
+    
+    def Movable(self, b=True):
+        """
+        Indicates whether a pane can be moved.
+
+        :param `b`: whether the pane can be moved or not.
+        """
+        
+        return self.SetFlag(self.optionMovable, b)
+
+
+    def NotebookDockable(self, b=True):
+        """
+        Indicates whether a pane can be docked in an automatic L{AuiNotebook}.
+
+        :param `b`: whether the pane can be docked in a notebook or not.
+        """
+        
+        return self.SetFlag(self.optionNotebookDockable, b)
+                                  
+
+    def DockFixed(self, b=True):
+        """
+        Causes the containing dock to have no resize sash. This is useful
+        for creating panes that span the entire width or height of a dock, but should
+        not be resizable in the other direction.
+
+        :param `b`: whether the pane will have a resize sash or not.
+        """
+
+        return self.SetFlag(self.optionDockFixed, b)
+
+                                   
+    def Dockable(self, b=True):
+        """
+        Specifies whether a frame can be docked or not. It is the same as specifying
+        L{TopDockable} . L{BottomDockable} . L{LeftDockable} . L{RightDockable} .
+
+        :param `b`: whether the frame can be docked or not.
+        """
+
+        return self.TopDockable(b).BottomDockable(b).LeftDockable(b).RightDockable(b)
+    
+
+    def TopSnappable(self, b=True):
+        """
+        Indicates whether a pane can be snapped at the top of the main frame.
+
+        :param `b`: whether the pane can be snapped at the top of the main frame or not.
+        """
+        
+        return self.SetFlag(self.optionTopSnapped, b)
+
+    
+    def BottomSnappable(self, b=True):
+        """
+        Indicates whether a pane can be snapped at the bottom of the main frame.
+
+        :param `b`: whether the pane can be snapped at the bottom of the main frame or not.
+        """
+        
+        return self.SetFlag(self.optionBottomSnapped, b)
+
+    
+    def LeftSnappable(self, b=True):
+        """
+        Indicates whether a pane can be snapped on the left of the main frame.
+
+        :param `b`: whether the pane can be snapped at the left of the main frame or not.
+        """
+
+        return self.SetFlag(self.optionLeftSnapped, b)
+
+    
+    def RightSnappable(self, b=True):
+        """
+        Indicates whether a pane can be snapped on the right of the main frame.
+
+        :param `b`: whether the pane can be snapped at the right of the main frame or not.
+        """
+        
+        return self.SetFlag(self.optionRightSnapped, b)
+
+
+    def Snappable(self, b=True):
+        """
+        Indicates whether a pane can be snapped on the main frame. This is
+        equivalent as calling L{TopSnappable} . L{BottomSnappable} . L{LeftSnappable} . L{RightSnappable} .
+
+        :param `b`: whether the pane can be snapped on the main frame or not.
+        """
+    
+        return self.TopSnappable(b).BottomSnappable(b).LeftSnappable(b).RightSnappable(b)
+
+
+    def FlyOut(self, b=True):
+        """
+        Indicates whether a pane, when floating, has a "fly-out" effect
+        (i.e., floating panes which only show themselves when moused over).
+
+        :param `b`: whether the pane can be snapped on the main frame or not.
+        """
+
+        return self.SetFlag(self.optionFlyOut, b)
+    
+    
+    # Copy over the members that pertain to docking position
+    def SetDockPos(self, source):
+        """
+        Copies the `source` pane members that pertain to docking position to `self`.
+
+        :param `source`: the source pane from where to copy the attributes.
+        """
+        
+        self.dock_direction = source.dock_direction
+        self.dock_layer = source.dock_layer
+        self.dock_row = source.dock_row
+        self.dock_pos = source.dock_pos
+        self.dock_proportion = source.dock_proportion
+        self.floating_pos = wx.Point(*source.floating_pos)
+        self.floating_size = wx.Size(*source.floating_size)
+        self.rect = wx.Rect(*source.rect)
+        
+        return self
+
+
+    def DefaultPane(self):
+        """ Specifies that the pane should adopt the default pane settings. """
+        
+        state = self.state    
+        state |= self.optionTopDockable | self.optionBottomDockable | \
+                 self.optionLeftDockable | self.optionRightDockable | \
+                 self.optionNotebookDockable | \
+                 self.optionFloatable | self.optionMovable | self.optionResizable | \
+                 self.optionCaption | self.optionPaneBorder | self.buttonClose
+
+        self.state = state
+        
+        return self
+    
+    
+    def CentrePane(self):
+        """
+        Specifies that the pane should adopt the default center pane settings.
+
+        Centre panes usually do not have caption bars. This function provides an easy way of
+        preparing a pane to be displayed in the center dock position.
+        """
+        
+        return self.CenterPane()
+
+    
+    def CenterPane(self):
+        """
+        Specifies that the pane should adopt the default center pane settings.
+
+        Centre panes usually do not have caption bars. This function provides an easy way of
+        preparing a pane to be displayed in the center dock position.
+        """
+        
+        self.state = 0
+        return self.Center().PaneBorder().Resizable()
+    
+     
+    def ToolbarPane(self):
+        """ Specifies that the pane should adopt the default toolbar pane settings. """
+        
+        self.DefaultPane()
+        state = self.state
+        
+        state |= (self.optionToolbar | self.optionGripper)
+        state &= ~(self.optionResizable | self.optionCaption | self.optionCaptionLeft)
+        
+        if self.dock_layer == 0:
+            self.dock_layer = 10
+
+        self.state = state
+        
+        return self
+
+
+    def Icon(self, icon):
+        """
+        Specifies whether an icon is drawn on the left of the caption text when
+        the pane is docked. If `icon` is ``None`` or `wx.NullIcon`, no icon is drawn on
+        the caption space.
+
+        :param icon: an icon to draw on the caption space, or ``None``.
+        """
+
+        if icon is None:
+            icon = wx.NullIcon
+            
+        self.icon = icon
+        return self        
+    
+
+    def SetFlag(self, flag, option_state):
+        """
+        Turns the property given by `flag` on or off with the `option_state`
+        parameter.
+
+        :param `flag`: the property to set;
+        :param `option_state`: either ``True`` or ``False``.
+        """
+        
+        state = self.state
+        
+        if option_state:
+            state |= flag
+        else:
+            state &= ~flag
+
+        self.state = state
+
+        if flag in [self.buttonClose, self.buttonMaximize, self.buttonMinimize, self.buttonPin]:
+            self.ResetButtons()
+            
+        return self
+    
+    
+    def HasFlag(self, flag):
+        """
+        Returns ``True`` if the the property specified by flag is active for the pane.
+
+        :param `flag`: the property to check for activity.
+        """
+        
+        return (self.state & flag and [True] or [False])[0]
+
+
+    def ResetButtons(self):
+        """
+        Resets all the buttons and recreates them from scratch depending on the
+        L{AuiPaneInfo} flags.
+        """
+
+        floating = self.HasFlag(self.optionFloating)
+        self.buttons = []
+
+        if not floating and self.HasMinimizeButton():
+            button = AuiPaneButton(AUI_BUTTON_MINIMIZE)
+            self.buttons.append(button)
+    
+        if not floating and self.HasMaximizeButton():
+            button = AuiPaneButton(AUI_BUTTON_MAXIMIZE_RESTORE)
+            self.buttons.append(button)
+
+        if not floating and self.HasPinButton():
+            button = AuiPaneButton(AUI_BUTTON_PIN)
+            self.buttons.append(button)
+
+        if self.HasCloseButton():
+            button = AuiPaneButton(AUI_BUTTON_CLOSE)
+            self.buttons.append(button)
+        
+
+    def CountButtons(self):
+        """ Returns the number of visible buttons in the docked pane. """
+
+        n = 0
+        
+        if self.HasCaption() or self.HasCaptionLeft():
+            if isinstance(wx.GetTopLevelParent(self.window), AuiFloatingFrame):
+                return 1
+            
+            if self.HasCloseButton():
+                n += 1
+            if self.HasMaximizeButton():
+                n += 1
+            if self.HasMinimizeButton():
+                n += 1
+            if self.HasPinButton():
+                n += 1
+
+        return n
+    
+
+    def IsHorizontal(self):
+        """ Returns ``True`` if the pane `dock_direction` is horizontal. """
+
+        return self.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]
+
+    def IsVertical(self):
+        """ Returns ``True`` if the pane `dock_direction` is vertical. """
+
+        return self.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]
+
+
+# Null AuiPaneInfo reference
+NonePaneInfo = AuiPaneInfo()
+
+
+# ---------------------------------------------------------------------------- #
+
+class AuiDockingGuide(wx.Frame):
+    """ Base class for L{AuiCenterDockingGuide} and L{AuiSingleDockingGuide}."""
+
+    def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP |
+                 wx.FRAME_NO_TASKBAR | wx.NO_BORDER, name="AuiDockingGuide"):
+        """
+        Default class constructor. Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiDockingGuide} parent;
+        :param `id`: the window identifier. It may take a value of -1 to indicate a default value.
+        :param `title`: the caption to be displayed on the frame's title bar.
+        :param `pos`: the window position. A value of (-1, -1) indicates a default position,
+         chosen by either the windowing system or wxPython, depending on platform.
+        :param `size`: the window 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. 
+        :param `name`: the name of the window. This parameter is used to associate a name with the
+         item, allowing the application user to set Motif resource values for individual windows.
+        """
+
+        wx.Frame.__init__(self, parent, id, title, pos, size, style, name=name)
+
+
+    def HitTest(self, x, y):
+        """
+        To be overridden by parent classes.
+
+        :param `x`: the `x` mouse position;
+        :param `y`: the `y` mouse position.
+        """
+
+        return 0
+
+    
+    def ValidateNotebookDocking(self, valid):
+        """
+        To be overridden by parent classes.
+
+        :param `valid`: whether a pane can be docked on top to another to form an automatic
+         L{AuiNotebook}.
+        """
+        
+        return 0
+
+# ============================================================================
+# implementation
+# ============================================================================
+
+# ---------------------------------------------------------------------------
+# AuiDockingGuideWindow
+# ---------------------------------------------------------------------------
+
+class AuiDockingGuideWindow(wx.Window):
+    """ Target class for L{AuiSingleDockingGuide} and L{AuiCenterDockingGuide}. """
+
+    def __init__(self, parent, rect, direction=0, center=False, useAero=False):
+        """
+        Default class constructor. Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiDockingGuideWindow} parent;
+        :param `rect`: the window rect;
+        :param `direction`: one of ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, ``wx.RIGHT``,
+         ``wx.CENTER``;
+        :param `center`: whether the calling class is a L{AuiCenterDockingGuide};
+        :param `useAero`: whether to use the new Aero-style bitmaps or Whidbey-style bitmaps
+         for the docking guide.
+        """
+
+        wx.Window.__init__(self, parent, -1, rect.GetPosition(), rect.GetSize(), wx.NO_BORDER)
+
+        self._direction = direction
+        self._center = center
+        self._valid = True
+        self._useAero = useAero
+        
+        self._bmp_unfocus, self._bmp_focus = GetDockingImage(direction, useAero, center)
+        
+        self._currentImage = self._bmp_unfocus
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+
+    def SetValid(self, valid):
+        """
+        Sets the docking direction as valid or invalid.
+
+        :param `valid`: whether the docking direction is allowed or not.
+        """
+
+        self._valid = valid
+
+
+    def IsValid(self):
+        """ Returns whether the docking direction is valid. """
+        
+        return self._valid
+
+
+    def OnEraseBackground(self, event):
+        """
+        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiDockingGuideWindow}.
+
+        :param `event`: a `wx.EraseEvent` to be processed.
+
+        :note: This is intentionally empty to reduce flickering while drawing.
+        """
+
+        pass
+
+
+    def DrawBackground(self, dc):
+        """
+        Draws the docking guide background.
+
+        :param `dc`: a `wx.DC` device context object.
+        """
+
+        rect = self.GetClientRect()
+
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        dc.SetBrush(wx.Brush(colourTargetBackground))
+        dc.DrawRectangleRect(rect)
+
+        dc.SetPen(wx.Pen(colourTargetBorder))
+
+        left = rect.GetLeft()
+        top = rect.GetTop()
+        right = rect.GetRight()
+        bottom = rect.GetBottom()
+
+        if self._direction != wx.CENTER:
+        
+            if not self._center or self._direction != wx.BOTTOM:
+                dc.DrawLine(left, top, right+1, top)
+            if not self._center or self._direction != wx.RIGHT:
+                dc.DrawLine(left, top, left, bottom+1)
+            if not self._center or self._direction != wx.LEFT:
+                dc.DrawLine(right, top, right, bottom+1)
+            if not self._center or self._direction != wx.TOP:
+                dc.DrawLine(left, bottom, right+1, bottom)
+
+            dc.SetPen(wx.Pen(colourTargetShade))
+
+            if self._direction != wx.RIGHT:
+                dc.DrawLine(left + 1, top + 1, left + 1, bottom)
+            if self._direction != wx.BOTTOM:
+                dc.DrawLine(left + 1, top + 1, right, top + 1)
+
+        
+    def DrawDottedLine(self, dc, point, length, vertical):
+        """
+        Draws a dotted line (not used if the docking guide images are ok).
+
+        :param `dc`: a `wx.DC` device context object;
+        :param `point`: a `wx.Point` where to start drawing the dotted line;
+        :param `length`: the length of the dotted line;
+        :param `vertical`: whether it is a vertical docking guide window or not.
+        """
+
+        for i in xrange(0, length, 2):
+            dc.DrawPoint(point.x, point.y)
+            if vertical:
+                point.y += 2
+            else:
+                point.x += 2
+        
+
+    def DrawIcon(self, dc):
+        """
+        Draws the docking guide icon (not used if the docking guide images are ok).
+
+        :param `dc`: a `wx.DC` device context object.
+        """
+
+        rect = wx.Rect(*self.GetClientRect())
+        point = wx.Point()
+        length = 0
+
+        rect.Deflate(4, 4)
+        dc.SetPen(wx.Pen(colourIconBorder))
+        dc.SetBrush(wx.Brush(colourIconBackground))
+        dc.DrawRectangleRect(rect)
+
+        right1 = rect.GetRight() + 1
+        bottom1 = rect.GetBottom() + 1
+
+        dc.SetPen(wx.Pen(colourIconShadow))
+        dc.DrawLine(rect.x + 1, bottom1, right1 + 1, bottom1)
+        dc.DrawLine(right1, rect.y + 1, right1, bottom1 + 1)
+
+        rect.Deflate(1, 1)
+
+        if self._direction == wx.TOP:
+            rect.height -= rect.height / 2
+            point = rect.GetBottomLeft()
+            length = rect.width
+
+        elif self._direction == wx.LEFT:
+            rect.width -= rect.width / 2
+            point = rect.GetTopRight()
+            length = rect.height
+
+        elif self._direction == wx.RIGHT:
+            rect.x += rect.width / 2
+            rect.width -= rect.width / 2
+            point = rect.GetTopLeft()
+            length = rect.height
+
+        elif self._direction == wx.BOTTOM:
+            rect.y += rect.height / 2
+            rect.height -= rect.height / 2
+            point = rect.GetTopLeft()
+            length = rect.width
+
+        elif self._direction == wx.CENTER:
+            rect.Deflate(1, 1)
+            point = rect.GetTopLeft()
+            length = rect.width
+
+        dc.GradientFillLinear(rect, colourIconDockingPart1,
+                              colourIconDockingPart2, self._direction)
+
+        dc.SetPen(wx.Pen(colourIconBorder))
+
+        if self._direction == wx.CENTER:        
+            self.DrawDottedLine(dc, rect.GetTopLeft(), rect.width, False)
+            self.DrawDottedLine(dc, rect.GetTopLeft(), rect.height, True)
+            self.DrawDottedLine(dc, rect.GetBottomLeft(), rect.width, False)
+            self.DrawDottedLine(dc, rect.GetTopRight(), rect.height, True)
+        
+        elif self._direction in [wx.TOP, wx.BOTTOM]:
+            self.DrawDottedLine(dc, point, length, False)
+        
+        else:
+            self.DrawDottedLine(dc, point, length, True)
+        
+
+    def DrawArrow(self, dc):
+        """
+        Draws the docking guide arrow icon (not used if the docking guide images are ok).
+
+        :param `dc`: a `wx.DC` device context object.
+        """
+
+        rect = self.GetClientRect()
+        point = wx.Point()
+
+        point.x = (rect.GetLeft() + rect.GetRight()) / 2
+        point.y = (rect.GetTop() + rect.GetBottom()) / 2
+        rx, ry = wx.Size(), wx.Size()
+        
+        if self._direction == wx.TOP:
+            rx = wx.Size(1, 0)
+            ry = wx.Size(0, 1)
+
+        elif self._direction == wx.LEFT:
+            rx = wx.Size(0, -1)
+            ry = wx.Size(1, 0)
+
+        elif self._direction == wx.RIGHT:
+            rx = wx.Size(0, 1)
+            ry = wx.Size(-1, 0)
+
+        elif self._direction == wx.BOTTOM:
+            rx = wx.Size(-1, 0)
+            ry = wx.Size(0, -1)        
+
+        point.x += ry.x*3
+        point.y += ry.y*3
+
+        dc.SetPen(wx.Pen(colourIconArrow))
+
+        for i in xrange(4):
+            pt1 = wx.Point(point.x - rx.x*i, point.y - rx.y*i)
+            pt2 = wx.Point(point.x + rx.x*(i+1), point.y + rx.y*(i+1))
+            dc.DrawLinePoint(pt1, pt2)
+            point.x += ry.x
+            point.y += ry.y
+
+    
+    def OnPaint(self, event):
+        """
+        Handles the ``wx.EVT_PAINT`` event for L{AuiDockingGuideWindow}.
+
+        :param `event`: a `wx.PaintEvent` to be processed.
+        """
+
+        dc = wx.AutoBufferedPaintDC(self)
+        if self._currentImage.IsOk() and self._valid:
+            dc.DrawBitmap(self._currentImage, 0, 0, True)
+        else:
+            self.Draw(dc)
+
+
+    def Draw(self, dc):
+        """
+        Draws the whole docking guide window (not used if the docking guide images are ok).
+
+        :param `dc`: a `wx.DC` device context object.
+        """
+
+        self.DrawBackground(dc)
+
+        if self._valid:
+            self.DrawIcon(dc)
+            self.DrawArrow(dc)
+
+
+    def UpdateDockGuide(self, pos):
+        """
+        Updates the docking guide images depending on the mouse position, using focused
+        images if the mouse is inside the docking guide or unfocused images if it is
+        outside.
+
+        :param `pos`: a `wx.Point` mouse position.
+        """
+
+        inside = self.GetScreenRect().Contains(pos)
+        
+        if inside:
+            image = self._bmp_focus
+        else:
+            image = self._bmp_unfocus
+
+        if image != self._currentImage:
+            self._currentImage = image
+            self.Refresh()
+            self.Update()
+
+
+# ---------------------------------------------------------------------------
+# AuiSingleDockingGuide
+# ---------------------------------------------------------------------------
+
+class AuiSingleDockingGuide(AuiDockingGuide):
+    """ A docking guide window for single docking hint (not diamond-shaped HUD). """
+    
+    def __init__(self, parent, direction=0):
+        """
+        Default class constructor. Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiSingleDockingGuide} parent;
+        :param `direction`: one of ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, ``wx.RIGHT``.
+        """
+
+        self._direction = direction
+
+        style = wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP | \
+                wx.FRAME_NO_TASKBAR | wx.NO_BORDER
+
+        # Use of FRAME_SHAPED on wxMac causes the frame to be visible
+        # breaking the docking hints.
+        if wx.Platform != '__WXMAC__':
+            style |= wx.FRAME_SHAPED
+
+        AuiDockingGuide.__init__(self, parent, style=style, name="auiSingleDockTarget")
+        
+        self.Hide()
+
+        useAero = GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_AERO_DOCKING_GUIDES
+        useWhidbey = GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_WHIDBEY_DOCKING_GUIDES
+        
+        self._useAero = useAero or useWhidbey
+        self._valid = True
+        
+        if useAero:
+            sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
+        elif useWhidbey:
+            sizeX, sizeY = whidbeySizeX, whidbeySizeY
+        else:
+            sizeX, sizeY = guideSizeX, guideSizeY
+
+        if direction not in [wx.TOP, wx.BOTTOM]:
+            sizeX, sizeY = sizeY, sizeX
+
+        if self._useAero:
+            self.CreateShapesWithStyle(useWhidbey)
+            
+            if wx.Platform == "__WXGTK__":
+                self.Bind(wx.EVT_WINDOW_CREATE, self.SetGuideShape)
+            else:
+                self.SetGuideShape()
+            
+            self.SetSize(self.region.GetBox().GetSize())
+        else:
+            self.SetSize((sizeX, sizeY))
+            
+        self.rect = wx.Rect(0, 0, sizeX, sizeY)
+
+        if self._useAero:
+            useAero = (useWhidbey and [2] or [1])[0]
+        else:
+            useAero = 0
+            
+        self.target = AuiDockingGuideWindow(self, self.rect, direction, False, useAero)
+
+
+    def CreateShapesWithStyle(self, useWhidbey):
+        """
+        Creates the docking guide window shape based on which docking bitmaps are used.
+
+        :param `useWhidbey`: if ``True``, use Whidbey-style bitmaps; if ``False``, use the
+         Aero-style bitmaps.
+         """
+
+        sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
+        if useWhidbey:
+            sizeX, sizeY = whidbeySizeX, whidbeySizeY
+
+        if self._direction not in [wx.TOP, wx.BOTTOM]:
+            sizeX, sizeY = sizeY, sizeX
+
+        useAero = (useWhidbey and [2] or [1])[0]      
+        bmp, dummy = GetDockingImage(self._direction, useAero, False)
+        region = wx.RegionFromBitmap(bmp)
+            
+        self.region = region
+        
+
+    def AeroMove(self, pos):
+        """
+        Moves the docking window to the new position. Overridden in children classes.
+
+        :param `pos`: the new docking guide position.
+        """
+
+        pass
+    
+
+    def SetGuideShape(self, event=None):
+        """
+        Sets the correct shape for the docking guide window.
+
+        :param `event`: on wxGTK, a `wx.WindowCreateEvent` event to process.
+        """
+
+        self.SetShape(self.region)        
+                
+        if event is not None:
+            # Skip the event on wxGTK
+            event.Skip()
+            wx.CallAfter(wx.SafeYield, self, True)
+
+
+    def SetShape(self, region):
+        """
+        If the platform supports it, sets the shape of the window to that depicted by `region`.
+        The system will not display or respond to any mouse event for the pixels that lie
+        outside of the region. To reset the window to the normal rectangular shape simply call
+        L{SetShape} again with an empty region. 
+
+        :param `region`: the shape of the frame.
+
+        :note: Overridden for wxMac.        
+        """
+        
+        if wx.Platform == '__WXMAC__':
+            # HACK so we don't crash when SetShape is called
+            return
+        else:
+            super(AuiSingleDockingGuide, self).SetShape(region)
+
+
+    def SetValid(self, valid):
+        """
+        Sets the docking direction as valid or invalid.
+
+        :param `valid`: whether the docking direction is allowed or not.
+        """
+
+        self._valid = valid
+
+
+    def IsValid(self):
+        """ Returns whether the docking direction is valid. """
+        
+        return self._valid
+
+
+    def UpdateDockGuide(self, pos):
+        """
+        Updates the docking guide images depending on the mouse position, using focused
+        images if the mouse is inside the docking guide or unfocused images if it is
+        outside.
+
+        :param `pos`: a `wx.Point` mouse position.
+        """
+
+        self.target.UpdateDockGuide(pos)
+
+        
+    def HitTest(self, x, y):
+        """
+        Checks if the mouse position is inside the target window rect.
+
+        :param `x`: the `x` mouse position;
+        :param `y`: the `y` mouse position.
+        """
+
+        if self.target.GetScreenRect().Contains((x, y)):
+            return wx.ALL
+
+        return -1
+
+
+# ---------------------------------------------------------------------------
+# AuiCenterDockingGuide
+# ---------------------------------------------------------------------------
+
+class AuiCenterDockingGuide(AuiDockingGuide):
+    """ A docking guide window for multiple docking hint (diamond-shaped HUD). """
+    
+    def __init__(self, parent):
+        """
+        Default class constructor.
+        Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiCenterDockingGuide} parent.
+        """
+
+        AuiDockingGuide.__init__(self, parent, style=wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP |
+                                 wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.FRAME_SHAPED,
+                                 name="auiCenterDockTarget")
+
+        self.Hide()
+
+        self.CreateShapesWithStyle()
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        
+        if wx.Platform == "__WXGTK__":
+            self.Bind(wx.EVT_WINDOW_CREATE, self.SetGuideShape)
+        else:
+            self.SetGuideShape()
+            
+        self.SetSize(self.region.GetBox().GetSize())
+
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+
+    def CreateShapesWithStyle(self):
+        """ Creates the docking guide window shape based on which docking bitmaps are used. """
+
+        useAero = (GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_AERO_DOCKING_GUIDES) != 0
+        useWhidbey = (GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_WHIDBEY_DOCKING_GUIDES) != 0
+
+        self._useAero = 0
+        if useAero:
+            self._useAero = 1
+        elif useWhidbey:
+            self._useAero = 2
+        
+        if useAero:
+            sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
+        elif useWhidbey:
+            sizeX, sizeY = whidbeySizeX, whidbeySizeY          
+        else:
+            sizeX, sizeY = guideSizeX, guideSizeY
+
+        rectLeft = wx.Rect(0, sizeY, sizeY, sizeX)
+        rectTop = wx.Rect(sizeY, 0, sizeX, sizeY)
+        rectRight = wx.Rect(sizeY+sizeX, sizeY, sizeY, sizeX)
+        rectBottom = wx.Rect(sizeY, sizeX + sizeY, sizeX, sizeY)
+        rectCenter = wx.Rect(sizeY, sizeY, sizeX, sizeX)
+            
+        if not self._useAero:
+
+            self.targetLeft = AuiDockingGuideWindow(self, rectLeft, wx.LEFT, True, useAero)
+            self.targetTop = AuiDockingGuideWindow(self, rectTop, wx.TOP, True, useAero)
+            self.targetRight = AuiDockingGuideWindow(self, rectRight, wx.RIGHT, True, useAero)
+            self.targetBottom = AuiDockingGuideWindow(self, rectBottom, wx.BOTTOM, True, useAero)
+            self.targetCenter = AuiDockingGuideWindow(self, rectCenter, wx.CENTER, True, useAero)
+
+            
+            # top-left diamond
+            tld = [wx.Point(rectTop.x, rectTop.y+rectTop.height-8),
+                   wx.Point(rectLeft.x+rectLeft.width-8, rectLeft.y),
+                   rectTop.GetBottomLeft()]
+            # bottom-left diamond
+            bld = [wx.Point(rectLeft.x+rectLeft.width-8, rectLeft.y+rectLeft.height),
+                   wx.Point(rectBottom.x, rectBottom.y+8),
+                   rectBottom.GetTopLeft()]
+            # top-right diamond
+            trd = [wx.Point(rectTop.x+rectTop.width, rectTop.y+rectTop.height-8),
+                   wx.Point(rectRight.x+8, rectRight.y),
+                   rectRight.GetTopLeft()]        
+            # bottom-right diamond
+            brd = [wx.Point(rectRight.x+8, rectRight.y+rectRight.height),
+                   wx.Point(rectBottom.x+rectBottom.width, rectBottom.y+8),
+                   rectBottom.GetTopRight()]
+
+            self._triangles = [tld[0:2], bld[0:2],
+                               [wx.Point(rectTop.x+rectTop.width-1, rectTop.y+rectTop.height-8),
+                                wx.Point(rectRight.x+7, rectRight.y)],
+                               [wx.Point(rectRight.x+7, rectRight.y+rectRight.height),
+                                wx.Point(rectBottom.x+rectBottom.width-1, rectBottom.y+8)]]
+            
+            region = wx.Region()
+            region.UnionRect(rectLeft)
+            region.UnionRect(rectTop)
+            region.UnionRect(rectRight)
+            region.UnionRect(rectBottom)
+            region.UnionRect(rectCenter)
+            region.UnionRegion(wx.RegionFromPoints(tld))
+            region.UnionRegion(wx.RegionFromPoints(bld))
+            region.UnionRegion(wx.RegionFromPoints(trd))
+            region.UnionRegion(wx.RegionFromPoints(brd))
+
+        elif useAero:
+
+            self._aeroBmp = aero_dock_pane.GetBitmap()
+            region = wx.RegionFromBitmap(self._aeroBmp)
+
+            self._allAeroBmps = [aero_dock_pane_left.GetBitmap(), aero_dock_pane_top.GetBitmap(),
+                                 aero_dock_pane_right.GetBitmap(), aero_dock_pane_bottom.GetBitmap(),
+                                 aero_dock_pane_center.GetBitmap(), aero_dock_pane.GetBitmap()]
+            self._deniedBitmap = aero_denied.GetBitmap()
+            self._aeroRects = [rectLeft, rectTop, rectRight, rectBottom, rectCenter]
+            self._valid = True
+
+        elif useWhidbey:
+
+            self._aeroBmp = whidbey_dock_pane.GetBitmap()
+            region = wx.RegionFromBitmap(self._aeroBmp)
+
+            self._allAeroBmps = [whidbey_dock_pane_left.GetBitmap(), whidbey_dock_pane_top.GetBitmap(),
+                                 whidbey_dock_pane_right.GetBitmap(), whidbey_dock_pane_bottom.GetBitmap(),
+                                 whidbey_dock_pane_center.GetBitmap(), whidbey_dock_pane.GetBitmap()]
+            self._deniedBitmap = whidbey_denied.GetBitmap()
+            self._aeroRects = [rectLeft, rectTop, rectRight, rectBottom, rectCenter]
+            self._valid = True
+            
+            
+        self.region = region
+        
+
+    def SetGuideShape(self, event=None):
+        """
+        Sets the correct shape for the docking guide window.
+
+        :param `event`: on wxGTK, a `wx.WindowCreateEvent` event to process.
+        """
+
+        self.SetShape(self.region)        
+
+        if event is not None:
+            # Skip the event on wxGTK
+            event.Skip()
+            wx.CallAfter(wx.SafeYield, self, True)
+
+            
+    def UpdateDockGuide(self, pos):
+        """
+        Updates the docking guides images depending on the mouse position, using focused
+        images if the mouse is inside the docking guide or unfocused images if it is
+        outside.
+
+        :param `pos`: a `wx.Point` mouse position.
+        """
+
+        if not self._useAero:
+            for target in self.GetChildren():
+                target.UpdateDockGuide(pos)
+        else:
+            lenRects = len(self._aeroRects)
+            for indx, rect in enumerate(self._aeroRects):
+                if rect.Contains(pos):
+                    if self._allAeroBmps[indx] != self._aeroBmp:
+                        if indx < lenRects - 1 or (indx == lenRects - 1 and self._valid):
+                            self._aeroBmp = self._allAeroBmps[indx]
+                            self.Refresh()
+                        else:
+                            self._aeroBmp = self._allAeroBmps[-1]
+                            self.Refresh()
+                            
+                    return
+
+            if self._aeroBmp != self._allAeroBmps[-1]:
+                self._aeroBmp = self._allAeroBmps[-1]
+                self.Refresh()
+
+
+    def HitTest(self, x, y):
+        """
+        Checks if the mouse position is inside the target windows rect.
+
+        :param `x`: the `x` mouse position;
+        :param `y`: the `y` mouse position.
+        """
+
+        if not self._useAero:
+            if self.targetLeft.GetScreenRect().Contains((x, y)):
+                return wx.LEFT
+            if self.targetTop.GetScreenRect().Contains((x, y)):
+                return wx.UP
+            if self.targetRight.GetScreenRect().Contains((x, y)):
+                return wx.RIGHT
+            if self.targetBottom.GetScreenRect().Contains((x, y)):
+                return wx.DOWN
+            if self.targetCenter.IsValid() and self.targetCenter.GetScreenRect().Contains((x, y)):
+                return wx.CENTER
+        else:
+            constants = [wx.LEFT, wx.UP, wx.RIGHT, wx.DOWN, wx.CENTER]
+            lenRects = len(self._aeroRects)
+            for indx, rect in enumerate(self._aeroRects):
+                if rect.Contains((x, y)):
+                    if indx < lenRects or (indx == lenRects-1 and self._valid):
+                        return constants[indx]
+
+        return -1
+
+
+    def ValidateNotebookDocking(self, valid):
+        """
+        Sets whether a pane can be docked on top of another to create an automatic
+        L{AuiNotebook}.
+
+        :param `valid`: whether a pane can be docked on top to another to form an automatic
+         L{AuiNotebook}.
+        """
+
+        if not self._useAero:
+            if self.targetCenter.IsValid() != valid:        
+                self.targetCenter.SetValid(valid)
+                self.targetCenter.Refresh()
+        else:
+            if self._valid != valid:
+                self._valid = valid
+                self.Refresh()
+    
+
+    def AeroMove(self, pos):
+        """
+        Moves the docking guide window to the new position.
+
+        :param `pos`: the new docking guide position.
+        """
+
+        if not self._useAero:
+            return
+
+        useWhidbey = (GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_WHIDBEY_DOCKING_GUIDES) != 0
+
+        if useWhidbey:
+            sizeX, sizeY = whidbeySizeX, whidbeySizeY            
+        else:
+            sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
+            
+        size = self.GetSize()
+        
+        leftRect, topRect, rightRect, bottomRect, centerRect = self._aeroRects
+        thePos = pos + wx.Point((size.x-sizeY)/2, (size.y-sizeX)/2)
+        
+        centerRect.SetPosition(thePos)
+
+        leftRect.SetPosition(thePos + wx.Point(-sizeY, 0))
+        topRect.SetPosition(thePos + wx.Point(0, -sizeY))
+        rightRect.SetPosition(thePos + wx.Point(sizeX, 0))
+        bottomRect.SetPosition(thePos + wx.Point(0, sizeX))
+        
+        
+    def OnEraseBackground(self, event):
+        """
+        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiCenterDockingGuide}.
+
+        :param `event`: `wx.EraseEvent` to be processed.
+
+        :note: This is intentionally empty to reduce flickering while drawing.
+        """
+        
+        pass
+
+
+    def OnPaint(self, event):
+        """
+        Handles the ``wx.EVT_PAINT`` event for L{AuiCenterDockingGuide}.
+
+        :param `event`: a `wx.PaintEvent` to be processed.
+        """
+
+        dc = wx.AutoBufferedPaintDC(self)
+
+        if self._useAero:
+            dc.SetBrush(wx.TRANSPARENT_BRUSH)
+            dc.SetPen(wx.TRANSPARENT_PEN)
+        else:
+            dc.SetBrush(wx.Brush(colourTargetBackground))
+            dc.SetPen(wx.Pen(colourTargetBorder))
+
+        rect = self.GetClientRect()
+        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
+
+        if self._useAero:
+            dc.DrawBitmap(self._aeroBmp, 0, 0, True)
+            if not self._valid:
+                diff = (self._useAero == 2 and [1] or [0])[0]
+                bmpX, bmpY = self._deniedBitmap.GetWidth(), self._deniedBitmap.GetHeight()
+                xPos, yPos = (rect.x + (rect.width)/2 - bmpX/2), (rect.y + (rect.height)/2 - bmpY/2)
+                dc.DrawBitmap(self._deniedBitmap, xPos+1, yPos+diff, True)
+                
+            return
+        
+        dc.SetPen(wx.Pen(colourTargetBorder, 2))
+        for pts in self._triangles:
+            dc.DrawLinePoint(pts[0], pts[1])
+            
+
+# ----------------------------------------------------------------------------
+# AuiDockingHintWindow
+# ----------------------------------------------------------------------------
+
+class AuiDockingHintWindow(wx.Frame):
+    """ The original wxAUI docking window hint. """
+
+    def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition,
+                 size=wx.Size(1, 1), style=wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT |
+                 wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.FRAME_SHAPED,
+                 name="auiHintWindow"):
+        """
+        Default class constructor. Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiDockingGuide} parent;
+        :param `id`: the window identifier. It may take a value of -1 to indicate a default value.
+        :param `title`: the caption to be displayed on the frame's title bar;
+        :param `pos`: the window position. A value of (-1, -1) indicates a default position,
+         chosen by either the windowing system or wxPython, depending on platform;
+        :param `size`: the window 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;
+        :param `name`: the name of the window. This parameter is used to associate a name with the
+         item, allowing the application user to set Motif resource values for individual windows.
+        """
+        if wx.Platform == '__WXMAC__' and style & wx.FRAME_SHAPED:
+            # Having the shaped frame causes the frame to not be visible
+            # with the transparent style hints.
+            style -= wx.FRAME_SHAPED
+
+        wx.Frame.__init__(self, parent, id, title, pos, size, style, name=name)
+        
+        self._blindMode = False
+        self.SetBackgroundColour(colourHintBackground)
+        
+        # Can't set background colour on a frame on wxMac
+        # so add a panel to set the colour on.
+        if wx.Platform == '__WXMAC__':
+            sizer = wx.BoxSizer(wx.HORIZONTAL)
+            self.panel = wx.Panel(self)
+            sizer.Add(self.panel, 1, wx.EXPAND)
+            self.SetSizer(sizer)
+            self.panel.SetBackgroundColour(colourHintBackground)
+
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        
+
+    def MakeVenetianBlinds(self):
+        """
+        Creates the "venetian blind" effect if L{AuiManager} has the ``AUI_MGR_VENETIAN_BLINDS_HINT``
+        flag set.
+        """
+
+        amount = 128
+        size = self.GetClientSize()
+        region = wx.Region(0, 0, size.x, 1)
+
+        for y in xrange(size.y):
+
+            # Reverse the order of the bottom 4 bits
+            j = (y & 8 and [1] or [0])[0] | (y & 4 and [2] or [0])[0] | \
+                (y & 2 and [4] or [0])[0] | (y & 1 and [8] or [0])[0]
+            
+            if 16*j+8 < amount:
+                region.Union(0, y, size.x, 1)
+                        
+        self.SetShape(region)
+
+
+    def SetBlindMode(self, agwFlags):
+        """
+        Sets whether venetian blinds or transparent hints will be shown as docking hint.
+        This depends on the L{AuiManager} flags.
+
+        :param `agwFlags`: the L{AuiManager} flags.
+        """
+
+        self._blindMode = (agwFlags & AUI_MGR_VENETIAN_BLINDS_HINT) != 0
+
+        if self._blindMode or not self.CanSetTransparent():
+            self.MakeVenetianBlinds()
+            self.SetTransparent(255)
+        
+        else:
+            self.SetShape(wx.Region())
+            if agwFlags & AUI_MGR_HINT_FADE == 0:                
+                self.SetTransparent(80)
+            else:
+                self.SetTransparent(0)
+
+
+    def SetShape(self, region):
+        """
+        If the platform supports it, sets the shape of the window to that depicted by `region`.
+        The system will not display or respond to any mouse event for the pixels that lie
+        outside of the region. To reset the window to the normal rectangular shape simply call
+        L{SetShape} again with an empty region. 
+
+        :param `region`: the shape of the frame (an instance of `wx.Region`).
+
+        :note: Overridden for wxMac.        
+        """
+        
+        if wx.Platform == '__WXMAC__':
+            # HACK so we don't crash when SetShape is called
+            return
+        else:
+            super(AuiDockingHintWindow, self).SetShape(region)
+
+
+    def Show(self, show=True):
+        """
+        Show the hint window.
+
+        :param `show`: whether to show or hide the hint docking window.
+        """
+        
+        super(AuiDockingHintWindow, self).Show(show)
+        if wx.Platform == '__WXMAC__':
+            # Need to manually do layout since its a borderless frame.
+            self.Layout()
+
+
+    def OnSize(self, event):
+        """
+        Handles the ``wx.EVT_SIZE`` event for L{AuiDockingHintWindow}.
+
+        :param `event`: a `wx.SizeEvent` to be processed.
+        """
+
+        if self._blindMode or not self.CanSetTransparent():
+            self.MakeVenetianBlinds()
+
+
+# ---------------------------------------------------------------------------- #
+
+# -- AuiFloatingFrame class implementation --            
+
+class AuiFloatingFrame(wx.MiniFrame):
+    """ AuiFloatingFrame is the frame class that holds floating panes. """
+
+    def __init__(self, parent, owner_mgr, pane=None, id=wx.ID_ANY, title="",
+                 style=wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT |
+                 wx.FRAME_NO_TASKBAR | wx.CLIP_CHILDREN):
+        """
+        Default class constructor. Used internally, do not call it in your code!
+
+        :param `parent`: the L{AuiFloatingFrame} parent;
+        :param `owner_mgr`: the L{AuiManager} that manages the floating pane;
+        :param `pane`: the L{AuiPaneInfo} pane that is about to float;
+        :param `id`: the window identifier. It may take a value of -1 to indicate a default value.
+        :param `title`: the caption to be displayed on the frame's title bar.
+        :param `style`: the window style.
+        """
+            
+        if pane and pane.IsResizeable():
+            style += wx.RESIZE_BORDER
+        if pane:
+            self._is_toolbar = pane.IsToolbar()
+
+        self._useNativeMiniframes = False
+        if AuiManager_UseNativeMiniframes(owner_mgr):
+            # On wxMac we always use native miniframes
+            self._useNativeMiniframes = True
+            style += wx.CAPTION + wx.SYSTEM_MENU
+            if pane.HasCloseButton():
+                style += wx.CLOSE_BOX
+            if pane.HasMaximizeButton():
+                style += wx.MAXIMIZE_BOX
+            if pane.HasMinimizeButton():
+                style += wx.MINIMIZE_BOX
+            
+        wx.MiniFrame.__init__(self, parent, id, title, pos=pane.floating_pos,
+                              size=pane.floating_size, style=style, name="auiFloatingFrame")
+
+        self._fly_timer = wx.Timer(self, wx.ID_ANY)
+        self._check_fly_timer = wx.Timer(self, wx.ID_ANY)
+        
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
+        self.Bind(wx.EVT_TIMER, self.OnCheckFlyTimer, self._check_fly_timer)
+        self.Bind(wx.EVT_TIMER, self.OnFlyTimer, self._fly_timer)
+        self.Bind(EVT_AUI_FIND_MANAGER, self.OnFindManager)
+
+        if self._useNativeMiniframes:
+            self.Bind(wx.EVT_MOVE, self.OnMoveEvent)
+            self.Bind(wx.EVT_MOVING, self.OnMoveEvent)
+            self.Bind(wx.EVT_IDLE, self.OnIdle)
+            self._useNativeMiniframes = True
+            self.SetExtraStyle(wx.WS_EX_PROCESS_IDLE)
+        else:
+            self.Bind(wx.EVT_MOVE, self.OnMove)
+
+        self._fly = False
+        self._send_size = True
+        self._alpha_amount = 255
+        
+        self._owner_mgr = owner_mgr
+        self._moving = False
+        self._lastDirection = None
+        self._transparent = 255
+
+        self._last_rect = wx.Rect()
+        self._last2_rect = wx.Rect()
+        self._last3_rect = wx.Rect()
+
+        self._mgr = AuiManager()
+        self._mgr.SetManagedWindow(self)
+        self._mgr.SetArtProvider(owner_mgr.GetArtProvider())
+        self._mgr.SetAGWFlags(owner_mgr.GetAGWFlags())
+
+
+    def CopyAttributes(self, pane):
+        """
+        Copies all the attributes of the input `pane` into another L{AuiPaneInfo}.
+
+        :param `pane`: the source L{AuiPaneInfo} from where to copy attributes.
+        """
+
+        contained_pane = AuiPaneInfo()
+
+        contained_pane.name = pane.name
+        contained_pane.caption = pane.caption
+        contained_pane.window = pane.window
+        contained_pane.frame = pane.frame
+        contained_pane.state = pane.state
+        contained_pane.dock_direction = pane.dock_direction
+        contained_pane.dock_layer = pane.dock_layer
+        contained_pane.dock_row = pane.dock_row
+        contained_pane.dock_pos = pane.dock_pos
+        contained_pane.best_size = wx.Size(*pane.best_size)
+        contained_pane.min_size = wx.Size(*pane.min_size)
+        contained_pane.max_size = wx.Size(*pane.max_size)
+        contained_pane.floating_pos = wx.Point(*pane.floating_pos)
+        contained_pane.floating_size = wx.Size(*pane.floating_size)
+        contained_pane.dock_proportion = pane.dock_proportion
+        contained_pane.buttons = pane.buttons
+        contained_pane.rect = wx.Rect(*pane.rect)
+        contained_pane.icon = pane.icon
+        contained_pane.notebook_id = pane.notebook_id
+        contained_pane.transparent = pane.transparent
+        contained_pane.snapped = pane.snapped
+        contained_pane.minimize_mode = pane.minimize_mode
+
+        return contained_pane
+    
+
+    def SetPaneWindow(self, pane):
+        """
+        Sets all the properties of a pane.
+
+        :param `pane`: the L{AuiPaneInfo} to analyze.
+        """
+
+        self._is_toolbar = pane.IsToolbar()
+        self._pane_window = pane.window
+
+        if isinstance(pane.window, auibar.AuiToolBar):
+            pane.window.SetAuiManager(self._mgr)
+        
+        self._pane_window.Reparent(self)
+        
+        contained_pane = self.CopyAttributes(pane)
+        
+        contained_pane.Dock().Center().Show(). \
+                       CaptionVisible(False). \
+                       PaneBorder(False). \
+                       Layer(0).Row(0).Position(0)
+
+        if not contained_pane.HasGripper() and not self._useNativeMiniframes:
+            contained_pane.CaptionVisible(True)
+
+        indx = self._owner_mgr._panes.index(pane)
+
+        # Carry over the minimum size
+        pane_min_size = pane.window.GetMinSize()
+
+        # if the best size is smaller than the min size
+        # then set the min size to the best size as well
+        pane_best_size = contained_pane.best_size
+        if pane_best_size.IsFullySpecified() and (pane_best_size.x < pane_min_size.x or \
+                                                  pane_best_size.y < pane_min_size.y):
+
+            pane_min_size = pane_best_size
+            self._pane_window.SetMinSize(pane_min_size)
+    
+        # if the frame window's max size is greater than the min size
+        # then set the max size to the min size as well
+        cur_max_size = self.GetMaxSize()
+        if cur_max_size.IsFullySpecified() and  (cur_max_size.x < pane_min_size.x or \
+                                                 cur_max_size.y < pane_min_size.y):
+            self.SetMaxSize(pane_min_size)
+
+        art_provider = self._mgr.GetArtProvider()
+        caption_size = art_provider.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        button_size = art_provider.GetMetric(AUI_DOCKART_PANE_BUTTON_SIZE) + \
+                      4*art_provider.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
+
+        min_size = pane.window.GetMinSize()
+
+        if min_size.y < caption_size or min_size.x < button_size:
+            new_x, new_y = min_size.x, min_size.y
+            if min_size.y < caption_size:
+                new_y = (pane.IsResizeable() and [2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_Y)+caption_size] or [1])[0]
+            if min_size.x < button_size:
+                new_x = (pane.IsResizeable() and [2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_X)+button_size] or [1])[0]
+                
+            self.SetMinSize((new_x, new_y))
+        else:
+            self.SetMinSize(min_size)
+
+        self._mgr.AddPane(self._pane_window, contained_pane)
+        self._mgr.Update()           
+
+        if pane.min_size.IsFullySpecified():
+            # because SetSizeHints() calls Fit() too (which sets the window
+            # size to its minimum allowed), we keep the size before calling
+            # SetSizeHints() and reset it afterwards...
+            tmp = self.GetSize()
+            self.GetSizer().SetSizeHints(self)
+            self.SetSize(tmp)
+        
+        self.SetTitle(pane.caption)
+
+        if pane.floating_size != wx.Size(-1, -1):
+            self.SetSize(pane.floating_size)
+        else:
+            size = pane.best_size
+            if size == wx.Size(-1, -1):
+                size = pane.min_size
+            if size == wx.Size(-1, -1):
+                size = self._pane_window.GetSize()
+            if self._owner_mgr and pane.HasGripper():
+                if pane.HasGripperTop():
+                    size.y += self._owner_mgr._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
+                else:
+                    size.x += self._owner_mgr._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
+
+            if not self._useNativeMiniframes:
+                size.y += self._owner_mgr._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+                
+            pane.floating_size = size
+            
+            self.SetClientSize(size)
+
+        self._owner_mgr._panes[indx] = pane
+
+        self._fly_step = abs(pane.floating_size.y - \
+                             (caption_size + 2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_Y)))/10
+
+        self._floating_size = wx.Size(*self.GetSize())
+
+        if pane.IsFlyOut():
+            self._check_fly_timer.Start(50)
+
+        
+    def GetOwnerManager(self):
+        """ Returns the L{AuiManager} that manages the pane. """
+
+        return self._owner_mgr
+
+
+    def OnSize(self, event):
+        """
+        Handles the ``wx.EVT_SIZE`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.SizeEvent` to be processed.
+        """
+
+        if self._owner_mgr and self._send_size:
+            self._owner_mgr.OnFloatingPaneResized(self._pane_window, event.GetSize())
+
+    
+    def OnClose(self, event):
+        """
+        Handles the ``wx.EVT_CLOSE`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.CloseEvent` to be processed.
+        """
+
+        if self._owner_mgr:
+            self._owner_mgr.OnFloatingPaneClosed(self._pane_window, event)
+
+        if not event.GetVeto():
+            self._mgr.DetachPane(self._pane_window)
+
+            if isinstance(self._pane_window, auibar.AuiToolBar):
+                self._pane_window.SetAuiManager(self._owner_mgr)
+
+            # if we do not do this, then we can crash...
+            if self._owner_mgr and self._owner_mgr._action_window == self:
+                self._owner_mgr._action_window = None
+
+            self.Destroy()
+    
+
+    def OnActivate(self, event):
+        """
+        Handles the ``wx.EVT_ACTIVATE`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.ActivateEvent` to be processed.
+        """
+
+        if self._owner_mgr and event.GetActive():
+            self._owner_mgr.OnFloatingPaneActivated(self._pane_window)
+
+
+    def OnMove(self, event):
+        """
+        Handles the ``wx.EVT_MOVE`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.MoveEvent` to be processed.
+
+        :note: This event is not processed on wxMAC or if L{AuiManager} is not using the
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
+        """
+
+        if self._owner_mgr:
+            self._owner_mgr.OnFloatingPaneMoved(self._pane_window, event)
+                
+
+    def OnMoveEvent(self, event):
+        """
+        Handles the ``wx.EVT_MOVE`` and ``wx.EVT_MOVING`` events for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.MoveEvent` to be processed.
+
+        :note: This event is only processed on wxMAC or if L{AuiManager} is using the
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
+        """
+
+        win_rect = self.GetRect()
+
+        if win_rect == self._last_rect:
+            return
+
+        # skip the first move event
+        if self._last_rect.IsEmpty():        
+            self._last_rect = wx.Rect(*win_rect)
+            return
+        
+        # skip if moving too fast to avoid massive redraws and
+        # jumping hint windows
+        if abs(win_rect.x - self._last_rect.x) > 3 or abs(win_rect.y - self._last_rect.y) > 3:
+            self._last3_rect = wx.Rect(*self._last2_rect)
+            self._last2_rect = wx.Rect(*self._last_rect)
+            self._last_rect = wx.Rect(*win_rect)
+            return
+
+        # prevent frame redocking during resize
+        if self._last_rect.GetSize() != win_rect.GetSize():
+            self._last3_rect = wx.Rect(*self._last2_rect)
+            self._last2_rect = wx.Rect(*self._last_rect)
+            self._last_rect = wx.Rect(*win_rect)
+            return
+
+        self._last3_rect = wx.Rect(*self._last2_rect)
+        self._last2_rect = wx.Rect(*self._last_rect)
+        self._last_rect = wx.Rect(*win_rect)
+
+        if _VERSION_STRING < "2.9":
+            leftDown = wx.GetMouseState().LeftDown()
+        else:
+            leftDown = wx.GetMouseState().LeftIsDown()
+
+        if not leftDown:
+            return
+
+        if not self._moving:        
+            self.OnMoveStart(event)
+            self._moving = True
+
+        if self._last3_rect.IsEmpty():
+            return
+
+        self.OnMoving(event)
+
+
+    def OnIdle(self, event):
+        """
+        Handles the ``wx.EVT_IDLE`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.IdleEvent` event to be processed.
+
+        :note: This event is only processed on wxMAC or if L{AuiManager} is using the
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.        
+        """
+
+        if self._moving:        
+            if _VERSION_STRING < "2.9":
+                leftDown = wx.GetMouseState().LeftDown()
+            else:
+                leftDown = wx.GetMouseState().LeftIsDown()
+
+            if not leftDown:
+                self._moving = False
+                self.OnMoveFinished()
+            else:            
+                event.RequestMore()
+
+        
+    def OnMoveStart(self, event):
+        """
+        The user has just started moving the floating pane.
+
+        :param `event`: an instance of `wx.MouseEvent`.
+    
+        :note: This method is used only on wxMAC or if L{AuiManager} is using the
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
+        """
+
+        # notify the owner manager that the pane has started to move
+        if self._owner_mgr:
+            if self._owner_mgr._from_move:
+                return
+            self._owner_mgr._action_window = self._pane_window
+            point = wx.GetMousePosition()
+            action_offset = point - self.GetPosition()
+
+            if self._is_toolbar:
+                self._owner_mgr._toolbar_action_offset = action_offset
+                self._owner_mgr.OnMotion_DragToolbarPane(point)
+            else:
+                self._owner_mgr._action_offset = action_offset
+                self._owner_mgr.OnMotion_DragFloatingPane(point)
+
+    
+    def OnMoving(self, event):
+        """
+        The user is moving the floating pane.
+
+        :param `event`: an instance of `wx.MouseEvent`.
+        
+        :note: This method is used only on wxMAC or if L{AuiManager} is using the
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
+        """
+
+        # notify the owner manager that the pane is moving
+        self.OnMoveStart(event)
+        
+
+    def OnMoveFinished(self):
+        """
+        The user has just finished moving the floating pane.
+
+        :note: This method is used only on wxMAC or if L{AuiManager} is using the
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
+        """
+
+        # notify the owner manager that the pane has finished moving
+        if self._owner_mgr:
+            self._owner_mgr._action_window = self._pane_window
+            point = wx.GetMousePosition()
+            if self._is_toolbar:
+                self._owner_mgr.OnLeftUp_DragToolbarPane(point)
+            else:
+                self._owner_mgr.OnLeftUp_DragFloatingPane(point)
+
+            self._owner_mgr.OnFloatingPaneMoved(self._pane_window, point)
+    
+
+    def OnCheckFlyTimer(self, event):
+        """
+        Handles the ``wx.EVT_TIMER`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.TimerEvent` to be processed.
+
+        :note: This is used solely for "fly-out" panes.        
+        """
+        
+        if self._owner_mgr:
+            pane = self._mgr.GetPane(self._pane_window)
+            if pane.IsFlyOut():
+                if self.IsShownOnScreen():
+                    self.FlyOut()
+                        
+
+    def OnFindManager(self, event):
+        """
+        Handles the ``EVT_AUI_FIND_MANAGER`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a L{AuiManagerEvent} event to be processed.
+        """
+        
+        event.SetManager(self._owner_mgr)
+
+
+    def FlyOut(self):
+        """ Starts the flying in and out of a floating pane. """
+
+        if self._fly_timer.IsRunning():
+            return
+
+        if _VERSION_STRING < "2.9":
+            leftDown = wx.GetMouseState().LeftDown()
+        else:
+            leftDown = wx.GetMouseState().LeftIsDown()
+
+        if leftDown:
+            return
+        
+        rect = wx.Rect(*self.GetScreenRect())
+        rect.Inflate(10, 10)
+
+        if rect.Contains(wx.GetMousePosition()):
+            if not self._fly:
+                return
+            self._send_size = False
+            self._fly_timer.Start(5)
+        else:
+            if self._fly:
+                return
+            self._send_size = False
+            self._fly_timer.Start(5)
+
+
+    def OnFlyTimer(self, event):            
+        """
+        Handles the ``wx.EVT_TIMER`` event for L{AuiFloatingFrame}.
+
+        :param `event`: a `wx.TimerEvent` to be processed.
+        """
+
+        current_size = self.GetClientSize()
+        floating_size = wx.Size(*self._owner_mgr.GetPane(self._pane_window).floating_size)
+
+        if floating_size.y == -1:
+            floating_size = self._floating_size
+        
+        if not self._fly:
+            min_size = self._mgr.GetArtProvider().GetMetric(AUI_DOCKART_CAPTION_SIZE)
+
+            if wx.Platform != "__WXMSW__":
+                min_size += 2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_Y)
+
+            if current_size.y - self._fly_step <= min_size:
+                self.SetClientSize((current_size.x, min_size))
+                self._fly = True
+                self._fly_timer.Stop()
+                self._send_size = True
+            else:
+                self.SetClientSize((current_size.x, current_size.y-self._fly_step))
+
+        else:
+            if current_size.y + self._fly_step >= floating_size.y:
+                self.SetClientSize((current_size.x, floating_size.y))
+                self._fly = False
+                self._fly_timer.Stop()
+                self._send_size = True
+            else:
+                self.SetClientSize((current_size.x, current_size.y+self._fly_step))
+
+        self.Update()
+        self.Refresh()
+
+
+    def FadeOut(self):
+        """ Actually starts the fading out of the floating pane. """
+
+        while 1:
+            self._alpha_amount -= 10
+            if self._alpha_amount <= 0:
+                self._alpha_amount = 255
+                return
+
+            self.SetTransparent(self._alpha_amount)
+            wx.SafeYield()
+            wx.MilliSleep(15)
+
+    
+# -- static utility functions --
+
+def DrawResizeHint(dc, rect):
+    """
+    Draws a resize hint while a sash is dragged.
+
+    :param `rect`: a `wx.Rect` rectangle which specifies the sash dimensions.
+    """
+        
+    if wx.Platform == "__WXMSW__" and wx.App.GetComCtl32Version() >= 600:
+        if wx.GetOsVersion()[1] > 5:
+            # Windows Vista
+            dc.SetPen(wx.Pen("black", 2, wx.SOLID))
+            dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        else:
+            # Draw the nice XP style splitter
+            dc.SetPen(wx.TRANSPARENT_PEN)
+            dc.SetBrush(wx.BLACK_BRUSH)
+        dc.SetLogicalFunction(wx.INVERT)
+        dc.DrawRectangleRect(rect)
+        dc.SetLogicalFunction(wx.COPY)
+    else:
+        stipple = PaneCreateStippleBitmap()
+        brush = wx.BrushFromBitmap(stipple)
+        dc.SetBrush(brush)
+        dc.SetPen(wx.TRANSPARENT_PEN)
+
+        dc.SetLogicalFunction(wx.XOR)
+        dc.DrawRectangleRect(rect)    
+
+
+def CopyDocksAndPanes(src_docks, src_panes):
+    """
+    This utility function creates shallow copies of
+    the dock and pane info. L{AuiDockInfo} usually contain pointers
+    to L{AuiPaneInfo} classes, thus this function is necessary to reliably
+    reconstruct that relationship in the new dock info and pane info arrays.
+
+    :param `src_docks`: a list of L{AuiDockInfo} classes;
+    :param `src_panes`: a list of L{AuiPaneInfo} classes.
+    """
+    
+    dest_docks = src_docks
+    dest_panes = src_panes
+
+    for ii in xrange(len(dest_docks)):
+        dock = dest_docks[ii]
+        for jj in xrange(len(dock.panes)):
+            for kk in xrange(len(src_panes)):
+                if dock.panes[jj] == src_panes[kk]:
+                    dock.panes[jj] = dest_panes[kk]
+
+    return dest_docks, dest_panes
+
+
+def CopyDocksAndPanes2(src_docks, src_panes):
+    """
+    This utility function creates full copies of
+    the dock and pane info. L{AuiDockInfo} usually contain pointers
+    to L{AuiPaneInfo} classes, thus this function is necessary to reliably
+    reconstruct that relationship in the new dock info and pane info arrays.
+
+    :param `src_docks`: a list of L{AuiDockInfo} classes;
+    :param `src_panes`: a list of L{AuiPaneInfo} classes.
+    """
+    
+    dest_docks = []
+
+    for ii in xrange(len(src_docks)):
+        dest_docks.append(AuiDockInfo())
+        dest_docks[ii].dock_direction = src_docks[ii].dock_direction
+        dest_docks[ii].dock_layer = src_docks[ii].dock_layer
+        dest_docks[ii].dock_row = src_docks[ii].dock_row
+        dest_docks[ii].size = src_docks[ii].size
+        dest_docks[ii].min_size = src_docks[ii].min_size
+        dest_docks[ii].resizable = src_docks[ii].resizable
+        dest_docks[ii].fixed = src_docks[ii].fixed
+        dest_docks[ii].toolbar = src_docks[ii].toolbar
+        dest_docks[ii].panes = src_docks[ii].panes
+        dest_docks[ii].rect = wx.Rect(*src_docks[ii].rect)
+
+    dest_panes = []
+
+    for ii in xrange(len(src_panes)):
+        dest_panes.append(AuiPaneInfo())
+        dest_panes[ii].name = src_panes[ii].name
+        dest_panes[ii].caption = src_panes[ii].caption
+        dest_panes[ii].window = src_panes[ii].window
+        dest_panes[ii].frame = src_panes[ii].frame
+        dest_panes[ii].state = src_panes[ii].state
+        dest_panes[ii].dock_direction = src_panes[ii].dock_direction
+        dest_panes[ii].dock_layer = src_panes[ii].dock_layer
+        dest_panes[ii].dock_row = src_panes[ii].dock_row
+        dest_panes[ii].dock_pos = src_panes[ii].dock_pos
+        dest_panes[ii].best_size = wx.Size(*src_panes[ii].best_size)
+        dest_panes[ii].min_size = wx.Size(*src_panes[ii].min_size)
+        dest_panes[ii].max_size = wx.Size(*src_panes[ii].max_size)
+        dest_panes[ii].floating_pos = wx.Point(*src_panes[ii].floating_pos)
+        dest_panes[ii].floating_size = wx.Size(*src_panes[ii].floating_size)
+        dest_panes[ii].dock_proportion = src_panes[ii].dock_proportion
+        dest_panes[ii].buttons = src_panes[ii].buttons
+        dest_panes[ii].rect = wx.Rect(*src_panes[ii].rect)
+        dest_panes[ii].icon = src_panes[ii].icon
+        dest_panes[ii].notebook_id = src_panes[ii].notebook_id
+        dest_panes[ii].transparent = src_panes[ii].transparent
+        dest_panes[ii].snapped = src_panes[ii].snapped
+        dest_panes[ii].minimize_mode = src_panes[ii].minimize_mode
+
+    for ii in xrange(len(dest_docks)):
+        dock = dest_docks[ii]
+        for jj in xrange(len(dock.panes)):
+            for kk in xrange(len(src_panes)):
+                if dock.panes[jj] == src_panes[kk]:
+                    dock.panes[jj] = dest_panes[kk]
+
+        dest_docks[ii] = dock
+        
+    return dest_docks, dest_panes
+
+
+def GetMaxLayer(docks, dock_direction):
+    """
+    This is an internal function which returns
+    the highest layer inside the specified dock.
+
+    :param `docks`: a list of L{AuiDockInfo};
+    :param `dock_direction`: the L{AuiDockInfo} docking direction to analyze.
+    """
+    
+    max_layer = 0
+
+    for dock in docks:
+        if dock.dock_direction == dock_direction and dock.dock_layer > max_layer and not dock.fixed:
+            max_layer = dock.dock_layer
+    
+    return max_layer
+
+
+def GetMaxRow(panes, dock_direction, dock_layer):
+    """
+    This is an internal function which returns
+    the highest layer inside the specified dock.
+
+    :param `panes`: a list of L{AuiPaneInfo};
+    :param `dock_direction`: the L{AuiPaneInfo} docking direction to analyze;
+    :param `dock_layer`: the L{AuiPaneInfo} layer to analyze.
+    """
+    
+    max_row = 0
+
+    for pane in panes:
+        if pane.dock_direction == dock_direction and pane.dock_layer == dock_layer and \
+           pane.dock_row > max_row:
+            max_row = pane.dock_row
+    
+    return max_row
+
+
+def DoInsertDockLayer(panes, dock_direction, dock_layer):
+    """
+    This is an internal function that inserts a new dock
+    layer by incrementing all existing dock layer values by one.
+    
+    :param `panes`: a list of L{AuiPaneInfo};
+    :param `dock_direction`: the L{AuiPaneInfo} docking direction to analyze;
+    :param `dock_layer`: the L{AuiPaneInfo} layer to analyze.
+    """
+    
+    for ii in xrange(len(panes)):
+        pane = panes[ii]
+        if not pane.IsFloating() and pane.dock_direction == dock_direction and pane.dock_layer >= dock_layer:
+            pane.dock_layer = pane.dock_layer + 1
+
+        panes[ii] = pane
+
+    return panes
+
+
+def DoInsertDockRow(panes, dock_direction, dock_layer, dock_row):
+    """
+    This is an internal function that inserts a new dock
+    row by incrementing all existing dock row values by one.
+    
+    :param `panes`: a list of L{AuiPaneInfo};
+    :param `dock_direction`: the L{AuiPaneInfo} docking direction to analyze;
+    :param `dock_layer`: the L{AuiPaneInfo} layer to analyze;
+    :param `dock_row`: the L{AuiPaneInfo} row to analyze.
+    """
+    
+    for pane in panes:
+        if not pane.IsFloating() and pane.dock_direction == dock_direction and \
+           pane.dock_layer == dock_layer and pane.dock_row >= dock_row:
+            pane.dock_row += 1
+
+    return panes
+
+    
+def DoInsertPane(panes, dock_direction, dock_layer, dock_row, dock_pos):
+    """
+    This is an internal function that inserts a new pane
+    by incrementing all existing dock position values by one.
+    
+    :param `panes`: a list of L{AuiPaneInfo};
+    :param `dock_direction`: the L{AuiPaneInfo} docking direction to analyze;
+    :param `dock_layer`: the L{AuiPaneInfo} layer to analyze;
+    :param `dock_row`: the L{AuiPaneInfo} row to analyze;
+    :param `dock_pos`: the L{AuiPaneInfo} row to analyze.
+    """
+
+    for ii in xrange(len(panes)):
+        pane = panes[ii]
+        if not pane.IsFloating() and pane.dock_direction == dock_direction and \
+           pane.dock_layer == dock_layer and  pane.dock_row == dock_row and \
+           pane.dock_pos >= dock_pos:
+            pane.dock_pos = pane.dock_pos + 1
+
+        panes[ii] = pane
+
+    return panes
+
+
+def FindDocks(docks, dock_direction, dock_layer=-1, dock_row=-1, reverse=False):
+    """
+    This is an internal function that returns a list of docks which meet
+    the specified conditions in the parameters and returns a sorted array
+    (sorted by layer and then row).
+    
+    :param `docks`: a list of L{AuiDockInfo};
+    :param `dock_direction`: the L{AuiDockInfo} docking direction to analyze;
+    :param `dock_layer`: the L{AuiDockInfo} layer to analyze;
+    :param `dock_row`: the L{AuiDockInfo} row to analyze;
+    """
+    
+    matchDocks = [(d.dock_layer, d.dock_row, d.dock_direction, d) for d in docks if \
+                  (dock_direction == -1 or dock_direction == d.dock_direction) and \
+                  ((dock_layer == -1 or dock_layer == d.dock_layer) and \
+                  (dock_row == -1 or dock_row == d.dock_row))]
+    
+    arr = [x[-1] for x in sorted(matchDocks, reverse=reverse)]
+    
+    return arr
+
+
+def FindOppositeDocks(docks, dock_direction):
+    """
+    This is an internal function that returns a list of docks
+    which is related to the opposite direction.
+
+    :param `docks`: a list of L{AuiDockInfo};
+    :param `dock_direction`: the L{AuiDockInfo} docking direction to analyze;
+    """
+
+    if dock_direction == AUI_DOCK_LEFT:
+        arr = FindDocks(docks, AUI_DOCK_RIGHT, -1, -1)
+    elif dock_direction == AUI_DOCK_TOP:
+        arr = FindDocks(docks, AUI_DOCK_BOTTOM, -1, -1)
+    elif dock_direction == AUI_DOCK_RIGHT:
+        arr = FindDocks(docks, AUI_DOCK_LEFT, -1, -1)
+    elif dock_direction == AUI_DOCK_BOTTOM:
+        arr = FindDocks(docks, AUI_DOCK_TOP, -1, -1)
+
+    return arr    
+
+
+def FindPaneInDock(dock, window):
+    """
+    This method looks up a specified window pointer inside a dock.
+    If found, the corresponding L{AuiPaneInfo} pointer is returned, otherwise ``None``.
+
+    :param `dock`: a L{AuiDockInfo} structure;
+    :param `window`: a `wx.Window` derived window (associated to a pane).
+    """
+
+    for p in dock.panes:
+        if p.window == window:
+            return p
+    
+    return None
+
+
+def GetToolBarDockOffsets(docks):
+    """
+    Returns the toolbar dock offsets (top-left and bottom-right).
+
+    :param `docks`: a list of L{AuiDockInfo} to analyze.
+    """
+
+    top_left = wx.Size(0, 0)
+    bottom_right = wx.Size(0, 0)
+
+    for dock in docks:
+        if dock.toolbar:
+            dock_direction = dock.dock_direction
+            if dock_direction == AUI_DOCK_LEFT:
+                top_left.x += dock.rect.width
+                bottom_right.x += dock.rect.width
+
+            elif dock_direction == AUI_DOCK_TOP:
+                top_left.y += dock.rect.height
+                bottom_right.y += dock.rect.height
+
+            elif dock_direction == AUI_DOCK_RIGHT:
+                bottom_right.x += dock.rect.width
+            
+            elif dock_direction == AUI_DOCK_BOTTOM:
+                bottom_right.y += dock.rect.height
+
+    return top_left, bottom_right        
+    
+
+def GetInternalFrameRect(window, docks):
+    """
+    Returns the window rectangle excluding toolbars.
+
+    :param `window`: a `wx.Window` derived window;
+    :param `docks`: a list of L{AuiDockInfo} structures.
+    """
+
+    frameRect = wx.Rect()
+
+    frameRect.SetTopLeft(window.ClientToScreen(window.GetClientAreaOrigin()))
+    frameRect.SetSize(window.GetClientSize())
+
+    top_left, bottom_right = GetToolBarDockOffsets(docks)
+
+    # make adjustments for toolbars
+    frameRect.x += top_left.x
+    frameRect.y += top_left.y
+    frameRect.width -= bottom_right.x
+    frameRect.height -= bottom_right.y
+
+    return frameRect
+
+
+def CheckOutOfWindow(window, pt):
+    """
+    Checks if a point is outside the window rectangle.
+    
+    :param `window`: a `wx.Window` derived window;
+    :param `pt`: a `wx.Point` object.
+    """
+
+    auiWindowMargin = 30
+    marginRect = wx.Rect(*window.GetClientRect())
+    marginRect.Inflate(auiWindowMargin, auiWindowMargin)
+
+    return not marginRect.Contains(pt)
+
+
+def CheckEdgeDrop(window, docks, pt):
+    """
+    Checks on which edge of a window the drop action has taken place.
+
+    :param `window`: a `wx.Window` derived window;
+    :param `docks`: a list of L{AuiDockInfo} structures;
+    :param `pt`: a `wx.Point` object.
+    """
+
+    screenPt = window.ClientToScreen(pt)
+    clientSize = window.GetClientSize()
+    frameRect = GetInternalFrameRect(window, docks)
+
+    if screenPt.y >= frameRect.GetTop() and screenPt.y < frameRect.GetBottom():
+        if pt.x < auiLayerInsertOffset and pt.x > auiLayerInsertOffset - auiLayerInsertPixels:
+            return wx.LEFT
+        
+        if pt.x >= clientSize.x - auiLayerInsertOffset and \
+           pt.x < clientSize.x - auiLayerInsertOffset + auiLayerInsertPixels:
+            return wx.RIGHT
+        
+    if screenPt.x >= frameRect.GetLeft() and screenPt.x < frameRect.GetRight():
+        if pt.y < auiLayerInsertOffset and pt.y > auiLayerInsertOffset - auiLayerInsertPixels:
+            return wx.TOP
+        
+        if pt.y >= clientSize.y - auiLayerInsertOffset and \
+           pt.y < clientSize.y - auiLayerInsertOffset + auiLayerInsertPixels:
+            return wx.BOTTOM
+
+    return -1
+
+
+def RemovePaneFromDocks(docks, pane, exc=None):
+    """
+    Removes a pane window from all docks
+    with a possible exception specified by parameter `exc`.
+
+    :param `docks`: a list of L{AuiDockInfo} structures;
+    :param `pane`: the L{AuiPaneInfo} pane to be removed;
+    :param `exc`: the possible pane exception.
+    """
+    
+    for ii in xrange(len(docks)):
+        d = docks[ii]
+        if d == exc:
+            continue
+        pi = FindPaneInDock(d, pane.window)
+        if pi:
+            d.panes.remove(pi)
+
+        docks[ii] = d            
+
+    return docks
+
+
+def RenumberDockRows(docks):
+    """
+    Takes a dock and assigns sequential numbers
+    to existing rows.  Basically it takes out the gaps so if a
+    dock has rows with numbers 0, 2, 5, they will become 0, 1, 2.
+
+    :param `docks`: a list of L{AuiDockInfo} structures.    
+    """
+    
+    for ii in xrange(len(docks)):
+        dock = docks[ii]
+        dock.dock_row = ii
+        for jj in xrange(len(dock.panes)):
+            dock.panes[jj].dock_row = ii
+
+        docks[ii] = dock
+        
+    return docks
+
+
+def SetActivePane(panes, active_pane):
+    """
+    Sets the active pane, as well as cycles through
+    every other pane and makes sure that all others' active flags
+    are turned off.
+
+    :param `panes`: a list of L{AuiPaneInfo} structures;
+    :param `active_pane`: the pane to be made active (if found).
+    """
+
+    for pane in panes:
+        pane.state &= ~AuiPaneInfo.optionActive
+
+    for pane in panes:
+        if pane.window == active_pane and not pane.IsNotebookPage():
+            pane.state |= AuiPaneInfo.optionActive
+            return True, panes
+            
+    return False, panes
+        
+
+def ShowDockingGuides(guides, show):
+    """
+    Shows or hide the docking guide windows.
+
+    :param `guides`: a list of L{AuiDockingGuideInfo} classes;
+    :param `show`: whether to show or hide the docking guide windows.
+    """
+
+    for target in guides:
+        
+        if show and not target.host.IsShown():
+            target.host.Show()
+            target.host.Update()
+        
+        elif not show and target.host.IsShown():        
+            target.host.Hide()
+        
+
+def RefreshDockingGuides(guides):
+    """
+    Refreshes the docking guide windows.
+
+    :param `guides`: a list of L{AuiDockingGuideInfo} classes;
+    """
+    
+    for target in guides:
+        if target.host.IsShown():
+            target.host.Refresh()
+        
+    
+def PaneSortFunc(p1, p2):
+    """
+    This function is used to sort panes by dock position.
+
+    :param `p1`: a L{AuiPaneInfo} instance;
+    :param `p2`: another L{AuiPaneInfo} instance.    
+    """
+    
+    return (p1.dock_pos < p2.dock_pos and [-1] or [1])[0]
+
+
+def GetNotebookRoot(panes, notebook_id):
+    """
+    Returns the L{AuiPaneInfo} which has the specified `notebook_id`.
+
+    :param `panes`: a list of L{AuiPaneInfo} instances;
+    :param `notebook_id`: the target notebook id.
+    """    
+
+    for paneInfo in panes:
+        if paneInfo.IsNotebookControl() and paneInfo.notebook_id == notebook_id:
+            return paneInfo
+        
+    return None
+
+
+def EscapeDelimiters(s):
+    """
+    Changes ``;`` into ``\`` and ``|`` into ``\|`` in the input string.  
+
+    :param `s`: the string to be analyzed.
+
+    :note: This is an internal functions which is used for saving perspectives.    
+    """
+    
+    result = s.replace(";", "\\")
+    result = result.replace("|", "|\\")
+    
+    return result
+
+
+def IsDifferentDockingPosition(pane1, pane2):
+    """
+    Returns whether `pane1` and `pane2` are in a different docking position
+    based on pane status, docking direction, docking layer and docking row.
+
+    :param `pane1`: a L{AuiPaneInfo} instance;
+    :param `pane2`: another L{AuiPaneInfo} instance.
+    """
+
+    return pane1.IsFloating() != pane2.IsFloating() or \
+           pane1.dock_direction != pane2.dock_direction or \
+           pane1.dock_layer != pane2.dock_layer or \
+           pane1.dock_row != pane2.dock_row
+
+
+# Convenience function
+def AuiManager_HasLiveResize(manager):
+    """
+    Static function which returns if the input `manager` should have "live resize"
+    behaviour.
+
+    :param `manager`: an instance of L{AuiManager}.
+
+    :note: This method always returns ``True`` on wxMac as this platform doesn't have
+     the ability to use `wx.ScreenDC` to draw sashes.
+    """
+
+    # With Core Graphics on Mac, it's not possible to show sash feedback,
+    # so we'll always use live update instead.
+    
+    if wx.Platform == "__WXMAC__":
+        return True
+    else:
+        return (manager.GetAGWFlags() & AUI_MGR_LIVE_RESIZE) == AUI_MGR_LIVE_RESIZE
+
+
+# Convenience function
+def AuiManager_UseNativeMiniframes(manager):
+    """
+    Static function which returns if the input `manager` should use native `wx.MiniFrame` as
+    floating panes.
+
+    :param `manager`: an instance of L{AuiManager}.
+
+    :note: This method always returns ``True`` on wxMac as this platform doesn't have
+     the ability to use custom drawn miniframes.
+    """
+
+    # With Core Graphics on Mac, it's not possible to show sash feedback,
+    # so we'll always use live update instead.
+    
+    if wx.Platform == "__WXMAC__":
+        return True
+    else:
+        return (manager.GetAGWFlags() & AUI_MGR_USE_NATIVE_MINIFRAMES) == AUI_MGR_USE_NATIVE_MINIFRAMES
+
+
+def GetManager(window):
+    """
+    This function will return the aui manager for a given window.
+    
+    :param `window`: this parameter should be any child window or grand-child
+     window (and so on) of the frame/window managed by L{AuiManager}. The window
+     does not need to be managed by the manager itself, nor does it even need
+     to be a child or sub-child of a managed window. It must however be inside
+     the window hierarchy underneath the managed window.
+    """
+    
+    if not isinstance(wx.GetTopLevelParent(window), AuiFloatingFrame):
+        if isinstance(window, auibar.AuiToolBar):
+            return window.GetAuiManager()
+    
+    evt = AuiManagerEvent(wxEVT_AUI_FIND_MANAGER)
+    evt.SetManager(None)
+    evt.ResumePropagation(wx.EVENT_PROPAGATE_MAX)
+
+    if not window.GetEventHandler().ProcessEvent(evt):
+        return None
+
+    return evt.GetManager()
+
+
+# ---------------------------------------------------------------------------- #
+
+class AuiManager(wx.EvtHandler):
+    """
+    AuiManager manages the panes associated with it for a particular `wx.Frame`,
+    using a pane's L{AuiPaneInfo} information to determine each pane's docking and
+    floating behavior. L{AuiManager} uses wxPython's sizer mechanism to plan the
+    layout of each frame. It uses a replaceable dock art class to do all drawing,
+    so all drawing is localized in one area, and may be customized depending on an
+    applications' specific needs.
+
+    L{AuiManager} works as follows: the programmer adds panes to the class, or makes
+    changes to existing pane properties (dock position, floating state, show state, etc...).
+    To apply these changes, the L{AuiManager.Update} function is called. This batch
+    processing can be used to avoid flicker, by modifying more than one pane at a time,
+    and then "committing" all of the changes at once by calling `Update()`.
+
+    Panes can be added quite easily::
+
+        text1 = wx.TextCtrl(self, -1)
+        text2 = wx.TextCtrl(self, -1)
+        self._mgr.AddPane(text1, AuiPaneInfo().Left().Caption("Pane Number One"))
+        self._mgr.AddPane(text2, AuiPaneInfo().Bottom().Caption("Pane Number Two"))
+
+        self._mgr.Update()
+
+
+    Later on, the positions can be modified easily. The following will float an
+    existing pane in a tool window::
+
+        self._mgr.GetPane(text1).Float()
+
+
+    **Layers, Rows and Directions, Positions:**
+    
+    Inside AUI, the docking layout is figured out by checking several pane parameters.
+    Four of these are important for determining where a pane will end up.
+
+    **Direction** - Each docked pane has a direction, `Top`, `Bottom`, `Left`, `Right`, or `Center`.
+    This is fairly self-explanatory. The pane will be placed in the location specified
+    by this variable.
+
+    **Position** - More than one pane can be placed inside of a "dock". Imagine two panes
+    being docked on the left side of a window. One pane can be placed over another.
+    In proportionally managed docks, the pane position indicates it's sequential position,
+    starting with zero. So, in our scenario with two panes docked on the left side, the
+    top pane in the dock would have position 0, and the second one would occupy position 1. 
+
+    **Row** - A row can allow for two docks to be placed next to each other. One of the most
+    common places for this to happen is in the toolbar. Multiple toolbar rows are allowed,
+    the first row being in row 0, and the second in row 1. Rows can also be used on
+    vertically docked panes. 
+
+    **Layer** - A layer is akin to an onion. Layer 0 is the very center of the managed pane.
+    Thus, if a pane is in layer 0, it will be closest to the center window (also sometimes
+    known as the "content window"). Increasing layers "swallow up" all layers of a lower
+    value. This can look very similar to multiple rows, but is different because all panes
+    in a lower level yield to panes in higher levels. The best way to understand layers
+    is by running the AUI sample (`AUI.py`).
+    """
+
+    def __init__(self, managed_window=None, agwFlags=None):
+        """
+        Default class constructor.
+        
+        :param `managed_window`: specifies the window which should be managed;
+        :param `agwFlags`: specifies options which allow the frame management behavior to be
+         modified. `agwFlags` can be a combination of the following style bits:
+
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_MGR_ALLOW_FLOATING``           Allow floating of panes
+         ``AUI_MGR_ALLOW_ACTIVE_PANE``        If a pane becomes active, "highlight" it in the interface
+         ``AUI_MGR_TRANSPARENT_DRAG``         If the platform supports it, set transparency on a floating pane while it is dragged by the user
+         ``AUI_MGR_TRANSPARENT_HINT``         If the platform supports it, show a transparent hint window when the user is about to dock a floating pane
+         ``AUI_MGR_VENETIAN_BLINDS_HINT``     Show a "venetian blind" effect when the user is about to dock a floating pane
+         ``AUI_MGR_RECTANGLE_HINT``           Show a rectangle hint effect when the user is about to dock a floating pane
+         ``AUI_MGR_HINT_FADE``                If the platform supports it, the hint window will fade in and out
+         ``AUI_MGR_NO_VENETIAN_BLINDS_FADE``  Disables the "venetian blind" fade in and out
+         ``AUI_MGR_LIVE_RESIZE``              Live resize when the user drag a sash
+         ``AUI_MGR_ANIMATE_FRAMES``           Fade-out floating panes when they are closed (all platforms which support frames transparency) and show a moving rectangle when they are docked (Windows < Vista and GTK only)
+         ``AUI_MGR_AERO_DOCKING_GUIDES``      Use the new Aero-style bitmaps as docking guides
+         ``AUI_MGR_PREVIEW_MINIMIZED_PANES``  Slide in and out minimized panes to preview them
+         ``AUI_MGR_WHIDBEY_DOCKING_GUIDES``   Use the new Whidbey-style bitmaps as docking guides
+         ``AUI_MGR_SMOOTH_DOCKING``           Performs a "smooth" docking of panes (a la PyQT)
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES``    Use miniframes with native caption bar as floating panes instead or custom drawn caption bars (forced on wxMac)
+         ``AUI_MGR_AUTONB_NO_CAPTION``        Panes that merge into an automatic notebook will not have the pane caption visible
+         ==================================== ==================================
+
+         Default value for `agwFlags` is:
+         ``AUI_MGR_DEFAULT`` = ``AUI_MGR_ALLOW_FLOATING`` | ``AUI_MGR_TRANSPARENT_HINT`` | ``AUI_MGR_HINT_FADE`` | ``AUI_MGR_NO_VENETIAN_BLINDS_FADE``
+
+         :note: If using the ``AUI_MGR_USE_NATIVE_MINIFRAMES``, double-clicking on a
+          floating pane caption will not re-dock the pane, but simply maximize it (if
+          L{AuiPaneInfo.MaximizeButton} has been set to ``True``) or do nothing.
+        """
+
+        wx.EvtHandler.__init__(self)
+        
+        self._action = actionNone
+        self._action_window = None
+        self._hover_button = None
+        self._art = dockart.AuiDefaultDockArt()
+        self._hint_window = None
+        self._active_pane = None
+        self._has_maximized = False
+        self._has_minimized = False
+
+        self._frame = None
+        self._dock_constraint_x = 0.3
+        self._dock_constraint_y = 0.3
+        self._reserved = None
+    
+        self._panes = []
+        self._docks = []
+        self._uiparts = []
+        
+        self._guides = []
+        self._notebooks = []
+
+        self._masterManager = None
+        self._currentDragItem = -1
+        self._lastknowndocks = {}
+
+        self._hint_fadetimer = wx.Timer(self, wx.ID_ANY)
+        self._hint_fademax = 50
+        self._last_hint = wx.Rect()
+
+        self._from_move = False
+        self._last_rect = wx.Rect()
+        
+        if agwFlags is None:
+            agwFlags = AUI_MGR_DEFAULT
+            
+        self._agwFlags = agwFlags
+        self._is_docked = (False, wx.RIGHT, wx.TOP, 0)
+        self._snap_limits = (15, 15)
+
+        if wx.Platform == "__WXMSW__":
+            self._animation_step = 30.0
+        else:
+            self._animation_step = 5.0
+
+        self._hint_rect = wx.Rect()
+
+        self._preview_timer = wx.Timer(self, wx.ID_ANY)
+        self._sliding_frame = None
+
+        self._autoNBTabArt = tabart.AuiDefaultTabArt()
+        self._autoNBStyle = AUI_NB_DEFAULT_STYLE | AUI_NB_BOTTOM | \
+                            AUI_NB_SUB_NOTEBOOK | AUI_NB_TAB_EXTERNAL_MOVE
+        self._autoNBStyle -= AUI_NB_DRAW_DND_TAB
+
+        if managed_window:
+            self.SetManagedWindow(managed_window)
+
+        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_SET_CURSOR, self.OnSetCursor)
+        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_MOTION, self.OnMotion)
+        self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
+        self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus)
+        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost)
+        self.Bind(wx.EVT_TIMER, self.OnHintFadeTimer, self._hint_fadetimer)
+        self.Bind(wx.EVT_TIMER, self.SlideIn, self._preview_timer)
+
+        self.Bind(wx.EVT_MOVE, self.OnMove)
+        self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged)
+        
+        self.Bind(EVT_AUI_PANE_BUTTON, self.OnPaneButton)
+        self.Bind(EVT_AUI_RENDER, self.OnRender)
+        self.Bind(EVT_AUI_FIND_MANAGER, self.OnFindManager)
+        self.Bind(EVT_AUI_PANE_MIN_RESTORE, self.OnRestoreMinimizedPane)
+        self.Bind(EVT_AUI_PANE_DOCKED, self.OnPaneDocked)
+
+        self.Bind(auibook.EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnTabBeginDrag)
+        self.Bind(auibook.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnTabPageClose)
+        self.Bind(auibook.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnTabSelected)
+        
+
+    def CreateFloatingFrame(self, parent, pane_info):
+        """
+        Creates a floating frame for the windows.
+
+        :param `parent`: the floating frame parent;
+        :param `pane_info`: the L{AuiPaneInfo} class with all the pane's information.
+        """
+
+        return AuiFloatingFrame(parent, self, pane_info)
+
+
+    def CanDockPanel(self, p):
+        """
+        Returns whether a pane can be docked or not.
+
+        :param `p`: the L{AuiPaneInfo} class with all the pane's information.
+        """        
+
+        # is the pane dockable?
+        if not p.IsDockable():
+            return False
+
+        # if a key modifier is pressed while dragging the frame,
+        # don't dock the window
+        return not (wx.GetKeyState(wx.WXK_CONTROL) or wx.GetKeyState(wx.WXK_ALT))
+
+
+    def GetPaneByWidget(self, window):
+        """
+        This version of L{GetPane} looks up a pane based on a
+        'pane window'.
+
+        :param `window`: a `wx.Window` derived window.
+
+        :see: L{GetPane}
+        """
+
+        for p in self._panes:
+            if p.window == window:
+                return p
+
+        return NonePaneInfo
+
+
+    def GetPaneByName(self, name):
+        """
+        This version of L{GetPane} looks up a pane based on a
+        'pane name'.
+
+        :param `name`: the pane name.
+
+        :see: L{GetPane}        
+        """
+        
+        for p in self._panes:
+            if p.name == name:
+                return p
+        
+        return NonePaneInfo
+
+
+    def GetPane(self, item):
+        """
+        Looks up a L{AuiPaneInfo} structure based
+        on the supplied window pointer. Upon failure, L{GetPane}
+        returns an empty L{AuiPaneInfo}, a condition which can be checked
+        by calling L{AuiPaneInfo.IsOk}.
+
+        The pane info's structure may then be modified. Once a pane's
+        info is modified, L{Update} must be called to
+        realize the changes in the UI.
+
+        :param `item`: either a pane name or a `wx.Window`.        
+        """
+
+        if isinstance(item, basestring):
+            return self.GetPaneByName(item)
+        else:
+            return self.GetPaneByWidget(item)
+
+
+    def GetAllPanes(self):
+        """ Returns a reference to all the pane info structures. """
+        
+        return self._panes
+
+
+    def ShowPane(self, window, show):
+        """
+        Shows or hides a pane based on the window passed as input.
+
+        :param `window`: a `wx.Window` derived window;
+        :param `show`: ``True`` to show the pane, ``False`` otherwise.
+        """
+
+        p = self.GetPane(window)
+        
+        if p.IsOk():
+            if p.IsNotebookPage():
+                if show:
+                
+                    notebook = self._notebooks[p.notebook_id]
+                    id = notebook.GetPageIndex(p.window)
+                    if id >= 0:
+                        notebook.SetSelection(id)
+                    self.ShowPane(notebook, True)
+                
+            else:
+                p.Show(show)
+                
+            if p.frame:
+                p.frame.Raise()
+                
+            self.Update()
+
+            
+    def HitTest(self, x, y):
+        """
+        This is an internal function which determines
+        which UI item the specified coordinates are over.
+        
+        :param `x`: specifies a x position in client coordinates;
+        :param `y`: specifies a y position in client coordinates.
+        """
+
+        result = None
+
+        for item in self._uiparts:
+            # we are not interested in typeDock, because this space 
+            # isn't used to draw anything, just for measurements
+            # besides, the entire dock area is covered with other
+            # rectangles, which we are interested in.
+            if item.type == AuiDockUIPart.typeDock:
+                continue
+
+            # if we already have a hit on a more specific item, we are not
+            # interested in a pane hit.  If, however, we don't already have
+            # a hit, returning a pane hit is necessary for some operations
+            if item.type in [AuiDockUIPart.typePane, AuiDockUIPart.typePaneBorder] and result:
+                continue
+        
+            # if the point is inside the rectangle, we have a hit
+            if item.rect.Contains((x, y)):
+                result = item
+        
+        return result
+
+
+    def PaneHitTest(self, panes, pt):
+        """
+        Similar to L{HitTest}, but it checks in which L{AuiPaneInfo} rectangle the
+        input point belongs to.
+
+        :param `panes`: a list of L{AuiPaneInfo} instances;
+        :param `pt`: a `wx.Point` object.
+        """
+
+        for paneInfo in panes:
+            if paneInfo.IsDocked() and paneInfo.IsShown() and paneInfo.rect.Contains(pt):
+                return paneInfo
+
+        return NonePaneInfo
+
+
+    # SetAGWFlags() and GetAGWFlags() allow the owner to set various
+    # options which are global to AuiManager
+
+    def SetAGWFlags(self, agwFlags):
+        """
+        This method is used to specify L{AuiManager}'s settings flags.
+
+        :param `agwFlags`: specifies options which allow the frame management behavior
+         to be modified. `agwFlags` can be one of the following style bits:
+
+         ==================================== ==================================
+         Flag name                            Description
+         ==================================== ==================================
+         ``AUI_MGR_ALLOW_FLOATING``           Allow floating of panes
+         ``AUI_MGR_ALLOW_ACTIVE_PANE``        If a pane becomes active, "highlight" it in the interface
+         ``AUI_MGR_TRANSPARENT_DRAG``         If the platform supports it, set transparency on a floating pane while it is dragged by the user
+         ``AUI_MGR_TRANSPARENT_HINT``         If the platform supports it, show a transparent hint window when the user is about to dock a floating pane
+         ``AUI_MGR_VENETIAN_BLINDS_HINT``     Show a "venetian blind" effect when the user is about to dock a floating pane
+         ``AUI_MGR_RECTANGLE_HINT``           Show a rectangle hint effect when the user is about to dock a floating pane
+         ``AUI_MGR_HINT_FADE``                If the platform supports it, the hint window will fade in and out
+         ``AUI_MGR_NO_VENETIAN_BLINDS_FADE``  Disables the "venetian blind" fade in and out
+         ``AUI_MGR_LIVE_RESIZE``              Live resize when the user drag a sash
+         ``AUI_MGR_ANIMATE_FRAMES``           Fade-out floating panes when they are closed (all platforms which support frames transparency) and show a moving rectangle when they are docked (Windows < Vista and GTK only)
+         ``AUI_MGR_AERO_DOCKING_GUIDES``      Use the new Aero-style bitmaps as docking guides
+         ``AUI_MGR_PREVIEW_MINIMIZED_PANES``  Slide in and out minimized panes to preview them
+         ``AUI_MGR_WHIDBEY_DOCKING_GUIDES``   Use the new Whidbey-style bitmaps as docking guides        
+         ``AUI_MGR_SMOOTH_DOCKING``           Performs a "smooth" docking of panes (a la PyQT)
+         ``AUI_MGR_USE_NATIVE_MINIFRAMES``    Use miniframes with native caption bar as floating panes instead or custom drawn caption bars (forced on wxMac)
+         ``AUI_MGR_AUTONB_NO_CAPTION``        Panes that merge into an automatic notebook will not have the pane caption visible
+         ==================================== ==================================
+
+         :note: If using the ``AUI_MGR_USE_NATIVE_MINIFRAMES``, double-clicking on a
+          floating pane caption will not re-dock the pane, but simply maximize it (if
+          L{AuiPaneInfo.MaximizeButton} has been set to ``True``) or do nothing.
+        
+        """
+        
+        self._agwFlags = agwFlags
+
+        if len(self._guides) > 0:
+            self.CreateGuideWindows()
+
+        if self._hint_window and agwFlags & AUI_MGR_RECTANGLE_HINT == 0:
+            self.CreateHintWindow()
+
+
+    def GetAGWFlags(self):
+        """
+        Returns the current manager's flags.
+
+        :see: L{SetAGWFlags} for a list of possible L{AuiManager} flags.
+        """
+        
+        return self._agwFlags
+        
+
+    def SetManagedWindow(self, managed_window):
+        """
+        Called to specify the frame or window which is to be managed by L{AuiManager}.
+        Frame management is not restricted to just frames. Child windows or custom
+        controls are also allowed.
+
+        :param `managed_window`: specifies the window which should be managed by
+         the AUI manager.
+        """
+
+        if not managed_window:
+            raise Exception("Specified managed window must be non-null. ")
+        
+        self._frame = managed_window
+        self._frame.PushEventHandler(self)
+
+        # if the owner is going to manage an MDI parent frame,
+        # we need to add the MDI client window as the default
+        # center pane
+
+        if isinstance(self._frame, wx.MDIParentFrame):
+            mdi_frame = self._frame
+            client_window = mdi_frame.GetClientWindow()
+
+            if not client_window:
+                raise Exception("Client window is None!")
+
+            self.AddPane(client_window, AuiPaneInfo().Name("mdiclient").
+                         CenterPane().PaneBorder(False))
+
+        elif isinstance(self._frame, tabmdi.AuiMDIParentFrame):
+
+            mdi_frame = self._frame
+            client_window = mdi_frame.GetClientWindow()
+
+            if not client_window:
+                raise Exception("Client window is None!")
+
+            self.AddPane(client_window, AuiPaneInfo().Name("mdiclient").
+                         CenterPane().PaneBorder(False))
+
+
+    def GetManagedWindow(self):
+        """ Returns the window being managed by L{AuiManager}. """
+        
+        return self._frame
+
+
+    def SetFrame(self, managed_window):
+        """
+        Called to specify the frame or window which is to be managed by L{AuiManager}.
+        Frame management is not restricted to just frames. Child windows or custom
+        controls are also allowed.
+
+        :param `managed_window`: specifies the window which should be managed by
+         the AUI manager.
+
+        :warning: This method is now deprecated, use L{SetManagedWindow} instead.
+        """
+
+        DeprecationWarning("This method is deprecated, use SetManagedWindow instead.")
+        return self.SetManagedWindow(managed_window)
+    
+        
+    def GetFrame(self):
+        """
+        Returns the window being managed by L{AuiManager}.
+
+        :warning: This method is now deprecated, use L{GetManagedWindow} instead.
+        """
+
+        DeprecationWarning("This method is deprecated, use GetManagedWindow instead.")        
+        return self._frame
+
+
+    def CreateGuideWindows(self):
+        """ Creates the VS2005 HUD guide windows. """
+
+        self.DestroyGuideWindows()
+
+        self._guides.append(AuiDockingGuideInfo().Left().
+                            Host(AuiSingleDockingGuide(self._frame, wx.LEFT)))
+        self._guides.append(AuiDockingGuideInfo().Top().
+                            Host(AuiSingleDockingGuide(self._frame, wx.TOP)))
+        self._guides.append(AuiDockingGuideInfo().Right().
+                            Host(AuiSingleDockingGuide(self._frame, wx.RIGHT)))
+        self._guides.append(AuiDockingGuideInfo().Bottom().
+                            Host(AuiSingleDockingGuide(self._frame, wx.BOTTOM)))
+        self._guides.append(AuiDockingGuideInfo().Centre().
+                            Host(AuiCenterDockingGuide(self._frame)))
+
+
+    def DestroyGuideWindows(self):
+        """ Destroys the VS2005 HUD guide windows. """
+
+        for guide in self._guides:
+            if guide.host:
+                guide.host.Destroy()
+        
+        self._guides = []
+    
+
+    def CreateHintWindow(self):
+        """ Creates the standard wxAUI hint window. """
+
+        self.DestroyHintWindow()
+
+        self._hint_window = AuiDockingHintWindow(self._frame)
+        self._hint_window.SetBlindMode(self._agwFlags)
+
+
+    def DestroyHintWindow(self):
+        """ Destroys the standard wxAUI hint window. """
+
+        if self._hint_window:
+
+            self._hint_window.Destroy()
+            self._hint_window = None
+
+
+    def UnInit(self):
+        """
+        Uninitializes the framework and should be called before a managed frame or
+        window is destroyed. L{UnInit} is usually called in the managed `wx.Frame`/`wx.Window`
+        destructor.
+
+        It is necessary to call this function before the managed frame or window is
+        destroyed, otherwise the manager cannot remove its custom event handlers
+        from a window.
+        """
+
+        if self._frame:
+            self._frame.RemoveEventHandler(self)
+
+
+    def GetArtProvider(self):
+        """ Returns the current art provider being used. """
+        
+        return self._art
+
+
+    def ProcessMgrEvent(self, event):
+        """
+        Process the AUI events sent to the manager.
+
+        :param `event`: the event to process, an instance of L{AuiManagerEvent}.
+        """
+
+        # first, give the owner frame a chance to override
+        if self._frame:
+            if self._frame.GetEventHandler().ProcessEvent(event):
+                return
+        
+        self.ProcessEvent(event)
+
+
+    def FireEvent(self, evtType, pane, canVeto=False):
+        """
+        Fires one of the ``EVT_AUI_PANE_FLOATED``/``FLOATING``/``DOCKING``/``DOCKED``/``ACTIVATED`` event. 
+
+        :param `evtType`: one of the aforementioned events;
+        :param `pane`: the L{AuiPaneInfo} instance associated to this event;
+        :param `canVeto`: whether the event can be vetoed or not.
+        """        
+
+        event = AuiManagerEvent(evtType)
+        event.SetPane(pane)
+        event.SetCanVeto(canVeto)
+        self.ProcessMgrEvent(event)
+
+        return event
+
+    
+    def CanUseModernDockArt(self):
+        """
+        Returns whether L{ModernDockArt} can be used (Windows XP / Vista / 7 only,
+        requires Mark Hammonds's `pywin32` package).
+        """
+
+        if not _winxptheme:
+            return False
+
+        # Get the size of a small close button (themed)
+        hwnd = self._frame.GetHandle()
+        hTheme = winxptheme.OpenThemeData(hwnd, "Window")
+
+        if not hTheme:
+            return False
+
+        return True
+            
+    
+    def SetArtProvider(self, art_provider):
+        """
+        Instructs L{AuiManager} to use art provider specified by the parameter
+        `art_provider` for all drawing calls. This allows plugable look-and-feel
+        features.
+
+        :param `art_provider`: a AUI dock art provider.
+
+        :note: The previous art provider object, if any, will be deleted by L{AuiManager}.
+        """
+
+        # delete the last art provider, if any
+        del self._art
+        
+        # assign the new art provider
+        self._art = art_provider
+
+        for pane in self.GetAllPanes():
+            if pane.IsFloating() and pane.frame:                
+                pane.frame._mgr.SetArtProvider(art_provider)
+                pane.frame._mgr.Update()
+
+
+    def AddPane(self, window, arg1=None, arg2=None, target=None):
+        """
+        Tells the frame manager to start managing a child window. There
+        are four versions of this function. The first verison allows the full spectrum
+        of pane parameter possibilities (L{AddPane1}). The second version is used for
+        simpler user interfaces which do not require as much configuration (L{AddPane2}).
+        The L{AddPane3} version allows a drop position to be specified, which will determine
+        where the pane will be added. The L{AddPane4} version allows to turn the target
+        L{AuiPaneInfo} pane into a notebook and the added pane into a page.
+
+        In wxPython, simply call L{AddPane}.
+
+        :param `window`: the child window to manage;
+        :param `arg1`: a L{AuiPaneInfo} or an integer value (direction);
+        :param `arg2`: a L{AuiPaneInfo} or a `wx.Point` (drop position);
+        :param `target`: a L{AuiPaneInfo} to be turned into a notebook
+         and new pane added to it as a page. (additionally, target can be any pane in 
+         an existing notebook)
+         """
+        if target in self._panes:
+            return self.AddPane4(window, arg1, target)
+
+        if type(arg1) == type(1):
+            # This Is Addpane2
+            if arg1 is None:
+                arg1 = wx.LEFT
+            if arg2 is None:
+                arg2 = ""
+            return self.AddPane2(window, arg1, arg2)
+        else:
+            if isinstance(arg2, wx.Point):
+                return self.AddPane3(window, arg1, arg2)
+            else:
+                return self.AddPane1(window, arg1)
+        
+
+    def AddPane1(self, window, pane_info):
+        """ See comments on L{AddPane}. """
+
+        # check if the pane has a valid window
+        if not window:
+            return False
+
+        # check if the pane already exists
+        if self.GetPane(pane_info.window).IsOk():
+            return False
+
+        # check if the pane name already exists, this could reveal a
+        # bug in the library user's application
+        already_exists = False
+        if pane_info.name != "" and self.GetPane(pane_info.name).IsOk():
+            warnings.warn("A pane with the name '%s' already exists in the manager!"%pane_info.name)
+            already_exists = True
+
+        # if the new pane is docked then we should undo maximize
+        if pane_info.IsDocked():
+            self.RestoreMaximizedPane()
+
+        self._panes.append(pane_info)
+        pinfo = self._panes[-1]
+
+        # set the pane window
+        pinfo.window = window
+
+        # if the pane's name identifier is blank, create a random string
+        if pinfo.name == "" or already_exists:
+            pinfo.name = ("%s%08x%08x%08x")%(pinfo.window.GetName(), time.time(),
+                                             time.clock(), len(self._panes))
+
+        # set initial proportion (if not already set)
+        if pinfo.dock_proportion == 0:
+            pinfo.dock_proportion = 100000
+
+        floating = isinstance(self._frame, AuiFloatingFrame)
+
+        pinfo.buttons = []
+
+        if not floating and pinfo.HasMinimizeButton():
+            button = AuiPaneButton(AUI_BUTTON_MINIMIZE)
+            pinfo.buttons.append(button)
+    
+        if not floating and pinfo.HasMaximizeButton():
+            button = AuiPaneButton(AUI_BUTTON_MAXIMIZE_RESTORE)
+            pinfo.buttons.append(button)
+
+        if not floating and pinfo.HasPinButton():
+            button = AuiPaneButton(AUI_BUTTON_PIN)
+            pinfo.buttons.append(button)
+
+        if pinfo.HasCloseButton():
+            button = AuiPaneButton(AUI_BUTTON_CLOSE)
+            pinfo.buttons.append(button)
+
+        if pinfo.HasGripper():
+            if isinstance(pinfo.window, auibar.AuiToolBar):
+                # prevent duplicate gripper -- both AuiManager and AuiToolBar
+                # have a gripper control.  The toolbar's built-in gripper
+                # meshes better with the look and feel of the control than ours,
+                # so turn AuiManager's gripper off, and the toolbar's on.
+
+                tb = pinfo.window
+                pinfo.SetFlag(AuiPaneInfo.optionGripper, False)
+                tb.SetGripperVisible(True)
+
+        if pinfo.window:
+            if pinfo.best_size == wx.Size(-1, -1):
+                pinfo.best_size = pinfo.window.GetClientSize()
+
+            if isinstance(pinfo.window, wx.ToolBar):
+                # GetClientSize() doesn't get the best size for
+                # a toolbar under some newer versions of wxWidgets,
+                # so use GetBestSize()
+                pinfo.best_size = pinfo.window.GetBestSize()
+
+                # this is needed for Win2000 to correctly fill toolbar backround
+                # it should probably be repeated once system colour change happens
+                if wx.Platform == "__WXMSW__" and pinfo.window.UseBgCol():
+                    pinfo.window.SetBackgroundColour(self.GetArtProvider().GetColour(AUI_DOCKART_BACKGROUND_COLOUR))
+                
+            if pinfo.min_size != wx.Size(-1, -1):
+                if pinfo.best_size.x < pinfo.min_size.x:
+                    pinfo.best_size.x = pinfo.min_size.x
+                if pinfo.best_size.y < pinfo.min_size.y:
+                    pinfo.best_size.y = pinfo.min_size.y
+
+        self._panes[-1] = pinfo
+        if isinstance(window, auibar.AuiToolBar):
+            window.SetAuiManager(self)
+
+        return True
+
+
+    def AddPane2(self, window, direction, caption):
+        """ See comments on L{AddPane}. """
+        
+        pinfo = AuiPaneInfo()
+        pinfo.Caption(caption)
+        
+        if direction == wx.TOP:
+            pinfo.Top()
+        elif direction == wx.BOTTOM:
+            pinfo.Bottom()
+        elif direction == wx.LEFT:
+            pinfo.Left()
+        elif direction == wx.RIGHT:
+            pinfo.Right()
+        elif direction == wx.CENTER:
+            pinfo.CenterPane()
+        
+        return self.AddPane(window, pinfo)
+
+
+    def AddPane3(self, window, pane_info, drop_pos):
+        """ See comments on L{AddPane}. """
+        
+        if not self.AddPane(window, pane_info):
+            return False
+
+        pane = self.GetPane(window)
+        indx = self._panes.index(pane)
+
+        ret, pane = self.DoDrop(self._docks, self._panes, pane, drop_pos, wx.Point(0, 0))
+        self._panes[indx] = pane
+
+        return True
+
+
+    def AddPane4(self, window, pane_info, target):
+        """ See comments on L{AddPane}. """
+        
+        if not self.AddPane(window, pane_info):
+            return False
+               
+        paneInfo = self.GetPane(window)
+        
+        if not paneInfo.IsNotebookDockable():
+            return self.AddPane1(window, pane_info)
+        if not target.IsNotebookDockable() and not target.IsNotebookControl():
+            return self.AddPane1(window, pane_info)
+
+        if not target.HasNotebook():
+            self.CreateNotebookBase(self._panes, target)
+        
+        # Add new item to notebook
+        paneInfo.NotebookPage(target.notebook_id)
+
+        # we also want to remove our captions sometimes
+        self.RemoveAutoNBCaption(paneInfo)
+        self.UpdateNotebook()
+        
+        return True
+
+
+    def InsertPane(self, window, pane_info, insert_level=AUI_INSERT_PANE):
+        """
+        This method is used to insert either a previously unmanaged pane window
+        into the frame manager, or to insert a currently managed pane somewhere else.
+        L{InsertPane} will push all panes, rows, or docks aside and insert the window
+        into the position specified by `pane_info`.
+
+        Because `pane_info` can specify either a pane, dock row, or dock layer, the
+        `insert_level` parameter is used to disambiguate this. The parameter `insert_level`
+        can take a value of ``AUI_INSERT_PANE``, ``AUI_INSERT_ROW`` or ``AUI_INSERT_DOCK``.
+
+        :param `window`: the window to be inserted and managed;
+        :param `pane_info`: the insert location for the new window;
+        :param `insert_level`: the insertion level of the new pane.
+        """
+
+        if not window:
+            raise Exception("Invalid window passed to InsertPane.")
+                            
+        # shift the panes around, depending on the insert level
+        if insert_level == AUI_INSERT_PANE:
+            self._panes = DoInsertPane(self._panes, pane_info.dock_direction,
+                                       pane_info.dock_layer, pane_info.dock_row,
+                                       pane_info.dock_pos)
+
+        elif insert_level == AUI_INSERT_ROW:
+            self._panes = DoInsertDockRow(self._panes, pane_info.dock_direction,
+                                          pane_info.dock_layer, pane_info.dock_row)
+
+        elif insert_level == AUI_INSERT_DOCK:
+            self._panes = DoInsertDockLayer(self._panes, pane_info.dock_direction,
+                                            pane_info.dock_layer)
+        
+        # if the window already exists, we are basically just moving/inserting the
+        # existing window.  If it doesn't exist, we need to add it and insert it
+        existing_pane = self.GetPane(window)
+        indx = self._panes.index(existing_pane)
+        
+        if not existing_pane.IsOk():
+        
+            return self.AddPane(window, pane_info)
+        
+        else:
+        
+            if pane_info.IsFloating():
+                existing_pane.Float()
+                if pane_info.floating_pos != wx.Point(-1, -1):
+                    existing_pane.FloatingPosition(pane_info.floating_pos)
+                if pane_info.floating_size != wx.Size(-1, -1):
+                    existing_pane.FloatingSize(pane_info.floating_size)
+            else:
+                # if the new pane is docked then we should undo maximize
+                self.RestoreMaximizedPane()
+
+                existing_pane.Direction(pane_info.dock_direction)
+                existing_pane.Layer(pane_info.dock_layer)
+                existing_pane.Row(pane_info.dock_row)
+                existing_pane.Position(pane_info.dock_pos)
+
+            self._panes[indx] = existing_pane                
+            
+        return True
+
+    
+    def DetachPane(self, window):
+        """
+        Tells the L{AuiManager} to stop managing the pane specified
+        by `window`. The window, if in a floated frame, is reparented to the frame
+        managed by L{AuiManager}.
+
+        :param `window`: the window to be un-managed.
+        """
+        
+        for p in self._panes:
+            if p.window == window:
+                if p.frame:
+                    # we have a floating frame which is being detached. We need to
+                    # reparent it to self._frame and destroy the floating frame
+
+                    # reduce flicker
+                    p.window.SetSize((1, 1))
+                    if p.frame.IsShown():
+                        p.frame.Show(False)
+
+                    if self._action_window == p.frame:
+                        self._action_window = None
+                        
+                    # reparent to self._frame and destroy the pane
+                    p.window.Reparent(self._frame)
+                    p.frame.SetSizer(None)
+                    p.frame.Destroy()
+                    p.frame = None
+
+                elif p.IsNotebookPage():
+                    notebook = self._notebooks[p.notebook_id]
+                    id = notebook.GetPageIndex(p.window)
+                    notebook.RemovePage(id)
+                
+                # make sure there are no references to this pane in our uiparts,
+                # just in case the caller doesn't call Update() immediately after
+                # the DetachPane() call.  This prevets obscure crashes which would
+                # happen at window repaint if the caller forgets to call Update()
+                counter = 0
+                for pi in xrange(len(self._uiparts)):
+                    part = self._uiparts[counter]
+                    if part.pane == p:
+                        self._uiparts.pop(counter)
+                        counter -= 1
+
+                    counter += 1
+            
+                self._panes.remove(p)
+                return True
+        
+        return False
+
+
+    def ClosePane(self, pane_info):
+        """
+        Destroys or hides the pane depending on its flags.
+
+        :param `pane_info`: a L{AuiPaneInfo} instance.
+        """
+
+        # if we were maximized, restore
+        if pane_info.IsMaximized():
+            self.RestorePane(pane_info)
+
+        if pane_info.frame:
+            if self._agwFlags & AUI_MGR_ANIMATE_FRAMES:
+                pane_info.frame.FadeOut()
+
+        # first, hide the window
+        if pane_info.window and pane_info.window.IsShown():
+            pane_info.window.Show(False)
+
+        # make sure that we are the parent of this window
+        if pane_info.window and pane_info.window.GetParent() != self._frame:
+            pane_info.window.Reparent(self._frame)
+
+        # if we have a frame, destroy it
+        if pane_info.frame:
+            pane_info.frame.Destroy()
+            pane_info.frame = None
+            
+        elif pane_info.IsNotebookPage():
+            # if we are a notebook page, remove ourselves...
+            # the  code would index out of bounds 
+            # if the last page of a sub-notebook was closed
+            # because the notebook would be deleted, before this
+            # code is executed.
+            # This code just prevents an out-of bounds error.
+            if self._notebooks:
+                nid = pane_info.notebook_id
+                if nid >= 0 and nid < len(self._notebooks):
+                    notebook = self._notebooks[nid]
+                    page_idx = notebook.GetPageIndex(pane_info.window)
+                    if page_idx >= 0:
+                        notebook.RemovePage(page_idx)
+                                
+        # now we need to either destroy or hide the pane
+        to_destroy = 0
+        if pane_info.IsDestroyOnClose():
+            to_destroy = pane_info.window
+            self.DetachPane(to_destroy)
+        else:
+            if isinstance(pane_info.window, auibar.AuiToolBar) and pane_info.IsFloating():
+                tb = pane_info.window
+                if pane_info.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]:
+                    tb.SetAGWWindowStyleFlag(tb.GetAGWWindowStyleFlag() | AUI_TB_VERTICAL)
+                
+            pane_info.Dock().Hide()
+
+        if pane_info.IsNotebookControl():
+
+            notebook = self._notebooks[pane_info.notebook_id]
+            while notebook.GetPageCount():
+                window = notebook.GetPage(0)
+                notebook.RemovePage(0)
+                info = self.GetPane(window)
+                if info.IsOk():
+                    info.notebook_id = -1
+                    info.dock_direction = AUI_DOCK_NONE
+                    # Note: this could change our paneInfo reference ...
+                    self.ClosePane(info)
+
+        if to_destroy:
+            to_destroy.Destroy()
+
+
+    def MaximizePane(self, pane_info, savesizes=True):
+        """
+        Maximizes the input pane.
+
+        :param `pane_info`: a L{AuiPaneInfo} instance.
+        :param `savesizes`: whether to save previous dock sizes.
+        """
+
+        if savesizes:
+            self.SavePreviousDockSizes(pane_info)
+                
+        for p in self._panes:
+            
+            # save hidden state
+            p.SetFlag(p.savedHiddenState, p.HasFlag(p.optionHidden))
+
+            if not p.IsToolbar() and not p.IsFloating():
+                p.Restore()
+        
+                # hide the pane, because only the newly
+                # maximized pane should show
+                p.Hide()
+
+        pane_info.previousDockPos = pane_info.dock_pos
+
+        # mark ourselves maximized
+        pane_info.Maximize()
+        pane_info.Show()
+        self._has_maximized = True
+
+        # last, show the window
+        if pane_info.window and not pane_info.window.IsShown():
+            pane_info.window.Show(True)
+
+            
+    def SavePreviousDockSizes(self, pane_info):
+        """
+        Stores the previous dock sizes, to be used in a "restore" action later.
+
+        :param `pane_info`: a L{AuiPaneInfo} instance.
+        """
+
+        for d in self._docks:
+            if not d.toolbar:
+                for p in d.panes:
+                    p.previousDockSize = d.size
+                    if pane_info is not p:
+                        p.SetFlag(p.needsRestore, True)
+
+        
+    def RestorePane(self, pane_info):
+        """
+        Restores the input pane from a previous maximized or minimized state.
+
+        :param `pane_info`: a L{AuiPaneInfo} instance.
+        """
+        
+        # restore all the panes
+        for p in self._panes:
+            if not p.IsToolbar():
+                p.SetFlag(p.optionHidden, p.HasFlag(p.savedHiddenState))
+
+        pane_info.SetFlag(pane_info.needsRestore, True)
+
+        # mark ourselves non-maximized
+        pane_info.Restore()
+        self._has_maximized = False
+        self._has_minimized = False
+
+        # last, show the window
+        if pane_info.window and not pane_info.window.IsShown():
+            pane_info.window.Show(True)
+
+
+    def RestoreMaximizedPane(self):
+        """ Restores the current maximized pane (if any). """
+        
+        # restore all the panes
+        for p in self._panes:
+            if p.IsMaximized():
+                self.RestorePane(p)
+                break
+
+
+    def ActivatePane(self, window):
+        """
+        Activates the pane to which `window` is associated.
+
+        :param `window`: a `wx.Window` derived window.
+        """
+
+        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
+            while window:
+                ret, self._panes = SetActivePane(self._panes, window)
+                if ret:
+                    break
+
+                window = window.GetParent()
+
+            self.RefreshCaptions()
+            self.FireEvent(wxEVT_AUI_PANE_ACTIVATED, window, canVeto=False)
+            
+
+    def CreateNotebook(self):
+        """
+        Creates an automatic L{AuiNotebook} when a pane is docked on
+        top of another pane.
+        """
+
+        notebook = auibook.AuiNotebook(self._frame, -1, wx.Point(0, 0), wx.Size(0, 0), agwStyle=self._autoNBStyle)
+
+        # This is so we can get the tab-drag event.
+        notebook.GetAuiManager().SetMasterManager(self)
+        notebook.SetArtProvider(self._autoNBTabArt.Clone())
+        self._notebooks.append(notebook)
+
+        return notebook
+
+
+    def SetAutoNotebookTabArt(self, art):
+        """
+        Sets the default tab art provider for automatic notebooks.
+
+        :param `art`: a tab art provider.
+        """
+
+        for nb in self._notebooks:
+            nb.SetArtProvider(art.Clone())
+            nb.Refresh()
+            nb.Update()
+
+        self._autoNBTabArt = art
+
+
+    def GetAutoNotebookTabArt(self):
+        """ Returns the default tab art provider for automatic notebooks. """
+
+        return self._autoNBTabArt        
+        
+
+    def SetAutoNotebookStyle(self, agwStyle):
+        """
+        Sets the default AGW-specific window style for automatic notebooks.
+
+        :param `agwStyle`: the underlying L{AuiNotebook} 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 {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)
+         ==================================== ==================================
+
+        """
+
+        for nb in self._notebooks:
+            nb.SetAGWWindowStyleFlag(agwStyle)
+            nb.Refresh()
+            nb.Update()
+
+        self._autoNBStyle = agwStyle
+
+
+    def GetAutoNotebookStyle(self):
+        """
+        Returns the default AGW-specific window style for automatic notebooks.
+
+        :see: L{SetAutoNotebookStyle} method for a list of possible styles.
+        """
+
+        return self._autoNBStyle
+
+
+    def SavePaneInfo(self, pane):
+        """
+        This method is similar to L{SavePerspective}, with the exception
+        that it only saves information about a single pane. It is used in
+        combination with L{LoadPaneInfo}.
+
+        :param `pane`: a L{AuiPaneInfo} instance to save.        
+        """
+
+        result = "name=" + EscapeDelimiters(pane.name) + ";"
+        result += "caption=" + EscapeDelimiters(pane.caption) + ";"
+
+        result += "state=%u;"%pane.state
+        result += "dir=%d;"%pane.dock_direction
+        result += "layer=%d;"%pane.dock_layer
+        result += "row=%d;"%pane.dock_row
+        result += "pos=%d;"%pane.dock_pos
+        result += "prop=%d;"%pane.dock_proportion
+        result += "bestw=%d;"%pane.best_size.x
+        result += "besth=%d;"%pane.best_size.y
+        result += "minw=%d;"%pane.min_size.x
+        result += "minh=%d;"%pane.min_size.y
+        result += "maxw=%d;"%pane.max_size.x
+        result += "maxh=%d;"%pane.max_size.y
+        result += "floatx=%d;"%pane.floating_pos.x
+        result += "floaty=%d;"%pane.floating_pos.y
+        result += "floatw=%d;"%pane.floating_size.x
+        result += "floath=%d;"%pane.floating_size.y
+        result += "notebookid=%d;"%pane.notebook_id
+        result += "transparent=%d"%pane.transparent
+
+        return result
+
+
+    def LoadPaneInfo(self, pane_part, pane):
+        """
+        This method is similar to to L{LoadPerspective}, with the exception that
+        it only loads information about a single pane. It is used in combination
+        with L{SavePaneInfo}.
+
+        :param `pane_part`: the string to analyze;
+        :param `pane`: the L{AuiPaneInfo} structure in which to load `pane_part`.
+        """
+
+        # replace escaped characters so we can
+        # split up the string easily
+        pane_part = pane_part.replace("\\|", "\a")
+        pane_part = pane_part.replace("\\;", "\b")
+
+        options = pane_part.split(";")
+        for items in options:
+
+            val_name, value = items.split("=")
+            val_name = val_name.strip()
+
+            if val_name == "name":
+                pane.name = value
+            elif val_name == "caption":
+                pane.caption = value
+            elif val_name == "state":
+                pane.state = int(value)
+            elif val_name == "dir":
+                pane.dock_direction = int(value)
+            elif val_name == "layer":
+                pane.dock_layer = int(value)
+            elif val_name == "row":
+                pane.dock_row = int(value)
+            elif val_name == "pos":
+                pane.dock_pos = int(value)
+            elif val_name == "prop":
+                pane.dock_proportion = int(value)
+            elif val_name == "bestw":
+                pane.best_size.x = int(value)
+            elif val_name == "besth":
+                pane.best_size.y = int(value)
+                pane.best_size = wx.Size(pane.best_size.x, pane.best_size.y)
+            elif val_name == "minw":
+                pane.min_size.x = int(value)
+            elif val_name == "minh":
+                pane.min_size.y = int(value)
+                pane.min_size = wx.Size(pane.min_size.x, pane.min_size.y)
+            elif val_name == "maxw":
+                pane.max_size.x = int(value)
+            elif val_name == "maxh":
+                pane.max_size.y = int(value)
+                pane.max_size = wx.Size(pane.max_size.x, pane.max_size.y)
+            elif val_name == "floatx":
+                pane.floating_pos.x = int(value)
+            elif val_name == "floaty":
+                pane.floating_pos.y = int(value)
+                pane.floating_pos = wx.Point(pane.floating_pos.x, pane.floating_pos.y)
+            elif val_name == "floatw":
+                pane.floating_size.x = int(value)
+            elif val_name == "floath":
+                pane.floating_size.y = int(value)
+                pane.floating_size = wx.Size(pane.floating_size.x, pane.floating_size.y)
+            elif val_name == "notebookid":
+                pane.notebook_id = int(value)
+            elif val_name == "transparent":
+                pane.transparent = int(value)
+            else:
+                raise Exception("Bad perspective string")
+
+        # replace escaped characters so we can
+        # split up the string easily
+        pane.name = pane.name.replace("\a", "|")
+        pane.name = pane.name.replace("\b", ";")
+        pane.caption = pane.caption.replace("\a", "|")
+        pane.caption = pane.caption.replace("\b", ";")
+        pane_part = pane_part.replace("\a", "|")
+        pane_part = pane_part.replace("\b", ";")
+
+        return pane
+    
+
+    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.
+        """
+
+        result = "layout2|"
+
+        for pane in self._panes:
+            result += self.SavePaneInfo(pane) + "|"
+        
+        for dock in self._docks:
+            result = result + ("dock_size(%d,%d,%d)=%d|")%(dock.dock_direction,
+                                                           dock.dock_layer,
+                                                           dock.dock_row,
+                                                           dock.size)
+        return result
+
+
+    def LoadPerspective(self, layout, update=True):
+        """
+        Loads a layout which was saved with L{SavePerspective}.
+        
+        If the `update` flag parameter is ``True``, L{Update} will be
+        automatically invoked, thus realizing the saved perspective on screen.
+
+        :param `layout`: a string which contains a saved AUI layout;
+        :param `update`: whether to update immediately the window or not.
+        """
+
+        input = layout
+
+        # check layout string version
+        #    'layout1' = wxAUI 0.9.0 - wxAUI 0.9.2
+        #    'layout2' = wxAUI 0.9.2 (wxWidgets 2.8)
+        index = input.find("|")
+        part = input[0:index].strip()
+        input = input[index+1:]
+        
+        if part != "layout2":
+            return False
+
+        # mark all panes currently managed as docked and hidden
+        for pane in self._panes:
+            pane.Dock().Hide()
+
+        # clear out the dock array; this will be reconstructed
+        self._docks = []
+
+        # replace escaped characters so we can
+        # split up the string easily
+        input = input.replace("\\|", "\a")
+        input = input.replace("\\;", "\b")
+
+        while 1:
+
+            pane = AuiPaneInfo()
+            index = input.find("|")
+            pane_part = input[0:index].strip()
+            input = input[index+1:]
+
+            # if the string is empty, we're done parsing
+            if pane_part == "":
+                break
+
+            if pane_part[0:9] == "dock_size":
+                index = pane_part.find("=")
+                val_name = pane_part[0:index]
+                value = pane_part[index+1:]
+
+                index = val_name.find("(")
+                piece = val_name[index+1:]
+                index = piece.find(")")
+                piece = piece[0:index]
+
+                vals = piece.split(",")
+                dir = int(vals[0])
+                layer = int(vals[1])
+                row = int(vals[2])
+                size = int(value)
+                
+                dock = AuiDockInfo()
+                dock.dock_direction = dir
+                dock.dock_layer = layer
+                dock.dock_row = row
+                dock.size = size
+                self._docks.append(dock)
+                
+                continue
+
+            # Undo our escaping as LoadPaneInfo needs to take an unescaped
+            # name so it can be called by external callers
+            pane_part = pane_part.replace("\a", "|")
+            pane_part = pane_part.replace("\b", ";")
+
+            pane = self.LoadPaneInfo(pane_part, pane)
+
+            p = self.GetPane(pane.name)
+                
+            if not p.IsOk():
+                if pane.IsNotebookControl():
+                    # notebook controls - auto add...
+                    self._panes.append(pane)
+                    indx = self._panes.index(pane)
+                else:
+                    # the pane window couldn't be found
+                    # in the existing layout -- skip it
+                    continue
+
+            else:
+                indx = self._panes.index(p)
+            pane.window = p.window
+            pane.frame = p.frame
+            pane.buttons = p.buttons
+            self._panes[indx] = pane
+
+            if isinstance(pane.window, auibar.AuiToolBar) and (pane.IsFloatable() or pane.IsDockable()):
+                pane.window.SetGripperVisible(True)
+            
+        if update:
+            self.Update()
+
+        return True
+
+
+    def GetPanePositionsAndSizes(self, dock):
+        """
+        Returns all the panes positions and sizes in a dock.
+
+        :param `dock`: a L{AuiDockInfo} instance.
+        """
+        
+        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
+        gripper_size = self._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
+
+        positions = []
+        sizes = []
+
+        action_pane = -1
+        pane_count = len(dock.panes)
+
+        # find the pane marked as our action pane
+        for pane_i in xrange(pane_count):
+            pane = dock.panes[pane_i]
+            if pane.HasFlag(AuiPaneInfo.actionPane):
+                if action_pane != -1:
+                    raise Exception("Too many action panes!")
+                action_pane = pane_i
+            
+        # set up each panes default position, and
+        # determine the size (width or height, depending
+        # on the dock's orientation) of each pane
+        for pane in dock.panes:
+            positions.append(pane.dock_pos)
+            size = 0
+            
+            if pane.HasBorder():
+                size += pane_border_size*2
+                    
+            if dock.IsHorizontal():
+                if pane.HasGripper() and not pane.HasGripperTop():
+                    size += gripper_size
+
+                if pane.HasCaptionLeft():
+                    size += caption_size
+                    
+                size += pane.best_size.x
+                 
+            else:
+                if pane.HasGripper() and pane.HasGripperTop():
+                    size += gripper_size
+
+                if pane.HasCaption() and not pane.HasCaptionLeft():
+                    size += caption_size
+                    
+                size += pane.best_size.y
+       
+            sizes.append(size)
+
+        # if there is no action pane, just return the default
+        # positions (as specified in pane.pane_pos)
+        if action_pane == -1:
+            return positions, sizes
+
+        offset = 0
+        for pane_i in xrange(action_pane-1, -1, -1):
+            amount = positions[pane_i+1] - (positions[pane_i] + sizes[pane_i])
+            if amount >= 0:
+                offset += amount
+            else:
+                positions[pane_i] -= -amount
+
+            offset += sizes[pane_i]
+        
+        # if the dock mode is fixed, make sure none of the panes
+        # overlap we will bump panes that overlap
+        offset = 0
+        for pane_i in xrange(action_pane, pane_count):
+            amount = positions[pane_i] - offset
+            if amount >= 0:
+                offset += amount
+            else:
+                positions[pane_i] += -amount
+
+            offset += sizes[pane_i]
+
+        return positions, sizes
+    
+
+    def LayoutAddPane(self, cont, dock, pane, uiparts, spacer_only):
+        """
+        Adds a pane into the existing layout (in an existing dock).
+
+        :param `cont`: a `wx.Sizer` object;
+        :param `dock`: the L{AuiDockInfo} structure in which to add the pane;
+        :param `pane`: the L{AuiPaneInfo} instance to add to the dock;
+        :param `uiparts`: a list of UI parts in the interface;
+        :param `spacer_only`: whether to add a simple spacer or a real window.
+        """
+        
+        sizer_item = wx.SizerItem()
+        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        gripper_size = self._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
+        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
+        pane_button_size = self._art.GetMetric(AUI_DOCKART_PANE_BUTTON_SIZE)
+
+        # find out the orientation of the item (orientation for panes
+        # is the same as the dock's orientation)
+
+        if dock.IsHorizontal():
+            orientation = wx.HORIZONTAL
+        else:
+            orientation = wx.VERTICAL
+
+        # this variable will store the proportion
+        # value that the pane will receive
+        pane_proportion = pane.dock_proportion
+
+        horz_pane_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        vert_pane_sizer = wx.BoxSizer(wx.VERTICAL)
+
+        if pane.HasGripper():
+            
+            part = AuiDockUIPart()
+            if pane.HasGripperTop():
+                sizer_item = vert_pane_sizer.Add((1, gripper_size), 0, wx.EXPAND)
+            else:
+                sizer_item = horz_pane_sizer.Add((gripper_size, 1), 0, wx.EXPAND)
+
+            part.type = AuiDockUIPart.typeGripper
+            part.dock = dock
+            part.pane = pane
+            part.button = None
+            part.orientation = orientation
+            part.cont_sizer = horz_pane_sizer
+            part.sizer_item = sizer_item
+            uiparts.append(part)
+
+        button_count = len(pane.buttons)
+        button_width_total = button_count*pane_button_size
+        if button_count >= 1:
+            button_width_total += 3
+
+        caption, captionLeft = pane.HasCaption(), pane.HasCaptionLeft()
+        button_count = len(pane.buttons)
+
+        if captionLeft:
+            caption_sizer = wx.BoxSizer(wx.VERTICAL)
+
+            # add pane buttons to the caption
+            dummy_parts = []
+            for btn_id in xrange(len(pane.buttons)-1, -1, -1):
+                sizer_item = caption_sizer.Add((caption_size, pane_button_size), 0, wx.EXPAND)
+                part = AuiDockUIPart()
+                part.type = AuiDockUIPart.typePaneButton
+                part.dock = dock
+                part.pane = pane
+                part.button = pane.buttons[btn_id]
+                part.orientation = orientation
+                part.cont_sizer = caption_sizer
+                part.sizer_item = sizer_item
+                dummy_parts.append(part)
+            
+            sizer_item = caption_sizer.Add((caption_size, 1), 1, wx.EXPAND)
+            vert_pane_sizer = wx.BoxSizer(wx.HORIZONTAL)
+
+            # create the caption sizer
+            part = AuiDockUIPart()
+
+            part.type = AuiDockUIPart.typeCaption
+            part.dock = dock
+            part.pane = pane
+            part.button = None
+            part.orientation = orientation
+            part.cont_sizer = vert_pane_sizer
+            part.sizer_item = sizer_item
+            caption_part_idx = len(uiparts)
+            uiparts.append(part)
+            uiparts.extend(dummy_parts)
+
+        elif caption:
+
+            caption_sizer = wx.BoxSizer(wx.HORIZONTAL)
+            sizer_item = caption_sizer.Add((1, caption_size), 1, wx.EXPAND)
+
+            # create the caption sizer
+            part = AuiDockUIPart()
+
+            part.type = AuiDockUIPart.typeCaption
+            part.dock = dock
+            part.pane = pane
+            part.button = None
+            part.orientation = orientation
+            part.cont_sizer = vert_pane_sizer
+            part.sizer_item = sizer_item
+            caption_part_idx = len(uiparts)
+            uiparts.append(part)
+
+            # add pane buttons to the caption
+            for button in pane.buttons:
+                sizer_item = caption_sizer.Add((pane_button_size, caption_size), 0, wx.EXPAND)                        
+                part = AuiDockUIPart()
+                part.type = AuiDockUIPart.typePaneButton
+                part.dock = dock
+                part.pane = pane
+                part.button = button
+                part.orientation = orientation
+                part.cont_sizer = caption_sizer
+                part.sizer_item = sizer_item
+                uiparts.append(part)
+
+        if caption or captionLeft:
+            # if we have buttons, add a little space to the right
+            # of them to ease visual crowding
+            if button_count >= 1:
+                if captionLeft:
+                    caption_sizer.Add((caption_size, 3), 0, wx.EXPAND)
+                else:
+                    caption_sizer.Add((3, caption_size), 0, wx.EXPAND)
+
+            # add the caption sizer
+            sizer_item = vert_pane_sizer.Add(caption_sizer, 0, wx.EXPAND)
+            uiparts[caption_part_idx].sizer_item = sizer_item
+                    
+        # add the pane window itself
+        if spacer_only or not pane.window:
+            sizer_item = vert_pane_sizer.Add((1, 1), 1, wx.EXPAND)
+        else:
+            sizer_item = vert_pane_sizer.Add(pane.window, 1, wx.EXPAND)
+            vert_pane_sizer.SetItemMinSize(pane.window, (1, 1))
+
+        part = AuiDockUIPart()        
+        part.type = AuiDockUIPart.typePane
+        part.dock = dock
+        part.pane = pane
+        part.button = None
+        part.orientation = orientation
+        part.cont_sizer = vert_pane_sizer
+        part.sizer_item = sizer_item
+        uiparts.append(part)
+
+        # determine if the pane should have a minimum size if the pane is
+        # non-resizable (fixed) then we must set a minimum size. Alternatively,
+        # if the pane.min_size is set, we must use that value as well
+        
+        min_size = pane.min_size
+        if pane.IsFixed():
+            if min_size == wx.Size(-1, -1):
+                min_size = pane.best_size
+                pane_proportion = 0
+
+        if min_size != wx.Size(-1, -1):
+            vert_pane_sizer.SetItemMinSize(len(vert_pane_sizer.GetChildren())-1, (min_size.x, min_size.y))
+        
+        # add the vertical/horizontal sizer (caption, pane window) to the
+        # horizontal sizer (gripper, vertical sizer)
+        horz_pane_sizer.Add(vert_pane_sizer, 1, wx.EXPAND)
+
+        # finally, add the pane sizer to the dock sizer
+        if pane.HasBorder():
+            # allowing space for the pane's border
+            sizer_item = cont.Add(horz_pane_sizer, pane_proportion,
+                                  wx.EXPAND | wx.ALL, pane_border_size)
+            part = AuiDockUIPart()
+            part.type = AuiDockUIPart.typePaneBorder
+            part.dock = dock
+            part.pane = pane
+            part.button = None
+            part.orientation = orientation
+            part.cont_sizer = cont
+            part.sizer_item = sizer_item
+            uiparts.append(part)
+        else:
+            sizer_item = cont.Add(horz_pane_sizer, pane_proportion, wx.EXPAND)
+        
+        return uiparts
+        
+        
+    def LayoutAddDock(self, cont, dock, uiparts, spacer_only):
+        """
+        Adds a dock into the existing layout.
+
+        :param `cont`: a `wx.Sizer` object;
+        :param `dock`: the L{AuiDockInfo} structure to add to the layout;
+        :param `uiparts`: a list of UI parts in the interface;
+        :param `spacer_only`: whether to add a simple spacer or a real window.
+        """
+        
+        sizer_item = wx.SizerItem()
+        part = AuiDockUIPart()
+
+        sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+        orientation = (dock.IsHorizontal() and [wx.HORIZONTAL] or [wx.VERTICAL])[0]
+
+        # resizable bottom and right docks have a sash before them
+        if not self._has_maximized and not dock.fixed and \
+           dock.dock_direction in [AUI_DOCK_BOTTOM, AUI_DOCK_RIGHT]:
+        
+            sizer_item = cont.Add((sash_size, sash_size), 0, wx.EXPAND)
+
+            part.type = AuiDockUIPart.typeDockSizer
+            part.orientation = orientation
+            part.dock = dock
+            part.pane = None
+            part.button = None
+            part.cont_sizer = cont
+            part.sizer_item = sizer_item
+            uiparts.append(part)
+        
+        # create the sizer for the dock
+        dock_sizer = wx.BoxSizer(orientation)
+
+        # add each pane to the dock
+        has_maximized_pane = False
+        pane_count = len(dock.panes)
+
+        if dock.fixed:
+        
+            # figure out the real pane positions we will
+            # use, without modifying the each pane's pane_pos member
+            pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock)
+
+            offset = 0
+            for pane_i in xrange(pane_count):
+            
+                pane = dock.panes[pane_i]
+                pane_pos = pane_positions[pane_i]
+
+                if pane.IsMaximized():
+                    has_maximized_pane = True
+
+                amount = pane_pos - offset
+                if amount > 0:
+                
+                    if dock.IsVertical():
+                        sizer_item = dock_sizer.Add((1, amount), 0, wx.EXPAND)
+                    else:
+                        sizer_item = dock_sizer.Add((amount, 1), 0, wx.EXPAND)
+
+                    part = AuiDockUIPart()
+                    part.type = AuiDockUIPart.typeBackground
+                    part.dock = dock
+                    part.pane = None
+                    part.button = None
+                    part.orientation = (orientation==wx.HORIZONTAL and \
+                                        [wx.VERTICAL] or [wx.HORIZONTAL])[0]
+                    part.cont_sizer = dock_sizer
+                    part.sizer_item = sizer_item
+                    uiparts.append(part)
+
+                    offset = offset + amount
+                
+                uiparts = self.LayoutAddPane(dock_sizer, dock, pane, uiparts, spacer_only)
+
+                offset = offset + pane_sizes[pane_i]
+            
+            # at the end add a very small stretchable background area
+            sizer_item = dock_sizer.Add((0, 0), 1, wx.EXPAND)
+            part = AuiDockUIPart()
+            part.type = AuiDockUIPart.typeBackground
+            part.dock = dock
+            part.pane = None
+            part.button = None
+            part.orientation = orientation
+            part.cont_sizer = dock_sizer
+            part.sizer_item = sizer_item
+            uiparts.append(part)
+        
+        else:
+        
+            for pane_i in xrange(pane_count):
+            
+                pane = dock.panes[pane_i]
+
+                if pane.IsMaximized():
+                    has_maximized_pane = True
+
+                # if this is not the first pane being added,
+                # we need to add a pane sizer
+                if not self._has_maximized and pane_i > 0:
+                    sizer_item = dock_sizer.Add((sash_size, sash_size), 0, wx.EXPAND)
+                    part = AuiDockUIPart()
+                    part.type = AuiDockUIPart.typePaneSizer
+                    part.dock = dock
+                    part.pane = dock.panes[pane_i-1]
+                    part.button = None
+                    part.orientation = (orientation==wx.HORIZONTAL and \
+                                        [wx.VERTICAL] or [wx.HORIZONTAL])[0]
+                    part.cont_sizer = dock_sizer
+                    part.sizer_item = sizer_item
+                    uiparts.append(part)
+                
+                uiparts = self.LayoutAddPane(dock_sizer, dock, pane, uiparts, spacer_only)
+            
+        if dock.dock_direction == AUI_DOCK_CENTER or has_maximized_pane:
+            sizer_item = cont.Add(dock_sizer, 1, wx.EXPAND)
+        else:
+            sizer_item = cont.Add(dock_sizer, 0, wx.EXPAND)
+
+        part = AuiDockUIPart()
+        part.type = AuiDockUIPart.typeDock
+        part.dock = dock
+        part.pane = None
+        part.button = None
+        part.orientation = orientation
+        part.cont_sizer = cont
+        part.sizer_item = sizer_item
+        uiparts.append(part)
+
+        if dock.IsHorizontal():
+            cont.SetItemMinSize(dock_sizer, (0, dock.size))
+        else:
+            cont.SetItemMinSize(dock_sizer, (dock.size, 0))
+
+        #  top and left docks have a sash after them
+        if not self._has_maximized and not dock.fixed and \
+           dock.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_LEFT]:
+        
+            sizer_item = cont.Add((sash_size, sash_size), 0, wx.EXPAND)
+
+            part = AuiDockUIPart()
+            part.type = AuiDockUIPart.typeDockSizer
+            part.dock = dock
+            part.pane = None
+            part.button = None
+            part.orientation = orientation
+            part.cont_sizer = cont
+            part.sizer_item = sizer_item
+            uiparts.append(part)
+        
+        return uiparts
+    
+
+    def LayoutAll(self, panes, docks, uiparts, spacer_only=False, oncheck=True):
+        """
+        Layouts all the UI structures in the interface.
+
+        :param `panes`: a list of L{AuiPaneInfo} instances;
+        :param `docks`: a list of L{AuiDockInfo} classes;
+        :param `uiparts`: a list of UI parts in the interface;
+        :param `spacer_only`: whether to add a simple spacer or a real window;
+        :param `oncheck`: whether to store the results in a class member or not.
+        """
+        
+        container = wx.BoxSizer(wx.VERTICAL)
+
+        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
+        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        cli_size = self._frame.GetClientSize()
+        
+        # empty all docks out
+        for dock in docks:
+            dock.panes = []
+            if dock.fixed:
+                # always reset fixed docks' sizes, because
+                # the contained windows may have been resized
+                dock.size = 0
+            
+        dock_count = len(docks)
+        
+        # iterate through all known panes, filing each
+        # of them into the appropriate dock. If the
+        # pane does not exist in the dock, add it
+        for p in panes:
+
+            # don't layout hidden panes.
+            if p.IsShown():
+                
+                # find any docks with the same dock direction, dock layer, and
+                # dock row as the pane we are working on
+                arr = FindDocks(docks, p.dock_direction, p.dock_layer, p.dock_row)
+
+                if arr:
+                    dock = arr[0]
+
+                else:
+                    # dock was not found, so we need to create a new one
+                    d = AuiDockInfo()
+                    d.dock_direction = p.dock_direction
+                    d.dock_layer = p.dock_layer
+                    d.dock_row = p.dock_row
+                    docks.append(d)
+                    dock = docks[-1]
+
+                    if p.HasFlag(p.needsRestore) and not p.HasFlag(p.wasMaximized):
+                   
+                        isHor = dock.IsHorizontal()
+                        sashSize = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+
+                        # get the sizes of any docks that might 
+                        # overlap with our restored dock
+
+                        # make list of widths or heights from the size in the dock rects
+                        sizes = [d.rect[2:][isHor] for \
+                                 d in docks if d.IsOk() and \
+                                 (d.IsHorizontal() == isHor) and \
+                                 not d.toolbar and \
+                                 d.dock_direction != AUI_DOCK_CENTER]
+                        
+                        frameRect = GetInternalFrameRect(self._frame, self._docks)
+
+                        # set max size allowing for sashes and absolute minimum
+                        maxsize = frameRect[2:][isHor] - sum(sizes) - (len(sizes)*10) - (sashSize*len(sizes))
+                        dock.size = min(p.previousDockSize,maxsize)
+
+                    else:
+                        dock.size = 0
+
+                if p.HasFlag(p.wasMaximized):
+                    self.MaximizePane(p, savesizes=False)
+                    p.SetFlag(p.wasMaximized, False)
+
+                if p.HasFlag(p.needsRestore):
+                    if p.previousDockPos is not None:
+                        DoInsertPane(dock.panes, dock.dock_direction, dock.dock_layer, dock.dock_row, p.previousDockPos)
+                        p.dock_pos = p.previousDockPos
+                        p.previousDockPos = None
+                    p.SetFlag(p.needsRestore, False)
+
+                if p.IsDocked():
+                    # remove the pane from any existing docks except this one
+                    docks = RemovePaneFromDocks(docks, p, dock)
+
+                    # pane needs to be added to the dock,
+                    # if it doesn't already exist 
+                    if not FindPaneInDock(dock, p.window):
+                        dock.panes.append(p)
+                else:
+                    # remove the pane from any existing docks
+                    docks = RemovePaneFromDocks(docks, p)
+                
+        # remove any empty docks
+        docks = [dock for dock in docks if dock.panes]
+
+        dock_count = len(docks)
+        # configure the docks further
+        for ii, dock in enumerate(docks):
+            # sort the dock pane array by the pane's
+            # dock position (dock_pos), in ascending order
+            dock.panes.sort(PaneSortFunc)
+            dock_pane_count = len(dock.panes)
+            
+            # for newly created docks, set up their initial size
+            if dock.size == 0:
+                size = 0
+                for pane in dock.panes:
+                    pane_size = pane.best_size
+                    if pane_size == wx.Size(-1, -1):
+                        pane_size = pane.min_size
+                    if pane_size == wx.Size(-1, -1) and pane.window:
+                        pane_size = pane.window.GetSize()
+                    if dock.IsHorizontal():
+                        size = max(pane_size.y, size)
+                    else:
+                        size = max(pane_size.x, size)
+                
+                # add space for the border (two times), but only
+                # if at least one pane inside the dock has a pane border
+                for pane in dock.panes:
+                    if pane.HasBorder():
+                        size = size + pane_border_size*2
+                        break
+                    
+                # if pane is on the top or bottom, add the caption height,
+                # but only if at least one pane inside the dock has a caption
+                if dock.IsHorizontal():
+                    for pane in dock.panes:
+                        if pane.HasCaption() and not pane.HasCaptionLeft():
+                            size = size + caption_size
+                            break
+                else:
+                    for pane in dock.panes:
+                        if pane.HasCaptionLeft() and not pane.HasCaption():
+                            size = size + caption_size
+                            break
+                    
+                # new dock's size may not be more than the dock constraint
+                # parameter specifies.  See SetDockSizeConstraint()
+                max_dock_x_size = int(self._dock_constraint_x*float(cli_size.x))
+                max_dock_y_size = int(self._dock_constraint_y*float(cli_size.y))
+                if cli_size <= wx.Size(20, 20):
+                    max_dock_x_size = 10000
+                    max_dock_y_size = 10000
+
+                if dock.IsHorizontal():
+                    size = min(size, max_dock_y_size)
+                else:
+                    size = min(size, max_dock_x_size)
+
+                # absolute minimum size for a dock is 10 pixels
+                if size < 10:
+                    size = 10
+
+                dock.size = size
+
+            # determine the dock's minimum size
+            plus_border = False
+            plus_caption = False
+            plus_caption_left = False
+            dock_min_size = 0
+            for pane in dock.panes:
+                if pane.min_size != wx.Size(-1, -1):
+                    if pane.HasBorder():
+                        plus_border = True
+                    if pane.HasCaption():
+                        plus_caption = True
+                    if pane.HasCaptionLeft():
+                        plus_caption_left = True
+                    if dock.IsHorizontal():
+                        if pane.min_size.y > dock_min_size:
+                            dock_min_size = pane.min_size.y
+                    else:
+                        if pane.min_size.x > dock_min_size:
+                            dock_min_size = pane.min_size.x
+                    
+            if plus_border:
+                dock_min_size += pane_border_size*2
+            if plus_caption and dock.IsHorizontal():
+                dock_min_size += caption_size
+            if plus_caption_left and dock.IsVertical():
+                dock_min_size += caption_size
+               
+            dock.min_size = dock_min_size
+            
+            # if the pane's current size is less than it's
+            # minimum, increase the dock's size to it's minimum
+            if dock.size < dock.min_size:
+                dock.size = dock.min_size
+
+            # determine the dock's mode (fixed or proportional)
+            # determine whether the dock has only toolbars
+            action_pane_marked = False
+            dock.fixed = True
+            dock.toolbar = True
+            for pane in dock.panes:
+                if not pane.IsFixed():
+                    dock.fixed = False
+                if not pane.IsToolbar():
+                    dock.toolbar = False
+                if pane.HasFlag(AuiPaneInfo.optionDockFixed):
+                    dock.fixed = True
+                if pane.HasFlag(AuiPaneInfo.actionPane):
+                    action_pane_marked = True
+
+            # if the dock mode is proportional and not fixed-pixel,
+            # reassign the dock_pos to the sequential 0, 1, 2, 3
+            # e.g. remove gaps like 1, 2, 30, 500
+            if not dock.fixed:
+                for jj in xrange(dock_pane_count):
+                    pane = dock.panes[jj]
+                    pane.dock_pos = jj
+                
+            # if the dock mode is fixed, and none of the panes
+            # are being moved right now, make sure the panes
+            # do not overlap each other.  If they do, we will
+            # adjust the panes' positions
+            if dock.fixed and not action_pane_marked:
+                pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock)
+                offset = 0
+                for jj in xrange(dock_pane_count):
+                    pane = dock.panes[jj]
+                    pane.dock_pos = pane_positions[jj]
+                    amount = pane.dock_pos - offset
+                    if amount >= 0:
+                        offset += amount
+                    else:
+                        pane.dock_pos += -amount
+
+                    offset += pane_sizes[jj]
+                    dock.panes[jj] = pane
+
+            if oncheck:
+                self._docks[ii] = dock                    
+
+        # shrink docks if needed 
+##        docks = self.SmartShrink(docks, AUI_DOCK_TOP)
+##        docks = self.SmartShrink(docks, AUI_DOCK_LEFT)
+
+        if oncheck:
+            self._docks = docks
+            
+        # discover the maximum dock layer
+        max_layer = 0
+        dock_count = len(docks)
+        
+        for ii in xrange(dock_count):
+            max_layer = max(max_layer, docks[ii].dock_layer)
+
+        # clear out uiparts
+        uiparts = []
+
+        # create a bunch of box sizers,
+        # from the innermost level outwards.
+        cont = None
+        middle = None
+
+        if oncheck:
+            docks = self._docks
+        
+        for layer in xrange(max_layer+1):
+            # find any docks in this layer
+            arr = FindDocks(docks, -1, layer, -1)
+            # if there aren't any, skip to the next layer
+            if not arr:
+                continue
+
+            old_cont = cont
+
+            # create a container which will hold this layer's
+            # docks (top, bottom, left, right)
+            cont = wx.BoxSizer(wx.VERTICAL)
+
+            # find any top docks in this layer
+            arr = FindDocks(docks, AUI_DOCK_TOP, layer, -1)
+            for row in arr:
+                uiparts = self.LayoutAddDock(cont, row, uiparts, spacer_only)
+            
+            # fill out the middle layer (which consists
+            # of left docks, content area and right docks)
+            
+            middle = wx.BoxSizer(wx.HORIZONTAL)
+
+            # find any left docks in this layer
+            arr = FindDocks(docks, AUI_DOCK_LEFT, layer, -1)
+            for row in arr:
+                uiparts = self.LayoutAddDock(middle, row, uiparts, spacer_only)
+            
+            # add content dock (or previous layer's sizer
+            # to the middle
+            if not old_cont:
+                # find any center docks
+                arr = FindDocks(docks, AUI_DOCK_CENTER, -1, -1)
+                if arr:
+                    for row in arr:
+                       uiparts = self.LayoutAddDock(middle, row, uiparts, spacer_only)
+                       
+                elif not self._has_maximized:
+                    # there are no center docks, add a background area
+                    sizer_item = middle.Add((1, 1), 1, wx.EXPAND)
+                    part = AuiDockUIPart()
+                    part.type = AuiDockUIPart.typeBackground
+                    part.pane = None
+                    part.dock = None
+                    part.button = None
+                    part.cont_sizer = middle
+                    part.sizer_item = sizer_item
+                    uiparts.append(part)
+            else:
+                middle.Add(old_cont, 1, wx.EXPAND)
+            
+            # find any right docks in this layer
+            arr = FindDocks(docks, AUI_DOCK_RIGHT, layer, -1, reverse=True)
+            for row in arr:
+                uiparts = self.LayoutAddDock(middle, row, uiparts, spacer_only)
+                    
+            if len(middle.GetChildren()) > 0:
+                cont.Add(middle, 1, wx.EXPAND)
+
+            # find any bottom docks in this layer
+            arr = FindDocks(docks, AUI_DOCK_BOTTOM, layer, -1, reverse=True)
+            for row in arr:
+                    uiparts = self.LayoutAddDock(cont, row, uiparts, spacer_only)
+
+        if not cont:
+            # no sizer available, because there are no docks,
+            # therefore we will create a simple background area
+            cont = wx.BoxSizer(wx.VERTICAL)
+            sizer_item = cont.Add((1, 1), 1, wx.EXPAND)
+            part = AuiDockUIPart()
+            part.type = AuiDockUIPart.typeBackground
+            part.pane = None
+            part.dock = None
+            part.button = None
+            part.cont_sizer = middle
+            part.sizer_item = sizer_item
+            uiparts.append(part)
+
+        if oncheck:
+            self._uiparts = uiparts
+            self._docks = docks
+
+        container.Add(cont, 1, wx.EXPAND)
+
+        if oncheck:
+            return container
+        else:
+            return container, panes, docks, uiparts
+
+
+    def SetDockSizeConstraint(self, width_pct, height_pct):
+        """
+        When a user creates a new dock by dragging a window into a docked position,
+        often times the large size of the window will create a dock that is unwieldly
+        large.
+
+        L{AuiManager} by default limits the size of any new dock to 1/3 of the window
+        size. For horizontal docks, this would be 1/3 of the window height. For vertical
+        docks, 1/3 of the width. Calling this function will adjust this constraint value.
+
+        The numbers must be between 0.0 and 1.0. For instance, calling L{SetDockSizeConstraint}
+        with (0.5, 0.5) will cause new docks to be limited to half of the size of the entire
+        managed window.
+
+        :param `width_pct`: a float number representing the x dock size constraint;
+        :param `width_pct`: a float number representing the y dock size constraint.
+        """
+
+        self._dock_constraint_x = max(0.0, min(1.0, width_pct))
+        self._dock_constraint_y = max(0.0, min(1.0, height_pct))
+
+
+    def GetDockSizeConstraint(self):
+        """
+        Returns the current dock constraint values.
+
+        :see: L{SetDockSizeConstraint}
+        """
+
+        return self._dock_constraint_x, self._dock_constraint_y
+
+
+    def Update(self):
+        """
+        This method is called after any number of changes are made to any of the
+        managed panes. L{Update} must be invoked after L{AddPane} or L{InsertPane} are
+        called in order to "realize" or "commit" the changes.
+
+        In addition, any number of changes may be made to L{AuiPaneInfo} structures
+        (retrieved with L{GetPane}), but to realize the changes, L{Update}
+        must be called. This construction allows pane flicker to be avoided by updating
+        the whole layout at one time.
+        """
+
+        self._hover_button = None
+        self._action_part = None
+        
+        # destroy floating panes which have been
+        # redocked or are becoming non-floating
+        for p in self._panes:
+            if p.IsFloating() or not p.frame:
+                continue
+            
+            # because the pane is no longer in a floating, we need to
+            # reparent it to self._frame and destroy the floating frame
+            # reduce flicker
+            p.window.SetSize((1, 1))
+
+            # the following block is a workaround for bug #1531361
+            # (see wxWidgets sourceforge page).  On wxGTK (only), when
+            # a frame is shown/hidden, a move event unfortunately
+            # also gets fired.  Because we may be dragging around
+            # a pane, we need to cancel that action here to prevent
+            # a spurious crash.
+            if self._action_window == p.frame:
+                if self._frame.HasCapture():
+                    self._frame.ReleaseMouse()
+                self._action = actionNone
+                self._action_window = None
+
+            # hide the frame
+            if p.frame.IsShown():
+                p.frame.Show(False)
+
+            if self._action_window == p.frame:
+                self._action_window = None
+        
+            # reparent to self._frame and destroy the pane
+            p.window.Reparent(self._frame)
+            if isinstance(p.window, auibar.AuiToolBar):
+                p.window.SetAuiManager(self)
+
+            if p.frame:
+                p.frame.SetSizer(None)
+                p.frame.Destroy()
+            p.frame = None
+
+        # Only the master manager should create/destroy notebooks...
+        if not self._masterManager:
+            self.UpdateNotebook()
+        
+        # delete old sizer first
+        self._frame.SetSizer(None)
+
+        # create a layout for all of the panes
+        sizer = self.LayoutAll(self._panes, self._docks, self._uiparts, False)
+
+        # hide or show panes as necessary,
+        # and float panes as necessary
+        
+        pane_count = len(self._panes)
+        
+        for ii in xrange(pane_count):
+            p = self._panes[ii]
+            pFrame = p.frame
+
+            if p.IsFloating():
+                if pFrame is None:
+                    # we need to create a frame for this
+                    # pane, which has recently been floated
+                    frame = self.CreateFloatingFrame(self._frame, p)
+
+                    # on MSW and Mac, if the owner desires transparent dragging, and
+                    # the dragging is happening right now, then the floating
+                    # window should have this style by default
+                    if self._action in [actionDragFloatingPane, actionDragToolbarPane] and \
+                       self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
+                        frame.SetTransparent(150)
+
+                    if p.IsToolbar():
+                        bar = p.window
+                        if isinstance(bar, auibar.AuiToolBar):
+                            bar.SetGripperVisible(False)
+                            agwStyle = bar.GetAGWWindowStyleFlag()
+                            bar.SetAGWWindowStyleFlag(agwStyle & ~AUI_TB_VERTICAL)
+                            bar.Realize()
+
+                        s = p.window.GetMinSize()
+                        p.BestSize(s)
+                        p.FloatingSize(wx.DefaultSize)
+
+                    frame.SetPaneWindow(p)
+                    p.needsTransparency = True
+                    p.frame = pFrame = frame
+                    if p.IsShown() and not frame.IsShown():
+                        frame.Show()
+                        frame.Update()
+                else:
+
+                    # frame already exists, make sure it's position
+                    # and size reflect the information in AuiPaneInfo
+                    if pFrame.GetPosition() != p.floating_pos or pFrame.GetSize() != p.floating_size:
+                        pFrame.SetDimensions(p.floating_pos.x, p.floating_pos.y,
+                                             p.floating_size.x, p.floating_size.y, wx.SIZE_USE_EXISTING)
+
+                    # update whether the pane is resizable or not
+                    style = p.frame.GetWindowStyleFlag()
+                    if p.IsFixed():
+                        style &= ~wx.RESIZE_BORDER
+                    else:
+                        style |= wx.RESIZE_BORDER
+
+                    p.frame.SetWindowStyleFlag(style)
+
+                    if pFrame.IsShown() != p.IsShown():
+                        p.needsTransparency = True
+                        pFrame.Show(p.IsShown())
+
+                if pFrame.GetTitle() != p.caption:
+                    pFrame.SetTitle(p.caption)
+                if p.icon.IsOk():
+                    pFrame.SetIcon(wx.IconFromBitmap(p.icon))
+                    
+            else:
+
+                if p.IsToolbar():
+#                    self.SwitchToolBarOrientation(p)
+                    p.best_size = p.window.GetBestSize()
+
+                if p.window and not p.IsNotebookPage() and p.window.IsShown() != p.IsShown():
+                    p.window.Show(p.IsShown())
+
+            if pFrame and p.needsTransparency:
+                if pFrame.IsShown() and pFrame._transparent != p.transparent:
+                    pFrame.SetTransparent(p.transparent)
+                    pFrame._transparent = p.transparent
+                    
+                p.needsTransparency = False
+
+            # if "active panes" are no longer allowed, clear
+            # any optionActive values from the pane states
+            if self._agwFlags & AUI_MGR_ALLOW_ACTIVE_PANE == 0:
+                p.state &= ~AuiPaneInfo.optionActive
+
+            self._panes[ii] = p
+
+        old_pane_rects = []
+        pane_count = len(self._panes)
+        
+        for p in self._panes:
+            r = wx.Rect()
+            if p.window and p.IsShown() and p.IsDocked():
+                r = p.rect
+
+            old_pane_rects.append(r)    
+            
+        # apply the new sizer
+        self._frame.SetSizer(sizer)
+        self._frame.SetAutoLayout(False)
+        self.DoFrameLayout()
+        
+        # now that the frame layout is done, we need to check
+        # the new pane rectangles against the old rectangles that
+        # we saved a few lines above here.  If the rectangles have
+        # changed, the corresponding panes must also be updated
+        for ii in xrange(pane_count):
+            p = self._panes[ii]
+            if p.window and p.IsShown() and p.IsDocked():
+                if p.rect != old_pane_rects[ii]:
+                    p.window.Refresh()
+                    p.window.Update()
+
+        if wx.Platform == "__WXMAC__":
+            self._frame.Refresh()
+        else:
+            self.Repaint()
+
+        if not self._masterManager:
+            e = self.FireEvent(wxEVT_AUI_PERSPECTIVE_CHANGED, None, canVeto=False)    
+
+
+    def UpdateNotebook(self):
+        """ Updates the automatic L{AuiNotebook} in the layout (if any exists). """
+
+        # Workout how many notebooks we need.
+        max_notebook = -1
+
+        # destroy floating panes which have been
+        # redocked or are becoming non-floating
+        for paneInfo in self._panes:
+            if max_notebook < paneInfo.notebook_id:
+                max_notebook = paneInfo.notebook_id
+        
+        # We are the master of our domain
+        extra_notebook = len(self._notebooks)
+        max_notebook += 1
+        
+        for i in xrange(extra_notebook, max_notebook):
+            self.CreateNotebook()
+
+        # Remove pages from notebooks that no-longer belong there ...
+        for nb, notebook in enumerate(self._notebooks):
+            pages = notebook.GetPageCount()
+            pageCounter, allPages = 0, pages
+
+            # Check each tab ...
+            for page in xrange(pages):
+
+                if page >= allPages:
+                    break
+                
+                window = notebook.GetPage(pageCounter)
+                paneInfo = self.GetPane(window)
+                if paneInfo.IsOk() and paneInfo.notebook_id != nb:
+                    notebook.RemovePage(pageCounter)
+                    window.Hide()
+                    window.Reparent(self._frame)
+                    pageCounter -= 1
+                    allPages -= 1
+
+                pageCounter += 1
+
+            notebook.DoSizing()
+
+        # Add notebook pages that aren't there already...
+        for paneInfo in self._panes:
+            if paneInfo.IsNotebookPage():
+            
+                title = (paneInfo.caption == "" and [paneInfo.name] or [paneInfo.caption])[0]
+
+                notebook = self._notebooks[paneInfo.notebook_id]
+                page_id = notebook.GetPageIndex(paneInfo.window)
+
+                if page_id < 0:
+                
+                    paneInfo.window.Reparent(notebook)
+                    notebook.AddPage(paneInfo.window, title, True, paneInfo.icon)
+                
+                # Update title and icon ...
+                else:
+                
+                    notebook.SetPageText(page_id, title)
+                    notebook.SetPageBitmap(page_id, paneInfo.icon)
+
+                notebook.DoSizing()
+                
+            # Wire-up newly created notebooks
+            elif paneInfo.IsNotebookControl() and not paneInfo.window:
+                paneInfo.window = self._notebooks[paneInfo.notebook_id]
+            
+        # Delete empty notebooks, and convert notebooks with 1 page to
+        # normal panes...
+        remap_ids = [-1]*len(self._notebooks)
+        nb_idx = 0
+
+        for nb, notebook in enumerate(self._notebooks): 
+            if notebook.GetPageCount() == 1:
+            
+                # Convert notebook page to pane...
+                window = notebook.GetPage(0)
+                child_pane = self.GetPane(window)
+                notebook_pane = self.GetPane(notebook)
+                if child_pane.IsOk() and notebook_pane.IsOk():
+                
+                    child_pane.SetDockPos(notebook_pane)
+                    child_pane.window.Hide()
+                    child_pane.window.Reparent(self._frame)
+                    child_pane.frame = None
+                    child_pane.notebook_id = -1
+                    if notebook_pane.IsFloating():
+                        child_pane.Float()
+
+                    self.DetachPane(notebook)
+
+                    notebook.RemovePage(0)
+                    notebook.Destroy()
+                
+                else:
+                
+                    raise Exception("Odd notebook docking")
+                
+            elif notebook.GetPageCount() == 0:
+            
+                self.DetachPane(notebook)
+                notebook.Destroy()
+            
+            else:
+            
+                # Correct page ordering. The original wxPython code
+                # for this did not work properly, and would misplace 
+                # windows causing errors.
+                notebook.Freeze()
+                self._notebooks[nb_idx] = notebook
+                pages = notebook.GetPageCount()
+                selected = notebook.GetPage(notebook.GetSelection())
+
+                # Take each page out of the notebook, group it with
+                # its current pane, and sort the list by pane.dock_pos
+                # order
+                pages_and_panes = []
+                for idx in reversed(range(pages)):
+                    page = notebook.GetPage(idx)
+                    pane = self.GetPane(page)
+                    pages_and_panes.append((page, pane))
+                    notebook.RemovePage(idx)
+                sorted_pnp = sorted(pages_and_panes, key=lambda tup: tup[1].dock_pos)
+
+                # Grab the attributes from the panes which are ordered
+                # correctly, and copy those attributes to the original
+                # panes. (This avoids having to change the ordering
+                # of self._panes) Then, add the page back into the notebook
+                sorted_attributes = [self.GetAttributes(tup[1])
+                                     for tup in sorted_pnp]
+                for attrs, tup in zip(sorted_attributes, pages_and_panes):
+                    pane = tup[1]
+                    self.SetAttributes(pane, attrs)
+                    notebook.AddPage(pane.window, pane.caption)
+
+                notebook.SetSelection(notebook.GetPageIndex(selected), True)
+                notebook.DoSizing()
+                notebook.Thaw()
+
+                # It's a keeper.
+                remap_ids[nb] = nb_idx
+                nb_idx += 1
+
+        # Apply remap...
+        nb_count = len(self._notebooks)
+        
+        if nb_count != nb_idx:
+        
+            self._notebooks = self._notebooks[0:nb_idx]
+            for p in self._panes:
+                if p.notebook_id >= 0:                
+                    p.notebook_id = remap_ids[p.notebook_id]
+                    if p.IsNotebookControl():
+                        p.SetNameFromNotebookId()
+                
+        # Make sure buttons are correct ...
+        for notebook in self._notebooks:
+            want_max = True
+            want_min = True
+            want_close = True
+
+            pages = notebook.GetPageCount()
+            for page in xrange(pages):
+            
+                win = notebook.GetPage(page)
+                pane = self.GetPane(win)
+                if pane.IsOk():
+                
+                    if not pane.HasCloseButton():
+                        want_close = False
+                    if not pane.HasMaximizeButton():
+                        want_max = False
+                    if not pane.HasMinimizeButton():
+                        want_min = False
+                
+            notebook_pane = self.GetPane(notebook)
+            if notebook_pane.IsOk():
+                if notebook_pane.HasMinimizeButton() != want_min:
+                    if want_min:
+                        button = AuiPaneButton(AUI_BUTTON_MINIMIZE)
+                        notebook_pane.state |= AuiPaneInfo.buttonMinimize
+                        notebook_pane.buttons.append(button)
+
+                    # todo: remove min/max
+            
+                if notebook_pane.HasMaximizeButton() != want_max:
+                    if want_max:
+                        button = AuiPaneButton(AUI_BUTTON_MAXIMIZE_RESTORE)
+                        notebook_pane.state |= AuiPaneInfo.buttonMaximize
+                        notebook_pane.buttons.append(button)
+                    
+                    # todo: remove min/max
+                
+                if notebook_pane.HasCloseButton() != want_close:
+                    if want_close:
+                        button = AuiPaneButton(AUI_BUTTON_CLOSE)
+                        notebook_pane.state |= AuiPaneInfo.buttonClose
+                        notebook_pane.buttons.append(button)
+                    
+                    # todo: remove close
+
+
+    def SmartShrink(self, docks, direction):
+        """
+        Used to intelligently shrink the docks' size (if needed).
+
+        :param `docks`: a list of L{AuiDockInfo} instances;
+        :param `direction`: the direction in which to shrink.
+        """
+
+        sashSize = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        clientSize = self._frame.GetClientSize()
+        ourDocks = FindDocks(docks, direction, -1, -1)
+        oppositeDocks = FindOppositeDocks(docks, direction)
+        oppositeSize = self.GetOppositeDockTotalSize(docks, direction)
+        ourSize = 0
+
+        for dock in ourDocks:
+            ourSize += dock.size
+
+            if not dock.toolbar:
+                ourSize += sashSize
+        
+        shrinkSize = ourSize + oppositeSize
+
+        if direction == AUI_DOCK_TOP or direction == AUI_DOCK_BOTTOM:
+            shrinkSize -= clientSize.y
+        else:
+            shrinkSize -= clientSize.x
+
+        if shrinkSize <= 0:
+            return docks
+
+        # Combine arrays
+        for dock in oppositeDocks:
+            ourDocks.append(dock)
+            
+        oppositeDocks = []
+
+        for dock in ourDocks:
+            if dock.toolbar or not dock.resizable:
+                continue
+
+            dockRange = dock.size - dock.min_size
+
+            if dock.min_size == 0:
+                dockRange -= sashSize
+                if direction == AUI_DOCK_TOP or direction == AUI_DOCK_BOTTOM:
+                    dockRange -= caption_size
+            
+            if dockRange >= shrinkSize:
+            
+                dock.size -= shrinkSize
+                return docks
+            
+            else:
+            
+                dock.size -= dockRange
+                shrinkSize -= dockRange
+            
+        return docks
+    
+
+    def UpdateDockingGuides(self, paneInfo):
+        """
+        Updates the docking guide windows positions and appearance.
+
+        :param `paneInfo`: a L{AuiPaneInfo} instance.
+        """
+
+        if len(self._guides) == 0:
+            self.CreateGuideWindows()
+
+        captionSize = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        frameRect = GetInternalFrameRect(self._frame, self._docks)
+        mousePos = wx.GetMousePosition()
+
+        for indx, guide in enumerate(self._guides):
+        
+            pt = wx.Point()
+            guide_size = guide.host.GetSize()
+            if not guide.host:
+                raise Exception("Invalid docking host")
+
+            direction = guide.dock_direction
+
+            if direction == AUI_DOCK_LEFT:
+                pt.x = frameRect.x + guide_size.x / 2 + 16
+                pt.y = frameRect.y + frameRect.height / 2
+
+            elif direction == AUI_DOCK_TOP:
+                pt.x = frameRect.x + frameRect.width / 2
+                pt.y = frameRect.y + guide_size.y / 2 + 16
+
+            elif direction == AUI_DOCK_RIGHT:
+                pt.x = frameRect.x + frameRect.width - guide_size.x / 2 - 16
+                pt.y = frameRect.y + frameRect.height / 2
+
+            elif direction == AUI_DOCK_BOTTOM:
+                pt.x = frameRect.x + frameRect.width / 2
+                pt.y = frameRect.y + frameRect.height - guide_size.y / 2 - 16
+
+            elif direction == AUI_DOCK_CENTER:
+                rc = paneInfo.window.GetScreenRect()
+                pt.x = rc.x + rc.width / 2
+                pt.y = rc.y + rc.height / 2
+                if paneInfo.HasCaption():
+                    pt.y -= captionSize / 2
+                elif paneInfo.HasCaptionLeft():
+                    pt.x -= captionSize / 2
+
+            # guide will be centered around point 'pt'
+            targetPosition = wx.Point(pt.x - guide_size.x / 2, pt.y - guide_size.y / 2)
+
+            if guide.host.GetPosition() != targetPosition:
+                guide.host.Move(targetPosition)
+                
+            guide.host.AeroMove(targetPosition)
+
+            if guide.dock_direction == AUI_DOCK_CENTER:
+                guide.host.ValidateNotebookDocking(paneInfo.IsNotebookDockable())
+
+            guide.host.UpdateDockGuide(mousePos)
+        
+        paneInfo.window.Lower()
+
+                        
+    def DoFrameLayout(self):
+        """
+        This is an internal function which invokes `wx.Sizer.Layout`
+        on the frame's main sizer, then measures all the various UI items
+        and updates their internal rectangles.
+
+        :note: This should always be called instead of calling
+         `self._managed_window.Layout()` directly.
+        """
+
+        self._frame.Layout()
+        
+        for part in self._uiparts:            
+            # get the rectangle of the UI part
+            # originally, this code looked like this:
+            #    part.rect = wx.Rect(part.sizer_item.GetPosition(),
+            #                       part.sizer_item.GetSize())
+            # this worked quite well, with one exception: the mdi
+            # client window had a "deferred" size variable 
+            # that returned the wrong size.  It looks like
+            # a bug in wx, because the former size of the window
+            # was being returned.  So, we will retrieve the part's
+            # rectangle via other means
+
+            part.rect = part.sizer_item.GetRect()
+            flag = part.sizer_item.GetFlag()
+            border = part.sizer_item.GetBorder()
+            
+            if flag & wx.TOP:
+                part.rect.y -= border
+                part.rect.height += border
+            if flag & wx.LEFT:
+                part.rect.x -= border
+                part.rect.width += border
+            if flag & wx.BOTTOM:
+                part.rect.height += border
+            if flag & wx.RIGHT:
+                part.rect.width += border
+
+            if part.type == AuiDockUIPart.typeDock:
+                part.dock.rect = part.rect
+            if part.type == AuiDockUIPart.typePane:
+                part.pane.rect = part.rect
+
+
+    def GetPanePart(self, wnd):
+        """
+        Looks up the pane border UI part of the
+        pane specified. This allows the caller to get the exact rectangle
+        of the pane in question, including decorations like caption and border.
+
+        :param `wnd`: the window to which the pane border belongs to.        
+        """
+
+        for part in self._uiparts:
+            if part.type == AuiDockUIPart.typePaneBorder and \
+               part.pane and part.pane.window == wnd:
+                return part
+
+        for part in self._uiparts:
+            if part.type == AuiDockUIPart.typePane and \
+               part.pane and part.pane.window == wnd:
+                return part
+    
+        return None
+
+
+    def GetDockPixelOffset(self, test):
+        """
+        This is an internal function which returns
+        a dock's offset in pixels from the left side of the window
+        (for horizontal docks) or from the top of the window (for
+        vertical docks).
+
+        This value is necessary for calculating fixed-pane/toolbar offsets
+        when they are dragged.
+
+        :param `test`: a fake L{AuiPaneInfo} for testing purposes.
+        """
+
+        # the only way to accurately calculate the dock's
+        # offset is to actually run a theoretical layout
+        docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
+        panes.append(test)
+
+        sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False)
+        client_size = self._frame.GetClientSize()
+        sizer.SetDimension(0, 0, client_size.x, client_size.y)
+        sizer.Layout()
+
+        for part in uiparts:
+            pos = part.sizer_item.GetPosition()
+            size = part.sizer_item.GetSize()
+            part.rect = wx.RectPS(pos, size)
+            if part.type == AuiDockUIPart.typeDock:
+                part.dock.rect = part.rect
+
+        sizer.Destroy()
+
+        for dock in docks:
+            if test.dock_direction == dock.dock_direction and \
+               test.dock_layer == dock.dock_layer and  \
+               test.dock_row == dock.dock_row:
+            
+                if dock.IsVertical():
+                    return dock.rect.y
+                else:
+                    return dock.rect.x
+            
+        return 0
+    
+
+    def GetPartnerDock(self, dock):
+        """
+        Returns the partner dock for the input dock.
+
+        :param `dock`: a L{AuiDockInfo} instance.
+        """
+
+        for layer in xrange(dock.dock_layer, -1, -1):
+        
+            bestDock = None
+
+            for tmpDock in self._docks:
+            
+                if tmpDock.dock_layer != layer:
+                    continue
+                
+                if tmpDock.dock_direction != dock.dock_direction:
+                    continue
+
+                if tmpDock.dock_layer < dock.dock_layer:
+                
+                    if not bestDock or tmpDock.dock_row < bestDock.dock_row:
+                        bestDock = tmpDock
+                
+                elif tmpDock.dock_row > dock.dock_row:
+                
+                    if not bestDock or tmpDock.dock_row > bestDock.dock_row:
+                        bestDock = tmpDock
+                
+            if bestDock:
+                return bestDock
+        
+        return None
+
+
+    def GetPartnerPane(self, dock, pane):
+        """
+        Returns the partner pane for the input pane. They both need to live
+        in the same L{AuiDockInfo}.
+
+        :param `dock`: a L{AuiDockInfo} instance;
+        :param `pane`: a L{AuiPaneInfo} class.
+        """
+        
+        panePosition = -1
+
+        for i, tmpPane in enumerate(dock.panes):        
+            if tmpPane.window == pane.window:
+                panePosition = i
+            elif not tmpPane.IsFixed() and panePosition != -1:
+                return tmpPane
+        
+        return None
+
+
+    def GetTotalPixSizeAndProportion(self, dock):
+        """
+        Returns the dimensions and proportion of the input dock.
+
+        :param `dock`: the L{AuiDockInfo} structure to analyze.
+        """
+
+        totalPixsize = 0
+        totalProportion = 0
+
+        # determine the total proportion of all resizable panes,
+        # and the total size of the dock minus the size of all
+        # the fixed panes
+        for tmpPane in dock.panes:
+        
+            if tmpPane.IsFixed():
+                continue
+
+            totalProportion += tmpPane.dock_proportion
+
+            if dock.IsHorizontal():
+                totalPixsize += tmpPane.rect.width
+            else:
+                totalPixsize += tmpPane.rect.height
+
+##            if tmpPane.min_size.IsFullySpecified():
+##            
+##                if dock.IsHorizontal():
+##                    totalPixsize -= tmpPane.min_size.x
+##                else:
+##                    totalPixsize -= tmpPane.min_size.y
+            
+        return totalPixsize, totalProportion
+
+
+    def GetOppositeDockTotalSize(self, docks, direction):
+        """
+        Returns the dimensions of the dock which lives opposite of the input dock.
+
+        :param `docks`: a list of L{AuiDockInfo} structures to analyze;
+        :param `direction`: the direction in which to look for the opposite dock.
+        """
+        
+        sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
+        minSizeMax = 0
+        result = sash_size
+        vertical = False
+
+        if direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]:
+            vertical = True
+
+        # Get minimum size of the most inner area
+        for tmpDock in docks:
+        
+            if tmpDock.dock_layer != 0:
+                continue
+
+            if tmpDock.dock_direction != AUI_DOCK_CENTER and tmpDock.IsVertical() != vertical:
+                continue
+
+            for tmpPane in tmpDock.panes:
+            
+                minSize = pane_border_size*2 - sash_size
+
+                if vertical:
+                    minSize += tmpPane.min_size.y + caption_size
+                else:
+                    minSize += tmpPane.min_size.x
+
+                if minSize > minSizeMax:
+                    minSizeMax = minSize
+            
+        result += minSizeMax
+
+        # Get opposite docks
+        oppositeDocks = FindOppositeDocks(docks, direction)
+
+        # Sum size of the opposite docks and their sashes
+        for dock in oppositeDocks:
+            result += dock.size
+            # if it's not a toolbar add the sash_size too
+            if not dock.toolbar:
+                result += sash_size
+        
+        return result
+
+
+    def CalculateDockSizerLimits(self, dock):
+        """
+        Calculates the minimum and maximum sizes allowed for the input dock.
+
+        :param `dock`: the L{AuiDockInfo} structure to analyze.
+        """
+
+        docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
+
+        sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+        opposite_size = self.GetOppositeDockTotalSize(docks, dock.dock_direction)
+
+        for tmpDock in docks:
+        
+            if tmpDock.dock_direction == dock.dock_direction and \
+               tmpDock.dock_layer == dock.dock_layer and \
+               tmpDock.dock_row == dock.dock_row:
+        
+                tmpDock.size = 1
+                break
+        
+        sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False)
+        client_size = self._frame.GetClientSize()
+        sizer.SetDimension(0, 0, client_size.x, client_size.y)
+        sizer.Layout()
+
+        for part in uiparts:
+        
+            part.rect = wx.RectPS(part.sizer_item.GetPosition(), part.sizer_item.GetSize())
+            if part.type == AuiDockUIPart.typeDock:
+                part.dock.rect = part.rect
+        
+        sizer.Destroy()
+        new_dock = None
+
+        for tmpDock in docks:
+            if tmpDock.dock_direction == dock.dock_direction and \
+               tmpDock.dock_layer == dock.dock_layer and \
+               tmpDock.dock_row == dock.dock_row:
+            
+                new_dock = tmpDock
+                break
+            
+        partnerDock = self.GetPartnerDock(dock)
+
+        if partnerDock:
+            partnerRange = partnerDock.size - partnerDock.min_size
+            if partnerDock.min_size == 0:
+                partnerRange -= sash_size
+                if dock.IsHorizontal():
+                    partnerRange -= caption_size
+            
+            direction = dock.dock_direction
+            
+            if direction == AUI_DOCK_LEFT:
+                minPix = new_dock.rect.x + new_dock.rect.width
+                maxPix = dock.rect.x + dock.rect.width
+                maxPix += partnerRange
+
+            elif direction == AUI_DOCK_TOP:
+                minPix = new_dock.rect.y + new_dock.rect.height
+                maxPix = dock.rect.y + dock.rect.height
+                maxPix += partnerRange
+
+            elif direction == AUI_DOCK_RIGHT:
+                minPix = dock.rect.x - partnerRange - sash_size
+                maxPix = new_dock.rect.x - sash_size
+
+            elif direction == AUI_DOCK_BOTTOM:
+                minPix = dock.rect.y - partnerRange - sash_size
+                maxPix = new_dock.rect.y - sash_size
+
+            return minPix, maxPix
+        
+        direction = new_dock.dock_direction
+        
+        if direction == AUI_DOCK_LEFT:
+            minPix = new_dock.rect.x + new_dock.rect.width
+            maxPix = client_size.x - opposite_size - sash_size
+
+        elif direction == AUI_DOCK_TOP:
+            minPix = new_dock.rect.y + new_dock.rect.height
+            maxPix = client_size.y - opposite_size - sash_size
+
+        elif direction == AUI_DOCK_RIGHT:
+            minPix = opposite_size
+            maxPix = new_dock.rect.x - sash_size
+
+        elif direction == AUI_DOCK_BOTTOM:
+            minPix = opposite_size
+            maxPix = new_dock.rect.y - sash_size
+
+        return minPix, maxPix
+
+
+    def CalculatePaneSizerLimits(self, dock, pane):
+        """
+        Calculates the minimum and maximum sizes allowed for the input pane.
+
+        :param `dock`: the L{AuiDockInfo} structure to which `pane` belongs to;
+        :param `pane`: a L{AuiPaneInfo} class for which calculation are requested.
+        """
+        
+        if pane.IsFixed():
+            if dock.IsHorizontal():
+                minPix = maxPix = pane.rect.x + 1 + pane.rect.width
+            else:
+                minPix = maxPix = pane.rect.y + 1 + pane.rect.height
+
+            return minPix, maxPix
+        
+        totalPixsize, totalProportion = self.GetTotalPixSizeAndProportion(dock)
+        partnerPane = self.GetPartnerPane(dock, pane)
+
+        if dock.IsHorizontal():
+        
+            minPix = pane.rect.x + 1
+            maxPix = pane.rect.x + 1 + pane.rect.width
+
+            if pane.min_size.IsFullySpecified():
+                minPix += pane.min_size.x
+            else:
+                minPix += 1
+
+            if partnerPane:
+                maxPix += partnerPane.rect.width
+
+                if partnerPane.min_size.IsFullySpecified():
+                    maxPix -= partnerPane.min_size.x - 1
+            
+            else:
+                minPix = maxPix
+        
+        else:
+        
+            minPix = pane.rect.y + 1
+            maxPix = pane.rect.y + 1 + pane.rect.height
+
+            if pane.min_size.IsFullySpecified():
+                minPix += pane.min_size.y
+            else:
+                minPix += 1
+
+            if partnerPane:            
+                maxPix += partnerPane.rect.height
+
+                if partnerPane.min_size.IsFullySpecified():
+                    maxPix -= partnerPane.min_size.y - 1
+            
+            else:            
+                minPix = maxPix
+            
+        return minPix, maxPix
+
+
+    def CheckMovableSizer(self, part):
+        """
+        Checks if a UI part can be actually resized.
+
+        :param `part`: a UI part.
+        """
+
+        # a dock may not be resized if it has a single
+        # pane which is not resizable
+        if part.type == AuiDockUIPart.typeDockSizer and part.dock and \
+           len(part.dock.panes) == 1 and part.dock.panes[0].IsFixed():
+        
+            return False
+        
+        if part.pane:
+        
+            # panes that may not be resized should be ignored here
+            minPix, maxPix = self.CalculatePaneSizerLimits(part.dock, part.pane)
+
+            if minPix == maxPix:
+                return False
+        
+        return True
+
+
+    def PaneFromTabEvent(self, event):
+        """
+        Returns a L{AuiPaneInfo} from a L{AuiNotebookEvent} event.
+
+        :param `event`: a L{AuiNotebookEvent} event.
+        """
+
+        obj = event.GetEventObject()
+
+        if obj and isinstance(obj, auibook.AuiTabCtrl):
+        
+            page_idx = obj.GetActivePage()
+
+            if page_idx >= 0:
+                page = obj.GetPage(page_idx)
+                window = page.window
+                if window:
+                    return self.GetPane(window)
+            
+        elif obj and isinstance(obj, auibook.AuiNotebook):
+        
+            page_idx = event.GetSelection()
+            
+            if page_idx >= 0:
+                window = obj.GetPage(page_idx)
+                if window:
+                    return self.GetPane(window)
+            
+        return NonePaneInfo
+
+
+    def OnTabBeginDrag(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_BEGIN_DRAG`` event.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        if self._masterManager:
+            self._masterManager.OnTabBeginDrag(event)
+        
+        else:
+            paneInfo = self.PaneFromTabEvent(event)
+            
+            if paneInfo.IsOk():
+            
+                # It's one of ours!
+                self._action = actionDragFloatingPane
+                mouse = wx.GetMousePosition()
+
+                # set initial float position - may have to think about this
+                # offset a bit more later ...
+                self._action_offset = wx.Point(20, 10)
+                self._toolbar_action_offset = wx.Point(20, 10)
+                
+                paneInfo.floating_pos = mouse - self._action_offset
+                paneInfo.dock_pos = AUI_DOCK_NONE
+                paneInfo.notebook_id = -1
+
+                tab = event.GetEventObject()
+
+                if tab.HasCapture():
+                    tab.ReleaseMouse()
+
+                # float the window
+                if paneInfo.IsMaximized():
+                    self.RestorePane(paneInfo)
+                paneInfo.Float()
+                self.Update()
+
+                self._action_window = paneInfo.window
+                    
+                self._frame.CaptureMouse()
+                event.SetDispatched(True)
+            
+            else:
+            
+                # not our window
+                event.Skip()
+            
+
+    def OnTabPageClose(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_PAGE_CLOSE`` event.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+
+        if self._masterManager:
+            self._masterManager.OnTabPageClose(event)
+        
+        else:
+        
+            p = self.PaneFromTabEvent(event)
+            if p.IsOk():
+            
+                # veto it because we will call "RemovePage" ourselves
+                event.Veto()
+
+                # Now ask the app if they really want to close...
+                # fire pane close event
+                e = AuiManagerEvent(wxEVT_AUI_PANE_CLOSE)
+                e.SetPane(p)
+                e.SetCanVeto(True)
+                self.ProcessMgrEvent(e)
+
+                if e.GetVeto():
+                    return
+
+                self.ClosePane(p)
+                self.Update()
+            else:
+                event.Skip()
+            
+
+    def OnTabSelected(self, event):
+        """
+        Handles the ``EVT_AUINOTEBOOK_PAGE_CHANGED`` event.
+
+        :param `event`: a L{AuiNotebookEvent} event to be processed.
+        """
+        
+        if self._masterManager:
+            self._masterManager.OnTabSelected(event)
+            return
+        
+        obj = event.GetEventObject()
+
+        if obj and isinstance(obj, auibook.AuiNotebook):
+        
+            notebook = obj
+            page = notebook.GetPage(event.GetSelection())
+            paneInfo = self.GetPane(page)
+
+            if paneInfo.IsOk():
+                notebookRoot = GetNotebookRoot(self._panes, paneInfo.notebook_id)
+                if notebookRoot:
+                
+                    notebookRoot.Caption(paneInfo.caption)
+                    self.RefreshCaptions()
+                
+        event.Skip()
+
+
+    def GetNotebooks(self):
+        """ Returns all the automatic L{AuiNotebook} in the L{AuiManager}. """
+
+        if self._masterManager:
+            return self._masterManager.GetNotebooks()
+        
+        return self._notebooks
+
+
+    def SetMasterManager(self, manager):
+        """
+        Sets the master manager for an automatic L{AuiNotebook}.
+
+        :param `manager`: an instance of L{AuiManager}.
+        """
+
+        self._masterManager = manager
+
+        
+    def ProcessDockResult(self, target, new_pos):
+        """
+        This is a utility function used by L{DoDrop} - it checks
+        if a dock operation is allowed, the new dock position is copied into
+        the target info. If the operation was allowed, the function returns ``True``.
+
+        :param `target`: the L{AuiPaneInfo} instance to be docked;
+        :param `new_pos`: the new docking position if the docking operation is allowed.
+        """
+
+        allowed = False
+        direction = new_pos.dock_direction
+        
+        if direction == AUI_DOCK_TOP:
+            allowed = target.IsTopDockable()
+        elif direction == AUI_DOCK_BOTTOM:
+            allowed = target.IsBottomDockable()
+        elif direction == AUI_DOCK_LEFT:
+            allowed = target.IsLeftDockable()
+        elif direction == AUI_DOCK_RIGHT:
+            allowed = target.IsRightDockable()
+
+        if allowed:
+            target = new_pos
+
+            if target.IsToolbar():
+                self.SwitchToolBarOrientation(target)
+
+        return allowed, target
+
+
+    def SwitchToolBarOrientation(self, pane):
+        """
+        Switches the toolbar orientation from vertical to horizontal and vice-versa.
+        This is especially useful for vertical docked toolbars once they float.
+
+        :param `pane`: an instance of L{AuiPaneInfo}, which may have a L{AuiToolBar}
+         window associated with it.
+        """
+
+        if not isinstance(pane.window, auibar.AuiToolBar):
+            return pane
+        
+        if pane.IsFloating():
+            return pane
+
+        toolBar = pane.window
+        direction = pane.dock_direction
+        vertical = direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]
+
+        agwStyle = toolBar.GetAGWWindowStyleFlag()
+        new_agwStyle = agwStyle
+
+        if vertical:
+            new_agwStyle |= AUI_TB_VERTICAL
+        else:
+            new_agwStyle &= ~(AUI_TB_VERTICAL)
+
+        if agwStyle != new_agwStyle:
+            toolBar.SetAGWWindowStyleFlag(new_agwStyle)
+        if not toolBar.GetGripperVisible():
+            toolBar.SetGripperVisible(True)
+
+        s = pane.window.GetMinSize()
+        pane.BestSize(s)
+
+        if new_agwStyle != agwStyle:
+            toolBar.Realize()
+        
+        return pane
+
+
+    def DoDrop(self, docks, panes, target, pt, offset=wx.Point(0, 0)):
+        """
+        This is an important function. It basically takes a mouse position,
+        and determines where the panes new position would be. If the pane is to be
+        dropped, it performs the drop operation using the specified dock and pane
+        arrays. By specifying copy dock and pane arrays when calling, a "what-if"
+        scenario can be performed, giving precise coordinates for drop hints.
+
+        :param `docks`: a list of L{AuiDockInfo} classes;
+        :param `panes`: a list of L{AuiPaneInfo} instances;
+        :param `pt`: a mouse position to check for a drop operation;
+        :param `offset`: a possible offset from the input point `pt`.
+        """
+
+        if target.IsToolbar():
+            return self.DoDropToolbar(docks, panes, target, pt, offset)
+        elif target.IsFloating():
+            return self.DoDropFloatingPane(docks, panes, target, pt)
+        else:
+            return self.DoDropNonFloatingPane(docks, panes, target, pt)
+    
+
+    def CopyTarget(self, target):
+        """
+        Copies all the attributes of the input `target` into another L{AuiPaneInfo}.
+
+        :param `target`: the source L{AuiPaneInfo} from where to copy attributes.
+        """
+
+        drop = AuiPaneInfo()
+        drop.name = target.name
+        drop.caption = target.caption
+        drop.window = target.window
+        drop.frame = target.frame
+        drop.state = target.state
+        drop.dock_direction = target.dock_direction
+        drop.dock_layer = target.dock_layer
+        drop.dock_row = target.dock_row
+        drop.dock_pos = target.dock_pos
+        drop.best_size = wx.Size(*target.best_size)
+        drop.min_size = wx.Size(*target.min_size)
+        drop.max_size = wx.Size(*target.max_size)
+        drop.floating_pos = wx.Point(*target.floating_pos)
+        drop.floating_size = wx.Size(*target.floating_size)
+        drop.dock_proportion = target.dock_proportion
+        drop.buttons = target.buttons
+        drop.rect = wx.Rect(*target.rect)
+        drop.icon = target.icon
+        drop.notebook_id = target.notebook_id
+        drop.transparent = target.transparent
+        drop.snapped = target.snapped
+        drop.minimize_mode = target.minimize_mode
+
+        return drop        
+
+
+    def DoDropToolbar(self, docks, panes, target, pt, offset):
+        """
+        Handles the situation in which the dropped pane contains a toolbar.
+
+        :param `docks`: a list of L{AuiDockInfo} classes;
+        :param `panes`: a list of L{AuiPaneInfo} instances;
+        :param `target`: the target pane containing the toolbar;
+        :param `pt`: a mouse position to check for a drop operation;
+        :param `offset`: a possible offset from the input point `pt`.        
+        """
+        
+        drop = self.CopyTarget(target)
+
+        # The result should always be shown
+        drop.Show()
+
+        # Check to see if the toolbar has been dragged out of the window
+        if CheckOutOfWindow(self._frame, pt):
+            if self._agwFlags & AUI_MGR_ALLOW_FLOATING and drop.IsFloatable():
+                drop.Float()
+
+            return self.ProcessDockResult(target, drop)
+
+        # Allow directional change when the cursor leaves this rect
+        safeRect = wx.Rect(*target.rect)
+        if target.IsHorizontal():
+            safeRect.Inflate(100, 50)
+        else:
+            safeRect.Inflate(50, 100)
+        
+        # Check to see if the toolbar has been dragged to edge of the frame
+        dropDir = CheckEdgeDrop(self._frame, docks, pt)
+
+        if dropDir != -1:
+        
+            if dropDir == wx.LEFT:
+                drop.Dock().Left().Layer(auiToolBarLayer).Row(0). \
+                    Position(pt.y - self.GetDockPixelOffset(drop) - offset.y)
+
+            elif dropDir == wx.RIGHT:
+                drop.Dock().Right().Layer(auiToolBarLayer).Row(0). \
+                    Position(pt.y - self.GetDockPixelOffset(drop) - offset.y)
+
+            elif dropDir == wx.TOP:
+                drop.Dock().Top().Layer(auiToolBarLayer).Row(0). \
+                    Position(pt.x - self.GetDockPixelOffset(drop) - offset.x)
+
+            elif dropDir == wx.BOTTOM:
+                drop.Dock().Bottom().Layer(auiToolBarLayer).Row(0). \
+                    Position(pt.x - self.GetDockPixelOffset(drop) - offset.x)
+
+            if not target.IsFloating() and safeRect.Contains(pt) and \
+               target.dock_direction != drop.dock_direction:
+                return False, target
+        
+            return self.ProcessDockResult(target, drop)
+    
+        # If the windows is floating and out of the client area, do nothing
+        if drop.IsFloating() and not self._frame.GetClientRect().Contains(pt):
+            return False, target
+    
+        # Ok, can't drop on edge - check internals ...
+
+        clientSize = self._frame.GetClientSize()
+        x = Clip(pt.x, 0, clientSize.x - 1)
+        y = Clip(pt.y, 0, clientSize.y - 1)
+        part = self.HitTest(x, y)
+
+        if not part or not part.dock:
+            return False, target
+        
+        dock = part.dock
+        
+        # toolbars may only be moved in and to fixed-pane docks,
+        # otherwise we will try to float the pane.  Also, the pane
+        # should float if being dragged over center pane windows
+        if not dock.fixed or dock.dock_direction == AUI_DOCK_CENTER:
+        
+            if (self._agwFlags & AUI_MGR_ALLOW_FLOATING and drop.IsFloatable()) or \
+               dock.dock_direction not in [AUI_DOCK_CENTER, AUI_DOCK_NONE]:
+                if drop.IsFloatable():
+                    drop.Float()
+            
+            return self.ProcessDockResult(target, drop)
+        
+        # calculate the offset from where the dock begins
+        # to the point where the user dropped the pane
+        dockDropOffset = 0
+        if dock.IsHorizontal():
+            dockDropOffset = pt.x - dock.rect.x - offset.x
+        else:
+            dockDropOffset = pt.y - dock.rect.y - offset.y
+        
+        drop.Dock().Direction(dock.dock_direction).Layer(dock.dock_layer). \
+            Row(dock.dock_row).Position(dockDropOffset)
+
+        if (pt.y <= dock.rect.GetTop() + 2 and dock.IsHorizontal()) or \
+           (pt.x <= dock.rect.GetLeft() + 2 and dock.IsVertical()):
+        
+            if dock.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_LEFT]:
+                row = drop.dock_row
+                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row)
+                drop.dock_row = row
+            
+            else:
+                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row+1)
+                drop.dock_row = dock.dock_row + 1
+            
+        if (pt.y >= dock.rect.GetBottom() - 2 and dock.IsHorizontal()) or \
+           (pt.x >= dock.rect.GetRight() - 2 and dock.IsVertical()):
+        
+            if dock.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_LEFT]:
+                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row+1)
+                drop.dock_row = dock.dock_row+1
+            
+            else:
+                row = drop.dock_row
+                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row)
+                drop.dock_row = row
+
+        if not target.IsFloating() and safeRect.Contains(pt) and \
+           target.dock_direction != drop.dock_direction:
+            return False, target
+    
+        return self.ProcessDockResult(target, drop)
+
+
+    def DoDropFloatingPane(self, docks, panes, target, pt):
+        """
+        Handles the situation in which the dropped pane contains a normal window.
+
+        :param `docks`: a list of L{AuiDockInfo} classes;
+        :param `panes`: a list of L{AuiPaneInfo} instances;
+        :param `target`: the target pane containing the window;
+        :param `pt`: a mouse position to check for a drop operation.
+        """
+        
+        screenPt = self._frame.ClientToScreen(pt)
+        paneInfo = self.PaneHitTest(panes, pt)
+
+        if paneInfo.IsMaximized():
+            return False, target
+
+        if paneInfo.window is None:
+            return False, target
+
+        # search the dock guides.
+        # reverse order to handle the center first.
+        for i in xrange(len(self._guides)-1, -1, -1):
+            guide = self._guides[i]
+
+            # do hit testing on the guide
+            dir = guide.host.HitTest(screenPt.x, screenPt.y)
+
+            if dir == -1:  # point was outside of the dock guide
+                continue
+
+            if dir == wx.ALL:   # target is a single dock guide
+                return self.DoDropLayer(docks, target, guide.dock_direction)
+            
+            elif dir == wx.CENTER:
+
+                if not target.IsNotebookDockable():
+                    continue
+                if not paneInfo.IsNotebookDockable() and not paneInfo.IsNotebookControl():
+                    continue
+
+                if not paneInfo.HasNotebook():
+                
+                    # Add a new notebook pane with the original as a tab...
+                    self.CreateNotebookBase(panes, paneInfo)
+                
+                # Add new item to notebook
+                target.NotebookPage(paneInfo.notebook_id)
+            
+            else:
+            
+                drop_pane = False
+                drop_row = False
+
+                insert_dir = paneInfo.dock_direction
+                insert_layer = paneInfo.dock_layer
+                insert_row = paneInfo.dock_row
+                insert_pos = paneInfo.dock_pos
+
+                if insert_dir == AUI_DOCK_CENTER:
+                
+                    insert_layer = 0
+                    if dir == wx.LEFT:
+                        insert_dir = AUI_DOCK_LEFT
+                    elif dir == wx.UP:
+                        insert_dir = AUI_DOCK_TOP
+                    elif dir == wx.RIGHT:
+                        insert_dir = AUI_DOCK_RIGHT
+                    elif dir == wx.DOWN:
+                        insert_dir = AUI_DOCK_BOTTOM
+                
+                if insert_dir == AUI_DOCK_LEFT:
+                
+                    drop_pane = (dir == wx.UP   or dir == wx.DOWN)
+                    drop_row  = (dir == wx.LEFT or dir == wx.RIGHT)
+                    if dir == wx.RIGHT:
+                        insert_row += 1
+                    elif dir == wx.DOWN:
+                        insert_pos += 1
+                
+                elif insert_dir == AUI_DOCK_RIGHT:
+                
+                    drop_pane = (dir == wx.UP   or dir == wx.DOWN)
+                    drop_row  = (dir == wx.LEFT or dir == wx.RIGHT)
+                    if dir == wx.LEFT:
+                        insert_row += 1
+                    elif dir == wx.DOWN:
+                        insert_pos += 1
+                
+                elif insert_dir == AUI_DOCK_TOP:
+                
+                    drop_pane = (dir == wx.LEFT or dir == wx.RIGHT)
+                    drop_row  = (dir == wx.UP   or dir == wx.DOWN)
+                    if dir == wx.DOWN:
+                        insert_row += 1
+                    elif dir == wx.RIGHT:
+                        insert_pos += 1
+                
+                elif insert_dir == AUI_DOCK_BOTTOM:
+                
+                    drop_pane = (dir == wx.LEFT or dir == wx.RIGHT)
+                    drop_row  = (dir == wx.UP   or dir == wx.DOWN)
+                    if dir == wx.UP:
+                        insert_row += 1
+                    elif dir == wx.RIGHT:
+                        insert_pos += 1
+                
+                if paneInfo.dock_direction == AUI_DOCK_CENTER:
+                    insert_row = GetMaxRow(panes, insert_dir, insert_layer) + 1
+
+                if drop_pane:
+                    return self.DoDropPane(panes, target, insert_dir, insert_layer, insert_row, insert_pos)
+
+                if drop_row:
+                    return self.DoDropRow(panes, target, insert_dir, insert_layer, insert_row)
+            
+            return True, target
+        
+        return False, target
+
+
+    def DoDropNonFloatingPane(self, docks, panes, target, pt):
+        """
+        Handles the situation in which the dropped pane is not floating.
+
+        :param `docks`: a list of L{AuiDockInfo} classes;
+        :param `panes`: a list of L{AuiPaneInfo} instances;
+        :param `target`: the target pane containing the toolbar;
+        :param `pt`: a mouse position to check for a drop operation.
+        """
+        
+        screenPt = self._frame.ClientToScreen(pt)
+        clientSize = self._frame.GetClientSize()
+        frameRect = GetInternalFrameRect(self._frame, self._docks)
+
+        drop = self.CopyTarget(target)
+
+        # The result should always be shown
+        drop.Show()
+
+        part = self.HitTest(pt.x, pt.y)
+
+        if not part:
+            return False, target
+
+        if part.type == AuiDockUIPart.typeDockSizer:
+        
+            if len(part.dock.panes) != 1:
+                return False, target
+            
+            part = self.GetPanePart(part.dock.panes[0].window)
+            if not part:
+                return False, target
+        
+        if not part.pane:
+            return False, target
+
+        part = self.GetPanePart(part.pane.window)
+        if not part:
+            return False, target
+
+        insert_dock_row = False
+        insert_row = part.pane.dock_row
+        insert_dir = part.pane.dock_direction
+        insert_layer = part.pane.dock_layer
+
+        direction = part.pane.dock_direction
+        
+        if direction == AUI_DOCK_TOP:
+            if pt.y >= part.rect.y and pt.y < part.rect.y+auiInsertRowPixels:
+                insert_dock_row = True
+
+        elif direction == AUI_DOCK_BOTTOM:
+            if pt.y > part.rect.y+part.rect.height-auiInsertRowPixels and \
+               pt.y <= part.rect.y + part.rect.height:
+                insert_dock_row = True
+
+        elif direction == AUI_DOCK_LEFT:
+            if pt.x >= part.rect.x and pt.x < part.rect.x+auiInsertRowPixels:
+                insert_dock_row = True
+
+        elif direction == AUI_DOCK_RIGHT:
+            if pt.x > part.rect.x+part.rect.width-auiInsertRowPixels and \
+               pt.x <= part.rect.x+part.rect.width:
+                insert_dock_row = True
+
+        elif direction == AUI_DOCK_CENTER:
+            
+                # "new row pixels" will be set to the default, but
+                # must never exceed 20% of the window size
+                new_row_pixels_x = auiNewRowPixels
+                new_row_pixels_y = auiNewRowPixels
+
+                if new_row_pixels_x > (part.rect.width*20)/100:
+                    new_row_pixels_x = (part.rect.width*20)/100
+
+                if new_row_pixels_y > (part.rect.height*20)/100:
+                    new_row_pixels_y = (part.rect.height*20)/100
+
+                # determine if the mouse pointer is in a location that
+                # will cause a new row to be inserted.  The hot spot positions
+                # are along the borders of the center pane
+
+                insert_layer = 0
+                insert_dock_row = True
+                pr = part.rect
+                
+                if pt.x >= pr.x and pt.x < pr.x + new_row_pixels_x:
+                    insert_dir = AUI_DOCK_LEFT
+                elif pt.y >= pr.y and pt.y < pr.y + new_row_pixels_y:
+                    insert_dir = AUI_DOCK_TOP
+                elif pt.x >= pr.x + pr.width - new_row_pixels_x and pt.x < pr.x + pr.width:
+                    insert_dir = AUI_DOCK_RIGHT
+                elif pt.y >= pr.y+ pr.height - new_row_pixels_y and pt.y < pr.y + pr.height:
+                    insert_dir = AUI_DOCK_BOTTOM
+                else:
+                    return False, target
+
+                insert_row = GetMaxRow(panes, insert_dir, insert_layer) + 1
+            
+        if insert_dock_row:
+        
+            panes = DoInsertDockRow(panes, insert_dir, insert_layer, insert_row)
+            drop.Dock().Direction(insert_dir).Layer(insert_layer). \
+                Row(insert_row).Position(0)
+                
+            return self.ProcessDockResult(target, drop)
+
+        # determine the mouse offset and the pane size, both in the
+        # direction of the dock itself, and perpendicular to the dock
+
+        if part.orientation == wx.VERTICAL:
+        
+            offset = pt.y - part.rect.y
+            size = part.rect.GetHeight()
+        
+        else:
+        
+            offset = pt.x - part.rect.x
+            size = part.rect.GetWidth()
+        
+        drop_position = part.pane.dock_pos
+
+        # if we are in the top/left part of the pane,
+        # insert the pane before the pane being hovered over
+        if offset <= size/2:
+        
+            drop_position = part.pane.dock_pos
+            panes = DoInsertPane(panes,
+                                 part.pane.dock_direction,
+                                 part.pane.dock_layer,
+                                 part.pane.dock_row,
+                                 part.pane.dock_pos)
+
+        # if we are in the bottom/right part of the pane,
+        # insert the pane before the pane being hovered over
+        if offset > size/2:
+        
+            drop_position = part.pane.dock_pos+1
+            panes = DoInsertPane(panes,
+                                 part.pane.dock_direction,
+                                 part.pane.dock_layer,
+                                 part.pane.dock_row,
+                                 part.pane.dock_pos+1)
+        
+
+        drop.Dock(). \
+                     Direction(part.dock.dock_direction). \
+                     Layer(part.dock.dock_layer).Row(part.dock.dock_row). \
+                     Position(drop_position)
+        
+        return self.ProcessDockResult(target, drop)
+
+
+    def DoDropLayer(self, docks, target, dock_direction):
+        """
+        Handles the situation in which `target` is a single dock guide.
+
+        :param `docks`: a list of L{AuiDockInfo} classes;
+        :param `target`: the target pane;
+        :param `dock_direction`: the docking direction.
+        """
+
+        drop = self.CopyTarget(target)
+        
+        if dock_direction == AUI_DOCK_LEFT:
+            drop.Dock().Left()
+            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_LEFT),
+                                     GetMaxLayer(docks, AUI_DOCK_BOTTOM)),
+                                 GetMaxLayer(docks, AUI_DOCK_TOP)) + 1
+
+        elif dock_direction == AUI_DOCK_TOP:
+            drop.Dock().Top()
+            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_TOP),
+                                     GetMaxLayer(docks, AUI_DOCK_LEFT)),
+                                 GetMaxLayer(docks, AUI_DOCK_RIGHT)) + 1
+
+        elif dock_direction == AUI_DOCK_RIGHT:
+            drop.Dock().Right()
+            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_RIGHT),
+                                     GetMaxLayer(docks, AUI_DOCK_TOP)),
+                                 GetMaxLayer(docks, AUI_DOCK_BOTTOM)) + 1
+
+        elif dock_direction == AUI_DOCK_BOTTOM:
+            drop.Dock().Bottom()
+            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_BOTTOM),
+                                     GetMaxLayer(docks, AUI_DOCK_LEFT)),
+                                 GetMaxLayer(docks, AUI_DOCK_RIGHT)) + 1
+
+        else:
+            return False, target
+        
+
+        drop.Dock().Layer(drop_new_layer)
+        return self.ProcessDockResult(target, drop)
+
+
+    def DoDropPane(self, panes, target, dock_direction, dock_layer, dock_row, dock_pos):
+        """
+        Drop a pane in the interface.
+
+        :param `panes`: a list of L{AuiPaneInfo} classes;
+        :param `target`: the target pane;
+        :param `dock_direction`: the docking direction;
+        :param `dock_layer`: the docking layer;
+        :param `dock_row`: the docking row;
+        :param `dock_pos`: the docking position.
+        """
+        
+        drop = self.CopyTarget(target)
+        panes = DoInsertPane(panes, dock_direction, dock_layer, dock_row, dock_pos)
+
+        drop.Dock().Direction(dock_direction).Layer(dock_layer).Row(dock_row).Position(dock_pos)
+        return self.ProcessDockResult(target, drop)
+
+
+    def DoDropRow(self, panes, target, dock_direction, dock_layer, dock_row):
+        """
+        Insert a row in the interface before dropping.
+
+        :param `panes`: a list of L{AuiPaneInfo} classes;
+        :param `target`: the target pane;
+        :param `dock_direction`: the docking direction;
+        :param `dock_layer`: the docking layer;
+        :param `dock_row`: the docking row.
+        """
+        
+        drop = self.CopyTarget(target)
+        panes = DoInsertDockRow(panes, dock_direction, dock_layer, dock_row)
+
+        drop.Dock().Direction(dock_direction).Layer(dock_layer).Row(dock_row).Position(0)
+        return self.ProcessDockResult(target, drop)
+
+
+    def ShowHint(self, rect):
+        """
+        Shows the AUI hint window.
+
+        :param `rect`: the hint rect calculated in advance.
+        """
+
+        if rect == self._last_hint:
+            return
+
+        if self._agwFlags & AUI_MGR_RECTANGLE_HINT and wx.Platform != "__WXMAC__":
+
+            if self._last_hint != rect:
+                # remove the last hint rectangle
+                self._last_hint = wx.Rect(*rect)
+                self._frame.Refresh()
+                self._frame.Update()
+            
+            screendc = wx.ScreenDC()
+            clip = wx.Region(1, 1, 10000, 10000)
+
+            # clip all floating windows, so we don't draw over them
+            for pane in self._panes:            
+                if pane.IsFloating() and pane.frame.IsShown():
+                
+                    rect2 = wx.Rect(*pane.frame.GetRect())
+                    if wx.Platform == "__WXGTK__":
+                        # wxGTK returns the client size, not the whole frame size
+                        rect2.width += 15
+                        rect2.height += 35
+                        rect2.Inflate(5, 5)
+
+                    clip.SubtractRect(rect2)
+                
+            # As we can only hide the hint by redrawing the managed window, we
+            # need to clip the region to the managed window too or we get
+            # nasty redrawn problems.
+            clip.IntersectRect(self._frame.GetRect())
+            screendc.SetClippingRegionAsRegion(clip)
+
+            stipple = PaneCreateStippleBitmap()
+            brush = wx.BrushFromBitmap(stipple)
+            screendc.SetBrush(brush)
+            screendc.SetPen(wx.TRANSPARENT_PEN)
+            screendc.DrawRectangle(rect.x, rect.y, 5, rect.height)
+            screendc.DrawRectangle(rect.x+5, rect.y, rect.width-10, 5)
+            screendc.DrawRectangle(rect.x+rect.width-5, rect.y, 5, rect.height)
+            screendc.DrawRectangle(rect.x+5, rect.y+rect.height-5, rect.width-10, 5)
+            RefreshDockingGuides(self._guides)
+
+            return
+            
+        if not self._hint_window:
+            self.CreateHintWindow()
+
+        if self._hint_window:
+            self._hint_window.SetRect(rect)
+            self._hint_window.Show()
+
+        self._hint_fadeamt = self._hint_fademax
+
+        if self._agwFlags & AUI_MGR_HINT_FADE:
+            self._hint_fadeamt = 0
+            self._hint_window.SetTransparent(self._hint_fadeamt)
+
+        if self._action == actionDragFloatingPane and self._action_window:
+            self._action_window.SetFocus()
+
+        if self._hint_fadeamt != self._hint_fademax: #  Only fade if we need to
+            # start fade in timer
+            self._hint_fadetimer.Start(5)
+
+        self._last_hint = wx.Rect(*rect)
+        
+
+    def HideHint(self):
+        """ Hides a transparent window hint if there is one. """
+
+        # hides a transparent window hint if there is one
+        if self._hint_window:
+            self._hint_window.Hide()
+
+        self._hint_fadetimer.Stop()
+        self._last_hint = wx.Rect()
+        
+
+    def IsPaneButtonVisible(self, part):
+        """
+        Returns whether a pane button in the pane caption is visible.
+
+        :param `part`: the UI part to analyze.
+        """
+
+        captionRect = wx.Rect()
+
+        for temp_part in self._uiparts:
+            if temp_part.pane == part.pane and \
+               temp_part.type == AuiDockUIPart.typeCaption:
+                captionRect = temp_part.rect
+                break
+
+        return captionRect.ContainsRect(part.rect)
+
+
+    def DrawPaneButton(self, dc, part, pt):
+        """
+        Draws a pane button in the caption (convenience function).
+
+        :param `dc`: a `wx.DC` device context object;
+        :param `part`: the UI part to analyze;
+        :param `pt`: a `wx.Point` object, specifying the mouse location.
+        """
+
+        if not self.IsPaneButtonVisible(part):
+            return
+
+        state = AUI_BUTTON_STATE_NORMAL
+
+        if part.rect.Contains(pt):
+
+            if _VERSION_STRING < "2.9":
+                leftDown = wx.GetMouseState().LeftDown()
+            else:
+                leftDown = wx.GetMouseState().LeftIsDown()
+
+            if leftDown:
+                state = AUI_BUTTON_STATE_PRESSED
+            else:
+                state = AUI_BUTTON_STATE_HOVER
+
+        self._art.DrawPaneButton(dc, self._frame, part.button.button_id,
+                                 state, part.rect, part.pane)
+
+    
+    def RefreshButton(self, part):
+        """
+        Refreshes a pane button in the caption.
+
+        :param `part`: the UI part to analyze.
+        """
+        
+        rect = wx.Rect(*part.rect)
+        rect.Inflate(2, 2)
+        self._frame.Refresh(True, rect)
+        self._frame.Update()
+
+
+    def RefreshCaptions(self):
+        """ Refreshes all pane captions. """
+
+        for part in self._uiparts:
+            if part.type == AuiDockUIPart.typeCaption:
+                self._frame.Refresh(True, part.rect)
+                self._frame.Update()
+
+
+    def CalculateHintRect(self, pane_window, pt, offset):
+        """
+        Calculates the drop hint rectangle.
+
+        The method first calls L{DoDrop} to determine the exact position the pane would
+        be at were if dropped. If the pane would indeed become docked at the
+        specified drop point, the the rectangle hint will be returned in
+        screen coordinates. Otherwise, an empty rectangle is returned.
+
+        :param `pane_window`: it is the window pointer of the pane being dragged;
+        :param `pt`: is the mouse position, in client coordinates;
+        :param `offset`: describes the offset that the mouse is from the upper-left
+         corner of the item being dragged.
+        """
+        
+        # we need to paint a hint rectangle to find out the exact hint rectangle,
+        # we will create a new temporary layout and then measure the resulting
+        # rectangle we will create a copy of the docking structures (self._docks)
+        # so that we don't modify the real thing on screen
+
+        rect = wx.Rect()
+        pane = self.GetPane(pane_window)
+        
+        attrs = self.GetAttributes(pane)
+        hint = AuiPaneInfo()
+        hint = self.SetAttributes(hint, attrs)
+        
+        if hint.name != "__HINT__":
+            self._oldname = hint.name
+            
+        hint.name = "__HINT__"
+        hint.PaneBorder(True)
+        hint.Show()
+
+        if not hint.IsOk():
+            hint.name = self._oldname
+            return rect
+
+        docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
+
+        # remove any pane already there which bears the same window
+        # this happens when you are moving a pane around in a dock
+        for ii in xrange(len(panes)):
+            if panes[ii].window == pane_window:
+                docks = RemovePaneFromDocks(docks, panes[ii])
+                panes.pop(ii)
+                break
+
+        # find out where the new pane would be
+        allow, hint = self.DoDrop(docks, panes, hint, pt, offset)
+
+        if not allow:
+            return rect
+        
+        panes.append(hint)
+
+        sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False)
+        
+        client_size = self._frame.GetClientSize()
+        sizer.SetDimension(0, 0, client_size.x, client_size.y)
+        sizer.Layout()
+
+        sought = "__HINT__"
+        
+        # For a notebook page, actually look for the noteboot itself.
+        if hint.IsNotebookPage():
+            id = hint.notebook_id
+            for pane in panes:
+                if pane.IsNotebookControl() and pane.notebook_id==id:
+                    sought = pane.name
+                    break
+
+        for part in uiparts:
+            if part.pane and part.pane.name == sought:    
+                rect.Union(wx.RectPS(part.sizer_item.GetPosition(),
+                                     part.sizer_item.GetSize()))
+
+        sizer.Destroy()
+
+        # check for floating frame ...
+        if rect.IsEmpty():
+            for p in panes:
+                if p.name == sought and p.IsFloating():
+                    return wx.RectPS(p.floating_pos, p.floating_size)
+    
+        if rect.IsEmpty():
+            return rect
+
+        # actually show the hint rectangle on the screen
+        rect.x, rect.y = self._frame.ClientToScreen((rect.x, rect.y))
+        if self._frame.GetLayoutDirection() == wx.Layout_RightToLeft:
+            # Mirror rectangle in RTL mode
+            rect.x -= rect.GetWidth()
+
+        return rect
+
+
+    def DrawHintRect(self, pane_window, pt, offset):
+        """
+        Calculates the hint rectangle by calling
+        L{CalculateHintRect}. If there is a rectangle, it shows it
+        by calling L{ShowHint}, otherwise it hides any hint
+        rectangle currently shown.
+
+        :param `pane_window`: it is the window pointer of the pane being dragged;
+        :param `pt`: is the mouse position, in client coordinates;
+        :param `offset`: describes the offset that the mouse is from the upper-left
+         corner of the item being dragged.
+        """
+
+        rect = self.CalculateHintRect(pane_window, pt, offset)
+
+        if rect.IsEmpty():
+            self.HideHint()
+            self._hint_rect = wx.Rect()
+        else:
+            self.ShowHint(rect)
+            self._hint_rect = wx.Rect(*rect)
+
+
+    def GetPartSizerRect(self, uiparts):
+        """
+        Returns the rectangle surrounding the specified UI parts.
+
+        :param `uiparts`: UI parts.
+        """
+
+        rect = wx.Rect()
+
+        for part in self._uiparts:
+            if part.pane and part.pane.name == "__HINT__":
+                rect.Union(wx.RectPS(part.sizer_item.GetPosition(),
+                                     part.sizer_item.GetSize()))
+
+        return rect
+
+
+    def GetAttributes(self, pane):
+        """
+        Returns all the attributes of a L{AuiPaneInfo}.
+
+        :param `pane`: a L{AuiPaneInfo} instance.
+        """
+
+        attrs = []
+        attrs.extend([pane.window, pane.frame, pane.state, pane.dock_direction,
+                      pane.dock_layer, pane.dock_pos, pane.dock_row, pane.dock_proportion,
+                      pane.floating_pos, pane.floating_size, pane.best_size,
+                      pane.min_size, pane.max_size, pane.caption, pane.name,
+                      pane.buttons, pane.rect, pane.icon, pane.notebook_id,
+                      pane.transparent, pane.snapped, pane.minimize_mode])
+
+        return attrs
+    
+
+    def SetAttributes(self, pane, attrs):
+        """
+        Sets all the attributes contained in `attrs` to a L{AuiPaneInfo}.
+
+        :param `pane`: a L{AuiPaneInfo} instance;
+        :param `attrs`: a list of attributes.
+        """
+        
+        pane.window = attrs[0]
+        pane.frame = attrs[1]
+        pane.state = attrs[2]
+        pane.dock_direction = attrs[3]
+        pane.dock_layer = attrs[4]
+        pane.dock_pos = attrs[5]
+        pane.dock_row = attrs[6]
+        pane.dock_proportion = attrs[7]
+        pane.floating_pos = attrs[8]
+        pane.floating_size = attrs[9]
+        pane.best_size = attrs[10]
+        pane.min_size = attrs[11]
+        pane.max_size = attrs[12]
+        pane.caption = attrs[13]
+        pane.name = attrs[14]
+        pane.buttons = attrs[15]
+        pane.rect = attrs[16]
+        pane.icon = attrs[17]
+        pane.notebook_id = attrs[18]
+        pane.transparent = attrs[19]
+        pane.snapped = attrs[20]
+        pane.minimize_mode = attrs[21]
+
+        return pane
+
+     
+    def OnFloatingPaneResized(self, wnd, size):
+        """
+        Handles the resizing of a floating pane.
+
+        :param `wnd`: a `wx.Window` derived window, managed by the pane;
+        :param `size`: a `wx.Size` object, specifying the new pane floating size.
+        """
+
+        # try to find the pane
+        pane = self.GetPane(wnd)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        if pane.frame:
+            indx = self._panes.index(pane)
+            pane.floating_pos = pane.frame.GetPosition()
+            pane.floating_size = size
+            self._panes[indx] = pane
+            if pane.IsSnappable():
+                self.SnapPane(pane, pane.floating_pos, pane.floating_size, True)
+            
+
+    def OnFloatingPaneClosed(self, wnd, event):
+        """
+        Handles the close event of a floating pane.
+
+        :param `wnd`: a `wx.Window` derived window, managed by the pane;
+        :param `event`: a `wx.CloseEvent` to be processed.
+        """
+        
+        # try to find the pane
+        pane = self.GetPane(wnd)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        # fire pane close event
+        e = AuiManagerEvent(wxEVT_AUI_PANE_CLOSE)
+        e.SetPane(pane)
+        e.SetCanVeto(event.CanVeto())
+        self.ProcessMgrEvent(e)
+
+        if e.GetVeto():
+            event.Veto()
+            return
+        else:
+            # close the pane, but check that it
+            # still exists in our pane array first
+            # (the event handler above might have removed it)
+
+            check = self.GetPane(wnd)
+            if check.IsOk():
+                self.ClosePane(pane)
+        
+
+    def OnFloatingPaneActivated(self, wnd):
+        """
+        Handles the activation event of a floating pane.
+
+        :param `wnd`: a `wx.Window` derived window, managed by the pane.
+        """
+        
+        pane = self.GetPane(wnd)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
+            ret, self._panes = SetActivePane(self._panes, wnd)
+            self.RefreshCaptions()
+            self.FireEvent(wxEVT_AUI_PANE_ACTIVATED, wnd, canVeto=False)
+
+
+    def OnFloatingPaneMoved(self, wnd, eventOrPt):
+        """
+        Handles the move event of a floating pane.
+
+        :param `wnd`: a `wx.Window` derived window, managed by the pane;
+        :param `eventOrPt`: a `wx.MoveEvent` to be processed or an instance of `wx.Point`.
+        """
+        
+        pane = self.GetPane(wnd)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        if not pane.IsSnappable():
+            return
+
+        if isinstance(eventOrPt, wx.Point):
+            pane_pos = wx.Point(*eventOrPt)
+        else:
+            pane_pos = eventOrPt.GetPosition()
+
+        pane_size = pane.floating_size
+
+        self.SnapPane(pane, pane_pos, pane_size, False)
+
+
+    def SnapPane(self, pane, pane_pos, pane_size, toSnap=False):
+        """
+        Snaps a floating pane to one of the main frame sides.
+
+        :param `pane`: a L{AuiPaneInfo} instance;
+        :param `pane_pos`: the new pane floating position;
+        :param `pane_size`: the new pane floating size;
+        :param `toSnap`: a bool variable to check if L{SnapPane} was called from
+         a move event.
+        """
+
+        if self._from_move:
+            return
+        
+        managed_window = self.GetManagedWindow()
+        wnd_pos = managed_window.GetPosition()
+        wnd_size = managed_window.GetSize()
+        snapX, snapY = self._snap_limits
+
+        if not toSnap:
+            pane.snapped = 0
+            if pane.IsLeftSnappable():
+                # Check if we can snap to the left
+                diff = wnd_pos.x - (pane_pos.x + pane_size.x)
+                if -snapX <= diff <= snapX:
+                    pane.snapped = wx.LEFT
+                    pane.floating_pos = wx.Point(wnd_pos.x-pane_size.x, pane_pos.y)
+            elif pane.IsTopSnappable():
+                # Check if we can snap to the top
+                diff = wnd_pos.y - (pane_pos.y + pane_size.y)
+                if -snapY <= diff <= snapY:
+                    pane.snapped = wx.TOP
+                    pane.floating_pos = wx.Point(pane_pos.x, wnd_pos.y-pane_size.y)
+            elif pane.IsRightSnappable():
+                # Check if we can snap to the right
+                diff = pane_pos.x - (wnd_pos.x + wnd_size.x)
+                if -snapX <= diff <= snapX:
+                    pane.snapped = wx.RIGHT
+                    pane.floating_pos = wx.Point(wnd_pos.x + wnd_size.x, pane_pos.y)
+            elif pane.IsBottomSnappable():
+                # Check if we can snap to the bottom
+                diff = pane_pos.y - (wnd_pos.y + wnd_size.y)
+                if -snapY <= diff <= snapY:
+                    pane.snapped = wx.BOTTOM
+                    pane.floating_pos = wx.Point(pane_pos.x, wnd_pos.y + wnd_size.y)
+
+        self.RepositionPane(pane, wnd_pos, wnd_size)
+
+
+    def RepositionPane(self, pane, wnd_pos, wnd_size):
+        """
+        Repositions a pane after the main frame has been moved/resized.
+        
+        :param `pane`: a L{AuiPaneInfo} instance;
+        :param `wnd_pos`: the main frame position;
+        :param `wnd_size`: the main frame size.
+        """
+
+        pane_pos = pane.floating_pos
+        pane_size = pane.floating_size
+
+        snap = pane.snapped
+        if snap == wx.LEFT:
+            floating_pos = wx.Point(wnd_pos.x - pane_size.x, pane_pos.y)
+        elif snap == wx.TOP:
+            floating_pos = wx.Point(pane_pos.x, wnd_pos.y - pane_size.y)
+        elif snap == wx.RIGHT:
+            floating_pos = wx.Point(wnd_pos.x + wnd_size.x, pane_pos.y)
+        elif snap == wx.BOTTOM:
+            floating_pos = wx.Point(pane_pos.x, wnd_pos.y + wnd_size.y)
+
+        if snap:
+            if pane_pos != floating_pos:
+                pane.floating_pos = floating_pos
+                self._from_move = True
+                pane.frame.SetPosition(pane.floating_pos)
+                self._from_move = False
+
+            
+    def OnGripperClicked(self, pane_window, start, offset):
+        """
+        Handles the mouse click on the pane gripper.
+
+        :param `pane_window`: a `wx.Window` derived window, managed by the pane;
+        :param `start`: a `wx.Point` object, specifying the clicking position;
+        :param `offset`: an offset point from the `start` position.
+        """
+
+        # try to find the pane
+        paneInfo = self.GetPane(pane_window)
+
+        if not paneInfo.IsOk():
+            raise Exception("Pane window not found")
+
+        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
+            # set the caption as active
+            ret, self._panes = SetActivePane(self._panes, pane_window)
+            self.RefreshCaptions()
+            self.FireEvent(wxEVT_AUI_PANE_ACTIVATED, pane_window, canVeto=False)
+        
+        self._action_part = None
+        self._action_pane = paneInfo
+        self._action_window = pane_window
+        self._action_start = start
+        self._action_offset = offset
+        self._toolbar_action_offset = wx.Point(*self._action_offset)
+        
+        self._frame.CaptureMouse()
+
+        if paneInfo.IsDocked():
+            self._action = actionClickCaption
+        else:
+            if paneInfo.IsToolbar():
+                self._action = actionDragToolbarPane
+            else:
+                self._action = actionDragFloatingPane
+
+            if paneInfo.frame:
+            
+                windowPt = paneInfo.frame.GetRect().GetTopLeft()
+                originPt = paneInfo.frame.ClientToScreen(wx.Point())
+                self._action_offset += originPt - windowPt
+                self._toolbar_action_offset = wx.Point(*self._action_offset)
+
+                if self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
+                    paneInfo.frame.SetTransparent(150)
+            
+            if paneInfo.IsToolbar():
+                self._frame.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
+        
+
+    def OnRender(self, event):        
+        """
+        Draws all of the pane captions, sashes,
+        backgrounds, captions, grippers, pane borders and buttons.
+        It renders the entire user interface. It binds the ``EVT_AUI_RENDER`` event.
+
+        :param `event`: an instance of L{AuiManagerEvent}.
+        """
+
+        # if the frame is about to be deleted, don't bother
+        if not self._frame or self._frame.IsBeingDeleted():
+            return
+        
+        if not self._frame.GetSizer():
+            return
+
+        mouse = wx.GetMouseState()
+        mousePos = wx.Point(mouse.GetX(), mouse.GetY())
+        point = self._frame.ScreenToClient(mousePos)
+        art = self._art
+
+        dc = event.GetDC()
+        
+        for part in self._uiparts:
+        
+            # don't draw hidden pane items or items that aren't windows
+            if part.sizer_item and ((not part.sizer_item.IsWindow() and \
+                                     not part.sizer_item.IsSpacer() and \
+                                     not part.sizer_item.IsSizer()) or \
+                                    not part.sizer_item.IsShown()):
+            
+                continue
+            
+            ptype = part.type
+                    
+            if ptype in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
+                art.DrawSash(dc, self._frame, part.orientation, part.rect)
+
+            elif ptype == AuiDockUIPart.typeBackground:
+                art.DrawBackground(dc, self._frame, part.orientation, part.rect)
+
+            elif ptype == AuiDockUIPart.typeCaption:
+                art.DrawCaption(dc, self._frame, part.pane.caption, part.rect, part.pane)
+
+            elif ptype == AuiDockUIPart.typeGripper:
+                art.DrawGripper(dc, self._frame, part.rect, part.pane)
+
+            elif ptype == AuiDockUIPart.typePaneBorder:
+                art.DrawBorder(dc, self._frame, part.rect, part.pane)
+
+            elif ptype == AuiDockUIPart.typePaneButton:                
+                self.DrawPaneButton(dc, part, point)
+
+
+    def Repaint(self, dc=None):
+        """
+        Repaints the entire frame decorations (sashes, borders, buttons and so on).
+        It renders the entire user interface.
+
+        :param `dc`: if not ``None``, an instance of `wx.PaintDC`.
+        """
+        
+        w, h = self._frame.GetClientSize()
+
+        # Figure out which dc to use; if one
+        # has been specified, use it, otherwise
+        # make a client dc
+        if dc is None:
+            client_dc = wx.ClientDC(self._frame)
+            dc = client_dc
+
+        # If the frame has a toolbar, the client area
+        # origin will not be (0, 0).
+        pt = self._frame.GetClientAreaOrigin()
+        if pt.x != 0 or pt.y != 0:
+            dc.SetDeviceOrigin(pt.x, pt.y)
+
+        # Render all the items
+        self.Render(dc)
+
+                
+    def Render(self, dc):
+        """
+        Fires a render event, which is normally handled by
+        L{OnRender}. This allows the render function to
+        be overridden via the render event.
+
+        This can be useful for painting custom graphics in the main window.
+        Default behavior can be invoked in the overridden function by calling
+        L{OnRender}.
+
+        :param `dc`: a `wx.DC` device context object.        
+        """
+
+        e = AuiManagerEvent(wxEVT_AUI_RENDER)
+        e.SetManager(self)
+        e.SetDC(dc)
+        self.ProcessMgrEvent(e)
+
+
+    def OnCaptionDoubleClicked(self, pane_window):
+        """
+        Handles the mouse double click on the pane caption.
+
+        :param `pane_window`: a `wx.Window` derived window, managed by the pane.
+        """
+
+        # try to find the pane
+        paneInfo = self.GetPane(pane_window)
+        if not paneInfo.IsOk():
+            raise Exception("Pane window not found")
+
+        if not paneInfo.IsFloatable() or not paneInfo.IsDockable() or \
+           self._agwFlags & AUI_MGR_ALLOW_FLOATING == 0:
+            return
+
+        indx = self._panes.index(paneInfo)
+        win_rect = None
+        
+        if paneInfo.IsFloating():
+            if paneInfo.name.startswith("__floating__"):
+                # It's a floating tab from a AuiNotebook
+                notebook = paneInfo.window.__aui_notebook__
+                notebook.ReDockPage(paneInfo)
+                self.Update()
+                return
+            else:
+
+                e = self.FireEvent(wxEVT_AUI_PANE_DOCKING, paneInfo, canVeto=True)
+                if e.GetVeto():
+                    self.HideHint()
+                    ShowDockingGuides(self._guides, False)
+                    return
+                
+                win_rect = paneInfo.frame.GetRect()
+                paneInfo.Dock()
+                if paneInfo.IsToolbar():
+                    paneInfo = self.SwitchToolBarOrientation(paneInfo)
+
+                e = self.FireEvent(wxEVT_AUI_PANE_DOCKED, paneInfo, canVeto=False)
+
+        else:
+
+            e = self.FireEvent(wxEVT_AUI_PANE_FLOATING, paneInfo, canVeto=True)
+            if e.GetVeto():
+                return
+
+            # float the window
+            if paneInfo.IsMaximized():
+                self.RestorePane(paneInfo)
+            
+            if paneInfo.floating_pos == wx.Point(-1, -1):
+                captionSize = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
+                paneInfo.floating_pos = pane_window.GetScreenPosition()
+                paneInfo.floating_pos.y -= captionSize
+
+            paneInfo.Float()
+            e = self.FireEvent(wxEVT_AUI_PANE_FLOATED, paneInfo, canVeto=False)
+
+        self._panes[indx] = paneInfo
+        self.Update()
+
+        if win_rect and self._agwFlags & AUI_MGR_ANIMATE_FRAMES:
+            paneInfo = self.GetPane(pane_window)
+            pane_rect = paneInfo.window.GetScreenRect()
+            self.AnimateDocking(win_rect, pane_rect)
+
+
+    def OnPaint(self, event):
+        """
+        Handles the ``wx.EVT_PAINT`` event for L{AuiManager}.
+
+        :param `event`: an instance of `wx.PaintEvent` to be processed.
+        """
+        
+        dc = wx.PaintDC(self._frame)
+        self.Repaint(dc)
+                
+
+    def OnEraseBackground(self, event):
+        """
+        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiManager}.
+
+        :param `event`: `wx.EraseEvent` to be processed.
+
+        :note: This is intentionally empty (excluding wxMAC) to reduce
+         flickering while drawing.
+        """
+        
+        if wx.Platform == "__WXMAC__":
+            event.Skip()
+
+
+    def OnSize(self, event):
+        """
+        Handles the ``wx.EVT_SIZE`` event for L{AuiManager}.
+
+        :param `event`: a `wx.SizeEvent` to be processed.
+        """
+        
+        skipped = False
+        if isinstance(self._frame, AuiFloatingFrame) and self._frame.IsShownOnScreen():
+            skipped = True
+            event.Skip()
+
+        if self._frame:
+                
+            self.DoFrameLayout()
+            if wx.Platform == "__WXMAC__":
+                self._frame.Refresh()
+            else:
+                self.Repaint()
+            
+            if isinstance(self._frame, wx.MDIParentFrame) or isinstance(self._frame, tabmdi.AuiMDIClientWindow) \
+               or isinstance(self._frame, tabmdi.AuiMDIParentFrame):
+                # for MDI parent frames, this event must not
+                # be "skipped".  In other words, the parent frame
+                # must not be allowed to resize the client window
+                # after we are finished processing sizing changes
+                return
+
+        if not skipped:
+            event.Skip()
+
+        # For the snap to screen...
+        self.OnMove(None)
+        
+
+    def OnFindManager(self, event):
+        """
+        Handles the ``EVT_AUI_FIND_MANAGER`` event for L{AuiManager}.
+
+        :param `event`: a L{AuiManagerEvent} event to be processed.
+        """
+        
+        # Initialize to None
+        event.SetManager(None)
+        
+        if not self._frame:
+            return
+        
+        # See it this window wants to overwrite
+        self._frame.ProcessEvent(event)
+
+        # if no, it must be us
+        if not event.GetManager():
+           event.SetManager(self)
+       
+
+    def OnSetCursor(self, event):
+        """
+        Handles the ``wx.EVT_SET_CURSOR`` event for L{AuiManager}.
+
+        :param `event`: a `wx.SetCursorEvent` to be processed.
+        """
+        
+        # determine cursor
+        part = self.HitTest(event.GetX(), event.GetY())
+        cursor = wx.NullCursor
+
+        if part:
+            if part.type in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
+
+                if not self.CheckMovableSizer(part):
+                    return
+                
+                if part.orientation == wx.VERTICAL:
+                    cursor = wx.StockCursor(wx.CURSOR_SIZEWE)
+                else:
+                    cursor = wx.StockCursor(wx.CURSOR_SIZENS)
+            
+            elif part.type == AuiDockUIPart.typeGripper:
+                cursor = wx.StockCursor(wx.CURSOR_SIZING)
+
+        event.SetCursor(cursor)
+
+
+    def UpdateButtonOnScreen(self, button_ui_part, event):
+        """
+        Updates/redraws the UI part containing a pane button.
+
+        :param `button_ui_part`: the UI part the button belongs to;
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        hit_test = self.HitTest(*event.GetPosition())
+
+        if not hit_test or not button_ui_part:
+            return
+    
+        state = AUI_BUTTON_STATE_NORMAL
+        
+        if hit_test == button_ui_part:
+            if event.LeftDown():
+                state = AUI_BUTTON_STATE_PRESSED
+            else:
+                state = AUI_BUTTON_STATE_HOVER
+        else:
+            if event.LeftDown():
+                state = AUI_BUTTON_STATE_HOVER
+        
+        # now repaint the button with hover state
+        cdc = wx.ClientDC(self._frame)
+
+        # if the frame has a toolbar, the client area
+        # origin will not be (0,0).
+        pt = self._frame.GetClientAreaOrigin()
+        if pt.x != 0 or pt.y != 0:
+            cdc.SetDeviceOrigin(pt.x, pt.y)
+
+        if hit_test.pane:        
+            self._art.DrawPaneButton(cdc, self._frame,
+                      button_ui_part.button.button_id,
+                      state,
+                      button_ui_part.rect, hit_test.pane)
+
+
+    def OnLeftDown(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_DOWN`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        part = self.HitTest(*event.GetPosition())
+
+        if not part:
+            event.Skip()
+            return
+        
+        self._currentDragItem = -1
+        
+        if part.type in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
+        
+            if not self.CheckMovableSizer(part):
+                return
+
+            self._action = actionResize
+            self._action_part = part
+            self._action_pane = None
+            self._action_rect = wx.Rect()
+            self._action_start = wx.Point(event.GetX(), event.GetY())
+            self._action_offset = wx.Point(event.GetX() - part.rect.x,
+                                           event.GetY() - part.rect.y)
+
+            # draw the resize hint
+            rect = wx.RectPS(self._frame.ClientToScreen(part.rect.GetPosition()),
+                             part.rect.GetSize())
+
+            self._action_rect = wx.Rect(*rect)
+
+            if not AuiManager_HasLiveResize(self):
+                if wx.Platform == "__WXMAC__":
+                    dc = wx.ClientDC(self._frame)
+                else:
+                    dc = wx.ScreenDC()
+                    
+                DrawResizeHint(dc, rect)
+
+            self._frame.CaptureMouse()
+        
+        elif part.type == AuiDockUIPart.typePaneButton:
+            if self.IsPaneButtonVisible(part):
+                self._action = actionClickButton
+                self._action_part = part
+                self._action_pane = None
+                self._action_start = wx.Point(*event.GetPosition())
+                self._frame.CaptureMouse()
+
+                self.RefreshButton(part)
+        
+        elif part.type in [AuiDockUIPart.typeCaption, AuiDockUIPart.typeGripper]:
+
+            # if we are managing a AuiFloatingFrame window, then
+            # we are an embedded AuiManager inside the AuiFloatingFrame.
+            # We want to initiate a toolbar drag in our owner manager
+            if isinstance(part.pane.window.GetParent(), AuiFloatingFrame):
+                rootManager = GetManager(part.pane.window)
+            else:
+                rootManager = self
+
+            offset = wx.Point(event.GetX() - part.rect.x, event.GetY() - part.rect.y)
+            rootManager.OnGripperClicked(part.pane.window, event.GetPosition(), offset)
+        
+        if wx.Platform != "__WXMAC__":
+            event.Skip()
+
+
+    def OnLeftDClick(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_DCLICK`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        part = self.HitTest(event.GetX(), event.GetY())
+
+        if part and part.type == AuiDockUIPart.typeCaption:
+            if isinstance(part.pane.window.GetParent(), AuiFloatingFrame):
+                rootManager = GetManager(part.pane.window)
+            else:
+                rootManager = self
+                
+            rootManager.OnCaptionDoubleClicked(part.pane.window)
+            
+        elif part and part.type in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
+            # Handles double click on AuiNotebook sashes to unsplit
+            sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+            for child in part.cont_sizer.GetChildren():
+                if child.IsSizer():
+                    win = child.GetSizer().GetContainingWindow()
+                    if isinstance(win, auibook.AuiNotebook):
+                        win.UnsplitDClick(part, sash_size, event.GetPosition())
+                        break
+                
+        event.Skip()
+
+
+    def DoEndResizeAction(self, event):
+        """
+        Ends a resize action, or for live update, resizes the sash.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        clientPt = event.GetPosition()
+        screenPt = self._frame.ClientToScreen(clientPt)
+
+        return self.RestrictResize(clientPt, screenPt, createDC=False)
+
+
+    def RestrictResize(self, clientPt, screenPt, createDC):
+        """ Common method between L{DoEndResizeAction} and L{OnLeftUp_Resize}. """
+
+        dock = self._action_part.dock
+        pane = self._action_part.pane
+
+        if createDC:
+            if wx.Platform == "__WXMAC__":
+                dc = wx.ClientDC(self._frame)
+            else:
+                dc = wx.ScreenDC()
+
+            DrawResizeHint(dc, self._action_rect)
+            self._action_rect = wx.Rect()
+        
+        newPos = clientPt - self._action_offset
+
+        if self._action_part.type == AuiDockUIPart.typeDockSizer:
+            minPix, maxPix = self.CalculateDockSizerLimits(dock)
+        else:
+            if not self._action_part.pane:
+                return
+            minPix, maxPix = self.CalculatePaneSizerLimits(dock, pane)
+
+        if self._action_part.orientation == wx.HORIZONTAL:
+            newPos.y = Clip(newPos.y, minPix, maxPix)
+        else:
+            newPos.x = Clip(newPos.x, minPix, maxPix)
+
+        if self._action_part.type == AuiDockUIPart.typeDockSizer:
+        
+            partnerDock = self.GetPartnerDock(dock)
+            sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
+            new_dock_size = 0
+            direction = dock.dock_direction
+
+            if direction == AUI_DOCK_LEFT:
+                new_dock_size = newPos.x - dock.rect.x
+
+            elif direction == AUI_DOCK_TOP:
+                new_dock_size = newPos.y - dock.rect.y
+
+            elif direction == AUI_DOCK_RIGHT:
+                new_dock_size = dock.rect.x + dock.rect.width - newPos.x - sash_size
+
+            elif direction == AUI_DOCK_BOTTOM:
+                new_dock_size = dock.rect.y + dock.rect.height - newPos.y - sash_size
+
+            deltaDockSize = new_dock_size - dock.size
+
+            if partnerDock:
+                if deltaDockSize > partnerDock.size - sash_size:
+                    deltaDockSize = partnerDock.size - sash_size
+
+                partnerDock.size -= deltaDockSize
+            
+            dock.size += deltaDockSize
+            self.Update()
+        
+        else:
+        
+            # determine the new pixel size that the user wants
+            # this will help us recalculate the pane's proportion
+            if dock.IsHorizontal():
+                oldPixsize = pane.rect.width
+                newPixsize = oldPixsize + newPos.x - self._action_part.rect.x
+                    
+            else:            
+                oldPixsize = pane.rect.height
+                newPixsize = oldPixsize + newPos.y - self._action_part.rect.y
+                                
+            totalPixsize, totalProportion = self.GetTotalPixSizeAndProportion(dock)
+            partnerPane = self.GetPartnerPane(dock, pane)
+
+            # prevent division by zero
+            if totalPixsize <= 0 or totalProportion <= 0 or not partnerPane:
+                return
+
+            # adjust for the surplus
+            while (oldPixsize > 0 and totalPixsize > 10 and \
+                  oldPixsize*totalProportion/totalPixsize < pane.dock_proportion):
+            
+                totalPixsize -= 1
+
+            # calculate the new proportion of the pane
+            
+            newProportion = newPixsize*totalProportion/totalPixsize
+            newProportion = Clip(newProportion, 1, totalProportion)
+            deltaProp = newProportion - pane.dock_proportion
+
+            if partnerPane.dock_proportion - deltaProp < 1:
+                deltaProp = partnerPane.dock_proportion - 1
+                newProportion = pane.dock_proportion + deltaProp
+            
+            # borrow the space from our neighbor pane to the
+            # right or bottom (depending on orientation)
+            partnerPane.dock_proportion -= deltaProp
+            pane.dock_proportion = newProportion
+
+            self.Update()
+        
+        return True
+    
+
+    def OnLeftUp(self, event):
+        """
+        Handles the ``wx.EVT_LEFT_UP`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        if self._action == actionResize:
+##            self._frame.Freeze()
+            self.OnLeftUp_Resize(event)
+##            self._frame.Thaw()
+        
+        elif self._action == actionClickButton:
+            self.OnLeftUp_ClickButton(event)
+        
+        elif self._action == actionDragFloatingPane:
+            self.OnLeftUp_DragFloatingPane(event)
+        
+        elif self._action == actionDragToolbarPane:
+            self.OnLeftUp_DragToolbarPane(event)
+            
+        else:
+            event.Skip()        
+
+        if self._frame.HasCapture():
+            self._frame.ReleaseMouse()
+            
+        self._action = actionNone
+
+
+    def OnMotion(self, event):
+        """
+        Handles the ``wx.EVT_MOTION`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        if self._action == actionResize:
+            self.OnMotion_Resize(event)
+        
+        elif self._action == actionClickCaption:
+            self.OnMotion_ClickCaption(event)
+        
+        elif self._action == actionDragFloatingPane:
+            self.OnMotion_DragFloatingPane(event)
+        
+        elif self._action == actionDragToolbarPane:
+            self.OnMotion_DragToolbarPane(event)
+        
+        else:
+            self.OnMotion_Other(event)
+                        
+    
+    def OnLeaveWindow(self, event):
+        """
+        Handles the ``wx.EVT_LEAVE_WINDOW`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        if self._hover_button:
+            self.RefreshButton(self._hover_button)
+            self._hover_button = None
+
+
+    def OnCaptureLost(self, event):
+        """
+        Handles the ``wx.EVT_MOUSE_CAPTURE_LOST`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MouseCaptureLostEvent` to be processed.
+        """
+        
+        # cancel the operation in progress, if any
+        if self._action != actionNone:
+            self._action = actionNone
+            self.HideHint()
+
+
+    def OnHintFadeTimer(self, event):
+        """
+        Handles the ``wx.EVT_TIMER`` event for L{AuiManager}.
+
+        :param `event`: a `wx.TimerEvent` to be processed.
+        """
+
+        if not self._hint_window or self._hint_fadeamt >= self._hint_fademax:
+            self._hint_fadetimer.Stop()
+            return
+
+        self._hint_fadeamt += 4
+        self._hint_window.SetTransparent(self._hint_fadeamt)
+
+
+    def OnMove(self, event):
+        """
+        Handles the ``wx.EVT_MOVE`` event for L{AuiManager}.
+
+        :param `event`: a `wx.MoveEvent` to be processed.
+        """
+
+        if event is not None:
+            event.Skip()
+
+        if isinstance(self._frame, AuiFloatingFrame) and self._frame.IsShownOnScreen():
+            return
+
+        docked, hAlign, vAlign, monitor = self._is_docked
+        if docked:
+            self.Snap()
+
+        for pane in self._panes:
+            if pane.IsSnappable():
+                if pane.IsFloating() and pane.IsShown():
+                    self.SnapPane(pane, pane.floating_pos, pane.floating_size, True)
+        
+
+    def OnSysColourChanged(self, event):
+        """
+        Handles the ``wx.EVT_SYS_COLOUR_CHANGED`` event for L{AuiManager}.
+
+        :param `event`: a `wx.SysColourChangedEvent` to be processed.
+        """
+        
+        # This event is probably triggered by a theme change 
+        # so we have to re-init the art provider.
+        if self._art:
+            self._art.Init()
+
+        if self._frame:
+            self.Update()
+            self._frame.Refresh()
+            
+
+    def OnChildFocus(self, event):
+        """
+        Handles the ``wx.EVT_CHILD_FOCUS`` event for L{AuiManager}.
+
+        :param `event`: a `wx.ChildFocusEvent` to be processed.
+        """
+
+        # when a child pane has it's focus set, we should change the 
+        # pane's active state to reflect this. (this is only true if 
+        # active panes are allowed by the owner)
+
+        window = event.GetWindow()
+        if isinstance(window, wx.Dialog):
+            # Ignore EVT_CHILD_FOCUS events originating from dialogs not
+            # managed by AUI
+            rootManager = None
+        elif isinstance(window.GetParent(), AuiFloatingFrame):
+            rootManager = GetManager(window)
+        else:
+            rootManager = self
+                
+        if rootManager:
+            rootManager.ActivatePane(window)
+            
+        event.Skip()
+
+
+    def OnMotion_ClickCaption(self, event):
+        """
+        Sub-handler for the L{OnMotion} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        clientPt = event.GetPosition()
+        screenPt = self._frame.ClientToScreen(clientPt)
+
+        drag_x_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_X)
+        drag_y_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_Y)
+
+        if not self._action_pane:
+            return
+
+        # we need to check if the mouse is now being dragged
+        if not (abs(clientPt.x - self._action_start.x) > drag_x_threshold or \
+                abs(clientPt.y - self._action_start.y) > drag_y_threshold):
+        
+            return
+        
+        # dragged -- we need to change the mouse action to 'drag'
+        if self._action_pane.IsToolbar():
+            self._action = actionDragToolbarPane
+            self._action_window = self._action_pane.window
+        
+        elif self._action_pane.IsFloatable() and self._agwFlags & AUI_MGR_ALLOW_FLOATING:
+
+            e = self.FireEvent(wxEVT_AUI_PANE_FLOATING, self._action_pane, canVeto=True)
+            if e.GetVeto():
+                return
+            
+            self._action = actionDragFloatingPane
+
+            # set initial float position
+            self._action_pane.floating_pos = screenPt - self._action_offset
+
+            # float the window
+            if self._action_pane.IsMaximized():
+                self.RestorePane(self._action_pane)
+                
+            self._action_pane.Hide()
+            self._action_pane.Float()
+            if wx.Platform == "__WXGTK__":
+                self._action_pane.Show()
+
+            e = self.FireEvent(wxEVT_AUI_PANE_FLOATED, self._action_pane, canVeto=False)
+
+            if not self._action_pane.frame:
+                self.Update()
+
+            self._action_window = self._action_pane.window
+
+            # adjust action offset for window frame
+            windowPt = self._action_pane.frame.GetRect().GetTopLeft()
+            originPt = self._action_pane.frame.ClientToScreen(wx.Point())
+            self._toolbar_action_offset = originPt - windowPt
+            
+            if self._agwFlags & AUI_MGR_USE_NATIVE_MINIFRAMES:
+                originPt = windowPt + wx.Point(3, 3)
+                
+            self._action_offset += originPt - windowPt
+
+            # action offset is used here to make it feel "natural" to the user
+            # to drag a docked pane and suddenly have it become a floating frame.
+            # Sometimes, however, the offset where the user clicked on the docked
+            # caption is bigger than the width of the floating frame itself, so
+            # in that case we need to set the action offset to a sensible value
+            frame_size = self._action_pane.frame.GetSize()
+            if self._action_offset.x > frame_size.x * 2 / 3:
+                self._action_offset.x = frame_size.x / 2
+            if self._action_offset.y > frame_size.y * 2 / 3:
+                self._action_offset.y = frame_size.y / 2
+
+            self.OnMotion_DragFloatingPane(event)
+            if wx.Platform != "__WXGTK__":
+                self._action_pane.Show()
+                
+            self.Update()
+
+
+    def OnMotion_Resize(self, event):
+        """
+        Sub-handler for the L{OnMotion} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        if AuiManager_HasLiveResize(self):
+            if self._currentDragItem != -1:
+                self._action_part = self._uiparts[self._currentDragItem]
+            else:
+                self._currentDragItem = self._uiparts.index(self._action_part)
+
+            if self._frame.HasCapture():
+                self._frame.ReleaseMouse()
+                
+            self.DoEndResizeAction(event)
+            self._frame.CaptureMouse()
+            return
+
+        if not self._action_part or not self._action_part.dock or not self._action_part.orientation:
+            return
+
+        clientPt = event.GetPosition()
+        screenPt = self._frame.ClientToScreen(clientPt)
+                    
+        dock = self._action_part.dock
+        pos = self._action_part.rect.GetPosition()
+
+        if self._action_part.type == AuiDockUIPart.typeDockSizer:
+            minPix, maxPix = self.CalculateDockSizerLimits(dock)
+        else:
+            if not self._action_part.pane:
+                return
+            
+            pane = self._action_part.pane
+            minPix, maxPix = self.CalculatePaneSizerLimits(dock, pane)
+
+        if self._action_part.orientation == wx.HORIZONTAL:
+            pos.y = Clip(clientPt.y - self._action_offset.y, minPix, maxPix)
+        else:
+            pos.x = Clip(clientPt.x - self._action_offset.x, minPix, maxPix)
+
+        hintrect = wx.RectPS(self._frame.ClientToScreen(pos), self._action_part.rect.GetSize())
+
+        if hintrect != self._action_rect:
+        
+            if wx.Platform == "__WXMAC__":
+                dc = wx.ClientDC(self._frame)
+            else:
+                dc = wx.ScreenDC()
+
+            DrawResizeHint(dc, self._action_rect)
+            DrawResizeHint(dc, hintrect)
+            self._action_rect = wx.Rect(*hintrect)
+                
+
+    def OnLeftUp_Resize(self, event):
+        """
+        Sub-handler for the L{OnLeftUp} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        if self._currentDragItem != -1 and AuiManager_HasLiveResize(self):
+            self._action_part = self._uiparts[self._currentDragItem]
+
+            if self._frame.HasCapture():
+                self._frame.ReleaseMouse()
+                
+            self.DoEndResizeAction(event)
+            self._currentDragItem = -1
+            return
+            
+        if not self._action_part or not self._action_part.dock:
+            return
+
+        clientPt = event.GetPosition()
+        screenPt = self._frame.ClientToScreen(clientPt)
+
+        return self.RestrictResize(clientPt, screenPt, createDC=True)
+        
+
+    def OnLeftUp_ClickButton(self, event):
+        """
+        Sub-handler for the L{OnLeftUp} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        self._hover_button = None
+
+        if self._action_part:
+            self.RefreshButton(self._action_part)
+
+            # make sure we're still over the item that was originally clicked
+            if self._action_part == self.HitTest(*event.GetPosition()):
+            
+                # fire button-click event
+                e = AuiManagerEvent(wxEVT_AUI_PANE_BUTTON)
+                e.SetManager(self)
+                e.SetPane(self._action_part.pane)
+                e.SetButton(self._action_part.button.button_id)
+                self.ProcessMgrEvent(e)
+        
+
+    def CheckPaneMove(self, pane):
+        """
+        Checks if a pane has moved by a visible amount.
+
+        :param `pane`: an instance of L{AuiPaneInfo}.
+        """
+
+        win_rect = pane.frame.GetRect()
+        win_rect.x, win_rect.y = pane.floating_pos
+        
+        if win_rect == self._last_rect:
+            return False
+
+        # skip the first move event
+        if self._last_rect.IsEmpty():
+            self._last_rect = wx.Rect(*win_rect)
+            return False
+
+        # skip if moving too fast to avoid massive redraws and
+        # jumping hint windows
+        if abs(win_rect.x - self._last_rect.x) > 10 or \
+           abs(win_rect.y - self._last_rect.y) > 10:
+            self._last_rect = wx.Rect(*win_rect)
+            return False
+
+        return True        
+        
+
+    def OnMotion_DragFloatingPane(self, eventOrPt):
+        """
+        Sub-handler for the L{OnMotion} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        isPoint = False
+        if isinstance(eventOrPt, wx.Point):
+            clientPt = self._frame.ScreenToClient(eventOrPt)
+            screenPt = wx.Point(*eventOrPt)
+            isPoint = True
+        else:
+            clientPt = eventOrPt.GetPosition()
+            screenPt = self._frame.ClientToScreen(clientPt)
+        
+        framePos = wx.Point()
+        
+        # try to find the pane
+        pane = self.GetPane(self._action_window)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        # update floating position
+        if pane.IsFloating():
+            diff = pane.floating_pos - (screenPt - self._action_offset)
+            pane.floating_pos = screenPt - self._action_offset
+
+        framePos = pane.floating_pos
+
+        # Move the pane window
+        if pane.frame:
+
+            if diff.x != 0 or diff.y != 0:
+                if wx.Platform == "__WXMSW__" and (self._agwFlags & AUI_MGR_TRANSPARENT_DRAG) == 0: # and not self.CheckPaneMove(pane):
+                    # return
+                    # HACK: Terrible hack on wxMSW (!)
+                    pane.frame.SetTransparent(254)
+                            
+                self._from_move = True
+                pane.frame.Move(pane.floating_pos)
+                self._from_move = False
+
+            if self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
+                pane.frame.SetTransparent(150)
+
+        # calculate the offset from the upper left-hand corner
+        # of the frame to the mouse pointer
+        action_offset = screenPt - framePos
+
+        # is the pane dockable?
+        if not self.CanDockPanel(pane):
+            self.HideHint()
+            ShowDockingGuides(self._guides, False)
+            return
+        
+        for paneInfo in self._panes:
+        
+            if not paneInfo.IsDocked() or not paneInfo.IsShown():
+                continue
+            if paneInfo.IsToolbar() or paneInfo.IsNotebookControl():
+                continue
+            if paneInfo.IsMaximized():
+                continue
+
+            if paneInfo.IsNotebookPage():
+            
+                notebookRoot = GetNotebookRoot(self._panes, paneInfo.notebook_id)
+
+                if not notebookRoot or not notebookRoot.IsDocked():
+                    continue
+            
+            rc = paneInfo.window.GetScreenRect()
+            if rc.Contains(screenPt):
+                if rc.height < 20 or rc.width < 20:
+                    return
+                
+                self.UpdateDockingGuides(paneInfo)
+                ShowDockingGuides(self._guides, True)
+                break
+
+        self.DrawHintRect(pane.window, clientPt, action_offset)
+
+
+    def OnLeftUp_DragFloatingPane(self, eventOrPt):
+        """
+        Sub-handler for the L{OnLeftUp} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+
+        if isinstance(eventOrPt, wx.Point):
+            clientPt = self._frame.ScreenToClient(eventOrPt)
+            screenPt = wx.Point(*eventOrPt)
+        else:
+            clientPt = eventOrPt.GetPosition()
+            screenPt = self._frame.ClientToScreen(clientPt)
+
+        # try to find the pane
+        paneInfo = self.GetPane(self._action_window)
+        if not paneInfo.IsOk():
+            raise Exception("Pane window not found")
+
+        ret = False
+        
+        if paneInfo.frame:
+        
+            # calculate the offset from the upper left-hand corner
+            # of the frame to the mouse pointer
+            framePos = paneInfo.frame.GetPosition()
+            action_offset = screenPt - framePos
+
+            # is the pane dockable?
+            if self.CanDockPanel(paneInfo):
+                # do the drop calculation
+                indx = self._panes.index(paneInfo)
+                ret, paneInfo = self.DoDrop(self._docks, self._panes, paneInfo, clientPt, action_offset)
+
+                if ret:
+                    e = self.FireEvent(wxEVT_AUI_PANE_DOCKING, paneInfo, canVeto=True)
+                    if e.GetVeto():
+                        self.HideHint()
+                        ShowDockingGuides(self._guides, False)
+                        return
+
+                    e = self.FireEvent(wxEVT_AUI_PANE_DOCKED, paneInfo, canVeto=False)
+
+                    if self._agwFlags & AUI_MGR_SMOOTH_DOCKING:
+                        self.SmoothDock(paneInfo)
+
+                self._panes[indx] = paneInfo
+            
+        # if the pane is still floating, update it's floating
+        # position (that we store)
+        if paneInfo.IsFloating():
+            paneInfo.floating_pos = paneInfo.frame.GetPosition()
+            if paneInfo.frame._transparent != paneInfo.transparent or self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
+                paneInfo.frame.SetTransparent(paneInfo.transparent)
+                paneInfo.frame._transparent = paneInfo.transparent
+        
+        elif self._has_maximized:
+            self.RestoreMaximizedPane()
+        
+        # reorder for dropping to a new notebook
+        # (caution: this code breaks the reference!)
+        tempPaneInfo = self.CopyTarget(paneInfo)
+        self._panes.remove(paneInfo)
+        self._panes.append(tempPaneInfo)
+
+        if ret:
+            self.Update()
+
+        self.HideHint()
+        ShowDockingGuides(self._guides, False)
+
+
+    def OnMotion_DragToolbarPane(self, eventOrPt):
+        """
+        Sub-handler for the L{OnMotion} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        isPoint = False
+        if isinstance(eventOrPt, wx.Point):
+            clientPt = self._frame.ScreenToClient(eventOrPt)
+            screenPt = wx.Point(*eventOrPt)
+            isPoint = True
+        else:
+            clientPt = eventOrPt.GetPosition()
+            screenPt = self._frame.ClientToScreen(clientPt)
+
+        pane = self.GetPane(self._action_window)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        pane.state |= AuiPaneInfo.actionPane
+        indx = self._panes.index(pane)
+
+        ret = False
+        wasFloating = pane.IsFloating()
+        # is the pane dockable?
+        if self.CanDockPanel(pane):
+            # do the drop calculation
+            ret, pane = self.DoDrop(self._docks, self._panes, pane, clientPt, self._action_offset)
+        
+        # update floating position
+        if pane.IsFloating():
+            pane.floating_pos = screenPt - self._toolbar_action_offset
+
+        # move the pane window
+        if pane.frame:
+            if wx.Platform == "__WXMSW__" and (self._agwFlags & AUI_MGR_TRANSPARENT_DRAG) == 0: # and not self.CheckPaneMove(pane):
+                # return
+                # HACK: Terrible hack on wxMSW (!)
+                pane.frame.SetTransparent(254)
+
+            self._from_move = True
+            pane.frame.Move(pane.floating_pos)
+            self._from_move = False
+                
+            if self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
+                pane.frame.SetTransparent(150)
+
+        self._panes[indx] = pane
+        if ret and wasFloating != pane.IsFloating() or (ret and not wasFloating):
+            wx.CallAfter(self.Update)
+
+        # when release the button out of the window.
+        # TODO: a better fix is needed.
+
+        if _VERSION_STRING < "2.9":
+            leftDown = wx.GetMouseState().LeftDown()
+        else:
+            leftDown = wx.GetMouseState().LeftIsDown()
+        
+        if not leftDown:
+            self._action = actionNone
+            self.OnLeftUp_DragToolbarPane(eventOrPt)
+
+
+    def OnMotion_Other(self, event):
+        """
+        Sub-handler for the L{OnMotion} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        part = self.HitTest(*event.GetPosition())
+
+        if part and part.type == AuiDockUIPart.typePaneButton \
+           and self.IsPaneButtonVisible(part):
+            if part != self._hover_button:
+            
+                if self._hover_button:
+                    self.RefreshButton(self._hover_button)
+
+                self._hover_button = part
+                self.RefreshButton(part)
+            
+        else:
+        
+            if self._hover_button:
+                self.RefreshButton(self._hover_button)
+            else:
+                event.Skip()
+
+            self._hover_button = None
+        
+        
+    def OnLeftUp_DragToolbarPane(self, eventOrPt):
+        """
+        Sub-handler for the L{OnLeftUp} event.
+
+        :param `event`: a `wx.MouseEvent` to be processed.
+        """
+        
+        isPoint = False
+        if isinstance(eventOrPt, wx.Point):
+            clientPt = self._frame.ScreenToClient(eventOrPt)
+            screenPt = wx.Point(*eventOrPt)
+            isPoint = True
+        else:
+            clientPt = eventOrPt.GetPosition()
+            screenPt = self._frame.ClientToScreen(clientPt)
+
+        # try to find the pane
+        pane = self.GetPane(self._action_window)
+        if not pane.IsOk():
+            raise Exception("Pane window not found")
+
+        if pane.IsFloating():
+            pane.floating_pos = pane.frame.GetPosition()
+            if pane.frame._transparent != pane.transparent or self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
+                pane.frame.SetTransparent(pane.transparent)
+                pane.frame._transparent = pane.transparent
+        
+        # save the new positions
+        docks = FindDocks(self._docks, pane.dock_direction, pane.dock_layer, pane.dock_row)
+        if len(docks) == 1:
+            dock = docks[0]
+            pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock)
+
+            for i in xrange(len(dock.panes)):
+                dock.panes[i].dock_pos = pane_positions[i]
+        
+        pane.state &= ~AuiPaneInfo.actionPane
+        self.Update()
+
+
+    def OnPaneButton(self, event):
+        """
+        Handles the ``EVT_AUI_PANE_BUTTON`` event for L{AuiManager}.
+
+        :param `event`: a L{AuiManagerEvent} event to be processed.
+        """
+
+        if not event.pane:
+            raise Exception("Pane Info passed to AuiManager.OnPaneButton must be non-null")
+
+        pane = event.pane
+
+        if event.button == AUI_BUTTON_CLOSE:
+
+            if isinstance(pane.window.GetParent(), AuiFloatingFrame):
+                rootManager = GetManager(pane.window)
+            else:
+                rootManager = self
+            
+            if rootManager != self:
+                self._frame.Close()
+                return
+
+            # fire pane close event
+            e = AuiManagerEvent(wxEVT_AUI_PANE_CLOSE)
+            e.SetManager(self)
+            e.SetPane(event.pane)
+            self.ProcessMgrEvent(e)
+
+            if not e.GetVeto():
+            
+                # close the pane, but check that it
+                # still exists in our pane array first
+                # (the event handler above might have removed it)
+
+                check = self.GetPane(pane.window)
+                if check.IsOk():                
+                    self.ClosePane(pane)
+                
+                self.Update()
+
+        # mn this performs the minimizing of a pane
+        elif event.button == AUI_BUTTON_MINIMIZE:
+            e = AuiManagerEvent(wxEVT_AUI_PANE_MINIMIZE)
+            e.SetManager(self)
+            e.SetPane(event.pane)
+            self.ProcessMgrEvent(e)
+
+            if not e.GetVeto():
+                self.MinimizePane(pane)
+    
+        elif event.button == AUI_BUTTON_MAXIMIZE_RESTORE and not pane.IsMaximized():
+        
+            # fire pane close event
+            e = AuiManagerEvent(wxEVT_AUI_PANE_MAXIMIZE)
+            e.SetManager(self)
+            e.SetPane(event.pane)
+            self.ProcessMgrEvent(e)
+
+            if not e.GetVeto():
+            
+                self.MaximizePane(pane)
+                self.Update()
+            
+        elif event.button == AUI_BUTTON_MAXIMIZE_RESTORE and pane.IsMaximized():
+        
+            # fire pane close event
+            e = AuiManagerEvent(wxEVT_AUI_PANE_RESTORE)
+            e.SetManager(self)
+            e.SetPane(event.pane)
+            self.ProcessMgrEvent(e)
+
+            if not e.GetVeto():
+            
+                self.RestorePane(pane)
+                self.Update()
+            
+        elif event.button == AUI_BUTTON_PIN:
+        
+            if self._agwFlags & AUI_MGR_ALLOW_FLOATING and pane.IsFloatable():
+                e = self.FireEvent(wxEVT_AUI_PANE_FLOATING, pane, canVeto=True)
+                if e.GetVeto():
+                    return
+
+                pane.Float()
+                e = self.FireEvent(wxEVT_AUI_PANE_FLOATED, pane, canVeto=False)
+
+            self.Update()
+
+
+    def MinimizePane(self, paneInfo):
+        """
+        Minimizes a pane in a newly and automatically created L{AuiToolBar}.
+
+        Clicking on the minimize button causes a new L{AuiToolBar} to be created
+        and added to the frame manager (currently the implementation is such that
+        panes at West will have a toolbar at the right, panes at South will have
+        toolbars at the bottom etc...) and the pane is hidden in the manager.
+        
+        Clicking on the restore button on the newly created toolbar will result in the
+        toolbar being removed and the original pane being restored.
+
+        :param `paneInfo`: a L{AuiPaneInfo} instance for the pane to be minimized.
+        """
+        
+        if not paneInfo.IsToolbar():
+
+            if paneInfo.IsMinimized():
+                # We are already minimized
+                return
+            
+            # Basically the idea is this.
+            #
+            # 1) create a toolbar, with a restore button 
+            #
+            # 2) place the new toolbar in the toolbar area representative of the location of the pane 
+            #  (NORTH/SOUTH/EAST/WEST, central area always to the right)
+            #
+            # 3) Hide the minimizing pane 
+
+
+            # personalize the toolbar style
+            tbStyle = AUI_TB_DEFAULT_STYLE
+            posMask = paneInfo.minimize_mode & AUI_MINIMIZE_POS_MASK
+            captMask = paneInfo.minimize_mode & AUI_MINIMIZE_CAPT_MASK
+            dockDirection = paneInfo.dock_direction
+            if captMask != 0:
+                tbStyle |= AUI_TB_TEXT
+            if posMask == AUI_MINIMIZE_POS_SMART:
+                if paneInfo.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]:
+                    tbStyle |= AUI_TB_HORZ_LAYOUT
+
+                elif paneInfo.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT, AUI_DOCK_CENTER]:
+                    tbStyle |= AUI_TB_VERTICAL
+                    if captMask == AUI_MINIMIZE_CAPT_SMART:
+                        tbStyle |= AUI_TB_CLOCKWISE
+                    
+            elif posMask in [AUI_MINIMIZE_POS_TOP, AUI_MINIMIZE_POS_BOTTOM]:
+                tbStyle |= AUI_TB_HORZ_LAYOUT
+                if posMask == AUI_MINIMIZE_POS_TOP:
+                    dockDirection = AUI_DOCK_TOP
+                else:
+                    dockDirection = AUI_DOCK_BOTTOM
+
+            else:
+                tbStyle |= AUI_TB_VERTICAL
+                if captMask == AUI_MINIMIZE_CAPT_SMART:
+                    tbStyle |= AUI_TB_CLOCKWISE
+                if posMask == AUI_MINIMIZE_POS_LEFT:
+                    dockDirection = AUI_DOCK_LEFT
+                elif posMask == AUI_MINIMIZE_POS_RIGHT:
+                    dockDirection = AUI_DOCK_RIGHT
+                elif posMask == AUI_MINIMIZE_POS_BOTTOM:
+                    dockDirection = AUI_DOCK_BOTTOM
+
+            # Create a new toolbar
+            # give it the same name as the minimized pane with _min appended
+
+            win_rect = paneInfo.window.GetScreenRect()
+            
+            minimize_toolbar = auibar.AuiToolBar(self.GetManagedWindow(), agwStyle=tbStyle)
+            minimize_toolbar.Hide()
+            minimize_toolbar.SetToolBitmapSize(wx.Size(16, 16))
+
+            if paneInfo.icon and paneInfo.icon.IsOk():
+                restore_bitmap = paneInfo.icon
+            else:
+                restore_bitmap = self._art._restore_bitmap
+                
+            minimize_toolbar.AddSimpleTool(ID_RESTORE_FRAME, paneInfo.caption, restore_bitmap, "Restore " + paneInfo.caption)
+            minimize_toolbar.SetAuiManager(self)
+            minimize_toolbar.Realize()
+            toolpanelname = paneInfo.name + "_min"
+
+            if paneInfo.IsMaximized():
+                paneInfo.SetFlag(paneInfo.wasMaximized, True)
+
+            if dockDirection == AUI_DOCK_TOP:
+                self.AddPane(minimize_toolbar, AuiPaneInfo(). \
+                    Name(toolpanelname).Caption(paneInfo.caption). \
+                    ToolbarPane().Top().BottomDockable(False). \
+                    LeftDockable(False).RightDockable(False).DestroyOnClose())
+                
+            elif dockDirection == AUI_DOCK_BOTTOM:
+                self.AddPane(minimize_toolbar, AuiPaneInfo(). \
+                    Name(toolpanelname).Caption(paneInfo.caption). \
+                    ToolbarPane().Bottom().TopDockable(False). \
+                    LeftDockable(False).RightDockable(False).DestroyOnClose())
+                
+            elif dockDirection == AUI_DOCK_LEFT:
+                self.AddPane(minimize_toolbar, AuiPaneInfo(). \
+                    Name(toolpanelname).Caption(paneInfo.caption). \
+                    ToolbarPane().Left().TopDockable(False). \
+                    BottomDockable(False).RightDockable(False).DestroyOnClose())
+
+            elif dockDirection in [AUI_DOCK_RIGHT, AUI_DOCK_CENTER]:
+                self.AddPane(minimize_toolbar, AuiPaneInfo(). \
+                    Name(toolpanelname).Caption(paneInfo.caption). \
+                    ToolbarPane().Right().TopDockable(False). \
+                    LeftDockable(False).BottomDockable(False).DestroyOnClose())
+
+            arr = FindDocks(self._docks, paneInfo.dock_direction, paneInfo.dock_layer, paneInfo.dock_row)
+
+            if arr:
+                dock = arr[0]
+                paneInfo.previousDockSize = dock.size
+
+            paneInfo.previousDockPos = paneInfo.dock_pos
+            
+            # mark ourselves minimized
+            paneInfo.Minimize()
+            paneInfo.Show(False)
+            self._has_minimized = True
+            # last, hide the window
+            if paneInfo.window and paneInfo.window.IsShown():
+                paneInfo.window.Show(False)
+
+            minimize_toolbar.Show()
+            self.Update()
+            if self._agwFlags & AUI_MGR_ANIMATE_FRAMES:
+                self.AnimateDocking(win_rect, minimize_toolbar.GetScreenRect())
+
+
+    def OnRestoreMinimizedPane(self, event):
+        """
+        Handles the ``EVT_AUI_PANE_MIN_RESTORE`` event for L{AuiManager}.
+
+        :param `event`: an instance of L{AuiManagerEvent} to be processed.
+        """
+
+        self.RestoreMinimizedPane(event.pane)
+
+
+    def OnPaneDocked(self, event):
+        """
+        Handles the ``EVT_AUI_PANE_DOCKED`` event for L{AuiManager}.
+
+        :param `event`: an instance of L{AuiManagerEvent} to be processed.
+        """
+
+        event.Skip()
+        self.RemoveAutoNBCaption(event.GetPane())        
+    
+
+    def CreateNotebookBase(self, panes, paneInfo):
+        """
+        Creates an auto-notebook base from a pane, and then add that pane as a page.
+
+        :param `panes`: Set of panes to append new notebook base pane to
+        :param `paneInfo`: L{AuiPaneInfo} instance to convert to new notebook.
+        """
+
+        # Create base notebook pane ...
+        nbid = len(self._notebooks)
+
+        baseInfo = AuiPaneInfo()
+        baseInfo.SetDockPos(paneInfo).NotebookControl(nbid). \
+            CloseButton(False).SetNameFromNotebookId(). \
+            NotebookDockable(False).Floatable(paneInfo.IsFloatable())
+        baseInfo.best_size = paneInfo.best_size
+        panes.append(baseInfo)
+
+        # add original pane as tab ...
+        paneInfo.NotebookPage(nbid)
+
+    def RemoveAutoNBCaption(self, pane):
+        """
+        Removes the caption on newly created automatic notebooks.
+
+        :param `pane`: an instance of L{AuiPaneInfo} (the target notebook).
+        """
+
+        if self._agwFlags & AUI_MGR_AUTONB_NO_CAPTION == 0:
+            return False
+
+        def RemoveCaption():
+            """ Sub-function used to remove the pane caption on automatic notebooks. """
+            
+            if pane.HasNotebook(): 
+                notebook = self._notebooks[pane.notebook_id] 
+                self.GetPane(notebook).CaptionVisible(False).PaneBorder(False)                
+                self.Update() 
+
+        # it seems the notebook isnt created by this stage, so remove 
+        # the caption a moment later 
+        wx.CallAfter(RemoveCaption)
+        return True
+        
+        
+    def RestoreMinimizedPane(self, paneInfo):
+        """
+        Restores a previously minimized pane.
+
+        :param `paneInfo`: a L{AuiPaneInfo} instance for the pane to be restored.
+        """
+
+        panename = paneInfo.name
+        panename = panename[0:-4]
+        pane = self.GetPane(panename)
+
+        pane.SetFlag(pane.needsRestore, True)
+
+        if not pane.IsOk():
+            panename = paneInfo.name
+            pane = self.GetPane(panename)
+            paneInfo = self.GetPane(panename + "_min")
+            if not paneInfo.IsOk():
+                # Already minimized
+                return
+        
+        if pane.IsOk():
+            if not pane.IsMinimized():
+                return
+            
+
+            if pane.HasFlag(pane.wasMaximized):
+
+                self.SavePreviousDockSizes(pane)
+                
+
+            self.ShowPane(pane.window, True)
+            pane.Show(True)
+            self._has_minimized = False
+            pane.SetFlag(pane.optionMinimized, False)
+            paneInfo.window.Show(False)
+            self.DetachPane(paneInfo.window)
+            paneInfo.Show(False)
+            paneInfo.Hide()
+
+            self.Update()
+
+
+    def AnimateDocking(self, win_rect, pane_rect):
+        """
+        Animates the minimization/docking of a pane a la Eclipse, using a `wx.ScreenDC`
+        to draw a "moving docking rectangle" on the screen.
+
+        :param `win_rect`: the original pane screen rectangle;
+        :param `pane_rect`: the newly created toolbar/pane screen rectangle.
+
+        :note: This functionality is not available on wxMAC as this platform doesn't have
+         the ability to use `wx.ScreenDC` to draw on-screen and on Windows > Vista.
+        """
+
+        if wx.Platform == "__WXMAC__":
+            # No wx.ScreenDC on the Mac...
+            return
+        if wx.Platform == "__WXMSW__" and wx.GetOsVersion()[1] > 5:
+            # No easy way to handle this on Vista...
+            return
+
+        xstart, ystart = win_rect.x, win_rect.y
+        xend, yend = pane_rect.x, pane_rect.y
+
+        step = self.GetAnimationStep()
+        
+        wstep = int(abs(win_rect.width - pane_rect.width)/step)
+        hstep = int(abs(win_rect.height - pane_rect.height)/step)
+        xstep = int(win_rect.x - pane_rect.x)/step
+        ystep = int(win_rect.y - pane_rect.y)/step
+        
+        dc = wx.ScreenDC()
+        dc.SetLogicalFunction(wx.INVERT)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        dc.SetPen(wx.LIGHT_GREY_PEN)
+        
+        for i in xrange(int(step)):
+            width, height = win_rect.width - i*wstep, win_rect.height - i*hstep
+            x, y = xstart - i*xstep, ystart - i*ystep
+            new_rect = wx.Rect(x, y, width, height)
+            dc.DrawRoundedRectangleRect(new_rect, 3)
+            wx.SafeYield()
+            wx.MilliSleep(10)
+            dc.DrawRoundedRectangleRect(new_rect, 3)
+            
+
+    def SmoothDock(self, paneInfo):
+        """
+        This method implements a smooth docking effect for floating panes, similar to
+        what the PyQT library does with its floating windows.
+
+        :param `paneInfo`: an instance of L{AuiPaneInfo}.
+
+        :note: The smooth docking effect can only be used if you set the ``AUI_MGR_SMOOTH_DOCKING``
+         style to L{AuiManager}.
+        """
+
+        if paneInfo.IsToolbar():
+            return
+
+        if not paneInfo.frame or self._hint_rect.IsEmpty():
+            return
+
+        hint_rect = self._hint_rect
+        win_rect = paneInfo.frame.GetScreenRect()
+
+        xstart, ystart = win_rect.x, win_rect.y
+        xend, yend = hint_rect.x, hint_rect.y
+
+        step = self.GetAnimationStep()/3
+
+        wstep = int((win_rect.width - hint_rect.width)/step)
+        hstep = int((win_rect.height - hint_rect.height)/step)
+        xstep = int((win_rect.x - hint_rect.x))/step
+        ystep = int((win_rect.y - hint_rect.y))/step
+
+        for i in xrange(int(step)):
+            width, height = win_rect.width - i*wstep, win_rect.height - i*hstep
+            x, y = xstart - i*xstep, ystart - i*ystep
+            new_rect = wx.Rect(x, y, width, height)
+            paneInfo.frame.SetRect(new_rect)
+            wx.MilliSleep(10)            
+        
+            
+    def SetSnapLimits(self, x, y):
+        """
+        Modifies the snap limits used when snapping the `managed_window` to the screen
+        (using L{SnapToScreen}) or when snapping the floating panes to one side of the
+        `managed_window` (using L{SnapPane}).
+
+        To change the limit after which the `managed_window` or the floating panes are
+        automatically stickled to the screen border (or to the `managed_window` side),
+        set these two variables. Default values are 15 pixels.
+    
+        :param `x`: the minimum horizontal distance below which the snap occurs;
+        :param `y`: the minimum vertical distance below which the snap occurs.
+        """
+
+        self._snap_limits = (x, y)
+        self.Snap()
+
+
+    def Snap(self):
+        """
+        Snaps the main frame to specified position on the screen.
+
+        :see: L{SnapToScreen}
+        """
+        
+        snap, hAlign, vAlign, monitor = self._is_docked
+        if not snap:
+            return
+
+        managed_window = self.GetManagedWindow()
+        snap_pos = self.GetSnapPosition()
+        wnd_pos = managed_window.GetPosition()
+        snapX, snapY = self._snap_limits
+        
+        if abs(snap_pos.x - wnd_pos.x) < snapX and abs(snap_pos.y - wnd_pos.y) < snapY:
+            managed_window.SetPosition(snap_pos)
+        
+
+    def SnapToScreen(self, snap=True, monitor=0, hAlign=wx.RIGHT, vAlign=wx.TOP):
+        """
+        Snaps the main frame to specified position on the screen.
+
+        :param `snap`: whether to snap the main frame or not;
+        :param `monitor`: the monitor display in which snapping the window;
+        :param `hAlign`: the horizontal alignment of the snapping position;
+        :param `vAlign`: the vertical alignment of the snapping position.
+        """
+        
+        if not snap:
+            self._is_docked = (False, wx.RIGHT, wx.TOP, 0)
+            return
+
+        displayCount = wx.Display.GetCount()
+        if monitor > displayCount:
+            raise Exception("Invalid monitor selected: you only have %d monitors"%displayCount)
+
+        self._is_docked = (True, hAlign, vAlign, monitor)
+        self.GetManagedWindow().SetPosition(self.GetSnapPosition())
+        
+
+    def GetSnapPosition(self):
+        """ Returns the main frame snapping position. """
+
+        snap, hAlign, vAlign, monitor = self._is_docked
+        
+        display = wx.Display(monitor)
+        area = display.GetClientArea()
+        size = self.GetManagedWindow().GetSize()
+        
+        pos = wx.Point()
+        if hAlign == wx.LEFT:
+            pos.x = area.x
+        elif hAlign == wx.CENTER:
+            pos.x = area.x + (area.width - size.x)/2
+        else:
+            pos.x = area.x + area.width - size.x
+
+        if vAlign == wx.TOP:
+            pos.y = area.y
+        elif vAlign == wx.CENTER:
+            pos.y = area.y + (area.height - size.y)/2
+        else:
+            pos.y = area.y + area.height - size.y
+
+        return pos            
+
+
+    def GetAnimationStep(self):
+        """ Returns the animation step speed (a float) to use in L{AnimateDocking}. """
+
+        return self._animation_step
+
+
+    def SetAnimationStep(self, step):
+        """
+        Sets the animation step speed (a float) to use in L{AnimateDocking}.
+
+        :param `step`: a floating point value for the animation speed.
+        """
+
+        self._animation_step = float(step)        
+
+        
+    def RequestUserAttention(self, pane_window):
+        """
+        Requests the user attention by intermittently highlighting the pane caption.
+
+        :param `pane_window`: a `wx.Window` derived window, managed by the pane.
+        """
+                
+        # try to find the pane
+        paneInfo = self.GetPane(pane_window)
+        if not paneInfo.IsOk():
+            raise Exception("Pane window not found")
+
+        dc = wx.ClientDC(self._frame)
+
+        # if the frame is about to be deleted, don't bother
+        if not self._frame or self._frame.IsBeingDeleted():
+            return
+        
+        if not self._frame.GetSizer():
+            return
+
+        for part in self._uiparts:
+            if part.pane == paneInfo:
+                self._art.RequestUserAttention(dc, self._frame, part.pane.caption, part.rect, part.pane)
+                self._frame.RefreshRect(part.rect, True)
+                break
+
+
+    def StartPreviewTimer(self, toolbar):
+        """
+        Starts a timer for sliding in and out a minimized pane.
+
+        :param `toolbar`: the L{AuiToolBar} containing the minimized pane tool.
+        """
+
+        toolbar_pane = self.GetPane(toolbar)
+        toolbar_name = toolbar_pane.name
+        
+        pane_name = toolbar_name[0:-4]
+        
+        self._sliding_pane = self.GetPane(pane_name)
+        self._sliding_rect = toolbar.GetScreenRect()
+        self._sliding_direction = toolbar_pane.dock_direction
+        self._sliding_frame = None
+        
+        self._preview_timer.Start(1000, wx.TIMER_ONE_SHOT)
+
+
+    def StopPreviewTimer(self):
+        """ Stops a timer for sliding in and out a minimized pane. """
+
+        if self._preview_timer.IsRunning():
+            self._preview_timer.Stop()
+
+        self.SlideOut()
+        self._sliding_pane = None
+
+
+    def SlideIn(self, event):
+        """
+        Handles the ``wx.EVT_TIMER`` event for L{AuiManager}.
+
+        :param `event`: a `wx.TimerEvent` to be processed.
+
+        :note: This is used solely for sliding in and out minimized panes.
+        """
+
+        window = self._sliding_pane.window
+        self._sliding_frame = wx.MiniFrame(None, -1, title=_("Pane Preview"),
+                                           style=wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP |
+                                           wx.FRAME_NO_TASKBAR | wx.CAPTION)
+        window.Reparent(self._sliding_frame)
+        self._sliding_frame.SetSize((0, 0))
+        window.Show()
+        self._sliding_frame.Show()
+        
+        size = window.GetBestSize()
+
+        startX, startY, stopX, stopY = GetSlidingPoints(self._sliding_rect, size, self._sliding_direction)
+        
+        step = stopX/10
+        window_size = 0
+        
+        for i in xrange(0, stopX, step):
+            window_size = i
+            self._sliding_frame.SetDimensions(startX, startY, window_size, stopY)
+            self._sliding_frame.Refresh()
+            self._sliding_frame.Update()
+            wx.MilliSleep(10)
+
+        self._sliding_frame.SetDimensions(startX, startY, stopX, stopY)
+        self._sliding_frame.Refresh()
+        self._sliding_frame.Update()
+        
+
+    def SlideOut(self):
+        """
+        Slides out a preview of a minimized pane.
+
+        :note: This is used solely for sliding in and out minimized panes.
+        """
+
+        if not self._sliding_frame:
+            return
+
+        window = self._sliding_frame.GetChildren()[0]
+        size = window.GetBestSize()
+        
+        startX, startY, stopX, stopY = GetSlidingPoints(self._sliding_rect, size, self._sliding_direction)
+
+        step = stopX/10
+        window_size = 0
+        
+        for i in xrange(stopX, 0, -step):
+            window_size = i
+            self._sliding_frame.SetDimensions(startX, startY, window_size, stopY)
+            self._sliding_frame.Refresh()
+            self._sliding_frame.Update()
+            self._frame.RefreshRect(wx.Rect(startX+window_size, startY, step, stopY))
+            self._frame.Update()
+            wx.MilliSleep(10)
+
+        self._sliding_frame.SetDimensions(startX, startY, 0, stopY)
+
+        window.Hide()
+        window.Reparent(self._frame)
+
+        self._sliding_frame.Hide()
+        self._sliding_frame.Destroy()
+        self._sliding_frame = None
+        self._sliding_pane = None
+        
+
+class AuiManager_DCP(AuiManager):
+    """
+    A class similar to L{AuiManager} but with a Dummy Center Pane (**DCP**).
+    The code for this class is still flickery due to the call to `wx.CallAfter`
+    and the double-update call.
+    """
+    
+    def __init__(self, *args, **keys):
+
+        AuiManager.__init__(self, *args, **keys)
+        self.hasDummyPane = False
+        
+
+    def _createDummyPane(self):
+        """ Creates a Dummy Center Pane (**DCP**). """
+
+        if self.hasDummyPane:
+            return
+
+        self.hasDummyPane = True
+        dummy = wx.Panel(self.GetManagedWindow())
+        info = AuiPaneInfo().CenterPane().NotebookDockable(True).Name('dummyCenterPane').DestroyOnClose(True)
+        self.AddPane(dummy, info)
+
+
+    def _destroyDummyPane(self):
+        """ Destroys the Dummy Center Pane (**DCP**). """
+
+        if not self.hasDummyPane:
+            return
+        
+        self.hasDummyPane = False
+        self.ClosePane(self.GetPane('dummyCenterPane'))
+
+        
+    def Update(self):
+        """
+        This method is called after any number of changes are made to any of the
+        managed panes. L{Update} must be invoked after L{AuiManager.AddPane} or L{AuiManager.InsertPane} are
+        called in order to "realize" or "commit" the changes.
+
+        In addition, any number of changes may be made to L{AuiPaneInfo} structures
+        (retrieved with L{AuiManager.GetPane}), but to realize the changes, L{Update}
+        must be called. This construction allows pane flicker to be avoided by updating
+        the whole layout at one time.
+        """
+        
+        AuiManager.Update(self)
+
+        # check if there's already a center pane (except our dummy pane)
+        dummyCenterPane = self.GetPane('dummyCenterPane')
+        haveCenterPane = any((pane != dummyCenterPane) and (pane.dock_direction == AUI_DOCK_CENTER) and
+                             not pane.IsFloating() and pane.IsShown() for pane in self.GetAllPanes())
+        if haveCenterPane:
+            if self.hasDummyPane:
+                # there's our dummy pane and also another center pane, therefor let's remove our dummy
+                def do():
+                    self._destroyDummyPane()
+                    self.Update()
+                wx.CallAfter(do)
+        else:
+            # if we get here, there's no center pane, create our dummy
+            if not self.hasDummyPane:
+                self._createDummyPane()
+
+                
diff --git a/aui/tabart.py b/aui/tabart.py
new file mode 100644 (file)
index 0000000..60b8e01
--- /dev/null
@@ -0,0 +1,2777 @@
+"""
+Tab art provider code - a tab provider provides all drawing functionality to
+the L{AuiNotebook}. This allows the L{AuiNotebook} to have a plugable look-and-feel.
+
+By default, a L{AuiNotebook} uses an instance of this class called L{AuiDefaultTabArt}
+which provides bitmap art and a colour scheme that is adapted to the major platforms'
+look. You can either derive from that class to alter its behaviour or write a
+completely new tab art class. Call L{AuiNotebook.SetArtProvider} to make use this
+new tab art.
+"""
+
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+
+if wx.Platform == '__WXMAC__':
+    import Carbon.Appearance
+
+from aui_utilities import BitmapFromBits, StepColour, IndentPressedBitmap, ChopText
+from aui_utilities import GetBaseColour, DrawMACCloseButton, LightColour, TakeScreenShot
+from aui_utilities import CopyAttributes
+
+from aui_constants import *
+
+
+# -- GUI helper classes and functions --
+class AuiCommandCapture(wx.PyEvtHandler):
+    """ A class to handle the dropdown window menu. """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        wx.PyEvtHandler.__init__(self)        
+        self._last_id = 0
+
+
+    def GetCommandId(self):
+        """ Returns the event command identifier. """
+
+        return self._last_id 
+
+
+    def ProcessEvent(self, event):
+        """
+        Processes an event, searching event tables and calling zero or more suitable
+        event handler function(s).
+
+        :param `event`: the event to process.
+
+        :note: Normally, your application would not call this function: it is called
+         in the wxPython implementation to dispatch incoming user interface events
+         to the framework (and application).
+         However, you might need to call it if implementing new functionality (such as
+         a new control) where you define new event types, as opposed to allowing the
+         user to override functions.
+
+         An instance where you might actually override the L{ProcessEvent} function is where
+         you want to direct event processing to event handlers not normally noticed by
+         wxPython. For example, in the document/view architecture, documents and views
+         are potential event handlers. When an event reaches a frame, L{ProcessEvent} will
+         need to be called on the associated document and view in case event handler
+         functions are associated with these objects. 
+
+         The normal order of event table searching is as follows:
+
+         1. If the object is disabled (via a call to `SetEvtHandlerEnabled`) the function
+            skips to step (6).
+         2. If the object is a `wx.Window`, L{ProcessEvent} is recursively called on the window's 
+            `wx.Validator`. If this returns ``True``, the function exits.
+         3. wxWidgets `SearchEventTable` is called for this event handler. If this fails, the
+            base class table is tried, and so on until no more tables exist or an appropriate
+            function was found, in which case the function exits.
+         4. The search is applied down the entire chain of event handlers (usually the chain
+            has a length of one). If this succeeds, the function exits.
+         5. If the object is a `wx.Window` and the event is a `wx.CommandEvent`, L{ProcessEvent} is
+            recursively applied to the parent window's event handler. If this returns ``True``,
+            the function exits.
+         6. Finally, L{ProcessEvent} is called on the `wx.App` object.
+        """
+        
+        if event.GetEventType() == wx.wxEVT_COMMAND_MENU_SELECTED:
+            self._last_id = event.GetId()
+            return True
+        
+        if self.GetNextHandler():
+            return self.GetNextHandler().ProcessEvent(event)
+
+        return False
+    
+
+class AuiDefaultTabArt(object):
+    """
+    Tab art provider code - a tab provider provides all drawing functionality to
+    the L{AuiNotebook}. This allows the L{AuiNotebook} to have a plugable look-and-feel.
+
+    By default, a L{AuiNotebook} uses an instance of this class called L{AuiDefaultTabArt}
+    which provides bitmap art and a colour scheme that is adapted to the major platforms'
+    look. You can either derive from that class to alter its behaviour or write a
+    completely new tab art class. Call L{AuiNotebook.SetArtProvider} to make use this
+    new tab art.
+    """
+    
+    def __init__(self):
+        """ Default class constructor. """
+
+        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._measuring_font = self._selected_font
+
+        self._fixed_tab_width = 100
+        self._tab_ctrl_height = 0
+        self._buttonRect = wx.Rect()
+
+        self.SetDefaultColours()
+
+        if wx.Platform == "__WXMAC__":
+            bmp_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)
+            self._active_close_bmp = DrawMACCloseButton(bmp_colour)
+            self._disabled_close_bmp = DrawMACCloseButton(wx.Colour(128, 128, 128))
+        else:
+            self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
+            self._disabled_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        self._hover_close_bmp = self._active_close_bmp
+        self._pressed_close_bmp = self._active_close_bmp
+
+        self._active_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.BLACK)
+        self._disabled_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        self._active_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.BLACK)
+        self._disabled_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        self._active_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.BLACK)
+        self._disabled_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        if wx.Platform == "__WXMAC__":
+            # Get proper highlight colour for focus rectangle from the
+            # current Mac theme.  kThemeBrushFocusHighlight is
+            # available on Mac OS 8.5 and higher
+            if hasattr(wx, 'MacThemeColour'):
+                c = wx.MacThemeColour(Carbon.Appearance.kThemeBrushFocusHighlight)
+            else:
+                brush = wx.Brush(wx.BLACK)
+                brush.MacSetTheme(Carbon.Appearance.kThemeBrushFocusHighlight)
+                c = brush.GetColour()
+            self._focusPen = wx.Pen(c, 2, wx.SOLID)
+        else:
+            self._focusPen = wx.Pen(wx.BLACK, 1, wx.USER_DASH)
+            self._focusPen.SetDashes([1, 1])
+            self._focusPen.SetCap(wx.CAP_BUTT)
+            
+            
+    def SetBaseColour(self, base_colour):
+        """
+        Sets a new base colour.
+
+        :param `base_colour`: an instance of `wx.Colour`.
+        """
+        
+        self._base_colour = base_colour
+        self._base_colour_pen = wx.Pen(self._base_colour)
+        self._base_colour_brush = wx.Brush(self._base_colour)
+
+
+    def SetDefaultColours(self, base_colour=None):
+        """
+        Sets the default colours, which are calculated from the given base colour.
+
+        :param `base_colour`: an instance of `wx.Colour`. If defaulted to ``None``, a colour
+         is generated accordingly to the platform and theme.
+        """
+
+        if base_colour is None:
+            base_colour = GetBaseColour()
+
+        self.SetBaseColour( base_colour )
+        self._border_colour = StepColour(base_colour, 75)
+        self._border_pen = wx.Pen(self._border_colour)
+
+        self._background_top_colour = StepColour(self._base_colour, 90)
+        self._background_bottom_colour = StepColour(self._base_colour, 170)
+        
+        self._tab_top_colour = self._base_colour
+        self._tab_bottom_colour = wx.WHITE
+        self._tab_gradient_highlight_colour = wx.WHITE
+
+        self._tab_inactive_top_colour = self._base_colour
+        self._tab_inactive_bottom_colour = StepColour(self._tab_inactive_top_colour, 160)
+        
+        self._tab_text_colour = lambda page: page.text_colour
+        self._tab_disabled_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
+
+
+    def Clone(self):
+        """ Clones the art object. """
+
+        art = type(self)()
+        art.SetNormalFont(self.GetNormalFont())
+        art.SetSelectedFont(self.GetSelectedFont())
+        art.SetMeasuringFont(self.GetMeasuringFont())
+
+        art = CopyAttributes(art, self)
+        return 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
+         ==================================== ==================================
+        
+        """
+
+        self._agwFlags = agwFlags
+
+
+    def GetAGWFlags(self):
+        """
+        Returns the tab art flags.
+
+        :see: L{SetAGWFlags} for a list of possible return values.
+        """
+
+        return self._agwFlags
+    
+            
+    def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
+        """
+        Sets the tab sizing information.
+        
+        :param `tab_ctrl_size`: the size of the tab control area;
+        :param `tab_count`: the number of tabs;
+        :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
+         to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
+        """
+        
+        self._fixed_tab_width = 100
+        minTabWidth, maxTabWidth = minMaxTabWidth
+
+        tot_width = tab_ctrl_size.x - self.GetIndentSize() - 4
+        agwFlags = self.GetAGWFlags()
+        
+        if agwFlags & AUI_NB_CLOSE_BUTTON:
+            tot_width -= self._active_close_bmp.GetWidth()
+        if agwFlags & AUI_NB_WINDOWLIST_BUTTON:
+            tot_width -= self._active_windowlist_bmp.GetWidth()
+
+        if tab_count > 0:
+            self._fixed_tab_width = tot_width/tab_count
+
+        if self._fixed_tab_width < 100:
+            self._fixed_tab_width = 100
+
+        if self._fixed_tab_width > tot_width/2:
+            self._fixed_tab_width = tot_width/2
+
+        if self._fixed_tab_width > 220:
+            self._fixed_tab_width = 220
+
+        if minTabWidth > -1:
+            self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
+        if maxTabWidth > -1:
+            self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
+
+        self._tab_ctrl_height = tab_ctrl_size.y
+    
+
+    def DrawBackground(self, dc, wnd, rect):
+        """
+        Draws the tab area background.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `rect`: the tab control rectangle.
+        """
+
+        self._buttonRect = wx.Rect()
+
+        # draw background
+        agwFlags = self.GetAGWFlags()
+        if agwFlags & AUI_NB_BOTTOM:
+            r = wx.Rect(rect.x, rect.y, rect.width+2, rect.height)
+
+        # TODO: else if (agwFlags & AUI_NB_LEFT) 
+        # TODO: else if (agwFlags & AUI_NB_RIGHT) 
+        else: #for AUI_NB_TOP
+            r = wx.Rect(rect.x, rect.y, rect.width+2, rect.height-3)
+
+        dc.GradientFillLinear(r, self._background_top_colour, self._background_bottom_colour, wx.SOUTH)
+
+        # draw base lines
+
+        dc.SetPen(self._border_pen)
+        y = rect.GetHeight()
+        w = rect.GetWidth()
+
+        if agwFlags & AUI_NB_BOTTOM:
+            dc.SetBrush(wx.Brush(self._background_bottom_colour))
+            dc.DrawRectangle(-1, 0, w+2, 4)
+
+        # TODO: else if (agwFlags & AUI_NB_LEFT) 
+        # TODO: else if (agwFlags & AUI_NB_RIGHT)
+        
+        else: # for AUI_NB_TOP
+            dc.SetBrush(self._base_colour_brush)
+            dc.DrawRectangle(-1, y-4, w+2, 4)
+
+
+    def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
+        """
+        Draws a single tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `page`: the tab control page associated with the tab;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
+        """
+
+        # if the caption is empty, measure some temporary text
+        caption = page.caption
+        if not caption:
+            caption = "Xj"
+
+        dc.SetFont(self._selected_font)
+        selected_textx, selected_texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        dc.SetFont(self._normal_font)
+        normal_textx, normal_texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        control = page.control
+
+        # figure out the size of the tab
+        tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
+                                             page.active, close_button_state, control)
+
+        tab_height = self._tab_ctrl_height - 3
+        tab_width = tab_size[0]
+        tab_x = in_rect.x
+        tab_y = in_rect.y + in_rect.height - tab_height
+
+        caption = page.caption
+
+        # select pen, brush and font for the tab to be drawn
+
+        if page.active:
+        
+            dc.SetFont(self._selected_font)
+            textx, texty = selected_textx, selected_texty
+        
+        else:
+        
+            dc.SetFont(self._normal_font)
+            textx, texty = normal_textx, normal_texty
+
+        if not page.enabled:
+            dc.SetTextForeground(self._tab_disabled_text_colour)
+            pagebitmap = page.dis_bitmap
+        else:
+            dc.SetTextForeground(self._tab_text_colour(page))
+            pagebitmap = page.bitmap
+            
+        # create points that will make the tab outline
+
+        clip_width = tab_width
+        if tab_x + clip_width > in_rect.x + in_rect.width:
+            clip_width = in_rect.x + in_rect.width - tab_x
+
+        # since the above code above doesn't play well with WXDFB or WXCOCOA,
+        # we'll just use a rectangle for the clipping region for now --
+        dc.SetClippingRegion(tab_x, tab_y, clip_width+1, tab_height-3)
+
+        border_points = [wx.Point() for i in xrange(6)]
+        agwFlags = self.GetAGWFlags()
+        
+        if agwFlags & AUI_NB_BOTTOM:
+        
+            border_points[0] = wx.Point(tab_x,             tab_y)
+            border_points[1] = wx.Point(tab_x,             tab_y+tab_height-6)
+            border_points[2] = wx.Point(tab_x+2,           tab_y+tab_height-4)
+            border_points[3] = wx.Point(tab_x+tab_width-2, tab_y+tab_height-4)
+            border_points[4] = wx.Point(tab_x+tab_width,   tab_y+tab_height-6)
+            border_points[5] = wx.Point(tab_x+tab_width,   tab_y)
+        
+        else: #if (agwFlags & AUI_NB_TOP) 
+        
+            border_points[0] = wx.Point(tab_x,             tab_y+tab_height-4)
+            border_points[1] = wx.Point(tab_x,             tab_y+2)
+            border_points[2] = wx.Point(tab_x+2,           tab_y)
+            border_points[3] = wx.Point(tab_x+tab_width-2, tab_y)
+            border_points[4] = wx.Point(tab_x+tab_width,   tab_y+2)
+            border_points[5] = wx.Point(tab_x+tab_width,   tab_y+tab_height-4)
+        
+        # TODO: else if (agwFlags & AUI_NB_LEFT) 
+        # TODO: else if (agwFlags & AUI_NB_RIGHT) 
+
+        drawn_tab_yoff = border_points[1].y
+        drawn_tab_height = border_points[0].y - border_points[1].y
+
+        if page.active:
+        
+            # draw active tab
+
+            # draw base background colour
+            r = wx.Rect(tab_x, tab_y, tab_width, tab_height)
+            dc.SetPen(self._base_colour_pen)
+            dc.SetBrush(self._base_colour_brush)
+            dc.DrawRectangle(r.x+1, r.y+1, r.width-1, r.height-4)
+
+            # this white helps fill out the gradient at the top of the tab
+            dc.SetPen( wx.Pen(self._tab_gradient_highlight_colour) )
+            dc.SetBrush( wx.Brush(self._tab_gradient_highlight_colour) )
+            dc.DrawRectangle(r.x+2, r.y+1, r.width-3, r.height-4)
+
+            # these two points help the rounded corners appear more antialiased
+            dc.SetPen(self._base_colour_pen)
+            dc.DrawPoint(r.x+2, r.y+1)
+            dc.DrawPoint(r.x+r.width-2, r.y+1)
+
+            # set rectangle down a bit for gradient drawing
+            r.SetHeight(r.GetHeight()/2)
+            r.x += 2
+            r.width -= 2
+            r.y += r.height
+            r.y -= 2
+
+            # draw gradient background
+            top_colour = self._tab_bottom_colour
+            bottom_colour = self._tab_top_colour
+            dc.GradientFillLinear(r, bottom_colour, top_colour, wx.NORTH)
+        
+        else:
+        
+            # draw inactive tab
+
+            r = wx.Rect(tab_x, tab_y+1, tab_width, tab_height-3)
+
+            # start the gradent up a bit and leave the inside border inset
+            # by a pixel for a 3D look.  Only the top half of the inactive
+            # tab will have a slight gradient
+            r.x += 3
+            r.y += 1
+            r.width -= 4
+            r.height /= 2
+            r.height -= 1
+
+            # -- draw top gradient fill for glossy look
+            top_colour = self._tab_inactive_top_colour
+            bottom_colour = self._tab_inactive_bottom_colour
+            dc.GradientFillLinear(r, bottom_colour, top_colour, wx.NORTH)
+
+            r.y += r.height
+            r.y -= 1
+
+            # -- draw bottom fill for glossy look
+            top_colour = self._tab_inactive_bottom_colour
+            bottom_colour = self._tab_inactive_bottom_colour
+            dc.GradientFillLinear(r, top_colour, bottom_colour, wx.SOUTH)
+        
+        # draw tab outline
+        dc.SetPen(self._border_pen)
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        dc.DrawPolygon(border_points)
+
+        # there are two horizontal grey lines at the bottom of the tab control,
+        # this gets rid of the top one of those lines in the tab control
+        if page.active:
+        
+            if agwFlags & AUI_NB_BOTTOM:
+                dc.SetPen(wx.Pen(self._background_bottom_colour))
+                
+            # TODO: else if (agwFlags & AUI_NB_LEFT) 
+            # TODO: else if (agwFlags & AUI_NB_RIGHT) 
+            else: # for AUI_NB_TOP
+                dc.SetPen(self._base_colour_pen)
+                
+            dc.DrawLine(border_points[0].x+1,
+                        border_points[0].y,
+                        border_points[5].x,
+                        border_points[5].y)
+        
+        text_offset = tab_x + 8
+        close_button_width = 0
+
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            close_button_width = self._active_close_bmp.GetWidth()
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                text_offset += close_button_width - 5
+                
+        bitmap_offset = 0
+        
+        if pagebitmap.IsOk():
+        
+            bitmap_offset = tab_x + 8
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
+                bitmap_offset += close_button_width - 5
+
+            # draw bitmap
+            dc.DrawBitmap(pagebitmap,
+                          bitmap_offset,
+                          drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2),
+                          True)
+
+            text_offset = bitmap_offset + pagebitmap.GetWidth()
+            text_offset += 3 # bitmap padding
+
+        else:
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
+                text_offset = tab_x + 8
+        
+        draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
+
+        ypos = drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2) - 1
+
+        offset_focus = text_offset     
+        if control is not None:
+            if control.GetPosition() != wx.Point(text_offset+1, ypos):
+                control.SetPosition(wx.Point(text_offset+1, ypos))
+
+            if not control.IsShown():
+                control.Show()
+
+            if paint_control:
+                bmp = TakeScreenShot(control.GetScreenRect())
+                dc.DrawBitmap(bmp, text_offset+1, ypos, True)
+                
+            controlW, controlH = control.GetSize()
+            text_offset += controlW + 4
+            textx += controlW + 4
+            
+        # draw tab text
+        rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
+        dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
+
+        # draw focus rectangle
+        if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
+            self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff, drawn_tab_height, rectx, recty)
+        
+        out_button_rect = wx.Rect()
+        
+        # draw close button if necessary
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+        
+            bmp = self._disabled_close_bmp
+
+            if close_button_state == AUI_BUTTON_STATE_HOVER:
+                bmp = self._hover_close_bmp
+            elif close_button_state == AUI_BUTTON_STATE_PRESSED:
+                bmp = self._pressed_close_bmp
+
+            shift = (agwFlags & AUI_NB_BOTTOM and [1] or [0])[0]
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                rect = wx.Rect(tab_x + 4, tab_y + (tab_height - bmp.GetHeight())/2 - shift,
+                               close_button_width, tab_height)
+            else:
+                rect = wx.Rect(tab_x + tab_width - close_button_width - 1,
+                               tab_y + (tab_height - bmp.GetHeight())/2 - shift,
+                               close_button_width, tab_height)
+
+            rect = IndentPressedBitmap(rect, close_button_state)
+            dc.DrawBitmap(bmp, rect.x, rect.y, True)
+
+            out_button_rect = rect
+        
+        out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
+
+        dc.DestroyClippingRegion()
+
+        return out_tab_rect, out_button_rect, x_extent
+    
+
+    def SetCustomButton(self, bitmap_id, button_state, bmp):
+        """
+        Sets a custom bitmap for the close, left, right and window list
+        buttons.
+        
+        :param `bitmap_id`: the button identifier;
+        :param `button_state`: the button state;
+        :param `bmp`: the custom bitmap to use for the button.
+        """
+
+        if bitmap_id == AUI_BUTTON_CLOSE:
+            if button_state == AUI_BUTTON_STATE_NORMAL:
+                self._active_close_bmp = bmp
+                self._hover_close_bmp = self._active_close_bmp
+                self._pressed_close_bmp = self._active_close_bmp
+                self._disabled_close_bmp = self._active_close_bmp
+                    
+            elif button_state == AUI_BUTTON_STATE_HOVER:
+                self._hover_close_bmp = bmp
+            elif button_state == AUI_BUTTON_STATE_PRESSED:
+                self._pressed_close_bmp = bmp
+            else:
+                self._disabled_close_bmp = bmp
+
+        elif bitmap_id == AUI_BUTTON_LEFT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                self._disabled_left_bmp = bmp
+            else:
+                self._active_left_bmp = bmp
+
+        elif bitmap_id == AUI_BUTTON_RIGHT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                self._disabled_right_bmp = bmp
+            else:
+                self._active_right_bmp = bmp
+
+        elif bitmap_id == AUI_BUTTON_WINDOWLIST:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                self._disabled_windowlist_bmp = bmp
+            else:
+                self._active_windowlist_bmp = bmp
+        
+
+    def GetIndentSize(self):
+        """ Returns the tabs indent size. """
+
+        return 5
+
+
+    def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
+        """
+        Returns the tab size for the given caption, bitmap and button state.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `caption`: the tab text caption;
+        :param `bitmap`: the bitmap displayed on the tab;
+        :param `active`: whether the tab is selected or not;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+
+        dc.SetFont(self._measuring_font)
+        measured_textx, measured_texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        # add padding around the text
+        tab_width = measured_textx
+        tab_height = measured_texty
+
+        # if the close button is showing, add space for it
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            tab_width += self._active_close_bmp.GetWidth() + 3
+
+        # if there's a bitmap, add space for it
+        if bitmap.IsOk():
+            tab_width += bitmap.GetWidth()
+            tab_width += 3 # right side bitmap padding
+            tab_height = max(tab_height, bitmap.GetHeight())
+        
+        # add padding
+        tab_width += 16
+        tab_height += 10
+
+        agwFlags = self.GetAGWFlags()
+        if agwFlags & AUI_NB_TAB_FIXED_WIDTH:
+            tab_width = self._fixed_tab_width
+
+        if control is not None:
+            tab_width += control.GetSize().GetWidth() + 4
+            
+        x_extent = tab_width
+
+        return (tab_width, tab_height), x_extent
+
+
+    def DrawButton(self, dc, wnd, in_rect, button, orientation):
+        """
+        Draws a button on the tab or on the tab area, depending on the button identifier. 
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `button`: an instance of the button class;
+        :param `orientation`: the tab orientation.
+        """
+
+        bitmap_id, button_state = button.id, button.cur_state
+        
+        if bitmap_id == AUI_BUTTON_CLOSE:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_close_bmp
+            elif button_state & AUI_BUTTON_STATE_HOVER:
+                bmp = self._hover_close_bmp
+            elif button_state & AUI_BUTTON_STATE_PRESSED:
+                bmp = self._pressed_close_bmp
+            else:
+                bmp = self._active_close_bmp
+
+        elif bitmap_id == AUI_BUTTON_LEFT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_left_bmp
+            else:
+                bmp = self._active_left_bmp
+
+        elif bitmap_id == AUI_BUTTON_RIGHT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_right_bmp
+            else:
+                bmp = self._active_right_bmp
+
+        elif bitmap_id == AUI_BUTTON_WINDOWLIST:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_windowlist_bmp
+            else:
+                bmp = self._active_windowlist_bmp
+
+        else:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = button.dis_bitmap
+            else:
+                bmp = button.bitmap
+                
+        if not bmp.IsOk():
+            return
+
+        rect = wx.Rect(*in_rect)
+
+        if orientation == wx.LEFT:
+        
+            rect.SetX(in_rect.x)
+            rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2))
+            rect.SetWidth(bmp.GetWidth())
+            rect.SetHeight(bmp.GetHeight())
+        
+        else:
+        
+            rect = wx.Rect(in_rect.x + in_rect.width - bmp.GetWidth(),
+                           ((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
+                           bmp.GetWidth(), bmp.GetHeight())
+        
+        rect = IndentPressedBitmap(rect, button_state)
+        dc.DrawBitmap(bmp, rect.x, rect.y, True)
+
+        out_rect = rect
+
+        if bitmap_id == AUI_BUTTON_RIGHT:
+            self._buttonRect = wx.Rect(rect.x, rect.y, 30, rect.height)
+        
+        return out_rect
+
+
+    def DrawFocusRectangle(self, dc, page, wnd, draw_text, text_offset, bitmap_offset, drawn_tab_yoff, drawn_tab_height, textx, texty):
+        """
+        Draws the focus rectangle on a tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `page`: the page associated with the tab;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `draw_text`: the text that has been drawn on the tab;
+        :param `text_offset`: the text offset on the tab;
+        :param `bitmap_offset`: the bitmap offset on the tab;
+        :param `drawn_tab_yoff`: the y offset of the tab text;
+        :param `drawn_tab_height`: the height of the tab;
+        :param `textx`: the x text extent;
+        :param `texty`: the y text extent.
+        """
+
+        if self.GetAGWFlags() & AUI_NB_NO_TAB_FOCUS:
+            return
+        
+        if page.active and wx.Window.FindFocus() == wnd:
+        
+            focusRectText = wx.Rect(text_offset, (drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2)),
+                                    textx, texty)
+
+            if page.bitmap.IsOk():
+                focusRectBitmap = wx.Rect(bitmap_offset, drawn_tab_yoff + (drawn_tab_height/2) - (page.bitmap.GetHeight()/2),
+                                          page.bitmap.GetWidth(), page.bitmap.GetHeight())
+
+            if page.bitmap.IsOk() and draw_text == "":
+                focusRect = wx.Rect(*focusRectBitmap)
+            elif not page.bitmap.IsOk() and draw_text != "":
+                focusRect = wx.Rect(*focusRectText)
+            elif page.bitmap.IsOk() and draw_text != "":
+                focusRect = focusRectText.Union(focusRectBitmap)
+
+            focusRect.Inflate(2, 2)
+
+            dc.SetBrush(wx.TRANSPARENT_BRUSH)
+            dc.SetPen(self._focusPen)
+            dc.DrawRoundedRectangleRect(focusRect, 2)
+        
+
+    def GetBestTabCtrlSize(self, wnd, pages, required_bmp_size):
+        """
+        Returns the best tab control size.
+
+        :param `wnd`: a `wx.Window` instance object;
+        :param `pages`: the pages associated with the tabs;
+        :param `required_bmp_size`: the size of the bitmap on the tabs.
+        """
+
+        dc = wx.ClientDC(wnd)
+        dc.SetFont(self._measuring_font)
+
+        # sometimes a standard bitmap size needs to be enforced, especially
+        # if some tabs have bitmaps and others don't.  This is important because
+        # it prevents the tab control from resizing when tabs are added.
+
+        measure_bmp = wx.NullBitmap
+        
+        if required_bmp_size.IsFullySpecified():
+            measure_bmp = wx.EmptyBitmap(required_bmp_size.x,
+                                         required_bmp_size.y)
+        
+        max_y = 0
+        
+        for page in pages:
+        
+            if measure_bmp.IsOk():
+                bmp = measure_bmp
+            else:
+                bmp = page.bitmap
+
+            # we don't use the caption text because we don't
+            # want tab heights to be different in the case
+            # of a very short piece of text on one tab and a very
+            # tall piece of text on another tab
+            s, x_ext = self.GetTabSize(dc, wnd, page.caption, bmp, True, AUI_BUTTON_STATE_HIDDEN, None)
+            max_y = max(max_y, s[1])
+
+            if page.control:
+                controlW, controlH = page.control.GetSize()
+                max_y = max(max_y, controlH+4)
+
+        return max_y + 2
+
+
+    def SetNormalFont(self, font):
+        """
+        Sets the normal font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._normal_font = font
+
+
+    def SetSelectedFont(self, font):
+        """
+        Sets the selected tab font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._selected_font = font
+
+
+    def SetMeasuringFont(self, font):
+        """
+        Sets the font for calculating text measurements.
+
+        :param `font`: a `wx.Font` object.
+        """
+
+        self._measuring_font = font
+
+
+    def GetNormalFont(self):
+        """ Returns the normal font for drawing tab labels. """
+
+        return self._normal_font
+
+
+    def GetSelectedFont(self):
+        """ Returns the selected tab font for drawing tab labels. """
+
+        return self._selected_font
+
+
+    def GetMeasuringFont(self):
+        """ Returns the font for calculating text measurements. """
+
+        return self._measuring_font
+    
+
+    def ShowDropDown(self, wnd, pages, active_idx):
+        """
+        Shows the drop-down window menu on the tab area.
+
+        :param `wnd`: a `wx.Window` derived window instance;
+        :param `pages`: the pages associated with the tabs;
+        :param `active_idx`: the active tab index.
+        """
+        
+        useImages = self.GetAGWFlags() & AUI_NB_USE_IMAGES_DROPDOWN
+        menuPopup = wx.Menu()
+
+        longest = 0
+        for i, page in enumerate(pages):
+        
+            caption = page.caption
+
+            # if there is no caption, make it a space.  This will prevent
+            # an assert in the menu code.
+            if caption == "":
+                caption = " "
+
+            # Save longest caption width for calculating menu width with
+            width = wnd.GetTextExtent(caption)[0]
+            if width > longest:
+                longest = width
+
+            if useImages:
+                menuItem = wx.MenuItem(menuPopup, 1000+i, caption)
+                if page.bitmap:
+                    menuItem.SetBitmap(page.bitmap)
+
+                menuPopup.AppendItem(menuItem)
+                
+            else:
+                
+                menuPopup.AppendCheckItem(1000+i, caption)
+                
+            menuPopup.Enable(1000+i, page.enabled)
+
+        if active_idx != -1 and not useImages:
+        
+            menuPopup.Check(1000+active_idx, True)
+        
+        # find out the screen coordinate at the bottom of the tab ctrl
+        cli_rect = wnd.GetClientRect()
+
+        # Calculate the approximate size of the popupmenu for setting the
+        # position of the menu when its shown.
+        # Account for extra padding on left/right of text on mac menus
+        if wx.Platform in ['__WXMAC__', '__WXMSW__']:
+            longest += 32
+
+        # Bitmap/Checkmark width + padding
+        longest += 20
+
+        if self.GetAGWFlags() & AUI_NB_CLOSE_BUTTON:
+            longest += 16
+
+        pt = wx.Point(cli_rect.x + cli_rect.GetWidth() - longest,
+                     cli_rect.y + cli_rect.height)
+
+        cc = AuiCommandCapture()
+        wnd.PushEventHandler(cc)
+        wnd.PopupMenu(menuPopup, pt)
+        command = cc.GetCommandId()
+        wnd.PopEventHandler(True)
+
+        if command >= 1000:
+            return command - 1000
+
+        return -1
+
+
+class AuiSimpleTabArt(object):
+    """ A simple-looking implementation of a tab art. """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        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._measuring_font = self._selected_font
+
+        self._agwFlags = 0
+        self._fixed_tab_width = 100
+
+        base_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
+
+        background_colour = base_colour
+        normaltab_colour = base_colour
+        selectedtab_colour = wx.WHITE
+
+        self._bkbrush = wx.Brush(background_colour)
+        self._normal_bkbrush = wx.Brush(normaltab_colour)
+        self._normal_bkpen = wx.Pen(normaltab_colour)
+        self._selected_bkbrush = wx.Brush(selectedtab_colour)
+        self._selected_bkpen = wx.Pen(selectedtab_colour)
+
+        self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
+        self._disabled_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        self._active_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.BLACK)
+        self._disabled_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        self._active_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.BLACK)
+        self._disabled_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.Colour(128, 128, 128))
+
+        self._active_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.BLACK)
+        self._disabled_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.Colour(128, 128, 128))
+
+
+    def Clone(self):
+        """ Clones the art object. """
+
+        art = type(self)()
+        art.SetNormalFont(self.GetNormalFont())
+        art.SetSelectedFont(self.GetSelectedFont())
+        art.SetMeasuringFont(self.GetMeasuringFont())
+
+        art = CopyAttributes(art, self)
+        return 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
+         ==================================== ==================================
+        
+        """
+
+        self._agwFlags = agwFlags
+
+
+    def GetAGWFlags(self):
+        """
+        Returns the tab art flags.
+
+        :see: L{SetAGWFlags} for a list of possible return values.
+        """
+
+        return self._agwFlags
+    
+
+    def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
+        """
+        Sets the tab sizing information.
+        
+        :param `tab_ctrl_size`: the size of the tab control area;
+        :param `tab_count`: the number of tabs;
+        :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
+         to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
+        """
+        
+        self._fixed_tab_width = 100
+        minTabWidth, maxTabWidth = minMaxTabWidth
+
+        tot_width = tab_ctrl_size.x - self.GetIndentSize() - 4
+
+        if self._agwFlags & AUI_NB_CLOSE_BUTTON:
+            tot_width -= self._active_close_bmp.GetWidth()
+        if self._agwFlags & AUI_NB_WINDOWLIST_BUTTON:
+            tot_width -= self._active_windowlist_bmp.GetWidth()
+
+        if tab_count > 0:
+            self._fixed_tab_width = tot_width/tab_count
+        
+        if self._fixed_tab_width < 100:
+            self._fixed_tab_width = 100
+
+        if self._fixed_tab_width > tot_width/2:
+            self._fixed_tab_width = tot_width/2
+
+        if self._fixed_tab_width > 220:
+            self._fixed_tab_width = 220
+
+        if minTabWidth > -1:
+            self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
+        if maxTabWidth > -1:
+            self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
+
+        self._tab_ctrl_height = tab_ctrl_size.y
+        
+
+    def DrawBackground(self, dc, wnd, rect):
+        """
+        Draws the tab area background.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `rect`: the tab control rectangle.
+        """
+        
+        # draw background
+        dc.SetBrush(self._bkbrush)
+        dc.SetPen(wx.TRANSPARENT_PEN)
+        dc.DrawRectangle(-1, -1, rect.GetWidth()+2, rect.GetHeight()+2)
+
+        # draw base line
+        dc.SetPen(wx.GREY_PEN)
+        dc.DrawLine(0, rect.GetHeight()-1, rect.GetWidth(), rect.GetHeight()-1)
+
+
+    def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
+        """
+        Draws a single tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `page`: the tab control page associated with the tab;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
+        """
+        
+        # if the caption is empty, measure some temporary text
+        caption = page.caption
+        if caption == "":
+            caption = "Xj"
+
+        agwFlags = self.GetAGWFlags()
+        
+        dc.SetFont(self._selected_font)
+        selected_textx, selected_texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        dc.SetFont(self._normal_font)
+        normal_textx, normal_texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        control = page.control
+
+        # figure out the size of the tab
+        tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
+                                             page.active, close_button_state, control)
+
+        tab_height = tab_size[1]
+        tab_width = tab_size[0]
+        tab_x = in_rect.x
+        tab_y = in_rect.y + in_rect.height - tab_height
+
+        caption = page.caption
+        # select pen, brush and font for the tab to be drawn
+
+        if page.active:
+        
+            dc.SetPen(self._selected_bkpen)
+            dc.SetBrush(self._selected_bkbrush)
+            dc.SetFont(self._selected_font)
+            textx = selected_textx
+            texty = selected_texty
+        
+        else:
+        
+            dc.SetPen(self._normal_bkpen)
+            dc.SetBrush(self._normal_bkbrush)
+            dc.SetFont(self._normal_font)
+            textx = normal_textx
+            texty = normal_texty
+
+        if not page.enabled:
+            dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
+        else:
+            dc.SetTextForeground(page.text_colour)
+        
+        # -- draw line --
+
+        points = [wx.Point() for i in xrange(7)]
+        points[0].x = tab_x
+        points[0].y = tab_y + tab_height - 1
+        points[1].x = tab_x + tab_height - 3
+        points[1].y = tab_y + 2
+        points[2].x = tab_x + tab_height + 3
+        points[2].y = tab_y
+        points[3].x = tab_x + tab_width - 2
+        points[3].y = tab_y
+        points[4].x = tab_x + tab_width
+        points[4].y = tab_y + 2
+        points[5].x = tab_x + tab_width
+        points[5].y = tab_y + tab_height - 1
+        points[6] = points[0]
+
+        dc.SetClippingRect(in_rect)
+        dc.DrawPolygon(points)
+
+        dc.SetPen(wx.GREY_PEN)
+        dc.DrawLines(points)
+
+        close_button_width = 0
+        
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+        
+            close_button_width = self._active_close_bmp.GetWidth()
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                if control:
+                    text_offset = tab_x + (tab_height/2) + close_button_width - (textx/2) - 2
+                else:
+                    text_offset = tab_x + (tab_height/2) + ((tab_width+close_button_width)/2) - (textx/2) - 2
+            else:
+                if control:
+                    text_offset = tab_x + (tab_height/2) + close_button_width - (textx/2)
+                else:
+                    text_offset = tab_x + (tab_height/2) + ((tab_width-close_button_width)/2) - (textx/2)
+        
+        else:
+        
+            text_offset = tab_x + (tab_height/3) + (tab_width/2) - (textx/2)
+            if control:
+                if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                    text_offset = tab_x + (tab_height/3) - (textx/2) + close_button_width + 2
+                else:
+                    text_offset = tab_x + (tab_height/3) - (textx/2)
+        
+        # set minimum text offset
+        if text_offset < tab_x + tab_height:
+            text_offset = tab_x + tab_height
+
+        # chop text if necessary
+        if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x))
+        else:
+            draw_text = ChopText(dc, caption,
+                                 tab_width - (text_offset-tab_x) - close_button_width)
+
+        ypos = (tab_y + tab_height)/2 - (texty/2) + 1
+
+        if control is not None:
+            if control.GetPosition() != wx.Point(text_offset+1, ypos):
+                control.SetPosition(wx.Point(text_offset+1, ypos))
+
+            if not control.IsShown():
+                control.Show()
+
+            if paint_control:
+                bmp = TakeScreenShot(control.GetScreenRect())
+                dc.DrawBitmap(bmp, text_offset+1, ypos, True)
+                
+            controlW, controlH = control.GetSize()
+            text_offset += controlW + 4
+
+        # draw tab text
+        rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
+        dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
+
+        # draw focus rectangle
+        if page.active and wx.Window.FindFocus() == wnd and (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
+        
+            focusRect = wx.Rect(text_offset, ((tab_y + tab_height)/2 - (texty/2) + 1),
+                                selected_textx, selected_texty)
+
+            focusRect.Inflate(2, 2)
+            # TODO:
+            # This should be uncommented when DrawFocusRect will become
+            # available in wxPython
+            # wx.RendererNative.Get().DrawFocusRect(wnd, dc, focusRect, 0)
+
+        out_button_rect = wx.Rect()        
+        # draw close button if necessary
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+        
+            if page.active:
+                bmp = self._active_close_bmp
+            else:
+                bmp = self._disabled_close_bmp
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                rect = wx.Rect(tab_x + tab_height - 2,
+                               tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
+                               close_button_width, tab_height - 1)
+            else:                
+                rect = wx.Rect(tab_x + tab_width - close_button_width - 1,
+                               tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
+                               close_button_width, tab_height - 1)
+            
+            self.DrawButtons(dc, rect, bmp, wx.WHITE, close_button_state)
+            out_button_rect = wx.Rect(*rect)
+        
+        out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
+        dc.DestroyClippingRegion()
+
+        return out_tab_rect, out_button_rect, x_extent  
+
+
+    def DrawButtons(self, dc, _rect, bmp, bkcolour, button_state):
+        """
+        Convenience method to draw tab buttons.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `_rect`: the tab rectangle;
+        :param `bmp`: the tab bitmap;
+        :param `bkcolour`: the tab background colour;
+        :param `button_state`: the state of the tab button.
+        """
+
+        rect = wx.Rect(*_rect)
+
+        if button_state == AUI_BUTTON_STATE_PRESSED:
+            rect.x += 1
+            rect.y += 1
+
+        if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]:
+            dc.SetBrush(wx.Brush(StepColour(bkcolour, 120)))
+            dc.SetPen(wx.Pen(StepColour(bkcolour, 75)))
+
+            # draw the background behind the button
+            dc.DrawRectangle(rect.x, rect.y, 15, 15)
+
+        # draw the button itself
+        dc.DrawBitmap(bmp, rect.x, rect.y, True)
+
+    
+    def GetIndentSize(self):
+        """ Returns the tabs indent size. """
+        
+        return 0
+
+
+    def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
+        """
+        Returns the tab size for the given caption, bitmap and button state.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `caption`: the tab text caption;
+        :param `bitmap`: the bitmap displayed on the tab;
+        :param `active`: whether the tab is selected or not;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+        
+        dc.SetFont(self._measuring_font)
+        measured_textx, measured_texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        tab_height = measured_texty + 4
+        tab_width = measured_textx + tab_height + 5
+
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            tab_width += self._active_close_bmp.GetWidth()
+
+        if self._agwFlags & AUI_NB_TAB_FIXED_WIDTH:
+            tab_width = self._fixed_tab_width
+
+        if control is not None:
+            controlW, controlH = control.GetSize()
+            tab_width += controlW + 4
+
+        x_extent = tab_width - (tab_height/2) - 1
+
+        return (tab_width, tab_height), x_extent
+
+
+    def DrawButton(self, dc, wnd, in_rect, button, orientation):
+        """
+        Draws a button on the tab or on the tab area, depending on the button identifier. 
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `button`: an instance of the button class;
+        :param `orientation`: the tab orientation.
+        """
+
+        bitmap_id, button_state = button.id, button.cur_state
+        
+        if bitmap_id == AUI_BUTTON_CLOSE:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_close_bmp
+            else:
+                bmp = self._active_close_bmp
+
+        elif bitmap_id == AUI_BUTTON_LEFT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_left_bmp
+            else:
+                bmp = self._active_left_bmp
+
+        elif bitmap_id == AUI_BUTTON_RIGHT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_right_bmp
+            else:
+                bmp = self._active_right_bmp
+
+        elif bitmap_id == AUI_BUTTON_WINDOWLIST:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = self._disabled_windowlist_bmp
+            else:
+                bmp = self._active_windowlist_bmp
+
+        else:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                bmp = button.dis_bitmap
+            else:
+                bmp = button.bitmap
+            
+        if not bmp.IsOk():
+            return
+
+        rect = wx.Rect(*in_rect)
+
+        if orientation == wx.LEFT:
+        
+            rect.SetX(in_rect.x)
+            rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2))
+            rect.SetWidth(bmp.GetWidth())
+            rect.SetHeight(bmp.GetHeight())
+        
+        else:
+        
+            rect = wx.Rect(in_rect.x + in_rect.width - bmp.GetWidth(),
+                           ((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
+                           bmp.GetWidth(), bmp.GetHeight())
+
+        self.DrawButtons(dc, rect, bmp, wx.WHITE, button_state)
+
+        out_rect = wx.Rect(*rect)
+        return out_rect
+
+
+    def ShowDropDown(self, wnd, pages, active_idx):
+        """
+        Shows the drop-down window menu on the tab area.
+
+        :param `wnd`: a `wx.Window` derived window instance;
+        :param `pages`: the pages associated with the tabs;
+        :param `active_idx`: the active tab index.
+        """
+        
+        menuPopup = wx.Menu()
+        useImages = self.GetAGWFlags() & AUI_NB_USE_IMAGES_DROPDOWN
+        
+        for i, page in enumerate(pages):
+
+            if useImages:
+                menuItem = wx.MenuItem(menuPopup, 1000+i, page.caption)
+                if page.bitmap:
+                    menuItem.SetBitmap(page.bitmap)
+
+                menuPopup.AppendItem(menuItem)
+                
+            else:
+                
+                menuPopup.AppendCheckItem(1000+i, page.caption)
+                
+            menuPopup.Enable(1000+i, page.enabled)
+        
+        if active_idx != -1 and not useImages:
+            menuPopup.Check(1000+active_idx, True)
+        
+        # find out where to put the popup menu of window
+        # items.  Subtract 100 for now to center the menu
+        # a bit, until a better mechanism can be implemented
+        pt = wx.GetMousePosition()
+        pt = wnd.ScreenToClient(pt)
+        
+        if pt.x < 100:
+            pt.x = 0
+        else:
+            pt.x -= 100
+
+        # find out the screen coordinate at the bottom of the tab ctrl
+        cli_rect = wnd.GetClientRect()
+        pt.y = cli_rect.y + cli_rect.height
+
+        cc = AuiCommandCapture()
+        wnd.PushEventHandler(cc)
+        wnd.PopupMenu(menuPopup, pt)
+        command = cc.GetCommandId()
+        wnd.PopEventHandler(True)
+
+        if command >= 1000:
+            return command-1000
+
+        return -1
+
+
+    def GetBestTabCtrlSize(self, wnd, pages, required_bmp_size):
+        """
+        Returns the best tab control size.
+
+        :param `wnd`: a `wx.Window` instance object;
+        :param `pages`: the pages associated with the tabs;
+        :param `required_bmp_size`: the size of the bitmap on the tabs.
+        """
+        
+        dc = wx.ClientDC(wnd)
+        dc.SetFont(self._measuring_font)
+        s, x_extent = self.GetTabSize(dc, wnd, "ABCDEFGHIj", wx.NullBitmap, True,
+                                      AUI_BUTTON_STATE_HIDDEN, None)
+
+        max_y = s[1]
+
+        for page in pages:
+            if page.control:
+                controlW, controlH = page.control.GetSize()
+                max_y = max(max_y, controlH+4)
+                
+            textx, texty, dummy = dc.GetMultiLineTextExtent(page.caption)
+            max_y = max(max_y, texty)
+        
+        return max_y + 3
+
+
+    def SetNormalFont(self, font):
+        """
+        Sets the normal font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+        
+        self._normal_font = font
+
+
+    def SetSelectedFont(self, font):
+        """
+        Sets the selected tab font for drawing tab labels.
+
+        :param `font`: a `wx.Font` object.
+        """
+        
+        self._selected_font = font
+
+
+    def SetMeasuringFont(self, font):
+        """
+        Sets the font for calculating text measurements.
+
+        :param `font`: a `wx.Font` object.
+        """
+        
+        self._measuring_font = font
+
+
+    def GetNormalFont(self):
+        """ Returns the normal font for drawing tab labels. """
+
+        return self._normal_font
+
+
+    def GetSelectedFont(self):
+        """ Returns the selected tab font for drawing tab labels. """
+
+        return self._selected_font
+
+
+    def GetMeasuringFont(self):
+        """ Returns the font for calculating text measurements. """
+
+        return self._measuring_font
+
+
+    def SetCustomButton(self, bitmap_id, button_state, bmp):
+        """
+        Sets a custom bitmap for the close, left, right and window list
+        buttons.
+        
+        :param `bitmap_id`: the button identifier;
+        :param `button_state`: the button state;
+        :param `bmp`: the custom bitmap to use for the button.
+        """
+        
+        if bitmap_id == AUI_BUTTON_CLOSE:
+            if button_state == AUI_BUTTON_STATE_NORMAL:
+                self._active_close_bmp = bmp
+                self._hover_close_bmp = self._active_close_bmp
+                self._pressed_close_bmp = self._active_close_bmp
+                self._disabled_close_bmp = self._active_close_bmp
+                    
+            elif button_state == AUI_BUTTON_STATE_HOVER:
+                self._hover_close_bmp = bmp
+            elif button_state == AUI_BUTTON_STATE_PRESSED:
+                self._pressed_close_bmp = bmp
+            else:
+                self._disabled_close_bmp = bmp
+
+        elif bitmap_id == AUI_BUTTON_LEFT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                self._disabled_left_bmp = bmp
+            else:
+                self._active_left_bmp = bmp
+
+        elif bitmap_id == AUI_BUTTON_RIGHT:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                self._disabled_right_bmp = bmp
+            else:
+                self._active_right_bmp = bmp
+
+        elif bitmap_id == AUI_BUTTON_WINDOWLIST:
+            if button_state & AUI_BUTTON_STATE_DISABLED:
+                self._disabled_windowlist_bmp = bmp
+            else:
+                self._active_windowlist_bmp = bmp
+    
+
+class VC71TabArt(AuiDefaultTabArt):
+    """ A class to draw tabs using the Visual Studio 2003 (VC71) style. """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        AuiDefaultTabArt.__init__(self)
+
+
+    def Clone(self):
+        """ Clones the art object. """
+
+        art = type(self)()
+        art.SetNormalFont(self.GetNormalFont())
+        art.SetSelectedFont(self.GetSelectedFont())
+        art.SetMeasuringFont(self.GetMeasuringFont())
+
+        art = CopyAttributes(art, self)
+        return art
+
+
+    def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
+        """
+        Draws a single tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `page`: the tab control page associated with the tab;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
+        """
+        
+        # Visual studio 7.1 style
+        # This code is based on the renderer included in FlatNotebook
+
+        # figure out the size of the tab
+
+        control = page.control
+        tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
+                                             close_button_state, control)
+
+        tab_height = self._tab_ctrl_height - 3
+        tab_width = tab_size[0]
+        tab_x = in_rect.x
+        tab_y = in_rect.y + in_rect.height - tab_height
+        clip_width = tab_width
+
+        if tab_x + clip_width > in_rect.x + in_rect.width - 4:
+            clip_width = (in_rect.x + in_rect.width) - tab_x - 4
+            
+        dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
+        agwFlags = self.GetAGWFlags()
+
+        if agwFlags & AUI_NB_BOTTOM:
+            tab_y -= 1
+
+        dc.SetPen((page.active and [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DHIGHLIGHT))] or \
+                   [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW))])[0])
+        dc.SetBrush((page.active and [wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))] or \
+                     [wx.TRANSPARENT_BRUSH])[0])
+
+        if page.active:
+
+            tabH = tab_height - 2
+            dc.DrawRectangle(tab_x, tab_y, tab_width, tabH)
+
+            rightLineY1 = (agwFlags & AUI_NB_BOTTOM and [vertical_border_padding - 2] or \
+                           [vertical_border_padding - 1])[0]
+            rightLineY2 = tabH + 3
+            dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW)))
+            dc.DrawLine(tab_x + tab_width - 1, rightLineY1 + 1, tab_x + tab_width - 1, rightLineY2)
+            
+            if agwFlags & AUI_NB_BOTTOM:
+                dc.DrawLine(tab_x + 1, rightLineY2 - 3 , tab_x + tab_width - 1, rightLineY2 - 3)
+                
+            dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)))
+            dc.DrawLine(tab_x + tab_width, rightLineY1, tab_x + tab_width, rightLineY2)
+            
+            if agwFlags & AUI_NB_BOTTOM:
+                dc.DrawLine(tab_x, rightLineY2 - 2, tab_x + tab_width, rightLineY2 - 2)
+
+        else:
+        
+            # We dont draw a rectangle for non selected tabs, but only
+            # vertical line on the right
+            blackLineY1 = (agwFlags & AUI_NB_BOTTOM and [vertical_border_padding + 2] or \
+                           [vertical_border_padding + 1])[0]
+            blackLineY2 = tab_height - 5
+            dc.DrawLine(tab_x + tab_width, blackLineY1, tab_x + tab_width, blackLineY2)
+        
+        border_points = [0, 0]
+        
+        if agwFlags & AUI_NB_BOTTOM:
+        
+            border_points[0] = wx.Point(tab_x, tab_y)
+            border_points[1] = wx.Point(tab_x, tab_y + tab_height - 6)
+        
+        else: # if (agwFlags & AUI_NB_TOP)
+        
+            border_points[0] = wx.Point(tab_x, tab_y + tab_height - 4)
+            border_points[1] = wx.Point(tab_x, tab_y + 2)
+
+        drawn_tab_yoff = border_points[1].y
+        drawn_tab_height = border_points[0].y - border_points[1].y
+
+        text_offset = tab_x + 8
+        close_button_width = 0
+
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            close_button_width = self._active_close_bmp.GetWidth()
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                text_offset += close_button_width - 5
+
+        if not page.enabled:
+            dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
+            pagebitmap = page.dis_bitmap
+        else:
+            dc.SetTextForeground(page.text_colour)
+            pagebitmap = page.bitmap
+
+        shift = 0
+        if agwFlags & AUI_NB_BOTTOM:
+            shift = (page.active and [1] or [2])[0]
+            
+        bitmap_offset = 0
+        if pagebitmap.IsOk():
+            bitmap_offset = tab_x + 8
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
+                bitmap_offset += close_button_width - 5
+
+            # draw bitmap
+            dc.DrawBitmap(pagebitmap, bitmap_offset,
+                          drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
+                          True)
+
+            text_offset = bitmap_offset + pagebitmap.GetWidth()
+            text_offset += 3 # bitmap padding
+        
+        else:
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
+                text_offset = tab_x + 8
+        
+        # if the caption is empty, measure some temporary text
+        caption = page.caption
+
+        if caption == "":
+            caption = "Xj"
+
+        if page.active:
+            dc.SetFont(self._selected_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+        else:
+            dc.SetFont(self._normal_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
+
+        ypos = drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2) - 1 + shift
+
+        offset_focus = text_offset
+        
+        if control is not None:
+            if control.GetPosition() != wx.Point(text_offset+1, ypos):
+                control.SetPosition(wx.Point(text_offset+1, ypos))
+
+            if not control.IsShown():
+                control.Show()
+
+            if paint_control:
+                bmp = TakeScreenShot(control.GetScreenRect())
+                dc.DrawBitmap(bmp, text_offset+1, ypos, True)
+                
+            controlW, controlH = control.GetSize()
+            text_offset += controlW + 4
+            textx += controlW + 4
+
+        # draw tab text
+        rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
+        dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
+
+        out_button_rect = wx.Rect()
+
+        # draw focus rectangle
+        if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
+            self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
+                                    drawn_tab_height+shift, rectx, recty)
+                
+        # draw 'x' on tab (if enabled)
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            close_button_width = self._active_close_bmp.GetWidth()
+
+            bmp = self._disabled_close_bmp
+
+            if close_button_state == AUI_BUTTON_STATE_HOVER:
+                bmp = self._hover_close_bmp
+            elif close_button_state == AUI_BUTTON_STATE_PRESSED:
+                bmp = self._pressed_close_bmp
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                rect = wx.Rect(tab_x + 4,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
+                               close_button_width, tab_height)
+            else:
+                rect = wx.Rect(tab_x + tab_width - close_button_width - 3,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
+                               close_button_width, tab_height)
+
+            # Indent the button if it is pressed down:
+            rect = IndentPressedBitmap(rect, close_button_state)
+            dc.DrawBitmap(bmp, rect.x, rect.y, True)
+
+            out_button_rect = rect        
+
+        out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
+        dc.DestroyClippingRegion()
+
+        return out_tab_rect, out_button_rect, x_extent
+
+
+class FF2TabArt(AuiDefaultTabArt):
+    """ A class to draw tabs using the Firefox 2 (FF2) style. """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        AuiDefaultTabArt.__init__(self)
+
+
+    def Clone(self):
+        """ Clones the art object. """
+
+        art = type(self)()
+        art.SetNormalFont(self.GetNormalFont())
+        art.SetSelectedFont(self.GetSelectedFont())
+        art.SetMeasuringFont(self.GetMeasuringFont())
+
+        art = CopyAttributes(art, self)
+        return art
+
+
+    def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control):
+        """
+        Returns the tab size for the given caption, bitmap and button state.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `caption`: the tab text caption;
+        :param `bitmap`: the bitmap displayed on the tab;
+        :param `active`: whether the tab is selected or not;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+        
+        tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
+                                                         active, close_button_state, control)
+
+        tab_width, tab_height = tab_size        
+
+        # add some vertical padding
+        tab_height += 2
+        
+        return (tab_width, tab_height), x_extent
+
+
+    def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
+        """
+        Draws a single tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `page`: the tab control page associated with the tab;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
+        """
+        
+        # Firefox 2 style
+
+        control = page.control
+
+        # figure out the size of the tab
+        tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
+                                             page.active, close_button_state, control)
+
+        tab_height = self._tab_ctrl_height - 2
+        tab_width = tab_size[0]
+        tab_x = in_rect.x
+        tab_y = in_rect.y + in_rect.height - tab_height
+
+        clip_width = tab_width
+        if tab_x + clip_width > in_rect.x + in_rect.width - 4:
+            clip_width = (in_rect.x + in_rect.width) - tab_x - 4
+            
+        dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
+
+        tabPoints = [wx.Point() for i in xrange(7)]
+        
+        adjust = 0
+        if not page.active:
+            adjust = 1
+
+        agwFlags = self.GetAGWFlags()
+        
+        tabPoints[0].x = tab_x + 3
+        tabPoints[0].y = (agwFlags & AUI_NB_BOTTOM and [3] or [tab_height - 2])[0]
+
+        tabPoints[1].x = tabPoints[0].x
+        tabPoints[1].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - (vertical_border_padding + 2) - adjust] or \
+                          [(vertical_border_padding + 2) + adjust])[0]
+
+        tabPoints[2].x = tabPoints[1].x+2
+        tabPoints[2].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding - adjust] or \
+                          [vertical_border_padding + adjust])[0]
+
+        tabPoints[3].x = tab_x + tab_width - 2
+        tabPoints[3].y = tabPoints[2].y
+
+        tabPoints[4].x = tabPoints[3].x + 2
+        tabPoints[4].y = tabPoints[1].y
+
+        tabPoints[5].x = tabPoints[4].x
+        tabPoints[5].y = tabPoints[0].y
+
+        tabPoints[6].x = tabPoints[0].x
+        tabPoints[6].y = tabPoints[0].y
+
+        rr = wx.RectPP(tabPoints[2], tabPoints[5])
+        self.DrawTabBackground(dc, rr, page.active, (agwFlags & AUI_NB_BOTTOM) == 0)
+
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
+
+        # Draw the tab as rounded rectangle
+        dc.DrawPolygon(tabPoints)
+
+        if page.active:
+            dc.DrawLine(tabPoints[0].x + 1, tabPoints[0].y, tabPoints[5].x , tabPoints[0].y)
+        
+        drawn_tab_yoff = tabPoints[1].y
+        drawn_tab_height = tabPoints[0].y - tabPoints[2].y
+
+        text_offset = tab_x + 8
+        close_button_width = 0
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            close_button_width = self._active_close_bmp.GetWidth()
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                text_offset += close_button_width - 4
+
+        if not page.enabled:
+            dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
+            pagebitmap = page.dis_bitmap
+        else:
+            dc.SetTextForeground(page.text_colour)
+            pagebitmap = page.bitmap
+
+        shift = -1
+        if agwFlags & AUI_NB_BOTTOM:
+            shift = 2
+        
+        bitmap_offset = 0
+        if pagebitmap.IsOk():
+            bitmap_offset = tab_x + 8
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
+                bitmap_offset += close_button_width - 4
+
+            # draw bitmap
+            dc.DrawBitmap(pagebitmap, bitmap_offset,
+                          drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
+                          True)
+
+            text_offset = bitmap_offset + pagebitmap.GetWidth()
+            text_offset += 3 # bitmap padding
+        
+        else:
+        
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
+                text_offset = tab_x + 8
+        
+        # if the caption is empty, measure some temporary text
+        caption = page.caption
+        if caption == "":
+            caption = "Xj"
+
+        if page.active:
+            dc.SetFont(self._selected_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+        else:
+            dc.SetFont(self._normal_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width + 1)
+        else:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
+
+        ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1 + shift
+
+        offset_focus = text_offset
+        
+        if control is not None:
+            if control.GetPosition() != wx.Point(text_offset+1, ypos):
+                control.SetPosition(wx.Point(text_offset+1, ypos))
+
+            if not control.IsShown():
+                control.Show()
+
+            if paint_control:
+                bmp = TakeScreenShot(control.GetScreenRect())
+                dc.DrawBitmap(bmp, text_offset+1, ypos, True)
+                
+            controlW, controlH = control.GetSize()
+            text_offset += controlW + 4
+            textx += controlW + 4
+        
+        # draw tab text
+        rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
+        dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
+
+        # draw focus rectangle
+        if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
+            self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
+                                    drawn_tab_height, rectx, recty)
+        
+        out_button_rect = wx.Rect()
+        # draw 'x' on tab (if enabled)
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+        
+            close_button_width = self._active_close_bmp.GetWidth()
+            bmp = self._disabled_close_bmp
+
+            if close_button_state == AUI_BUTTON_STATE_HOVER:
+                bmp = self._hover_close_bmp
+            elif close_button_state == AUI_BUTTON_STATE_PRESSED:
+                bmp = self._pressed_close_bmp
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                rect = wx.Rect(tab_x + 5,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
+                               close_button_width, tab_height)
+            else:
+                rect = wx.Rect(tab_x + tab_width - close_button_width - 3,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
+                               close_button_width, tab_height)
+
+            # Indent the button if it is pressed down:
+            rect = IndentPressedBitmap(rect, close_button_state)
+            dc.DrawBitmap(bmp, rect.x, rect.y, True)
+            out_button_rect = rect
+        
+        out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
+        dc.DestroyClippingRegion()
+    
+        return out_tab_rect, out_button_rect, x_extent
+
+
+    def DrawTabBackground(self, dc, rect, focus, upperTabs):
+        """
+        Draws the tab background for the Firefox 2 style.
+        This is more consistent with L{FlatNotebook} than before.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `rect`: rectangle the tab should be confined to;
+        :param `focus`: whether the tab has focus or not;
+        :param `upperTabs`: whether the style is ``AUI_NB_TOP`` or ``AUI_NB_BOTTOM``.
+        """
+
+        # Define the rounded rectangle base on the given rect
+        # we need an array of 9 points for it
+        regPts = [wx.Point() for indx in xrange(9)]
+
+        if focus:
+            if upperTabs:
+                leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*8)
+                rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*8)
+            else:
+                leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*5)
+                rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*5)
+        else:
+            leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
+            rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 2))
+
+        # Define the top region
+        top = wx.RectPP(rect.GetTopLeft(), rightPt)
+        bottom = wx.RectPP(leftPt, rect.GetBottomRight())
+
+        topStartColour = wx.WHITE
+
+        if not focus:
+            topStartColour = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 50)
+
+        topEndColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
+        bottomStartColour = topEndColour
+        bottomEndColour = topEndColour
+
+        # Incase we use bottom tabs, switch the colours
+        if upperTabs:
+            if focus:
+                dc.GradientFillLinear(top, topStartColour, topEndColour, wx.SOUTH)
+                dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
+            else:
+                dc.GradientFillLinear(top, topEndColour , topStartColour, wx.SOUTH)
+                dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
+
+        else:
+            if focus:
+                dc.GradientFillLinear(bottom, topEndColour, bottomEndColour, wx.SOUTH)
+                dc.GradientFillLinear(top, topStartColour, topStartColour, wx.SOUTH)
+            else:
+                dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
+                dc.GradientFillLinear(top, topEndColour, topStartColour, wx.SOUTH)
+        
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+
+class VC8TabArt(AuiDefaultTabArt):
+    """ A class to draw tabs using the Visual Studio 2005 (VC8) style. """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        AuiDefaultTabArt.__init__(self)
+
+
+    def Clone(self):
+        """ Clones the art object. """
+
+        art = type(self)()
+        art.SetNormalFont(self.GetNormalFont())
+        art.SetSelectedFont(self.GetSelectedFont())
+        art.SetMeasuringFont(self.GetMeasuringFont())
+
+        art = CopyAttributes(art, self)
+        return art
+
+
+    def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
+        """
+        Sets the tab sizing information.
+        
+        :param `tab_ctrl_size`: the size of the tab control area;
+        :param `tab_count`: the number of tabs;
+        :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
+         to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
+        """
+        
+        AuiDefaultTabArt.SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth)
+
+        minTabWidth, maxTabWidth = minMaxTabWidth
+        if minTabWidth > -1:
+            self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
+        if maxTabWidth > -1:
+            self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
+        
+        self._fixed_tab_width -= 5
+
+
+    def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
+        """
+        Returns the tab size for the given caption, bitmap and button state.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `caption`: the tab text caption;
+        :param `bitmap`: the bitmap displayed on the tab;
+        :param `active`: whether the tab is selected or not;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+        
+        tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
+                                                         active, close_button_state, control)
+
+        tab_width, tab_height = tab_size        
+
+        # add some padding
+        tab_width += 10
+        tab_height += 2
+
+        return (tab_width, tab_height), x_extent
+
+
+    def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
+        """
+        Draws a single tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `page`: the tab control page associated with the tab;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
+        """
+        
+        # Visual Studio 8 style
+
+        control = page.control
+
+        # figure out the size of the tab
+        tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
+                                             page.active, close_button_state, control)
+
+        tab_height = self._tab_ctrl_height - 1
+        tab_width = tab_size[0]
+        tab_x = in_rect.x
+        tab_y = in_rect.y + in_rect.height - tab_height
+
+        clip_width = tab_width + 3
+        if tab_x + clip_width > in_rect.x + in_rect.width - 4:
+            clip_width = (in_rect.x + in_rect.width) - tab_x - 4
+        
+        tabPoints = [wx.Point() for i in xrange(8)]
+
+        # If we draw the first tab or the active tab, 
+        # we draw a full tab, else we draw a truncated tab
+        #
+        #             X(2)                  X(3)
+        #        X(1)                            X(4)
+        #                                          
+        #                                           X(5)
+        #                                           
+        # X(0),(7)                                  X(6)
+        #
+        #
+
+        adjust = 0
+        if not page.active:
+            adjust = 1
+
+        agwFlags = self.GetAGWFlags()
+        tabPoints[0].x = (agwFlags & AUI_NB_BOTTOM and [tab_x] or [tab_x + adjust])[0]
+        tabPoints[0].y = (agwFlags & AUI_NB_BOTTOM and [2] or [tab_height - 3])[0]
+
+        tabPoints[1].x = tabPoints[0].x + tab_height - vertical_border_padding - 3 - adjust
+        tabPoints[1].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - (vertical_border_padding+2)] or \
+                          [(vertical_border_padding+2)])[0]
+
+        tabPoints[2].x = tabPoints[1].x + 4
+        tabPoints[2].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding] or \
+                          [vertical_border_padding])[0]
+
+        tabPoints[3].x = tabPoints[2].x + tab_width - tab_height + vertical_border_padding
+        tabPoints[3].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding] or \
+                          [vertical_border_padding])[0]
+
+        tabPoints[4].x = tabPoints[3].x + 1
+        tabPoints[4].y = (agwFlags & AUI_NB_BOTTOM and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0]
+
+        tabPoints[5].x = tabPoints[4].x + 1
+        tabPoints[5].y = (agwFlags & AUI_NB_BOTTOM and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0]
+
+        tabPoints[6].x = tabPoints[2].x + tab_width - tab_height + 2 + vertical_border_padding
+        tabPoints[6].y = tabPoints[0].y
+
+        tabPoints[7].x = tabPoints[0].x
+        tabPoints[7].y = tabPoints[0].y
+
+        self.FillVC8GradientColour(dc, tabPoints, page.active)        
+
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+        dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
+        dc.DrawPolygon(tabPoints)
+
+        if page.active:
+            # Delete the bottom line (or the upper one, incase we use wxBOTTOM) 
+            dc.SetPen(wx.WHITE_PEN)
+            dc.DrawLine(tabPoints[0].x, tabPoints[0].y, tabPoints[6].x, tabPoints[6].y)
+
+        dc.SetClippingRegion(tab_x, tab_y, clip_width + 2, tab_height - 3)            
+
+        drawn_tab_yoff = tabPoints[1].y
+        drawn_tab_height = tabPoints[0].y - tabPoints[2].y
+
+        text_offset = tab_x + 20
+        close_button_width = 0
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            close_button_width = self._active_close_bmp.GetWidth()
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                text_offset += close_button_width
+
+        if not page.enabled:
+            dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
+            pagebitmap = page.dis_bitmap
+        else:
+            dc.SetTextForeground(page.text_colour)
+            pagebitmap = page.bitmap
+
+        shift = 0
+        if agwFlags & AUI_NB_BOTTOM:
+            shift = (page.active and [1] or [2])[0]
+        
+        bitmap_offset = 0
+        if pagebitmap.IsOk():
+            bitmap_offset = tab_x + 20
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
+                bitmap_offset += close_button_width
+
+            # draw bitmap
+            dc.DrawBitmap(pagebitmap, bitmap_offset,
+                          drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
+                          True)
+
+            text_offset = bitmap_offset + pagebitmap.GetWidth()
+            text_offset += 3 # bitmap padding
+        
+        else:
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
+                text_offset = tab_x + tab_height
+        
+        # if the caption is empty, measure some temporary text
+        caption = page.caption
+        if caption == "":
+            caption = "Xj"
+
+        if page.active:
+            dc.SetFont(self._selected_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+        else:
+            dc.SetFont(self._normal_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x))
+        else:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
+
+        ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1 + shift
+
+        offset_focus = text_offset
+        
+        if control is not None:
+            if control.GetPosition() != wx.Point(text_offset+1, ypos):
+                control.SetPosition(wx.Point(text_offset+1, ypos))
+
+            if not control.IsShown():
+                control.Show()
+
+            if paint_control:
+                bmp = TakeScreenShot(control.GetScreenRect())
+                dc.DrawBitmap(bmp, text_offset+1, ypos, True)
+                
+            controlW, controlH = control.GetSize()
+            text_offset += controlW + 4
+            textx += controlW + 4
+
+        # draw tab text
+        rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
+        dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
+        
+        # draw focus rectangle
+        if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
+            self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
+                                    drawn_tab_height+shift, rectx, recty)
+        
+        out_button_rect = wx.Rect()
+        # draw 'x' on tab (if enabled)
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+        
+            close_button_width = self._active_close_bmp.GetWidth()
+            bmp = self._disabled_close_bmp
+
+            if close_button_state == AUI_BUTTON_STATE_HOVER:
+                bmp = self._hover_close_bmp
+            elif close_button_state == AUI_BUTTON_STATE_PRESSED:
+                bmp = self._pressed_close_bmp
+                
+            if page.active:
+                xpos = tab_x + tab_width - close_button_width + 3
+            else:
+                xpos = tab_x + tab_width - close_button_width - 5
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                rect = wx.Rect(tab_x + 20,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
+                               close_button_width, tab_height)
+            else:
+                rect = wx.Rect(xpos,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
+                               close_button_width, tab_height)
+
+            # Indent the button if it is pressed down:
+            rect = IndentPressedBitmap(rect, close_button_state)
+            dc.DrawBitmap(bmp, rect.x, rect.y, True)
+            out_button_rect = rect
+        
+        out_tab_rect = wx.Rect(tab_x, tab_y, x_extent, tab_height)
+        dc.DestroyClippingRegion()
+
+        return out_tab_rect, out_button_rect, x_extent
+        
+
+    def FillVC8GradientColour(self, dc, tabPoints, active):
+        """
+        Fills the tab with the Visual Studio 2005 gradient background.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `tabPoints`: a list of `wx.Point` objects describing the tab shape;
+        :param `active`: whether the tab is selected or not.
+        """
+
+        xList = [pt.x for pt in tabPoints]
+        yList = [pt.y for pt in tabPoints]
+        
+        minx, maxx = min(xList), max(xList)
+        miny, maxy = min(yList), max(yList)
+
+        rect = wx.Rect(minx, maxy, maxx-minx, miny-maxy+1)        
+        region = wx.RegionFromPoints(tabPoints)
+
+        if self._buttonRect.width > 0:
+            buttonRegion = wx.Region(*self._buttonRect)
+            region.XorRegion(buttonRegion)
+        
+        dc.SetClippingRegionAsRegion(region)
+
+        if active:
+            bottom_colour = top_colour = wx.WHITE
+        else:
+            bottom_colour = StepColour(self._base_colour, 90)
+            top_colour = StepColour(self._base_colour, 170)
+
+        dc.GradientFillLinear(rect, top_colour, bottom_colour, wx.SOUTH)
+        dc.DestroyClippingRegion()
+        
+
+class ChromeTabArt(AuiDefaultTabArt):
+    """
+    A class to draw tabs using the Google Chrome browser style.
+    It uses custom bitmap to render the tabs, so that the look and feel is as close
+    as possible to the Chrome style.
+    """
+
+    def __init__(self):
+        """ Default class constructor. """
+
+        AuiDefaultTabArt.__init__(self)
+
+        self.SetBitmaps(mirror=False)
+        
+        closeBmp = tab_close.GetBitmap()
+        closeHBmp = tab_close_h.GetBitmap()
+        closePBmp = tab_close_p.GetBitmap()
+
+        self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_NORMAL, closeBmp)
+        self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_HOVER, closeHBmp)
+        self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_PRESSED, closePBmp)
+        
+
+    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
+         ==================================== ==================================
+
+        :note: Overridden from L{AuiDefaultTabArt}.
+        """
+
+        if agwFlags & AUI_NB_TOP:
+            self.SetBitmaps(mirror=False)
+        elif agwFlags & AUI_NB_BOTTOM:
+            self.SetBitmaps(mirror=True)
+
+        AuiDefaultTabArt.SetAGWFlags(self, agwFlags)            
+
+
+    def SetBitmaps(self, mirror):
+        """
+        Assigns the tab custom bitmaps
+
+        :param `mirror`: whether to vertically mirror the bitmap or not.
+        """
+
+        bmps = [tab_active_left.GetBitmap(), tab_active_center.GetBitmap(),
+                tab_active_right.GetBitmap(), tab_inactive_left.GetBitmap(),
+                tab_inactive_center.GetBitmap(), tab_inactive_right.GetBitmap()]
+
+        if mirror:
+            for indx, bmp in enumerate(bmps):
+                img = bmp.ConvertToImage()
+                img = img.Mirror(horizontally=False)
+                bmps[indx] = img.ConvertToBitmap()
+                
+        self._leftActiveBmp = bmps[0]
+        self._centerActiveBmp = bmps[1]
+        self._rightActiveBmp = bmps[2]
+        self._leftInactiveBmp = bmps[3]
+        self._centerInactiveBmp = bmps[4]
+        self._rightInactiveBmp = bmps[5]
+            
+
+    def Clone(self):
+        """ Clones the art object. """
+
+        art = type(self)()
+        art.SetNormalFont(self.GetNormalFont())
+        art.SetSelectedFont(self.GetSelectedFont())
+        art.SetMeasuringFont(self.GetMeasuringFont())
+
+        art = CopyAttributes(art, self)
+        return art
+
+
+    def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
+        """
+        Sets the tab sizing information.
+        
+        :param `tab_ctrl_size`: the size of the tab control area;
+        :param `tab_count`: the number of tabs;
+        :param `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
+         to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
+        """
+        
+        AuiDefaultTabArt.SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth)
+
+        minTabWidth, maxTabWidth = minMaxTabWidth
+        if minTabWidth > -1:
+            self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
+        if maxTabWidth > -1:
+            self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
+
+        self._fixed_tab_width -= 5
+
+
+    def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
+        """
+        Returns the tab size for the given caption, bitmap and button state.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `caption`: the tab text caption;
+        :param `bitmap`: the bitmap displayed on the tab;
+        :param `active`: whether the tab is selected or not;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `control`: a `wx.Window` instance inside a tab (or ``None``).
+        """
+        
+        tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
+                                                         active, close_button_state, control)
+
+        tab_width, tab_height = tab_size        
+
+        # add some padding
+        tab_width += self._leftActiveBmp.GetWidth()
+        tab_height += 2
+
+        tab_height = max(tab_height, self._centerActiveBmp.GetHeight())        
+
+        return (tab_width, tab_height), x_extent
+
+
+    def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
+        """
+        Draws a single tab.
+
+        :param `dc`: a `wx.DC` device context;
+        :param `wnd`: a `wx.Window` instance object;
+        :param `page`: the tab control page associated with the tab;
+        :param `in_rect`: rectangle the tab should be confined to;
+        :param `close_button_state`: the state of the close button on the tab;
+        :param `paint_control`: whether to draw the control inside a tab (if any) on a `wx.MemoryDC`.
+        """
+        
+        # Chrome tab style
+
+        control = page.control
+        # figure out the size of the tab
+        tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
+                                             close_button_state, control)
+
+        agwFlags = self.GetAGWFlags()
+        
+        tab_height = self._tab_ctrl_height - 1
+        tab_width = tab_size[0]
+        tab_x = in_rect.x
+        tab_y = in_rect.y + in_rect.height - tab_height
+        clip_width = tab_width
+
+        if tab_x + clip_width > in_rect.x + in_rect.width - 4:
+            clip_width = (in_rect.x + in_rect.width) - tab_x - 4
+            
+        dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
+        drawn_tab_yoff = 1
+
+        if page.active:
+            left = self._leftActiveBmp
+            center = self._centerActiveBmp
+            right = self._rightActiveBmp
+        else:
+            left = self._leftInactiveBmp
+            center = self._centerInactiveBmp
+            right = self._rightInactiveBmp
+
+        dc.DrawBitmap(left, tab_x, tab_y)
+        leftw = left.GetWidth()
+        centerw = center.GetWidth()
+        rightw = right.GetWidth()
+
+        available = tab_x + tab_width - rightw
+        posx = tab_x + leftw
+        
+        while 1:
+            if posx >= available:
+                break
+            dc.DrawBitmap(center, posx, tab_y)
+            posx += centerw
+
+        dc.DrawBitmap(right, posx, tab_y)
+
+        drawn_tab_height = center.GetHeight()
+        text_offset = tab_x + leftw
+        
+        close_button_width = 0
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+            close_button_width = self._active_close_bmp.GetWidth()
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                text_offset += close_button_width
+
+        if not page.enabled:
+            dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
+            pagebitmap = page.dis_bitmap
+        else:
+            dc.SetTextForeground(page.text_colour)
+            pagebitmap = page.bitmap
+        
+        bitmap_offset = 0
+        if pagebitmap.IsOk():
+            bitmap_offset = tab_x + leftw
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
+                bitmap_offset += close_button_width
+
+            # draw bitmap
+            dc.DrawBitmap(pagebitmap, bitmap_offset,
+                          drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2),
+                          True)
+
+            text_offset = bitmap_offset + pagebitmap.GetWidth()
+            text_offset += 3 # bitmap padding
+        
+        else:
+        
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
+                text_offset = tab_x + leftw
+        
+        # if the caption is empty, measure some temporary text
+        caption = page.caption
+        if caption == "":
+            caption = "Xj"
+
+        if page.active:
+            dc.SetFont(self._selected_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+        else:
+            dc.SetFont(self._normal_font)
+            textx, texty, dummy = dc.GetMultiLineTextExtent(caption)
+
+        if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - leftw)
+        else:
+            draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width - leftw)
+
+        ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1
+
+        if control is not None:
+            if control.GetPosition() != wx.Point(text_offset+1, ypos):
+                control.SetPosition(wx.Point(text_offset+1, ypos))
+
+            if not control.IsShown():
+                control.Show()
+
+            if paint_control:
+                bmp = TakeScreenShot(control.GetScreenRect())
+                dc.DrawBitmap(bmp, text_offset+1, ypos, True)
+                
+            controlW, controlH = control.GetSize()
+            text_offset += controlW + 4
+
+        # draw tab text
+        rectx, recty, dummy = dc.GetMultiLineTextExtent(draw_text)
+        dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
+                
+        out_button_rect = wx.Rect()
+        # draw 'x' on tab (if enabled)
+        if close_button_state != AUI_BUTTON_STATE_HIDDEN:
+        
+            close_button_width = self._active_close_bmp.GetWidth()
+            bmp = self._disabled_close_bmp
+
+            if close_button_state == AUI_BUTTON_STATE_HOVER:
+                bmp = self._hover_close_bmp
+            elif close_button_state == AUI_BUTTON_STATE_PRESSED:
+                bmp = self._pressed_close_bmp
+
+            if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
+                rect = wx.Rect(tab_x + leftw - 2,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + 1,
+                               close_button_width, tab_height)
+            else:
+                rect = wx.Rect(tab_x + tab_width - close_button_width - rightw + 2,
+                               drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + 1,
+                               close_button_width, tab_height)
+
+            if agwFlags & AUI_NB_BOTTOM:
+                rect.y -= 1
+                
+            # Indent the button if it is pressed down:
+            rect = IndentPressedBitmap(rect, close_button_state)
+            dc.DrawBitmap(bmp, rect.x, rect.y, True)
+            out_button_rect = rect
+            
+        out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
+        dc.DestroyClippingRegion()
+
+        return out_tab_rect, out_button_rect, x_extent        
+
+
diff --git a/aui/tabmdi.py b/aui/tabmdi.py
new file mode 100644 (file)
index 0000000..ef09e9f
--- /dev/null
@@ -0,0 +1,666 @@
+__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
+__date__ = "31 March 2009"
+
+
+import wx
+
+import auibook
+from aui_constants import *
+
+_ = wx.GetTranslation
+
+#-----------------------------------------------------------------------------
+# AuiMDIParentFrame
+#-----------------------------------------------------------------------------
+
+class AuiMDIParentFrame(wx.Frame):
+
+    def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE|wx.VSCROLL|wx.HSCROLL,
+                 name="AuiMDIParentFrame"):
+
+        wx.Frame.__init__(self, parent, id, title, pos, size, style, name=name)
+        self.Init()
+
+        self.Bind(wx.EVT_MENU, self.DoHandleMenu, id=wx.ID_ANY)
+
+        # this style can be used to prevent a window from having the standard MDI
+        # "Window" menu
+        if not style & wx.FRAME_NO_WINDOW_MENU:
+        
+            self._pWindowMenu = wx.Menu()
+            self._pWindowMenu.Append(wxWINDOWCLOSE,    _("Cl&ose"))
+            self._pWindowMenu.Append(wxWINDOWCLOSEALL, _("Close All"))
+            self._pWindowMenu.AppendSeparator()
+            self._pWindowMenu.Append(wxWINDOWNEXT,     _("&Next"))
+            self._pWindowMenu.Append(wxWINDOWPREV,     _("&Previous"))
+    
+        self._pClientWindow = self.OnCreateClient()
+
+
+    def SetArtProvider(self, provider):
+
+        if self._pClientWindow:
+            self._pClientWindow.SetArtProvider(provider)
+    
+
+    def GetArtProvider(self):
+
+        if not self._pClientWindow:
+            return None
+
+        return self._pClientWindow.GetArtProvider()
+
+
+    def GetNotebook(self):
+
+        return self._pClientWindow
+
+
+    def SetWindowMenu(self, pMenu):
+
+        # Replace the window menu from the currently loaded menu bar.
+        pMenuBar = self.GetMenuBar()
+
+        if self._pWindowMenu:
+            self.RemoveWindowMenu(pMenuBar)
+            del self._pWindowMenu
+            self._pWindowMenu = None
+
+        if pMenu:
+            self._pWindowMenu = pMenu
+            self.AddWindowMenu(pMenuBar)
+        
+
+    def GetWindowMenu(self):
+
+        return self._pWindowMenu
+    
+
+    def SetMenuBar(self, pMenuBar):
+
+        # Remove the Window menu from the old menu bar
+        self.RemoveWindowMenu(self.GetMenuBar())
+
+        # Add the Window menu to the new menu bar.
+        self.AddWindowMenu(pMenuBar)
+
+        wx.Frame.SetMenuBar(self, pMenuBar)
+
+
+    def SetChildMenuBar(self, pChild):
+
+        if not pChild:
+        
+            # No Child, set Our menu bar back.
+            if self._pMyMenuBar:
+                self.SetMenuBar(self._pMyMenuBar)
+            else:
+                self.SetMenuBar(self.GetMenuBar())
+
+            # Make sure we know our menu bar is in use
+            self._pMyMenuBar = None
+        
+        else:
+        
+            if pChild.GetMenuBar() == None:
+                return
+
+            # Do we need to save the current bar?
+            if self._pMyMenuBar == None:
+                self._pMyMenuBar = self.GetMenuBar()
+
+            self.SetMenuBar(pChild.GetMenuBar())
+    
+
+    def ProcessEvent(self, event):
+
+        # stops the same event being processed repeatedly
+        if self._pLastEvt == event:
+            return False
+        
+        self._pLastEvt = event
+
+        # let the active child (if any) process the event first.
+        res = False
+        if self._pActiveChild and event.IsCommandEvent() and \
+           event.GetEventObject() != self._pClientWindow and \
+           event.GetEventType() not in [wx.wxEVT_ACTIVATE, wx.wxEVT_SET_FOCUS,
+                                        wx.wxEVT_KILL_FOCUS, wx.wxEVT_CHILD_FOCUS,
+                                        wx.wxEVT_COMMAND_SET_FOCUS, wx.wxEVT_COMMAND_KILL_FOCUS]:
+        
+            res = self._pActiveChild.GetEventHandler().ProcessEvent(event)
+        
+        if not res:
+        
+            # if the event was not handled this frame will handle it,
+            # which is why we need the protection code at the beginning
+            # of this method
+            res = self.GetEventHandler().ProcessEvent(event)
+        
+        self._pLastEvt = None
+
+        return res
+
+
+    def GetActiveChild(self):
+
+        return self._pActiveChild
+
+
+    def SetActiveChild(self, pChildFrame):
+
+        self._pActiveChild = pChildFrame
+
+
+    def GetClientWindow(self):
+
+        return self._pClientWindow
+
+
+    def OnCreateClient(self):
+
+        return AuiMDIClientWindow(self)
+
+
+    def ActivateNext(self):
+
+        if self._pClientWindow and self._pClientWindow.GetSelection() != wx.NOT_FOUND:
+        
+            active = self._pClientWindow.GetSelection() + 1
+            if active >= self._pClientWindow.GetPageCount():
+                active = 0
+
+            self._pClientWindow.SetSelection(active)
+        
+
+    def ActivatePrevious(self):
+
+        if self._pClientWindow and self._pClientWindow.GetSelection() != wx.NOT_FOUND:
+        
+            active = self._pClientWindow.GetSelection() - 1
+            if active < 0:
+                active = self._pClientWindow.GetPageCount() - 1
+
+            self._pClientWindow.SetSelection(active)
+    
+
+    def Init(self):
+
+        self._pLastEvt = None
+
+        self._pClientWindow = None
+        self._pActiveChild = None
+        self._pWindowMenu = None
+        self._pMyMenuBar = None
+
+
+    def RemoveWindowMenu(self, pMenuBar):
+
+        if pMenuBar and self._pWindowMenu:
+        
+            # Remove old window menu
+            pos = pMenuBar.FindMenu(_("&Window"))
+            if pos != wx.NOT_FOUND:            
+                pMenuBar.Remove(pos)
+            
+
+    def AddWindowMenu(self, pMenuBar):
+
+        if pMenuBar and self._pWindowMenu:
+        
+            pos = pMenuBar.FindMenu(wx.GetStockLabel(wx.ID_HELP, wx.STOCK_NOFLAGS))
+            if pos == wx.NOT_FOUND:
+                pMenuBar.Append(self._pWindowMenu, _("&Window"))
+            else:
+                pMenuBar.Insert(pos, self._pWindowMenu, _("&Window"))
+    
+
+    def DoHandleMenu(self, event):
+
+        evId = event.GetId()
+        
+        if evId == wxWINDOWCLOSE:
+            if self._pActiveChild:
+                self._pActiveChild.Close()
+
+        elif evId == wxWINDOWCLOSEALL:
+            
+            while self._pActiveChild:            
+                if not self._pActiveChild.Close():
+                    return # failure
+                
+        elif evId == wxWINDOWNEXT:
+            self.ActivateNext()
+
+        elif evId == wxWINDOWPREV:
+            self.ActivatePrevious()
+
+        else:
+            event.Skip()
+
+    
+    def Tile(self, orient=wx.HORIZONTAL):
+
+        client_window = self.GetClientWindow()
+        if not client_window:
+            raise Exception("Missing MDI Client Window")
+
+        cur_idx = client_window.GetSelection()
+        if cur_idx == -1:
+            return
+
+        if orient == wx.VERTICAL:
+        
+            client_window.Split(cur_idx, wx.LEFT)
+        
+        elif orient == wx.HORIZONTAL:
+        
+            client_window.Split(cur_idx, wx.TOP)
+    
+
+#-----------------------------------------------------------------------------
+# AuiMDIChildFrame
+#-----------------------------------------------------------------------------
+
+class AuiMDIChildFrame(wx.PyPanel):
+
+    def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="AuiMDIChildFrame"):
+
+        pClientWindow = parent.GetClientWindow()
+        if pClientWindow is None:
+            raise Exception("Missing MDI client window.")
+
+        self.Init()
+        
+        # see comment in constructor
+        if style & wx.MINIMIZE:
+            self._activate_on_create = False
+
+        cli_size = pClientWindow.GetClientSize()
+
+        # create the window off-screen to prevent flicker
+        wx.PyPanel.__init__(self, pClientWindow, id, wx.Point(cli_size.x+1, cli_size.y+1),
+                            size, wx.NO_BORDER, name=name)
+
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        self.Show(False)
+        self.SetMDIParentFrame(parent)
+
+        # this is the currently active child
+        parent.SetActiveChild(self)
+        self._title = title
+
+        pClientWindow.AddPage(self, title, self._activate_on_create)
+        pClientWindow.Refresh()
+
+        self.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
+        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+
+    def Init(self):
+
+        # There are two ways to create an tabbed mdi child fram without
+        # making it the active document.  Either Show(False) can be called
+        # before Create() (as is customary on some ports with wxFrame-type
+        # windows), or wx.MINIMIZE can be passed in the style flags.  Note that
+        # AuiMDIChildFrame is not really derived from wxFrame, as MDIChildFrame
+        # is, but those are the expected symantics.  No style flag is passed
+        # onto the panel underneath.
+
+        self._activate_on_create = True
+
+        self._pMDIParentFrame = None
+        self._pMenuBar = None
+        
+        self._mdi_currect = None
+        self._mdi_newrect = wx.Rect()
+        self._icon = None
+        self._icon_bundle = None
+
+
+    def Destroy(self):
+
+        pParentFrame = self.GetMDIParentFrame()
+        if not pParentFrame:
+            raise Exception("Missing MDI Parent Frame")
+
+        pClientWindow = pParentFrame.GetClientWindow()
+        if not pClientWindow:
+            raise Exception("Missing MDI Client Window")
+
+        if pParentFrame.GetActiveChild() == self:
+        
+            # deactivate ourself
+            event = wx.ActivateEvent(wx.wxEVT_ACTIVATE, False, self.GetId())
+            event.SetEventObject(self)
+            self.GetEventHandler().ProcessEvent(event)
+
+            pParentFrame.SetActiveChild(None)
+            pParentFrame.SetChildMenuBar(None)
+        
+        for pos in xrange(pClientWindow.GetPageCount()):
+            if pClientWindow.GetPage(pos) == self:
+                return pClientWindow.DeletePage(pos)
+
+        return False
+
+
+    def SetMenuBar(self, menu_bar):
+
+        pOldMenuBar = self._pMenuBar
+        self._pMenuBar = menu_bar
+
+        if self._pMenuBar:
+        
+            pParentFrame = self.GetMDIParentFrame()
+            if not pParentFrame:
+                raise Exception("Missing MDI Parent Frame")
+
+            self._pMenuBar.Reparent(pParentFrame)
+            if pParentFrame.GetActiveChild() == self:
+            
+                # replace current menu bars
+                if pOldMenuBar:
+                    pParentFrame.SetChildMenuBar(None)
+                    
+                pParentFrame.SetChildMenuBar(self)
+            
+
+    def GetMenuBar(self):
+
+        return self._pMenuBar
+
+
+    def SetTitle(self, title):
+
+        self._title = title
+
+        pParentFrame = self.GetMDIParentFrame()
+        if not pParentFrame:
+            raise Exception("Missing MDI Parent Frame")
+        
+        pClientWindow = pParentFrame.GetClientWindow()
+        if pClientWindow is not None:
+        
+            for pos in xrange(pClientWindow.GetPageCount()):
+                if pClientWindow.GetPage(pos) == self:
+                    pClientWindow.SetPageText(pos, self._title)
+                    break
+
+
+    def GetTitle(self):
+
+        return self._title
+
+
+    def SetIcons(self, icons):
+
+        # get icon with the system icon size
+        self.SetIcon(icons.GetIcon(-1))
+        self._icon_bundle = icons
+
+
+    def GetIcons(self):
+
+        return self._icon_bundle
+
+
+    def SetIcon(self, icon):
+
+        pParentFrame = self.GetMDIParentFrame()
+        if not pParentFrame:
+            raise Exception("Missing MDI Parent Frame")
+
+        self._icon = icon
+
+        bmp = wx.BitmapFromIcon(self._icon)
+
+        pClientWindow = pParentFrame.GetClientWindow()
+        if pClientWindow is not None:
+            idx = pClientWindow.GetPageIndex(self)
+            if idx != -1:
+                pClientWindow.SetPageBitmap(idx, bmp)
+        
+
+    def GetIcon(self):
+
+        return self._icon
+
+
+    def Activate(self):
+
+        pParentFrame = self.GetMDIParentFrame()
+        if not pParentFrame:
+            raise Exception("Missing MDI Parent Frame")
+        
+        pClientWindow = pParentFrame.GetClientWindow()
+        if pClientWindow is not None:
+        
+            for pos in xrange(pClientWindow.GetPageCount()):
+                if pClientWindow.GetPage(pos) == self:
+                    pClientWindow.SetSelection(pos)
+                    break
+            
+
+    def OnMenuHighlight(self, event):
+
+        if self._pMDIParentFrame:
+    
+            # we don't have any help text for this item,
+            # but may be the MDI frame does?
+            self._pMDIParentFrame.OnMenuHighlight(event)
+
+
+    def OnActivate(self, event):
+
+        # do nothing
+        pass
+
+
+    def OnCloseWindow(self, event):
+
+        pParentFrame = self.GetMDIParentFrame()
+        if pParentFrame:
+            if pParentFrame.GetActiveChild() == self:
+            
+                pParentFrame.SetActiveChild(None)
+                pParentFrame.SetChildMenuBar(None)
+            
+            pClientWindow = pParentFrame.GetClientWindow()
+            idx = pClientWindow.GetPageIndex(self)
+            
+            if idx != wx.NOT_FOUND:
+                pClientWindow.RemovePage(idx)
+
+        self.Destroy()
+
+
+    def SetMDIParentFrame(self, parentFrame):
+
+        self._pMDIParentFrame = parentFrame
+
+
+    def GetMDIParentFrame(self):
+
+        return self._pMDIParentFrame
+
+
+    def CreateStatusBar(self, number=1, style=1, winid=1, name=""):
+        
+        return None
+
+
+    def GetStatusBar(self):
+
+        return None
+    
+
+    def SetStatusText(self, text, number=0):
+
+        pass
+
+    
+    def SetStatusWidths(self, widths_field):
+
+        pass
+    
+
+    # no toolbar bars
+    def CreateToolBar(self, style=1, winid=-1, name=""):
+        
+        return None
+
+    
+    def GetToolBar(self):
+
+        return None
+    
+
+    # no maximize etc
+    def Maximize(self, maximize=True):
+
+        pass
+
+
+    def Restore(self):
+    
+        pass
+
+    
+    def Iconize(self, iconize=True):
+
+        pass
+
+    
+    def IsMaximized(self):
+
+        return True
+
+    
+    def IsIconized(self):
+
+        return False
+
+    
+    def ShowFullScreen(self, show=True, style=0):
+
+        return False
+
+    
+    def IsFullScreen(self):
+
+        return False        
+
+
+    def IsTopLevel(self):
+
+        return False
+
+
+    # renamed from Show().
+    def ActivateOnCreate(self, activate_on_create):
+
+        self._activate_on_create = activate_on_create
+        return True
+
+    
+    def Show(self, show=True):
+
+        wx.PyPanel.Show(self, show)
+
+
+    def ApplyMDIChildFrameRect(self):
+
+        if self._mdi_currect != self._mdi_newrect:
+            self.SetDimensions(*self._mdi_newrect)
+            self._mdi_currect = wx.Rect(*self._mdi_newrect)
+
+
+#-----------------------------------------------------------------------------
+# AuiMDIClientWindow
+#-----------------------------------------------------------------------------
+
+class AuiMDIClientWindow(auibook.AuiNotebook):
+
+    def __init__(self, parent, agwStyle=0):
+
+        auibook.AuiNotebook.__init__(self, parent, wx.ID_ANY, wx.Point(0, 0), wx.Size(100, 100),
+                                     agwStyle=AUI_NB_DEFAULT_STYLE|wx.NO_BORDER)
+
+        caption_icon_size = wx.Size(wx.SystemSettings.GetMetric(wx.SYS_SMALLICON_X),
+                                    wx.SystemSettings.GetMetric(wx.SYS_SMALLICON_Y))
+        self.SetUniformBitmapSize(caption_icon_size)
+
+        bkcolour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_APPWORKSPACE)
+        self.SetOwnBackgroundColour(bkcolour)
+
+        self._mgr.GetArtProvider().SetColour(AUI_DOCKART_BACKGROUND_COLOUR, bkcolour)
+
+        self.Bind(auibook.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.Bind(auibook.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnPageClose)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+
+
+    def SetSelection(self, nPage):
+
+        return auibook.AuiNotebook.SetSelection(self, nPage)
+
+
+    def PageChanged(self, old_selection, new_selection):
+
+        # don't do anything if the page doesn't actually change
+        if old_selection == new_selection:
+            return
+
+        # notify old active child that it has been deactivated
+        if old_selection != -1 and old_selection < self.GetPageCount():
+        
+            old_child = self.GetPage(old_selection)
+            if not old_child:
+                raise Exception("AuiMDIClientWindow.PageChanged - null page pointer")
+
+            event = wx.ActivateEvent(wx.wxEVT_ACTIVATE, False, old_child.GetId())
+            event.SetEventObject(old_child)
+            old_child.GetEventHandler().ProcessEvent(event)
+        
+        # notify new active child that it has been activated
+        if new_selection != -1:
+        
+            active_child = self.GetPage(new_selection)
+            if not active_child:
+                raise Exception("AuiMDIClientWindow.PageChanged - null page pointer")
+
+            event = wx.ActivateEvent(wx.wxEVT_ACTIVATE, True, active_child.GetId())
+            event.SetEventObject(active_child)
+            active_child.GetEventHandler().ProcessEvent(event)
+
+            if active_child.GetMDIParentFrame():
+                active_child.GetMDIParentFrame().SetActiveChild(active_child)
+                active_child.GetMDIParentFrame().SetChildMenuBar(active_child)
+
+
+    def OnPageClose(self, event):
+
+        wnd = self.GetPage(event.GetSelection())
+        wnd.Close()
+
+        # regardless of the result of wnd.Close(), we've
+        # already taken care of the close operations, so
+        # suppress further processing
+        event.Veto()
+
+
+    def OnPageChanged(self, event):
+
+        self.PageChanged(event.GetOldSelection(), event.GetSelection())
+
+
+    def OnSize(self, event):
+
+        auibook.AuiNotebook.OnSize(self, event)
+
+        for pos in xrange(self.GetPageCount()):
+            self.GetPage(pos).ApplyMDIChildFrameRect()
index 3b3ee17..b441a32 100644 (file)
@@ -221,7 +221,6 @@ ChdTxtPathOut = {'TableUc1': 'TableUc1.csv',
         'R3DCoul': ffr(tempfile.mkstemp(prefix='iramuteq')[1]),
         'RESULT_CHD':  'resultats-chd.html',
         'RESULT_AFC':  'resultats-afc.html',
         'R3DCoul': ffr(tempfile.mkstemp(prefix='iramuteq')[1]),
         'RESULT_CHD':  'resultats-chd.html',
         'RESULT_AFC':  'resultats-afc.html',
-        'Act01':  'Act01.csv',
         'Et01':  'Et01.csv',
         'Rchdquest':ffr(tempfile.mkstemp(prefix='iramuteq')[1]),
         'RTxtProfGraph':ffr(tempfile.mkstemp(prefix='iramuteq')[1]),
         'Et01':  'Et01.csv',
         'Rchdquest':ffr(tempfile.mkstemp(prefix='iramuteq')[1]),
         'RTxtProfGraph':ffr(tempfile.mkstemp(prefix='iramuteq')[1]),
index e043707..7cc9306 100644 (file)
--- a/corpus.py
+++ b/corpus.py
@@ -11,7 +11,6 @@ from time import time
 from functions import decoupercharact, ReadDicoAsDico, DoConf
 import re
 import sqlite3
 from functions import decoupercharact, ReadDicoAsDico, DoConf
 import re
 import sqlite3
-import numpy
 import itertools
 import logging
 from operator import itemgetter
 import itertools
 import logging
 from operator import itemgetter
index 8a10f67..e72ebd9 100644 (file)
@@ -45,14 +45,17 @@ class History :
     def read(self) :
         d = shelve.open(self.filein)
         self.history = d.get('history', [])
     def read(self) :
         d = shelve.open(self.filein)
         self.history = d.get('history', [])
+        self.matrix = d.get('matrix', [])
         self.ordercorpus = dict([[corpus['uuid'], i] for i, corpus in enumerate(self.history)])
         self.ordercorpus = dict([[corpus['uuid'], i] for i, corpus in enumerate(self.history)])
-        self.corpus = dict([[corpus['uuid'], corpus] for i, corpus in enumerate(self.history)])
+        self.corpus = dict([[corpus['uuid'], corpus] for corpus in self.history])
         self.analyses = dict([[analyse['uuid'], analyse] for corpus in self.history for analyse in corpus.get('analyses', [])])
         self.analyses = dict([[analyse['uuid'], analyse] for corpus in self.history for analyse in corpus.get('analyses', [])])
+        self.matrixanalyse = dict([[mat['uuid'], mat] for mat in self.matrix])
         d.close()
 
     def write(self) :
         d = shelve.open(self.filein)
         d['history'] = self.history
         d.close()
 
     def write(self) :
         d = shelve.open(self.filein)
         d['history'] = self.history
+        d['matrix'] = self.matrix
         d.close()
     
     def add(self, analyse) :
         d.close()
     
     def add(self, analyse) :
@@ -77,6 +80,13 @@ class History :
         self.write()
         self.read()
 
         self.write()
         self.read()
 
+    def addMatrix(self, analyse) :
+        tosave = {'uuid' : analyse['uuid'], 'ira': analyse['ira'], 'type' : analyse['type']}
+        tosave['name'] = analyse['name']
+        self.matrix.append(tosave)
+        self.write()
+        self.read()
+
     def addmultiple(self, analyses) :
         for analyse in analyses :
             tosave = {'uuid' : analyse['uuid'], 'ira': analyse['ira'], 'type' : analyse['type']}
     def addmultiple(self, analyses) :
         for analyse in analyses :
             tosave = {'uuid' : analyse['uuid'], 'ira': analyse['ira'], 'type' : analyse['type']}
@@ -96,9 +106,11 @@ class History :
             self.history.pop(self.ordercorpus[analyse['uuid']])
             if analyse['uuid'] in self.openedcorpus :
                 del self.openedcorpus[analyse['uuid']]
             self.history.pop(self.ordercorpus[analyse['uuid']])
             if analyse['uuid'] in self.openedcorpus :
                 del self.openedcorpus[analyse['uuid']]
-        else :
+        elif analyse['uuid'] in self.analyses :
             todel = [i for i, ana in enumerate(self.corpus[analyse['corpus']]['analyses']) if ana['uuid'] == analyse['uuid']][0]
             self.history[self.ordercorpus[analyse['corpus']]]['analyses'].pop(todel)
             todel = [i for i, ana in enumerate(self.corpus[analyse['corpus']]['analyses']) if ana['uuid'] == analyse['uuid']][0]
             self.history[self.ordercorpus[analyse['corpus']]]['analyses'].pop(todel)
+        elif analyse['uuid'] in self.matrixanalyse :
+            self.matrix = [mat for mat in self.matrix if mat['uuid'] != analyse['uuid']]
         self.write()
         self.read()
 
         self.write()
         self.read()
 
index aad2ebd..c59894b 100644 (file)
@@ -28,8 +28,10 @@ import logging
 #------------------------------------
 import wx
 #import wx.aui
 #------------------------------------
 import wx
 #import wx.aui
-import wx.lib.agw.aui as aui
-#import agw.aui as aui
+if wx.__version__ >= '2.11' :
+    import wx.lib.agw.aui as aui
+else :
+    import aui
 import wx.html
 import wx.grid
 import wx.lib.hyperlink as hl
 import wx.html
 import wx.grid
 import wx.lib.hyperlink as hl
index 0e2d5a9..2a30f5c 100644 (file)
--- a/layout.py
+++ b/layout.py
@@ -7,8 +7,10 @@
 import os
 import wx
 import wx.lib.hyperlink as hl
 import os
 import wx
 import wx.lib.hyperlink as hl
-#import agw.aui as aui
-import wx.lib.agw.aui as aui
+if wx.__version__ >= '2.11' :
+    import wx.lib.agw.aui as aui
+else :
+    import aui
 from chemins import ConstructPathOut, ChdTxtPathOut, FFF, ffr, PathOut, StatTxtPathOut, simipath
 from ConfigParser import ConfigParser
 from functions import ReadProfileAsDico, GetTxtProfile, read_list_file, ReadList, exec_rcode, print_liste, BugReport, DoConf, indices_simi, check_Rresult, progressbar
 from chemins import ConstructPathOut, ChdTxtPathOut, FFF, ffr, PathOut, StatTxtPathOut, simipath
 from ConfigParser import ConfigParser
 from functions import ReadProfileAsDico, GetTxtProfile, read_list_file, ReadList, exec_rcode, print_liste, BugReport, DoConf, indices_simi, check_Rresult, progressbar
@@ -303,16 +305,16 @@ class OpenCHDS():
 
        Profile = DictPathOut['PROFILE_OUT']
        AntiProfile = DictPathOut['ANTIPRO_OUT']
 
        Profile = DictPathOut['PROFILE_OUT']
        AntiProfile = DictPathOut['ANTIPRO_OUT']
+       self.encoding = self.corpus.parametres['syscoding']
        if isinstance(self.corpus, Corpus) :
        if isinstance(self.corpus, Corpus) :
-            self.encoding = self.corpus.parametres['syscoding']
             self.corpus.make_ucecl_from_R(self.pathout['uce'])
             self.corpus.make_ucecl_from_R(self.pathout['uce'])
-       elif 'tableau' in dir(gparent) :
-            self.encoding = gparent.tableau.parametres['syscoding']
+            corpname = self.corpus.parametres['corpus_name']
+       else :
+           corpname = self.corpus.parametres['name']
 
        clnb = parametres['clnb']
        dlg = progressbar(self, maxi = 4 + clnb) 
        self.clnb = clnb 
 
        clnb = parametres['clnb']
        dlg = progressbar(self, maxi = 4 + clnb) 
        self.clnb = clnb 
-       corpname = self.corpus.parametres['corpus_name']
        print 'lecture des profils'
        dlg.Update(2, u'lecture des profils')
  
        print 'lecture des profils'
        dlg.Update(2, u'lecture des profils')
  
@@ -336,12 +338,14 @@ class OpenCHDS():
 
        if isinstance(self.corpus, Corpus) :
            panel.corpus = self.corpus
 
        if isinstance(self.corpus, Corpus) :
            panel.corpus = self.corpus
-           panel.dictpathout = self.DictPathOut
-           panel.pathout = self.DictPathOut
-           panel.parent = self.parent
-           panel.DictProfile = self.DictProfile
-           panel.cluster_size = self.cluster_size
-           panel.debtext = self.debtext
+       else :
+           panel.tableau = self.corpus
+       panel.dictpathout = self.DictPathOut
+       panel.pathout = self.DictPathOut
+       panel.parent = self.parent
+       panel.DictProfile = self.DictProfile
+       panel.cluster_size = self.cluster_size
+       panel.debtext = self.debtext
 
 #       self.ID_rapport = wx.NewId()
 #       #rap_img = wx.Image(os.path.join(self.parent.images_path,'icone_rap_16.png'), wx.BITMAP_TYPE_ANY).ConvertToBitmap()
 
 #       self.ID_rapport = wx.NewId()
 #       #rap_img = wx.Image(os.path.join(self.parent.images_path,'icone_rap_16.png'), wx.BITMAP_TYPE_ANY).ConvertToBitmap()
@@ -365,8 +369,11 @@ class OpenCHDS():
        if isinstance(self.corpus, Corpus) :
            panel.TabChdSim.corpus = corpus
            panel.TabChdSim.corpus.dictpathout = self.DictPathOut
        if isinstance(self.corpus, Corpus) :
            panel.TabChdSim.corpus = corpus
            panel.TabChdSim.corpus.dictpathout = self.DictPathOut
-           panel.parametres = self.parametres
-           self.panel = panel
+       else :
+           panel.TabChdSim.tableau = corpus
+           panel.TabChdSim.tableau.dictpathout = self.DictPathOut
+       panel.parametres = self.parametres
+       self.panel = panel
 
        self.notenb = self.parent.nb.GetPageCount()
 
 
        self.notenb = self.parent.nb.GetPageCount()
 
@@ -542,7 +549,7 @@ class OpenCHDS():
     
         
 
     
         
 
-def PrintRapport(self, corpus, parametres, txt = True):
+def PrintRapport(self, corpus, parametres, istxt = True):
     #if sys.platform == 'win32':
     #    sep = '\r\n'
     #else:
     #if sys.platform == 'win32':
     #    sep = '\r\n'
     #else:
@@ -554,8 +561,9 @@ def PrintRapport(self, corpus, parametres, txt = True):
 
 
 """ % datetime.datetime.now().ctime()
 
 
 """ % datetime.datetime.now().ctime()
-    totocc = corpus.gettotocc()
-    if txt :
+    print istxt
+    if istxt :
+        totocc = corpus.gettotocc()
         txt += u'nombre d\'uci: %i%s' % (corpus.getucinb(), sep)
         txt += u'nombre d\'uce: %i%s' % (corpus.getucenb(), sep)
         txt += u'nombre de formes: %i%s' % (len(corpus.formes), sep)
         txt += u'nombre d\'uci: %i%s' % (corpus.getucinb(), sep)
         txt += u'nombre d\'uce: %i%s' % (corpus.getucenb(), sep)
         txt += u'nombre de formes: %i%s' % (len(corpus.formes), sep)
@@ -571,24 +579,24 @@ def PrintRapport(self, corpus, parametres, txt = True):
                 txt += u'taille uc1 : %i\n' % parametres['tailleuc1']
             else:
                 txt += u'taille uc1 / uc2: %i / %i - %i / %i%s' % (parametres['tailleuc1'], parametres['tailleuc2'], parametres['lenuc1'], parametres['lenuc2'], sep)
                 txt += u'taille uc1 : %i\n' % parametres['tailleuc1']
             else:
                 txt += u'taille uc1 / uc2: %i / %i - %i / %i%s' % (parametres['tailleuc1'], parametres['tailleuc2'], parametres['lenuc1'], parametres['lenuc2'], sep)
-    elif not txt :
+    else :
         self.Ucenb = self.nbind
         txt += u'nombre d\'individus : %i%s' % (self.nbind, sep)
         txt += u'nombre de classes : %i%s' % (self.clnb, sep)
         self.Ucenb = self.nbind
         txt += u'nombre d\'individus : %i%s' % (self.nbind, sep)
         txt += u'nombre de classes : %i%s' % (self.clnb, sep)
-    if txt :
+    if istxt :
         txt += u'nombre de classes : %i%s' % (parametres['clnb'], sep)
         if parametres['classif_mode'] == 0 or parametres['classif_mode'] == 1 :
             txt += u'%i uce classées sur %i (%.2f%%)%s' % (sum([len(cl) for cl in corpus.lc]), corpus.getucenb(), (float(sum([len(cl) for cl in corpus.lc])) / float(corpus.getucenb())) * 100, sep)
         elif self.parametres['classif_mode'] == 2 :
             txt += u'%i uci classées sur %i (%.2f%%)%s' % (sum([len(cl) for cl in corpus.lc]), corpus.getucinb(), (float(sum([len(cl) for cl in corpus.lc]))) / float(corpus.getucinb()) * 100, sep)
         txt += u'nombre de classes : %i%s' % (parametres['clnb'], sep)
         if parametres['classif_mode'] == 0 or parametres['classif_mode'] == 1 :
             txt += u'%i uce classées sur %i (%.2f%%)%s' % (sum([len(cl) for cl in corpus.lc]), corpus.getucenb(), (float(sum([len(cl) for cl in corpus.lc])) / float(corpus.getucenb())) * 100, sep)
         elif self.parametres['classif_mode'] == 2 :
             txt += u'%i uci classées sur %i (%.2f%%)%s' % (sum([len(cl) for cl in corpus.lc]), corpus.getucinb(), (float(sum([len(cl) for cl in corpus.lc]))) / float(corpus.getucinb()) * 100, sep)
-    elif analyse == 'quest' :
+    else :
         txt += u'%i uce classées sur %i (%.2f%%)%s' % (self.ucecla, self.Ucenb, (float(self.ucecla) / float(self.Ucenb)) * 100, sep)
  
     txt += """
 ###########################
 temps d'analyse : %s
 ###########################
         txt += u'%i uce classées sur %i (%.2f%%)%s' % (self.ucecla, self.Ucenb, (float(self.ucecla) / float(self.Ucenb)) * 100, sep)
  
     txt += """
 ###########################
 temps d'analyse : %s
 ###########################
-""" % parametres['time']
+""" % parametres.get('time', '')
     with open(self.pathout['pre_rapport'], 'w') as f :
         f.write(txt)
 
     with open(self.pathout['pre_rapport'], 'w') as f :
         f.write(txt)
 
index 2237fcc..75607f3 100644 (file)
@@ -67,7 +67,7 @@ class ListForSpec(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ColumnSor
         self.SetColumnWidth(0, 180)
 
         for i in range(1,len(first)-1):
         self.SetColumnWidth(0, 180)
 
         for i in range(1,len(first)-1):
-            self.SetColumnWidth(i, len(first[i]) * 10)
+            self.SetColumnWidth(i, self.checkcolumnwidth(len(first[i]) * 10))
         
         self.itemDataMap = dlist
         self.itemIndexMap = dlist.keys()
         
         self.itemDataMap = dlist
         self.itemIndexMap = dlist.keys()
@@ -88,6 +88,12 @@ class ListForSpec(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ColumnSor
 
 #-----------------------------------------------------------------------------------------    
         
 
 #-----------------------------------------------------------------------------------------    
         
+    def checkcolumnwidth(self, width) :
+        if width < 80 :
+            return 80
+        else :
+            return width
+
     def OnGetItemText(self, item, col):
         index=self.itemIndexMap[item]
         s = self.itemDataMap[index][col]
     def OnGetItemText(self, item, col):
         index=self.itemIndexMap[item]
         s = self.itemDataMap[index][col]
index 29a0d63..2cf40c8 100644 (file)
@@ -4,7 +4,7 @@
 #Copyright (c) 2008-2012, Pierre Ratinaud
 #Lisense: GNU/GPL
 
 #Copyright (c) 2008-2012, Pierre Ratinaud
 #Lisense: GNU/GPL
 
-from chemins import ChdTxtPathOut, StatTxtPathOut, construct_simipath
+from chemins import ChdTxtPathOut, StatTxtPathOut, construct_simipath, PathOut
 from layout import OpenCHDS, dolexlayout, StatLayout, WordCloudLayout, OpenCorpus, SimiLayout
 from corpus import Corpus, copycorpus
 from tableau import Tableau
 from layout import OpenCHDS, dolexlayout, StatLayout, WordCloudLayout, OpenCorpus, SimiLayout
 from corpus import Corpus, copycorpus
 from tableau import Tableau
@@ -12,6 +12,7 @@ import os
 import shelve
 from tabsimi import DoSimi
 from functions import BugReport, DoConf
 import shelve
 from tabsimi import DoSimi
 from functions import BugReport, DoConf
+from tableau import Tableau
 import logging
 
 log = logging.getLogger('iramuteq.openanalyse')
 import logging
 
 log = logging.getLogger('iramuteq.openanalyse')
@@ -30,7 +31,7 @@ class OpenAnalyse():
         
         if self.conf['type'] == 'corpus' :
             corpus = self.opencorpus()
         
         if self.conf['type'] == 'corpus' :
             corpus = self.opencorpus()
-        elif self.conf['corpus'] in self.parent.history.corpus :
+        elif self.conf.get('corpus', False) in self.parent.history.corpus :
             if self.conf['uuid'] in self.parent.history.analyses :
                 intree  = True
             else :
             if self.conf['uuid'] in self.parent.history.analyses :
                 intree  = True
             else :
@@ -47,6 +48,19 @@ class OpenAnalyse():
             self.doopen(corpus)
         else :
             corpus = None
             self.doopen(corpus)
         else :
             corpus = None
+            if isinstance(parametres, dict) :
+                tableau = Tableau(parent, parametres['ira'])
+            else :
+                tableau = Tableau(parent, parametres)
+            tableau.parametres = self.conf 
+            tableau.dictpathout = PathOut(filename = tableau.parametres['filename'], dirout = self.conf['pathout'], analyse_type = self.conf['type'])
+            tableau.dictpathout.basefiles(ChdTxtPathOut)
+            tableau.read_tableau(tableau.dictpathout['db'])
+            if self.parent.tree.IsInTree(uuid = self.conf['uuid']) :
+                self.parent.tree.GiveFocus(uuid = self.conf['uuid'], bold = True)
+            else :
+                self.parent.tree.AddAnalyse(self.conf, bold = True)
+            self.doopen(tableau)
         self.parent.history.addtab(self.conf)
     
     def redopath(self, conf, path) :
         self.parent.history.addtab(self.conf)
     
     def redopath(self, conf, path) :
@@ -116,4 +130,7 @@ class OpenAnalyse():
         elif self.conf['type'] == 'wordcloud' :
             self.parent.ShowMenu(_("Text analysis"))
             WordCloudLayout(self.parent, corpus, self.conf)
         elif self.conf['type'] == 'wordcloud' :
             self.parent.ShowMenu(_("Text analysis"))
             WordCloudLayout(self.parent, corpus, self.conf)
+        elif self.conf['type'] == 'gnepamatrix' :
+            self.parent.ShowMenu(_("Spreadsheet analysis"))
+            OpenCHDS(self.parent,  corpus, self.conf, Alceste = False)
         
         
index 9a89195..1eeee9c 100644 (file)
@@ -6,8 +6,12 @@
 
 import tempfile
 from ProfList import *
 
 import tempfile
 from ProfList import *
+import wx
+if wx.__version__ >= '2.11' :
 #import agw.aui as aui
 #import agw.aui as aui
-import wx.lib.agw.aui as aui
+    import wx.lib.agw.aui as aui
+else :
+    import aui
 from functions import exec_rcode, check_Rresult, ReadProfileAsDico, ReadList
 from listlex import *
 from dialog import PrefSegProf, PrefProfTypes
 from functions import exec_rcode, check_Rresult, ReadProfileAsDico, ReadList
 from listlex import *
 from dialog import PrefSegProf, PrefProfTypes
index 39fd377..03d7a66 100644 (file)
@@ -4,15 +4,15 @@
 #Copyright (c) 2008-2009 Pierre Ratinaud
 #Lisense: GNU/GPL
 
 #Copyright (c) 2008-2009 Pierre Ratinaud
 #Lisense: GNU/GPL
 
-from chemins import ConstructPathOut, ChdTxtPathOut, ConstructAfcUciPath, ffr
+from chemins import ConstructPathOut, ChdTxtPathOut, ConstructAfcUciPath, ffr, PathOut
 from functions import sortedby, CreateIraFile, print_liste, exec_rcode, check_Rresult
 from PrintRScript import RchdQuest
 from layout import OpenCHDS, PrintRapport
 from dialog import PrefQuestAlc
 from functions import sortedby, CreateIraFile, print_liste, exec_rcode, check_Rresult
 from PrintRScript import RchdQuest
 from layout import OpenCHDS, PrintRapport
 from dialog import PrefQuestAlc
+from analysematrix import AnalyseMatrix
 import os
 import sys
 import wx
 import os
 import sys
 import wx
-from numpy import *
 import tempfile
 import time
 
 import tempfile
 import time
 
@@ -22,52 +22,63 @@ class AnalyseQuest():
         dlg = PrefQuestAlc(parent)
         dlg.CenterOnParent()
         self.val = dlg.ShowModal()
         dlg = PrefQuestAlc(parent)
         dlg.CenterOnParent()
         self.val = dlg.ShowModal()
+        parametres = parent.tableau.parametre
         if self.val == wx.ID_OK :
         if self.val == wx.ID_OK :
+            parametres['nbcl_p1'] = dlg.spin_nbcl.GetValue()
+            parametres['mincl'] = dlg.spin_mincl.GetValue()
             if dlg.m_radioBox1.GetSelection() == 1 :
             if dlg.m_radioBox1.GetSelection() == 1 :
-                ListAct = dlg.nactives
-                ListSup = dlg.varsup
-                nbcl_p1 = dlg.spin_nbcl.GetValue()
-                mincl = dlg.spin_mincl.GetValue()
-                DoQuestAlceste(parent, ListAct, ListSup, nbcl = nbcl_p1, mincl = mincl)
-            else:
-                nbcl_p1 = dlg.spin_nbcl.GetValue()
-                mincl = dlg.spin_mincl.GetValue()
-                DoQuestAlceste(parent, nbcl = nbcl_p1, mincl = mincl) 
+                parametres['listact'] = dlg.nactives
+                parametres['listsup'] = dlg.varsup
+            else :
+                parametres['formatted'] = 1
+            DoQuestAlceste(parent, parametres)
 
 
-class DoQuestAlceste:
-    def __init__(self, parent, ListAct=False, ListSup=False, nbcl = 10, mincl = 10):
-        self.t1 = time.time()
-#-------------------------------------------------------------------               
-        dlg = wx.ProgressDialog("Traitements",
-                               "Veuillez patienter...",
-                               maximum=5,
-                               parent=parent,
-                               style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME
-                                )
-        dlg.Center()
-        count = 1
-        keepGoing = dlg.Update(count)
-#-------------------------------------------------------------------
-        self.pathout = ConstructPathOut(parent.tableau.parametre['filename'], 'AlcesteQuest')
+class DoQuestAlceste(AnalyseMatrix):
+    def __init__(self, parent, parametres):
+        parametres['pathout'] = ConstructPathOut(parent.tableau.parametre['filename'], 'gnepaMatrix')
+        self.parametres = parametres
+        self.parametres['type'] = 'gnepamatrix'
         self.DictForme = {}
         self.DictFormeSup = {}
         self.Min = 10
         self.Linecontent = []
         self.parent = parent
         self.RPath = self.parent.PathPath.get('PATHS', 'rpath')
         self.DictForme = {}
         self.DictFormeSup = {}
         self.Min = 10
         self.Linecontent = []
         self.parent = parent
         self.RPath = self.parent.PathPath.get('PATHS', 'rpath')
-        self.dictpathout = ChdTxtPathOut(self.pathout)
-        self.parent.tableau.dictpathout = self.dictpathout
+        #self.dictpathout = PathOut(dirout = self.pathout)
+        #self.dictpathout = self.pathout
+        #self.dictpathout.basefiles(ChdTxtPathOut)
+        #self.pathout = self.dictpathout
         self.clnb = ''
         self.clnb = ''
-        self.ListAct = ListAct
+        self.ListAct = parametres.get('listact', False)
         self.ucecla = ''
         self.ucecla = ''
-        self.parent = parent
+        dlg = wx.ProgressDialog("Traitements",
+                               "Veuillez patienter...",
+                               maximum=5,
+                               parent=self.parent,
+                               style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME
+                                )
+
+        AnalyseMatrix.__init__(self, parent, parent.tableau, self.parametres, dlg = dlg)
+
+
 #-----------------------------------------------------------
 #-----------------------------------------------------------
+    def doanalyse(self) :
+#-------------------------------------------------------------------               
+        self.dictpathout = self.pathout
+        self.dictpathout.basefiles(ChdTxtPathOut)
+        self.parent.tableau.dictpathout = self.dictpathout
+
+        self.dlg.Center()
+        count = 1
+        keepGoing = self.dlg.Update(count)
+#-------------------------------------------------------------------
         count += 1
         count += 1
-        dlg.Update(count, u"passage en O/1")
-        if not ListAct:
-            self.parent.tableau.make_01_alc_format(self.dictpathout['Act01'])
+        self.dlg.Update(count, u"passage en O/1")
+        if 'formatted' in self.parametres:
+            self.parent.tableau.make_01_alc_format(self.dictpathout['mat01'])
         else:
         else:
-            self.parent.tableau.make_01_from_selection(ListAct, ListSup)
+            self.parent.tableau.make_01_from_selection(self.parametres['listact'], self.parametres['listsup'])
         file = open(self.dictpathout['listeuce1'], 'w')
         file.write('num uce;num uc\n')
         for i in range(0, len(self.parent.tableau.linecontent)):
         file = open(self.dictpathout['listeuce1'], 'w')
         file.write('num uce;num uc\n')
         for i in range(0, len(self.parent.tableau.linecontent)):
@@ -75,27 +86,28 @@ class DoQuestAlceste:
         file.close()
         self.nbind = len(self.parent.tableau.linecontent)
 #------------------------------------------------------------
         file.close()
         self.nbind = len(self.parent.tableau.linecontent)
 #------------------------------------------------------------
-        RchdQuest(self.dictpathout, parent.RscriptsPath, nbcl, mincl)
+        RchdQuest(self.dictpathout, self.parent.RscriptsPath, self.parametres['nbcl_p1'], self.parametres['mincl'])
 #------------------------------------------------------------
         count += 1
 #------------------------------------------------------------
         count += 1
-        dlg.Update(count, u"Analyse (patientez...)")
+        self.dlg.Update(count, u"Analyse (patientez...)")
         
         pid = exec_rcode(self.RPath, self.dictpathout['Rchdquest'], wait = False)
         while pid.poll() == None :
         
         pid = exec_rcode(self.RPath, self.dictpathout['Rchdquest'], wait = False)
         while pid.poll() == None :
-            dlg.Pulse(u"Analyse (patientez...)")
+            self.dlg.Pulse(u"Analyse (patientez...)")
             time.sleep(0.2)
         check_Rresult(self.parent, pid)
 #------------------------------------------------------------
         count += 1
             time.sleep(0.2)
         check_Rresult(self.parent, pid)
 #------------------------------------------------------------
         count += 1
-        dlg.Update(count, u"Ecriture des résultats")
+        self.dlg.Update(count, u"Ecriture des résultats")
         self.parent.tableau.buildprofil()
         self.clnb = self.parent.tableau.clnb
         self.parent.tableau.buildprofil()
         self.clnb = self.parent.tableau.clnb
+        self.parametres['clnb'] = self.clnb
         self.ucecla = self.parent.tableau.ucecla
         self.BuildProfile()
         temps = time.time() - self.t1
         self.ucecla = self.parent.tableau.ucecla
         self.BuildProfile()
         temps = time.time() - self.t1
-        PrintRapport(self, 'quest')
+        PrintRapport(self, self, {}, istxt = False)
         self.parent.tableau.save_tableau(self.dictpathout['db'])
         self.parent.tableau.save_tableau(self.dictpathout['db'])
-        CreateIraFile(self.dictpathout, self.clnb, corpname = os.path.basename(parent.filename), section = 'questionnaire')
+        #CreateIraFile(self.dictpathout, self.clnb, corpname = os.path.basename(self.parent.filename), section = 'questionnaire')
         afc_graph_list = [[os.path.basename(self.dictpathout['AFC2DL_OUT']), u'Variables actives - coordonnées - facteurs 1 / 2'],
                          [os.path.basename(self.dictpathout['AFC2DSL_OUT']), u'variables illustratives - coordonnées - facteurs 1 / 2'],
                          [os.path.basename(self.dictpathout['AFC2DCL_OUT']), u'Classes - Coordonnées - facteur 1 / 2'],
         afc_graph_list = [[os.path.basename(self.dictpathout['AFC2DL_OUT']), u'Variables actives - coordonnées - facteurs 1 / 2'],
                          [os.path.basename(self.dictpathout['AFC2DSL_OUT']), u'variables illustratives - coordonnées - facteurs 1 / 2'],
                          [os.path.basename(self.dictpathout['AFC2DCL_OUT']), u'Classes - Coordonnées - facteur 1 / 2'],
@@ -108,11 +120,11 @@ class DoQuestAlceste:
         print_liste(self.dictpathout['liste_graph_chd'], chd_graph_list)
 
         self.tableau = self.parent.tableau
         print_liste(self.dictpathout['liste_graph_chd'], chd_graph_list)
 
         self.tableau = self.parent.tableau
-        OpenCHDS(self.parent, self, self.dictpathout['ira'], False)
+        #OpenCHDS(self.parent, self, self.dictpathout['ira'], False)
 #------------------------------------------------------------
         print 'fini', time.time() - self.t1
         count += 1
 #------------------------------------------------------------
         print 'fini', time.time() - self.t1
         count += 1
-        dlg.Update(count, "Fini")
+        self.dlg.Update(count, "Fini")
     
     def BuildProfile(self):
         print 'build profile'
     
     def BuildProfile(self):
         print 'build profile'
index 2508bb4..6010eaa 100644 (file)
@@ -13,7 +13,6 @@ from ConfigParser import ConfigParser
 from functions import CreateIraFile, print_liste, exec_rcode, check_Rresult
 from dialog import CHDDialog, PrefQuestAlc, ClusterNbDialog
 import tempfile
 from functions import CreateIraFile, print_liste, exec_rcode, check_Rresult
 from dialog import CHDDialog, PrefQuestAlc, ClusterNbDialog
 import tempfile
-from numpy import *
 import time
 
 
 import time
 
 
index a8f7428..06f58e8 100644 (file)
@@ -7,12 +7,15 @@ import codecs
 import sys
 import xlrd
 import ooolib
 import sys
 import xlrd
 import ooolib
+import os
 import tempfile
 import re
 import htmlentitydefs
 import tempfile
 import re
 import htmlentitydefs
-from numpy import *
 import shelve
 import shelve
+from uuid import uuid4
+import logging
 
 
+log = logging.getLogger('iramuteq.tableau')
 
 ##
 # Removes HTML or XML character references and entities from a text string.
 
 ##
 # Removes HTML or XML character references and entities from a text string.
@@ -55,8 +58,11 @@ class Tableau() :
         self.parametre = {'filename' : filename}
         self.parametre['filetype'] = filetype
         self.parametre['encodage'] = encodage
         self.parametre = {'filename' : filename}
         self.parametre['filetype'] = filetype
         self.parametre['encodage'] = encodage
+        self.parametre['pathout'] = os.path.dirname(os.path.abspath(filename))
         self.parametre['mineff'] = 3
         self.parametre['syscoding'] = sys.getdefaultencoding()
         self.parametre['mineff'] = 3
         self.parametre['syscoding'] = sys.getdefaultencoding()
+        self.parametre['type'] = 'matrix'
+        self.parametre['name'] = 'unNOm'
         self.sups = {}
         self.actives = {}
         self.listactives = None
         self.sups = {}
         self.actives = {}
         self.listactives = None
@@ -71,6 +77,7 @@ class Tableau() :
         self.colnb = 0
         self.rownb = 0
         self.classes = []
         self.colnb = 0
         self.rownb = 0
         self.classes = []
+        self.parametres = self.parametre
 
     def read_tableau(self, fileout) :
         d=shelve.open(fileout)
 
     def read_tableau(self, fileout) :
         d=shelve.open(fileout)
@@ -200,9 +207,8 @@ class Tableau() :
         return dico
     
     def select_col(self, listcol) :
         return dico
     
     def select_col(self, listcol) :
-        ArTable = array(self.linecontent)
-        selcol = ArTable[:, listcol]
-        selcol = selcol.tolist()
+        dc = dict(zip(listcol, listcol))
+        selcol = [[val for i, val in enumerate(row) if i in dc] for row in self.linecontent]
         return selcol
     
     def write01(self, fileout, dico, linecontent) :
         return selcol
     
     def write01(self, fileout, dico, linecontent) :
@@ -220,10 +226,7 @@ class Tableau() :
     def make_01_from_selection(self, listact, listsup = None, dowrite = True) :
         selcol = self.select_col(listact)
         self.actives = self.make_dico(selcol)
     def make_01_from_selection(self, listact, listsup = None, dowrite = True) :
         selcol = self.select_col(listact)
         self.actives = self.make_dico(selcol)
-        if 'Act01' in self.dictpathout and dowrite:
-            self.write01(self.dictpathout['Act01'], self.actives, selcol)
-        elif 'mat01' in self.dictpathout and dowrite:
-            self.write01(self.dictpathout['mat01'], self.actives, selcol)
+        self.write01(self.dictpathout['mat01'], self.actives, selcol)
         if listsup is not None :
             selcol = self.select_col(listsup)
             self.sups = self.make_dico(selcol)
         if listsup is not None :
             selcol = self.select_col(listsup)
             self.sups = self.make_dico(selcol)
index 2af9792..8a5ca35 100644 (file)
@@ -10,8 +10,10 @@ from dialog import SelectColDial, OptLexi
 from guifunct import PrefSimi
 from listlex import *
 import wx
 from guifunct import PrefSimi
 from listlex import *
 import wx
-import wx.lib.agw.aui as aui
-from numpy import *
+if wx.__version__ >= '2.11' :
+    import wx.lib.agw.aui as aui
+else :
+    import aui
 import os
 import tempfile
 import datetime
 import os
 import tempfile
 import datetime
index 031e4e7..fd34232 100644 (file)
@@ -12,7 +12,6 @@ import wx
 import os
 import sys
 import tempfile
 import os
 import sys
 import tempfile
-from numpy import *
 from functions import exec_rcode, check_Rresult
 from time import sleep
 
 from functions import exec_rcode, check_Rresult
 from time import sleep
 
index a866276..5ff4c37 100644 (file)
@@ -208,10 +208,11 @@ class Lexico(AnalyseText) :
         tmpscript = open(tmpfile, 'w')
         tmpscript.write(txt)
         tmpscript.close()
         tmpscript = open(tmpfile, 'w')
         tmpscript.write(txt)
         tmpscript.close()
-        pid = exec_rcode(self.parent.RPath, tmpfile, wait = False)
-        while pid.poll() == None :
-            sleep(0.2)
-        check_Rresult(self.parent, pid)
+        self.doR(tmpfile, dlg = self.dlg, message = 'R...')
+        #pid = exec_rcode(self.parent.RPath, tmpfile, wait = False)
+        #while pid.poll() == None :
+        #    sleep(0.2)
+        #check_Rresult(self.parent, pid)
 
     def preferences(self) :
         listet = self.corpus.make_etoiles()
 
     def preferences(self) :
         listet = self.corpus.make_etoiles()
@@ -294,9 +295,11 @@ class Lexico(AnalyseText) :
 
         tabout = self.corpus.make_efftype_from_etoiles(self.listet)
         write_tab(tabout, self.dictpathout['tabletypem'])
 
         tabout = self.corpus.make_efftype_from_etoiles(self.listet)
         write_tab(tabout, self.dictpathout['tabletypem'])
-        #dlg.Update(2, u'R...')
+        if self.dlg :
+            self.dlg.Update(2, u'R...')
         self.DoR()
         self.DoR()
-        #dlg.Update(3, u'Chargement...')
+        if self.dlg :
+            self.dlg.Update(3, u'Chargement...')
         afcf_graph_list = [[os.path.basename(self.dictpathout['afcf_row']), u'lignes'],\
                             [os.path.basename(self.dictpathout['afcf_col']), u'colonnes']]
         afct_graph_list = [[os.path.basename(self.dictpathout['afct_row']), u'lignes'],\
         afcf_graph_list = [[os.path.basename(self.dictpathout['afcf_row']), u'lignes'],\
                             [os.path.basename(self.dictpathout['afcf_col']), u'colonnes']]
         afct_graph_list = [[os.path.basename(self.dictpathout['afct_row']), u'lignes'],\
index 918ee44..8650be1 100644 (file)
@@ -61,21 +61,21 @@ class SimiTxt(AnalyseText):
         else : 
             return False
 
         else : 
             return False
 
-    def preferences(self) :
-        dial = StatDialog(self, self.parent)
-        dial.CenterOnParent()
-        val = dial.ShowModal()
-        if val == 5100 :
-            if dial.radio_lem.GetSelection() == 0 :
-                lem = 1
-            else :
-                lem = 0            
-            self.parametres['lem'] = lem
-            dial.Destroy()
-            return self.parametres
-        else :
-            dial.Destroy()
-            return None
+#    def preferences(self) :
+#        dial = StatDialog(self, self.parent)
+#        dial.CenterOnParent()
+#        val = dial.ShowModal()
+#        if val == 5100 :
+#            if dial.radio_lem.GetSelection() == 0 :
+#                lem = 1
+#            else :
+#                lem = 0            
+#            self.parametres['lem'] = lem
+#            dial.Destroy()
+#            return self.parametres
+#        else :
+#            dial.Destroy()
+#            return None
 
     def makesimiparam(self) :
         self.paramsimi = {'coeff' : 0,
 
     def makesimiparam(self) :
         self.paramsimi = {'coeff' : 0,
index d14522f..afc9ff1 100644 (file)
@@ -31,20 +31,7 @@ class Stat(AnalyseText) :
         self.make_stats()
 
     def preferences(self) :
         self.make_stats()
 
     def preferences(self) :
-        dial = StatDialog(self, self.parent)
-        dial.CenterOnParent()
-        val = dial.ShowModal()
-        if val == 5100 :
-            if dial.radio_lem.GetSelection() == 0 :
-                lem = 1
-            else :
-                lem = 0            
-            self.parametres['lem'] = lem
-            dial.Destroy()
-            return self.parametres
-        else :
-            dial.Destroy()
-            return None
+        return self.parametres
 
     def make_stats(self):
         if self.dlg :
 
     def make_stats(self):
         if self.dlg :
diff --git a/tools.py b/tools.py
new file mode 100644 (file)
index 0000000..3b1b0d9
--- /dev/null
+++ b/tools.py
@@ -0,0 +1,156 @@
+#!/bin/env python
+# -*- coding: utf-8 -*-
+#Author: Pierre Ratinaud
+#Copyright (c) 2008-2013, Pierre Ratinaud
+#Lisense: GNU GPL
+
+import codecs
+import os
+from dialog import ExtractDialog
+from corpus import Corpus, copycorpus
+import wx
+
+
+parametres = {'filein' : 'corpus/lru2.txt',
+              'encodein' : 'utf8',
+              'encodeout' : 'utf8',
+              'mods' : [u'*annee_2010', u'*annee_2011']}
+
+def istext(line) :
+    if line.startswith(u'**** ') :
+        return True
+    else :
+        return False
+
+def testvar(line, variable) :
+    line = line.split()
+    varmod = [val.split('_') for val in line[1:]]
+    vars = [var[0] for var in varmod]
+    if variable in vars :
+        return '_'.join([variable, varmod[vars.index(variable)][1]]).replace(u'*','')
+    else :
+        return False
+
+def testmod(line, mods) :
+    line = line.split()
+    for mod in mods :
+        if mod in line[1:] :
+            return mod.replace(u'*','')
+    return False
+
+
+class Extract :
+    def __init__(self, parent, option) :
+        dial = ExtractDialog(parent, option)
+        dial.CenterOnParent()
+        res = dial.ShowModal()
+        if res == wx.ID_OK :
+            parametres = dial.make_param()
+            if option == 'splitvar' :
+                SplitFromVar(parametres)
+            else :
+                ExtractMods(parametres)
+
+class SplitFromVar :
+    def __init__(self, parametres) :
+        self.filein = parametres['filein']
+        self.var = parametres['var']
+        self.encodein = parametres['encodein']
+        self.encodeout = parametres['encodeout']
+        self.basepath = os.path.dirname(self.filein)
+        self.doparse()
+
+    def doparse(self) :
+        keepline = False
+        filedict = {}
+        with codecs.open(self.filein, 'r', self.encodein) as fin :
+             for line in fin :
+                 if istext(line) :
+                     varmod = testvar(line, self.var)
+                     if varmod :
+                         keepline = True
+                         if varmod not in filedict :
+                             filename = os.path.join(self.basepath, varmod + '.txt')
+                             filedict[varmod] = open(filename, 'w')
+                         fileout = filedict[varmod]
+                     else : 
+                         keepline = False
+                 if keepline :
+                     fileout.write(line.encode(self.encodeout))
+        for f in filedict :
+            filedict[f].close()
+
+class ExtractMods :
+    def __init__(self, parametres) :
+        self.onefile = parametres.get('onefile', False)
+        self.filein = parametres['filein']
+        self.mods = parametres['mods']
+        self.encodein = parametres['encodein']
+        self.encodeout = parametres['encodeout']
+        self.basepath = os.path.dirname(self.filein)
+        if self.onefile :
+            filename = os.path.join(self.basepath, '_'.join([mod.replace(u'*','') for mod in self.mods])+'.txt')
+            self.fileout = open(filename, 'w')
+        self.doparse()
+
+    def doparse(self) :
+        keepline = False
+        filedict = {}
+        with codecs.open(self.filein, 'r', self.encodein) as fin :
+             for line in fin :
+                 if istext(line) :
+                     modinline = testmod(line, self.mods)
+                     if modinline :
+                         keepline = True
+                         if not self.onefile :
+                            if modinline not in filedict :
+                                filename = os.path.join(self.basepath, modinline + '.txt')
+                                filedict[modinline] = open(filename, 'w')
+                            fileout = filedict[modinline]
+                         else :
+                             fileout = self.fileout
+                     else : 
+                         keepline = False
+                 if keepline :
+                     fileout.write(line.encode(self.encodeout))
+        if not self.onefile :
+            for f in filedict :
+                filedict[f].close()
+        else :
+            self.fileout.close()
+
+
+class SubCorpus(Corpus) :
+    def __init__(self, parent, corpus, sgts) :
+        Corpus.__init__(self, parent, corpus.parametres)
+        self.sgts = sgts
+        self.corpus = copycorpus(corpus)
+        self.corpus.make_lems(self.parametres['lem'])
+        textes = list(set([corpus.getucefromid(sgt).uci for sgt in sgts]))
+        self.ucis = [corpus.ucis[i] for i in textes]
+        for texte in self.ucis :
+            texte.uces = [uce for uce in texte.uces if uce.ident in self.sgts] 
+        self.make_formes(corpus)
+        self.pathout = corpus.pathout 
+        self.parametres['sub'] = self.sgts
+
+    def make_formes(self, corpus) :
+        self.formes = {}
+        for forme in self.corpus.formes :
+            sgtseff = self.corpus.getformeuceseff(forme)
+            sgts = set(self.sgts).intersection(sgtseff.keys())
+            if len(sgts) :
+                self.formes[forme] = self.corpus.formes[forme]
+                self.formes[forme].freq = sum([sgtseff[sgt] for sgt in sgts])
+
+    def getlemuces(self, lem) :
+        return list(set(self.sgts).intersection(self.corpus.getlemuces(lem)))
+
+
+
+
+
+
+if __name__ == '__main__' :
+    #SplitFromVar(parametres)
+    ExtractMods(parametres, True)
diff --git a/tree.py b/tree.py
index ea3d153..e9ab091 100644 (file)
--- a/tree.py
+++ b/tree.py
@@ -114,7 +114,7 @@ class LeftTree(CT.CustomTreeCtrl):
         self.history = parent.history
         self.h = self.history.history
         self.root = self.AddRoot("Iramuteq")
         self.history = parent.history
         self.h = self.history.history
         self.root = self.AddRoot("Iramuteq")
-
+        
         if not(self.GetAGWWindowStyleFlag() & CT.TR_HIDE_ROOT):
             self.SetPyData(self.root, None)
             self.SetItemImage(self.root, 24, CT.TreeItemIcon_Normal)
         if not(self.GetAGWWindowStyleFlag() & CT.TR_HIDE_ROOT):
             self.SetPyData(self.root, None)
             self.SetItemImage(self.root, 24, CT.TreeItemIcon_Normal)
@@ -129,11 +129,17 @@ class LeftTree(CT.CustomTreeCtrl):
             if 'analyses' in corpus :
                 for y in corpus['analyses'] :
                     last = self.AppendItem(child, y['name'], ct_type=0)
             if 'analyses' in corpus :
                 for y in corpus['analyses'] :
                     last = self.AppendItem(child, y['name'], ct_type=0)
-                        
                     self.SetPyData(last, y)
                     self.SetItemImage(last, 24, CT.TreeItemIcon_Normal)
                     self.SetItemImage(last, 13, CT.TreeItemIcon_Expanded)
                     self.SetPyData(last, y)
                     self.SetItemImage(last, 24, CT.TreeItemIcon_Normal)
                     self.SetItemImage(last, 13, CT.TreeItemIcon_Expanded)
-    
+
+        for matrix in self.history.matrix :
+            last = self.AppendItem(self.root, matrix['name'])
+            self.SetPyData(last, matrix)
+            self.SetItemImage(last, 24, CT.TreeItemIcon_Normal)
+            self.SetItemImage(last, 13, CT.TreeItemIcon_Expanded)
+
+
         self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
         #self.Bind(wx.EVT_IDLE, self.OnIdle)
 
         self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
         #self.Bind(wx.EVT_IDLE, self.OnIdle)
 
@@ -258,6 +264,19 @@ class LeftTree(CT.CustomTreeCtrl):
             self.GiveFocus(child, uuid, bold)
             child, cookie = self.GetNextChild(itemParent, cookie)
 
             self.GiveFocus(child, uuid, bold)
             child, cookie = self.GetNextChild(itemParent, cookie)
 
+    def IsInTree(self, itemParent = None, uuid = None) :
+        if itemParent is None :
+            itemParent = self.root
+        child, cookie = self.GetFirstChild(itemParent)
+        while child :
+            pydata = self.GetPyData(child)
+            if pydata['uuid'] == uuid :
+                return True
+            self.GiveFocus(child, uuid)
+            child, cookie = self.GetNextChild(itemParent, cookie)
+        return False
+
+
     def OnRightDown(self, event):
         
         pt = event.GetPosition()
     def OnRightDown(self, event):
         
         pt = event.GetPosition()
@@ -656,21 +675,24 @@ class LeftTree(CT.CustomTreeCtrl):
         dlg.Destroy()
 
     def AddAnalyse(self, parametres, itemParent = None, bold = True) :
         dlg.Destroy()
 
     def AddAnalyse(self, parametres, itemParent = None, bold = True) :
-        uuid = parametres['corpus']
-        if itemParent is None :
-            itemParent = self.root
-        child, cookie = self.GetFirstChild(itemParent)
-        corpus = None
-        while child :
-            pydata = self.GetPyData(child)
-            if pydata['uuid'] == uuid :
-                corpus = child
-                break
-            self.GiveFocus(child, uuid)
-            child, cookie = self.GetNextChild(itemParent, cookie)
-        #item = self.AppendItem(child, parametres['name'])
-        if corpus is not None : 
-            item = self.AppendItem(corpus, parametres['name'])
+        uuid = parametres.get('corpus', None)
+        if uuid is not None :
+            if itemParent is None :
+                itemParent = self.root
+            child, cookie = self.GetFirstChild(itemParent)
+            corpus = None
+            while child :
+                pydata = self.GetPyData(child)
+                if pydata['uuid'] == uuid :
+                    corpus = child
+                    break
+                self.GiveFocus(child, uuid)
+                child, cookie = self.GetNextChild(itemParent, cookie)
+            #item = self.AppendItem(child, parametres['name'])
+            if corpus is not None : 
+                item = self.AppendItem(corpus, parametres['name'])
+            else :
+                item = self.AppendItem(self.root, parametres['name'])
         else :
             item = self.AppendItem(self.root, parametres['name'])
         self.SetPyData(item, parametres)
         else :
             item = self.AppendItem(self.root, parametres['name'])
         self.SetPyData(item, parametres)
index 33a3766..d300d3a 100644 (file)
@@ -4,7 +4,6 @@
 
 #a simple treetagger parser
 import codecs
 
 #a simple treetagger parser
 import codecs
-import numpy
 import time
 import re
 from functions import ReadDicoAsDico
 import time
 import re
 from functions import ReadDicoAsDico