# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------- #
# 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.4 SVN HEAD
#
#
# Python Code By:
#
# Andrea Gavana, @ 23 Dec 2005
# Latest Revision: 17 Feb 2013, 21.00 GMT
#
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
# Write To Me At:
#
# andrea.gavana@gmail.com
# andrea.gavana@maerskoil.com
#
# Or, Obviously, To The wxPython Mailing List!!!
#
# Tags:        phoenix-port, unittest, documented, py3-port
#
# End Of Comments
# --------------------------------------------------------------------------- #

"""
Description
===========

`framemanager.py` is the central module of the AUI class framework.

:class:`AuiManager` manages the panes associated with it for a particular :class:`wx.Frame`, using
a pane's :class:`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 :meth:`AuiManager.Update() <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 json
import wx
# just for isinstance
from time import time

try:
    from time import perf_counter
except ImportError:  # clock is removed in py3.8
    from time import clock as perf_counter
import warnings

import six

from . import auibar
from . import auibook

from . import tabmdi
from . import dockart
from . import tabart

from .aui_utilities import Clip, PaneCreateStippleBitmap, GetDockingImage, GetSlidingPoints, \
                           StepColour, svg_to_bitmap

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

# AUI Events
wxEVT_AUI_PANE_BUTTON = wx.NewEventType()
wxEVT_AUI_PANE_CLOSE = wx.NewEventType()
wxEVT_AUI_PANE_CLOSED = 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` is about to be closed. """
EVT_AUI_PANE_CLOSED = wx.PyEventBinder(wxEVT_AUI_PANE_CLOSED, 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 :class:`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` :class:`AuiDockingGuideInfo` to `self`.

        :param `other`: another instance of :class:`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 :class:`AuiDockingGuideWindow` or :class:`AuiDockingHintWindow`.
        """

        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 integer `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 :class:`AuiManager`. """

    def __init__(self, eventType, id=1):
        """
        Default class constructor.

        :param integer `eventType`: the event kind;
        :param integer `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 :class:`AuiManager` to the current event.

        :param `mgr`: an instance of :class:`AuiManager`.
        """

        self.manager = mgr

    def SetDC(self, pdc):
        """
        Associates a :class:`wx.DC` device context to this event.

        :param `pdc`: a :class:`wx.DC` device context object.
        """

        self.dc = pdc

    def SetPane(self, p):
        """
        Associates a :class:`AuiPaneInfo` instance to this event.

        :param `p`: a :class:`AuiPaneInfo` instance.
        """

        self.pane = p

    def SetButton(self, b):
        """
        Associates a :class:`AuiPaneButton` instance to this event.

        :param `b`: a :class:`AuiPaneButton` instance.
        """

        self.button = b

    def GetManager(self):
        """ Returns the associated :class:`AuiManager` (if any). """

        return self.manager

    def GetDC(self):
        """ Returns the associated :class:`wx.DC` device context (if any). """

        return self.dc

    def GetPane(self):
        """ Returns the associated :class:`AuiPaneInfo` structure (if any). """

        return self.pane

    def GetButton(self):
        """ Returns the associated :class:`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 bool `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 bool `can_veto`: ``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
    # used internally to indicate that the toolbar is used to hold the button
    # to restore the minimized pane
    minimizeToolbar = 2**37

    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.minimize_target = None
        self.active_page = -1

        self.DefaultPane()

    def dock_direction_get(self):
        """
        Getter for the `dock_direction`.

        :see: :meth:`~AuiPaneInfo.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 integer `value`: the docking direction. This can be 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 :class:`AuiPaneInfo` structure is valid.

        :note: A pane structure is valid if it has an associated window.
        """

        return self.window is not 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 :meth:`~AuiPaneInfo.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
        :class:`~wx.lib.agw.aui.auibook.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_TOOLBAR``        0x06 Minimizes the pane on a target :class:`~wx.lib.agw.aui.auibar.AuiToolBar`
        ``AUI_MINIMIZE_POS_MASK``           0x17 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``           0x17 Filters the position flags
        ``AUI_MINIMIZE_CAPT_MASK``          0x18 Filters the caption flags
        ============================== ========= ==============================

        """

        return self.minimize_mode

    def GetOrientation(self):
        """
        Returns ``wx.VERTICAL`` if the pane is left/right docked,
            otherwise ``wx.HORIZONTAL``
        """

        vertical = self.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]
        return wx.VERTICAL if vertical else wx.HORIZONTAL

    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 :class:`wx.Window` derived window to this pane.

        This normally does not need to be specified, as the window pointer is
        automatically assigned to the :class:`AuiPaneInfo` structure as soon as it is
        added to the manager.

        :param `w`: a :class:`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 :meth:`AuiManager.SavePerspective` and :meth:`AuiManager.LoadPerspective`,
           you will have to specify a name for your pane using :meth:`~AuiPaneInfo.Name`, as perspectives
           containing randomly generated names can not be properly restored.
        """

        self.name = name
        return self

    def Caption(self, caption):
        """
        Sets the caption of the pane.

        :param string `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 :meth:`~AuiPaneInfo.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 :meth:`~AuiPaneInfo.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 :meth:`~AuiPaneInfo.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 :meth:`~AuiPaneInfo.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 :meth:`~AuiPaneInfo.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 :meth:`~AuiPaneInfo.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 :meth:`Left`, :meth:`Right`, :meth:`Top` or :meth:`Bottom`,
        except that docking direction may be specified programmatically via the parameter `direction`.

        :param integer `direction`: the direction of the docked pane.

        :see: :meth:`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 integer `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 integer `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 integer `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 :class:`wx.Size` object, then :meth:`~AuiPaneInfo.MinSize1` is called. Otherwise, :meth:`~AuiPaneInfo.MinSize2` is called.

        :param `arg1`: a :class:`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, tuple):
            ret = self.MinSize1(wx.Size(*arg1))
        elif isinstance(arg1, six.integer_types) and arg2 is not None:
            ret = self.MinSize2(arg1, arg2)
        else:
            raise Exception("Invalid argument passed to `MinSize`: arg1=%s, arg2=%s" % (repr(arg1), repr(arg2)))

        return ret

    def MinSize1(self, size):
        """
        Sets the minimum size of the pane.

        :see: :meth:`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: :meth:`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 :class:`wx.Size` object, then :meth:`~AuiPaneInfo.MaxSize1` is called. Otherwise, :meth:`~AuiPaneInfo.MaxSize2` is called.

        :param `arg1`: a :class:`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, tuple):
            ret = self.MaxSize1(wx.Size(*arg1))
        elif isinstance(arg1, six.integer_types) and arg2 is not None:
            ret = self.MaxSize2(arg1, arg2)
        else:
            raise Exception("Invalid argument passed to `MaxSize`: arg1=%s, arg2=%s" % (repr(arg1), repr(arg2)))

        return ret

    def MaxSize1(self, size):
        """
        Sets the maximum size of the pane.

        :see: :meth:`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: :meth:`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 :class:`wx.Size` object, then :meth:`BestSize1` is called. Otherwise, :meth:`BestSize2` is called.

        :param `arg1`: a :class:`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, tuple):
            ret = self.BestSize1(wx.Size(*arg1))
        elif isinstance(arg1, six.integer_types) and arg2 is not None:
            ret = self.BestSize2(arg1, arg2)
        else:
            raise Exception("Invalid argument passed to `BestSize`: arg1=%s, arg2=%s" % (repr(arg1), repr(arg2)))

        return ret

    def BestSize1(self, size):
        """
        Sets the best size of the pane.

        :see: :meth:`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: :meth:`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 :class:`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 :class:`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 :class:`~wx.lib.agw.aui.auibar.AuiToolBar`.

        Clicking on the minimize button causes a new :class:`~wx.lib.agw.aui.auibar.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 minimize button is visible.

        :param integer `mode`: 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
        ``AUI_MINIMIZE_POS_TOOLBAR``        0x06 Minimizes the pane on a target :class:`~wx.lib.agw.aui.auibar.AuiToolBar`
        ============================== ========= ==============================

        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
        ============================== ========= ==============================

        .. note::

           In order to use the ``AUI_MINIMIZE_POS_TOOLBAR`` flag, the instance of :class:`AuiManager`
           you pass as an input for :meth:`MinimizeTarget` **must** have a real name and not the randomly
           generated one. Remember to set the :meth:`Name` property of the toolbar pane before calling this method.

        """

        self.minimize_mode = mode
        return self

    def MinimizeTarget(self, toolbarPane):
        """
        Minimizes the panes using a :class:`AuiPaneInfo` as a target. As :class:`AuiPaneInfo` properties
        need to be copied back and forth every time the perspective has changed, we
        only store the toobar **name**.

        :param `toolbarPane`: an instance of :class:`AuiPaneInfo`, containing a :class:`~wx.lib.agw.aui.auibar.AuiToolBar`.

        .. note::

           In order to use this functionality (and with the ``AUI_MINIMIZE_POS_TOOLBAR``
           flag set), the instance of :class:`AuiPaneInfo` you pass as an input **must** have a real
           name and not the randomly generated one. Remember to set the :meth:`Name` property of
           the toolbar pane before calling this method.

        """

        self.minimize_target = toolbarPane.name
        return self

    def Restore(self):
        """ Is the reverse of :meth:`Maximize` and :meth:`Minimize`."""

        return self.SetFlag(self.optionMaximized | self.optionMinimized, False)

    def Fixed(self):
        """
        Forces a pane to be fixed size so that it cannot be resized.
        After calling :meth:`Fixed`, :meth:`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 :meth:`Fixed`.

        :param bool `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 integer `alpha`: a 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 :meth:`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 :meth:`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 :meth:`Show(False) <Show>` achieve the same effect.
        """

        return self.SetFlag(self.optionHidden, True)

    def Show(self, show=True):
        """
        Indicates that a pane should be shown.

        :param bool `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 :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.

        :param integer `id`: the notebook id;
        :param integer `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 (:class:`~wx.lib.agw.aui.auibook.AuiNotebook`).

        :param integer `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 :class:`~wx.lib.agw.aui.auibook.AuiNotebook` or not. """

        return self.notebook_id >= 0

    def IsNotebookPage(self):
        """ Returns whether the pane is a notebook page in a :class:`~wx.lib.agw.aui.auibook.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 (:class:`~wx.lib.agw.aui.auibook.AuiNotebook`). """

        return not self.IsNotebookPage() and self.HasNotebook()

    def SetNameFromNotebookId(self):
        """ Sets the pane name once docked in a :class:`~wx.lib.agw.aui.auibook.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 bool `visible`: whether the caption should be visible or not;
        :param bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 bool `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 :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.

        :param bool `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 bool `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
        :meth:`TopDockable` . :meth:`BottomDockable` . :meth:`LeftDockable` . :meth:`RightDockable` .

        :param bool `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 bool `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 bool `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 bool `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 bool `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 :meth:`TopSnappable` . :meth:`BottomSnappable` . :meth:`LeftSnappable` . :meth:`RightSnappable` .

        :param bool `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 bool `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,
         an instance of :class:`AuiPaneInfo`.
        """

        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 :class:`NullIcon`, no icon is drawn on
        the caption space.

        :param icon: an icon to draw on the caption space, or ``None``.
        :type `icon`: :class:`Icon` 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 integer `flag`: the property to set;
        :param bool `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 integer `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
        :class:`AuiManager` 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()
""" Null :class:`AuiPaneInfo` reference, an invalid instance of :class:`AuiPaneInfo`. """


# ---------------------------------------------------------------------------- #

class AuiDockingGuide(wx.Frame):
    """ Base class for :class:`AuiSingleDockingGuide` and :class:`AuiCenterDockingGuide`."""

    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 :class:`AuiManager` parent;
        :param integer `id`: the window identifier. It may take a value of -1 to indicate a default value.
        :param string `title`: the caption to be displayed on the frame's title bar.
        :param wx.Point `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 wx.Size `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 integer `style`: the window style.
        :param string `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 integer `x`: the `x` mouse position;
        :param integer `y`: the `y` mouse position.
        """

        return 0

    def ValidateNotebookDocking(self, valid):
        """
        To be overridden by parent classes.

        :param bool `valid`: whether a pane can be docked on top to another to form an automatic
         :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
        """

        return 0


