Mercurial > traipse_dev
diff orpg/tools/PyAUI.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orpg/tools/PyAUI.py Tue Jul 14 16:41:58 2009 -0500 @@ -0,0 +1,4931 @@ +# --------------------------------------------------------------------------- # +# PYAUI 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: 0.9.2 +# +# +# Python Code By: +# +# Andrea Gavana, @ 23 Dec 2005 +# Latest Revision: 30 Jun 2006, 21.00 GMT +# +# +# PyAUI version 0.9.2 Adds: +# +# * Fixes For Display Glitches; +# * Fixes For Other Bugs Found In Previous Versions. +# +# +# TODO List/Caveats +# +# 1. Using The New Versions Of wxPython (2.6.2.1pre.20060106 Or Higher) There +# Is A New Method Called wx.GetMouseState() That Gets Rid Of The Import Of +# win32all or ctypes. Moreover, It Should Make PyAUI Working On All +# Platforms (I Hope). +# +# +# Latest Patches: +# +# 1) Reduced Flicker While Drawing The Dock Hint +# 2) Made Impossoible To Drag The Sash Separator Outside The Main Frame +# 3) Improved Repaint When Using The Active Pane Option +# 4) Fixed The Mac Problem (Thanks To David Pratt) And Applied The wxGTK Patches +# Suggested By Robin Dunn To Correctly Draw The Dock Hint +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To Me At: +# +# andrea.gavana@agip.it +# andrea_gavan@tin.it +# +# Or, Obviously, To The wxPython Mailing List!!! +# +# with OS X support and refactoring implemented by Chris Mellon (arkanes@gmail.com) - +# contact me directly or on wxPython ML for more info +# +# +# End Of Comments +# --------------------------------------------------------------------------- # + +""" +PyAUI is an Advanced User Interface library that aims to implement "cutting-edge" +interface usability and design features so developers can quickly and easily create +beautiful and usable application interfaces. + +Vision and Design Principles + +PyAUI attempts to encapsulate the following aspects of the user interface: + +* Frame Management: Frame management provides the means to open, move and hide common +controls that are needed to interact with the document, and allow these configurations +to be saved into different perspectives and loaded at a later time. + +* Toolbars: Toolbars are a specialized subset of the frame management system and should +behave similarly to other docked components. However, they also require additional +functionality, such as "spring-loaded" rebar support, "chevron" buttons and end-user +customizability. + +* Modeless Controls: Modeless controls expose a tool palette or set of options that +float above the application content while allowing it to be accessed. Usually accessed +by the toolbar, these controls disappear when an option is selected, but may also be +"torn off" the toolbar into a floating frame of their own. + +* Look and Feel: Look and feel encompasses the way controls are drawn, both when shown +statically as well as when they are being moved. This aspect of user interface design +incorporates "special effects" such as transparent window dragging as well as frame animation. + +PyAUI adheres to the following principles: + +- Use native floating frames to obtain a native look and feel for all platforms; +- Use existing wxPython code where possible, such as sizer implementation for frame management; +- Use standard wxPython coding conventions. + + +Usage: + +The following example shows a simple implementation that utilizes AuiManager to manage +three text controls in a frame window: + +class MyFrame(wx.Frame): + + def __init__(self, parent, id=-1, title="PyAUI Test", pos=wx.DefaultPosition, + size=(800, 600), style=wx.DEFAULT_FRAME_STYLE): + + wx.Frame.__init__(self, parent, id, title, pos, size, style) + + self._mgr = PyAUI.AuiManager() + + # notify PyAUI which frame to use + self._mgr.SetFrame(self) + + # create several text controls + text1 = wx.TextCtrl(self, -1, "Pane 1 - sample text", + wx.DefaultPosition, wx.Size(200,150), + wx.NO_BORDER | wx.TE_MULTILINE) + + text2 = wx.TextCtrl(self, -1, "Pane 2 - sample text", + wx.DefaultPosition, wx.Size(200,150), + wx.NO_BORDER | wx.TE_MULTILINE) + + text3 = wx.TextCtrl(self, -1, "Main content window", + wx.DefaultPosition, wx.Size(200,150), + wx.NO_BORDER | wx.TE_MULTILINE) + + # add the panes to the manager + self._mgr.AddPane(text1, wx.LEFT, "Pane Number One") + self._mgr.AddPane(text2, wx.BOTTOM, "Pane Number Two") + self._mgr.AddPane(text3, wx.CENTER) + + # tell the manager to "commit" all the changes just made + self._mgr.Update() + + self.Bind(wx.EVT_CLOSE, self.OnClose) + + + def OnClose(self, event): + + # deinitialize the frame manager + self._mgr.UnInit() + + self.Destroy() + event.Skip() + + +# our normal wxApp-derived class, as usual + +app = wx.PySimpleApp() + +frame = MyFrame(None) +app.SetTopWindow(frame) +frame.Show() + +app.MainLoop() + +What's New: + +PyAUI version 0.9.2 Adds: + +* Fixes For Display Glitches; +* Fixes For Other Bugs Found In Previous Versions. + + +License And Version: + +PyAUI Library Is Freeware And Distributed Under The wxPython License. + +Latest Revision: Andrea Gavana @ 30 Jun 2006, 21.00 GMT +Version 0.9.2. + +""" + +from orpg.orpg_wx import * +import cStringIO, zlib +import time + +_libimported = None +_newversion = False + +# Check For The New wxVersion: It Should Be > 2.6.2.1pre.20060102 +# In Order To Let PyAUI Working On All Platforms + +wxver = wx.VERSION_STRING +if wxver < "2.7": + wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point) + +if hasattr(wx, "GetMouseState"): + _newversion = True + if wx.Platform == "__WXMSW__": + try: + import win32api + import win32con + import winxpgui + _libimported = "MH" + except: + try: + import ctypes + _libimported = "ctypes" + except: + pass + +else: + if wx.Platform == "__WXMSW__": + try: + import win32api + import win32con + import winxpgui + _libimported = "MH" + except: + try: + import ctypes + _libimported = "ctypes" + except: + raise "\nERROR: At Present, On Windows Machines, You Need To Install "\ + "Mark Hammond's pywin32 Extensions Or The ctypes Module, Or Download" \ + "The Latest wxPython Version." + + else: + raise "\nSorry: I Still Don't Know How To Work On GTK/MAC Platforms... " \ + "Please Download The Latest wxPython Version." + + +if wx.Platform == "__WXMAC__": + try: + import ctypes + _carbon_dll = ctypes.cdll.LoadLibrary(r'/System/Frameworks/Carbon.framework/Carbon') + except: + _carbon_dll = None + +# Docking Styles +AUI_DOCK_NONE = 0 +AUI_DOCK_TOP = 1 +AUI_DOCK_RIGHT = 2 +AUI_DOCK_BOTTOM = 3 +AUI_DOCK_LEFT = 4 +AUI_DOCK_CENTER = 5 +AUI_DOCK_CENTRE = AUI_DOCK_CENTER + +# Floating/Dragging Styles +AUI_MGR_ALLOW_FLOATING = 1 +AUI_MGR_ALLOW_ACTIVE_PANE = 2 +AUI_MGR_TRANSPARENT_DRAG = 4 +AUI_MGR_TRANSPARENT_HINT = 8 +AUI_MGR_TRANSPARENT_HINT_FADE = 16 + +AUI_MGR_DEFAULT = AUI_MGR_ALLOW_FLOATING | \ + AUI_MGR_TRANSPARENT_HINT | \ + AUI_MGR_TRANSPARENT_HINT_FADE | \ + AUI_MGR_TRANSPARENT_DRAG + +# Panes Customization +AUI_ART_SASH_SIZE = 0 +AUI_ART_CAPTION_SIZE = 1 +AUI_ART_GRIPPER_SIZE = 2 +AUI_ART_PANE_BORDER_SIZE = 3 +AUI_ART_PANE_BUTTON_SIZE = 4 +AUI_ART_BACKGROUND_COLOUR = 5 +AUI_ART_SASH_COLOUR = 6 +AUI_ART_ACTIVE_CAPTION_COLOUR = 7 +AUI_ART_ACTIVE_CAPTION_GRADIENT_COLOUR = 8 +AUI_ART_INACTIVE_CAPTION_COLOUR = 9 +AUI_ART_INACTIVE_CAPTION_GRADIENT_COLOUR = 10 +AUI_ART_ACTIVE_CAPTION_TEXT_COLOUR = 11 +AUI_ART_INACTIVE_CAPTION_TEXT_COLOUR = 12 +AUI_ART_BORDER_COLOUR = 13 +AUI_ART_GRIPPER_COLOUR = 14 +AUI_ART_CAPTION_FONT = 15 +AUI_ART_GRADIENT_TYPE = 16 + +# Caption Gradient Type +AUI_GRADIENT_NONE = 0 +AUI_GRADIENT_VERTICAL = 1 +AUI_GRADIENT_HORIZONTAL = 2 + +# Pane Button State +AUI_BUTTON_STATE_NORMAL = 0 +AUI_BUTTON_STATE_HOVER = 1 +AUI_BUTTON_STATE_PRESSED = 2 + +# Pane Insert Level +AUI_INSERT_PANE = 0 +AUI_INSERT_ROW = 1 +AUI_INSERT_DOCK = 2 + +# some built in bitmaps +close_bits = '\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00' \ + '\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00' \ + '\xef\x00\x00\x00\xfb\x00\x00\x00\xcf\x00\x00\x00\xf9\x00\x00\x00' \ + '\x9f\x00\x00\x00\xfc\x00\x00\x00?\x00\x00\x00\xfe\x00\x00\x00?\x00' \ + '\x00\x00\xfe\x00\x00\x00\x9f\x00\x00\x00\xfc\x00\x00\x00\xcf\x00' \ + '\x00\x00\xf9\x00\x00\x00\xef\x00\x00\x00\xfb\x00\x00\x00\xff\x00' \ + '\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00' \ + '\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00' + +pin_bits = '\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff' \ + '\x00\x00\x00\xff\x00\x00\x00\x1f\x00\x00\x00\xfc\x00\x00\x00\xdf\x00' \ + '\x00\x00\xfc\x00\x00\x00\xdf\x00\x00\x00\xfc\x00\x00\x00\xdf\x00\x00' \ + '\x00\xfc\x00\x00\x00\xdf\x00\x00\x00\xfc\x00\x00\x00\xdf\x00\x00\x00' \ + '\xfc\x00\x00\x00\x0f\x00\x00\x00\xf8\x00\x00\x00\x7f\x00\x00\x00\xff' \ + '\x00\x00\x00\x7f\x00\x00\x00\xff\x00\x00\x00\x7f\x00\x00\x00\xff\x00' \ + '\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00' \ + '\x00\xff\x00\x00\x00\xff\x00\x00\x00' + +# PyAUI Event +wxEVT_AUI_PANEBUTTON = wx.NewEventType() +EVT_AUI_PANEBUTTON = wx.PyEventBinder(wxEVT_AUI_PANEBUTTON, 0) +wxEVT_AUI_PANECLOSE = wx.NewEventType() +EVT_AUI_PANECLOSE = wx.PyEventBinder(wxEVT_AUI_PANECLOSE, 0) + + +def GetCloseData(): + return zlib.decompress( +'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\x02 \xcc\xc1\ +\x06$\xe5?\xffO\x04R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4Gy\xba8\x86X\xf4\ +\x9e\r:\xcd\xc7\xa0\xc0\xe1\xf5\xfb\xbf\xff\xbb\xc2\xacb6\xdbg\xaez\xb9|\x1c\ +\x9a\x82kU\x99xW\x16K\xf5\xdccS\xdad\xe9\xf3\xe0\xa4\x0f\x0f\xaf\xcb\xea\x88\ +\x8bV\xd7k\x1eoN\xdf\xb2\xdd\xc8\xd0\xe7Cw2{\xdd\\uf\xfd}3\x0f\xb0\xd4=\x0ff\ +\xdfr$\\\xe5\xcf\xa9\xfd3\xfa\xcdu\xa4\x7fk\xa6\x89\x03ma\xf0t\xf5sY\xe7\x94\ +\xd0\x04\x00\x1714z') + + +def GetCloseBitmap(): + return wx.BitmapFromImage(GetCloseImage()) + + +def GetCloseImage(): + stream = cStringIO.StringIO(GetCloseData()) + return wx.ImageFromStream(stream) + + +def StepColour(c, percent): + """ + StepColour() it a utility function that simply darkens + a color by the specified percentage. + """ + + r = c.Red() + g = c.Green() + b = c.Blue() + + return wx.Colour(min((r*percent)/100, 255), + min((g*percent)/100, 255), + min((b*percent)/100, 255)) + + +def LightContrastColour(c): + + amount = 120 + + # if the color is especially dark, then + # make the contrast even lighter + if c.Red() < 128 and c.Green() < 128 and c.Blue() < 128: + amount = 160 + + return StepColour(c, amount) + + +def BitmapFromBits(color, type=0): + """ + BitmapFromBits() is a utility function that creates a + masked bitmap from raw bits (XBM format). + """ + + if type == 0: # Close Bitmap + img = GetCloseImage() + else: + # this should be GetClosePin()... but what the hell is a "pin"? + img = GetCloseImage() + + img.Replace(255, 255, 255, 123, 123, 123) + img.Replace(0, 0, 0, color.Red(), color.Green(), color.Blue()) + + return img.ConvertToBitmap() + + +def DrawGradientRectangle(dc, rect, start_color, end_color, direction): + + rd = end_color.Red() - start_color.Red() + gd = end_color.Green() - start_color.Green() + bd = end_color.Blue() - start_color.Blue() + + if direction == AUI_GRADIENT_VERTICAL: + high = rect.GetHeight() - 1 + else: + high = rect.GetWidth() - 1 + + for ii in xrange(high+1): + r = start_color.Red() + ((ii*rd*100)/high)/100 + g = start_color.Green() + ((ii*gd*100)/high)/100 + b = start_color.Blue() + ((ii*bd*100)/high)/100 + + p = wx.Pen(wx.Colour(r, g, b)) + dc.SetPen(p) + + if direction == AUI_GRADIENT_VERTICAL: + dc.DrawLine(rect.x, rect.y+ii, rect.x+rect.width, rect.y+ii) + else: + dc.DrawLine(rect.x+ii, rect.y, rect.x+ii, rect.y+rect.height) + + +class DockInfo: + + def __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): + + return (self.dock_direction != 0 and [True] or [False])[0] + + + def IsHorizontal(self): + + return ((self.dock_direction == AUI_DOCK_TOP or \ + self.dock_direction == AUI_DOCK_BOTTOM) and \ + [True] or [False])[0] + + + def IsVertical(self): + + return ((self.dock_direction == AUI_DOCK_LEFT or \ + self.dock_direction == AUI_DOCK_RIGHT or \ + self.dock_direction == AUI_DOCK_CENTER) and [True] or [False])[0] + + +class DockUIPart: + + typeCaption = 0 + typeGripper = 1 + typeDock = 2 + typeDockSizer = 3 + typePane = 4 + typePaneSizer = 5 + typeBackground = 6 + typePaneBorder = 7 + typePaneButton = 8 + + def __init__(self): + + self.orientation = wx.VERTICAL + self.type = 0 + self.rect = wx.Rect() + + +class PaneButton: + + def __init__(self, button_id): + + self.button_id = button_id + + +# event declarations/classes + +class AuiManagerEvent(wx.PyCommandEvent): + + def __init__(self, eventType, id=1): + + wx.PyCommandEvent.__init__(self, eventType, id) + + self.pane = None + self.button = 0 + + + def SetPane(self, p): + + self.pane = p + + + def SetButton(self, b): + + self.button = b + + + def GetPane(self): + + return self.pane + + + def GetButton(self): + + return self.button + + +# -- DefaultDockArt class implementation -- +# +# DefaultDockArt is an art provider class which does all of the drawing for +# AuiManager. This allows the library caller to customize the dock art +# (probably by deriving from this class), or to completely replace all drawing +# with custom dock art. The active dock art class can be set via +# AuiManager.SetDockArt() + +class DefaultDockArt: + + def __init__(self): + + base_color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) + darker1_color = StepColour(base_color, 85) + darker2_color = StepColour(base_color, 70) + darker3_color = StepColour(base_color, 60) + darker4_color = StepColour(base_color, 50) + darker5_color = StepColour(base_color, 40) + + self._active_caption_colour = LightContrastColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) + self._active_caption_gradient_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + self._active_caption_text_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) + self._inactive_caption_colour = StepColour(darker1_color, 80) + self._inactive_caption_gradient_colour = darker1_color + self._inactive_caption_text_colour = wx.BLACK + + sash_color = base_color + caption_color = darker1_color + paneborder_color = darker2_color + selectbutton_color = base_color + selectbuttonpen_color = darker3_color + + self._sash_brush = wx.Brush(base_color) + self._background_brush = wx.Brush(base_color) + self._border_pen = wx.Pen(darker2_color) + self._gripper_brush = wx.Brush(base_color) + self._gripper_pen1 = wx.Pen(darker5_color) + self._gripper_pen2 = wx.Pen(darker3_color) + self._gripper_pen3 = wx.WHITE_PEN + + self._caption_font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False) + + self._inactive_close_bitmap = BitmapFromBits(self._inactive_caption_text_colour, 0) + self._inactive_pin_bitmap = BitmapFromBits(self._inactive_caption_text_colour, 1) + self._active_close_bitmap = BitmapFromBits(self._active_caption_text_colour, 0) + self._active_pin_bitmap = BitmapFromBits(self._active_caption_text_colour, 1) + + # default metric values + self._sash_size = 4 + self._caption_size = 17 + self._border_size = 1 + self._button_size = 14 + self._gripper_size = 9 + self._gradient_type = AUI_GRADIENT_VERTICAL + + + def GetMetric(self, id): + + if id == AUI_ART_SASH_SIZE: + return self._sash_size + elif id == AUI_ART_CAPTION_SIZE: + return self._caption_size + elif id == AUI_ART_GRIPPER_SIZE: + return self._gripper_size + elif id == AUI_ART_PANE_BORDER_SIZE: + return self._border_size + elif id == AUI_ART_PANE_BUTTON_SIZE: + return self._button_size + elif id == AUI_ART_GRADIENT_TYPE: + return self._gradient_type + else: + raise "\nERROR: Invalid Metric Ordinal. " + + + def SetMetric(self, id, new_val): + + if id == AUI_ART_SASH_SIZE: + self._sash_size = new_val + elif id == AUI_ART_CAPTION_SIZE: + self._caption_size = new_val + elif id == AUI_ART_GRIPPER_SIZE: + self._gripper_size = new_val + elif id == AUI_ART_PANE_BORDER_SIZE: + self._border_size = new_val + elif id == AUI_ART_PANE_BUTTON_SIZE: + self._button_size = new_val + elif id == AUI_ART_GRADIENT_TYPE: + self._gradient_type = new_val + else: + raise "\nERROR: Invalid Metric Ordinal. " + + + def GetColor(self, id): + + if id == AUI_ART_BACKGROUND_COLOUR: + return self._background_brush.GetColour() + elif id == AUI_ART_SASH_COLOUR: + return self._sash_brush.GetColour() + elif id == AUI_ART_INACTIVE_CAPTION_COLOUR: + return self._inactive_caption_colour + elif id == AUI_ART_INACTIVE_CAPTION_GRADIENT_COLOUR: + return self._inactive_caption_gradient_colour + elif id == AUI_ART_INACTIVE_CAPTION_TEXT_COLOUR: + return self._inactive_caption_text_colour + elif id == AUI_ART_ACTIVE_CAPTION_COLOUR: + return self._active_caption_colour + elif id == AUI_ART_ACTIVE_CAPTION_GRADIENT_COLOUR: + return self._active_caption_gradient_colour + elif id == AUI_ART_ACTIVE_CAPTION_TEXT_COLOUR: + return self._active_caption_text_colour + elif id == AUI_ART_BORDER_COLOUR: + return self._border_pen.GetColour() + elif id == AUI_ART_GRIPPER_COLOUR: + return self._gripper_brush.GetColour() + else: + raise "\nERROR: Invalid Metric Ordinal. " + + + def SetColor(self, id, colour): + + if id == AUI_ART_BACKGROUND_COLOUR: + self._background_brush.SetColour(colour) + elif id == AUI_ART_SASH_COLOUR: + self._sash_brush.SetColour(colour) + elif id == AUI_ART_INACTIVE_CAPTION_COLOUR: + self._inactive_caption_colour = colour + elif id == AUI_ART_INACTIVE_CAPTION_GRADIENT_COLOUR: + self._inactive_caption_gradient_colour = colour + elif id == AUI_ART_INACTIVE_CAPTION_TEXT_COLOUR: + self._inactive_caption_text_colour = colour + elif id == AUI_ART_ACTIVE_CAPTION_COLOUR: + self._active_caption_colour = colour + elif id == AUI_ART_ACTIVE_CAPTION_GRADIENT_COLOUR: + self._active_caption_gradient_colour = colour + elif id == AUI_ART_ACTIVE_CAPTION_TEXT_COLOUR: + self._active_caption_text_colour = colour + elif id == AUI_ART_BORDER_COLOUR: + self._border_pen.SetColour(colour) + elif id == AUI_ART_GRIPPER_COLOUR: + self._gripper_brush.SetColour(colour) + self._gripper_pen1.SetColour(StepColour(colour, 40)) + self._gripper_pen2.SetColour(StepColour(colour, 60)) + else: + raise "\nERROR: Invalid Metric Ordinal. " + + + GetColour = GetColor + SetColour = SetColor + + def SetFont(self, id, font): + + if id == AUI_ART_CAPTION_FONT: + self._caption_font = font + + + def GetFont(self, id): + + if id == AUI_ART_CAPTION_FONT: + return self._caption_font + + return wx.NoneFont + + + def DrawSash(self, dc, orient, rect): + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetBrush(self._sash_brush) + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + + def DrawBackground(self, dc, orient, rect): + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetBrush(self._background_brush) + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + + def DrawBorder(self, dc, rect, pane): + + drect = wx.Rect() + drect.x = rect.x + drect.y = rect.y + drect.width = rect.width + drect.height = rect.height + + dc.SetPen(self._border_pen) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + + border_width = self.GetMetric(AUI_ART_PANE_BORDER_SIZE) + + if pane.IsToolbar(): + + for ii in xrange(0, border_width): + + dc.SetPen(wx.WHITE_PEN) + dc.DrawLine(drect.x, drect.y, drect.x+drect.width, drect.y) + dc.DrawLine(drect.x, drect.y, drect.x, drect.y+drect.height) + dc.SetPen(self._border_pen) + dc.DrawLine(drect.x, drect.y+drect.height-1, + drect.x+drect.width, drect.y+drect.height-1) + dc.DrawLine(drect.x+drect.width-1, drect.y, + drect.x+drect.width-1, drect.y+drect.height) + drect.Deflate(1, 1) + + else: + + for ii in xrange(0, border_width): + + dc.DrawRectangle(drect.x, drect.y, drect.width, drect.height) + drect.Deflate(1, 1) + + + def DrawCaptionBackground(self, dc, rect, active): + + if self._gradient_type == AUI_GRADIENT_NONE: + if active: + dc.SetBrush(wx.Brush(self._active_caption_colour)) + else: + dc.SetBrush(wx.Brush(self._inactive_caption_colour)) + + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + else: + if active: + DrawGradientRectangle(dc, rect, self._active_caption_colour, + self._active_caption_gradient_colour, + self._gradient_type) + else: + DrawGradientRectangle(dc, rect, self._inactive_caption_colour, + self._inactive_caption_gradient_colour, + self._gradient_type) + + + def DrawCaption(self, dc, text, rect, pane): + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetFont(self._caption_font) + + self.DrawCaptionBackground(dc, rect, ((pane.state & AuiPaneInfo.optionActive) and \ + [True] or [False])[0]) + + if pane.state & AuiPaneInfo.optionActive: + dc.SetTextForeground(self._active_caption_text_colour) + else: + dc.SetTextForeground(self._inactive_caption_text_colour) + + w, h = dc.GetTextExtent("ABCDEFHXfgkj") + + dc.SetClippingRegion(rect.x, rect.y, rect.width, rect.height) + dc.DrawText(text, rect.x+3, rect.y+(rect.height/2)-(h/2)-1) + dc.DestroyClippingRegion() + + + def DrawGripper(self, dc, rect, pane): + + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetBrush(self._gripper_brush) + + dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) + + if not pane.HasGripperTop(): + y = 5 + while 1: + dc.SetPen(self._gripper_pen1) + dc.DrawPoint(rect.x+3, rect.y+y) + dc.SetPen(self._gripper_pen2) + dc.DrawPoint(rect.x+3, rect.y+y+1) + dc.DrawPoint(rect.x+4, rect.y+y) + dc.SetPen(self._gripper_pen3) + dc.DrawPoint(rect.x+5, rect.y+y+1) + dc.DrawPoint(rect.x+5, rect.y+y+2) + dc.DrawPoint(rect.x+4, rect.y+y+2) + y = y + 4 + if y > rect.GetHeight() - 5: + break + else: + x = 5 + while 1: + dc.SetPen(self._gripper_pen1) + dc.DrawPoint(rect.x+x, rect.y+3) + dc.SetPen(self._gripper_pen2) + dc.DrawPoint(rect.x+x+1, rect.y+3) + dc.DrawPoint(rect.x+x, rect.y+4) + dc.SetPen(self._gripper_pen3) + dc.DrawPoint(rect.x+x+1, rect.y+5) + dc.DrawPoint(rect.x+x+2, rect.y+5) + dc.DrawPoint(rect.x+x+2, rect.y+4) + x = x + 4 + if x > rect.GetWidth() - 5: + break + + + def DrawPaneButton(self, dc, button, button_state, rect, pane): + + drect = wx.Rect() + drect.x = rect.x + drect.y = rect.y + drect.width = rect.width + drect.height = rect.height + + if button_state == AUI_BUTTON_STATE_PRESSED: + + drect.x = drect.x + 1 + drect.y = drect.y + 1 + + if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]: + + if pane.state & AuiPaneInfo.optionActive: + dc.SetBrush(wx.Brush(StepColour(self._active_caption_colour, 120))) + dc.SetPen(wx.Pen(StepColour(self._active_caption_colour, 70))) + else: + dc.SetBrush(wx.Brush(StepColour(self._inactive_caption_colour, 120))) + dc.SetPen(wx.Pen(StepColour(self._inactive_caption_colour, 70))) + + # draw the background behind the button + dc.DrawRectangle(drect.x, drect.y, 15, 15) + + if button == AuiPaneInfo.buttonClose: + if pane.state & AuiPaneInfo.optionActive: + + bmp = self._active_close_bitmap + + else: + bmp = self._inactive_close_bitmap + elif button == AuiPaneInfo.buttonPin: + if pane.state & AuiPaneInfo.optionActive: + + bmp = self._active_pin_bitmap + + else: + bmp = self._inactive_pin_bitmap + + # draw the button itself + dc.DrawBitmap(bmp, drect.x, drect.y, True) + + +# -- AuiPaneInfo class implementation -- +# +# 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. + +class AuiPaneInfo: + + 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 + + buttonClose = 2**24 + buttonMaximize = 2**25 + buttonMinimize = 2**26 + buttonPin = 2**27 + buttonCustom1 = 2**28 + buttonCustom2 = 2**29 + buttonCustom3 = 2**30 + actionPane = 2**31 # used internally + + def __init__(self): + + wx.DefaultSize = wx.Size(-1, -1) + 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.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.rect = wx.Rect() + + self.DefaultPane() + + + def IsOk(self): + """ IsOk() returns True if the AuiPaneInfo structure is valid. """ + + return (self.window != None and [True] or [False])[0] + + + def IsFixed(self): + """ IsFixed() returns True if the pane cannot be resized. """ + + return not self.HasFlag(self.optionResizable) + + + def IsResizable(self): + """ IsResizeable() returns True if the pane can be resized. """ + + return self.HasFlag(self.optionResizable) + + + def IsShown(self): + """ IsShown() returns True if the pane should be drawn on the screen. """ + + return not self.HasFlag(self.optionHidden) + + + def IsFloating(self): + """ IsFloating() returns True if the pane is floating. """ + + return self.HasFlag(self.optionFloating) + + + def IsDocked(self): + """ IsDocked() returns True if the pane is docked. """ + + return not self.HasFlag(self.optionFloating) + + + def IsToolbar(self): + """ IsToolbar() returns True if the pane contains a toolbar. """ + + return self.HasFlag(self.optionToolbar) + + + def IsTopDockable(self): + """ + IsTopDockable() returns True if the pane can be docked at the top + of the managed frame. + """ + + return self.HasFlag(self.optionTopDockable) + + + def IsBottomDockable(self): + """ + IsBottomDockable() returns True if the pane can be docked at the bottom + of the managed frame. + """ + + return self.HasFlag(self.optionBottomDockable) + + + def IsLeftDockable(self): + """ + IsLeftDockable() returns True if the pane can be docked at the left + of the managed frame. + """ + + return self.HasFlag(self.optionLeftDockable) + + + def IsRightDockable(self): + """ + IsRightDockable() returns True if the pane can be docked at the right + of the managed frame. + """ + + return self.HasFlag(self.optionRightDockable) + + + def IsDockable(self): + """ IsDockable() returns True if the pane can be docked. """ + + return self.IsTopDockable() or self.IsBottomDockable() or self.IsLeftDockable() or \ + self.IsRightDockable() + + + def IsFloatable(self): + """ + IsFloatable() returns True if the pane can be undocked and displayed as a + floating window. + """ + + return self.HasFlag(self.optionFloatable) + + + def IsMovable(self): + """ + IsMoveable() returns True if the docked frame can be undocked or moved to + another dock position. + """ + + return self.HasFlag(self.optionMovable) + + + def HasCaption(self): + """ HasCaption() returns True if the pane displays a caption. """ + + return self.HasFlag(self.optionCaption) + + + def HasGripper(self): + """ HasGripper() returns True if the pane displays a gripper. """ + + return self.HasFlag(self.optionGripper) + + + def HasBorder(self): + """ HasBorder() returns True if the pane displays a border. """ + + return self.HasFlag(self.optionPaneBorder) + + + def HasCloseButton(self): + """ + HasCloseButton() returns True if the pane displays a button to close + the pane. + """ + + return self.HasFlag(self.buttonClose) + + + def HasMaximizeButton(self): + """ + HasMaximizeButton() returns True if the pane displays a button to + maximize the pane. + """ + + return self.HasFlag(self.buttonMaximize) + + + def HasMinimizeButton(self): + """ + HasMinimizeButton() returns True if the pane displays a button to + minimize the pane. + """ + + return self.HasFlag(self.buttonMinimize) + + + def HasPinButton(self): + """ HasPinButton() returns True if the pane displays a button to float the pane. """ + + return self.HasFlag(self.buttonPin) + + + def HasGripperTop(self): + + return self.HasFlag(self.optionGripperTop) + + + def Window(self, w): + + self.window = w + return self + + + def Name(self, n): + """ Name() sets the name of the pane so it can be referenced in lookup functions. """ + + self.name = n + return self + + + def Caption(self, c): + """ Caption() sets the caption of the pane. """ + + self.caption = c + return self + + + def Left(self): + """ Left() sets the pane dock position to the left side of the frame. """ + + self.dock_direction = AUI_DOCK_LEFT + return self + + + def Right(self): + """ Right() sets the pane dock position to the right side of the frame. """ + + self.dock_direction = AUI_DOCK_RIGHT + return self + + + def Top(self): + """ Top() sets the pane dock position to the top of the frame. """ + + self.dock_direction = AUI_DOCK_TOP + return self + + + def Bottom(self): + """ Bottom() sets the pane dock position to the bottom of the frame. """ + + self.dock_direction = AUI_DOCK_BOTTOM + return self + + + def Center(self): + """ Center() sets the pane to the center position of the frame. """ + + self.dock_direction = AUI_DOCK_CENTER + return self + + + def Centre(self): + """ Centre() sets the pane to the center position of the frame. """ + + self.dock_direction = AUI_DOCK_CENTRE + return self + + + def Direction(self, direction): + """ Direction() determines the direction of the docked pane. """ + + self.dock_direction = direction + return self + + + def Layer(self, layer): + """ Layer() determines the layer of the docked pane. """ + + self.dock_layer = layer + return self + + + def Row(self, row): + """ Row() determines the row of the docked pane. """ + + self.dock_row = row + return self + + + def Position(self, pos): + """ Position() determines the position of the docked pane. """ + + self.dock_pos = pos + return self + + + def MinSize(self, arg1=None, arg2=None): + """ MinSize() sets the minimum size of the pane. """ + + if isinstance(arg1, wx.Size): + ret = self.MinSize1(arg1) + else: + ret = self.MinSize2(arg1, arg2) + + return ret + + + def MinSize1(self, size): + + self.min_size = size + return self + + + def MinSize2(self, x, y): + + self.min_size.Set(x,y) + return self + + + def MaxSize(self, arg1=None, arg2=None): + """ MaxSize() sets the maximum size of the pane. """ + + if isinstance(arg1, wx.Size): + ret = self.MaxSize1(arg1) + else: + ret = self.MaxSize2(arg1, arg2) + + return ret + + + def MaxSize1(self, size): + + self.max_size = size + return self + + + def MaxSize2(self, x, y): + + self.max_size.Set(x,y) + return self + + + def BestSize(self, arg1=None, arg2=None): + """ BestSize() sets the ideal size for the pane. """ + + if isinstance(arg1, wx.Size): + ret = self.BestSize1(arg1) + else: + ret = self.BestSize2(arg1, arg2) + + return ret + + + def BestSize1(self, size): + + self.best_size = size + return self + + + def BestSize2(self, x, y): + + self.best_size.Set(x,y) + return self + + + def FloatingPosition(self, pos): + """ FloatingPosition() sets the position of the floating pane. """ + + self.floating_pos = pos + return self + + + def FloatingSize(self, size): + """ FloatingSize() sets the size of the floating pane. """ + + self.floating_size = size + return self + + + def Fixed(self): + """ Fixed() forces a pane to be fixed size so that it cannot be resized. """ + + return self.SetFlag(self.optionResizable, False) + + + def Resizable(self, resizable=True): + """ + Resizable() allows a pane to be resizable if resizable is True, and forces + it to be a fixed size if resizeable is False. + """ + + return self.SetFlag(self.optionResizable, resizable) + + + def Dock(self): + """ Dock() indicates that a pane should be docked. """ + + return self.SetFlag(self.optionFloating, False) + + + def Float(self): + """ Float() indicates that a pane should be floated. """ + + return self.SetFlag(self.optionFloating, True) + + + def Hide(self): + """ Hide() indicates that a pane should be hidden. """ + + return self.SetFlag(self.optionHidden, True) + + + def Show(self, show=True): + """ Show() indicates that a pane should be shown. """ + + return self.SetFlag(self.optionHidden, not show) + + + def CaptionVisible(self, visible=True): + """ CaptionVisible() indicates that a pane caption should be visible. """ + + return self.SetFlag(self.optionCaption, visible) + + + def PaneBorder(self, visible=True): + """ PaneBorder() indicates that a border should be drawn for the pane. """ + + return self.SetFlag(self.optionPaneBorder, visible) + + + def Gripper(self, visible=True): + """ Gripper() indicates that a gripper should be drawn for the pane. """ + + return self.SetFlag(self.optionGripper, visible) + + + def GripperTop(self, attop=True): + """ GripperTop() indicates that a gripper should be drawn for the pane. """ + + return self.SetFlag(self.optionGripperTop, attop) + + + def CloseButton(self, visible=True): + """ CloseButton() indicates that a close button should be drawn for the pane. """ + + return self.SetFlag(self.buttonClose, visible) + + + def MaximizeButton(self, visible=True): + """ MaximizeButton() indicates that a maximize button should be drawn for the pane. """ + + return self.SetFlag(self.buttonMaximize, visible) + + + def MinimizeButton(self, visible=True): + """ MinimizeButton() indicates that a minimize button should be drawn for the pane. """ + + return self.SetFlag(self.buttonMinimize, visible) + + + def PinButton(self, visible=True): + """ PinButton() indicates that a pin button should be drawn for the pane. """ + + return self.SetFlag(self.buttonPin, visible) + + + def DestroyOnClose(self, b=True): + """ DestroyOnClose() indicates whether a pane should be destroyed when it is closed. """ + + return self.SetFlag(self.optionDestroyOnClose, b) + + + def TopDockable(self, b=True): + """ TopDockable() indicates whether a pane can be docked at the top of the frame. """ + + return self.SetFlag(self.optionTopDockable, b) + + + def BottomDockable(self, b=True): + """ BottomDockable() indicates whether a pane can be docked at the bottom of the frame. """ + + return self.SetFlag(self.optionBottomDockable, b) + + + def LeftDockable(self, b=True): + """ LeftDockable() indicates whether a pane can be docked on the left of the frame. """ + + return self.SetFlag(self.optionLeftDockable, b) + + + def RightDockable(self, b=True): + """ RightDockable() indicates whether a pane can be docked on the right of the frame. """ + + return self.SetFlag(self.optionRightDockable, b) + + + def Floatable(self, b=True): + """ Floatable() indicates whether a frame can be floated. """ + + return self.SetFlag(self.optionFloatable, b) + + + def Movable(self, b=True): + """ Movable() indicates whether a frame can be moved. """ + + return self.SetFlag(self.optionMovable, b) + + + def Dockable(self, b=True): + + return self.TopDockable(b).BottomDockable(b).LeftDockable(b).RightDockable(b) + + + def DefaultPane(self): + """ DefaultPane() specifies that the pane should adopt the default pane settings. """ + + state = self.state + state |= self.optionTopDockable | self.optionBottomDockable | \ + self.optionLeftDockable | self.optionRightDockable | \ + self.optionFloatable | self.optionMovable | self.optionResizable | \ + self.optionCaption | self.optionPaneBorder | self.buttonClose + + self.state = state + + return self + + + def CentrePane(self): + """ CentrePane() specifies that the pane should adopt the default center pane settings. """ + + return self.CenterPane() + + + def CenterPane(self): + """ CenterPane() specifies that the pane should adopt the default center pane settings. """ + + self.state = 0 + return self.Center().PaneBorder().Resizable() + + + def ToolbarPane(self): + """ ToolbarPane() 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) + + if self.dock_layer == 0: + self.dock_layer = 10 + + self.state = state + + return self + + + def SetFlag(self, flag, option_state): + """ SetFlag() turns the property given by flag on or off with the option_state parameter. """ + + state = self.state + + if option_state: + state |= flag + else: + state &= ~flag + + self.state = state + + return self + + + def HasFlag(self, flag): + """ HasFlag() returns True if the the property specified by flag is active for the pane. """ + + return (self.state & flag and [True] or [False])[0] + + +NoneAuiPaneInfo = AuiPaneInfo() + +# -- AuiFloatingPane class implementation -- +# +# AuiFloatingPane implements a frame class with some special functionality +# which allows the library to sense when the frame move starts, is active, +# and completes. Note that it contains it's own AuiManager instance, +# which, in the future, would allow for nested managed frames. +# For now, with wxMSW, the wx.MiniFrame window is used, but on wxGTK, wx.Frame + +if wx.Platform == "__WXGTK__": + + class AuiFloatingPaneBaseClass(wx.Frame): + def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=0): + wx.Frame.__init__(self, parent, id, title, pos, size, style) + +else: + + class AuiFloatingPaneBaseClass(wx.MiniFrame): + def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=0): + wx.MiniFrame.__init__(self, parent, id, title, pos, size, style) + if wx.Platform == "__WXMAC__": + self.MacSetMetalAppearance(True) + + +class AuiFloatingPane(AuiFloatingPaneBaseClass): + + def __init__(self, parent, owner_mgr, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | + wx.CLOSE_BOX | wx.FRAME_NO_TASKBAR | + wx.FRAME_FLOAT_ON_PARENT | wx.CLIP_CHILDREN, + resizeborder=0): + + if not resizeborder: + style = style & ~wx.RESIZE_BORDER + + AuiFloatingPaneBaseClass.__init__(self, parent, id, title, pos, size, style) + self._owner_mgr = owner_mgr + self._moving = False + self._last_rect = wx.Rect() + self._mgr = AuiManager(None) + self._mgr.SetFrame(self) + self._mousedown = False + self.SetExtraStyle(wx.WS_EX_PROCESS_IDLE) + + self.Bind(wx.EVT_CLOSE, self.OnClose) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_MOVE, self.OnMoveEvent) + self.Bind(wx.EVT_MOVING, self.OnMoveEvent) + self.Bind(wx.EVT_IDLE, self.OnIdle) + self.Bind(wx.EVT_ACTIVATE, self.OnActivate) + + + def CopyAttributes(self, pane, contained_pane): + + 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 = pane.best_size + contained_pane.min_size = pane.min_size + contained_pane.max_size = pane.max_size + contained_pane.floating_pos = pane.floating_pos + contained_pane.floating_size = pane.floating_size + contained_pane.dock_proportion = pane.dock_proportion + contained_pane.buttons = pane.buttons + contained_pane.rect = pane.rect + + return contained_pane + + + def SetPaneWindow(self, pane): + + self._pane_window = pane.window + self._pane_window.Reparent(self) + + contained_pane = AuiPaneInfo() + + contained_pane = self.CopyAttributes(pane, contained_pane) + + contained_pane.Dock().Center().Show(). \ + CaptionVisible(False). \ + PaneBorder(False). \ + Layer(0).Row(0).Position(0) + + indx = self._owner_mgr._panes.index(pane) + self._owner_mgr._panes[indx] = pane + + self._mgr.AddPane(self._pane_window, contained_pane) + self._mgr.Update() + + if pane.min_size.IsFullySpecified(): + tmp = self.GetSize() + self.GetSizer().SetSizeHints(self) + self.SetSize(tmp) + + self.SetTitle(pane.caption) + + if pane.floating_size != wx.DefaultSize: + self.SetSize(pane.floating_size) + self._owner_mgr._panes[indx] = pane + else: + size = pane.best_size + if size == wx.DefaultSize: + size = pane.min_size + if size == wx.DefaultSize: + size = self._pane_window.GetSize() + if pane.HasGripper(): + if pane.HasGripperTop(): + size.y += self._owner_mgr._art.GetMetric(AUI_ART_GRIPPER_SIZE) + else: + size.x += self._owner_mgr._art.GetMetric(AUI_ART_GRIPPER_SIZE) + + pane.floating_size = size + self._owner_mgr._panes[indx] = pane + self.SetClientSize(size) + + + def OnSize(self, event): + + self._owner_mgr.OnAuiFloatingPaneResized(self._pane_window, event.GetSize()) + + + def OnClose(self, event): + self._owner_mgr.OnAuiFloatingPaneClosed(self._pane_window, event) + if event.GetSkipped(): + self.Destroy() + self._mgr.UnInit() + + + def OnMoveEvent(self, event): + + win_rect = self.GetRect() + + # skip the first move event + if self._last_rect.IsEmpty(): + self._last_rect = win_rect + return + + # prevent frame redocking during resize + if self._last_rect.GetSize() != win_rect.GetSize(): + self._last_rect = win_rect + return + + self._last_rect = win_rect + + if not self.IsMouseDown(): + return + + if not self._moving: + self.OnMoveStart() + self._moving = True + + self.OnMoving(event.GetRect()) + + + def IsMouseDown(self): + + if _newversion: + ms = wx.GetMouseState() + return ms.leftDown + else: + if wx.Platform == "__WXMSW__": + if _libimported == "MH": + return ((win32api.GetKeyState(win32con.VK_LBUTTON) & (1<<15))\ + and [True] or [False])[0] + elif _libimported == "ctypes": + return ((ctypes.windll.user32.GetKeyState(1) & (1<<15)) and \ + [True] or [False])[0] + + + def OnIdle(self, event): + + if self._moving: + if not self.IsMouseDown(): + self._moving = False + self.OnMoveFinished() + else: + event.RequestMore() + + event.Skip() + + + def OnMoveStart(self): + + # notify the owner manager that the pane has started to move + self._owner_mgr.OnAuiFloatingPaneMoveStart(self._pane_window) + + + def OnMoving(self, window_rect): + + # notify the owner manager that the pane is moving + self._owner_mgr.OnAuiFloatingPaneMoving(self._pane_window) + + + def OnMoveFinished(self): + + # notify the owner manager that the pane has finished moving + self._owner_mgr.OnAuiFloatingPaneMoved(self._pane_window) + + + def OnActivate(self, event): + + if event.GetActive(): + self._owner_mgr.OnAuiFloatingPaneActivated(self._pane_window) + + +# -- static utility functions -- + +def PaneCreateStippleBitmap(): + + data = [0, 0, 0, 192, 192, 192, 192, 192, 192, 0, 0, 0] + img = wx.EmptyImage(2, 2) + counter = 0 + + for ii in xrange(2): + for jj in xrange(2): + img.SetRGB(ii, jj, data[counter], data[counter+1], data[counter+2]) + counter = counter + 3 + + return img.ConvertToBitmap() + + +def DrawResizeHint(dc, rect): + + stipple = PaneCreateStippleBitmap() + brush = wx.BrushFromBitmap(stipple) + dc.SetBrush(brush) + dc.SetPen(wx.TRANSPARENT_PEN) + + dc.SetLogicalFunction(wx.XOR) + dc.DrawRectangleRect(rect) + + +def CopyDocksAndPanes(src_docks, src_panes): + """ + CopyDocksAndPanes() - this utility function creates shallow copies of + the dock and pane info. DockInfo's usually contain pointers + to AuiPaneInfo classes, thus this function is necessary to reliably + reconstruct that relationship in the new dock info and pane info arrays. + """ + + dest_docks = src_docks + dest_panes = src_panes + + for ii in xrange(len(dest_docks)): + dock = dest_docks[ii] + for jj in xrange(len(dock.panes)): + for kk in xrange(len(src_panes)): + if dock.panes[jj] == src_panes[kk]: + dock.panes[jj] = dest_panes[kk] + + return dest_docks, dest_panes + + +def CopyDocksAndPanes2(src_docks, src_panes): + """ + CopyDocksAndPanes2() - this utility function creates full copies of + the dock and pane info. DockInfo's usually contain pointers + to AuiPaneInfo classes, thus this function is necessary to reliably + reconstruct that relationship in the new dock info and pane info arrays. + """ + + dest_docks = [] + + for ii in xrange(len(src_docks)): + dest_docks.append(DockInfo()) + 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 = src_docks[ii].rect + + dest_panes = [] + + for ii in xrange(len(src_panes)): + dest_panes.append(AuiPaneInfo()) + dest_panes[ii].name = src_panes[ii].name + dest_panes[ii].caption = src_panes[ii].caption + dest_panes[ii].window = src_panes[ii].window + dest_panes[ii].frame = src_panes[ii].frame + dest_panes[ii].state = src_panes[ii].state + dest_panes[ii].dock_direction = src_panes[ii].dock_direction + dest_panes[ii].dock_layer = src_panes[ii].dock_layer + dest_panes[ii].dock_row = src_panes[ii].dock_row + dest_panes[ii].dock_pos = src_panes[ii].dock_pos + dest_panes[ii].best_size = src_panes[ii].best_size + dest_panes[ii].min_size = src_panes[ii].min_size + dest_panes[ii].max_size = src_panes[ii].max_size + dest_panes[ii].floating_pos = src_panes[ii].floating_pos + dest_panes[ii].floating_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 = src_panes[ii].rect + + for ii in xrange(len(dest_docks)): + dock = dest_docks[ii] + for jj in xrange(len(dock.panes)): + for kk in xrange(len(src_panes)): + if dock.panes[jj] == src_panes[kk]: + dock.panes[jj] = dest_panes[kk] + + dest_docks[ii] = dock + + return dest_docks, dest_panes + + +def GetMaxLayer(docks, dock_direction): + """ + GetMaxLayer() is an internal function which returns + the highest layer inside the specified dock. + """ + + 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, direction, layer): + """ + GetMaxRow() is an internal function which returns + the highest layer inside the specified dock. + """ + + max_row = 0 + + for pane in panes: + if pane.dock_direction == direction and pane.dock_layer == layer and \ + pane.dock_row > max_row: + max_row = pane.dock_row + + return max_row + + +def DoInsertDockLayer(panes, dock_direction, dock_layer): + """ + DoInsertDockLayer() is an internal function that inserts a new dock + layer by incrementing all existing dock layer values by one. + """ + + for ii in xrange(len(panes)): + pane = panes[ii] + if not pane.IsFloating() and pane.dock_direction == dock_direction and pane.dock_layer >= dock_layer: + pane.dock_layer = pane.dock_layer + 1 + + panes[ii] = pane + + return panes + + +def DoInsertDockRow(panes, dock_direction, dock_layer, dock_row): + """ + DoInsertDockRow() is an internal function that inserts a new dock + row by incrementing all existing dock row values by one. + """ + + for ii in xrange(len(panes)): + pane = panes[ii] + if not pane.IsFloating() and pane.dock_direction == dock_direction and \ + pane.dock_layer == dock_layer and pane.dock_row >= dock_row: + pane.dock_row = pane.dock_row + 1 + + panes[ii] = pane + + return panes + + +def DoInsertPane(panes, dock_direction, dock_layer, dock_row, dock_pos): + + for ii in xrange(len(panes)): + pane = panes[ii] + if not pane.IsFloating() and pane.dock_direction == dock_direction and \ + pane.dock_layer == dock_layer and pane.dock_row == dock_row and \ + pane.dock_pos >= dock_pos: + pane.dock_pos = pane.dock_pos + 1 + + panes[ii] = pane + + return panes + + +def FindDocks(docks, dock_direction, dock_layer=-1, dock_row=-1, arr=[]): + """ + FindDocks() 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). + """ + + begin_layer = dock_layer + end_layer = dock_layer + begin_row = dock_row + end_row = dock_row + dock_count = len(docks) + max_row = 0 + max_layer = 0 + + # discover the maximum dock layer and the max row + for ii in xrange(dock_count): + max_row = max(max_row, docks[ii].dock_row) + max_layer = max(max_layer, docks[ii].dock_layer) + + # if no dock layer was specified, search all dock layers + if dock_layer == -1: + begin_layer = 0 + end_layer = max_layer + + # if no dock row was specified, search all dock row + if dock_row == -1: + begin_row = 0 + end_row = max_row + + arr = [] + + for layer in xrange(begin_layer, end_layer+1): + for row in xrange(begin_row, end_row+1): + for ii in xrange(dock_count): + d = docks[ii] + if dock_direction == -1 or dock_direction == d.dock_direction: + if d.dock_layer == layer and d.dock_row == row: + arr.append(d) + + return arr + + +def FindPaneInDock(dock, window): + """ + FindPaneInDock() looks up a specified window pointer inside a dock. + If found, the corresponding AuiPaneInfo pointer is returned, otherwise None. + """ + + for p in dock.panes: + if p.window == window: + return p + + return None + + +def RemovePaneFromDocks(docks, pane, exc=None): + """ + RemovePaneFromDocks() removes a pane window from all docks + with a possible exception specified by parameter "except". + """ + + for ii in xrange(len(docks)): + d = docks[ii] + if d == exc: + continue + pi = FindPaneInDock(d, pane.window) + if pi: + d.panes.remove(pi) + + docks[ii] = d + + return docks + + +def RenumberDockRows(docks): + """ + RenumberDockRows() 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. + """ + + for ii in xrange(len(docks)): + dock = docks[ii] + dock.dock_row = ii + for jj in xrange(len(dock.panes)): + dock.panes[jj].dock_row = ii + + docks[ii] = dock + + return docks + + +def SetActivePane(panes, active_pane): + + for ii in xrange(len(panes)): + pane = panes[ii] + pane.state &= ~AuiPaneInfo.optionActive + + if pane.window == active_pane: + pane.state |= AuiPaneInfo.optionActive + + panes[ii] = pane + + return panes + + +def PaneSortFunc(p1, p2): + """ This function is used to sort panes by dock position. """ + + return (p1.dock_pos < p2.dock_pos and [-1] or [1])[0] + + +def EscapeDelimiters(s): + """ + EscapeDelimiters() changes "" into "\" and "|" into "\|" + in the input string. This is an internal functions which is + used for saving perspectives. + """ + + result = s.replace(";", "\\") + result = result.replace("|", "|\\") + + return result + + +actionNone = 0 +actionResize = 1 +actionClickButton = 2 +actionClickCaption = 3 +actionDragToolbarPane = 4 +actionDragAuiFloatingPane = 5 + +auiInsertRowPixels = 10 +auiNewRowPixels = 40 +auiLayerInsertPixels = 40 +auiLayerInsertOffset = 5 + +# -- AuiManager class implementation -- +# +# AuiManager manages the panes associated with it for a particular wx.Frame, +# using a pane's AuiPaneInfo information to determine each pane's docking and +# floating behavior. 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. +# +# 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 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, wx.LEFT, "Pane Caption") +# self._mgr.AddPane(text2, wx.BOTTOM, "Pane Caption") +# 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 PyAUI, 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 to 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 PyAUI sample (PyAUIDemo.py). + +class AuiManager(wx.EvtHandler): + + def __init__(self, frame=None, flags=None): + """ + Default Class Constructor. frame specifies the wx.Frame which should be managed. + flags specifies options which allow the frame management behavior to be modified. + """ + + wx.EvtHandler.__init__(self) + self._action = actionNone + self._last_mouse_move = wx.Point() + self._hover_button = None + self._art = DefaultDockArt() + self._hint_wnd = None + self._action_window = None + self._last_hint = wx.Rect() + self._hint_fadetimer = wx.Timer(self, wx.NewId()) + self._hintshown = False + + if flags is None: + flags = AUI_MGR_DEFAULT + + self._flags = flags + self._active_pane = None + + if frame: + self.SetFrame(frame) + + self._panes = [] + self._docks = [] + self._uiparts = [] + + 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_UP, self.OnLeftUp) + self.Bind(wx.EVT_MOTION, self.OnMotion) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + self.Bind(wx.EVT_TIMER, self.OnHintFadeTimer) + self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus) + self.Bind(EVT_AUI_PANEBUTTON, self.OnPaneButton) + + + def GetPaneByWidget(self, window): + """ + This version of GetPane() looks up a pane based on a + 'pane window', see below comment for more info. + """ + + for p in self._panes: + if p.window == window: + return p + + return NoneAuiPaneInfo + + + def GetPaneByName(self, name): + """ + This version of GetPane() looks up a pane based on a + 'pane name', see below comment for more info. + """ + + for p in self._panes: + if p.name == name: + return p + + return NoneAuiPaneInfo + + + def GetPane(self, item): + """ + GetPane() looks up a AuiPaneInfo structure based + on the supplied window pointer. Upon failure, GetPane() + returns an empty AuiPaneInfo, a condition which can be checked + by calling AuiPaneInfo.IsOk(). + + The pane info's structure may then be modified. Once a pane's + info is modified, AuiManager.Update() must be called to + realize the changes in the UI. + + AG: Added To Handle 2 Different Versions Of GetPane() For + wxPython/Python. + """ + + if isinstance(item, type("")): + return self.GetPaneByName(item) + else: + return self.GetPaneByWidget(item) + + + def GetAllPanes(self): + """ GetAllPanes() returns a reference to all the pane info structures. """ + + return self._panes + + + def HitTest(self, x, y): + """ + HitTest() is an internal function which determines + which UI item the specified coordinates are over + (x,y) specify a 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 == DockUIPart.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 == DockUIPart.typePane or \ + item.type == DockUIPart.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 + + + # SetFlags() and GetFlags() allow the owner to set various + # options which are global to AuiManager + + def SetFlags(self, flags): + """ + SetFlags() is used to specify AuiManager's settings flags. flags specifies + options which allow the frame management behavior to be modified. + """ + + self._flags = flags + + + def GetFlags(self): + """ GetFlags() returns the current manager's flags. """ + + return self._flags + + + def SetFrame(self, frame): + """ + SetFrame() is usually called once when the frame + manager class is being initialized. "frame" specifies + the frame which should be managed by the frame manager. + """ + + if not frame: + raise "\nERROR: Specified Frame Must Be Non-Null. " + + self._frame = frame + 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(frame, wx.MDIParentFrame): + mdi_frame = frame + client_window = mdi_frame.GetClientWindow() + + if not client_window: + raise "\nERROR: MDI Client Window Is Null. " + + self.AddPane(client_window, AuiPaneInfo().Name("mdiclient"). + CenterPane().PaneBorder(False)) + + + def GetFrame(self): + """ GetFrame() returns the frame pointer being managed by AuiManager. """ + + return self._frame + + + def UnInit(self): + """ + UnInit() must be called, usually in the destructor + of the frame class. If it is not called, usually this + will result in a crash upon program exit. + """ + + self._frame.RemoveEventHandler(self) + + + def GetArtProvider(self): + """ GetArtProvider() returns the current art provider being used. """ + + return self._art + + + def ProcessMgrEvent(self, event): + + # first, give the owner frame a chance to override + if self._frame: + if self._frame.ProcessEvent(event): + return + + if event.GetEventType() != wxEVT_AUI_PANECLOSE: + self.ProcessEvent(event) + + + def CanMakeWindowsTransparent(self): + if wx.Platform == "__WXMSW__": + version = wx.GetOsDescription() + found = version.find("XP") >= 0 or version.find("2000") >= 0 or version.find("NT") >= 0 + return found and _libimported + elif wx.Platform == "__WXMAC__" and _carbon_dll: + return True + else: + return False + +# on supported windows systems (Win2000 and greater), this function +# will make a frame window transparent by a certain amount + def MakeWindowTransparent(self, wnd, amount): + + if wnd.GetSize() == (0, 0): + return + + # this API call is not in all SDKs, only the newer ones, so + # we will runtime bind this + if wx.Platform == "__WXMSW__": + hwnd = wnd.GetHandle() + + if not hasattr(self, "_winlib"): + if _libimported == "MH": + self._winlib = win32api.LoadLibrary("user32") + elif _libimported == "ctypes": + self._winlib = ctypes.windll.user32 + + if _libimported == "MH": + pSetLayeredWindowAttributes = win32api.GetProcAddress(self._winlib, + "SetLayeredWindowAttributes") + + if pSetLayeredWindowAttributes == None: + return + + exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) + if 0 == (exstyle & 0x80000): + win32api.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, exstyle | 0x80000) + + winxpgui.SetLayeredWindowAttributes(hwnd, 0, amount, 2) + + elif _libimported == "ctypes": + style = self._winlib.GetWindowLongA(hwnd, 0xffffffecL) + style |= 0x00080000 + self._winlib.SetWindowLongA(hwnd, 0xffffffecL, style) + self._winlib.SetLayeredWindowAttributes(hwnd, 0, amount, 2) + + elif wx.Platform == "__WXMAC__" and _carbon_dll: + handle = _carbon_dll.GetControlOwner(wnd.GetHandle()) + if amount == 0: + amnt = float(0) + else: + amnt = float(amount)/255.0 #convert from the 0-255 amount to the float that Carbon wants + _carbon_dll.SetWindowAlpha(handle, ctypes.c_float(amnt)) + else: + #shouldn't be called, but just in case... + return + + + def SetArtProvider(self, art_provider): + """ + SetArtProvider() instructs AuiManager to use the + specified art provider for all drawing calls. This allows + plugable look-and-feel features. + """ + + # delete the last art provider, if any + del self._art + + # assign the new art provider + self._art = art_provider + + + def AddPane(self, window, arg1=None, arg2=None): + """ + AddPane() tells the frame manager to start managing a child window. There + are two versions of this function. The first verison allows the full spectrum + of pane parameter possibilities (AddPane1). The second version is used for + simpler user interfaces which do not require as much configuration (AddPane2). + In wxPython, simply call AddPane. + """ + + if type(arg1) == type(1): + # This Is Addpane2 + if arg1 is None: + arg1 = wx.LEFT + if arg2 is None: + arg2 = "" + return self.AddPane2(window, arg1, arg2) + else: + return self.AddPane1(window, arg1) + + + def AddPane1(self, window, pane_info): + + # 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 + + if isinstance(window, wx.ToolBar): + window.SetBestFittingSize() + + 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 len(pinfo.name) == 0 or pinfo.name == "": + pinfo.name = ("%s%08x%08x%08x")%(pinfo.window.GetName(), time.time(), + time.clock(), len(self._panes)) + + # set initial proportion (if not already set) + if pinfo.dock_proportion == 0: + pinfo.dock_proportion = 100000 + + if pinfo.HasCloseButton() and len(pinfo.buttons) == 0: + button = PaneButton(None) + button.button_id = AuiPaneInfo.buttonClose + pinfo.buttons.append(button) + + if pinfo.best_size == wx.DefaultSize and pinfo.window: + pinfo.best_size = pinfo.window.GetClientSize() + + if isinstance(pinfo.window, wx.ToolBar): + # GetClientSize() doesn't get the best size for + # a toolbar under some newer versions of wxWidgets, + # so use GetBestSize() + pinfo.best_size = pinfo.window.GetBestSize() + + # for some reason, wxToolBar::GetBestSize() is returning + # a size that is a pixel shy of the correct amount. + # I believe this to be the correct action, until + # wxToolBar::GetBestSize() is fixed. Is this assumption + # correct? + pinfo.best_size.y = pinfo.best_size.y + 1 + + # this is needed for Win2000 to correctly fill toolbar backround + # it should probably be repeated once system colour change happens + if wx.Platform == "__WXMSW__" and pinfo.window.UseBgCol(): + pinfo.window.SetBackgroundColour(self.GetArtProvider().GetColour(AUI_ART_BACKGROUND_COLOUR)) + + if pinfo.min_size != wx.DefaultSize: + 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 + + return True + + + def AddPane2(self, window, direction, caption): + + 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 InsertPane(self, window, pane_info, insert_level=AUI_INSERT_PANE): + """ + InsertPane() is used to insert either a previously unmanaged pane window + into the frame manager, or to insert a currently managed pane somewhere else. + InsertPane() will push all panes, rows, or docks aside and insert the window + into the position specified by insert_location. Because insert_location 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. + """ + + # 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.DefaultPosition: + existing_pane.FloatingPosition(pane_info.floating_pos) + if pane_info.floating_size != wx.DefaultSize: + existing_pane.FloatingSize(pane_info.floating_size) + else: + 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): + """ + DetachPane() tells the AuiManager to stop managing the pane specified + by window. The window, if in a floated frame, is reparented to the frame + managed by AuiManager. + """ + + 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 m_frame and destroy the floating frame + + # reduce flicker + p.window.SetSize(1,1) + p.frame.Show(False) + + # reparent to self._frame and destroy the pane + p.window.Reparent(self._frame) + p.frame.SetSizer(None) + p.frame.Destroy() + p.frame = None + + self._panes.remove(p) + return True + + return False + + + def SavePerspective(self): + """ + SavePerspective() saves all pane information as a single string. + This string may later be fed into LoadPerspective() to restore + all pane settings. This save and load mechanism allows an + exact pane configuration to be saved and restored at a later time. + """ + + result = "layout1|" + pane_count = len(self._panes) + + for pane_i in xrange(pane_count): + pane = self._panes[pane_i] + result = result + "name=" + EscapeDelimiters(pane.name) + ";" + result = result + "caption=" + EscapeDelimiters(pane.caption) + ";" + result = result + "state=%u;"%pane.state + result = result + "dir=%d;"%pane.dock_direction + result = result + "layer=%d;"%pane.dock_layer + result = result + "row=%d;"%pane.dock_row + result = result + "pos=%d;"%pane.dock_pos + result = result + "prop=%d;"%pane.dock_proportion + result = result + "bestw=%d;"%pane.best_size.x + result = result + "besth=%d;"%pane.best_size.y + result = result + "minw=%d;"%pane.min_size.x + result = result + "minh=%d;"%pane.min_size.y + result = result + "maxw=%d;"%pane.max_size.x + result = result + "maxh=%d;"%pane.max_size.y + result = result + "floatx=%d;"%pane.floating_pos.x + result = result + "floaty=%d;"%pane.floating_pos.y + result = result + "floatw=%d;"%pane.floating_size.x + result = result + "floath=%d"%pane.floating_size.y + result = result + "|" + + dock_count = len(self._docks) + + for dock_i in xrange(dock_count): + dock = self._docks[dock_i] + result = result + ("dock_size(%d,%d,%d)=%d|")%(dock.dock_direction, + dock.dock_layer, + dock.dock_row, + dock.size) + + return result + + + def LoadPerspective(self, layout, update=True): + """ + LoadPerspective() loads a layout which was saved with SavePerspective() + If the "update" flag parameter is True, the GUI will immediately be updated. + """ + + input = layout + # check layout string version + indx = input.index("|") + part = input[0:indx] + input = input[indx+1:] + part = part.strip() + + if part != "layout1": + return False + + olddocks = self._docks[:] + oldpanes = self._panes[:] + + # mark all panes currently managed as docked and hidden + pane_count = len(self._panes) + for pane_i in xrange(pane_count): + pane = self._panes[pane_i] + pane.Dock().Hide() + self._panes[pane_i] = pane + + # clear out the dock array this will be reconstructed + self._docks = [] + + # replace escaped characters so we can + # split up the string easily + input = input.replace("\\|", "\a") + input = input.replace("\\", "\b") + + input = input.split("|") + + for line in input: + + if line.startswith("dock_size"): + + indx = line.index("=") + size = int(line[indx+1:]) + indx1 = line.index("(") + indx2 = line.index(")") + line2 = line[indx1+1:indx2] + vals = line2.split(",") + dir = int(vals[0]) + layer = int(vals[1]) + row = int(vals[2]) + dock = DockInfo() + dock.dock_direction = dir + dock.dock_layer = layer + dock.dock_row = row + dock.size = size + + self._docks.append(dock) + + elif line.startswith("name"): + + newline = line.split(";") + pane = AuiPaneInfo() + + for newl in newline: + myline = newl.strip() + vals = myline.split("=") + val_name = vals[0] + value = vals[1] + if val_name == "name": + pane.name = value + elif val_name == "caption": + pane.caption = value + elif val_name == "state": + pane.state = int(value) + elif val_name == "dir": + pane.dock_direction = int(value) + elif val_name == "layer": + pane.dock_layer = int(value) + elif val_name == "row": + pane.dock_row = int(value) + elif val_name == "pos": + pane.dock_pos = int(value) + elif val_name == "prop": + pane.dock_proportion = int(value) + elif val_name == "bestw": + pane.best_size.x = int(value) + elif val_name == "besth": + pane.best_size.y = int(value) + pane.best_size = wx.Size(pane.best_size.x, pane.best_size.y) + elif val_name == "minw": + pane.min_size.x = int(value) + elif val_name == "minh": + pane.min_size.y = int(value) + pane.min_size = wx.Size(pane.min_size.x, pane.min_size.y) + elif val_name == "maxw": + pane.max_size.x = int(value) + elif val_name == "maxh": + pane.max_size.y = int(value) + pane.max_size = wx.Size(pane.max_size.x, pane.max_size.y) + elif val_name == "floatx": + pane.floating_pos.x = int(value) + elif val_name == "floaty": + pane.floating_pos.y = int(value) + pane.floating_pos = wx.Point(pane.floating_pos.x, pane.floating_pos.y) + elif val_name == "floatw": + pane.floating_size.x = int(value) + elif val_name == "floath": + pane.floating_size.y = int(value) + pane.floating_size = wx.Size(pane.floating_size.x, pane.floating_size.y) + else: + raise "\nERROR: Bad Perspective String." + + # replace escaped characters so we can + # split up the string easily + pane.name = pane.name.replace("\a", "|") + pane.name = pane.name.replace("\b", ";") + pane.caption = pane.caption.replace("\a", "|") + pane.caption = pane.caption.replace("\b", ";") + + p = self.GetPane(pane.name) + if not p.IsOk(): + # the pane window couldn't be found + # in the existing layout + return False + + indx = self._panes.index(p) + pane.window = p.window + pane.frame = p.frame + pane.buttons = p.buttons + self._panes[indx] = pane + + if update: + self.Update() + + return True + + + def GetPanePositionsAndSizes(self, dock): + """ Returns all the panes positions and sizes. """ + + caption_size = self._art.GetMetric(AUI_ART_CAPTION_SIZE) + pane_border_size = self._art.GetMetric(AUI_ART_PANE_BORDER_SIZE) + gripper_size = self._art.GetMetric(AUI_ART_GRIPPER_SIZE) + + positions = [] + sizes = [] + + action_pane = -1 + pane_count = len(dock.panes) + + # find the pane marked as our action pane + for pane_i in xrange(pane_count): + pane = dock.panes[pane_i] + if pane.state & AuiPaneInfo.actionPane: + 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 = size + pane_border_size*2 + + if dock.IsHorizontal(): + if pane.HasGripper() and not pane.HasGripperTop(): + size = size + gripper_size + + size = size + pane.best_size.x + + else: + if pane.HasGripper() and pane.HasGripperTop(): + size = size + gripper_size + + if pane.HasCaption(): + size = size + caption_size + + size = size + pane.best_size.y + + sizes.append(size) + + # if there is no action pane, just return the default + # positions (as specified in pane.pane_pos) + if action_pane == -1: + return positions, sizes + + offset = 0 + for pane_i in xrange(action_pane-1, -1, -1): + amount = positions[pane_i+1] - (positions[pane_i] + sizes[pane_i]) + if amount >= 0: + offset = offset + amount + else: + positions[pane_i] -= -amount + + offset = offset + sizes[pane_i] + + # if the dock mode is fixed, make sure none of the panes + # overlap we will bump panes that overlap + offset = 0 + for pane_i in xrange(action_pane, pane_count): + amount = positions[pane_i] - offset + if amount >= 0: + offset = offset + amount + else: + positions[pane_i] += -amount + + offset = offset + sizes[pane_i] + + return positions, sizes + + + def LayoutAddPane(self, cont, dock, pane, uiparts, spacer_only): + + sizer_item = wx.SizerItem() + caption_size = self._art.GetMetric(AUI_ART_CAPTION_SIZE) + gripper_size = self._art.GetMetric(AUI_ART_GRIPPER_SIZE) + pane_border_size = self._art.GetMetric(AUI_ART_PANE_BORDER_SIZE) + pane_button_size = self._art.GetMetric(AUI_ART_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 = DockUIPart() + 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 = DockUIPart.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) + + if pane.HasCaption(): + + # create the caption sizer + part = DockUIPart() + caption_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_item = caption_sizer.Add((1, caption_size), 1, wx.EXPAND) + part.type = DockUIPart.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 = DockUIPart() + part.type = DockUIPart.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) + + # 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: + sizer_item = vert_pane_sizer.Add((1, 1), 1, wx.EXPAND) + else: + sizer_item = vert_pane_sizer.Add(pane.window, 1, wx.EXPAND) + vert_pane_sizer.SetItemMinSize(pane.window, (1, 1)) + + part = DockUIPart() + part.type = DockUIPart.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. Alternitavely, + # 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.DefaultSize: + min_size = pane.best_size + pane_proportion = 0 + + if min_size != wx.DefaultSize: + vert_pane_sizer.SetItemMinSize( + len(vert_pane_sizer.GetChildren())-1, (min_size.x, min_size.y)) + + # add the verticle sizer (caption, pane window) to the + # horizontal sizer (gripper, verticle sizer) + horz_pane_sizer.Add(vert_pane_sizer, 1, wx.EXPAND) + + # finally, add the pane sizer to the dock sizer + if pane.HasBorder(): + # allowing space for the pane's border + sizer_item = cont.Add(horz_pane_sizer, pane_proportion, + wx.EXPAND | wx.ALL, pane_border_size) + part = DockUIPart() + part.type = DockUIPart.typePaneBorder + part.dock = dock + part.pane = pane + part.button = None + part.orientation = orientation + part.cont_sizer = cont + part.sizer_item = sizer_item + uiparts.append(part) + else: + sizer_item = cont.Add(horz_pane_sizer, pane_proportion, wx.EXPAND) + + return uiparts + + + def LayoutAddDock(self, cont, dock, uiparts, spacer_only): + + sizer_item = wx.SizerItem() + part = DockUIPart() + + sash_size = self._art.GetMetric(AUI_ART_SASH_SIZE) + orientation = (dock.IsHorizontal() and [wx.HORIZONTAL] or [wx.VERTICAL])[0] + + # resizable bottom and right docks have a sash before them + if not dock.fixed and (dock.dock_direction == AUI_DOCK_BOTTOM or \ + dock.dock_direction == AUI_DOCK_RIGHT): + + sizer_item = cont.Add((sash_size, sash_size), 0, wx.EXPAND) + + part.type = DockUIPart.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 + pane_count = len(dock.panes) + + if dock.fixed: + + # figure out the real pane positions we will + # use, without modifying the each pane's pane_pos member + pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock) + + offset = 0 + for pane_i in xrange(pane_count): + + pane = dock.panes[pane_i] + pane_pos = pane_positions[pane_i] + + 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 = DockUIPart() + part.type = DockUIPart.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((1, 1), 1, wx.EXPAND) + part = DockUIPart() + part.type = DockUIPart.typeBackground + part.dock = dock + part.pane = None + part.button = None + part.orientation = orientation + part.cont_sizer = dock_sizer + part.sizer_item = sizer_item + uiparts.append(part) + + else: + + for pane_i in xrange(pane_count): + + pane = dock.panes[pane_i] + + # if this is not the first pane being added, + # we need to add a pane sizer + if pane_i > 0: + sizer_item = dock_sizer.Add((sash_size, sash_size), 0, wx.EXPAND) + part = DockUIPart() + part.type = DockUIPart.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: + sizer_item = cont.Add(dock_sizer, 1, wx.EXPAND) + else: + sizer_item = cont.Add(dock_sizer, 0, wx.EXPAND) + + part = DockUIPart() + part.type = DockUIPart.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 dock.fixed and (dock.dock_direction == AUI_DOCK_TOP or \ + dock.dock_direction == AUI_DOCK_LEFT): + + sizer_item = cont.Add((sash_size, sash_size), 0, wx.EXPAND) + + part = DockUIPart() + part.type = DockUIPart.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): + + container = wx.BoxSizer(wx.VERTICAL) + + pane_border_size = self._art.GetMetric(AUI_ART_PANE_BORDER_SIZE) + caption_size = self._art.GetMetric(AUI_ART_CAPTION_SIZE) + cli_size = self._frame.GetClientSize() + + # empty all docks out + for ii in xrange(len(docks)): + docks[ii].panes = [] + + 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: + + # find any docks in this layer + arr = FindDocks(docks, p.dock_direction, p.dock_layer, p.dock_row) + + if len(arr) > 0: + dock = arr[0] + else: + # dock was not found, so we need to create a new one + d = DockInfo() + 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.IsDocked() and p.IsShown(): + # 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 + for ii in xrange(len(docks)-1, -1, -1): + if len(docks[ii].panes) == 0: + docks.pop(ii) + + dock_count = len(docks) + # configure the docks further + for ii in xrange(len(docks)): + dock = docks[ii] + dock_pane_count = len(dock.panes) + + # sort the dock pane array by the pane's + # dock position (dock_pos), in ascending order + dock.panes.sort(PaneSortFunc) + + # for newly created docks, set up their initial size + if dock.size == 0: + size = 0 + for jj in xrange(dock_pane_count): + pane = dock.panes[jj] + pane_size = pane.best_size + if pane_size == wx.DefaultSize: + pane_size = pane.min_size + if pane_size == wx.DefaultSize: + 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 jj in xrange(dock_pane_count): + if dock.panes[jj].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 jj in xrange(dock_pane_count): + if dock.panes[jj].HasCaption(): + size = size + caption_size + break + + # new dock's size may not be more than 1/3 of the frame size + if dock.IsHorizontal(): + size = min(size, cli_size.y/3) + else: + size = min(size, cli_size.x/3) + + if size < 10: + size = 10 + + dock.size = size + + # determine the dock's minimum size + plus_border = False + plus_caption = False + dock_min_size = 0 + for jj in xrange(dock_pane_count): + pane = dock.panes[jj] + if pane.min_size != wx.DefaultSize: + if pane.HasBorder(): + plus_border = True + if pane.HasCaption(): + plus_caption = 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 = dock_min_size + pane_border_size*2 + if plus_caption and dock.IsHorizontal(): + dock_min_size = 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 jj in xrange(dock_pane_count): + pane = dock.panes[jj] + if not pane.IsFixed(): + dock.fixed = False + if not pane.IsToolbar(): + dock.toolbar = False + if pane.state & AuiPaneInfo.actionPane: + action_pane_marked = True + + # if the dock mode is proportional and not fixed-pixel, + # reassign the dock_pos to the sequential 0, 1, 2, 3 + # e.g. remove gaps like 1, 2, 30, 500 + if not dock.fixed: + for jj in xrange(dock_pane_count): + pane = dock.panes[jj] + pane.dock_pos = jj + dock.panes[jj] = pane + + # if the dock mode is fixed, and none of the panes + # are being moved right now, make sure the panes + # do not overlap each other. If they do, we will + # adjust the panes' positions + if dock.fixed and not action_pane_marked: + pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock) + offset = 0 + for jj in xrange(dock_pane_count): + pane = dock.panes[jj] + pane.dock_pos = pane_positions[jj] + amount = pane.dock_pos - offset + if amount >= 0: + offset = offset + amount + else: + pane.dock_pos += -amount + + offset = offset + pane_sizes[jj] + dock.panes[jj] = pane + + if oncheck: + self._docks[ii] = dock + + # discover the maximum dock layer + max_layer = 0 + + for ii in xrange(dock_count): + max_layer = max(max_layer, docks[ii].dock_layer) + + # clear out uiparts + uiparts = [] + + # create a bunch of box sizers, + # from the innermost level outwards. + cont = None + middle = None + + if oncheck: + docks = self._docks + + for layer in xrange(max_layer+1): + # find any docks in this layer + arr = FindDocks(docks, -1, layer, -1) + # if there aren't any, skip to the next layer + if len(arr) == 0: + 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, arr) + arr = RenumberDockRows(arr) + if len(arr) > 0: + for row in xrange(len(arr)): + uiparts = self.LayoutAddDock(cont, arr[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, arr) + arr = RenumberDockRows(arr) + if len(arr) > 0: + for row in xrange(len(arr)): + uiparts = self.LayoutAddDock(middle, arr[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, arr) + if len(arr) > 0: + for row in xrange(len(arr)): + uiparts = self.LayoutAddDock(middle, arr[row], uiparts, spacer_only) + else: + # there are no center docks, add a background area + sizer_item = middle.Add((1, 1), 1, wx.EXPAND) + part = DockUIPart() + part.type = DockUIPart.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, arr) + arr = RenumberDockRows(arr) + if len(arr) > 0: + for row in xrange(len(arr)-1, -1, -1): + uiparts = self.LayoutAddDock(middle, arr[row], uiparts, spacer_only) + + cont.Add(middle, 1, wx.EXPAND) + + # find any bottom docks in this layer + arr = FindDocks(docks, AUI_DOCK_BOTTOM, layer, -1, arr) + arr = RenumberDockRows(arr) + if len(arr) > 0: + for row in xrange(len(arr)-1, -1, -1): + uiparts = self.LayoutAddDock(cont, arr[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 = DockUIPart() + part.type = DockUIPart.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 Update(self): + """ + Update() updates the layout. Whenever changes are made to + one or more panes, this function should be called. It is the + external entry point for running the layout engine. + """ + + pane_count = len(self._panes) + # delete old sizer first + self._frame.SetSizer(None) + + # destroy floating panes which have been + # redocked or are becoming non-floating + for ii in xrange(pane_count): + p = self._panes[ii] + if not p.IsFloating() and p.frame: + # 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)) + p.frame.Show(False) + + # reparent to self._frame and destroy the pane + p.window.Reparent(self._frame) + p.frame.SetSizer(None) + p.frame.Destroy() + p.frame = None + + self._panes[ii] = p + + # create a layout for all of the panes + sizer = self.LayoutAll(self._panes, self._docks, self._uiparts, False) + + # hide or show panes as necessary, + # and float panes as necessary + + pane_count = len(self._panes) + + for ii in xrange(pane_count): + p = self._panes[ii] + if p.IsFloating(): + if p.frame == None: + # we need to create a frame for this + # pane, which has recently been floated + resizeborder = 1 + if p.IsFixed(): + resizeborder = 0 + + frame = AuiFloatingPane(self._frame, self, -1, "", p.floating_pos, + p.floating_size, resizeborder=resizeborder) + + # on MSW, 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 == actionDragAuiFloatingPane and self.UseTransparentDrag(): + self.MakeWindowTransparent(frame, 150) + + frame.SetPaneWindow(p) + p.frame = frame + if p.IsShown(): + frame.Show() + else: + + # frame already exists, make sure it's position + # and size reflect the information in AuiPaneInfo + if p.frame.GetPosition() != p.floating_pos: + p.frame.SetDimensions(p.floating_pos.x, p.floating_pos.y, + -1, -1, wx.SIZE_USE_EXISTING) + p.frame.Show(p.IsShown()) + else: + + p.window.Show(p.IsShown()) + + # if "active panes" are no longer allowed, clear + # any optionActive values from the pane states + if self._flags & AUI_MGR_ALLOW_ACTIVE_PANE == 0: + p.state &= ~AuiPaneInfo.optionActive + + self._panes[ii] = p + + + old_pane_rects = [] + + for ii in xrange(pane_count): + r = wx.Rect() + p = self._panes[ii] + + if p.window and p.IsShown() and p.IsDocked(): + r = p.rect + + old_pane_rects.append(r) + + # apply the new sizer + self._frame.SetSizer(sizer) + self._frame.SetAutoLayout(False) + self.DoFrameLayout() + + # now that the frame layout is done, we need to check + # the new pane rectangles against the old rectangles that + # we saved a few lines above here. If the rectangles have + # changed, the corresponding panes must also be updated + for ii in xrange(pane_count): + p = self._panes[ii] + if p.window and p.IsShown() and p.IsDocked(): + if p.rect != old_pane_rects[ii]: + p.window.Refresh() + p.window.Update() + + self.Repaint() + + + def DoFrameLayout(self): + """ + DoFrameLayout() is an internal function which invokes wxSizer.Layout + on the frame's main sizer, then measures all the various UI items + and updates their internal rectangles. This should always be called + instead of calling self._frame.Layout() directly + """ + + self._frame.Layout() + + for ii in xrange(len(self._uiparts)): + part = self._uiparts[ii] + + # 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 == DockUIPart.typeDock: + part.dock.rect = part.rect + if part.type == DockUIPart.typePane: + part.pane.rect = part.rect + + self._uiparts[ii] = part + + + def GetPanePart(self, wnd): + """ + GetPanePart() 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. + """ + + for ii in xrange(len(self._uiparts)): + part = self._uiparts[ii] + if part.type == DockUIPart.typePaneBorder and \ + part.pane and part.pane.window == wnd: + return part + + for ii in xrange(len(self._uiparts)): + part = self._uiparts[ii] + if part.type == DockUIPart.typePane and \ + part.pane and part.pane.window == wnd: + return part + + return None + + + def GetDockPixelOffset(self, test): + """ + GetDockPixelOffset() 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 + fixel-pane/toolbar offsets when they are dragged. + """ + + # 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 ii in xrange(len(uiparts)): + part = uiparts[ii] + pos = part.sizer_item.GetPosition() + size = part.sizer_item.GetSize() + part.rect = wx.Rect(pos[0], pos[1], size[0], size[1]) + if part.type == DockUIPart.typeDock: + part.dock.rect = part.rect + + sizer.Destroy() + + for ii in xrange(len(docks)): + dock = docks[ii] + 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 ProcessDockResult(self, target, new_pos): + """ + ProcessDockResult() is a utility function used by 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. + """ + + allowed = False + if new_pos.dock_direction == AUI_DOCK_TOP: + allowed = target.IsTopDockable() + elif new_pos.dock_direction == AUI_DOCK_BOTTOM: + allowed = target.IsBottomDockable() + elif new_pos.dock_direction == AUI_DOCK_LEFT: + allowed = target.IsLeftDockable() + elif new_pos.dock_direction == AUI_DOCK_RIGHT: + allowed = target.IsRightDockable() + + if allowed: + target = new_pos + + return allowed, target + + + def DoDrop(self, docks, panes, target, pt, offset=wx.Point(0,0)): + """ + DoDrop() 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. + """ + + cli_size = self._frame.GetClientSize() + + 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 = target.best_size + drop.min_size = target.min_size + drop.max_size = target.max_size + drop.floating_pos = target.floating_pos + drop.floating_size = target.floating_size + drop.dock_proportion = target.dock_proportion + drop.buttons = target.buttons + drop.rect = target.rect + + # The result should always be shown + drop.Show() + + # Check to see if the pane has been dragged outside of the window + # (or near to the outside of the window), if so, dock it along the edge + + layer_insert_offset = auiLayerInsertOffset + + if target.IsToolbar(): + layer_insert_offset = 0 + + if pt.x < layer_insert_offset and \ + pt.x > layer_insert_offset-auiLayerInsertPixels: + new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_LEFT), + GetMaxLayer(docks, AUI_DOCK_BOTTOM)), + GetMaxLayer(docks, AUI_DOCK_TOP)) + 1 + + drop.Dock().Left().Layer(new_layer).Row(0). \ + Position(pt.y - self.GetDockPixelOffset(drop) - offset.y) + + return self.ProcessDockResult(target, drop) + + elif pt.y < layer_insert_offset and \ + pt.y > layer_insert_offset-auiLayerInsertPixels: + new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_TOP), + GetMaxLayer(docks, AUI_DOCK_LEFT)), + GetMaxLayer(docks, AUI_DOCK_RIGHT)) + 1 + + drop.Dock().Top().Layer(new_layer).Row(0). \ + Position(pt.x - self.GetDockPixelOffset(drop) - offset.x) + + return self.ProcessDockResult(target, drop) + + elif pt.x >= cli_size.x - layer_insert_offset and \ + pt.x < cli_size.x - layer_insert_offset + auiLayerInsertPixels: + + new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_RIGHT), + GetMaxLayer(docks, AUI_DOCK_TOP)), + GetMaxLayer(docks, AUI_DOCK_BOTTOM)) + 1 + + drop.Dock().Right().Layer(new_layer).Row(0). \ + Position(pt.y - self.GetDockPixelOffset(drop) - offset.y) + + return self.ProcessDockResult(target, drop) + + elif pt.y >= cli_size.y - layer_insert_offset and \ + pt.y < cli_size.y - layer_insert_offset + auiLayerInsertPixels: + + new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_BOTTOM), + GetMaxLayer(docks, AUI_DOCK_LEFT)), + GetMaxLayer(docks, AUI_DOCK_RIGHT)) + 1 + + drop.Dock().Bottom().Layer(new_layer).Row(0). \ + Position(pt.x - self.GetDockPixelOffset(drop) - offset.x) + + return self.ProcessDockResult(target, drop) + + part = self.HitTest(pt.x, pt.y) + + if drop.IsToolbar(): + if not part or not part.dock: + return False, target + + # calculate the offset from where the dock begins + # to the point where the user dropped the pane + dock_drop_offset = 0 + if part.dock.IsHorizontal(): + dock_drop_offset = pt.x - part.dock.rect.x - offset.x + else: + dock_drop_offset = pt.y - part.dock.rect.y - offset.y + + # 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 part.dock.fixed or part.dock.dock_direction == AUI_DOCK_CENTER: + if (self._flags & AUI_MGR_ALLOW_FLOATING) and (drop.IsFloatable() or (\ + part.dock.dock_direction != AUI_DOCK_CENTER and \ + part.dock.dock_direction != AUI_DOCK_NONE)): + + drop.Float() + + return self.ProcessDockResult(target, drop) + + drop.Dock(). \ + Direction(part.dock.dock_direction). \ + Layer(part.dock.dock_layer). \ + Row(part.dock.dock_row). \ + Position(dock_drop_offset) + + if pt.y < part.dock.rect.y + 2 and len(part.dock.panes) > 1: + row = drop.dock_row + panes = DoInsertDockRow(panes, part.dock.dock_direction, + part.dock.dock_layer, + part.dock.dock_row) + drop.dock_row = row + + if pt.y > part.dock.rect.y + part.dock.rect.height - 2 and \ + len(part.dock.panes) > 1: + panes = DoInsertDockRow(panes, part.dock.dock_direction, + part.dock.dock_layer, + part.dock.dock_row+1) + drop.dock_row = part.dock.dock_row + 1 + + return self.ProcessDockResult(target, drop) + + if not part: + return False, target + + if part.type == DockUIPart.typePaneBorder or \ + part.type == DockUIPart.typeCaption or \ + part.type == DockUIPart.typeGripper or \ + part.type == DockUIPart.typePaneButton or \ + part.type == DockUIPart.typePane or \ + part.type == DockUIPart.typePaneSizer or \ + part.type == DockUIPart.typeDockSizer or \ + part.type == DockUIPart.typeBackground: + + if part.type == DockUIPart.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 a normal frame is being dragged over a toolbar, insert it + # along the edge under the toolbar, but over all other panes. + # (this could be done much better, but somehow factoring this + # calculation with the one at the beginning of this function) + if part.dock and (hasattr(part.dock, "toolbar") and part.dock.toolbar): + layer = 0 + + if part.dock.dock_direction == AUI_DOCK_LEFT: + layer = max(max(GetMaxLayer(docks, AUI_DOCK_LEFT), + GetMaxLayer(docks, AUI_DOCK_BOTTOM)), + GetMaxLayer(docks, AUI_DOCK_TOP)) + elif part.dock.dock_direction == AUI_DOCK_TOP: + layer = max(max(GetMaxLayer(docks, AUI_DOCK_TOP), + GetMaxLayer(docks, AUI_DOCK_LEFT)), + GetMaxLayer(docks, AUI_DOCK_RIGHT)) + elif part.dock.dock_direction == AUI_DOCK_RIGHT: + layer = max(max(GetMaxLayer(docks, AUI_DOCK_RIGHT), + GetMaxLayer(docks, AUI_DOCK_TOP)), + GetMaxLayer(docks, AUI_DOCK_BOTTOM)) + elif part.dock.dock_direction == AUI_DOCK_BOTTOM: + layer = max(max(GetMaxLayer(docks, AUI_DOCK_BOTTOM), + GetMaxLayer(docks, AUI_DOCK_LEFT)), + GetMaxLayer(docks, AUI_DOCK_RIGHT)) + + panes = DoInsertDockRow(panes, part.dock.dock_direction, + layer, 0) + drop.Dock(). \ + Direction(part.dock.dock_direction). \ + Layer(layer).Row(0).Position(0) + + return self.ProcessDockResult(target, drop) + + 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 + + if part.pane.dock_direction == AUI_DOCK_TOP: + if pt.y >= part.rect.y and \ + pt.y < part.rect.y+auiInsertRowPixels: + insert_dock_row = True + + elif part.pane.dock_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 part.pane.dock_direction == AUI_DOCK_LEFT: + if pt.x >= part.rect.x and \ + pt.x < part.rect.x+auiInsertRowPixels: + insert_dock_row = True + + elif part.pane.dock_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 part.pane.dock_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 + + if pt.x >= part.rect.x and \ + pt.x < part.rect.x+new_row_pixels_x: + insert_dir = AUI_DOCK_LEFT + elif pt.y >= part.rect.y and \ + pt.y < part.rect.y+new_row_pixels_y: + insert_dir = AUI_DOCK_TOP + elif pt.x >= part.rect.x + part.rect.width-new_row_pixels_x and \ + pt.x < part.rect.x + part.rect.width: + insert_dir = AUI_DOCK_RIGHT + elif pt.y >= part.rect.y+ part.rect.height-new_row_pixels_y and \ + pt.y < part.rect.y + part.rect.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) + + return False, target + + + def UseTransparentHint(self): + return (self._flags & AUI_MGR_TRANSPARENT_HINT) and self.CanMakeWindowsTransparent() + + def OnHintFadeTimer(self, event): + #sanity check + if not self.UseTransparentHint(): + return + + if not self._hint_wnd or self._hint_fadeamt >= 50: + self._hint_fadetimer.Stop() + return + + self._hint_fadeamt = self._hint_fadeamt + 5 + self.MakeWindowTransparent(self._hint_wnd, self._hint_fadeamt) + + + def ShowHint(self, rect): + self._hintshown = True + if self.UseTransparentHint(): + if wx.Platform == "__WXMSW__": + if self._last_hint == rect: + return + self._last_hint = rect + + initial_fade = 50 + + if self._flags & AUI_MGR_TRANSPARENT_HINT_FADE: + initial_fade = 0 + + if self._hint_wnd == None: + + pt = rect.GetPosition() + size = rect.GetSize() + self._hint_wnd = wx.Frame(self._frame, -1, "", pt, size, + wx.FRAME_TOOL_WINDOW | + wx.FRAME_FLOAT_ON_PARENT | + wx.FRAME_NO_TASKBAR | + wx.NO_BORDER) + + self.MakeWindowTransparent(self._hint_wnd, initial_fade) + self._hint_wnd.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)) + self._hint_wnd.Show() + + # if we are dragging a floating pane, set the focus + + # back to that floating pane (otherwise it becomes unfocused) + + if self._action == actionDragAuiFloatingPane and self._action_window: + self._action_window.SetFocus() + + else: + + pt = rect.GetPosition() + size = rect.GetSize() + self.MakeWindowTransparent(self._hint_wnd, initial_fade) + self._hint_wnd.SetDimensions(pt.x, pt.y, rect.width, rect.height) + + if self._flags & AUI_MGR_TRANSPARENT_HINT_FADE: + # start fade in timer + self._hint_fadeamt = 0 + self._hint_fadetimer.SetOwner(self, 101) + self._hint_fadetimer.Start(5) + return + elif wx.Platform == "__WXMAC__": + if self._last_hint == rect: + return #same rect, already shown, no-op + if self._flags & AUI_MGR_TRANSPARENT_HINT_FADE: + initial_fade = 0 + else: + initial_fade = 80 + + if not self._hint_wnd: + self._hint_wnd = wx.MiniFrame(self._frame, + style=wx.FRAME_FLOAT_ON_PARENT|wx.FRAME_TOOL_WINDOW + |wx.CAPTION#|wx.FRAME_SHAPED + #without wxCAPTION + wx.FRAME_TOOL_WINDOW, the hint window + #gets focus & dims the main frames toolbar, which is both wrong + #and distracting. + #wx.CAPTION + wx.FRAME_TOOL_WINDOW cures the focus problem, + #but then it draws the caption. Adding wx.FRAME_SHAPED takes + #care of that, but then SetRect doesn't work to size - need to + #create a bitmap or mask or something. + ) + #can't set the background of a wx.Frame in OSX + p = wx.Panel(self._hint_wnd) + + #the caption color is a light silver thats really hard to see + #especially transparent. See if theres some other system + #setting that is more appropriate, or just extend the art provider + #to cover this + #p.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)) + p.SetBackgroundColour(wx.BLUE) + + self.MakeWindowTransparent(self._hint_wnd, initial_fade) + self._hint_wnd.SetRect(rect) + self._hint_wnd.Show() + + if self._action == actionDragAuiFloatingPane and self._action_window: + self._action_window.SetFocus() + + if self._flags & AUI_MGR_TRANSPARENT_HINT_FADE: + # start fade in timer + self._hint_fadeamt = 0 + self._hint_fadetimer.SetOwner(self, 101) + self._hint_fadetimer.Start(5) + return + + + if self._last_hint != rect: + # remove the last hint rectangle + self._last_hint = 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(): + recta = pane.frame.GetRect() + if wx.Platform == "__WXGTK__": + # wxGTK returns the client size, not the whole frame size + width, height = pane.frame.ClientToScreen((0,0)) - pane.frame.GetPosition() + recta.width = recta.width + width + recta.height = recta.height + height + recta.Inflate(5, 5) + #endif + + clip.SubtractRect(recta) + + screendc.SetClippingRegionAsRegion(clip) + + screendc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION))) + 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) + + + def HideHint(self): + + self._hintshown = False + + # hides a transparent window hint (currently wxMSW only) + if self.UseTransparentHint(): + if self._hint_wnd: + self._hint_fadetimer.Stop() + #self._hint_wnd.Destroy() + self.MakeWindowTransparent(self._hint_wnd, 0) + self._last_hint = wx.Rect() + + return + + # hides a painted hint by redrawing the frame window + if not self._last_hint.IsEmpty(): + self._frame.Refresh() + self._frame.Update() + self._last_hint = wx.Rect() + + + def DrawHintRect(self, pane_window, pt, offset): + """ + DrawHintRect() draws a drop hint rectangle. First calls DoDrop() to + determine the exact position the pane would be at were if dropped. If + the pame would indeed become docked at the specified drop point, + DrawHintRect() then calls ShowHint() to indicate this drop rectangle. + "pane_window" is the window pointer of the pane being dragged, pt is + the mouse position, in client coordinates. + """ + + # 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__" + + if not hint.IsOk(): + hint.name = self._oldname + return + + docks, panes = CopyDocksAndPanes2(self._docks, self._panes) + + # remove any pane already there which bears the same window + # this happens when you are moving a pane around in a dock + for ii in xrange(len(panes)): + if panes[ii].window == pane_window: + docks = RemovePaneFromDocks(docks, panes[ii]) + panes.pop(ii) + break + + # find out where the new pane would be + allow, hint = self.DoDrop(docks, panes, hint, pt, offset) + + if not allow: + self.HideHint() + return + + 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() + + for ii in xrange(len(uiparts)): + part = uiparts[ii] + if part.type == DockUIPart.typePaneBorder and \ + part.pane and part.pane.name == "__HINT__": + pos = part.sizer_item.GetPosition() + size = part.sizer_item.GetSize() + rect = wx.Rect(pos[0], pos[1], size[0], size[1]) + break + + sizer.Destroy() + + if rect.IsEmpty(): + self.HideHint() + return + + # actually show the hint rectangle on the screen + rect.x, rect.y = self._frame.ClientToScreen((rect.x, rect.y)) + self.ShowHint(rect) + + + def GetAttributes(self, pane): + + 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]) + + return attrs + + + def SetAttributes(self, pane, attrs): + + 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] + + return pane + + def UseTransparentDrag(self): + + if self._flags & AUI_MGR_TRANSPARENT_DRAG: + return self.CanMakeWindowsTransparent() + else: + return False + + + def OnAuiFloatingPaneMoveStart(self, wnd): + + # try to find the pane + pane = self.GetPane(wnd) + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + if self.UseTransparentDrag() and pane.IsDockable(): + self.MakeWindowTransparent(pane.frame, 150) + + + def OnAuiFloatingPaneMoving(self, wnd): + + # try to find the pane + pane = self.GetPane(wnd) + + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + pt = wx.GetMousePosition() + client_pt = self._frame.ScreenToClient(pt) + + # calculate the offset from the upper left-hand corner + # of the frame to the mouse pointer + frame_pos = pane.frame.GetPosition() + action_offset = wx.Point(pt[0]-frame_pos.x, pt[1]-frame_pos.y) + +## # no hint for toolbar floating windows +## if pane.IsToolbar() and self._action == actionDragAuiFloatingPane: +## +## oldname = pane.name +## indx = self._panes.index(pane) +## hint = pane +## docks, panes = CopyDocksAndPanes2(self._docks, self._panes) +## +## # find out where the new pane would be +## ret, hint = self.DoDrop(docks, panes, hint, client_pt) +## +## if not ret: +## return +## +## if hint.IsFloating(): +## return +## +## pane = hint +## pane.name = oldname +## +## self._panes[indx] = pane +## self._action = actionDragToolbarPane +## self._action_window = pane.window +## +## self.Update() +## +## return + + # if a key modifier is pressed while dragging the frame, + # don't dock the window + if wx.GetKeyState(wx.WXK_CONTROL) or wx.GetKeyState(wx.WXK_ALT): + self.HideHint() + return + + if pane.IsDockable(): + self.DrawHintRect(wnd, client_pt, action_offset) + + # reduces flicker + self._frame.Update() + + + def OnAuiFloatingPaneMoved(self, wnd): + + # try to find the pane + pane = self.GetPane(wnd) + + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + pt = wx.GetMousePosition() + client_pt = self._frame.ScreenToClient(pt) + + indx = self._panes.index(pane) + + # calculate the offset from the upper left-hand corner + # of the frame to the mouse pointer + frame_pos = pane.frame.GetPosition() + action_offset = wx.Point(pt[0]-frame_pos.x, pt[1]-frame_pos.y) + + # if a key modifier is pressed while dragging the frame, + # don't dock the window + if wx.GetKeyState(wx.WXK_CONTROL) or wx.GetKeyState(wx.WXK_ALT): + self.HideHint() + return + + if not pane.IsToolbar() and pane.IsDockable() and not self._hintshown: + if not pane.IsFloating(): + pane.Float() + pane.floating_pos = pane.frame.GetPosition() + self._panes[indx] = pane + if self.UseTransparentDrag(): + self.MakeWindowTransparent(pane.frame, 255) + + # do the drop calculation + allow, pane = self.DoDrop(self._docks, self._panes, pane, client_pt, action_offset) + + # if the pane is still floating, update it's floating + # position (that we store) + if pane.IsFloating(): + pane.floating_pos = pane.frame.GetPosition() + if self.UseTransparentDrag(): + self.MakeWindowTransparent(pane.frame, 255) + + if not pane.IsToolbar() and pane.IsDockable(): + pane.name = self._oldname + + self._panes[indx] = pane + + self.Update() + self.HideHint() + + + def OnAuiFloatingPaneResized(self, wnd, size): + + # try to find the pane + pane = self.GetPane(wnd) + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + indx = self._panes.index(pane) + pane.floating_size = size + self._panes[indx] = pane + + + def OnAuiFloatingPaneClosed(self, wnd, event): + # try to find the pane + pane = self.GetPane(wnd) + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + e = AuiManagerEvent(wxEVT_AUI_PANECLOSE) + e.SetPane(pane) + self.ProcessMgrEvent(e) + + if e.GetSkipped(): + indx = self._panes.index(pane) + # reparent the pane window back to us and + # prepare the frame window for destruction + pane.window.Show(False) + pane.window.Reparent(self._frame) + pane.frame = None + pane.Hide() + + self._panes[indx] = pane + event.Skip() + + + + def OnAuiFloatingPaneActivated(self, wnd): + + if self.GetFlags() & AUI_MGR_ALLOW_ACTIVE_PANE: + # try to find the pane + pane = self.GetPane(wnd) + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + self._panes = SetActivePane(self._panes, wnd) + self.Repaint() + + + def Render(self, dc): + """ + Render() draws all of the pane captions, sashes, + backgrounds, captions, grippers, pane borders and buttons. + It renders the entire user interface. + """ + + for part in self._uiparts: + + # don't draw hidden pane items + if part.sizer_item and not part.sizer_item.IsShown(): + continue + + if part.type == DockUIPart.typeDockSizer or \ + part.type == DockUIPart.typePaneSizer: + self._art.DrawSash(dc, part.orientation, part.rect) + elif part.type == DockUIPart.typeBackground: + self._art.DrawBackground(dc, part.orientation, part.rect) + elif part.type == DockUIPart.typeCaption: + self._art.DrawCaption(dc, part.pane.caption, part.rect, part.pane) + elif part.type == DockUIPart.typeGripper: + self._art.DrawGripper(dc, part.rect, part.pane) + elif part.type == DockUIPart.typePaneBorder: + self._art.DrawBorder(dc, part.rect, part.pane) + elif part.type == DockUIPart.typePaneButton: + self._art.DrawPaneButton(dc, part.button.button_id, + AUI_BUTTON_STATE_NORMAL, part.rect, part.pane) + + + def Repaint(self, dc=None): + + w, h = self._frame.GetClientSize() + # figure out which dc to use if one + # has been specified, use it, otherwise + # make a client dc + client_dc = None + + if not dc: + 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) + + # if we created a client_dc, delete it + if client_dc: + del client_dc + + + def OnPaint(self, event): + + dc = wx.PaintDC(self._frame) + + if wx.Platform == "__WXMAC__": + #Macs paint optimizations clip the area we need to paint a log + #of the time, this is a dirty hack to always paint everything + self.Repaint(None) + else: + self.Repaint(dc) + + + def OnEraseBackground(self, event): + + if wx.Platform == "__WXMAC__": + event.Skip() + + + def OnSize(self, event): + + if self._frame: + + self.DoFrameLayout() + wx.CallAfter(self.Repaint) + + if not isinstance(self._frame, wx.MDIParentFrame): + event.Skip() + + + def OnSetCursor(self, event): + + # determine cursor + part = self.HitTest(event.GetX(), event.GetY()) + cursor = None + + if part: + if part.type == DockUIPart.typeDockSizer or \ + part.type == DockUIPart.typePaneSizer: + + # a dock may not be resized if it has a single + # pane which is not resizable + if part.type == DockUIPart.typeDockSizer and part.dock and \ + len(part.dock.panes) == 1 and part.dock.panes[0].IsFixed(): + return + + # panes that may not be resized do not get a sizing cursor + if part.pane and part.pane.IsFixed(): + return + + if part.orientation == wx.VERTICAL: + cursor = wx.StockCursor(wx.CURSOR_SIZEWE) + else: + cursor = wx.StockCursor(wx.CURSOR_SIZENS) + + elif part.type == DockUIPart.typeGripper: + cursor = wx.StockCursor(wx.CURSOR_SIZING) + + if cursor is not None: + event.SetCursor(cursor) + + + def UpdateButtonOnScreen(self, button_ui_part, event): + + hit_test = self.HitTest(event.GetX(), event.GetY()) + 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) + + self._art.DrawPaneButton(cdc, + button_ui_part.button.button_id, + state, + button_ui_part.rect, hit_test.pane) + + + def OnLeftDown(self, event): + + part = self.HitTest(event.GetX(), event.GetY()) + + if part: + if part.dock and part.dock.dock_direction == AUI_DOCK_CENTER: + return + + if part.type == DockUIPart.typeDockSizer or \ + part.type == DockUIPart.typePaneSizer: + + # a dock may not be resized if it has a single + # pane which is not resizable + if part.type == DockUIPart.typeDockSizer and part.dock and \ + len(part.dock.panes) == 1 and part.dock.panes[0].IsFixed(): + return + + # panes that may not be resized should be ignored here + if part.pane and part.pane.IsFixed(): + return + + self._action = actionResize + self._action_part = part + self._action_hintrect = 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) + self._frame.CaptureMouse() + + elif part.type == DockUIPart.typePaneButton: + + self._action = actionClickButton + self._action_part = part + self._action_start = wx.Point(event.GetX(), event.GetY()) + self._frame.CaptureMouse() + + self.UpdateButtonOnScreen(part, event) + + elif part.type == DockUIPart.typeCaption or \ + part.type == DockUIPart.typeGripper: + + if self.GetFlags() & AUI_MGR_ALLOW_ACTIVE_PANE: + # set the caption as active + self._panes = SetActivePane(self._panes, part.pane.window) + self.Repaint() + + self._action = actionClickCaption + self._action_part = part + self._action_start = wx.Point(event.GetX(), event.GetY()) + self._action_offset = wx.Point(event.GetX() - part.rect.x, + event.GetY() - part.rect.y) + self._frame.CaptureMouse() + + if wx.Platform != "__WXMAC__": + event.Skip() + + + def OnLeftUp(self, event): + + if self._action == actionResize: + + self._frame.ReleaseMouse() + + # get rid of the hint rectangle + dc = wx.ScreenDC() + DrawResizeHint(dc, self._action_hintrect) + + # resize the dock or the pane + if self._action_part and self._action_part.type == DockUIPart.typeDockSizer: + rect = self._action_part.dock.rect + new_pos = wx.Point(event.GetX() - self._action_offset.x, + event.GetY() - self._action_offset.y) + + if self._action_part.dock.dock_direction == AUI_DOCK_LEFT: + self._action_part.dock.size = new_pos.x - rect.x + elif self._action_part.dock.dock_direction == AUI_DOCK_TOP: + self._action_part.dock.size = new_pos.y - rect.y + elif self._action_part.dock.dock_direction == AUI_DOCK_RIGHT: + self._action_part.dock.size = rect.x + rect.width - \ + new_pos.x - \ + self._action_part.rect.GetWidth() + elif self._action_part.dock.dock_direction == AUI_DOCK_BOTTOM: + self._action_part.dock.size = rect.y + rect.height - \ + new_pos.y - \ + self._action_part.rect.GetHeight() + + self.Update() + self.Repaint(None) + + elif self._action_part and \ + self._action_part.type == DockUIPart.typePaneSizer: + + dock = self._action_part.dock + pane = self._action_part.pane + + total_proportion = 0 + dock_pixels = 0 + new_pixsize = 0 + + caption_size = self._art.GetMetric(AUI_ART_CAPTION_SIZE) + pane_border_size = self._art.GetMetric(AUI_ART_PANE_BORDER_SIZE) + sash_size = self._art.GetMetric(AUI_ART_SASH_SIZE) + + new_pos = wx.Point(event.GetX() - self._action_offset.x, + event.GetY() - self._action_offset.y) + + # determine the pane rectangle by getting the pane part + pane_part = self.GetPanePart(pane.window) + if not pane_part: + raise "\nERROR: Pane border part not found -- shouldn't happen" + + # determine the new pixel size that the user wants + # this will help us recalculate the pane's proportion + if dock.IsHorizontal(): + new_pixsize = new_pos.x - pane_part.rect.x + else: + new_pixsize = new_pos.y - pane_part.rect.y + + # determine the size of the dock, based on orientation + if dock.IsHorizontal(): + dock_pixels = dock.rect.GetWidth() + else: + dock_pixels = dock.rect.GetHeight() + + # determine the total proportion of all resizable panes, + # and the total size of the dock minus the size of all + # the fixed panes + dock_pane_count = len(dock.panes) + pane_position = -1 + + for ii in xrange(dock_pane_count): + p = dock.panes[ii] + if p.window == pane.window: + pane_position = ii + + # while we're at it, subtract the pane sash + # width from the dock width, because this would + # skew our proportion calculations + if ii > 0: + dock_pixels = dock_pixels - sash_size + + # also, the whole size (including decorations) of + # all fixed panes must also be subtracted, because they + # are not part of the proportion calculation + if p.IsFixed(): + if dock.IsHorizontal(): + dock_pixels = dock_pixels - p.best_size.x + else: + dock_pixels = dock_pixels - p.best_size.y + else: + total_proportion = total_proportion + p.dock_proportion + + # find a pane in our dock to 'steal' space from or to 'give' + # space to -- this is essentially what is done when a pane is + # resized the pane should usually be the first non-fixed pane + # to the right of the action pane + borrow_pane = -1 + + for ii in xrange(pane_position+1, dock_pane_count): + p = dock.panes[ii] + if not p.IsFixed(): + borrow_pane = ii + break + + # demand that the pane being resized is found in this dock + # (this assert really never should be raised) + if pane_position == -1: + raise "\nERROR: Pane not found in dock" + + # prevent division by zero + if dock_pixels == 0 or total_proportion == 0 or borrow_pane == -1: + self._action = actionNone + return + + # calculate the new proportion of the pane + new_proportion = new_pixsize*total_proportion/dock_pixels + + # default minimum size + min_size = 0 + + # check against the pane's minimum size, if specified. please note + # that this is not enough to ensure that the minimum size will + # not be violated, because the whole frame might later be shrunk, + # causing the size of the pane to violate it's minimum size + if pane.min_size.IsFullySpecified(): + min_size = 0 + if pane.HasBorder(): + min_size = min_size + pane_border_size*2 + + # calculate minimum size with decorations (border,caption) + if pane_part.orientation == wx.VERTICAL: + min_size = min_size + pane.min_size.y + if pane.HasCaption(): + min_size = min_size + caption_size + else: + min_size = min_size + pane.min_size.x + + # for some reason, an arithmatic error somewhere is causing + # the proportion calculations to always be off by 1 pixel + # for now we will add the 1 pixel on, but we really should + # determine what's causing this. + min_size = min_size + 1 + + min_proportion = min_size*total_proportion/dock_pixels + + if new_proportion < min_proportion: + new_proportion = min_proportion + + prop_diff = new_proportion - pane.dock_proportion + + # borrow the space from our neighbor pane to the + # right or bottom (depending on orientation) + dock.panes[borrow_pane].dock_proportion -= prop_diff + pane.dock_proportion = new_proportion + + indxd = self._docks.index(dock) + indxp = self._panes.index(pane) + + self._docks[indxd] = dock + self._panes[indxp] = pane + + # repaint + self.Update() + self.Repaint(None) + + elif self._action == actionClickButton: + + self._hover_button = None + self._frame.ReleaseMouse() + self.UpdateButtonOnScreen(self._action_part, event) + + # make sure we're still over the item that was originally clicked + if self._action_part == self.HitTest(event.GetX(), event.GetY()): + # fire button-click event + e = AuiManagerEvent(wxEVT_AUI_PANEBUTTON) + e.SetPane(self._action_part.pane) + e.SetButton(self._action_part.button.button_id) + self.ProcessMgrEvent(e) + + elif self._action == actionClickCaption: + + self._frame.ReleaseMouse() + + elif self._action == actionDragAuiFloatingPane: + + self._frame.ReleaseMouse() + + elif self._action == actionDragToolbarPane: + + self._frame.ReleaseMouse() + + pane = self.GetPane(self._action_window) + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + # 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) + + dock_pane_count = len(dock.panes) + for ii in xrange(dock_pane_count): + dock.panes[ii].dock_pos = pane_positions[ii] + + pane.state &= ~AuiPaneInfo.actionPane + indx = self._panes.index(pane) + self._panes[indx] = pane + + self.Update() + + else: + + event.Skip() + + self._action = actionNone + self._last_mouse_move = wx.Point() # see comment in OnMotion() + + + def OnMotion(self, event): + + # sometimes when Update() is called from inside this method, + # a spurious mouse move event is generated this check will make + # sure that only real mouse moves will get anywhere in this method + # this appears to be a bug somewhere, and I don't know where the + # mouse move event is being generated. only verified on MSW + + mouse_pos = event.GetPosition() + if self._last_mouse_move == mouse_pos: + return + + self._last_mouse_move = mouse_pos + + if self._action == actionResize: + pos = self._action_part.rect.GetPosition() + if self._action_part.orientation == wx.HORIZONTAL: + pos.y = max(0, mouse_pos.y - self._action_offset.y) + pos.y = min(pos.y, self._frame.GetClientSize().y - self._action_part.rect.GetSize().y) + else: + pos.x = max(0, mouse_pos.x - self._action_offset.x) + pos.x = min(pos.x, self._frame.GetClientSize().x - self._action_part.rect.GetSize().x) + + mypos = self._frame.ClientToScreen(pos) + mysize = self._action_part.rect.GetSize() + rect = wx.Rect(mypos[0], mypos[1], mysize[0], mysize[1]) + + # is it the same as the old rectangle? + if self._action_hintrect == rect: + # heck, yes, no need to draw again, it will only bring about flicker + event.Skip() + return + + # otherwise draw the hint + + dc = wx.ScreenDC() + + if not self._action_hintrect.IsEmpty() and self._action_hintrect != rect: + DrawResizeHint(dc, self._action_hintrect) + + DrawResizeHint(dc, rect) + self._action_hintrect = rect + + elif self._action == actionClickCaption: + + drag_x_threshold = wx.SystemSettings_GetMetric(wx.SYS_DRAG_X) + drag_y_threshold = wx.SystemSettings_GetMetric(wx.SYS_DRAG_Y) + + # caption has been clicked. we need to check if the mouse + # is now being dragged. if it is, we need to change the + # mouse action to 'drag' + if abs(mouse_pos.x - self._action_start.x) > drag_x_threshold or \ + abs(mouse_pos.y - self._action_start.y) > drag_y_threshold: + + pane_info = self._action_part.pane + indx = self._panes.index(pane_info) + + if not pane_info.IsToolbar(): + + if self._flags & AUI_MGR_ALLOW_FLOATING and \ + pane_info.IsFloatable(): + + self._action = actionDragAuiFloatingPane + + # set initial float position + pt = self._frame.ClientToScreen(event.GetPosition()) + pane_info.floating_pos = wx.Point(pt.x - self._action_offset.x, + pt.y - self._action_offset.y) + # float the window + pane_info.Float() + self._panes[indx] = pane_info + + self.Update() + + self._action_window = pane_info.frame + + # 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_window.GetSize() + if frame_size.x <= self._action_offset.x: + self._action_offset.x = 30 + + else: + + self._action = actionDragToolbarPane + self._action_window = pane_info.window + + elif self._action == actionDragAuiFloatingPane: + + pt = self._frame.ClientToScreen(event.GetPosition()) + if self._action_window: + self._action_window.Move((pt.x - self._action_offset.x, + pt.y - self._action_offset.y)) + + elif self._action == actionDragToolbarPane: + + pane = self.GetPane(self._action_window) + if not pane.IsOk(): + raise "\nERROR: Pane Window Not Found" + + indx = self._panes.index(pane) + pane.state |= AuiPaneInfo.actionPane + + pt = event.GetPosition() + ret, pane = self.DoDrop(self._docks, self._panes, pane, pt, self._action_offset) + + if not ret: + return + + # if DoDrop() decided to float the pane, set up + # the floating pane's initial position + if pane.IsFloating(): + + pt = self._frame.ClientToScreen(event.GetPosition()) + pane.floating_pos = wx.Point(pt.x - self._action_offset.x, + pt.y - self._action_offset.y) + + self._panes[indx] = pane + + # this will do the actiual move operation + # in the case that the pane has been floated, + # this call will create the floating pane + # and do the reparenting + self.Update() + + # if the pane has been floated, change the mouse + # action actionDragAuiFloatingPane so that subsequent + # EVT_MOTION() events will move the floating pane + if pane.IsFloating(): + + pane.state &= ~AuiPaneInfo.actionPane + self._action = actionDragAuiFloatingPane + self._action_window = pane.frame + + self._panes[indx] = pane + + else: + + part = self.HitTest(event.GetX(), event.GetY()) + if part and part.type == DockUIPart.typePaneButton: + if part != self._hover_button: + # make the old button normal + if self._hover_button: + self.UpdateButtonOnScreen(self._hover_button, event) + + # mouse is over a button, so repaint the + # button in hover mode + self.UpdateButtonOnScreen(part, event) + self._hover_button = part + else: + if self._hover_button: + + self._hover_button = None + self.Repaint() + + else: + + event.Skip() + + + def OnLeaveWindow(self, event): + + if self._hover_button: + self._hover_button = None + self.Repaint() + + + def OnChildFocus(self, event): + + # 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) + if self.GetFlags() & AUI_MGR_ALLOW_ACTIVE_PANE: + if self.GetPane(event.GetWindow()).IsOk(): + self._panes = SetActivePane(self._panes, event.GetWindow()) + self._frame.Refresh() + + event.Skip() + + + def OnPaneButton(self, event): + """ + OnPaneButton() is an event handler that is called + when a pane button has been pressed. + """ + + pane = event.pane + indx = self._panes.index(pane) + + if event.button == AuiPaneInfo.buttonClose: + e = AuiManagerEvent(wxEVT_AUI_PANECLOSE) + e.SetPane(pane) + self.ProcessMgrEvent(e) + + if e.GetSkipped(): + pane.Hide() + self._panes[indx] = pane + self.Update() + + elif event.button == AuiPaneInfo.buttonPin: + + if self._flags & AUI_MGR_ALLOW_FLOATING and pane.IsFloatable(): + pane.Float() + + self._panes[indx] = pane + self.Update()