# ============================================================================
# implementation
# ============================================================================

# ---------------------------------------------------------------------------
# AuiDockingGuideWindow
# ---------------------------------------------------------------------------

class AuiDockingGuideWindow(wx.Window):
    """ Target class for :class:`AuiDockingGuide` and :class:`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 :class:`AuiManager` parent;
        :param wx.Rect `rect`: the window rect;
        :param integer `direction`: one of ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, ``wx.RIGHT``,
         ``wx.CENTER``;
        :param bool `center`: whether the calling class is a :class:`AuiCenterDockingGuide`;
        :param bool `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 bool `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 :class:`AuiDockingGuideWindow`.

        :param `event`: a :class:`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 :class:`wx.DC` device context object.
        """

        colourTargetBackground = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)
        colourTargetShade = StepColour(colourTargetBackground, 110)
        colourTargetBorder = StepColour(colourTargetBackground, 120)
        rect = self.GetClientRect()

        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.SetBrush(wx.Brush(colourTargetBackground))
        dc.DrawRectangle(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 :class:`wx.DC` device context object;
        :param `point`: a :class:`wx.Point` where to start drawing the dotted line;
        :param integer `length`: the length of the dotted line;
        :param bool `vertical`: whether it is a vertical docking guide window or not.
        """

        for i in range(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 :class:`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.DrawRectangle(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
        if self._direction != wx.CENTER:
            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 :class:`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))
        dc.SetBrush(wx.Brush(colourIconArrow))
        points = [wx.Point() for i in range(4)]
        points[0].x = point.x
        points[0].y = point.y

        points[1].x = point.x + ry.x*3 - rx.x*3
        points[1].y = point.y + ry.y*3 - rx.y*3

        points[2].x = point.x + ry.x*3 + rx.x*3
        points[2].y = point.y + ry.y*3 + rx.y*3
        points[3] = points[0]
        dc.DrawPolygon(points)

    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for :class:`AuiDockingGuideWindow`.

        :param `event`: a :class:`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 :class:`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 :class:`wx.Point` mouse position.
        """

        if not self.GetTopLevelParent().IsShownOnScreen() and self.IsShownOnScreen():
            return

        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 :class:`AuiManager` parent;
        :param integer `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.SetClientSize(self.region.GetBox().GetSize())
        else:
            self.SetClientSize((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 bool `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.Region(bmp, wx.Colour(0, 0, 0, 0))

        self.region = region

    def AeroMove(self, pos):
        """
        Moves the docking window to the new position. Overridden in children classes.

        :param wx.Point `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 :class:`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
        :meth:`SetShape` again with an empty region.

        :param Region `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 bool `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 wx.Point `pos`: the mouse position.
        """

        self.target.UpdateDockGuide(pos)

    def HitTest(self, x, y):
        """
        Checks if the mouse position is inside the target window rect.

        :param integer `x`: the `x` mouse position;
        :param integer `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 :class:`AuiManager` 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.SetClientSize(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.Union(rectLeft)
            region.Union(rectTop)
            region.Union(rectRight)
            region.Union(rectBottom)
            region.Union(rectCenter)
            region.Union(wx.Region(tld))
            region.Union(wx.Region(bld))
            region.Union(wx.Region(trd))
            region.Union(wx.Region(brd))

        elif useAero:

            self._aeroBmp = aero_dock_pane.GetBitmap()
            region = wx.Region(self._aeroBmp, wx.Colour(0, 0, 0, 0))

            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.Region(self._aeroBmp, wx.Colour(0, 0, 0, 0))

            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 :class:`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 wx.Point `pos`: the 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 integer `x`: the `x` mouse position;
        :param integer `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
        :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.

        :param bool `valid`: whether a pane can be docked on top to another to form an automatic
         :class:`~wx.lib.agw.aui.auibook.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 wx.Point `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.SetTopLeft(thePos)

        leftRect.SetTopLeft(thePos + wx.Point(-sizeY, 0))
        topRect.SetTopLeft(thePos + wx.Point(0, -sizeY))
        rightRect.SetTopLeft(thePos + wx.Point(sizeX, 0))
        bottomRect.SetTopLeft(thePos + wx.Point(0, sizeX))

    def OnEraseBackground(self, event):
        """
        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`AuiCenterDockingGuide`.

        :param `event`: :class:`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 :class:`AuiCenterDockingGuide`.

        :param `event`: a :class:`PaintEvent` to be processed.
        """

        colourTargetBackground = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)
        colourTargetBorder = StepColour(colourTargetBackground, 120)
        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.GetLogicalWidth(), self._deniedBitmap.GetLogicalHeight()
                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.DrawLine(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 :class:`AuiManager` parent;
        :param integer `id`: the window identifier. It may take a value of -1 to indicate a default value.
        :param string `title`: the caption to be displayed on the frame's title bar;
        :param wx.Point `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 wx.Size `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 integer `style`: the window style;
        :param string `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._art = parent.GetEventHandler().GetArtProvider()
        background = self._art.GetColour(AUI_DOCKART_HINT_WINDOW_COLOUR)
        self.SetBackgroundColour(background)
        border = self._art.GetColour(AUI_DOCKART_HINT_WINDOW_BORDER_COLOUR)
        self._border_pen = wx.Pen(border, 5)

        # 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(background)
        else:
            self.Bind(wx.EVT_PAINT, self.OnPaint)

        self.Bind(wx.EVT_SIZE, self.OnSize)

    def MakeVenetianBlinds(self):
        """
        Creates the "venetian blind" effect if :class:`AuiManager` has the ``AUI_MGR_VENETIAN_BLINDS_HINT``
        flag set.
        """

        amount = 128
        size_x, size_y = self.GetClientSize()
        region = wx.Region(0, 0, size_x, 1)

        ## for y in range(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)
        region_Union = region.Union  # local opt
        [region_Union(0, y, size_x, 1) for y in range(size_y)
         if 16 * ((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])
         + 8 < amount]

        self.SetShape(region)

    def SetBlindMode(self, agwFlags):
        """
        Sets whether venetian blinds or transparent hints will be shown as docking hint.
        This depends on the :class:`AuiManager` flags.

        :param integer `agwFlags`: the :class:`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
        :meth:`SetShape` again with an empty region.

        :param Region `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(AuiDockingHintWindow, self).SetShape(region)

    def Show(self, show=True):
        """
        Show the hint window.

        :param bool `show`: whether to show or hide the hint docking window.
        """

        background = self._art.GetColour(AUI_DOCKART_HINT_WINDOW_COLOUR)
        border = self._art.GetColour(AUI_DOCKART_HINT_WINDOW_BORDER_COLOUR)
        self._border_pen = wx.Pen(border, 5)

        if wx.Platform == '__WXMAC__':
            self.panel.SetBackgroundColour(background)
        else:
            self.SetBackgroundColour(background)

        super(AuiDockingHintWindow, self).Show(show)
        self.Refresh()

        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 :class:`AuiDockingHintWindow`.

        :param `event`: a :class:`wx.SizeEvent` to be processed.
        """

        if self._blindMode or not self.CanSetTransparent():
            self.MakeVenetianBlinds()

        self.Refresh()

    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for :class:`AuiDockingHintWindow`.

        :param `event`: an instance of :class:`PaintEvent` to be processed.
        """

        rect = wx.Rect((0, 0), self.GetSize())

        dc = wx.PaintDC(self)
        event.Skip()

        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(self._border_pen)
        rect.Deflate(1, 1)
        dc.DrawRectangle(rect)


# ---------------------------------------------------------------------------- #

# -- 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 :class:`AuiManager` parent;
        :param `owner_mgr`: the :class:`AuiManager` that manages the floating pane;
        :param `pane`: the :class:`AuiPaneInfo` pane that is about to float;
        :param integer `id`: the window identifier. It may take a value of -1 to indicate a default value.
        :param string `title`: the caption to be displayed on the frame's title bar.
        :param integer `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 :class:`AuiPaneInfo`.

        :param `pane`: the source :class:`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
        contained_pane.minimize_target = pane.minimize_target

        return contained_pane

    def SetPaneWindow(self, pane):
        """
        Sets all the properties of a pane.

        :param `pane`: the :class:`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.DoUpdate()

        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 :class:`AuiManager` that manages the pane. """

        return self._owner_mgr

    def OnSize(self, event):
        """
        Handles the ``wx.EVT_SIZE`` event for :class:`AuiFloatingFrame`.

        :param `event`: a :class:`wx.SizeEvent` to be processed.
        """

        if self._owner_mgr and self._send_size and self.IsShownOnScreen():
            self._owner_mgr.OnFloatingPaneResized(self._pane_window, event.GetSize())

    def OnClose(self, event):
        """
        Handles the ``wx.EVT_CLOSE`` event for :class:`AuiFloatingFrame`.

        :param `event`: a :class:`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)
                self._pane_window.ExitFloating(wx.HORIZONTAL)

            # 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._fly_timer.Stop()
            self._check_fly_timer.Stop()

            self._mgr.UnInit()
            self.Destroy()

    def OnActivate(self, event):
        """
        Handles the ``wx.EVT_ACTIVATE`` event for :class:`AuiFloatingFrame`.

        :param `event`: a :class:`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 :class:`AuiFloatingFrame`.

        :param `event`: a :class:`MoveEvent` to be processed.

        .. note::

           This event is not processed on wxMAC or if :class:`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 :class:`AuiFloatingFrame`.

        :param `event`: a :class:`MoveEvent` to be processed.

        .. note::

           This event is only processed on wxMAC or if :class:`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

        # As on OSX moving windows are not getting all move events, only sporadically, this difference
        # is almost always big on OSX, so avoid this early exit opportunity
        if wx.Platform != '__WXMAC__':
            # 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)

                # However still update the internally stored position to avoid
                # snapping back to the old one later.
                if self._owner_mgr:
                    self._owner_mgr.GetPane(self._pane_window).floating_pos = win_rect.GetPosition()

                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

        dir = wx.ALL

        horiz_dist = abs(win_rect.x - self._last3_rect.x)
        vert_dist = abs(win_rect.y - self._last3_rect.y)

        if vert_dist >= horiz_dist:
            if win_rect.y < self._last3_rect.y:
                dir = wx.NORTH
            else:
                dir = wx.SOUTH
        else:
            if win_rect.x < self._last3_rect.x:
                dir = wx.WEST
            else:
                dir = wx.EAST

        self._last3_rect = wx.Rect(*self._last2_rect)
        self._last2_rect = wx.Rect(*self._last_rect)
        self._last_rect = wx.Rect(*win_rect)

        if not wx.GetMouseState().LeftIsDown():
            return

        if not self._moving:
            self.OnMoveStart(event)
            self._moving = True

        if self._last3_rect.IsEmpty():
            return

        if event.GetEventType() == wx.wxEVT_MOVING:
            self.OnMoving(event.GetRect(), dir)
        else:
            self.OnMoving(wx.Rect(event.GetPosition(), self.GetSize()), dir)

    def OnIdle(self, event):
        """
        Handles the ``wx.EVT_IDLE`` event for :class:`AuiFloatingFrame`.

        :param `event`: a :class:`IdleEvent` event to be processed.

        .. note::

           This event is only processed on wxMAC if :class:`AuiManager` is using the
           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.

        """

        if self._moving:
            if not wx.GetMouseState().LeftIsDown():
                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 :class:`MouseEvent`.

        .. note::

           This event is only processed on wxMAC if :class:`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, rect, direction):
        """
        The user is moving the floating pane.

        :param wx.Rect `rect`: the pane client rectangle;
        :param integer `direction`: the direction in which the pane is moving, can be one of
         ``wx.NORTH``, ``wx.SOUTH``, ``wx.EAST`` or ``wx.WEST``.

        .. note::

           This event is only processed on wxMAC if :class:`AuiManager` is using the
           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
        """

        # notify the owner manager that the pane is moving
        self.OnMoveStart(None)
        self._lastDirection = direction

    def OnMoveFinished(self):
        """
        The user has just finished moving the floating pane.

        .. note::

           This method is used only on wxMAC if :class:`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 :class:`AuiFloatingFrame`.

        :param `event`: a :class:`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 :class:`AuiFloatingFrame`.

        :param `event`: a :class:`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 wx.GetMouseState().LeftIsDown():
            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 :class:`AuiFloatingFrame`.

        :param `event`: a :class:`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 wx.Rect `rect`: a 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.PENSTYLE_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.DrawRectangle(rect)
        dc.SetLogicalFunction(wx.COPY)
    else:
        stipple = PaneCreateStippleBitmap()
        brush = wx.Brush(stipple)
        dc.SetBrush(brush)
        dc.SetPen(wx.TRANSPARENT_PEN)

        dc.SetLogicalFunction(wx.XOR)
        dc.DrawRectangle(rect)


def CopyDocksAndPanes(src_docks, src_panes):
    """
    This utility function creates shallow copies of
    the dock and pane info. :class:`AuiManager` usually contain pointers
    to :class:`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 :class:`AuiDockInfo` classes;
    :param `src_panes`: a list of :class:`AuiPaneInfo` classes.
    """

    dest_docks = src_docks
    dest_panes = src_panes

    for ii in range(len(dest_docks)):
        dock = dest_docks[ii]
        for jj in range(len(dock.panes)):
            for kk in range(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. :class:`AuiManager` usually contain pointers
    to :class:`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 :class:`AuiDockInfo` classes;
    :param `src_panes`: a list of :class:`AuiPaneInfo` classes.
    """

    dest_docks = []

    for ii in range(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 range(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
        dest_panes[ii].minimize_target = src_panes[ii].minimize_target

    for ii in range(len(dest_docks)):
        dock = dest_docks[ii]
        for jj in range(len(dock.panes)):
            for kk in range(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 :class:`AuiDockInfo`;
    :param `dock_direction`: the :class:`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 :class:`AuiPaneInfo`;
    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
    :param integer `dock_layer`: the :class:`AuiDockInfo` 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 :class:`AuiPaneInfo`;
    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
    """

    for ii in range(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 :class:`AuiPaneInfo`;
    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze;
    :param integer `dock_row`: the :class:`AuiDockInfo` 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 :class:`AuiPaneInfo`;
    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
    :param integer `dock_row`: the :class:`AuiDockInfo` row to analyze;
    :param integer `dock_pos`: the :class:`AuiDockInfo` position to analyze.
    """

    for ii in range(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 :class:`AuiDockInfo`;
    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
    :param integer `dock_row`: the :class:`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 :class:`AuiDockInfo`;
    :param integer `dock_direction`: the :class:`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 :class:`AuiDockInfo` pointer is returned, otherwise ``None``.

    :param `dock`: a :class:`AuiDockInfo` structure;
    :param wx.Window `window`: the window associated to the pane we are seeking.
    """

    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 :class:`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 :class:`wx.Window` derived window;
    :param `docks`: a list of :class:`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 :class:`wx.Window` derived window;
    :param `pt`: a :class:`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 :class:`wx.Window` derived window;
    :param `docks`: a list of :class:`AuiDockInfo` structures;
    :param `pt`: a :class:`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 :class:`AuiDockInfo` structures;
    :param `pane`: the pane to be removed, an instance of :class:`AuiPaneInfo`;
    :param `exc`: the possible pane exception, an instance of :class:`AuiPaneInfo`.
    """

    for ii in range(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 :class:`AuiDockInfo` structures.
    """

    for ii in range(len(docks)):
        dock = docks[ii]
        dock.dock_row = ii
        for jj in range(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 :class:`AuiPaneInfo` structures;
    :param `active_pane`: the pane to be made active (if found), an instance of :class:`AuiPaneInfo`.
    """

    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 :class:`AuiDockingGuide` classes;
    :param bool `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 :class:`AuiDockingGuide` 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`: the first pane instance to compare, an instance of :class:`AuiPaneInfo`;
    :param `p2`: the second pane instance to compare, an instance of :class:`AuiPaneInfo`.
    """

    return (p1.dock_pos < p2.dock_pos and [-1] or [1])[0]


def GetNotebookRoot(panes, notebook_id):
    """
    Returns the :class:`~wx.lib.agw.aui.auibook.AuiNotebook` which has the specified `notebook_id`.

    :param `panes`: a list of :class:`AuiPaneInfo` instances;
    :param integer `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 string `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 :class:`AuiPaneInfo` instance;
    :param `pane2`: another :class:`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 :class:`AuiManager`.

    .. note::

       This method always returns ``True`` on wxMAC as this platform doesn't have
       the ability to use :class:`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 :class:`MiniFrame` as
    floating panes.

    :param `manager`: an instance of :class:`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 wx.Window `window`: this parameter should be any child window or grand-child
     window (and so on) of the frame/window managed by :class:`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 :class:`wx.Frame`,
    using a pane's :class:`AuiManager` information to determine each pane's docking and
    floating behavior. :class:`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.

    :class:`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 :meth:`AuiManager.Update() <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 wx.Window `managed_window`: specifies the window which should be managed;
        :param integer `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
            :meth:`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_WINDOW_DESTROY, self.OnDestroy)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        if '__WXGTK__' in wx.PlatformInfo:
            self.Bind(wx.EVT_WINDOW_CREATE, self.DoUpdateEvt)

        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_END_DRAG, self.OnTabEndDrag)
        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 wx.Window `parent`: the floating frame parent;
        :param `pane_info`: the :class:`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 :class:`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 :meth:`GetPane` looks up a pane based on a 'pane window'.

        :param `window`: a :class:`wx.Window` derived window.

        :see: :meth:`~AuiManager.GetPane`
        """

        for p in self._panes:
            if p.window == window:
                return p

        return NonePaneInfo

    def GetPaneByName(self, name):
        """
        This version of :meth:`GetPane` looks up a pane based on a 'pane name'.

        :param string `name`: the pane name.

        :see: :meth:`GetPane`
        """

        for p in self._panes:
            if p.name == name:
                return p

        return NonePaneInfo

    def GetPane(self, item):
        """
        Looks up a :class:`AuiPaneInfo` structure based on the supplied window pointer. Upon failure,
        :meth:`GetPane` returns an empty :class:`AuiPaneInfo`, a condition which can be checked
        by calling :meth:`AuiPaneInfo.IsOk() <AuiPaneInfo.IsOk>`.

        The pane info's structure may then be modified. Once a pane's info is modified, :meth:`Update`
        must be called to realize the changes in the UI.

        :param `item`: either a pane name or a :class:`wx.Window`.
        """

        if isinstance(item, six.string_types):
            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 wx.Window `window`: any subclass or derivation of :class:`wx.Window`;
        :param bool `show`: ``True`` to show the pane, ``False`` otherwise.
        """

        p = self.GetPane(window)

        if p.IsOk():
            if p.IsNotebookPage():
                notebook = self._notebooks[p.notebook_id]
                page_idx = notebook.GetPageIndex(p.window)
                if page_idx >= 0:
                    notebook.HidePage(page_idx, not show)
                    if show:
                        notebook.SetSelection(page_idx)
                if notebook.GetShownPageCount() > 0:
                    self.ShowPane(notebook, True)
                else:
                    self.ShowPane(notebook, False)

            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 integer `x`: specifies a x position in client coordinates;
        :param integer `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 :meth:`HitTest`, but it checks in which :class:`AuiManager` rectangle the
        input point belongs to.

        :param `panes`: a list of :class:`AuiPaneInfo` instances;
        :param wx.Point `pt`: the mouse position.
        """

        for paneInfo in panes:
            if paneInfo.IsNotebookPage():
                continue
            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 :class:`AuiManager` 's settings flags.

        :param integer `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
            :meth:`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: :meth:`SetAGWFlags` for a list of possible :class:`AuiManager` flags.
        """

        return self._agwFlags

    def SetManagedWindow(self, managed_window):
        """
        Called to specify the frame or window which is to be managed by :class:`AuiManager`.
        Frame management is not restricted to just frames. Child windows or custom
        controls are also allowed.

        :param wx.Window `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.UnInit()

        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 :class:`AuiManager`. """

        return self._frame

    def SetFrame(self, managed_window):
        """
        Called to specify the frame or window which is to be managed by :class:`AuiManager`.
        Frame management is not restricted to just frames. Child windows or custom
        controls are also allowed.

        :param wx.Window `managed_window`: specifies the window which should be managed by
         the AUI manager.

        .. deprecated:: 0.6
           This method is now deprecated, use :meth:`SetManagedWindow` instead.
        """

        DeprecationWarning("This method is deprecated, use SetManagedWindow instead.")
        return self.SetManagedWindow(managed_window)

    def GetFrame(self):
        """
        Returns the window being managed by :class:`AuiManager`.

        .. deprecated:: 0.6
           This method is now deprecated, use :meth:`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. :meth:`UnInit` is usually called in the managed :class:`wx.Frame` / :class:`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 not self._frame:
            return

        for klass in [self._frame] + list(self._frame.GetChildren()):
            handler = klass.GetEventHandler()
            if klass is not handler:
                if isinstance(handler, AuiManager):
                    klass.RemoveEventHandler(handler)

    def OnClose(self, event):
        """Called when the managed window is closed. Makes sure that :meth:`UnInit`
        is called.
        """

        event.Skip()
        if event.GetEventObject() == self._frame:
            wx.CallAfter(self.UnInit)

    def OnDestroy(self, event):
        """Called when the managed window is destroyed. Makes sure that :meth:`UnInit`
        is called.
        """

        if self._frame == event.GetEventObject():
            self.UnInit()

    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 :class:`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 integer `evtType`: one of the aforementioned events;
        :param `pane`: the :class:`AuiPaneInfo` instance associated to this event;
        :param bool `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 :class:`dockart` can be used (Windows XP / Vista / 7 only,
        requires Mark Hammonds's `pywin32 <https://sourceforge.net/projects/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 :class:`AuiManager` to use art provider specified by the parameter
        `art_provider` for all drawing calls. This allows pluggable look-and-feel
        features.

        :param `art_provider`: a AUI dock art provider.

        :note: The previous art provider object, if any, will be deleted by :class:`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 version allows the full spectrum
        of pane parameter possibilities (:meth:`AddPane1`). The second version is used for
        simpler user interfaces which do not require as much configuration (:meth:`AddPane2`).
        The :meth:`AddPane3` version allows a drop position to be specified, which will determine
        where the pane will be added. The :meth:`AddPane4` version allows to turn the target
        :class:`AuiPaneInfo` pane into a notebook and the added pane into a page.

        In your code, simply call :meth:`AddPane`.

        :param wx.Window `window`: the child window to manage;
        :param `arg1`: a :class:`AuiPaneInfo` or an integer value (direction);
        :param `arg2`: a :class:`AuiPaneInfo` or a :class:`wx.Point` (drop position);
        :param `target`: a :class:`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)
         """
        rtn = None
        if target in self._panes:
            rtn = self.AddPane4(window, arg1, target)

        elif isinstance(arg1, int):
            # This Is Addpane2
            if arg1 is None:
                arg1 = wx.LEFT
            if arg2 is None:
                arg2 = ""
            rtn = self.AddPane2(window, arg1, arg2)
        else:
            if isinstance(arg2, wx.Point):
                rtn = self.AddPane3(window, arg1, arg2)
            else:
                rtn = self.AddPane1(window, arg1)
        if rtn:
            pane = self.GetPane(window);
            if pane.IsOk():
                self.SwitchToolBarOrientation(pane)
        return rtn

    def AddPane1(self, window, pane_info):
        """ See comments on :meth:`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(), int(time()),
                                               int(perf_counter()), 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.GetBestSize()

            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 background
                # 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 :meth:`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 :meth:`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 :meth:`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.
        :meth:`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 wx.Window `window`: the window to be inserted and managed;
        :param `pane_info`: the insert location for the new window;
        :param integer `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 :class:`AuiManager` to stop managing the pane specified
        by `window`. The window, if in a floated frame, is reparented to the frame
        managed by :class:`AuiManager`.

        :param wx.Window `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._mgr.UnInit()
                    p.frame.Destroy()
                    p.frame = None

                elif p.IsNotebookPage():
                    notebook = self._notebooks[p.notebook_id]
                    id = notebook.GetPageIndex(p.window)
                    notebook.RemovePage(id)
                    p.window.Reparent(self._frame)

                # 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 range(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 :class:`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)

        # if we have a frame, destroy it
        if pane_info.frame:
            # 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)
            pane_info.frame.Destroy()
            pane_info.frame = None
            pane_info.Hide()

        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 in self._notebooks:
                    notebook = self._notebooks[nid]
                    page_idx = notebook.GetPageIndex(pane_info.window)
                    if page_idx >= 0:
                        if not pane_info.IsDestroyOnClose():
                            self.ShowPane(pane_info.window, False)

        else:
            if pane_info.window and pane_info.window.GetParent() != self._frame:
                pane_info.window.Reparent(self._frame)
            pane_info.Dock().Hide()

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

        if pane_info.IsNotebookControl():

            notebook = self._notebooks[pane_info.notebook_id]
            for idx in range(notebook.GetPageCount() - 1, -1, -1):
                window = notebook.GetPage(idx)
                info = self.GetPane(window)
                # close page if its IsDestroyOnClose flag is set
                if info.IsDestroyOnClose():
                    if info.IsOk():
                        # Note: this could change our paneInfo reference ...
                        self.ClosePane(info)

        if to_destroy:
            to_destroy.Destroy()

        # Now inform the app that we closed a pane.
        self.FireEvent(wxEVT_AUI_PANE_CLOSED, pane_info)

    def MaximizePane(self, pane_info, savesizes=True):
        """
        Maximizes the input pane.

        :param `pane_info`: a :class:`AuiPaneInfo` instance.
        :param bool `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() and not p.IsNotebookPage() and not p.IsMinimized():
                # ignore the notebook page as its parent (notebook ctrl) will take care
                # ignore the minimized pane as it is not showing, otherwise, we
                # also need to remember its status (minimized) to restore it.

                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 :class:`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 :class:`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 :class:`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 :class:`~wx.lib.agw.aui.auibook.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())

        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 integer `agwStyle`: the underlying :class:`~wx.lib.agw.aui.auibook.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 :class:`~wx.lib.agw.aui.auibook.AuiNotebook` tabs by mouse middle button click
         ``AUI_NB_SUB_NOTEBOOK``              This style is used by :class:`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
         ==================================== ==================================

        """

        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: :meth:`SetAutoNotebookStyle` method for a list of possible styles.
        """

        return self._autoNBStyle

    def SavePaneInfo(self, pane):
        """
        This method is similar to :meth:`SavePerspective`, with the exception
        that it only saves information about a single pane. It is used in
        combination with :meth:`LoadPaneInfo`.

        :param `pane`: a :class:`AuiPaneInfo` instance to save.
        """

        result = {"name": EscapeDelimiters(pane.name),
                  "caption": EscapeDelimiters(pane.caption),
                  "state": pane.state,
                  "dock_direction": pane.dock_direction,
                  "dock_layer": pane.dock_layer,
                  "dock_row": pane.dock_row,
                  "dock_pos": pane.dock_pos,
                  "dock_proportion": pane.dock_proportion,
                  "best_size": (pane.best_size.x, pane.best_size.y),
                  "min_size": (pane.min_size.x, pane.min_size.y),
                  "max_size": (pane.max_size.x, pane.max_size.y),
                  "floating_pos": (pane.floating_pos.x, pane.floating_pos.y),
                  "floating_size": (pane.floating_size.y, pane.floating_size.y),
                  "notebook_id": pane.notebook_id,
                  "transparent": pane.transparent,
                  }
        if pane.IsNotebookControl() and pane.window:
            result["active_page"] = pane.window.GetSelection()
        else:
            result["active_page"] = -1

        return result

    def LoadPaneInfo(self, pane_part, pane):
        """
        This method is similar to to :meth:`LoadPerspective`, with the exception that
        it only loads information about a single pane. It is used in combination
        with :meth:`SavePaneInfo`.

        :param string `pane_part`: the string to analyze;
        :param `pane`: the :class:`AuiPaneInfo` structure in which to load `pane_part`.
        """

        # replace escaped characters so we can
        # split up the string easily
        for val_name, value in pane_part.items():
            if hasattr(pane, val_name):
                if val_name in ['max_size', 'min_size', 'best_size', 'floating_size']:
                    setattr(pane, val_name, wx.Size(value[0], value[1]))
                elif val_name in ['floating_pos']:
                    setattr(pane, val_name, wx.Point(value[0], value[1]))
                else:
                    setattr(pane, val_name, value)
            else:
                print(f"Bad perspective string: {val_name}={value}")

        pane.ResetButtons()
        if pane.IsNotebookControl():
            pane.SetNameFromNotebookId()
        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 :class:`Config <ConfigBase>`).

        When a perspective is restored using :meth:`LoadPerspective`, the entire user
        interface will return to the state it was when the perspective was saved.
        """

        panes = []
        for pane in self._panes:
            if pane.HasFlag(AuiPaneInfo.minimizeToolbar):
                # ignore the generated minimized toolbar panes
                continue
            panes.append(self.SavePaneInfo(pane))

        docks = []
        for dock in self._docks:
            docks.append([dock.dock_direction, dock.dock_layer, dock.dock_row, dock.size])

        result = {"panes": panes, "docks": docks}
        return json.dumps(result)

    def LoadPerspective(self, layout, update=True, restorecaption=False):
        """
        Loads a layout which was saved with :meth:`SavePerspective`.

        If the `update` flag parameter is ``True``, :meth:`Update` will be
        automatically invoked, thus realizing the saved perspective on screen.

        :param string `layout`: a string which contains a saved AUI layout;
        :param bool `update`: whether to update immediately the window or not;
        :param bool `restorecaption`: ``False``, restore from persist storage,
         otherwise use the caption defined in code.
        """
        try:
            layout = json.loads(layout)
        except:
            return False

        # delete the generated minimized toolbar panes, as they will be re-created
        # if they are still minimized in the new perspective.
        panes = list(self._panes)
        for pane in panes:
            if pane.IsMinimized():
                self.RestoreMinimizedPane(pane)

        # mark all panes currently managed as docked and hidden
        saveCapt = {}  # see restorecaption param
        for pane in self._panes:

            # dock the notebook pages
            if pane.IsNotebookPage():
                notebook = self._notebooks[pane.notebook_id]
                idx = notebook.GetPageIndex(pane.window)
                notebook.RemovePage(idx)
                pane.window.Reparent(self._frame)
                nb = self.GetPane(notebook)
                pane.notebook_id = -1
                pane.Direction(nb.dock_direction)

            pane.Dock().Hide()
            saveCapt[pane.name] = pane.caption

        # clear out the dock array; this will be reconstructed
        self._docks = []

        docks = layout['docks']
        for d in docks:
            dock = AuiDockInfo()
            dock.dock_direction = d[0]
            dock.dock_layer = d[1]
            dock.dock_row = d[2]
            dock.size = d[3]
            self._docks.append(dock)

        panes = layout['panes']
        for pane_part in panes:

            pane = self.GetPane(pane_part.get('name', ''))
            if not pane.IsOk():
                pane = AuiPaneInfo()

            pane = self.LoadPaneInfo(pane_part, pane)

            if not pane.IsOk():
                if pane.IsNotebookControl():
                    # notebook controls - auto add...
                    self._panes.append(pane)

            if isinstance(pane.window, auibar.AuiToolBar) and (pane.IsFloatable() or pane.IsDockable()):
                pane.window.SetGripperVisible(True)

        if update:
            # update all panes to give notebook a chance to update it
            self.Update()

        for p in self._panes:
            if p.IsMinimized() and p.IsOk():
                self.MinimizePane(p, False)

        if update:
            self.Update()

        return True

    def GetPanePositionsAndSizes(self, dock):
        """
        Returns all the panes positions and sizes in a dock.

        :param `dock`: a :class:`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 range(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

        for pane_i in range(action_pane - 1, -1, -1):
            amount = positions[pane_i + 1] - (positions[pane_i] + sizes[pane_i])
            if amount >= 0:
                pass
            else:
                positions[pane_i] -= -amount

        # 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 range(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 :class:`wx.Sizer` object;
        :param `dock`: the :class:`AuiDockInfo` structure in which to add the pane;
        :param `pane`: the :class:`AuiPaneInfo` instance to add to the dock;
        :param `uiparts`: a list of UI parts in the interface;
        :param bool `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 range(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:
            if pane.window.GetContainingSizer():
                # Make sure that there is only one sizer to this window
                pane.window.GetContainingSizer().Detach(pane.window);
            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, int(round(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, int(round(pane_proportion)), wx.EXPAND)

        return uiparts

    def LayoutAddDock(self, cont, dock, uiparts, spacer_only):
        """
        Adds a dock into the existing layout.

        :param `cont`: a :class:`wx.Sizer` object;
        :param `dock`: the :class:`AuiDockInfo` structure to add to the layout;
        :param `uiparts`: a list of UI parts in the interface;
        :param bool `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 range(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 range(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 :class:`AuiPaneInfo` instances;
        :param `docks`: a list of :class:`AuiDockInfo` classes;
        :param `uiparts`: a list of UI parts in the interface;
        :param bool `spacer_only`: whether to add a simple spacer or a real window;
        :param bool `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_pane_count = len(dock.panes)
            if dock_pane_count > 1:
                # ~ dock.panes.sort(PaneSortFunc)
                dock.panes.sort(key=lambda pane: pane.dock_pos)

            # 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 tuple(cli_size) <= tuple(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 range(dock_pane_count):
                    pane = dock.panes[jj]
                    if pane.IsNotebookPage() and pane.notebook_id in self._notebooks:
                        # update dock_pos to its index in notebook
                        notebook = self._notebooks[pane.notebook_id]
                        pane.dock_pos = notebook.GetPageIndex(pane.window)
                    else:
                        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 range(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 range(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 range(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 unwieldy
        large.

        :class:`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 :meth:`SetDockSizeConstraint`
        with (0.5, 0.5) will cause new docks to be limited to half of the size of the entire
        managed window.

        :param float `width_pct`: a number representing the `x` dock size constraint;
        :param float `width_pct`: a 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: :meth:`SetDockSizeConstraint`
        """

        return self._dock_constraint_x, self._dock_constraint_y

    def Update(self):
        if '__WXGTK__' in wx.PlatformInfo:
            wx.CallAfter(self.DoUpdate)
        else:
            self.DoUpdate()

    def DoUpdateEvt(self, evt):
        self.Unbind(wx.EVT_WINDOW_CREATE)
        wx.CallAfter(self.DoUpdate)

    def DoUpdate(self):
        """
        This method is called after any number of changes are made to any of the
        managed panes. :meth:`Update` must be invoked after :meth:`AddPane`
        or :meth:`InsertPane` are called in order to "realize" or "commit" the changes.

        In addition, any number of changes may be made to :class:`AuiManager` structures
        (retrieved with :meth:`GetPane`), but to realize the changes, :meth:`Update`
        must be called. This construction allows pane flicker to be avoided by updating
        the whole layout at one time.
        """

        if not self.GetManagedWindow():
            return

        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)
                p.window.ExitFloating(p.GetOrientation())
                s = p.window.GetMinSize()
                p.BestSize(s)

            if p.frame:
                p.frame.SetSizer(None)
                while p.frame.GetEventHandler() is not p.frame:
                    p.frame.PopEventHandler()
                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 range(pane_count):
            p = self._panes[ii]
            pFrame = p.frame

            if p.IsFloating():
                if p.IsToolbar():
                    bar = p.window
                    if isinstance(bar, auibar.AuiToolBar):
                        bar.EnterFloating()

                    s = p.window.GetMinSize()
                    p.BestSize(s)
                    p.FloatingSize(wx.DefaultSize)

                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)

                    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.SetSize(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.Icon(p.icon))

                # call Layout() so the AuiToolBar::OnSize will called, and update
                # its overflow flag, otherwise, the minimized toolbar may
                # always show the overflow button
                pFrame.Layout()
            else:

                if p.IsToolbar():
                    self.SwitchToolBarOrientation(p)

                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 range(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()
            self._frame.Refresh(False)

        if not self._masterManager:
            e = self.FireEvent(wxEVT_AUI_PERSPECTIVE_CHANGED, None, canVeto=False)

    def UpdateNotebook(self):
        """ Updates the automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook` in the layout (if any exists). """

        # Remove pages from notebooks that no-longer belong there ...
        for nb, notebook in self._notebooks.items():
            pages = notebook.GetPageCount()
            pageCounter, allPages = 0, pages

            # Check each tab ...
            for page in range(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
                    paneInfo.Direction(self.GetPane(notebook).dock_direction)

                pageCounter += 1

            notebook.DoSizing()

        # Add notebook pages that aren't there already...
        pages_and_panes = {}
        for paneInfo in self._panes:
            if paneInfo.IsNotebookPage():
                if paneInfo.notebook_id not in self._notebooks:
                    self._notebooks[paneInfo.notebook_id] = self.CreateNotebook()

                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:

                    if paneInfo.notebook_id not in pages_and_panes:
                        pages_and_panes[paneInfo.notebook_id] = []
                    pages_and_panes[paneInfo.notebook_id].append(paneInfo)

                # 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:
                if paneInfo.notebook_id not in self._notebooks:
                    self._notebooks[paneInfo.notebook_id] = self.CreateNotebook()
                paneInfo.window = self._notebooks[paneInfo.notebook_id]

        for notebook_id, pnp in six.iteritems(pages_and_panes):
            # sort the panes with dock_pos
            sorted_pnp = sorted(pnp, key=lambda pane: pane.dock_pos)
            notebook = self._notebooks[notebook_id]
            for pane in sorted_pnp:
                title = (pane.caption == "" and [pane.name] or [pane.caption])[0]
                pane.window.Reparent(notebook)
                notebook.AddPage(pane.window, title, True, pane.icon)
            notebook.DoSizing()

        # Delete empty notebooks, and convert notebooks with 1 page to
        # normal panes...

        for nb in list(self._notebooks.keys()):
            notebook = self._notebooks[nb]
            if notebook.GetPageCount() == 1:

                # Convert notebook page to pane...
                window = notebook.GetPage(0)
                page = notebook.GetPageInfo(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.Show(not page.hidden)
                    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()
                    del self._notebooks[nb]
                else:

                    raise Exception("Odd notebook docking")

            elif notebook.GetPageCount() == 0:

                self.DetachPane(notebook)
                notebook.Destroy()
                del self._notebooks[nb]
            else:

                if notebook.GetShownPageCount() == 0:
                    # if no page is visible, hide the notebook
                    notebook_pane = self.GetPane(notebook)
                    if notebook_pane:
                        notebook_pane.Show(False)

        # Make sure buttons are correct ...
        for _, notebook in self._notebooks.items():
            want_max = True
            want_min = True
            want_close = True

            pages = notebook.GetPageCount()
            for page in range(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 0 <= notebook_pane.active_page < notebook.GetPageCount():
                    notebook.SetSelection(notebook_pane.active_page)
                    notebook_pane.active_page = -1
                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 :class:`AuiDockInfo` instances;
        :param integer `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 :class:`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())

            if guide.host.IsShownOnScreen():
                guide.host.UpdateDockGuide(mousePos)

        paneInfo.window.Lower()

    def DoFrameLayout(self):
        """
        This is an internal function which invokes :meth:`wx.Sizer.Layout() <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 wx.Window `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 :class:`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.Rect(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 :class:`AuiDockInfo` instance.
        """

        for layer in range(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 :class:`AuiDockInfo`.

        :param `dock`: a :class:`AuiDockInfo` instance;
        :param `pane`: a :class:`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 :class:`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 :class:`AuiDockInfo` structures to analyze;
        :param integer `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 :class:`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.Rect(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 :class:`AuiDockInfo` structure to which `pane` belongs to;
        :param `pane`: a :class:`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, an instance of :class:`AuiDockUIPart`.
        """

        # 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 :class:`AuiPaneInfo` from a :class:`~wx.lib.agw.aui.auibook.AuiNotebook` event.

        :param `event`: a :class:`~wx.lib.agw.aui.auibook.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 :class:`~wx.lib.agw.aui.auibook.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()
                # always shows the pane
                paneInfo.Show()

                # The call to Update may result in
                # the notebook that generated this
                # event being deleted, so we have
                # to do the call asynchronously.
                wx.CallAfter(self.Update)

                self._action_window = paneInfo.window

                self._frame.CaptureMouse()
                event.SetDispatched(True)

            else:

                # not our window
                event.Skip()

    def OnTabEndDrag(self, event):
        """
        Handles the ``EVT_AUINOTEBOOK_END_DRAG`` event.

        :param `event`: a :class:`~wx.lib.agw.aui.auibook.AuiNotebookEvent` event to be processed.
        """

        if self._masterManager:
            self._masterManager.OnTabEndDrag(event)
        else:
            self.Update()
            event.Skip()

    def OnTabPageClose(self, event):
        """
        Handles the ``EVT_AUINOTEBOOK_PAGE_CLOSE`` event.

        :param `event`: a :class:`~wx.lib.agw.aui.auibook.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

                # Close/update asynchronously, because
                # the notebook which generated the event
                # (and triggered this method call) will
                # be deleted.
                def close():
                    self.ClosePane(p)
                    self.Update()

                wx.CallAfter(close)
            else:
                event.Skip()

    def OnTabSelected(self, event):
        """
        Handles the ``EVT_AUINOTEBOOK_PAGE_CHANGED`` event.

        :param `event`: a :class:`~wx.lib.agw.aui.auibook.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 :class:`~wx.lib.agw.aui.auibook.AuiNotebook` in the :class:`AuiManager`. """

        if self._masterManager:
            return self._masterManager.GetNotebooks()

        return self._notebooks

    def SetMasterManager(self, manager):
        """
        Sets the master manager for an automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.

        :param `manager`: an instance of :class:`AuiManager`.
        """

        self._masterManager = manager

    def ProcessDockResult(self, target, new_pos):
        """
        This is a utility function used by :meth:`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 :class:`AuiPaneInfo` instance to be docked;
        :param integer `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


        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 :class:`AuiPaneInfo`, which may have a :class:`~wx.lib.agw.aui.auibar.AuiToolBar`
         window associated with it.
        """

        if not isinstance(pane.window, auibar.AuiToolBar):
            return pane

        if pane.IsFloating():
            return pane

        toolBar = pane.window
        toolBar.ExitFloating(pane.GetOrientation())
        toolBar.SetOrientation(pane.GetOrientation())
        s = pane.window.GetMinSize()
        pane.BestSize(s)

        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 :class:`AuiDockInfo` classes;
        :param `panes`: a list of :class:`AuiPaneInfo` instances;
        :param wx.Point `pt`: a mouse position to check for a drop operation;
        :param wx.Point `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 :class:`AuiPaneInfo`.

        :param `target`: the source :class:`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
        drop.minimize_target = target.minimize_target

        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 :class:`AuiDockInfo` classes;
        :param `panes`: a list of :class:`AuiPaneInfo` instances;
        :param `target`: the target pane containing the toolbar, an instance of :class:`AuiPaneInfo`;
        :param wx.Point `pt`: a mouse position to check for a drop operation;
        :param wx.Point `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 not drop.IsFloating() and 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 :class:`AuiDockInfo` classes;
        :param `panes`: a list of :class:`AuiPaneInfo` instances;
        :param `target`: the target pane containing the window, an instance of
         :class:`AuiPaneInfo`;
        :param wx.Point `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 range(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 :class:`AuiDockInfo` classes;
        :param `panes`: a list of :class:`AuiPaneInfo` instances;
        :param `target`: the target pane containing the toolbar, an instance of :class:`AuiPaneInfo`;
        :param wx.Point `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 :class:`AuiDockInfo` classes;
        :param `target`: the target pane, an instance of :class:`AuiPaneInfo`;
        :param integer `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 :class:`AuiPaneInfo` classes;
        :param `target`: the target pane, an instance of :class:`AuiPaneInfo`;
        :param integer `dock_direction`: the docking direction;
        :param integer `dock_layer`: the docking layer;
        :param integer `dock_row`: the docking row;
        :param integer `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 :class:`AuiPaneInfo` classes;
        :param `target`: the target pane, an instance of :class:`AuiPaneInfo`;
        :param integer `dock_direction`: the docking direction;
        :param integer `dock_layer`: the docking layer;
        :param integer `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 wx.Rect `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.Subtract(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.Intersect(self._frame.GetRect())
            screendc.SetDeviceClippingRegion(clip)

            stipple = PaneCreateStippleBitmap()
            brush = wx.Brush(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, an instance of :class:`AuiDockUIPart`.
        """

        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.Contains(part.rect)

    def DrawPaneButton(self, dc, part, pt):
        """
        Draws a pane button in the caption (convenience function).

        :param `dc`: a :class:`wx.DC` device context object;
        :param `part`: the UI part to analyze, an instance of :class:`AuiDockUIPart`;
        :param wx.Point `pt`: the mouse location.
        """

        if not self.IsPaneButtonVisible(part):
            return

        state = AUI_BUTTON_STATE_NORMAL

        if part.rect.Contains(pt):

            if wx.GetMouseState().LeftIsDown():
                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, an instance of :class:`AuiDockUIPart`.
        """

        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 :meth:`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 rectangle hint will be returned
        in client coordinates. Otherwise, an empty rectangle is returned.

        :param wx.Window `pane_window`: it is the window pointer of the pane being dragged;
        :param wx.Point `pt`: is the mouse position, in client coordinates;
        :param wx.Point `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 range(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 notebook 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.Rect(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.Rect(p.floating_pos, p.floating_size)

        return rect

    def ShowHintRect(self, rect):
        """
        If rect is not empty, show it by calling :meth:`ShowHint`, otherwise it
        hides any hint rectangle currently shown.

        :param wx.Rect `rect`: the hint rect in client coordinates;
        """

        if rect.IsEmpty():
            self.HideHint()
            self._hint_rect = wx.Rect()
        else:
            # 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()
            self.ShowHint(rect)
            self._hint_rect = wx.Rect(*rect)

    def DrawHintRect(self, pane_window, pt, offset):
        """
        Calculates the hint rectangle by calling :meth:`CalculateHintRect`. If there is a
        rectangle, it shows it by calling :meth:`ShowHint`, otherwise it hides any hint
        rectangle currently shown.

        :param wx.Window `pane_window`: it is the window pointer of the pane being dragged;
        :param wx.Point `pt`: is the mouse position, in client coordinates;
        :param wx.Point `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)
        self.ShowHintRect(rect)

    def GetPartSizerRect(self, uiparts):
        """
        Returns the rectangle surrounding the specified UI parts.

        :param list `uiparts`: list of :class:`AuiDockUIPart` parts.
        """

        rect = wx.Rect()

        for part in self._uiparts:
            if part.pane and part.pane.name == "__HINT__":
                rect.Union(wx.Rect(part.sizer_item.GetPosition(),
                                   part.sizer_item.GetSize()))

        return rect

    def GetAttributes(self, pane):
        """
        Returns all the attributes of a :class:`AuiPaneInfo`.

        :param `pane`: a :class:`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, pane.minimize_target])

        return attrs

    def SetAttributes(self, pane, attrs):
        """
        Sets all the attributes contained in `attrs` to a :class:`AuiPaneInfo`.

        :param `pane`: a :class:`AuiPaneInfo` instance;
        :param list `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]
        pane.minimize_target = attrs[22]

        return pane

    def OnFloatingPaneResized(self, wnd, size):
        """
        Handles the resizing of a floating pane.

        :param wx.Window `wnd`: the window managed by the pane;
        :param wx.Size `size`: 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 wx.Window `wnd`: the window managed by the pane;
        :param `event`: a :class:`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 wx.Window `wnd`: the 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 wx.Window `wnd`: the window managed by the pane;
        :param `eventOrPt`: a :class:`MoveEvent` to be processed or an instance of :class:`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 :class:`AuiPaneInfo` instance;
        :param wx.Point `pane_pos`: the new pane floating position;
        :param wx.Size `pane_size`: the new pane floating size;
        :param bool `toSnap`: a bool variable to check if :meth:`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 :class:`AuiPaneInfo` instance;
        :param wx.Point `wnd_pos`: the main frame position;
        :param wx.Size `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 wx.Window `pane_window`: the window managed by the pane;
        :param wx.Point `start`: the mouse-click position;
        :param wx.Point `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.Cursor(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 :class:`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

        if not self._frame.IsShownOnScreen():
            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 :class:`PaintDC`.
        """

        w, h = self._frame.GetClientSize()

        if w <= 0 or h <= 0:
            return

        # Figure out which dc to use; if one
        # has been specified, use it, otherwise
        # make a client dc
        if dc is None:
            if not self._frame.IsDoubleBuffered():
                client_dc = wx.BufferedDC(wx.ClientDC(self._frame), wx.Size(w, h))
            else:
                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 :meth:`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
        :meth:`OnRender`.

        :param `dc`: a :class:`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 wx.Window `pane_window`: the 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() and  isinstance(paneInfo.window, auibar.AuiToolBar):
                    paneInfo.window.ExitFloating(paneInfo.GetOrientation())

                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 :class:`AuiManager`.

        :param `event`: an instance of :class:`PaintEvent` to be processed.
        """

        dc = wx.PaintDC(self._frame)
        self.Repaint(dc)

    def OnEraseBackground(self, event):
        """
        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`AuiManager`.

        :param `event`: :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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.Cursor(wx.CURSOR_SIZEWE)
                else:
                    cursor = wx.Cursor(wx.CURSOR_SIZENS)

            elif part.type == AuiDockUIPart.typeGripper:
                cursor = wx.Cursor(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, an instance of :class:`AuiDockUIPart`.;
        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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.Rect(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 :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`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 :meth:`DoEndResizeAction` and :meth:`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 :class:`AuiManager`.

        :param `event`: a :class:`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)

        elif self._action == actionDragMovablePane:
            self.OnLeftUp_DragMovablePane(event)

        else:
            event.Skip()

        try:
            if self._frame.HasCapture():
                self._frame.ReleaseMouse()
        except RuntimeError:
            pass

        self._action = actionNone

    def OnMotion(self, event):
        """
        Handles the ``wx.EVT_MOTION`` event for :class:`AuiManager`.

        :param `event`: a :class:`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)

        elif self._action == actionDragMovablePane:
            self.OnMotion_DragMovablePane(event)

        else:
            self.OnMotion_Other(event)

    def OnLeaveWindow(self, event):
        """
        Handles the ``wx.EVT_LEAVE_WINDOW`` event for :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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 :class:`AuiManager`.

        :param `event`: a :class:`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()
        event.Skip()

    def OnChildFocus(self, event):
        """
        Handles the ``wx.EVT_CHILD_FOCUS`` event for :class:`AuiManager`.

        :param `event`: a :class:`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 :meth:`OnMotion` event.

        :param `event`: a :class:`MouseEvent` to be processed.
        """

        clientPt = event.GetPosition()
        screenPt = self._frame.ClientToScreen(clientPt)

        drag_x_threshold = max(4, wx.SystemSettings.GetMetric(wx.SYS_DRAG_X))
        drag_y_threshold = max(4, 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.DoUpdate()

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

        elif self._action_pane.IsMovable():
            self._action = actionDragMovablePane
            self._action_window = self._action_pane.window

    def OnMotion_Resize(self, event):
        """
        Sub-handler for the :meth:`OnMotion` event.

        :param `event`: a :class:`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.Rect(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 :meth:`OnLeftUp` event.

        :param `event`: a :class:`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 :meth:`OnLeftUp` event.

        :param `event`: a :class:`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 :class:`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 :meth:`OnMotion` event.

        :param `event`: a :class:`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
        else:
            diff = wx.Point()

        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) or (self._frame and self._frame.IsIconized()):
            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() or not notebookRoot.IsShown():
                    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

        wx.CallAfter(self.DrawHintRect, pane.window, clientPt, action_offset)

    def OnMotion_DragMovablePane(self, eventOrPt):
        """
        Sub-handler for the :meth:`OnMotion` event.

        :param `event`: a :class:`MouseEvent` to be processed.
        """

        # Try to find the pane.
        pane = self.GetPane(self._action_window)
        if not pane.IsOk():
            raise Exception("Pane window not found")

        if not self.CanDockPanel(pane) or (self._frame and self._frame.IsIconized()):
            self.HideHint()
            ShowDockingGuides(self._guides, False)
            return

        # Draw a hint for where the window will be moved.
        if isinstance(eventOrPt, wx.Point):
            pt = wx.Point(*eventOrPt)
        else:
            pt = eventOrPt.GetPosition()

        self.DrawHintRect(self._action_window, pt, wx.Point(0, 0))

        # Reduces flicker.
        self._frame.Update()

    def OnLeftUp_DragFloatingPane(self, eventOrPt):
        """
        Sub-handler for the :meth:`OnLeftUp` event.

        :param `event`: a :class:`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")

        if self._frame and self._frame.IsIconized():
            # if the main frame is iconized, not try to dock
            self.HideHint()
            ShowDockingGuides(self._guides, False)
            return

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

        if ret:
            # 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)

            self.Update()

            if tempPaneInfo.IsFloating():
                self.SnapPane(tempPaneInfo, tempPaneInfo.floating_pos, tempPaneInfo.floating_size, False)

        self.HideHint()
        ShowDockingGuides(self._guides, False)

    def OnLeftUp_DragMovablePane(self, event):
        """
        Sub-handler for the :meth:`OnLeftUp` event.

        :param `event`: a :class:`MouseEvent` to be processed.
        """

        # Try to find the pane.
        paneInfo = self.GetPane(self._action_window)
        if not paneInfo.IsOk():
            raise Exception("Pane window not found")

        # Hide the hint as it is no longer needed.
        self.HideHint()

        if self._frame and self._frame.IsIconized():
            # if the main frame is iconized, not try to dock
            self.HideHint()
            ShowDockingGuides(self._guides, False)
            return

        # is the pane dockable?
        if self.CanDockPanel(paneInfo):
            # Move the pane to new position.
            pt = event.GetPosition()
            # do the drop calculation
            indx = self._panes.index(paneInfo)
            ret, paneInfo = self.DoDrop(self._docks, self._panes, paneInfo, pt, wx.Point(0, 0))

            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 ret:
                # Update the layout to realize new position and e.g. form notebooks if needed.
                self.Update()

        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
            # Ensure active before doing actual display.
            ret, self._panes = SetActivePane(self._panes, paneInfo.window)

        # Make changes visible to user.
        self.Repaint()

        # Cancel the action and release the mouse.
        self._action = actionNone
        self._frame.ReleaseMouse()
        self._action_window = None

    def OnMotion_DragToolbarPane(self, eventOrPt):
        """
        Sub-handler for the :meth:`OnMotion` event.

        :param `event`: a :class:`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, new_pane = self.DoDrop(self._docks, self._panes, pane, clientPt, self._toolbar_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)

        rect = wx.Rect()
        if ret and not pane.IsFloating():
            # floating the toolbar or move it to the new location
            self._panes[indx] = new_pane
            self.SwitchToolBarOrientation(new_pane)
            wx.CallAfter(self.Update)
        elif ret and wasFloating:
            # show hint rect to dock a toolbar
            if not new_pane.IsFloating() and not (self._frame and self._frame.IsIconized()):
                # it is ready to dock the toolbar
                # calculate the rect if it will be docked there
                docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
                panes[indx] = new_pane
                if new_pane.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]:
                    new_pane.BestSize(new_pane.window.GetVertMinSize())
                else:
                    new_pane.BestSize(new_pane.window.GetHorzMinSize())
                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:
                    if part.pane and part.pane.name == new_pane.name:
                        rect.Union(wx.Rect(part.sizer_item.GetPosition(),
                                   part.sizer_item.GetSize()))
                sizer.Destroy()

        self.ShowHintRect(rect)

        # when release the button out of the window.
        # TODO: a better fix is needed.

        if not wx.GetMouseState().LeftIsDown():
            self._action = actionNone
            self.OnLeftUp_DragToolbarPane(eventOrPt)

    def OnMotion_Other(self, event):
        """
        Sub-handler for the :meth:`OnMotion` event.

        :param `event`: a :class:`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 :meth:`OnLeftUp` event.

        :param `event`: a :class:`MouseEvent` to be processed.
        """

        self.ShowHintRect(wx.Rect())
        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 self._frame and self._frame.IsIconized():
            # if the main frame is iconized, not try to dock
            self.HideHint()
            ShowDockingGuides(self._guides, False)
            return

        if pane.IsFloating() and pane.frame is not None:
            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

            if self.CanDockPanel(pane):
                # do the drop calculation
                ret, new_pane = self.DoDrop(self._docks, self._panes, pane, clientPt, self._toolbar_action_offset)
                if ret and not new_pane.IsFloating():
                    # drop the toolbar
                    indx = self._panes.index(pane)
                    self._panes[indx] = new_pane
                    pane = new_pane

        # 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 range(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 :class:`AuiManager`.

        :param `event`: a :class:`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, mgrUpdate=True):
        """
        Minimizes a pane in a newly and automatically created :class:`~wx.lib.agw.aui.auibar.AuiToolBar`.

        Clicking on the minimize button causes a new :class:`~wx.lib.agw.aui.auibar.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 :class:`AuiPaneInfo` instance for the pane to be minimized;
        :param bool `mgrUpdate`: ``True`` to call :meth:`Update` to realize the new layout,
         ``False`` otherwise.

        .. note::

           The `mgrUpdate` parameter is currently only used while loading perspectives using
           :meth:`LoadPerspective`, as minimized panes were not correctly taken into account before.

        """

        if not paneInfo.IsToolbar():

            if paneInfo.IsMinimized() and mgrUpdate:
                # 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 | AUI_TB_OVERFLOW
            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_TOOLBAR:
                minimize_toolbar_pane = self.GetPane(paneInfo.minimize_target)
                if not minimize_toolbar_pane.IsOk():
                    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
                else:
                    minimize_toolbar = minimize_toolbar_pane.window

            elif 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()

            if posMask != AUI_MINIMIZE_POS_TOOLBAR:
                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 = svg_to_bitmap(restore_svg, win=self._frame)#self._art._restore_bitmap

            if posMask == AUI_MINIMIZE_POS_TOOLBAR:
                xsize, ysize = minimize_toolbar.GetToolBitmapSize()
                if xsize != restore_bitmap.GetLogicalWidth():
                    img = restore_bitmap.ConvertToImage()
                    img.Rescale(xsize, ysize, wx.IMAGE_QUALITY_HIGH)
                    restore_bitmap = img.ConvertToBitmap()

            target = paneInfo.name

            minimize_toolbar.AddSimpleTool(ID_RESTORE_FRAME, paneInfo.caption, restore_bitmap,
                                           _(six.u("Restore %s")) % paneInfo.caption, target=target)
            minimize_toolbar.SetAuiManager(self)
            minimize_toolbar.Realize()
            toolpanelname = paneInfo.name + "_min"

            if paneInfo.IsMaximized():
                paneInfo.SetFlag(paneInfo.wasMaximized, True)

            if posMask != AUI_MINIMIZE_POS_TOOLBAR:
                minimize_toolbar_pane = AuiPaneInfo().\
                        Name(toolpanelname).Caption(paneInfo.caption). \
                        ToolbarPane().BottomDockable(False). \
                        LeftDockable(False).RightDockable(False).DestroyOnClose()
                minimize_toolbar_pane.SetFlag(AuiPaneInfo.minimizeToolbar, True)

                if dockDirection == AUI_DOCK_TOP:
                    minimize_toolbar_pane.Top()
                elif dockDirection == AUI_DOCK_BOTTOM:
                    minimize_toolbar_pane.Bottom()
                elif dockDirection == AUI_DOCK_LEFT:
                    minimize_toolbar_pane.Left()
                elif dockDirection in [AUI_DOCK_RIGHT, AUI_DOCK_CENTER]:
                    minimize_toolbar_pane.Right()

                self.AddPane(minimize_toolbar, minimize_toolbar_pane)

            minimize_toolbar_pane.best_size = minimize_toolbar.GetBestSize()
            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()

            if mgrUpdate:
                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 :class:`AuiManager`.

        :param `event`: an instance of :class:`AuiManagerEvent` to be processed.
        """

        self.RestoreMinimizedPane(event.pane)

    def OnPaneDocked(self, event):
        """
        Handles the ``EVT_AUI_PANE_DOCKED`` event for :class:`AuiManager`.

        :param `event`: an instance of :class:`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 list `panes`: set of panes to append new notebook base pane to
        :param `paneInfo`: the pane to be converted to a new notebook, an instance of
         :class:`AuiPaneInfo`.
        """

        # Create base notebook pane ...
        if self._notebooks:
            nbid = max(self._notebooks.keys()) + 1
        else:
            nbid = 0

        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 :class:`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 isn't 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 :class:`AuiPaneInfo` instance for the pane to be restored.
        """

        panename = paneInfo.name

        hasTarget =  paneInfo.minimize_mode & AUI_MINIMIZE_POS_TOOLBAR
        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 self._has_maximized:
                self.RestoreMaximizedPane()

            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)

            if hasTarget:
                targetName = pane.minimize_target
                toolbarPane = self.GetPane(targetName)
                toolbar = toolbarPane.window
                item = toolbar.FindToolByLabel(pane.caption)
                toolbar.DeleteTool(item.id)
                toolbar.Realize()
                toolbarPane.best_size = toolbar.GetBestSize()
            else:
                minimize_toolbar_pane = self.GetPane(pane.name+'_min')
                if minimize_toolbar_pane.IsOk():
                    minimize_toolbar_pane.window.Show(False)
                    self.DetachPane(minimize_toolbar_pane.window)
                    minimize_toolbar_pane.Show(False)
                    minimize_toolbar_pane.Hide()

            self.Update()

    def AnimateDocking(self, win_rect, pane_rect):
        """
        Animates the minimization/docking of a pane a la Eclipse, using a :class:`ScreenDC`
        to draw a "moving docking rectangle" on the screen.

        :param wx.Rect `win_rect`: the original pane screen rectangle;
        :param wx.Rect `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 :class:`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 range(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.DrawRoundedRectangle(new_rect, 3)
            wx.SafeYield()
            wx.MilliSleep(10)
            dc.DrawRoundedRectangle(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 :class:`AuiPaneInfo`.

        :note: The smooth docking effect can only be used if you set the ``AUI_MGR_SMOOTH_DOCKING``
         style to :class:`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 range(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 :meth:`SnapToScreen`) or when snapping the floating panes to one side of the
        `managed_window` (using :meth:`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 integer `x`: the minimum horizontal distance below which the snap occurs;
        :param integer `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: :meth:`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 bool `snap`: whether to snap the main frame or not;
        :param integer `monitor`: the monitor display in which snapping the window;
        :param integer `hAlign`: the horizontal alignment of the snapping position;
        :param integer `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 :meth:`AnimateDocking`. """

        return self._animation_step

    def SetAnimationStep(self, step):
        """
        Sets the animation step speed (a float) to use in :meth:`AnimateDocking`.

        :param float `step`: the animation speed.
        """

        self._animation_step = float(step)

    def RequestUserAttention(self, pane_window):
        """
        Requests the user attention by intermittently highlighting the pane caption.

        :param wx.Window `pane_window`: the 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 :class:`~wx.lib.agw.aui.auibar.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 :class:`AuiManager`.

        :param `event`: a :class:`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 = max(1, stopX // 10)
        window_size = 0

        for i in range(0, stopX, step):
            window_size = i
            self._sliding_frame.SetSize(startX, startY, window_size, stopY)
            self._sliding_frame.Refresh()
            self._sliding_frame.Update()
            wx.MilliSleep(10)

        self._sliding_frame.SetSize(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 = max(1, stopX // 10)
        window_size = 0
        for i in range(stopX, 0, -step):
            window_size = i
            self._sliding_frame.SetSize(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.SetSize(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 :class:`AuiManager` but with a Dummy Center Pane (**DCP**).
    The code for this class is still flickery due to the call to :func:`CallAfter`
    and the double-update call.
    """

    def __init__(self, *args, **keys):
        """ See :meth:`AuiManager.__init__` for the class construction. """

        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. :meth:`Update` must be invoked after :meth:`AuiManager.AddPane` or
        :meth:`AuiManager.InsertPane` are called in order to "realize" or "commit" the changes.

        In addition, any number of changes may be made to :class:`AuiManager` structures
        (retrieved with :meth:`AuiManager.GetPane`), but to realize the changes,
        :meth:`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, therefore 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:
                def do():
                    self._createDummyPane()
                    self.Update()

                wx.CallAfter(do)
