1# -*- coding: utf-8 -*-
2# --------------------------------------------------------------------------- #
3# AUI Library wxPython IMPLEMENTATION
4#
5# Original C++ Code From Kirix (wxAUI). You Can Find It At:
6#
7#    License: wxWidgets license
8#
9# http:#www.kirix.com/en/community/opensource/wxaui/about_wxaui.html
10#
11# Current wxAUI Version Tracked: wxWidgets 2.9.4 SVN HEAD
12#
13#
14# Python Code By:
15#
16# Andrea Gavana, @ 23 Dec 2005
17# Latest Revision: 17 Feb 2013, 21.00 GMT
18#
19# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
20# Write To Me At:
21#
22# andrea.gavana@gmail.com
23# andrea.gavana@maerskoil.com
24#
25# Or, Obviously, To The wxPython Mailing List!!!
26#
27# Tags:        phoenix-port, unittest, documented, py3-port
28#
29# End Of Comments
30# --------------------------------------------------------------------------- #
31
32"""
33Description
34===========
35
36`framemanager.py` is the central module of the AUI class framework.
37
38:class:`AuiManager` manages the panes associated with it for a particular :class:`wx.Frame`, using
39a pane's :class:`AuiPaneInfo` information to determine each pane's docking and floating
40behavior. AuiManager uses wxPython' sizer mechanism to plan the layout of each frame.
41It uses a replaceable dock art class to do all drawing, so all drawing is localized
42in one area, and may be customized depending on an application's specific needs.
43
44AuiManager works as follows: the programmer adds panes to the class, or makes
45changes to existing pane properties (dock position, floating state, show state, etc...).
46To apply these changes, AuiManager's :meth:`AuiManager.Update() <AuiManager.Update>` function is called. This batch
47processing can be used to avoid flicker, by modifying more than one pane at a time,
48and then "committing" all of the changes at once by calling `Update()`.
49
50Panes can be added quite easily::
51
52    text1 = wx.TextCtrl(self, -1)
53    text2 = wx.TextCtrl(self, -1)
54    self._mgr.AddPane(text1, AuiPaneInfo().Left().Caption("Pane Number One"))
55    self._mgr.AddPane(text2, AuiPaneInfo().Bottom().Caption("Pane Number Two"))
56
57    self._mgr.Update()
58
59
60Later on, the positions can be modified easily. The following will float an
61existing pane in a tool window::
62
63    self._mgr.GetPane(text1).Float()
64
65
66Layers, Rows and Directions, Positions
67======================================
68
69Inside AUI, the docking layout is figured out by checking several pane parameters.
70Four of these are important for determining where a pane will end up.
71
72**Direction** - Each docked pane has a direction, `Top`, `Bottom`, `Left`, `Right`, or `Center`.
73This is fairly self-explanatory. The pane will be placed in the location specified
74by this variable.
75
76**Position** - More than one pane can be placed inside of a "dock". Imagine two panes
77being docked on the left side of a window. One pane can be placed over another.
78In proportionally managed docks, the pane position indicates it's sequential position,
79starting with zero. So, in our scenario with two panes docked on the left side, the
80top pane in the dock would have position 0, and the second one would occupy position 1.
81
82**Row** - A row can allow for two docks to be placed next to each other. One of the most
83common places for this to happen is in the toolbar. Multiple toolbar rows are allowed,
84the first row being in row 0, and the second in row 1. Rows can also be used on
85vertically docked panes.
86
87**Layer** - A layer is akin to an onion. Layer 0 is the very center of the managed pane.
88Thus, if a pane is in layer 0, it will be closest to the center window (also sometimes
89known as the "content window"). Increasing layers "swallow up" all layers of a lower
90value. This can look very similar to multiple rows, but is different because all panes
91in a lower level yield to panes in higher levels. The best way to understand layers
92is by running the AUI sample (`AUI.py`).
93"""
94
95__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
96__date__ = "31 March 2009"
97
98
99import wx
100# just for isinstance
101try:
102    from time import time, process_time as clock
103except ImportError:
104    from time import time, clock
105import warnings
106
107import six
108
109from . import auibar
110from . import auibook
111
112from . import tabmdi
113from . import dockart
114from . import tabart
115
116from .aui_utilities import Clip, PaneCreateStippleBitmap, GetDockingImage, GetSlidingPoints
117
118from .aui_constants import *
119
120# Define this as a translation function
121_ = wx.GetTranslation
122
123_winxptheme = False
124if wx.Platform == "__WXMSW__":
125    try:
126        import winxptheme
127        _winxptheme = True
128    except ImportError:
129        pass
130
131# AUI Events
132wxEVT_AUI_PANE_BUTTON = wx.NewEventType()
133wxEVT_AUI_PANE_CLOSE = wx.NewEventType()
134wxEVT_AUI_PANE_MAXIMIZE = wx.NewEventType()
135wxEVT_AUI_PANE_RESTORE = wx.NewEventType()
136wxEVT_AUI_RENDER = wx.NewEventType()
137wxEVT_AUI_FIND_MANAGER = wx.NewEventType()
138wxEVT_AUI_PANE_MINIMIZE = wx.NewEventType()
139wxEVT_AUI_PANE_MIN_RESTORE = wx.NewEventType()
140wxEVT_AUI_PANE_FLOATING = wx.NewEventType()
141wxEVT_AUI_PANE_FLOATED = wx.NewEventType()
142wxEVT_AUI_PANE_DOCKING = wx.NewEventType()
143wxEVT_AUI_PANE_DOCKED = wx.NewEventType()
144wxEVT_AUI_PANE_ACTIVATED = wx.NewEventType()
145wxEVT_AUI_PERSPECTIVE_CHANGED = wx.NewEventType()
146
147EVT_AUI_PANE_BUTTON = wx.PyEventBinder(wxEVT_AUI_PANE_BUTTON, 0)
148""" Fires an event when the user left-clicks on a pane button. """
149EVT_AUI_PANE_CLOSE = wx.PyEventBinder(wxEVT_AUI_PANE_CLOSE, 0)
150""" A pane in `AuiManager` has been closed. """
151EVT_AUI_PANE_MAXIMIZE = wx.PyEventBinder(wxEVT_AUI_PANE_MAXIMIZE, 0)
152""" A pane in `AuiManager` has been maximized. """
153EVT_AUI_PANE_RESTORE = wx.PyEventBinder(wxEVT_AUI_PANE_RESTORE, 0)
154""" A pane in `AuiManager` has been restored from a maximized state. """
155EVT_AUI_RENDER = wx.PyEventBinder(wxEVT_AUI_RENDER, 0)
156""" Fires an event every time the AUI frame is being repainted. """
157EVT_AUI_FIND_MANAGER = wx.PyEventBinder(wxEVT_AUI_FIND_MANAGER, 0)
158""" Used to find which AUI manager is controlling a certain pane. """
159EVT_AUI_PANE_MINIMIZE = wx.PyEventBinder(wxEVT_AUI_PANE_MINIMIZE, 0)
160""" A pane in `AuiManager` has been minimized. """
161EVT_AUI_PANE_MIN_RESTORE = wx.PyEventBinder(wxEVT_AUI_PANE_MIN_RESTORE, 0)
162""" A pane in `AuiManager` has been restored from a minimized state. """
163EVT_AUI_PANE_FLOATING = wx.PyEventBinder(wxEVT_AUI_PANE_FLOATING, 0)
164""" A pane in `AuiManager` is about to be floated. """
165EVT_AUI_PANE_FLOATED = wx.PyEventBinder(wxEVT_AUI_PANE_FLOATED, 0)
166""" A pane in `AuiManager` has been floated. """
167EVT_AUI_PANE_DOCKING = wx.PyEventBinder(wxEVT_AUI_PANE_DOCKING, 0)
168""" A pane in `AuiManager` is about to be docked. """
169EVT_AUI_PANE_DOCKED = wx.PyEventBinder(wxEVT_AUI_PANE_DOCKED, 0)
170""" A pane in `AuiManager` has been docked. """
171EVT_AUI_PANE_ACTIVATED = wx.PyEventBinder(wxEVT_AUI_PANE_ACTIVATED, 0)
172""" A pane in `AuiManager` has been activated. """
173EVT_AUI_PERSPECTIVE_CHANGED = wx.PyEventBinder(wxEVT_AUI_PERSPECTIVE_CHANGED, 0)
174""" The layout in `AuiManager` has been changed. """
175
176# ---------------------------------------------------------------------------- #
177
178class AuiDockInfo(object):
179    """ A class to store all properties of a dock. """
180
181    def __init__(self):
182        """
183        Default class constructor.
184        Used internally, do not call it in your code!
185        """
186
187        object.__init__(self)
188
189        self.dock_direction = 0
190        self.dock_layer = 0
191        self.dock_row = 0
192        self.size = 0
193        self.min_size = 0
194        self.resizable = True
195        self.fixed = False
196        self.toolbar = False
197        self.rect = wx.Rect()
198        self.panes = []
199
200
201    def IsOk(self):
202        """
203        Returns whether a dock is valid or not.
204
205        In order to be valid, a dock needs to have a non-zero `dock_direction`.
206        """
207
208        return self.dock_direction != 0
209
210
211    def IsHorizontal(self):
212        """ Returns whether the dock is horizontal or not. """
213
214        return self.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]
215
216
217    def IsVertical(self):
218        """ Returns whether the dock is vertical or not. """
219
220        return self.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT, AUI_DOCK_CENTER]
221
222
223# ---------------------------------------------------------------------------- #
224
225class AuiDockingGuideInfo(object):
226    """ A class which holds information about VS2005 docking guide windows. """
227
228    def __init__(self, other=None):
229        """
230        Default class constructor.
231        Used internally, do not call it in your code!
232
233        :param `other`: another instance of :class:`AuiDockingGuideInfo`.
234        """
235
236        if other:
237            self.Assign(other)
238        else:
239            # window representing the docking target
240            self.host = None
241            # dock direction (top, bottom, left, right, center)
242            self.dock_direction = AUI_DOCK_NONE
243
244
245    def Assign(self, other):
246        """
247        Assigns the properties of the `other` :class:`AuiDockingGuideInfo` to `self`.
248
249        :param `other`: another instance of :class:`AuiDockingGuideInfo`.
250        """
251
252        self.host = other.host
253        self.dock_direction = other.dock_direction
254
255
256    def Host(self, h):
257        """
258        Hosts a docking guide window.
259
260        :param `h`: an instance of :class:`AuiDockingGuideWindow` or :class:`AuiDockingHintWindow`.
261        """
262
263        self.host = h
264        return self
265
266
267    def Left(self):
268        """ Sets the guide window to left docking. """
269
270        self.dock_direction = AUI_DOCK_LEFT
271        return self
272
273
274    def Right(self):
275        """ Sets the guide window to right docking. """
276
277        self.dock_direction = AUI_DOCK_RIGHT
278        return self
279
280
281    def Top(self):
282        """ Sets the guide window to top docking. """
283
284        self.dock_direction = AUI_DOCK_TOP
285        return self
286
287
288    def Bottom(self):
289        """ Sets the guide window to bottom docking. """
290
291        self.dock_direction = AUI_DOCK_BOTTOM
292        return self
293
294
295    def Center(self):
296        """ Sets the guide window to center docking. """
297
298        self.dock_direction = AUI_DOCK_CENTER
299        return self
300
301
302    def Centre(self):
303        """ Sets the guide window to centre docking. """
304
305        self.dock_direction = AUI_DOCK_CENTRE
306        return self
307
308
309# ---------------------------------------------------------------------------- #
310
311class AuiDockUIPart(object):
312    """ A class which holds attributes for a UI part in the interface. """
313
314    typeCaption = 0
315    typeGripper = 1
316    typeDock = 2
317    typeDockSizer = 3
318    typePane = 4
319    typePaneSizer = 5
320    typeBackground = 6
321    typePaneBorder = 7
322    typePaneButton = 8
323
324    def __init__(self):
325        """
326        Default class constructor.
327        Used internally, do not call it in your code!
328        """
329
330        self.orientation = wx.VERTICAL
331        self.type = 0
332        self.rect = wx.Rect()
333
334
335# ---------------------------------------------------------------------------- #
336
337class AuiPaneButton(object):
338    """ A simple class which describes the caption pane button attributes. """
339
340    def __init__(self, button_id):
341        """
342        Default class constructor.
343        Used internally, do not call it in your code!
344
345        :param integer `button_id`: the pane button identifier.
346        """
347
348        self.button_id = button_id
349
350
351# ---------------------------------------------------------------------------- #
352
353# event declarations/classes
354
355class AuiManagerEvent(wx.PyCommandEvent):
356    """ A specialized command event class for events sent by :class:`AuiManager`. """
357
358    def __init__(self, eventType, id=1):
359        """
360        Default class constructor.
361
362        :param integer `eventType`: the event kind;
363        :param integer `id`: the event identification number.
364        """
365
366        wx.PyCommandEvent.__init__(self, eventType, id)
367
368        self.manager = None
369        self.pane = None
370        self.button = 0
371        self.veto_flag = False
372        self.canveto_flag = True
373        self.dc = None
374
375
376    def SetManager(self, mgr):
377        """
378        Associates a :class:`AuiManager` to the current event.
379
380        :param `mgr`: an instance of :class:`AuiManager`.
381        """
382
383        self.manager = mgr
384
385
386    def SetDC(self, pdc):
387        """
388        Associates a :class:`wx.DC` device context to this event.
389
390        :param `pdc`: a :class:`wx.DC` device context object.
391        """
392
393        self.dc = pdc
394
395
396    def SetPane(self, p):
397        """
398        Associates a :class:`AuiPaneInfo` instance to this event.
399
400        :param `p`: a :class:`AuiPaneInfo` instance.
401        """
402
403        self.pane = p
404
405
406    def SetButton(self, b):
407        """
408        Associates a :class:`AuiPaneButton` instance to this event.
409
410        :param `b`: a :class:`AuiPaneButton` instance.
411        """
412
413        self.button = b
414
415
416    def GetManager(self):
417        """ Returns the associated :class:`AuiManager` (if any). """
418
419        return self.manager
420
421
422    def GetDC(self):
423        """ Returns the associated :class:`wx.DC` device context (if any). """
424
425        return self.dc
426
427
428    def GetPane(self):
429        """ Returns the associated :class:`AuiPaneInfo` structure (if any). """
430
431        return self.pane
432
433
434    def GetButton(self):
435        """ Returns the associated :class:`AuiPaneButton` instance (if any). """
436
437        return self.button
438
439
440    def Veto(self, veto=True):
441        """
442        Prevents the change announced by this event from happening.
443
444        It is in general a good idea to notify the user about the reasons for
445        vetoing the change because otherwise the applications behaviour (which
446        just refuses to do what the user wants) might be quite surprising.
447
448        :param bool `veto`: ``True`` to veto the event, ``False`` otherwise.
449        """
450
451        self.veto_flag = veto
452
453
454    def GetVeto(self):
455        """ Returns whether the event has been vetoed or not. """
456
457        return self.veto_flag
458
459
460    def SetCanVeto(self, can_veto):
461        """
462        Sets whether the event can be vetoed or not.
463
464        :param bool `can_veto`: ``True`` if the event can be vetoed, ``False`` otherwise.
465        """
466
467        self.canveto_flag = can_veto
468
469
470    def CanVeto(self):
471        """ Returns whether the event can be vetoed and has been vetoed. """
472
473        return  self.canveto_flag and self.veto_flag
474
475
476# ---------------------------------------------------------------------------- #
477
478class AuiPaneInfo(object):
479    """
480    AuiPaneInfo specifies all the parameters for a pane. These parameters specify where
481    the pane is on the screen, whether it is docked or floating, or hidden. In addition,
482    these parameters specify the pane's docked position, floating position, preferred
483    size, minimum size, caption text among many other parameters.
484    """
485
486    optionFloating         = 2**0
487    optionHidden           = 2**1
488    optionLeftDockable     = 2**2
489    optionRightDockable    = 2**3
490    optionTopDockable      = 2**4
491    optionBottomDockable   = 2**5
492    optionFloatable        = 2**6
493    optionMovable          = 2**7
494    optionResizable        = 2**8
495    optionPaneBorder       = 2**9
496    optionCaption          = 2**10
497    optionGripper          = 2**11
498    optionDestroyOnClose   = 2**12
499    optionToolbar          = 2**13
500    optionActive           = 2**14
501    optionGripperTop       = 2**15
502    optionMaximized        = 2**16
503    optionDockFixed        = 2**17
504    optionNotebookDockable = 2**18
505    optionMinimized        = 2**19
506    optionLeftSnapped      = 2**20
507    optionRightSnapped     = 2**21
508    optionTopSnapped       = 2**22
509    optionBottomSnapped    = 2**23
510    optionFlyOut           = 2**24
511    optionCaptionLeft      = 2**25
512
513    buttonClose            = 2**26
514    buttonMaximize         = 2**27
515    buttonMinimize         = 2**28
516    buttonPin              = 2**29
517
518    buttonCustom1          = 2**30
519    buttonCustom2          = 2**31
520    buttonCustom3          = 2**32
521
522    savedHiddenState       = 2**33    # used internally
523    actionPane             = 2**34    # used internally
524    wasMaximized           = 2**35    # used internally
525    needsRestore           = 2**36    # used internally
526
527
528    def __init__(self):
529        """ Default class constructor. """
530
531        self.window = None
532        self.frame = None
533        self.state = 0
534        self.dock_direction = AUI_DOCK_LEFT
535        self.dock_layer = 0
536        self.dock_row = 0
537        self.dock_pos = 0
538        self.minimize_mode = AUI_MINIMIZE_POS_SMART
539        self.floating_pos = wx.Point(-1, -1)
540        self.floating_size = wx.Size(-1, -1)
541        self.best_size = wx.Size(-1, -1)
542        self.min_size = wx.Size(-1, -1)
543        self.max_size = wx.Size(-1, -1)
544        self.dock_proportion = 0
545        self.caption = ""
546        self.buttons = []
547        self.name = ""
548        self.icon = wx.NullIcon
549        self.rect = wx.Rect()
550        self.notebook_id = -1
551        self.transparent = 255
552        self.needsTransparency = False
553        self.previousDockPos = None
554        self.previousDockSize = 0
555        self.snapped = 0
556        self.minimize_target = None
557
558        self.DefaultPane()
559
560
561    def dock_direction_get(self):
562        """
563        Getter for the `dock_direction`.
564
565        :see: :meth:`~AuiPaneInfo.dock_direction_set` for a set of valid docking directions.
566        """
567
568        if self.IsMaximized():
569            return AUI_DOCK_CENTER
570        else:
571            return self._dock_direction
572
573
574    def dock_direction_set(self, value):
575        """
576        Setter for the `dock_direction`.
577
578        :param integer `value`: the docking direction. This can be one of the following bits:
579
580        ============================ ======= =============================================
581        Dock Flag                     Value  Description
582        ============================ ======= =============================================
583        ``AUI_DOCK_NONE``                  0 No docking direction.
584        ``AUI_DOCK_TOP``                   1 Top docking direction.
585        ``AUI_DOCK_RIGHT``                 2 Right docking direction.
586        ``AUI_DOCK_BOTTOM``                3 Bottom docking direction.
587        ``AUI_DOCK_LEFT``                  4 Left docking direction.
588        ``AUI_DOCK_CENTER``                5 Center docking direction.
589        ``AUI_DOCK_CENTRE``                5 Centre docking direction.
590        ``AUI_DOCK_NOTEBOOK_PAGE``         6 Automatic AuiNotebooks docking style.
591        ============================ ======= =============================================
592
593        """
594
595        self._dock_direction = value
596
597    dock_direction = property(dock_direction_get, dock_direction_set)
598
599    def IsOk(self):
600        """
601        Returns ``True`` if the :class:`AuiPaneInfo` structure is valid.
602
603        :note: A pane structure is valid if it has an associated window.
604        """
605
606        return self.window != None
607
608
609    def IsMaximized(self):
610        """ Returns ``True`` if the pane is maximized. """
611
612        return self.HasFlag(self.optionMaximized)
613
614
615    def IsMinimized(self):
616        """ Returns ``True`` if the pane is minimized. """
617
618        return self.HasFlag(self.optionMinimized)
619
620
621    def IsFixed(self):
622        """ Returns ``True`` if the pane cannot be resized. """
623
624        return not self.HasFlag(self.optionResizable)
625
626
627    def IsResizeable(self):
628        """ Returns ``True`` if the pane can be resized. """
629
630        return self.HasFlag(self.optionResizable)
631
632
633    def IsShown(self):
634        """ Returns ``True`` if the pane is currently shown. """
635
636        return not self.HasFlag(self.optionHidden)
637
638
639    def IsFloating(self):
640        """ Returns ``True`` if the pane is floating. """
641
642        return self.HasFlag(self.optionFloating)
643
644
645    def IsDocked(self):
646        """ Returns ``True`` if the pane is docked. """
647
648        return not self.HasFlag(self.optionFloating)
649
650
651    def IsToolbar(self):
652        """ Returns ``True`` if the pane contains a toolbar. """
653
654        return self.HasFlag(self.optionToolbar)
655
656
657    def IsTopDockable(self):
658        """
659        Returns ``True`` if the pane can be docked at the top
660        of the managed frame.
661        """
662
663        return self.HasFlag(self.optionTopDockable)
664
665
666    def IsBottomDockable(self):
667        """
668        Returns ``True`` if the pane can be docked at the bottom
669        of the managed frame.
670        """
671
672        return self.HasFlag(self.optionBottomDockable)
673
674
675    def IsLeftDockable(self):
676        """
677        Returns ``True`` if the pane can be docked at the left
678        of the managed frame.
679        """
680
681        return self.HasFlag(self.optionLeftDockable)
682
683
684    def IsRightDockable(self):
685        """
686        Returns ``True`` if the pane can be docked at the right
687        of the managed frame.
688        """
689
690        return self.HasFlag(self.optionRightDockable)
691
692
693    def IsDockable(self):
694        """ Returns ``True`` if the pane can be docked. """
695
696        return self.IsTopDockable() or self.IsBottomDockable() or self.IsLeftDockable() or \
697               self.IsRightDockable() or self.IsNotebookDockable()
698
699
700    def IsFloatable(self):
701        """
702        Returns ``True`` if the pane can be undocked and displayed as a
703        floating window.
704        """
705
706        return self.HasFlag(self.optionFloatable)
707
708
709    def IsMovable(self):
710        """
711        Returns ``True`` if the docked frame can be undocked or moved to
712        another dock position.
713        """
714
715        return self.HasFlag(self.optionMovable)
716
717
718    def IsDestroyOnClose(self):
719        """
720        Returns ``True`` if the pane should be destroyed when it is closed.
721
722        Normally a pane is simply hidden when the close button is clicked. Calling :meth:`~AuiPaneInfo.DestroyOnClose`
723        with a ``True`` input parameter will cause the window to be destroyed when the user clicks
724        the pane's close button.
725        """
726
727        return self.HasFlag(self.optionDestroyOnClose)
728
729
730    def IsNotebookDockable(self):
731        """
732        Returns ``True`` if a pane can be docked on top to another to create a
733        :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
734        """
735
736        return self.HasFlag(self.optionNotebookDockable)
737
738
739    def IsTopSnappable(self):
740        """ Returns ``True`` if the pane can be snapped at the top of the managed frame. """
741
742        return self.HasFlag(self.optionTopSnapped)
743
744
745    def IsBottomSnappable(self):
746        """ Returns ``True`` if the pane can be snapped at the bottom of the managed frame. """
747
748        return self.HasFlag(self.optionBottomSnapped)
749
750
751    def IsLeftSnappable(self):
752        """ Returns ``True`` if the pane can be snapped on the left of the managed frame. """
753
754        return self.HasFlag(self.optionLeftSnapped)
755
756
757    def IsRightSnappable(self):
758        """ Returns ``True`` if the pane can be snapped on the right of the managed frame. """
759
760        return self.HasFlag(self.optionRightSnapped)
761
762
763    def IsSnappable(self):
764        """ Returns ``True`` if the pane can be snapped. """
765
766        return self.IsTopSnappable() or self.IsBottomSnappable() or self.IsLeftSnappable() or \
767               self.IsRightSnappable()
768
769
770    def IsFlyOut(self):
771        """ Returns ``True`` if the floating pane has a "fly-out" effect. """
772
773        return self.HasFlag(self.optionFlyOut)
774
775
776    def HasCaption(self):
777        """ Returns ``True`` if the pane displays a caption. """
778
779        return self.HasFlag(self.optionCaption)
780
781
782    def HasCaptionLeft(self):
783        """ Returns ``True`` if the pane displays a caption on the left (rotated by 90 degrees). """
784
785        return self.HasFlag(self.optionCaptionLeft)
786
787
788    def HasGripper(self):
789        """ Returns ``True`` if the pane displays a gripper. """
790
791        return self.HasFlag(self.optionGripper)
792
793
794    def HasBorder(self):
795        """ Returns ``True`` if the pane displays a border. """
796
797        return self.HasFlag(self.optionPaneBorder)
798
799
800    def HasCloseButton(self):
801        """ Returns ``True`` if the pane displays a button to close the pane. """
802
803        return self.HasFlag(self.buttonClose)
804
805
806    def HasMaximizeButton(self):
807        """ Returns ``True`` if the pane displays a button to maximize the pane. """
808
809        return self.HasFlag(self.buttonMaximize)
810
811
812    def HasMinimizeButton(self):
813        """ Returns ``True`` if the pane displays a button to minimize the pane. """
814
815        return self.HasFlag(self.buttonMinimize)
816
817
818    def GetMinimizeMode(self):
819        """
820        Returns the minimization style for this pane.
821
822        Possible return values are:
823
824        ============================== ========= ==============================
825        Minimize Mode Flag             Hex Value Description
826        ============================== ========= ==============================
827        ``AUI_MINIMIZE_POS_SMART``          0x01 Minimizes the pane on the closest tool bar
828        ``AUI_MINIMIZE_POS_TOP``            0x02 Minimizes the pane on the top tool bar
829        ``AUI_MINIMIZE_POS_LEFT``           0x03 Minimizes the pane on its left tool bar
830        ``AUI_MINIMIZE_POS_RIGHT``          0x04 Minimizes the pane on its right tool bar
831        ``AUI_MINIMIZE_POS_BOTTOM``         0x05 Minimizes the pane on its bottom tool bar
832        ``AUI_MINIMIZE_POS_TOOLBAR``        0x06 Minimizes the pane on a target :class:`~wx.lib.agw.aui.auibar.AuiToolBar`
833        ``AUI_MINIMIZE_POS_MASK``           0x17 Mask to filter the position flags
834        ``AUI_MINIMIZE_CAPT_HIDE``           0x0 Hides the caption of the minimized pane
835        ``AUI_MINIMIZE_CAPT_SMART``         0x08 Displays the caption in the best rotation (horizontal or clockwise)
836        ``AUI_MINIMIZE_CAPT_HORZ``          0x10 Displays the caption horizontally
837        ``AUI_MINIMIZE_CAPT_MASK``          0x18 Mask to filter the caption flags
838        ============================== ========= ==============================
839
840        The flags can be filtered with the following masks:
841
842        ============================== ========= ==============================
843        Minimize Mask Flag             Hex Value Description
844        ============================== ========= ==============================
845        ``AUI_MINIMIZE_POS_MASK``           0x17 Filters the position flags
846        ``AUI_MINIMIZE_CAPT_MASK``          0x18 Filters the caption flags
847        ============================== ========= ==============================
848
849        """
850
851        return self.minimize_mode
852
853
854    def HasPinButton(self):
855        """ Returns ``True`` if the pane displays a button to float the pane. """
856
857        return self.HasFlag(self.buttonPin)
858
859
860    def HasGripperTop(self):
861        """ Returns ``True`` if the pane displays a gripper at the top. """
862
863        return self.HasFlag(self.optionGripperTop)
864
865
866    def Window(self, w):
867        """
868        Associate a :class:`wx.Window` derived window to this pane.
869
870        This normally does not need to be specified, as the window pointer is
871        automatically assigned to the :class:`AuiPaneInfo` structure as soon as it is
872        added to the manager.
873
874        :param `w`: a :class:`wx.Window` derived window.
875        """
876
877        self.window = w
878        return self
879
880
881    def Name(self, name):
882        """
883        Sets the name of the pane so it can be referenced in lookup functions.
884
885        If a name is not specified by the user, a random name is assigned to the pane
886        when it is added to the manager.
887
888        :param `name`: a string specifying the pane name.
889
890        .. warning::
891
892           If you are using :meth:`AuiManager.SavePerspective` and :meth:`AuiManager.LoadPerspective`,
893           you will have to specify a name for your pane using :meth:`~AuiPaneInfo.Name`, as perspectives
894           containing randomly generated names can not be properly restored.
895        """
896
897        self.name = name
898        return self
899
900
901    def Caption(self, caption):
902        """
903        Sets the caption of the pane.
904
905        :param string `caption`: a string specifying the pane caption.
906        """
907
908        self.caption = caption
909        return self
910
911
912    def Left(self):
913        """
914        Sets the pane dock position to the left side of the frame.
915
916        :note: This is the same thing as calling :meth:`~AuiPaneInfo.Direction` with ``AUI_DOCK_LEFT`` as
917         parameter.
918        """
919
920        self.dock_direction = AUI_DOCK_LEFT
921        return self
922
923
924    def Right(self):
925        """
926        Sets the pane dock position to the right side of the frame.
927
928        :note: This is the same thing as calling :meth:`~AuiPaneInfo.Direction` with ``AUI_DOCK_RIGHT`` as
929         parameter.
930        """
931
932        self.dock_direction = AUI_DOCK_RIGHT
933        return self
934
935
936    def Top(self):
937        """
938        Sets the pane dock position to the top of the frame.
939
940        :note: This is the same thing as calling :meth:`~AuiPaneInfo.Direction` with ``AUI_DOCK_TOP`` as
941         parameter.
942        """
943
944        self.dock_direction = AUI_DOCK_TOP
945        return self
946
947
948    def Bottom(self):
949        """
950        Sets the pane dock position to the bottom of the frame.
951
952        :note: This is the same thing as calling :meth:`~AuiPaneInfo.Direction` with ``AUI_DOCK_BOTTOM`` as
953         parameter.
954        """
955
956        self.dock_direction = AUI_DOCK_BOTTOM
957        return self
958
959
960    def Center(self):
961        """
962        Sets the pane to the center position of the frame.
963
964        The centre pane is the space in the middle after all border panes (left, top,
965        right, bottom) are subtracted from the layout.
966
967        :note: This is the same thing as calling :meth:`~AuiPaneInfo.Direction` with ``AUI_DOCK_CENTER`` as
968         parameter.
969        """
970
971        self.dock_direction = AUI_DOCK_CENTER
972        return self
973
974
975    def Centre(self):
976        """
977        Sets the pane to the center position of the frame.
978
979        The centre pane is the space in the middle after all border panes (left, top,
980        right, bottom) are subtracted from the layout.
981
982        :note: This is the same thing as calling :meth:`~AuiPaneInfo.Direction` with ``AUI_DOCK_CENTRE`` as
983         parameter.
984        """
985
986        self.dock_direction = AUI_DOCK_CENTRE
987        return self
988
989
990    def Direction(self, direction):
991        """
992        Determines the direction of the docked pane. It is functionally the
993        same as calling :meth:`Left`, :meth:`Right`, :meth:`Top` or :meth:`Bottom`,
994        except that docking direction may be specified programmatically via the parameter `direction`.
995
996        :param integer `direction`: the direction of the docked pane.
997
998        :see: :meth:`dock_direction_set` for a list of valid docking directions.
999        """
1000
1001        self.dock_direction = direction
1002        return self
1003
1004
1005    def Layer(self, layer):
1006        """
1007        Determines the layer of the docked pane.
1008
1009        The dock layer is similar to an onion, the inner-most layer being layer 0. Each
1010        shell moving in the outward direction has a higher layer number. This allows for
1011        more complex docking layout formation.
1012
1013        :param integer `layer`: the layer of the docked pane.
1014        """
1015
1016        self.dock_layer = layer
1017        return self
1018
1019
1020    def Row(self, row):
1021        """
1022        Determines the row of the docked pane.
1023
1024        :param integer `row`: the row of the docked pane.
1025        """
1026
1027        self.dock_row = row
1028        return self
1029
1030
1031    def Position(self, pos):
1032        """
1033        Determines the position of the docked pane.
1034
1035        :param integer `pos`: the position of the docked pane.
1036        """
1037
1038        self.dock_pos = pos
1039        return self
1040
1041
1042    def MinSize(self, arg1=None, arg2=None):
1043        """
1044        Sets the minimum size of the pane.
1045
1046        This method is split in 2 versions depending on the input type. If `arg1` is
1047        a :class:`wx.Size` object, then :meth:`~AuiPaneInfo.MinSize1` is called. Otherwise, :meth:`~AuiPaneInfo.MinSize2` is called.
1048
1049        :param `arg1`: a :class:`wx.Size` object, a (x, y) tuple or or a `x` coordinate.
1050        :param `arg2`: a `y` coordinate (only if `arg1` is a `x` coordinate, otherwise unused).
1051        """
1052
1053        if isinstance(arg1, wx.Size):
1054            ret = self.MinSize1(arg1)
1055        elif isinstance(arg1, tuple):
1056            ret = self.MinSize1(wx.Size(*arg1))
1057        elif isinstance(arg1, six.integer_types) and arg2 is not None:
1058            ret = self.MinSize2(arg1, arg2)
1059        else:
1060            raise Exception("Invalid argument passed to `MinSize`: arg1=%s, arg2=%s"%(repr(arg1), repr(arg2)))
1061
1062        return ret
1063
1064
1065    def MinSize1(self, size):
1066        """
1067        Sets the minimum size of the pane.
1068
1069        :see: :meth:`MinSize` for an explanation of input parameters.
1070        """
1071        self.min_size = size
1072        return self
1073
1074
1075    def MinSize2(self, x, y):
1076        """
1077        Sets the minimum size of the pane.
1078
1079        :see: :meth:`MinSize` for an explanation of input parameters.
1080        """
1081
1082        self.min_size = wx.Size(x, y)
1083        return self
1084
1085
1086    def MaxSize(self, arg1=None, arg2=None):
1087        """
1088        Sets the maximum size of the pane.
1089
1090        This method is split in 2 versions depending on the input type. If `arg1` is
1091        a :class:`wx.Size` object, then :meth:`~AuiPaneInfo.MaxSize1` is called. Otherwise, :meth:`~AuiPaneInfo.MaxSize2` is called.
1092
1093        :param `arg1`: a :class:`wx.Size` object, a (x, y) tuple or a `x` coordinate.
1094        :param `arg2`: a `y` coordinate (only if `arg1` is a `x` coordinate, otherwise unused).
1095        """
1096
1097        if isinstance(arg1, wx.Size):
1098            ret = self.MaxSize1(arg1)
1099        elif isinstance(arg1, tuple):
1100            ret = self.MaxSize1(wx.Size(*arg1))
1101        elif isinstance(arg1, six.integer_types) and arg2 is not None:
1102            ret = self.MaxSize2(arg1, arg2)
1103        else:
1104            raise Exception("Invalid argument passed to `MaxSize`: arg1=%s, arg2=%s"%(repr(arg1), repr(arg2)))
1105
1106        return ret
1107
1108
1109    def MaxSize1(self, size):
1110        """
1111        Sets the maximum size of the pane.
1112
1113        :see: :meth:`MaxSize` for an explanation of input parameters.
1114        """
1115
1116        self.max_size = size
1117        return self
1118
1119
1120    def MaxSize2(self, x, y):
1121        """
1122        Sets the maximum size of the pane.
1123
1124        :see: :meth:`MaxSize` for an explanation of input parameters.
1125        """
1126
1127        self.max_size.Set(x,y)
1128        return self
1129
1130
1131    def BestSize(self, arg1=None, arg2=None):
1132        """
1133        Sets the ideal size for the pane. The docking manager will attempt to use
1134        this size as much as possible when docking or floating the pane.
1135
1136        This method is split in 2 versions depending on the input type. If `arg1` is
1137        a :class:`wx.Size` object, then :meth:`BestSize1` is called. Otherwise, :meth:`BestSize2` is called.
1138
1139        :param `arg1`: a :class:`wx.Size` object, a (x, y) tuple or a `x` coordinate.
1140        :param `arg2`: a `y` coordinate (only if `arg1` is a `x` coordinate, otherwise unused).
1141        """
1142
1143        if isinstance(arg1, wx.Size):
1144            ret = self.BestSize1(arg1)
1145        elif isinstance(arg1, tuple):
1146            ret = self.BestSize1(wx.Size(*arg1))
1147        elif isinstance(arg1, six.integer_types) and arg2 is not None:
1148            ret = self.BestSize2(arg1, arg2)
1149        else:
1150            raise Exception("Invalid argument passed to `BestSize`: arg1=%s, arg2=%s"%(repr(arg1), repr(arg2)))
1151
1152        return ret
1153
1154
1155    def BestSize1(self, size):
1156        """
1157        Sets the best size of the pane.
1158
1159        :see: :meth:`BestSize` for an explanation of input parameters.
1160        """
1161
1162        self.best_size = size
1163        return self
1164
1165
1166    def BestSize2(self, x, y):
1167        """
1168        Sets the best size of the pane.
1169
1170        :see: :meth:`BestSize` for an explanation of input parameters.
1171        """
1172
1173        self.best_size.Set(x,y)
1174        return self
1175
1176
1177    def FloatingPosition(self, pos):
1178        """
1179        Sets the position of the floating pane.
1180
1181        :param `pos`: a :class:`wx.Point` or a tuple indicating the pane floating position.
1182        """
1183
1184        self.floating_pos = wx.Point(*pos)
1185        return self
1186
1187
1188    def FloatingSize(self, size):
1189        """
1190        Sets the size of the floating pane.
1191
1192        :param `size`: a :class:`wx.Size` or a tuple indicating the pane floating size.
1193        """
1194
1195        self.floating_size = wx.Size(*size)
1196        return self
1197
1198
1199    def Maximize(self):
1200        """ Makes the pane take up the full area."""
1201
1202        return self.SetFlag(self.optionMaximized, True)
1203
1204
1205    def Minimize(self):
1206        """
1207        Makes the pane minimized in a :class:`~wx.lib.agw.aui.auibar.AuiToolBar`.
1208
1209        Clicking on the minimize button causes a new :class:`~wx.lib.agw.aui.auibar.AuiToolBar` to be created
1210        and added to the frame manager, (currently the implementation is such that
1211        panes at West will have a toolbar at the right, panes at South will have
1212        toolbars at the bottom etc...) and the pane is hidden in the manager.
1213
1214        Clicking on the restore button on the newly created toolbar will result in the
1215        toolbar being removed and the original pane being restored.
1216        """
1217
1218        return self.SetFlag(self.optionMinimized, True)
1219
1220
1221    def MinimizeMode(self, mode):
1222        """
1223        Sets the expected minimized mode if the minimize button is visible.
1224
1225        :param integer `mode`: the minimized pane can have a specific position in the work space:
1226
1227        ============================== ========= ==============================
1228        Minimize Mode Flag             Hex Value Description
1229        ============================== ========= ==============================
1230        ``AUI_MINIMIZE_POS_SMART``          0x01 Minimizes the pane on the closest tool bar
1231        ``AUI_MINIMIZE_POS_TOP``            0x02 Minimizes the pane on the top tool bar
1232        ``AUI_MINIMIZE_POS_LEFT``           0x03 Minimizes the pane on its left tool bar
1233        ``AUI_MINIMIZE_POS_RIGHT``          0x04 Minimizes the pane on its right tool bar
1234        ``AUI_MINIMIZE_POS_BOTTOM``         0x05 Minimizes the pane on its bottom tool bar
1235        ``AUI_MINIMIZE_POS_TOOLBAR``        0x06 Minimizes the pane on a target :class:`~wx.lib.agw.aui.auibar.AuiToolBar`
1236        ============================== ========= ==============================
1237
1238        The caption of the minimized pane can be displayed in different modes:
1239
1240        ============================== ========= ==============================
1241        Caption Mode Flag              Hex Value Description
1242        ============================== ========= ==============================
1243        ``AUI_MINIMIZE_CAPT_HIDE``           0x0 Hides the caption of the minimized pane
1244        ``AUI_MINIMIZE_CAPT_SMART``         0x08 Displays the caption in the best rotation (horizontal in the top and in
1245                                                 the bottom tool bar or clockwise in the right and in the left tool bar)
1246        ``AUI_MINIMIZE_CAPT_HORZ``          0x10 Displays the caption horizontally
1247        ============================== ========= ==============================
1248
1249        .. note::
1250
1251           In order to use the ``AUI_MINIMIZE_POS_TOOLBAR`` flag, the instance of :class:`AuiManager`
1252           you pass as an input for :meth:`MinimizeTarget` **must** have a real name and not the randomly
1253           generated one. Remember to set the :meth:`Name` property of the toolbar pane before calling this method.
1254
1255        """
1256
1257        self.minimize_mode = mode
1258        return self
1259
1260
1261    def MinimizeTarget(self, toolbarPane):
1262        """
1263        Minimizes the panes using a :class:`AuiPaneInfo` as a target. As :class:`AuiPaneInfo` properties
1264        need to be copied back and forth every time the perspective has changed, we
1265        only store the toobar **name**.
1266
1267        :param `toolbarPane`: an instance of :class:`AuiPaneInfo`, containing a :class:`~wx.lib.agw.aui.auibar.AuiToolBar`.
1268
1269        .. note::
1270
1271           In order to use this functionality (and with the ``AUI_MINIMIZE_POS_TOOLBAR``
1272           flag set), the instance of :class:`AuiPaneInfo` you pass as an input **must** have a real
1273           name and not the randomly generated one. Remember to set the :meth:`Name` property of
1274           the toolbar pane before calling this method.
1275
1276        """
1277
1278        self.minimize_target = toolbarPane.name
1279        return self
1280
1281
1282    def Restore(self):
1283        """ Is the reverse of :meth:`Maximize` and :meth:`Minimize`."""
1284
1285        return self.SetFlag(self.optionMaximized | self.optionMinimized, False)
1286
1287
1288    def Fixed(self):
1289        """
1290        Forces a pane to be fixed size so that it cannot be resized.
1291        After calling :meth:`Fixed`, :meth:`IsFixed` will return ``True``.
1292        """
1293
1294        return self.SetFlag(self.optionResizable, False)
1295
1296
1297    def Resizable(self, resizable=True):
1298        """
1299        Allows a pane to be resizable if `resizable` is ``True``, and forces
1300        it to be a fixed size if `resizeable` is ``False``.
1301
1302        If `resizable` is ``False``, this is simply an antonym for :meth:`Fixed`.
1303
1304        :param bool `resizable`: whether the pane will be resizeable or not.
1305        """
1306
1307        return self.SetFlag(self.optionResizable, resizable)
1308
1309
1310    def Transparent(self, alpha):
1311        """
1312        Makes the pane transparent when floating.
1313
1314        :param integer `alpha`: a value between 0 and 255 for pane transparency.
1315        """
1316
1317        if alpha < 0 or alpha > 255:
1318            raise Exception("Invalid transparency value (%s)"%repr(alpha))
1319
1320        self.transparent = alpha
1321        self.needsTransparency = True
1322
1323
1324    def Dock(self):
1325        """ Indicates that a pane should be docked. It is the opposite of :meth:`Float`. """
1326
1327        if self.IsNotebookPage():
1328            self.notebook_id = -1
1329            self.dock_direction = AUI_DOCK_NONE
1330
1331        return self.SetFlag(self.optionFloating, False)
1332
1333
1334    def Float(self):
1335        """ Indicates that a pane should be floated. It is the opposite of :meth:`Dock`. """
1336
1337        if self.IsNotebookPage():
1338            self.notebook_id = -1
1339            self.dock_direction = AUI_DOCK_NONE
1340
1341        return self.SetFlag(self.optionFloating, True)
1342
1343
1344    def Hide(self):
1345        """
1346        Indicates that a pane should be hidden.
1347
1348        Calling :meth:`Show(False) <Show>` achieve the same effect.
1349        """
1350
1351        return self.SetFlag(self.optionHidden, True)
1352
1353
1354    def Show(self, show=True):
1355        """
1356        Indicates that a pane should be shown.
1357
1358        :param bool `show`: whether the pane should be shown or not.
1359        """
1360
1361        return self.SetFlag(self.optionHidden, not show)
1362
1363
1364    # By defaulting to 1000, the tab will get placed at the end
1365    def NotebookPage(self, id, tab_position=1000):
1366        """
1367        Forces a pane to be a notebook page, so that the pane can be
1368        docked on top to another to create a :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
1369
1370        :param integer `id`: the notebook id;
1371        :param integer `tab_position`: the tab number of the pane once docked in a notebook.
1372        """
1373
1374        # Remove any floating frame
1375        self.Dock()
1376        self.notebook_id = id
1377        self.dock_pos = tab_position
1378        self.dock_row = 0
1379        self.dock_layer = 0
1380        self.dock_direction = AUI_DOCK_NOTEBOOK_PAGE
1381
1382        return self
1383
1384
1385    def NotebookControl(self, id):
1386        """
1387        Forces a pane to be a notebook control (:class:`~wx.lib.agw.aui.auibook.AuiNotebook`).
1388
1389        :param integer `id`: the notebook id.
1390        """
1391
1392        self.notebook_id = id
1393        self.window = None
1394        self.buttons = []
1395
1396        if self.dock_direction == AUI_DOCK_NOTEBOOK_PAGE:
1397            self.dock_direction = AUI_DOCK_NONE
1398
1399        return self
1400
1401
1402    def HasNotebook(self):
1403        """ Returns whether a pane has a :class:`~wx.lib.agw.aui.auibook.AuiNotebook` or not. """
1404
1405        return self.notebook_id >= 0
1406
1407
1408    def IsNotebookPage(self):
1409        """ Returns whether the pane is a notebook page in a :class:`~wx.lib.agw.aui.auibook.AuiNotebook`. """
1410
1411        return self.notebook_id >= 0 and self.dock_direction == AUI_DOCK_NOTEBOOK_PAGE
1412
1413
1414    def IsNotebookControl(self):
1415        """ Returns whether the pane is a notebook control (:class:`~wx.lib.agw.aui.auibook.AuiNotebook`). """
1416
1417        return not self.IsNotebookPage() and self.HasNotebook()
1418
1419
1420    def SetNameFromNotebookId(self):
1421        """ Sets the pane name once docked in a :class:`~wx.lib.agw.aui.auibook.AuiNotebook` using the notebook id. """
1422
1423        if self.notebook_id >= 0:
1424            self.name = "__notebook_%d"%self.notebook_id
1425
1426        return self
1427
1428
1429    def CaptionVisible(self, visible=True, left=False):
1430        """
1431        Indicates that a pane caption should be visible. If `visible` is ``False``, no pane
1432        caption is drawn.
1433
1434        :param bool `visible`: whether the caption should be visible or not;
1435        :param bool `left`: whether the caption should be drawn on the left (rotated by 90 degrees) or not.
1436        """
1437
1438        if left:
1439            self.SetFlag(self.optionCaption, False)
1440            return self.SetFlag(self.optionCaptionLeft, visible)
1441
1442        self.SetFlag(self.optionCaptionLeft, False)
1443        return self.SetFlag(self.optionCaption, visible)
1444
1445
1446    def PaneBorder(self, visible=True):
1447        """
1448        Indicates that a border should be drawn for the pane.
1449
1450        :param bool `visible`: whether the pane border should be visible or not.
1451        """
1452
1453        return self.SetFlag(self.optionPaneBorder, visible)
1454
1455
1456    def Gripper(self, visible=True):
1457        """
1458        Indicates that a gripper should be drawn for the pane.
1459
1460        :param bool `visible`: whether the gripper should be visible or not.
1461        """
1462
1463        return self.SetFlag(self.optionGripper, visible)
1464
1465
1466    def GripperTop(self, attop=True):
1467        """
1468        Indicates that a gripper should be drawn at the top of the pane.
1469
1470        :param bool `attop`: whether the gripper should be drawn at the top or not.
1471        """
1472
1473        return self.SetFlag(self.optionGripperTop, attop)
1474
1475
1476    def CloseButton(self, visible=True):
1477        """
1478        Indicates that a close button should be drawn for the pane.
1479
1480        :param bool `visible`: whether the close button should be visible or not.
1481        """
1482
1483        return self.SetFlag(self.buttonClose, visible)
1484
1485
1486    def MaximizeButton(self, visible=True):
1487        """
1488        Indicates that a maximize button should be drawn for the pane.
1489
1490        :param bool `visible`: whether the maximize button should be visible or not.
1491        """
1492
1493        return self.SetFlag(self.buttonMaximize, visible)
1494
1495
1496    def MinimizeButton(self, visible=True):
1497        """
1498        Indicates that a minimize button should be drawn for the pane.
1499
1500        :param bool `visible`: whether the minimize button should be visible or not.
1501        """
1502
1503        return self.SetFlag(self.buttonMinimize, visible)
1504
1505
1506    def PinButton(self, visible=True):
1507        """
1508        Indicates that a pin button should be drawn for the pane.
1509
1510        :param bool `visible`: whether the pin button should be visible or not.
1511        """
1512
1513        return self.SetFlag(self.buttonPin, visible)
1514
1515
1516    def DestroyOnClose(self, b=True):
1517        """
1518        Indicates whether a pane should be destroyed when it is closed.
1519
1520        Normally a pane is simply hidden when the close button is clicked. Setting
1521        `b` to ``True`` will cause the window to be destroyed when the user clicks
1522        the pane's close button.
1523
1524        :param bool `b`: whether the pane should be destroyed when it is closed or not.
1525        """
1526
1527        return self.SetFlag(self.optionDestroyOnClose, b)
1528
1529
1530    def TopDockable(self, b=True):
1531        """
1532        Indicates whether a pane can be docked at the top of the frame.
1533
1534        :param bool `b`: whether the pane can be docked at the top or not.
1535        """
1536
1537        return self.SetFlag(self.optionTopDockable, b)
1538
1539
1540    def BottomDockable(self, b=True):
1541        """
1542        Indicates whether a pane can be docked at the bottom of the frame.
1543
1544        :param bool `b`: whether the pane can be docked at the bottom or not.
1545        """
1546
1547        return self.SetFlag(self.optionBottomDockable, b)
1548
1549
1550    def LeftDockable(self, b=True):
1551        """
1552        Indicates whether a pane can be docked on the left of the frame.
1553
1554        :param bool `b`: whether the pane can be docked at the left or not.
1555        """
1556
1557        return self.SetFlag(self.optionLeftDockable, b)
1558
1559
1560    def RightDockable(self, b=True):
1561        """
1562        Indicates whether a pane can be docked on the right of the frame.
1563
1564        :param bool `b`: whether the pane can be docked at the right or not.
1565        """
1566
1567        return self.SetFlag(self.optionRightDockable, b)
1568
1569
1570    def Floatable(self, b=True):
1571        """
1572        Sets whether the user will be able to undock a pane and turn it
1573        into a floating window.
1574
1575        :param bool `b`: whether the pane can be floated or not.
1576        """
1577
1578        return self.SetFlag(self.optionFloatable, b)
1579
1580
1581    def Movable(self, b=True):
1582        """
1583        Indicates whether a pane can be moved.
1584
1585        :param bool `b`: whether the pane can be moved or not.
1586        """
1587
1588        return self.SetFlag(self.optionMovable, b)
1589
1590
1591    def NotebookDockable(self, b=True):
1592        """
1593        Indicates whether a pane can be docked in an automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
1594
1595        :param bool `b`: whether the pane can be docked in a notebook or not.
1596        """
1597
1598        return self.SetFlag(self.optionNotebookDockable, b)
1599
1600
1601    def DockFixed(self, b=True):
1602        """
1603        Causes the containing dock to have no resize sash. This is useful
1604        for creating panes that span the entire width or height of a dock, but should
1605        not be resizable in the other direction.
1606
1607        :param bool `b`: whether the pane will have a resize sash or not.
1608        """
1609
1610        return self.SetFlag(self.optionDockFixed, b)
1611
1612
1613    def Dockable(self, b=True):
1614        """
1615        Specifies whether a frame can be docked or not. It is the same as specifying
1616        :meth:`TopDockable` . :meth:`BottomDockable` . :meth:`LeftDockable` . :meth:`RightDockable` .
1617
1618        :param bool `b`: whether the frame can be docked or not.
1619        """
1620
1621        return self.TopDockable(b).BottomDockable(b).LeftDockable(b).RightDockable(b)
1622
1623
1624    def TopSnappable(self, b=True):
1625        """
1626        Indicates whether a pane can be snapped at the top of the main frame.
1627
1628        :param bool `b`: whether the pane can be snapped at the top of the main frame or not.
1629        """
1630
1631        return self.SetFlag(self.optionTopSnapped, b)
1632
1633
1634    def BottomSnappable(self, b=True):
1635        """
1636        Indicates whether a pane can be snapped at the bottom of the main frame.
1637
1638        :param bool `b`: whether the pane can be snapped at the bottom of the main frame or not.
1639        """
1640
1641        return self.SetFlag(self.optionBottomSnapped, b)
1642
1643
1644    def LeftSnappable(self, b=True):
1645        """
1646        Indicates whether a pane can be snapped on the left of the main frame.
1647
1648        :param bool `b`: whether the pane can be snapped at the left of the main frame or not.
1649        """
1650
1651        return self.SetFlag(self.optionLeftSnapped, b)
1652
1653
1654    def RightSnappable(self, b=True):
1655        """
1656        Indicates whether a pane can be snapped on the right of the main frame.
1657
1658        :param bool `b`: whether the pane can be snapped at the right of the main frame or not.
1659        """
1660
1661        return self.SetFlag(self.optionRightSnapped, b)
1662
1663
1664    def Snappable(self, b=True):
1665        """
1666        Indicates whether a pane can be snapped on the main frame. This is
1667        equivalent as calling :meth:`TopSnappable` . :meth:`BottomSnappable` . :meth:`LeftSnappable` . :meth:`RightSnappable` .
1668
1669        :param bool `b`: whether the pane can be snapped on the main frame or not.
1670        """
1671
1672        return self.TopSnappable(b).BottomSnappable(b).LeftSnappable(b).RightSnappable(b)
1673
1674
1675    def FlyOut(self, b=True):
1676        """
1677        Indicates whether a pane, when floating, has a "fly-out" effect
1678        (i.e., floating panes which only show themselves when moused over).
1679
1680        :param bool `b`: whether the pane can be snapped on the main frame or not.
1681        """
1682
1683        return self.SetFlag(self.optionFlyOut, b)
1684
1685
1686    # Copy over the members that pertain to docking position
1687    def SetDockPos(self, source):
1688        """
1689        Copies the `source` pane members that pertain to docking position to `self`.
1690
1691        :param `source`: the source pane from where to copy the attributes,
1692         an instance of :class:`AuiPaneInfo`.
1693        """
1694
1695        self.dock_direction = source.dock_direction
1696        self.dock_layer = source.dock_layer
1697        self.dock_row = source.dock_row
1698        self.dock_pos = source.dock_pos
1699        self.dock_proportion = source.dock_proportion
1700        self.floating_pos = wx.Point(*source.floating_pos)
1701        self.floating_size = wx.Size(*source.floating_size)
1702        self.rect = wx.Rect(*source.rect)
1703
1704        return self
1705
1706
1707    def DefaultPane(self):
1708        """ Specifies that the pane should adopt the default pane settings. """
1709
1710        state = self.state
1711        state |= self.optionTopDockable | self.optionBottomDockable | \
1712                 self.optionLeftDockable | self.optionRightDockable | \
1713                 self.optionNotebookDockable | \
1714                 self.optionFloatable | self.optionMovable | self.optionResizable | \
1715                 self.optionCaption | self.optionPaneBorder | self.buttonClose
1716
1717        self.state = state
1718        return self
1719
1720
1721    def CentrePane(self):
1722        """
1723        Specifies that the pane should adopt the default center pane settings.
1724
1725        Centre panes usually do not have caption bars. This function provides an easy way of
1726        preparing a pane to be displayed in the center dock position.
1727        """
1728
1729        return self.CenterPane()
1730
1731
1732    def CenterPane(self):
1733        """
1734        Specifies that the pane should adopt the default center pane settings.
1735
1736        Centre panes usually do not have caption bars. This function provides an easy way of
1737        preparing a pane to be displayed in the center dock position.
1738        """
1739
1740        self.state = 0
1741        return self.Center().PaneBorder().Resizable()
1742
1743
1744    def ToolbarPane(self):
1745        """ Specifies that the pane should adopt the default toolbar pane settings. """
1746
1747        self.DefaultPane()
1748        state = self.state
1749
1750        state |= (self.optionToolbar | self.optionGripper)
1751        state &= ~(self.optionResizable | self.optionCaption | self.optionCaptionLeft)
1752
1753        if self.dock_layer == 0:
1754            self.dock_layer = 10
1755
1756        self.state = state
1757
1758        return self
1759
1760
1761    def Icon(self, icon):
1762        """
1763        Specifies whether an icon is drawn on the left of the caption text when
1764        the pane is docked. If `icon` is ``None`` or :class:`NullIcon`, no icon is drawn on
1765        the caption space.
1766
1767        :param icon: an icon to draw on the caption space, or ``None``.
1768        :type `icon`: :class:`Icon` or ``None``
1769        """
1770
1771        if icon is None:
1772            icon = wx.NullIcon
1773
1774        self.icon = icon
1775        return self
1776
1777
1778    def SetFlag(self, flag, option_state):
1779        """
1780        Turns the property given by `flag` on or off with the `option_state`
1781        parameter.
1782
1783        :param integer `flag`: the property to set;
1784        :param bool `option_state`: either ``True`` or ``False``.
1785        """
1786
1787        state = self.state
1788
1789        if option_state:
1790            state |= flag
1791        else:
1792            state &= ~flag
1793
1794        self.state = state
1795
1796        if flag in [self.buttonClose, self.buttonMaximize, self.buttonMinimize, self.buttonPin]:
1797            self.ResetButtons()
1798
1799        return self
1800
1801
1802    def HasFlag(self, flag):
1803        """
1804        Returns ``True`` if the the property specified by flag is active for the pane.
1805
1806        :param integer `flag`: the property to check for activity.
1807        """
1808
1809        return (self.state & flag and [True] or [False])[0]
1810
1811
1812    def ResetButtons(self):
1813        """
1814        Resets all the buttons and recreates them from scratch depending on the
1815        :class:`AuiManager` flags.
1816        """
1817
1818        floating = self.HasFlag(self.optionFloating)
1819        self.buttons = []
1820
1821        if not floating and self.HasMinimizeButton():
1822            button = AuiPaneButton(AUI_BUTTON_MINIMIZE)
1823            self.buttons.append(button)
1824
1825        if not floating and self.HasMaximizeButton():
1826            button = AuiPaneButton(AUI_BUTTON_MAXIMIZE_RESTORE)
1827            self.buttons.append(button)
1828
1829        if not floating and self.HasPinButton():
1830            button = AuiPaneButton(AUI_BUTTON_PIN)
1831            self.buttons.append(button)
1832
1833        if self.HasCloseButton():
1834            button = AuiPaneButton(AUI_BUTTON_CLOSE)
1835            self.buttons.append(button)
1836
1837
1838    def CountButtons(self):
1839        """ Returns the number of visible buttons in the docked pane. """
1840
1841        n = 0
1842
1843        if self.HasCaption() or self.HasCaptionLeft():
1844            if isinstance(wx.GetTopLevelParent(self.window), AuiFloatingFrame):
1845                return 1
1846
1847            if self.HasCloseButton():
1848                n += 1
1849            if self.HasMaximizeButton():
1850                n += 1
1851            if self.HasMinimizeButton():
1852                n += 1
1853            if self.HasPinButton():
1854                n += 1
1855
1856        return n
1857
1858
1859    def IsHorizontal(self):
1860        """ Returns ``True`` if the pane `dock_direction` is horizontal. """
1861
1862        return self.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]
1863
1864    def IsVertical(self):
1865        """ Returns ``True`` if the pane `dock_direction` is vertical. """
1866
1867        return self.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]
1868
1869
1870# Null AuiPaneInfo reference
1871NonePaneInfo = AuiPaneInfo()
1872""" Null :class:`AuiPaneInfo` reference, an invalid instance of :class:`AuiPaneInfo`. """
1873
1874
1875# ---------------------------------------------------------------------------- #
1876
1877class AuiDockingGuide(wx.Frame):
1878    """ Base class for :class:`AuiSingleDockingGuide` and :class:`AuiCenterDockingGuide`."""
1879
1880    def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition,
1881                 size=wx.DefaultSize, style=wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP |
1882                 wx.FRAME_NO_TASKBAR | wx.NO_BORDER, name="AuiDockingGuide"):
1883        """
1884        Default class constructor. Used internally, do not call it in your code!
1885
1886        :param `parent`: the :class:`AuiManager` parent;
1887        :param integer `id`: the window identifier. It may take a value of -1 to indicate a default value.
1888        :param string `title`: the caption to be displayed on the frame's title bar.
1889        :param wx.Point `pos`: the window position. A value of (-1, -1) indicates a default position,
1890         chosen by either the windowing system or wxPython, depending on platform.
1891        :param wx.Size `size`: the window size. A value of (-1, -1) indicates a default size, chosen by
1892         either the windowing system or wxPython, depending on platform.
1893        :param integer `style`: the window style.
1894        :param string `name`: the name of the window. This parameter is used to associate a name with the
1895         item, allowing the application user to set Motif resource values for individual windows.
1896        """
1897
1898        wx.Frame.__init__(self, parent, id, title, pos, size, style, name=name)
1899
1900
1901    def HitTest(self, x, y):
1902        """
1903        To be overridden by parent classes.
1904
1905        :param integer `x`: the `x` mouse position;
1906        :param integer `y`: the `y` mouse position.
1907        """
1908
1909        return 0
1910
1911
1912    def ValidateNotebookDocking(self, valid):
1913        """
1914        To be overridden by parent classes.
1915
1916        :param bool `valid`: whether a pane can be docked on top to another to form an automatic
1917         :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
1918        """
1919
1920        return 0
1921
1922# ============================================================================
1923# implementation
1924# ============================================================================
1925
1926# ---------------------------------------------------------------------------
1927# AuiDockingGuideWindow
1928# ---------------------------------------------------------------------------
1929
1930class AuiDockingGuideWindow(wx.Window):
1931    """ Target class for :class:`AuiDockingGuide` and :class:`AuiCenterDockingGuide`. """
1932
1933    def __init__(self, parent, rect, direction=0, center=False, useAero=False):
1934        """
1935        Default class constructor. Used internally, do not call it in your code!
1936
1937        :param `parent`: the :class:`AuiManager` parent;
1938        :param wx.Rect `rect`: the window rect;
1939        :param integer `direction`: one of ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, ``wx.RIGHT``,
1940         ``wx.CENTER``;
1941        :param bool `center`: whether the calling class is a :class:`AuiCenterDockingGuide`;
1942        :param bool `useAero`: whether to use the new Aero-style bitmaps or Whidbey-style bitmaps
1943         for the docking guide.
1944        """
1945
1946        wx.Window.__init__(self, parent, -1, rect.GetPosition(), rect.GetSize(), wx.NO_BORDER)
1947
1948        self._direction = direction
1949        self._center = center
1950        self._valid = True
1951        self._useAero = useAero
1952
1953        self._bmp_unfocus, self._bmp_focus = GetDockingImage(direction, useAero, center)
1954
1955        self._currentImage = self._bmp_unfocus
1956        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
1957
1958        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
1959        self.Bind(wx.EVT_PAINT, self.OnPaint)
1960
1961
1962    def SetValid(self, valid):
1963        """
1964        Sets the docking direction as valid or invalid.
1965
1966        :param bool `valid`: whether the docking direction is allowed or not.
1967        """
1968
1969        self._valid = valid
1970
1971
1972    def IsValid(self):
1973        """ Returns whether the docking direction is valid. """
1974
1975        return self._valid
1976
1977
1978    def OnEraseBackground(self, event):
1979        """
1980        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`AuiDockingGuideWindow`.
1981
1982        :param `event`: a :class:`EraseEvent` to be processed.
1983
1984        :note: This is intentionally empty to reduce flickering while drawing.
1985        """
1986
1987        pass
1988
1989
1990    def DrawBackground(self, dc):
1991        """
1992        Draws the docking guide background.
1993
1994        :param `dc`: a :class:`wx.DC` device context object.
1995        """
1996
1997        rect = self.GetClientRect()
1998
1999        dc.SetPen(wx.TRANSPARENT_PEN)
2000        dc.SetBrush(wx.Brush(colourTargetBackground))
2001        dc.DrawRectangle(rect)
2002
2003        dc.SetPen(wx.Pen(colourTargetBorder))
2004
2005        left = rect.GetLeft()
2006        top = rect.GetTop()
2007        right = rect.GetRight()
2008        bottom = rect.GetBottom()
2009
2010        if self._direction != wx.CENTER:
2011
2012            if not self._center or self._direction != wx.BOTTOM:
2013                dc.DrawLine(left, top, right+1, top)
2014            if not self._center or self._direction != wx.RIGHT:
2015                dc.DrawLine(left, top, left, bottom+1)
2016            if not self._center or self._direction != wx.LEFT:
2017                dc.DrawLine(right, top, right, bottom+1)
2018            if not self._center or self._direction != wx.TOP:
2019                dc.DrawLine(left, bottom, right+1, bottom)
2020
2021            dc.SetPen(wx.Pen(colourTargetShade))
2022
2023            if self._direction != wx.RIGHT:
2024                dc.DrawLine(left + 1, top + 1, left + 1, bottom)
2025            if self._direction != wx.BOTTOM:
2026                dc.DrawLine(left + 1, top + 1, right, top + 1)
2027
2028
2029    def DrawDottedLine(self, dc, point, length, vertical):
2030        """
2031        Draws a dotted line (not used if the docking guide images are ok).
2032
2033        :param `dc`: a :class:`wx.DC` device context object;
2034        :param `point`: a :class:`wx.Point` where to start drawing the dotted line;
2035        :param integer `length`: the length of the dotted line;
2036        :param bool `vertical`: whether it is a vertical docking guide window or not.
2037        """
2038
2039        for i in range(0, length, 2):
2040            dc.DrawPoint(point.x, point.y)
2041            if vertical:
2042                point.y += 2
2043            else:
2044                point.x += 2
2045
2046
2047    def DrawIcon(self, dc):
2048        """
2049        Draws the docking guide icon (not used if the docking guide images are ok).
2050
2051        :param `dc`: a :class:`wx.DC` device context object.
2052        """
2053
2054        rect = wx.Rect(*self.GetClientRect())
2055        point = wx.Point()
2056        length = 0
2057
2058        rect.Deflate(4, 4)
2059        dc.SetPen(wx.Pen(colourIconBorder))
2060        dc.SetBrush(wx.Brush(colourIconBackground))
2061        dc.DrawRectangle(rect)
2062
2063        right1 = rect.GetRight() + 1
2064        bottom1 = rect.GetBottom() + 1
2065
2066        dc.SetPen(wx.Pen(colourIconShadow))
2067        dc.DrawLine(rect.x + 1, bottom1, right1 + 1, bottom1)
2068        dc.DrawLine(right1, rect.y + 1, right1, bottom1 + 1)
2069
2070        rect.Deflate(1, 1)
2071
2072        if self._direction == wx.TOP:
2073            rect.height -= rect.height / 2
2074            point = rect.GetBottomLeft()
2075            length = rect.width
2076
2077        elif self._direction == wx.LEFT:
2078            rect.width -= rect.width / 2
2079            point = rect.GetTopRight()
2080            length = rect.height
2081
2082        elif self._direction == wx.RIGHT:
2083            rect.x += rect.width / 2
2084            rect.width -= rect.width / 2
2085            point = rect.GetTopLeft()
2086            length = rect.height
2087
2088        elif self._direction == wx.BOTTOM:
2089            rect.y += rect.height / 2
2090            rect.height -= rect.height / 2
2091            point = rect.GetTopLeft()
2092            length = rect.width
2093
2094        elif self._direction == wx.CENTER:
2095            rect.Deflate(1, 1)
2096            point = rect.GetTopLeft()
2097            length = rect.width
2098
2099        dc.GradientFillLinear(rect, colourIconDockingPart1,
2100                              colourIconDockingPart2, self._direction)
2101
2102        dc.SetPen(wx.Pen(colourIconBorder))
2103
2104        if self._direction == wx.CENTER:
2105            self.DrawDottedLine(dc, rect.GetTopLeft(), rect.width, False)
2106            self.DrawDottedLine(dc, rect.GetTopLeft(), rect.height, True)
2107            self.DrawDottedLine(dc, rect.GetBottomLeft(), rect.width, False)
2108            self.DrawDottedLine(dc, rect.GetTopRight(), rect.height, True)
2109
2110        elif self._direction in [wx.TOP, wx.BOTTOM]:
2111            self.DrawDottedLine(dc, point, length, False)
2112
2113        else:
2114            self.DrawDottedLine(dc, point, length, True)
2115
2116
2117    def DrawArrow(self, dc):
2118        """
2119        Draws the docking guide arrow icon (not used if the docking guide images are ok).
2120
2121        :param `dc`: a :class:`wx.DC` device context object.
2122        """
2123
2124        rect = self.GetClientRect()
2125        point = wx.Point()
2126
2127        point.x = (rect.GetLeft() + rect.GetRight()) / 2
2128        point.y = (rect.GetTop() + rect.GetBottom()) / 2
2129        rx, ry = wx.Size(), wx.Size()
2130
2131        if self._direction == wx.TOP:
2132            rx = wx.Size(1, 0)
2133            ry = wx.Size(0, 1)
2134
2135        elif self._direction == wx.LEFT:
2136            rx = wx.Size(0, -1)
2137            ry = wx.Size(1, 0)
2138
2139        elif self._direction == wx.RIGHT:
2140            rx = wx.Size(0, 1)
2141            ry = wx.Size(-1, 0)
2142
2143        elif self._direction == wx.BOTTOM:
2144            rx = wx.Size(-1, 0)
2145            ry = wx.Size(0, -1)
2146
2147        point.x += ry.x*3
2148        point.y += ry.y*3
2149
2150        dc.SetPen(wx.Pen(colourIconArrow))
2151
2152        for i in range(4):
2153            pt1 = wx.Point(point.x - rx.x*i, point.y - rx.y*i)
2154            pt2 = wx.Point(point.x + rx.x*(i+1), point.y + rx.y*(i+1))
2155            dc.DrawLine(pt1, pt2)
2156            point.x += ry.x
2157            point.y += ry.y
2158
2159
2160    def OnPaint(self, event):
2161        """
2162        Handles the ``wx.EVT_PAINT`` event for :class:`AuiDockingGuideWindow`.
2163
2164        :param `event`: a :class:`PaintEvent` to be processed.
2165        """
2166
2167        dc = wx.AutoBufferedPaintDC(self)
2168        if self._currentImage.IsOk() and self._valid:
2169            dc.DrawBitmap(self._currentImage, 0, 0, True)
2170        else:
2171            self.Draw(dc)
2172
2173
2174    def Draw(self, dc):
2175        """
2176        Draws the whole docking guide window (not used if the docking guide images are ok).
2177
2178        :param `dc`: a :class:`wx.DC` device context object.
2179        """
2180
2181        self.DrawBackground(dc)
2182
2183        if self._valid:
2184            self.DrawIcon(dc)
2185            self.DrawArrow(dc)
2186
2187
2188    def UpdateDockGuide(self, pos):
2189        """
2190        Updates the docking guide images depending on the mouse position, using focused
2191        images if the mouse is inside the docking guide or unfocused images if it is
2192        outside.
2193
2194        :param `pos`: a :class:`wx.Point` mouse position.
2195        """
2196
2197        if not self.GetTopLevelParent().IsShownOnScreen() and self.IsShownOnScreen():
2198            return
2199
2200        inside = self.GetScreenRect().Contains(pos)
2201
2202        if inside:
2203            image = self._bmp_focus
2204        else:
2205            image = self._bmp_unfocus
2206
2207        if image != self._currentImage:
2208            self._currentImage = image
2209            self.Refresh()
2210            self.Update()
2211
2212
2213# ---------------------------------------------------------------------------
2214# AuiSingleDockingGuide
2215# ---------------------------------------------------------------------------
2216
2217class AuiSingleDockingGuide(AuiDockingGuide):
2218    """ A docking guide window for single docking hint (not diamond-shaped HUD). """
2219
2220    def __init__(self, parent, direction=0):
2221        """
2222        Default class constructor. Used internally, do not call it in your code!
2223
2224        :param `parent`: the :class:`AuiManager` parent;
2225        :param integer `direction`: one of ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, ``wx.RIGHT``.
2226        """
2227
2228        self._direction = direction
2229
2230        style = wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP | \
2231                wx.FRAME_NO_TASKBAR | wx.NO_BORDER
2232
2233        # Use of FRAME_SHAPED on wxMac causes the frame to be visible
2234        # breaking the docking hints.
2235        if wx.Platform != '__WXMAC__':
2236            style |= wx.FRAME_SHAPED
2237
2238        AuiDockingGuide.__init__(self, parent, style=style, name="auiSingleDockTarget")
2239
2240        self.Hide()
2241
2242        useAero = GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_AERO_DOCKING_GUIDES
2243        useWhidbey = GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_WHIDBEY_DOCKING_GUIDES
2244
2245        self._useAero = useAero or useWhidbey
2246        self._valid = True
2247
2248        if useAero:
2249            sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
2250        elif useWhidbey:
2251            sizeX, sizeY = whidbeySizeX, whidbeySizeY
2252        else:
2253            sizeX, sizeY = guideSizeX, guideSizeY
2254
2255        if direction not in [wx.TOP, wx.BOTTOM]:
2256            sizeX, sizeY = sizeY, sizeX
2257
2258        if self._useAero:
2259            self.CreateShapesWithStyle(useWhidbey)
2260
2261            if wx.Platform == "__WXGTK__":
2262                self.Bind(wx.EVT_WINDOW_CREATE, self.SetGuideShape)
2263            else:
2264                self.SetGuideShape()
2265
2266            self.SetClientSize(self.region.GetBox().GetSize())
2267        else:
2268            self.SetClientSize((sizeX, sizeY))
2269
2270        self.rect = wx.Rect(0, 0, sizeX, sizeY)
2271
2272        if self._useAero:
2273            useAero = (useWhidbey and [2] or [1])[0]
2274        else:
2275            useAero = 0
2276
2277        self.target = AuiDockingGuideWindow(self, self.rect, direction, False, useAero)
2278
2279
2280    def CreateShapesWithStyle(self, useWhidbey):
2281        """
2282        Creates the docking guide window shape based on which docking bitmaps are used.
2283
2284        :param bool `useWhidbey`: if ``True``, use Whidbey-style bitmaps; if ``False``, use the
2285         Aero-style bitmaps.
2286         """
2287
2288        sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
2289        if useWhidbey:
2290            sizeX, sizeY = whidbeySizeX, whidbeySizeY
2291
2292        if self._direction not in [wx.TOP, wx.BOTTOM]:
2293            sizeX, sizeY = sizeY, sizeX
2294
2295        useAero = (useWhidbey and [2] or [1])[0]
2296        bmp, dummy = GetDockingImage(self._direction, useAero, False)
2297        region = wx.Region(bmp)
2298
2299        self.region = region
2300
2301
2302    def AeroMove(self, pos):
2303        """
2304        Moves the docking window to the new position. Overridden in children classes.
2305
2306        :param wx.Point `pos`: the new docking guide position.
2307        """
2308
2309        pass
2310
2311
2312    def SetGuideShape(self, event=None):
2313        """
2314        Sets the correct shape for the docking guide window.
2315
2316        :param `event`: on wxGTK, a :class:`wx.WindowCreateEvent` event to process.
2317        """
2318
2319        self.SetShape(self.region)
2320
2321        if event is not None:
2322            # Skip the event on wxGTK
2323            event.Skip()
2324            wx.CallAfter(wx.SafeYield, self, True)
2325
2326
2327    def SetShape(self, region):
2328        """
2329        If the platform supports it, sets the shape of the window to that depicted by `region`.
2330        The system will not display or respond to any mouse event for the pixels that lie
2331        outside of the region. To reset the window to the normal rectangular shape simply call
2332        :meth:`SetShape` again with an empty region.
2333
2334        :param Region `region`: the shape of the frame.
2335
2336        :note: Overridden for wxMAC.
2337        """
2338
2339        if wx.Platform == '__WXMAC__':
2340            # HACK so we don't crash when SetShape is called
2341            return
2342        else:
2343            super(AuiSingleDockingGuide, self).SetShape(region)
2344
2345
2346    def SetValid(self, valid):
2347        """
2348        Sets the docking direction as valid or invalid.
2349
2350        :param bool `valid`: whether the docking direction is allowed or not.
2351        """
2352
2353        self._valid = valid
2354
2355
2356    def IsValid(self):
2357        """ Returns whether the docking direction is valid. """
2358
2359        return self._valid
2360
2361
2362    def UpdateDockGuide(self, pos):
2363        """
2364        Updates the docking guide images depending on the mouse position, using focused
2365        images if the mouse is inside the docking guide or unfocused images if it is
2366        outside.
2367
2368        :param wx.Point `pos`: the mouse position.
2369        """
2370
2371        self.target.UpdateDockGuide(pos)
2372
2373
2374    def HitTest(self, x, y):
2375        """
2376        Checks if the mouse position is inside the target window rect.
2377
2378        :param integer `x`: the `x` mouse position;
2379        :param integer `y`: the `y` mouse position.
2380        """
2381
2382        if self.target.GetScreenRect().Contains((x, y)):
2383            return wx.ALL
2384
2385        return -1
2386
2387
2388# ---------------------------------------------------------------------------
2389# AuiCenterDockingGuide
2390# ---------------------------------------------------------------------------
2391
2392class AuiCenterDockingGuide(AuiDockingGuide):
2393    """ A docking guide window for multiple docking hint (diamond-shaped HUD). """
2394
2395    def __init__(self, parent):
2396        """
2397        Default class constructor.
2398        Used internally, do not call it in your code!
2399
2400        :param `parent`: the :class:`AuiManager` parent.
2401        """
2402
2403        AuiDockingGuide.__init__(self, parent, style=wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP |
2404                                 wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.FRAME_SHAPED,
2405                                 name="auiCenterDockTarget")
2406
2407        self.Hide()
2408
2409        self.CreateShapesWithStyle()
2410        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
2411
2412        if wx.Platform == "__WXGTK__":
2413            self.Bind(wx.EVT_WINDOW_CREATE, self.SetGuideShape)
2414        else:
2415            self.SetGuideShape()
2416
2417        self.SetClientSize(self.region.GetBox().GetSize())
2418
2419        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
2420        self.Bind(wx.EVT_PAINT, self.OnPaint)
2421
2422
2423    def CreateShapesWithStyle(self):
2424        """ Creates the docking guide window shape based on which docking bitmaps are used. """
2425
2426        useAero = (GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_AERO_DOCKING_GUIDES) != 0
2427        useWhidbey = (GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_WHIDBEY_DOCKING_GUIDES) != 0
2428
2429        self._useAero = 0
2430        if useAero:
2431            self._useAero = 1
2432        elif useWhidbey:
2433            self._useAero = 2
2434
2435        if useAero:
2436            sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
2437        elif useWhidbey:
2438            sizeX, sizeY = whidbeySizeX, whidbeySizeY
2439        else:
2440            sizeX, sizeY = guideSizeX, guideSizeY
2441
2442        rectLeft = wx.Rect(0, sizeY, sizeY, sizeX)
2443        rectTop = wx.Rect(sizeY, 0, sizeX, sizeY)
2444        rectRight = wx.Rect(sizeY+sizeX, sizeY, sizeY, sizeX)
2445        rectBottom = wx.Rect(sizeY, sizeX + sizeY, sizeX, sizeY)
2446        rectCenter = wx.Rect(sizeY, sizeY, sizeX, sizeX)
2447
2448        if not self._useAero:
2449
2450            self.targetLeft = AuiDockingGuideWindow(self, rectLeft, wx.LEFT, True, useAero)
2451            self.targetTop = AuiDockingGuideWindow(self, rectTop, wx.TOP, True, useAero)
2452            self.targetRight = AuiDockingGuideWindow(self, rectRight, wx.RIGHT, True, useAero)
2453            self.targetBottom = AuiDockingGuideWindow(self, rectBottom, wx.BOTTOM, True, useAero)
2454            self.targetCenter = AuiDockingGuideWindow(self, rectCenter, wx.CENTER, True, useAero)
2455
2456
2457            # top-left diamond
2458            tld = [wx.Point(rectTop.x, rectTop.y+rectTop.height-8),
2459                   wx.Point(rectLeft.x+rectLeft.width-8, rectLeft.y),
2460                   rectTop.GetBottomLeft()]
2461            # bottom-left diamond
2462            bld = [wx.Point(rectLeft.x+rectLeft.width-8, rectLeft.y+rectLeft.height),
2463                   wx.Point(rectBottom.x, rectBottom.y+8),
2464                   rectBottom.GetTopLeft()]
2465            # top-right diamond
2466            trd = [wx.Point(rectTop.x+rectTop.width, rectTop.y+rectTop.height-8),
2467                   wx.Point(rectRight.x+8, rectRight.y),
2468                   rectRight.GetTopLeft()]
2469            # bottom-right diamond
2470            brd = [wx.Point(rectRight.x+8, rectRight.y+rectRight.height),
2471                   wx.Point(rectBottom.x+rectBottom.width, rectBottom.y+8),
2472                   rectBottom.GetTopRight()]
2473
2474            self._triangles = [tld[0:2], bld[0:2],
2475                               [wx.Point(rectTop.x+rectTop.width-1, rectTop.y+rectTop.height-8),
2476                                wx.Point(rectRight.x+7, rectRight.y)],
2477                               [wx.Point(rectRight.x+7, rectRight.y+rectRight.height),
2478                                wx.Point(rectBottom.x+rectBottom.width-1, rectBottom.y+8)]]
2479
2480            region = wx.Region()
2481            region.Union(rectLeft)
2482            region.Union(rectTop)
2483            region.Union(rectRight)
2484            region.Union(rectBottom)
2485            region.Union(rectCenter)
2486            region.Union(wx.Region(tld))
2487            region.Union(wx.Region(bld))
2488            region.Union(wx.Region(trd))
2489            region.Union(wx.Region(brd))
2490
2491        elif useAero:
2492
2493            self._aeroBmp = aero_dock_pane.GetBitmap()
2494            region = wx.Region(self._aeroBmp)
2495
2496            self._allAeroBmps = [aero_dock_pane_left.GetBitmap(), aero_dock_pane_top.GetBitmap(),
2497                                 aero_dock_pane_right.GetBitmap(), aero_dock_pane_bottom.GetBitmap(),
2498                                 aero_dock_pane_center.GetBitmap(), aero_dock_pane.GetBitmap()]
2499            self._deniedBitmap = aero_denied.GetBitmap()
2500            self._aeroRects = [rectLeft, rectTop, rectRight, rectBottom, rectCenter]
2501            self._valid = True
2502
2503        elif useWhidbey:
2504
2505            self._aeroBmp = whidbey_dock_pane.GetBitmap()
2506            region = wx.Region(self._aeroBmp)
2507
2508            self._allAeroBmps = [whidbey_dock_pane_left.GetBitmap(), whidbey_dock_pane_top.GetBitmap(),
2509                                 whidbey_dock_pane_right.GetBitmap(), whidbey_dock_pane_bottom.GetBitmap(),
2510                                 whidbey_dock_pane_center.GetBitmap(), whidbey_dock_pane.GetBitmap()]
2511            self._deniedBitmap = whidbey_denied.GetBitmap()
2512            self._aeroRects = [rectLeft, rectTop, rectRight, rectBottom, rectCenter]
2513            self._valid = True
2514
2515
2516        self.region = region
2517
2518
2519    def SetGuideShape(self, event=None):
2520        """
2521        Sets the correct shape for the docking guide window.
2522
2523        :param `event`: on wxGTK, a :class:`wx.WindowCreateEvent` event to process.
2524        """
2525
2526        self.SetShape(self.region)
2527
2528        if event is not None:
2529            # Skip the event on wxGTK
2530            event.Skip()
2531            wx.CallAfter(wx.SafeYield, self, True)
2532
2533
2534    def UpdateDockGuide(self, pos):
2535        """
2536        Updates the docking guides images depending on the mouse position, using focused
2537        images if the mouse is inside the docking guide or unfocused images if it is
2538        outside.
2539
2540        :param wx.Point `pos`: the mouse position.
2541        """
2542
2543        if not self._useAero:
2544            for target in self.GetChildren():
2545                target.UpdateDockGuide(pos)
2546        else:
2547            lenRects = len(self._aeroRects)
2548            for indx, rect in enumerate(self._aeroRects):
2549                if rect.Contains(pos):
2550                    if self._allAeroBmps[indx] != self._aeroBmp:
2551                        if indx < lenRects - 1 or (indx == lenRects - 1 and self._valid):
2552                            self._aeroBmp = self._allAeroBmps[indx]
2553                            self.Refresh()
2554                        else:
2555                            self._aeroBmp = self._allAeroBmps[-1]
2556                            self.Refresh()
2557
2558                    return
2559
2560            if self._aeroBmp != self._allAeroBmps[-1]:
2561                self._aeroBmp = self._allAeroBmps[-1]
2562                self.Refresh()
2563
2564
2565    def HitTest(self, x, y):
2566        """
2567        Checks if the mouse position is inside the target windows rect.
2568
2569        :param integer `x`: the `x` mouse position;
2570        :param integer `y`: the `y` mouse position.
2571        """
2572
2573        if not self._useAero:
2574            if self.targetLeft.GetScreenRect().Contains((x, y)):
2575                return wx.LEFT
2576            if self.targetTop.GetScreenRect().Contains((x, y)):
2577                return wx.UP
2578            if self.targetRight.GetScreenRect().Contains((x, y)):
2579                return wx.RIGHT
2580            if self.targetBottom.GetScreenRect().Contains((x, y)):
2581                return wx.DOWN
2582            if self.targetCenter.IsValid() and self.targetCenter.GetScreenRect().Contains((x, y)):
2583                return wx.CENTER
2584        else:
2585            constants = [wx.LEFT, wx.UP, wx.RIGHT, wx.DOWN, wx.CENTER]
2586            lenRects = len(self._aeroRects)
2587            for indx, rect in enumerate(self._aeroRects):
2588                if rect.Contains((x, y)):
2589                    if indx < lenRects or (indx == lenRects-1 and self._valid):
2590                        return constants[indx]
2591
2592        return -1
2593
2594
2595    def ValidateNotebookDocking(self, valid):
2596        """
2597        Sets whether a pane can be docked on top of another to create an automatic
2598        :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
2599
2600        :param bool `valid`: whether a pane can be docked on top to another to form an automatic
2601         :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
2602        """
2603
2604        if not self._useAero:
2605            if self.targetCenter.IsValid() != valid:
2606                self.targetCenter.SetValid(valid)
2607                self.targetCenter.Refresh()
2608        else:
2609            if self._valid != valid:
2610                self._valid = valid
2611                self.Refresh()
2612
2613
2614    def AeroMove(self, pos):
2615        """
2616        Moves the docking guide window to the new position.
2617
2618        :param wx.Point `pos`: the new docking guide position.
2619        """
2620
2621        if not self._useAero:
2622            return
2623
2624        useWhidbey = (GetManager(self.GetParent()).GetAGWFlags() & AUI_MGR_WHIDBEY_DOCKING_GUIDES) != 0
2625
2626        if useWhidbey:
2627            sizeX, sizeY = whidbeySizeX, whidbeySizeY
2628        else:
2629            sizeX, sizeY = aeroguideSizeX, aeroguideSizeY
2630
2631        size = self.GetSize()
2632
2633        leftRect, topRect, rightRect, bottomRect, centerRect = self._aeroRects
2634        thePos = pos + wx.Point((size.x-sizeY)/2, (size.y-sizeX)/2)
2635
2636        centerRect.SetTopLeft(thePos)
2637
2638        leftRect.SetTopLeft(thePos + wx.Point(-sizeY, 0))
2639        topRect.SetTopLeft(thePos + wx.Point(0, -sizeY))
2640        rightRect.SetTopLeft(thePos + wx.Point(sizeX, 0))
2641        bottomRect.SetTopLeft(thePos + wx.Point(0, sizeX))
2642
2643
2644    def OnEraseBackground(self, event):
2645        """
2646        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`AuiCenterDockingGuide`.
2647
2648        :param `event`: :class:`EraseEvent` to be processed.
2649
2650        :note: This is intentionally empty to reduce flickering while drawing.
2651        """
2652
2653        pass
2654
2655
2656    def OnPaint(self, event):
2657        """
2658        Handles the ``wx.EVT_PAINT`` event for :class:`AuiCenterDockingGuide`.
2659
2660        :param `event`: a :class:`PaintEvent` to be processed.
2661        """
2662
2663        dc = wx.AutoBufferedPaintDC(self)
2664
2665        if self._useAero:
2666            dc.SetBrush(wx.TRANSPARENT_BRUSH)
2667            dc.SetPen(wx.TRANSPARENT_PEN)
2668        else:
2669            dc.SetBrush(wx.Brush(colourTargetBackground))
2670            dc.SetPen(wx.Pen(colourTargetBorder))
2671
2672        rect = self.GetClientRect()
2673        dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
2674
2675        if self._useAero:
2676            dc.DrawBitmap(self._aeroBmp, 0, 0, True)
2677            if not self._valid:
2678                diff = (self._useAero == 2 and [1] or [0])[0]
2679                bmpX, bmpY = self._deniedBitmap.GetWidth(), self._deniedBitmap.GetHeight()
2680                xPos, yPos = (rect.x + (rect.width)/2 - bmpX/2), (rect.y + (rect.height)/2 - bmpY/2)
2681                dc.DrawBitmap(self._deniedBitmap, xPos+1, yPos+diff, True)
2682
2683            return
2684
2685        dc.SetPen(wx.Pen(colourTargetBorder, 2))
2686        for pts in self._triangles:
2687            dc.DrawLine(pts[0], pts[1])
2688
2689
2690# ----------------------------------------------------------------------------
2691# AuiDockingHintWindow
2692# ----------------------------------------------------------------------------
2693
2694class AuiDockingHintWindow(wx.Frame):
2695    """ The original wxAUI docking window hint. """
2696
2697    def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition,
2698                 size=wx.Size(1, 1), style=wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT |
2699                 wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.FRAME_SHAPED,
2700                 name="auiHintWindow"):
2701        """
2702        Default class constructor. Used internally, do not call it in your code!
2703
2704        :param `parent`: the :class:`AuiManager` parent;
2705        :param integer `id`: the window identifier. It may take a value of -1 to indicate a default value.
2706        :param string `title`: the caption to be displayed on the frame's title bar;
2707        :param wx.Point `pos`: the window position. A value of (-1, -1) indicates a default position,
2708         chosen by either the windowing system or wxPython, depending on platform;
2709        :param wx.Size `size`: the window size. A value of (-1, -1) indicates a default size, chosen by
2710         either the windowing system or wxPython, depending on platform;
2711        :param integer `style`: the window style;
2712        :param string `name`: the name of the window. This parameter is used to associate a name with the
2713         item, allowing the application user to set Motif resource values for individual windows.
2714        """
2715        if wx.Platform == '__WXMAC__' and style & wx.FRAME_SHAPED:
2716            # Having the shaped frame causes the frame to not be visible
2717            # with the transparent style hints.
2718            style -= wx.FRAME_SHAPED
2719
2720        wx.Frame.__init__(self, parent, id, title, pos, size, style, name=name)
2721
2722        self._blindMode = False
2723
2724        self._art = parent.GetEventHandler().GetArtProvider()
2725        background = self._art.GetColour(AUI_DOCKART_HINT_WINDOW_COLOUR)
2726        self.SetBackgroundColour(background)
2727
2728        # Can't set background colour on a frame on wxMac
2729        # so add a panel to set the colour on.
2730        if wx.Platform == '__WXMAC__':
2731            sizer = wx.BoxSizer(wx.HORIZONTAL)
2732            self.panel = wx.Panel(self)
2733            sizer.Add(self.panel, 1, wx.EXPAND)
2734            self.SetSizer(sizer)
2735            self.panel.SetBackgroundColour(background)
2736        else:
2737            self.Bind(wx.EVT_PAINT, self.OnPaint)
2738
2739        self.Bind(wx.EVT_SIZE, self.OnSize)
2740
2741
2742    def MakeVenetianBlinds(self):
2743        """
2744        Creates the "venetian blind" effect if :class:`AuiManager` has the ``AUI_MGR_VENETIAN_BLINDS_HINT``
2745        flag set.
2746        """
2747
2748        amount = 128
2749        size = self.GetClientSize()
2750        region = wx.Region(0, 0, size.x, 1)
2751
2752        for y in range(size.y):
2753
2754            # Reverse the order of the bottom 4 bits
2755            j = (y & 8 and [1] or [0])[0] | (y & 4 and [2] or [0])[0] | \
2756                (y & 2 and [4] or [0])[0] | (y & 1 and [8] or [0])[0]
2757
2758            if 16*j+8 < amount:
2759                region.Union(0, y, size.x, 1)
2760
2761        self.SetShape(region)
2762
2763
2764    def SetBlindMode(self, agwFlags):
2765        """
2766        Sets whether venetian blinds or transparent hints will be shown as docking hint.
2767        This depends on the :class:`AuiManager` flags.
2768
2769        :param integer `agwFlags`: the :class:`AuiManager` flags.
2770        """
2771
2772        self._blindMode = (agwFlags & AUI_MGR_VENETIAN_BLINDS_HINT) != 0
2773
2774        if self._blindMode or not self.CanSetTransparent():
2775            self.MakeVenetianBlinds()
2776            self.SetTransparent(255)
2777
2778        else:
2779            self.SetShape(wx.Region())
2780            if agwFlags & AUI_MGR_HINT_FADE == 0:
2781                self.SetTransparent(80)
2782            else:
2783                self.SetTransparent(0)
2784
2785
2786    def SetShape(self, region):
2787        """
2788        If the platform supports it, sets the shape of the window to that depicted by `region`.
2789        The system will not display or respond to any mouse event for the pixels that lie
2790        outside of the region. To reset the window to the normal rectangular shape simply call
2791        :meth:`SetShape` again with an empty region.
2792
2793        :param Region `region`: the shape of the frame.
2794
2795        :note: Overridden for wxMAC.
2796        """
2797
2798        if wx.Platform == '__WXMAC__':
2799            # HACK so we don't crash when SetShape is called
2800            return
2801        else:
2802            super(AuiDockingHintWindow, self).SetShape(region)
2803
2804
2805    def Show(self, show=True):
2806        """
2807        Show the hint window.
2808
2809        :param bool `show`: whether to show or hide the hint docking window.
2810        """
2811
2812        background = self._art.GetColour(AUI_DOCKART_HINT_WINDOW_COLOUR)
2813
2814        if wx.Platform == '__WXMAC__':
2815            self.panel.SetBackgroundColour(background)
2816        else:
2817            self.SetBackgroundColour(background)
2818
2819        super(AuiDockingHintWindow, self).Show(show)
2820        self.Refresh()
2821
2822        if wx.Platform == '__WXMAC__':
2823            # Need to manually do layout since its a borderless frame.
2824            self.Layout()
2825
2826
2827    def OnSize(self, event):
2828        """
2829        Handles the ``wx.EVT_SIZE`` event for :class:`AuiDockingHintWindow`.
2830
2831        :param `event`: a :class:`wx.SizeEvent` to be processed.
2832        """
2833
2834        if self._blindMode or not self.CanSetTransparent():
2835            self.MakeVenetianBlinds()
2836
2837        self.Refresh()
2838
2839
2840    def OnPaint(self, event):
2841        """
2842        Handles the ``wx.EVT_PAINT`` event for :class:`AuiDockingHintWindow`.
2843
2844        :param `event`: an instance of :class:`PaintEvent` to be processed.
2845        """
2846
2847        rect = wx.Rect(wx.Point(0, 0), self.GetSize())
2848
2849        dc = wx.PaintDC(self)
2850        event.Skip()
2851
2852        dc.SetBrush(wx.TRANSPARENT_BRUSH)
2853        dc.SetPen(wx.Pen(wx.Colour(60, 60, 60), 5))
2854        rect.Deflate(1, 1)
2855        dc.DrawRectangle(rect)
2856
2857
2858# ---------------------------------------------------------------------------- #
2859
2860# -- AuiFloatingFrame class implementation --
2861
2862class AuiFloatingFrame(wx.MiniFrame):
2863    """ AuiFloatingFrame is the frame class that holds floating panes. """
2864
2865    def __init__(self, parent, owner_mgr, pane=None, id=wx.ID_ANY, title="",
2866                 style=wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT |
2867                 wx.FRAME_NO_TASKBAR | wx.CLIP_CHILDREN):
2868        """
2869        Default class constructor. Used internally, do not call it in your code!
2870
2871        :param `parent`: the :class:`AuiManager` parent;
2872        :param `owner_mgr`: the :class:`AuiManager` that manages the floating pane;
2873        :param `pane`: the :class:`AuiPaneInfo` pane that is about to float;
2874        :param integer `id`: the window identifier. It may take a value of -1 to indicate a default value.
2875        :param string `title`: the caption to be displayed on the frame's title bar.
2876        :param integer `style`: the window style.
2877        """
2878
2879        if pane and pane.IsResizeable():
2880            style += wx.RESIZE_BORDER
2881        if pane:
2882            self._is_toolbar = pane.IsToolbar()
2883
2884        self._useNativeMiniframes = False
2885        if AuiManager_UseNativeMiniframes(owner_mgr):
2886            # On wxMac we always use native miniframes
2887            self._useNativeMiniframes = True
2888            style += wx.CAPTION + wx.SYSTEM_MENU
2889            if pane.HasCloseButton():
2890                style += wx.CLOSE_BOX
2891            if pane.HasMaximizeButton():
2892                style += wx.MAXIMIZE_BOX
2893            if pane.HasMinimizeButton():
2894                style += wx.MINIMIZE_BOX
2895
2896        wx.MiniFrame.__init__(self, parent, id, title, pos=pane.floating_pos,
2897                              size=pane.floating_size, style=style, name="auiFloatingFrame")
2898
2899        self._fly_timer = wx.Timer(self, wx.ID_ANY)
2900        self._check_fly_timer = wx.Timer(self, wx.ID_ANY)
2901
2902        self.Bind(wx.EVT_CLOSE, self.OnClose)
2903        self.Bind(wx.EVT_SIZE, self.OnSize)
2904        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
2905        self.Bind(wx.EVT_TIMER, self.OnCheckFlyTimer, self._check_fly_timer)
2906        self.Bind(wx.EVT_TIMER, self.OnFlyTimer, self._fly_timer)
2907        self.Bind(EVT_AUI_FIND_MANAGER, self.OnFindManager)
2908
2909        if self._useNativeMiniframes:
2910            self.Bind(wx.EVT_MOVE, self.OnMoveEvent)
2911            self.Bind(wx.EVT_MOVING, self.OnMoveEvent)
2912            self.Bind(wx.EVT_IDLE, self.OnIdle)
2913            self._useNativeMiniframes = True
2914            self.SetExtraStyle(wx.WS_EX_PROCESS_IDLE)
2915        else:
2916            self.Bind(wx.EVT_MOVE, self.OnMove)
2917
2918        self._fly = False
2919        self._send_size = True
2920        self._alpha_amount = 255
2921
2922        self._owner_mgr = owner_mgr
2923        self._moving = False
2924        self._lastDirection = None
2925        self._transparent = 255
2926
2927        self._last_rect = wx.Rect()
2928        self._last2_rect = wx.Rect()
2929        self._last3_rect = wx.Rect()
2930
2931        self._mgr = AuiManager()
2932        self._mgr.SetManagedWindow(self)
2933        self._mgr.SetArtProvider(owner_mgr.GetArtProvider())
2934        self._mgr.SetAGWFlags(owner_mgr.GetAGWFlags())
2935
2936
2937    def CopyAttributes(self, pane):
2938        """
2939        Copies all the attributes of the input `pane` into another :class:`AuiPaneInfo`.
2940
2941        :param `pane`: the source :class:`AuiPaneInfo` from where to copy attributes.
2942        """
2943
2944        contained_pane = AuiPaneInfo()
2945
2946        contained_pane.name = pane.name
2947        contained_pane.caption = pane.caption
2948        contained_pane.window = pane.window
2949        contained_pane.frame = pane.frame
2950        contained_pane.state = pane.state
2951        contained_pane.dock_direction = pane.dock_direction
2952        contained_pane.dock_layer = pane.dock_layer
2953        contained_pane.dock_row = pane.dock_row
2954        contained_pane.dock_pos = pane.dock_pos
2955        contained_pane.best_size = wx.Size(*pane.best_size)
2956        contained_pane.min_size = wx.Size(*pane.min_size)
2957        contained_pane.max_size = wx.Size(*pane.max_size)
2958        contained_pane.floating_pos = wx.Point(*pane.floating_pos)
2959        contained_pane.floating_size = wx.Size(*pane.floating_size)
2960        contained_pane.dock_proportion = pane.dock_proportion
2961        contained_pane.buttons = pane.buttons
2962        contained_pane.rect = wx.Rect(*pane.rect)
2963        contained_pane.icon = pane.icon
2964        contained_pane.notebook_id = pane.notebook_id
2965        contained_pane.transparent = pane.transparent
2966        contained_pane.snapped = pane.snapped
2967        contained_pane.minimize_mode = pane.minimize_mode
2968        contained_pane.minimize_target = pane.minimize_target
2969
2970        return contained_pane
2971
2972
2973    def SetPaneWindow(self, pane):
2974        """
2975        Sets all the properties of a pane.
2976
2977        :param `pane`: the :class:`AuiPaneInfo` to analyze.
2978        """
2979
2980        self._is_toolbar = pane.IsToolbar()
2981        self._pane_window = pane.window
2982
2983        if isinstance(pane.window, auibar.AuiToolBar):
2984            pane.window.SetAuiManager(self._mgr)
2985
2986        self._pane_window.Reparent(self)
2987
2988        contained_pane = self.CopyAttributes(pane)
2989
2990        contained_pane.Dock().Center().Show(). \
2991                       CaptionVisible(False). \
2992                       PaneBorder(False). \
2993                       Layer(0).Row(0).Position(0)
2994
2995        if not contained_pane.HasGripper() and not self._useNativeMiniframes:
2996            contained_pane.CaptionVisible(True)
2997
2998        indx = self._owner_mgr._panes.index(pane)
2999
3000        # Carry over the minimum size
3001        pane_min_size = pane.window.GetMinSize()
3002
3003        # if the best size is smaller than the min size
3004        # then set the min size to the best size as well
3005        pane_best_size = contained_pane.best_size
3006        if pane_best_size.IsFullySpecified() and (pane_best_size.x < pane_min_size.x or \
3007                                                  pane_best_size.y < pane_min_size.y):
3008
3009            pane_min_size = pane_best_size
3010            self._pane_window.SetMinSize(pane_min_size)
3011
3012        # if the frame window's max size is greater than the min size
3013        # then set the max size to the min size as well
3014        cur_max_size = self.GetMaxSize()
3015        if cur_max_size.IsFullySpecified() and  (cur_max_size.x < pane_min_size.x or \
3016                                                 cur_max_size.y < pane_min_size.y):
3017            self.SetMaxSize(pane_min_size)
3018
3019        art_provider = self._mgr.GetArtProvider()
3020        caption_size = art_provider.GetMetric(AUI_DOCKART_CAPTION_SIZE)
3021        button_size = art_provider.GetMetric(AUI_DOCKART_PANE_BUTTON_SIZE) + \
3022                      4*art_provider.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
3023
3024        min_size = pane.window.GetMinSize()
3025
3026        if min_size.y < caption_size or min_size.x < button_size:
3027            new_x, new_y = min_size.x, min_size.y
3028            if min_size.y < caption_size:
3029                new_y = (pane.IsResizeable() and [2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_Y)+caption_size] or [1])[0]
3030            if min_size.x < button_size:
3031                new_x = (pane.IsResizeable() and [2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_X)+button_size] or [1])[0]
3032
3033            self.SetMinSize((new_x, new_y))
3034        else:
3035            self.SetMinSize(min_size)
3036
3037        self._mgr.AddPane(self._pane_window, contained_pane)
3038        self._mgr.DoUpdate()
3039
3040        if pane.min_size.IsFullySpecified():
3041            # because SetSizeHints() calls Fit() too (which sets the window
3042            # size to its minimum allowed), we keep the size before calling
3043            # SetSizeHints() and reset it afterwards...
3044            tmp = self.GetSize()
3045            self.GetSizer().SetSizeHints(self)
3046            self.SetSize(tmp)
3047
3048        self.SetTitle(pane.caption)
3049
3050        if pane.floating_size != wx.Size(-1, -1):
3051            self.SetSize(pane.floating_size)
3052        else:
3053            size = pane.best_size
3054            if size == wx.Size(-1, -1):
3055                size = pane.min_size
3056            if size == wx.Size(-1, -1):
3057                size = self._pane_window.GetSize()
3058            if self._owner_mgr and pane.HasGripper():
3059                if pane.HasGripperTop():
3060                    size.y += self._owner_mgr._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
3061                else:
3062                    size.x += self._owner_mgr._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
3063
3064            if not self._useNativeMiniframes:
3065                size.y += self._owner_mgr._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
3066
3067            pane.floating_size = size
3068
3069            self.SetClientSize(size)
3070
3071        self._owner_mgr._panes[indx] = pane
3072
3073        self._fly_step = abs(pane.floating_size.y - \
3074                             (caption_size + 2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_Y)))/10
3075
3076        self._floating_size = wx.Size(*self.GetSize())
3077
3078        if pane.IsFlyOut():
3079            self._check_fly_timer.Start(50)
3080
3081
3082    def GetOwnerManager(self):
3083        """ Returns the :class:`AuiManager` that manages the pane. """
3084
3085        return self._owner_mgr
3086
3087
3088    def OnSize(self, event):
3089        """
3090        Handles the ``wx.EVT_SIZE`` event for :class:`AuiFloatingFrame`.
3091
3092        :param `event`: a :class:`wx.SizeEvent` to be processed.
3093        """
3094
3095        if self._owner_mgr and self._send_size and self.IsShownOnScreen():
3096            self._owner_mgr.OnFloatingPaneResized(self._pane_window, event.GetSize())
3097
3098
3099    def OnClose(self, event):
3100        """
3101        Handles the ``wx.EVT_CLOSE`` event for :class:`AuiFloatingFrame`.
3102
3103        :param `event`: a :class:`CloseEvent` to be processed.
3104        """
3105        if self._owner_mgr:
3106            self._owner_mgr.OnFloatingPaneClosed(self._pane_window, event)
3107
3108        if not event.GetVeto():
3109            self._mgr.DetachPane(self._pane_window)
3110
3111            if isinstance(self._pane_window, auibar.AuiToolBar):
3112                self._pane_window.SetAuiManager(self._owner_mgr)
3113
3114            # if we do not do this, then we can crash...
3115            if self._owner_mgr and self._owner_mgr._action_window == self:
3116                self._owner_mgr._action_window = None
3117
3118            self._mgr.UnInit()
3119            self.Destroy()
3120
3121
3122    def OnActivate(self, event):
3123        """
3124        Handles the ``wx.EVT_ACTIVATE`` event for :class:`AuiFloatingFrame`.
3125
3126        :param `event`: a :class:`ActivateEvent` to be processed.
3127        """
3128
3129        if self._owner_mgr and event.GetActive():
3130            self._owner_mgr.OnFloatingPaneActivated(self._pane_window)
3131
3132
3133    def OnMove(self, event):
3134        """
3135        Handles the ``wx.EVT_MOVE`` event for :class:`AuiFloatingFrame`.
3136
3137        :param `event`: a :class:`MoveEvent` to be processed.
3138
3139        .. note::
3140
3141           This event is not processed on wxMAC or if :class:`AuiManager` is not using the
3142           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
3143
3144        """
3145
3146        if self._owner_mgr:
3147            self._owner_mgr.OnFloatingPaneMoved(self._pane_window, event)
3148
3149
3150    def OnMoveEvent(self, event):
3151        """
3152        Handles the ``wx.EVT_MOVE`` and ``wx.EVT_MOVING`` events for :class:`AuiFloatingFrame`.
3153
3154        :param `event`: a :class:`MoveEvent` to be processed.
3155
3156        .. note::
3157
3158           This event is only processed on wxMAC or if :class:`AuiManager` is using the
3159           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
3160        """
3161
3162        win_rect = self.GetRect()
3163
3164        if win_rect == self._last_rect:
3165            return
3166
3167        # skip the first move event
3168        if self._last_rect.IsEmpty():
3169            self._last_rect = wx.Rect(*win_rect)
3170            return
3171
3172        # As on OSX moving windows are not getting all move events, only sporadically, this difference
3173        # is almost always big on OSX, so avoid this early exit opportunity
3174        if wx.Platform != '__WXMAC__':
3175            # skip if moving too fast to avoid massive redraws and
3176            # jumping hint windows
3177            if abs(win_rect.x - self._last_rect.x) > 3 or abs(win_rect.y - self._last_rect.y) > 3:
3178                self._last3_rect = wx.Rect(*self._last2_rect)
3179                self._last2_rect = wx.Rect(*self._last_rect)
3180                self._last_rect = wx.Rect(*win_rect)
3181
3182                # However still update the internally stored position to avoid
3183                # snapping back to the old one later.
3184                if self._owner_mgr:
3185                    self._owner_mgr.GetPane(self._pane_window).floating_pos = win_rect.GetPosition()
3186
3187                return
3188
3189        # prevent frame redocking during resize
3190        if self._last_rect.GetSize() != win_rect.GetSize():
3191            self._last3_rect = wx.Rect(*self._last2_rect)
3192            self._last2_rect = wx.Rect(*self._last_rect)
3193            self._last_rect = wx.Rect(*win_rect)
3194            return
3195
3196        dir = wx.ALL
3197
3198        horiz_dist = abs(win_rect.x - self._last3_rect.x)
3199        vert_dist = abs(win_rect.y - self._last3_rect.y)
3200
3201        if vert_dist >= horiz_dist:
3202            if win_rect.y < self._last3_rect.y:
3203                dir = wx.NORTH
3204            else:
3205                dir = wx.SOUTH
3206        else:
3207            if win_rect.x < self._last3_rect.x:
3208                dir = wx.WEST
3209            else:
3210                dir = wx.EAST
3211
3212        self._last3_rect = wx.Rect(*self._last2_rect)
3213        self._last2_rect = wx.Rect(*self._last_rect)
3214        self._last_rect = wx.Rect(*win_rect)
3215
3216        if not wx.GetMouseState().LeftIsDown():
3217            return
3218
3219        if not self._moving:
3220            self.OnMoveStart(event)
3221            self._moving = True
3222
3223        if self._last3_rect.IsEmpty():
3224            return
3225
3226        if event.GetEventType() == wx.wxEVT_MOVING:
3227            self.OnMoving(event.GetRect(), dir)
3228        else:
3229            self.OnMoving(wx.Rect(event.GetPosition(), self.GetSize()), dir)
3230
3231
3232    def OnIdle(self, event):
3233        """
3234        Handles the ``wx.EVT_IDLE`` event for :class:`AuiFloatingFrame`.
3235
3236        :param `event`: a :class:`IdleEvent` event to be processed.
3237
3238        .. note::
3239
3240           This event is only processed on wxMAC if :class:`AuiManager` is using the
3241           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
3242
3243        """
3244
3245        if self._moving:
3246            if not wx.GetMouseState().LeftIsDown():
3247                self._moving = False
3248                self.OnMoveFinished()
3249            else:
3250                event.RequestMore()
3251
3252
3253    def OnMoveStart(self, event):
3254        """
3255        The user has just started moving the floating pane.
3256
3257        :param `event`: an instance of :class:`MouseEvent`.
3258
3259        .. note::
3260
3261           This event is only processed on wxMAC if :class:`AuiManager` is using the
3262           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
3263
3264        """
3265
3266        # notify the owner manager that the pane has started to move
3267        if self._owner_mgr:
3268            if self._owner_mgr._from_move:
3269                return
3270            self._owner_mgr._action_window = self._pane_window
3271            point = wx.GetMousePosition()
3272            action_offset = point - self.GetPosition()
3273
3274            if self._is_toolbar:
3275                self._owner_mgr._toolbar_action_offset = action_offset
3276                self._owner_mgr.OnMotion_DragToolbarPane(point)
3277            else:
3278                self._owner_mgr._action_offset = action_offset
3279                self._owner_mgr.OnMotion_DragFloatingPane(point)
3280
3281
3282    def OnMoving(self, rect, direction):
3283        """
3284        The user is moving the floating pane.
3285
3286        :param wx.Rect `rect`: the pane client rectangle;
3287        :param integer `direction`: the direction in which the pane is moving, can be one of
3288         ``wx.NORTH``, ``wx.SOUTH``, ``wx.EAST`` or ``wx.WEST``.
3289
3290        .. note::
3291
3292           This event is only processed on wxMAC if :class:`AuiManager` is using the
3293           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
3294        """
3295
3296        # notify the owner manager that the pane is moving
3297        self.OnMoveStart(None)
3298        self._lastDirection = direction
3299
3300
3301    def OnMoveFinished(self):
3302        """
3303        The user has just finished moving the floating pane.
3304
3305        .. note::
3306
3307           This method is used only on wxMAC if :class:`AuiManager` is using the
3308           ``AUI_MGR_USE_NATIVE_MINIFRAMES`` style.
3309
3310        """
3311
3312        # notify the owner manager that the pane has finished moving
3313        if self._owner_mgr:
3314            self._owner_mgr._action_window = self._pane_window
3315            point = wx.GetMousePosition()
3316            if self._is_toolbar:
3317                self._owner_mgr.OnLeftUp_DragToolbarPane(point)
3318            else:
3319                self._owner_mgr.OnLeftUp_DragFloatingPane(point)
3320
3321            self._owner_mgr.OnFloatingPaneMoved(self._pane_window, point)
3322
3323
3324    def OnCheckFlyTimer(self, event):
3325        """
3326        Handles the ``wx.EVT_TIMER`` event for :class:`AuiFloatingFrame`.
3327
3328        :param `event`: a :class:`TimerEvent` to be processed.
3329
3330        :note: This is used solely for "fly-out" panes.
3331        """
3332
3333        if self._owner_mgr:
3334            pane = self._mgr.GetPane(self._pane_window)
3335            if pane.IsFlyOut():
3336                if self.IsShownOnScreen():
3337                    self.FlyOut()
3338
3339
3340    def OnFindManager(self, event):
3341        """
3342        Handles the ``EVT_AUI_FIND_MANAGER`` event for :class:`AuiFloatingFrame`.
3343
3344        :param `event`: a :class:`AuiManagerEvent` event to be processed.
3345        """
3346
3347        event.SetManager(self._owner_mgr)
3348
3349
3350    def FlyOut(self):
3351        """ Starts the flying in and out of a floating pane. """
3352
3353        if self._fly_timer.IsRunning():
3354            return
3355
3356        if wx.GetMouseState().LeftIsDown():
3357            return
3358
3359        rect = wx.Rect(*self.GetScreenRect())
3360        rect.Inflate(10, 10)
3361
3362        if rect.Contains(wx.GetMousePosition()):
3363            if not self._fly:
3364                return
3365            self._send_size = False
3366            self._fly_timer.Start(5)
3367        else:
3368            if self._fly:
3369                return
3370            self._send_size = False
3371            self._fly_timer.Start(5)
3372
3373
3374    def OnFlyTimer(self, event):
3375        """
3376        Handles the ``wx.EVT_TIMER`` event for :class:`AuiFloatingFrame`.
3377
3378        :param `event`: a :class:`TimerEvent` to be processed.
3379        """
3380
3381        current_size = self.GetClientSize()
3382        floating_size = wx.Size(*self._owner_mgr.GetPane(self._pane_window).floating_size)
3383
3384        if floating_size.y == -1:
3385            floating_size = self._floating_size
3386
3387        if not self._fly:
3388            min_size = self._mgr.GetArtProvider().GetMetric(AUI_DOCKART_CAPTION_SIZE)
3389
3390            if wx.Platform != "__WXMSW__":
3391                min_size += 2*wx.SystemSettings.GetMetric(wx.SYS_EDGE_Y)
3392
3393            if current_size.y - self._fly_step <= min_size:
3394                self.SetClientSize((current_size.x, min_size))
3395                self._fly = True
3396                self._fly_timer.Stop()
3397                self._send_size = True
3398            else:
3399                self.SetClientSize((current_size.x, current_size.y-self._fly_step))
3400
3401        else:
3402            if current_size.y + self._fly_step >= floating_size.y:
3403                self.SetClientSize((current_size.x, floating_size.y))
3404                self._fly = False
3405                self._fly_timer.Stop()
3406                self._send_size = True
3407            else:
3408                self.SetClientSize((current_size.x, current_size.y+self._fly_step))
3409
3410        self.Update()
3411        self.Refresh()
3412
3413
3414    def FadeOut(self):
3415        """ Actually starts the fading out of the floating pane. """
3416
3417        while 1:
3418            self._alpha_amount -= 10
3419            if self._alpha_amount <= 0:
3420                self._alpha_amount = 255
3421                return
3422
3423            self.SetTransparent(self._alpha_amount)
3424            wx.SafeYield()
3425            wx.MilliSleep(15)
3426
3427
3428# -- static utility functions --
3429
3430def DrawResizeHint(dc, rect):
3431    """
3432    Draws a resize hint while a sash is dragged.
3433
3434    :param wx.Rect `rect`: a rectangle which specifies the sash dimensions.
3435    """
3436
3437    if wx.Platform == "__WXMSW__" and wx.App.GetComCtl32Version() >= 600:
3438        if wx.GetOsVersion()[1] > 5:
3439            # Windows Vista
3440            dc.SetPen(wx.Pen("black", 2, wx.PENSTYLE_SOLID))
3441            dc.SetBrush(wx.TRANSPARENT_BRUSH)
3442        else:
3443            # Draw the nice XP style splitter
3444            dc.SetPen(wx.TRANSPARENT_PEN)
3445            dc.SetBrush(wx.BLACK_BRUSH)
3446        dc.SetLogicalFunction(wx.INVERT)
3447        dc.DrawRectangle(rect)
3448        dc.SetLogicalFunction(wx.COPY)
3449    else:
3450        stipple = PaneCreateStippleBitmap()
3451        brush = wx.Brush(stipple)
3452        dc.SetBrush(brush)
3453        dc.SetPen(wx.TRANSPARENT_PEN)
3454
3455        dc.SetLogicalFunction(wx.XOR)
3456        dc.DrawRectangle(rect)
3457
3458
3459def CopyDocksAndPanes(src_docks, src_panes):
3460    """
3461    This utility function creates shallow copies of
3462    the dock and pane info. :class:`AuiManager` usually contain pointers
3463    to :class:`AuiPaneInfo` classes, thus this function is necessary to reliably
3464    reconstruct that relationship in the new dock info and pane info arrays.
3465
3466    :param `src_docks`: a list of :class:`AuiDockInfo` classes;
3467    :param `src_panes`: a list of :class:`AuiPaneInfo` classes.
3468    """
3469
3470    dest_docks = src_docks
3471    dest_panes = src_panes
3472
3473    for ii in range(len(dest_docks)):
3474        dock = dest_docks[ii]
3475        for jj in range(len(dock.panes)):
3476            for kk in range(len(src_panes)):
3477                if dock.panes[jj] == src_panes[kk]:
3478                    dock.panes[jj] = dest_panes[kk]
3479
3480    return dest_docks, dest_panes
3481
3482
3483def CopyDocksAndPanes2(src_docks, src_panes):
3484    """
3485    This utility function creates full copies of
3486    the dock and pane info. :class:`AuiManager` usually contain pointers
3487    to :class:`AuiPaneInfo` classes, thus this function is necessary to reliably
3488    reconstruct that relationship in the new dock info and pane info arrays.
3489
3490    :param `src_docks`: a list of :class:`AuiDockInfo` classes;
3491    :param `src_panes`: a list of :class:`AuiPaneInfo` classes.
3492    """
3493
3494    dest_docks = []
3495
3496    for ii in range(len(src_docks)):
3497        dest_docks.append(AuiDockInfo())
3498        dest_docks[ii].dock_direction = src_docks[ii].dock_direction
3499        dest_docks[ii].dock_layer = src_docks[ii].dock_layer
3500        dest_docks[ii].dock_row = src_docks[ii].dock_row
3501        dest_docks[ii].size = src_docks[ii].size
3502        dest_docks[ii].min_size = src_docks[ii].min_size
3503        dest_docks[ii].resizable = src_docks[ii].resizable
3504        dest_docks[ii].fixed = src_docks[ii].fixed
3505        dest_docks[ii].toolbar = src_docks[ii].toolbar
3506        dest_docks[ii].panes = src_docks[ii].panes
3507        dest_docks[ii].rect = wx.Rect(*src_docks[ii].rect)
3508
3509    dest_panes = []
3510
3511    for ii in range(len(src_panes)):
3512        dest_panes.append(AuiPaneInfo())
3513        dest_panes[ii].name = src_panes[ii].name
3514        dest_panes[ii].caption = src_panes[ii].caption
3515        dest_panes[ii].window = src_panes[ii].window
3516        dest_panes[ii].frame = src_panes[ii].frame
3517        dest_panes[ii].state = src_panes[ii].state
3518        dest_panes[ii].dock_direction = src_panes[ii].dock_direction
3519        dest_panes[ii].dock_layer = src_panes[ii].dock_layer
3520        dest_panes[ii].dock_row = src_panes[ii].dock_row
3521        dest_panes[ii].dock_pos = src_panes[ii].dock_pos
3522        dest_panes[ii].best_size = wx.Size(*src_panes[ii].best_size)
3523        dest_panes[ii].min_size = wx.Size(*src_panes[ii].min_size)
3524        dest_panes[ii].max_size = wx.Size(*src_panes[ii].max_size)
3525        dest_panes[ii].floating_pos = wx.Point(*src_panes[ii].floating_pos)
3526        dest_panes[ii].floating_size = wx.Size(*src_panes[ii].floating_size)
3527        dest_panes[ii].dock_proportion = src_panes[ii].dock_proportion
3528        dest_panes[ii].buttons = src_panes[ii].buttons
3529        dest_panes[ii].rect = wx.Rect(*src_panes[ii].rect)
3530        dest_panes[ii].icon = src_panes[ii].icon
3531        dest_panes[ii].notebook_id = src_panes[ii].notebook_id
3532        dest_panes[ii].transparent = src_panes[ii].transparent
3533        dest_panes[ii].snapped = src_panes[ii].snapped
3534        dest_panes[ii].minimize_mode = src_panes[ii].minimize_mode
3535        dest_panes[ii].minimize_target = src_panes[ii].minimize_target
3536
3537    for ii in range(len(dest_docks)):
3538        dock = dest_docks[ii]
3539        for jj in range(len(dock.panes)):
3540            for kk in range(len(src_panes)):
3541                if dock.panes[jj] == src_panes[kk]:
3542                    dock.panes[jj] = dest_panes[kk]
3543
3544        dest_docks[ii] = dock
3545
3546    return dest_docks, dest_panes
3547
3548
3549def GetMaxLayer(docks, dock_direction):
3550    """
3551    This is an internal function which returns
3552    the highest layer inside the specified dock.
3553
3554    :param `docks`: a list of :class:`AuiDockInfo`;
3555    :param `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze.
3556    """
3557
3558    max_layer = 0
3559
3560    for dock in docks:
3561        if dock.dock_direction == dock_direction and dock.dock_layer > max_layer and not dock.fixed:
3562            max_layer = dock.dock_layer
3563
3564    return max_layer
3565
3566
3567def GetMaxRow(panes, dock_direction, dock_layer):
3568    """
3569    This is an internal function which returns
3570    the highest layer inside the specified dock.
3571
3572    :param `panes`: a list of :class:`AuiPaneInfo`;
3573    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
3574    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
3575    """
3576
3577    max_row = 0
3578
3579    for pane in panes:
3580        if pane.dock_direction == dock_direction and pane.dock_layer == dock_layer and \
3581           pane.dock_row > max_row:
3582            max_row = pane.dock_row
3583
3584    return max_row
3585
3586
3587def DoInsertDockLayer(panes, dock_direction, dock_layer):
3588    """
3589    This is an internal function that inserts a new dock
3590    layer by incrementing all existing dock layer values by one.
3591
3592    :param `panes`: a list of :class:`AuiPaneInfo`;
3593    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
3594    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
3595    """
3596
3597    for ii in range(len(panes)):
3598        pane = panes[ii]
3599        if not pane.IsFloating() and pane.dock_direction == dock_direction and pane.dock_layer >= dock_layer:
3600            pane.dock_layer = pane.dock_layer + 1
3601
3602        panes[ii] = pane
3603
3604    return panes
3605
3606
3607def DoInsertDockRow(panes, dock_direction, dock_layer, dock_row):
3608    """
3609    This is an internal function that inserts a new dock
3610    row by incrementing all existing dock row values by one.
3611
3612    :param `panes`: a list of :class:`AuiPaneInfo`;
3613    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
3614    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze;
3615    :param integer `dock_row`: the :class:`AuiDockInfo` row to analyze.
3616    """
3617
3618    for pane in panes:
3619        if not pane.IsFloating() and pane.dock_direction == dock_direction and \
3620           pane.dock_layer == dock_layer and pane.dock_row >= dock_row:
3621            pane.dock_row += 1
3622
3623    return panes
3624
3625
3626def DoInsertPane(panes, dock_direction, dock_layer, dock_row, dock_pos):
3627    """
3628    This is an internal function that inserts a new pane
3629    by incrementing all existing dock position values by one.
3630
3631    :param `panes`: a list of :class:`AuiPaneInfo`;
3632    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
3633    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
3634    :param integer `dock_row`: the :class:`AuiDockInfo` row to analyze;
3635    :param integer `dock_pos`: the :class:`AuiDockInfo` position to analyze.
3636    """
3637
3638    for ii in range(len(panes)):
3639        pane = panes[ii]
3640        if not pane.IsFloating() and pane.dock_direction == dock_direction and \
3641           pane.dock_layer == dock_layer and  pane.dock_row == dock_row and \
3642           pane.dock_pos >= dock_pos:
3643            pane.dock_pos = pane.dock_pos + 1
3644
3645        panes[ii] = pane
3646
3647    return panes
3648
3649
3650def FindDocks(docks, dock_direction, dock_layer=-1, dock_row=-1, reverse=False):
3651    """
3652    This is an internal function that returns a list of docks which meet
3653    the specified conditions in the parameters and returns a sorted array
3654    (sorted by layer and then row).
3655
3656    :param `docks`: a list of :class:`AuiDockInfo`;
3657    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
3658    :param integer `dock_layer`: the :class:`AuiDockInfo` layer to analyze.
3659    :param integer `dock_row`: the :class:`AuiDockInfo` row to analyze;
3660    """
3661
3662    matchDocks = [(d.dock_layer, d.dock_row, d.dock_direction, d) for d in docks if \
3663                  (dock_direction == -1 or dock_direction == d.dock_direction) and \
3664                  ((dock_layer == -1 or dock_layer == d.dock_layer) and \
3665                  (dock_row == -1 or dock_row == d.dock_row))]
3666
3667    arr = [x[-1] for x in sorted(matchDocks, reverse=reverse)]
3668
3669    return arr
3670
3671
3672def FindOppositeDocks(docks, dock_direction):
3673    """
3674    This is an internal function that returns a list of docks
3675    which is related to the opposite direction.
3676
3677    :param `docks`: a list of :class:`AuiDockInfo`;
3678    :param integer `dock_direction`: the :class:`AuiDockInfo` docking direction to analyze;
3679    """
3680
3681    if dock_direction == AUI_DOCK_LEFT:
3682        arr = FindDocks(docks, AUI_DOCK_RIGHT, -1, -1)
3683    elif dock_direction == AUI_DOCK_TOP:
3684        arr = FindDocks(docks, AUI_DOCK_BOTTOM, -1, -1)
3685    elif dock_direction == AUI_DOCK_RIGHT:
3686        arr = FindDocks(docks, AUI_DOCK_LEFT, -1, -1)
3687    elif dock_direction == AUI_DOCK_BOTTOM:
3688        arr = FindDocks(docks, AUI_DOCK_TOP, -1, -1)
3689
3690    return arr
3691
3692
3693def FindPaneInDock(dock, window):
3694    """
3695    This method looks up a specified window pointer inside a dock.
3696    If found, the corresponding :class:`AuiDockInfo` pointer is returned, otherwise ``None``.
3697
3698    :param `dock`: a :class:`AuiDockInfo` structure;
3699    :param wx.Window `window`: the window associated to the pane we are seeking.
3700    """
3701
3702    for p in dock.panes:
3703        if p.window == window:
3704            return p
3705
3706    return None
3707
3708
3709def GetToolBarDockOffsets(docks):
3710    """
3711    Returns the toolbar dock offsets (top-left and bottom-right).
3712
3713    :param `docks`: a list of :class:`AuiDockInfo` to analyze.
3714    """
3715
3716    top_left = wx.Size(0, 0)
3717    bottom_right = wx.Size(0, 0)
3718
3719    for dock in docks:
3720        if dock.toolbar:
3721            dock_direction = dock.dock_direction
3722            if dock_direction == AUI_DOCK_LEFT:
3723                top_left.x += dock.rect.width
3724                bottom_right.x += dock.rect.width
3725
3726            elif dock_direction == AUI_DOCK_TOP:
3727                top_left.y += dock.rect.height
3728                bottom_right.y += dock.rect.height
3729
3730            elif dock_direction == AUI_DOCK_RIGHT:
3731                bottom_right.x += dock.rect.width
3732
3733            elif dock_direction == AUI_DOCK_BOTTOM:
3734                bottom_right.y += dock.rect.height
3735
3736    return top_left, bottom_right
3737
3738
3739def GetInternalFrameRect(window, docks):
3740    """
3741    Returns the window rectangle excluding toolbars.
3742
3743    :param `window`: a :class:`wx.Window` derived window;
3744    :param `docks`: a list of :class:`AuiDockInfo` structures.
3745    """
3746
3747    frameRect = wx.Rect()
3748
3749    frameRect.SetTopLeft(window.ClientToScreen(window.GetClientAreaOrigin()))
3750    frameRect.SetSize(window.GetClientSize())
3751
3752    top_left, bottom_right = GetToolBarDockOffsets(docks)
3753
3754    # make adjustments for toolbars
3755    frameRect.x += top_left.x
3756    frameRect.y += top_left.y
3757    frameRect.width -= bottom_right.x
3758    frameRect.height -= bottom_right.y
3759
3760    return frameRect
3761
3762
3763def CheckOutOfWindow(window, pt):
3764    """
3765    Checks if a point is outside the window rectangle.
3766
3767    :param `window`: a :class:`wx.Window` derived window;
3768    :param `pt`: a :class:`wx.Point` object.
3769    """
3770
3771    auiWindowMargin = 30
3772    marginRect = wx.Rect(*window.GetClientRect())
3773    marginRect.Inflate(auiWindowMargin, auiWindowMargin)
3774
3775    return not marginRect.Contains(pt)
3776
3777
3778def CheckEdgeDrop(window, docks, pt):
3779    """
3780    Checks on which edge of a window the drop action has taken place.
3781
3782    :param `window`: a :class:`wx.Window` derived window;
3783    :param `docks`: a list of :class:`AuiDockInfo` structures;
3784    :param `pt`: a :class:`wx.Point` object.
3785    """
3786
3787    screenPt = window.ClientToScreen(pt)
3788    clientSize = window.GetClientSize()
3789    frameRect = GetInternalFrameRect(window, docks)
3790
3791    if screenPt.y >= frameRect.GetTop() and screenPt.y < frameRect.GetBottom():
3792        if pt.x < auiLayerInsertOffset and pt.x > auiLayerInsertOffset - auiLayerInsertPixels:
3793            return wx.LEFT
3794
3795        if pt.x >= clientSize.x - auiLayerInsertOffset and \
3796           pt.x < clientSize.x - auiLayerInsertOffset + auiLayerInsertPixels:
3797            return wx.RIGHT
3798
3799    if screenPt.x >= frameRect.GetLeft() and screenPt.x < frameRect.GetRight():
3800        if pt.y < auiLayerInsertOffset and pt.y > auiLayerInsertOffset - auiLayerInsertPixels:
3801            return wx.TOP
3802
3803        if pt.y >= clientSize.y - auiLayerInsertOffset and \
3804           pt.y < clientSize.y - auiLayerInsertOffset + auiLayerInsertPixels:
3805            return wx.BOTTOM
3806
3807    return -1
3808
3809
3810def RemovePaneFromDocks(docks, pane, exc=None):
3811    """
3812    Removes a pane window from all docks
3813    with a possible exception specified by parameter `exc`.
3814
3815    :param `docks`: a list of :class:`AuiDockInfo` structures;
3816    :param `pane`: the pane to be removed, an instance of :class:`AuiPaneInfo`;
3817    :param `exc`: the possible pane exception, an instance of :class:`AuiPaneInfo`.
3818    """
3819
3820    for ii in range(len(docks)):
3821        d = docks[ii]
3822        if d == exc:
3823            continue
3824        pi = FindPaneInDock(d, pane.window)
3825        if pi:
3826            d.panes.remove(pi)
3827
3828        docks[ii] = d
3829
3830    return docks
3831
3832
3833def RenumberDockRows(docks):
3834    """
3835    Takes a dock and assigns sequential numbers
3836    to existing rows.  Basically it takes out the gaps so if a
3837    dock has rows with numbers 0, 2, 5, they will become 0, 1, 2.
3838
3839    :param `docks`: a list of :class:`AuiDockInfo` structures.
3840    """
3841
3842    for ii in range(len(docks)):
3843        dock = docks[ii]
3844        dock.dock_row = ii
3845        for jj in range(len(dock.panes)):
3846            dock.panes[jj].dock_row = ii
3847
3848        docks[ii] = dock
3849
3850    return docks
3851
3852
3853def SetActivePane(panes, active_pane):
3854    """
3855    Sets the active pane, as well as cycles through
3856    every other pane and makes sure that all others' active flags
3857    are turned off.
3858
3859    :param `panes`: a list of :class:`AuiPaneInfo` structures;
3860    :param `active_pane`: the pane to be made active (if found), an instance of :class:`AuiPaneInfo`.
3861    """
3862
3863    for pane in panes:
3864        pane.state &= ~AuiPaneInfo.optionActive
3865
3866    for pane in panes:
3867        if pane.window == active_pane and not pane.IsNotebookPage():
3868            pane.state |= AuiPaneInfo.optionActive
3869            return True, panes
3870
3871    return False, panes
3872
3873
3874def ShowDockingGuides(guides, show):
3875    """
3876    Shows or hide the docking guide windows.
3877
3878    :param `guides`: a list of :class:`AuiDockingGuide` classes;
3879    :param bool `show`: whether to show or hide the docking guide windows.
3880    """
3881
3882    for target in guides:
3883
3884        if show and not target.host.IsShown():
3885            target.host.Show()
3886            target.host.Update()
3887
3888        elif not show and target.host.IsShown():
3889            target.host.Hide()
3890
3891
3892def RefreshDockingGuides(guides):
3893    """
3894    Refreshes the docking guide windows.
3895
3896    :param `guides`: a list of :class:`AuiDockingGuide` classes;
3897    """
3898
3899    for target in guides:
3900        if target.host.IsShown():
3901            target.host.Refresh()
3902
3903
3904def PaneSortFunc(p1, p2):
3905    """
3906    This function is used to sort panes by dock position.
3907
3908    :param `p1`: the first pane instance to compare, an instance of :class:`AuiPaneInfo`;
3909    :param `p2`: the second pane instance to compare, an instance of :class:`AuiPaneInfo`.
3910    """
3911
3912    return (p1.dock_pos < p2.dock_pos and [-1] or [1])[0]
3913
3914
3915def GetNotebookRoot(panes, notebook_id):
3916    """
3917    Returns the :class:`~wx.lib.agw.aui.auibook.AuiNotebook` which has the specified `notebook_id`.
3918
3919    :param `panes`: a list of :class:`AuiPaneInfo` instances;
3920    :param integer `notebook_id`: the target notebook id.
3921    """
3922
3923    for paneInfo in panes:
3924        if paneInfo.IsNotebookControl() and paneInfo.notebook_id == notebook_id:
3925            return paneInfo
3926
3927    return None
3928
3929
3930def EscapeDelimiters(s):
3931    """
3932    Changes ``;`` into ``\`` and ``|`` into ``\|`` in the input string.
3933
3934    :param string `s`: the string to be analyzed.
3935
3936    :note: This is an internal functions which is used for saving perspectives.
3937    """
3938
3939    result = s.replace(";", "\\")
3940    result = result.replace("|", "|\\")
3941
3942    return result
3943
3944
3945def IsDifferentDockingPosition(pane1, pane2):
3946    """
3947    Returns whether `pane1` and `pane2` are in a different docking position
3948    based on pane status, docking direction, docking layer and docking row.
3949
3950    :param `pane1`: a :class:`AuiPaneInfo` instance;
3951    :param `pane2`: another :class:`AuiPaneInfo` instance.
3952    """
3953
3954    return pane1.IsFloating() != pane2.IsFloating() or \
3955           pane1.dock_direction != pane2.dock_direction or \
3956           pane1.dock_layer != pane2.dock_layer or \
3957           pane1.dock_row != pane2.dock_row
3958
3959
3960# Convenience function
3961def AuiManager_HasLiveResize(manager):
3962    """
3963    Static function which returns if the input `manager` should have "live resize"
3964    behaviour.
3965
3966    :param `manager`: an instance of :class:`AuiManager`.
3967
3968    .. note::
3969
3970       This method always returns ``True`` on wxMAC as this platform doesn't have
3971       the ability to use :class:`ScreenDC` to draw sashes.
3972
3973    """
3974
3975    # With Core Graphics on Mac, it's not possible to show sash feedback,
3976    # so we'll always use live update instead.
3977
3978    if wx.Platform == "__WXMAC__":
3979        return True
3980    else:
3981        return (manager.GetAGWFlags() & AUI_MGR_LIVE_RESIZE) == AUI_MGR_LIVE_RESIZE
3982
3983
3984# Convenience function
3985def AuiManager_UseNativeMiniframes(manager):
3986    """
3987    Static function which returns if the input `manager` should use native :class:`MiniFrame` as
3988    floating panes.
3989
3990    :param `manager`: an instance of :class:`AuiManager`.
3991
3992    .. note::
3993
3994       This method always returns ``True`` on wxMAC as this platform doesn't have
3995       the ability to use custom drawn miniframes.
3996
3997    """
3998
3999    # With Core Graphics on Mac, it's not possible to show sash feedback,
4000    # so we'll always use live update instead.
4001
4002    if wx.Platform == "__WXMAC__":
4003        return True
4004    else:
4005        return (manager.GetAGWFlags() & AUI_MGR_USE_NATIVE_MINIFRAMES) == AUI_MGR_USE_NATIVE_MINIFRAMES
4006
4007
4008def GetManager(window):
4009    """
4010    This function will return the aui manager for a given window.
4011
4012    :param wx.Window `window`: this parameter should be any child window or grand-child
4013     window (and so on) of the frame/window managed by :class:`AuiManager`. The window
4014     does not need to be managed by the manager itself, nor does it even need
4015     to be a child or sub-child of a managed window. It must however be inside
4016     the window hierarchy underneath the managed window.
4017    """
4018
4019    if not isinstance(wx.GetTopLevelParent(window), AuiFloatingFrame):
4020        if isinstance(window, auibar.AuiToolBar):
4021            return window.GetAuiManager()
4022
4023    evt = AuiManagerEvent(wxEVT_AUI_FIND_MANAGER)
4024    evt.SetManager(None)
4025    evt.ResumePropagation(wx.EVENT_PROPAGATE_MAX)
4026
4027    if not window.GetEventHandler().ProcessEvent(evt):
4028        return None
4029
4030    return evt.GetManager()
4031
4032
4033# ---------------------------------------------------------------------------- #
4034
4035class AuiManager(wx.EvtHandler):
4036    """
4037    AuiManager manages the panes associated with it for a particular :class:`wx.Frame`,
4038    using a pane's :class:`AuiManager` information to determine each pane's docking and
4039    floating behavior. :class:`AuiManager` uses wxPython's sizer mechanism to plan the
4040    layout of each frame. It uses a replaceable dock art class to do all drawing,
4041    so all drawing is localized in one area, and may be customized depending on an
4042    applications' specific needs.
4043
4044    :class:`AuiManager` works as follows: the programmer adds panes to the class, or makes
4045    changes to existing pane properties (dock position, floating state, show state, etc...).
4046    To apply these changes, the :meth:`AuiManager.Update() <AuiManager.Update>` function is called. This batch
4047    processing can be used to avoid flicker, by modifying more than one pane at a time,
4048    and then "committing" all of the changes at once by calling `Update()`.
4049
4050    Panes can be added quite easily::
4051
4052        text1 = wx.TextCtrl(self, -1)
4053        text2 = wx.TextCtrl(self, -1)
4054        self._mgr.AddPane(text1, AuiPaneInfo().Left().Caption("Pane Number One"))
4055        self._mgr.AddPane(text2, AuiPaneInfo().Bottom().Caption("Pane Number Two"))
4056
4057        self._mgr.Update()
4058
4059
4060    Later on, the positions can be modified easily. The following will float an
4061    existing pane in a tool window::
4062
4063        self._mgr.GetPane(text1).Float()
4064
4065
4066    **Layers, Rows and Directions, Positions:**
4067
4068    Inside AUI, the docking layout is figured out by checking several pane parameters.
4069    Four of these are important for determining where a pane will end up.
4070
4071    **Direction** - Each docked pane has a direction, `Top`, `Bottom`, `Left`, `Right`, or `Center`.
4072    This is fairly self-explanatory. The pane will be placed in the location specified
4073    by this variable.
4074
4075    **Position** - More than one pane can be placed inside of a "dock". Imagine two panes
4076    being docked on the left side of a window. One pane can be placed over another.
4077    In proportionally managed docks, the pane position indicates it's sequential position,
4078    starting with zero. So, in our scenario with two panes docked on the left side, the
4079    top pane in the dock would have position 0, and the second one would occupy position 1.
4080
4081    **Row** - A row can allow for two docks to be placed next to each other. One of the most
4082    common places for this to happen is in the toolbar. Multiple toolbar rows are allowed,
4083    the first row being in row 0, and the second in row 1. Rows can also be used on
4084    vertically docked panes.
4085
4086    **Layer** - A layer is akin to an onion. Layer 0 is the very center of the managed pane.
4087    Thus, if a pane is in layer 0, it will be closest to the center window (also sometimes
4088    known as the "content window"). Increasing layers "swallow up" all layers of a lower
4089    value. This can look very similar to multiple rows, but is different because all panes
4090    in a lower level yield to panes in higher levels. The best way to understand layers
4091    is by running the AUI sample (`AUI.py`).
4092    """
4093
4094    def __init__(self, managed_window=None, agwFlags=None):
4095        """
4096        Default class constructor.
4097
4098        :param wx.Window `managed_window`: specifies the window which should be managed;
4099        :param integer `agwFlags`: specifies options which allow the frame management behavior to be
4100         modified. `agwFlags` can be a combination of the following style bits:
4101
4102         ==================================== ==================================
4103         Flag name                            Description
4104         ==================================== ==================================
4105         ``AUI_MGR_ALLOW_FLOATING``           Allow floating of panes
4106         ``AUI_MGR_ALLOW_ACTIVE_PANE``        If a pane becomes active, "highlight" it in the interface
4107         ``AUI_MGR_TRANSPARENT_DRAG``         If the platform supports it, set transparency on a floating pane while it is dragged by the user
4108         ``AUI_MGR_TRANSPARENT_HINT``         If the platform supports it, show a transparent hint window when the user is about to dock a floating pane
4109         ``AUI_MGR_VENETIAN_BLINDS_HINT``     Show a "venetian blind" effect when the user is about to dock a floating pane
4110         ``AUI_MGR_RECTANGLE_HINT``           Show a rectangle hint effect when the user is about to dock a floating pane
4111         ``AUI_MGR_HINT_FADE``                If the platform supports it, the hint window will fade in and out
4112         ``AUI_MGR_NO_VENETIAN_BLINDS_FADE``  Disables the "venetian blind" fade in and out
4113         ``AUI_MGR_LIVE_RESIZE``              Live resize when the user drag a sash
4114         ``AUI_MGR_ANIMATE_FRAMES``           Fade-out floating panes when they are closed (all platforms which support frames transparency)
4115                                              and show a moving rectangle when they are docked (Windows < Vista and GTK only)
4116         ``AUI_MGR_AERO_DOCKING_GUIDES``      Use the new Aero-style bitmaps as docking guides
4117         ``AUI_MGR_PREVIEW_MINIMIZED_PANES``  Slide in and out minimized panes to preview them
4118         ``AUI_MGR_WHIDBEY_DOCKING_GUIDES``   Use the new Whidbey-style bitmaps as docking guides
4119         ``AUI_MGR_SMOOTH_DOCKING``           Performs a "smooth" docking of panes (a la PyQT)
4120         ``AUI_MGR_USE_NATIVE_MINIFRAMES``    Use miniframes with native caption bar as floating panes instead or custom drawn caption bars (forced on wxMAC)
4121         ``AUI_MGR_AUTONB_NO_CAPTION``        Panes that merge into an automatic notebook will not have the pane caption visible
4122         ==================================== ==================================
4123
4124         Default value for `agwFlags` is:
4125         ``AUI_MGR_DEFAULT`` = ``AUI_MGR_ALLOW_FLOATING`` | ``AUI_MGR_TRANSPARENT_HINT`` | ``AUI_MGR_HINT_FADE`` | ``AUI_MGR_NO_VENETIAN_BLINDS_FADE``
4126
4127         .. note::
4128
4129            If using the ``AUI_MGR_USE_NATIVE_MINIFRAMES``, double-clicking on a
4130            floating pane caption will not re-dock the pane, but simply maximize it (if
4131            :meth:`AuiPaneInfo.MaximizeButton` has been set to ``True``) or do nothing.
4132
4133        """
4134
4135        wx.EvtHandler.__init__(self)
4136
4137        self._action = actionNone
4138        self._action_window = None
4139        self._hover_button = None
4140        self._art = dockart.AuiDefaultDockArt()
4141        self._hint_window = None
4142        self._active_pane = None
4143        self._has_maximized = False
4144        self._has_minimized = False
4145
4146        self._frame = None
4147        self._dock_constraint_x = 0.3
4148        self._dock_constraint_y = 0.3
4149        self._reserved = None
4150
4151        self._panes = []
4152        self._docks = []
4153        self._uiparts = []
4154
4155        self._guides = []
4156        self._notebooks = []
4157
4158        self._masterManager = None
4159        self._currentDragItem = -1
4160        self._lastknowndocks = {}
4161
4162        self._hint_fadetimer = wx.Timer(self, wx.ID_ANY)
4163        self._hint_fademax = 50
4164        self._last_hint = wx.Rect()
4165
4166        self._from_move = False
4167        self._last_rect = wx.Rect()
4168
4169        if agwFlags is None:
4170            agwFlags = AUI_MGR_DEFAULT
4171
4172        self._agwFlags = agwFlags
4173        self._is_docked = (False, wx.RIGHT, wx.TOP, 0)
4174        self._snap_limits = (15, 15)
4175
4176        if wx.Platform == "__WXMSW__":
4177            self._animation_step = 30.0
4178        else:
4179            self._animation_step = 5.0
4180
4181        self._hint_rect = wx.Rect()
4182
4183        self._preview_timer = wx.Timer(self, wx.ID_ANY)
4184        self._sliding_frame = None
4185
4186        self._autoNBTabArt = tabart.AuiDefaultTabArt()
4187        self._autoNBStyle = AUI_NB_DEFAULT_STYLE | AUI_NB_BOTTOM | \
4188                            AUI_NB_SUB_NOTEBOOK | AUI_NB_TAB_EXTERNAL_MOVE
4189        self._autoNBStyle -= AUI_NB_DRAW_DND_TAB
4190
4191        if managed_window:
4192            self.SetManagedWindow(managed_window)
4193
4194        self.Bind(wx.EVT_PAINT, self.OnPaint)
4195        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
4196        self.Bind(wx.EVT_SIZE, self.OnSize)
4197        self.Bind(wx.EVT_SET_CURSOR, self.OnSetCursor)
4198        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
4199        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
4200        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
4201        self.Bind(wx.EVT_MOTION, self.OnMotion)
4202        self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
4203        self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus)
4204        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost)
4205        self.Bind(wx.EVT_TIMER, self.OnHintFadeTimer, self._hint_fadetimer)
4206        self.Bind(wx.EVT_TIMER, self.SlideIn, self._preview_timer)
4207        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
4208        self.Bind(wx.EVT_CLOSE, self.OnClose)
4209        if '__WXGTK__' in wx.PlatformInfo:
4210            self.Bind(wx.EVT_WINDOW_CREATE, self.DoUpdateEvt)
4211
4212        self.Bind(wx.EVT_MOVE, self.OnMove)
4213        self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged)
4214
4215        self.Bind(EVT_AUI_PANE_BUTTON, self.OnPaneButton)
4216        self.Bind(EVT_AUI_RENDER, self.OnRender)
4217        self.Bind(EVT_AUI_FIND_MANAGER, self.OnFindManager)
4218        self.Bind(EVT_AUI_PANE_MIN_RESTORE, self.OnRestoreMinimizedPane)
4219        self.Bind(EVT_AUI_PANE_DOCKED, self.OnPaneDocked)
4220
4221        self.Bind(auibook.EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnTabBeginDrag)
4222        self.Bind(auibook.EVT_AUINOTEBOOK_END_DRAG, self.OnTabEndDrag)
4223        self.Bind(auibook.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnTabPageClose)
4224        self.Bind(auibook.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnTabSelected)
4225
4226
4227    def CreateFloatingFrame(self, parent, pane_info):
4228        """
4229        Creates a floating frame for the windows.
4230
4231        :param wx.Window `parent`: the floating frame parent;
4232        :param `pane_info`: the :class:`AuiPaneInfo` class with all the pane's information.
4233        """
4234
4235        return AuiFloatingFrame(parent, self, pane_info)
4236
4237
4238    def CanDockPanel(self, p):
4239        """
4240        Returns whether a pane can be docked or not.
4241
4242        :param `p`: the :class:`AuiPaneInfo` class with all the pane's information.
4243        """
4244
4245        # is the pane dockable?
4246        if not p.IsDockable():
4247            return False
4248
4249        # if a key modifier is pressed while dragging the frame,
4250        # don't dock the window
4251        return not (wx.GetKeyState(wx.WXK_CONTROL) or wx.GetKeyState(wx.WXK_ALT))
4252
4253
4254    def GetPaneByWidget(self, window):
4255        """
4256        This version of :meth:`GetPane` looks up a pane based on a 'pane window'.
4257
4258        :param `window`: a :class:`wx.Window` derived window.
4259
4260        :see: :meth:`~AuiManager.GetPane`
4261        """
4262
4263        for p in self._panes:
4264            if p.window == window:
4265                return p
4266
4267        return NonePaneInfo
4268
4269
4270    def GetPaneByName(self, name):
4271        """
4272        This version of :meth:`GetPane` looks up a pane based on a 'pane name'.
4273
4274        :param string `name`: the pane name.
4275
4276        :see: :meth:`GetPane`
4277        """
4278
4279        for p in self._panes:
4280            if p.name == name:
4281                return p
4282
4283        return NonePaneInfo
4284
4285
4286    def GetPane(self, item):
4287        """
4288        Looks up a :class:`AuiPaneInfo` structure based on the supplied window pointer. Upon failure,
4289        :meth:`GetPane` returns an empty :class:`AuiPaneInfo`, a condition which can be checked
4290        by calling :meth:`AuiPaneInfo.IsOk() <AuiPaneInfo.IsOk>`.
4291
4292        The pane info's structure may then be modified. Once a pane's info is modified, :meth:`Update`
4293        must be called to realize the changes in the UI.
4294
4295        :param `item`: either a pane name or a :class:`wx.Window`.
4296        """
4297
4298        if isinstance(item, six.string_types):
4299            return self.GetPaneByName(item)
4300        else:
4301            return self.GetPaneByWidget(item)
4302
4303
4304    def GetAllPanes(self):
4305        """ Returns a reference to all the pane info structures. """
4306
4307        return self._panes
4308
4309
4310    def ShowPane(self, window, show):
4311        """
4312        Shows or hides a pane based on the window passed as input.
4313
4314        :param wx.Window `window`: any subclass or derivation of :class:`wx.Window`;
4315        :param bool `show`: ``True`` to show the pane, ``False`` otherwise.
4316        """
4317
4318        p = self.GetPane(window)
4319
4320        if p.IsOk():
4321            if p.IsNotebookPage():
4322                notebook = self._notebooks[p.notebook_id]
4323                page_idx = notebook.GetPageIndex(p.window)
4324                if page_idx >= 0:
4325                    notebook.HidePage(page_idx, not show)
4326                    if show:
4327                        notebook.SetSelection(page_idx)
4328                if notebook.GetShownPageCount() > 0:
4329                    self.ShowPane(notebook, True)
4330                else:
4331                    self.ShowPane(notebook, False)
4332
4333            else:
4334                p.Show(show)
4335
4336            if p.frame:
4337                p.frame.Raise()
4338
4339            self.Update()
4340
4341
4342    def HitTest(self, x, y):
4343        """
4344        This is an internal function which determines
4345        which UI item the specified coordinates are over.
4346
4347        :param integer `x`: specifies a x position in client coordinates;
4348        :param integer `y`: specifies a y position in client coordinates.
4349        """
4350
4351        result = None
4352
4353        for item in self._uiparts:
4354            # we are not interested in typeDock, because this space
4355            # isn't used to draw anything, just for measurements
4356            # besides, the entire dock area is covered with other
4357            # rectangles, which we are interested in.
4358            if item.type == AuiDockUIPart.typeDock:
4359                continue
4360
4361            # if we already have a hit on a more specific item, we are not
4362            # interested in a pane hit.  If, however, we don't already have
4363            # a hit, returning a pane hit is necessary for some operations
4364            if item.type in [AuiDockUIPart.typePane, AuiDockUIPart.typePaneBorder] and result:
4365                continue
4366
4367            # if the point is inside the rectangle, we have a hit
4368            if item.rect.Contains((x, y)):
4369                result = item
4370
4371        return result
4372
4373
4374    def PaneHitTest(self, panes, pt):
4375        """
4376        Similar to :meth:`HitTest`, but it checks in which :class:`AuiManager` rectangle the
4377        input point belongs to.
4378
4379        :param `panes`: a list of :class:`AuiPaneInfo` instances;
4380        :param wx.Point `pt`: the mouse position.
4381        """
4382
4383        for paneInfo in panes:
4384            if paneInfo.IsDocked() and paneInfo.IsShown() and paneInfo.rect.Contains(pt):
4385                return paneInfo
4386
4387        return NonePaneInfo
4388
4389
4390    # SetAGWFlags() and GetAGWFlags() allow the owner to set various
4391    # options which are global to AuiManager
4392
4393    def SetAGWFlags(self, agwFlags):
4394        """
4395        This method is used to specify :class:`AuiManager` 's settings flags.
4396
4397        :param integer `agwFlags`: specifies options which allow the frame management behavior
4398         to be modified. `agwFlags` can be one of the following style bits:
4399
4400         ==================================== ==================================
4401         Flag name                            Description
4402         ==================================== ==================================
4403         ``AUI_MGR_ALLOW_FLOATING``           Allow floating of panes
4404         ``AUI_MGR_ALLOW_ACTIVE_PANE``        If a pane becomes active, "highlight" it in the interface
4405         ``AUI_MGR_TRANSPARENT_DRAG``         If the platform supports it, set transparency on a floating pane while it is dragged by the user
4406         ``AUI_MGR_TRANSPARENT_HINT``         If the platform supports it, show a transparent hint window when the user is about to dock a floating pane
4407         ``AUI_MGR_VENETIAN_BLINDS_HINT``     Show a "venetian blind" effect when the user is about to dock a floating pane
4408         ``AUI_MGR_RECTANGLE_HINT``           Show a rectangle hint effect when the user is about to dock a floating pane
4409         ``AUI_MGR_HINT_FADE``                If the platform supports it, the hint window will fade in and out
4410         ``AUI_MGR_NO_VENETIAN_BLINDS_FADE``  Disables the "venetian blind" fade in and out
4411         ``AUI_MGR_LIVE_RESIZE``              Live resize when the user drag a sash
4412         ``AUI_MGR_ANIMATE_FRAMES``           Fade-out floating panes when they are closed (all platforms which support frames transparency)
4413                                              and show a moving rectangle when they are docked (Windows < Vista and GTK only)
4414         ``AUI_MGR_AERO_DOCKING_GUIDES``      Use the new Aero-style bitmaps as docking guides
4415         ``AUI_MGR_PREVIEW_MINIMIZED_PANES``  Slide in and out minimized panes to preview them
4416         ``AUI_MGR_WHIDBEY_DOCKING_GUIDES``   Use the new Whidbey-style bitmaps as docking guides
4417         ``AUI_MGR_SMOOTH_DOCKING``           Performs a "smooth" docking of panes (a la PyQT)
4418         ``AUI_MGR_USE_NATIVE_MINIFRAMES``    Use miniframes with native caption bar as floating panes instead or custom drawn caption bars (forced on wxMAC)
4419         ``AUI_MGR_AUTONB_NO_CAPTION``        Panes that merge into an automatic notebook will not have the pane caption visible
4420         ==================================== ==================================
4421
4422         .. note::
4423
4424            If using the ``AUI_MGR_USE_NATIVE_MINIFRAMES``, double-clicking on a
4425            floating pane caption will not re-dock the pane, but simply maximize it (if
4426            :meth:`AuiPaneInfo.MaximizeButton` has been set to ``True``) or do nothing.
4427
4428        """
4429
4430        self._agwFlags = agwFlags
4431
4432        if len(self._guides) > 0:
4433            self.CreateGuideWindows()
4434
4435        if self._hint_window and agwFlags & AUI_MGR_RECTANGLE_HINT == 0:
4436            self.CreateHintWindow()
4437
4438
4439    def GetAGWFlags(self):
4440        """
4441        Returns the current manager's flags.
4442
4443        :see: :meth:`SetAGWFlags` for a list of possible :class:`AuiManager` flags.
4444        """
4445
4446        return self._agwFlags
4447
4448
4449    def SetManagedWindow(self, managed_window):
4450        """
4451        Called to specify the frame or window which is to be managed by :class:`AuiManager`.
4452        Frame management is not restricted to just frames. Child windows or custom
4453        controls are also allowed.
4454
4455        :param wx.Window `managed_window`: specifies the window which should be managed by
4456         the AUI manager.
4457        """
4458
4459        if not managed_window:
4460            raise Exception("Specified managed window must be non-null. ")
4461
4462        self.UnInit()
4463
4464        self._frame = managed_window
4465        self._frame.PushEventHandler(self)
4466
4467        # if the owner is going to manage an MDI parent frame,
4468        # we need to add the MDI client window as the default
4469        # center pane
4470
4471        if isinstance(self._frame, wx.MDIParentFrame):
4472            mdi_frame = self._frame
4473            client_window = mdi_frame.GetClientWindow()
4474
4475            if not client_window:
4476                raise Exception("Client window is None!")
4477
4478            self.AddPane(client_window, AuiPaneInfo().Name("mdiclient").
4479                         CenterPane().PaneBorder(False))
4480
4481        elif isinstance(self._frame, tabmdi.AuiMDIParentFrame):
4482
4483            mdi_frame = self._frame
4484            client_window = mdi_frame.GetClientWindow()
4485
4486            if not client_window:
4487                raise Exception("Client window is None!")
4488
4489            self.AddPane(client_window, AuiPaneInfo().Name("mdiclient").
4490                         CenterPane().PaneBorder(False))
4491
4492
4493    def GetManagedWindow(self):
4494        """ Returns the window being managed by :class:`AuiManager`. """
4495
4496        return self._frame
4497
4498
4499    def SetFrame(self, managed_window):
4500        """
4501        Called to specify the frame or window which is to be managed by :class:`AuiManager`.
4502        Frame management is not restricted to just frames. Child windows or custom
4503        controls are also allowed.
4504
4505        :param wx.Window `managed_window`: specifies the window which should be managed by
4506         the AUI manager.
4507
4508        .. deprecated:: 0.6
4509           This method is now deprecated, use :meth:`SetManagedWindow` instead.
4510        """
4511
4512        DeprecationWarning("This method is deprecated, use SetManagedWindow instead.")
4513        return self.SetManagedWindow(managed_window)
4514
4515
4516    def GetFrame(self):
4517        """
4518        Returns the window being managed by :class:`AuiManager`.
4519
4520        .. deprecated:: 0.6
4521           This method is now deprecated, use :meth:`GetManagedWindow` instead.
4522        """
4523
4524        DeprecationWarning("This method is deprecated, use GetManagedWindow instead.")
4525        return self._frame
4526
4527
4528    def CreateGuideWindows(self):
4529        """ Creates the VS2005 HUD guide windows. """
4530
4531        self.DestroyGuideWindows()
4532
4533        self._guides.append(AuiDockingGuideInfo().Left().
4534                            Host(AuiSingleDockingGuide(self._frame, wx.LEFT)))
4535        self._guides.append(AuiDockingGuideInfo().Top().
4536                            Host(AuiSingleDockingGuide(self._frame, wx.TOP)))
4537        self._guides.append(AuiDockingGuideInfo().Right().
4538                            Host(AuiSingleDockingGuide(self._frame, wx.RIGHT)))
4539        self._guides.append(AuiDockingGuideInfo().Bottom().
4540                            Host(AuiSingleDockingGuide(self._frame, wx.BOTTOM)))
4541        self._guides.append(AuiDockingGuideInfo().Centre().
4542                            Host(AuiCenterDockingGuide(self._frame)))
4543
4544
4545    def DestroyGuideWindows(self):
4546        """ Destroys the VS2005 HUD guide windows. """
4547        for guide in self._guides:
4548            if guide.host:
4549                guide.host.Destroy()
4550        self._guides = []
4551
4552
4553    def CreateHintWindow(self):
4554        """ Creates the standard wxAUI hint window. """
4555
4556        self.DestroyHintWindow()
4557
4558        self._hint_window = AuiDockingHintWindow(self._frame)
4559        self._hint_window.SetBlindMode(self._agwFlags)
4560
4561
4562    def DestroyHintWindow(self):
4563        """ Destroys the standard wxAUI hint window. """
4564
4565        if self._hint_window:
4566            self._hint_window.Destroy()
4567            self._hint_window = None
4568
4569
4570    def UnInit(self):
4571        """
4572        Uninitializes the framework and should be called before a managed frame or
4573        window is destroyed. :meth:`UnInit` is usually called in the managed :class:`wx.Frame` / :class:`wx.Window`
4574        destructor.
4575
4576        It is necessary to call this function before the managed frame or window is
4577        destroyed, otherwise the manager cannot remove its custom event handlers
4578        from a window.
4579        """
4580
4581        if not self._frame:
4582            return
4583
4584        for klass in [self._frame] + list(self._frame.GetChildren()):
4585            handler = klass.GetEventHandler()
4586            if klass is not handler:
4587                if isinstance(handler, AuiManager):
4588                    klass.RemoveEventHandler(handler)
4589
4590
4591    def OnClose(self, event):
4592        """Called when the managed window is closed. Makes sure that :meth:`UnInit`
4593        is called.
4594        """
4595
4596        event.Skip()
4597        if event.GetEventObject() == self._frame:
4598            wx.CallAfter(self.UnInit)
4599
4600
4601    def OnDestroy(self, event) :
4602        """Called when the managed window is destroyed. Makes sure that :meth:`UnInit`
4603        is called.
4604        """
4605
4606        if self._frame == event.GetEventObject():
4607            self.UnInit()
4608
4609
4610    def GetArtProvider(self):
4611        """ Returns the current art provider being used. """
4612
4613        return self._art
4614
4615
4616    def ProcessMgrEvent(self, event):
4617        """
4618        Process the AUI events sent to the manager.
4619
4620        :param `event`: the event to process, an instance of :class:`AuiManagerEvent`.
4621        """
4622
4623        # first, give the owner frame a chance to override
4624        if self._frame:
4625            if self._frame.GetEventHandler().ProcessEvent(event):
4626                return
4627
4628        self.ProcessEvent(event)
4629
4630
4631    def FireEvent(self, evtType, pane, canVeto=False):
4632        """
4633        Fires one of the ``EVT_AUI_PANE_FLOATED`` / ``FLOATING`` / ``DOCKING`` / ``DOCKED`` / ``ACTIVATED`` event.
4634
4635        :param integer `evtType`: one of the aforementioned events;
4636        :param `pane`: the :class:`AuiPaneInfo` instance associated to this event;
4637        :param bool `canVeto`: whether the event can be vetoed or not.
4638        """
4639
4640        event = AuiManagerEvent(evtType)
4641        event.SetPane(pane)
4642        event.SetCanVeto(canVeto)
4643        self.ProcessMgrEvent(event)
4644
4645        return event
4646
4647
4648    def CanUseModernDockArt(self):
4649        """
4650        Returns whether :class:`dockart` can be used (Windows XP / Vista / 7 only,
4651        requires Mark Hammonds's `pywin32 <http://sourceforge.net/projects/pywin32/>`_ package).
4652        """
4653
4654        if not _winxptheme:
4655            return False
4656
4657        # Get the size of a small close button (themed)
4658        hwnd = self._frame.GetHandle()
4659        hTheme = winxptheme.OpenThemeData(hwnd, "Window")
4660
4661        if not hTheme:
4662            return False
4663
4664        return True
4665
4666
4667    def SetArtProvider(self, art_provider):
4668        """
4669        Instructs :class:`AuiManager` to use art provider specified by the parameter
4670        `art_provider` for all drawing calls. This allows plugable look-and-feel
4671        features.
4672
4673        :param `art_provider`: a AUI dock art provider.
4674
4675        :note: The previous art provider object, if any, will be deleted by :class:`AuiManager`.
4676        """
4677
4678        # delete the last art provider, if any
4679        del self._art
4680
4681        # assign the new art provider
4682        self._art = art_provider
4683
4684        for pane in self.GetAllPanes():
4685            if pane.IsFloating() and pane.frame:
4686                pane.frame._mgr.SetArtProvider(art_provider)
4687                pane.frame._mgr.Update()
4688
4689
4690    def AddPane(self, window, arg1=None, arg2=None, target=None):
4691        """
4692        Tells the frame manager to start managing a child window. There
4693        are four versions of this function. The first verison allows the full spectrum
4694        of pane parameter possibilities (:meth:`AddPane1`). The second version is used for
4695        simpler user interfaces which do not require as much configuration (:meth:`AddPane2`).
4696        The :meth:`AddPane3` version allows a drop position to be specified, which will determine
4697        where the pane will be added. The :meth:`AddPane4` version allows to turn the target
4698        :class:`AuiPaneInfo` pane into a notebook and the added pane into a page.
4699
4700        In your code, simply call :meth:`AddPane`.
4701
4702        :param wx.Window `window`: the child window to manage;
4703        :param `arg1`: a :class:`AuiPaneInfo` or an integer value (direction);
4704        :param `arg2`: a :class:`AuiPaneInfo` or a :class:`wx.Point` (drop position);
4705        :param `target`: a :class:`AuiPaneInfo` to be turned into a notebook
4706         and new pane added to it as a page. (additionally, target can be any pane in
4707         an existing notebook)
4708         """
4709
4710        if target in self._panes:
4711            return self.AddPane4(window, arg1, target)
4712
4713        if isinstance(arg1, int):
4714            # This Is Addpane2
4715            if arg1 is None:
4716                arg1 = wx.LEFT
4717            if arg2 is None:
4718                arg2 = ""
4719            return self.AddPane2(window, arg1, arg2)
4720        else:
4721            if isinstance(arg2, wx.Point):
4722                return self.AddPane3(window, arg1, arg2)
4723            else:
4724                return self.AddPane1(window, arg1)
4725
4726
4727    def AddPane1(self, window, pane_info):
4728        """ See comments on :meth:`AddPane`. """
4729
4730        # check if the pane has a valid window
4731        if not window:
4732            return False
4733
4734        # check if the pane already exists
4735        if self.GetPane(pane_info.window).IsOk():
4736            return False
4737
4738        # check if the pane name already exists, this could reveal a
4739        # bug in the library user's application
4740        already_exists = False
4741        if pane_info.name != "" and self.GetPane(pane_info.name).IsOk():
4742            warnings.warn("A pane with the name '%s' already exists in the manager!"%pane_info.name)
4743            already_exists = True
4744
4745        # if the new pane is docked then we should undo maximize
4746        if pane_info.IsDocked():
4747            self.RestoreMaximizedPane()
4748
4749        self._panes.append(pane_info)
4750        pinfo = self._panes[-1]
4751
4752        # set the pane window
4753        pinfo.window = window
4754
4755        # if the pane's name identifier is blank, create a random string
4756        if pinfo.name == "" or already_exists:
4757            pinfo.name = ("%s%08x%08x%08x") % (pinfo.window.GetName(), int(time()),
4758                                               int(clock()), len(self._panes))
4759
4760        # set initial proportion (if not already set)
4761        if pinfo.dock_proportion == 0:
4762            pinfo.dock_proportion = 100000
4763
4764        floating = isinstance(self._frame, AuiFloatingFrame)
4765
4766        pinfo.buttons = []
4767
4768        if not floating and pinfo.HasMinimizeButton():
4769            button = AuiPaneButton(AUI_BUTTON_MINIMIZE)
4770            pinfo.buttons.append(button)
4771
4772        if not floating and pinfo.HasMaximizeButton():
4773            button = AuiPaneButton(AUI_BUTTON_MAXIMIZE_RESTORE)
4774            pinfo.buttons.append(button)
4775
4776        if not floating and pinfo.HasPinButton():
4777            button = AuiPaneButton(AUI_BUTTON_PIN)
4778            pinfo.buttons.append(button)
4779
4780        if pinfo.HasCloseButton():
4781            button = AuiPaneButton(AUI_BUTTON_CLOSE)
4782            pinfo.buttons.append(button)
4783
4784        if pinfo.HasGripper():
4785            if isinstance(pinfo.window, auibar.AuiToolBar):
4786                # prevent duplicate gripper -- both AuiManager and AuiToolBar
4787                # have a gripper control.  The toolbar's built-in gripper
4788                # meshes better with the look and feel of the control than ours,
4789                # so turn AuiManager's gripper off, and the toolbar's on.
4790
4791                tb = pinfo.window
4792                pinfo.SetFlag(AuiPaneInfo.optionGripper, False)
4793                tb.SetGripperVisible(True)
4794
4795        if pinfo.window:
4796            if pinfo.best_size == wx.Size(-1, -1):
4797                pinfo.best_size = pinfo.window.GetBestSize()
4798
4799            if isinstance(pinfo.window, wx.ToolBar):
4800                # GetClientSize() doesn't get the best size for
4801                # a toolbar under some newer versions of wxWidgets,
4802                # so use GetBestSize()
4803                pinfo.best_size = pinfo.window.GetBestSize()
4804
4805                # this is needed for Win2000 to correctly fill toolbar backround
4806                # it should probably be repeated once system colour change happens
4807                if wx.Platform == "__WXMSW__" and pinfo.window.UseBgCol():
4808                    pinfo.window.SetBackgroundColour(self.GetArtProvider().GetColour(AUI_DOCKART_BACKGROUND_COLOUR))
4809
4810            if pinfo.min_size != wx.Size(-1, -1):
4811                if pinfo.best_size.x < pinfo.min_size.x:
4812                    pinfo.best_size.x = pinfo.min_size.x
4813                if pinfo.best_size.y < pinfo.min_size.y:
4814                    pinfo.best_size.y = pinfo.min_size.y
4815
4816        self._panes[-1] = pinfo
4817        if isinstance(window, auibar.AuiToolBar):
4818            window.SetAuiManager(self)
4819
4820        return True
4821
4822
4823    def AddPane2(self, window, direction, caption):
4824        """ See comments on :meth:`AddPane`. """
4825
4826        pinfo = AuiPaneInfo()
4827        pinfo.Caption(caption)
4828
4829        if direction == wx.TOP:
4830            pinfo.Top()
4831        elif direction == wx.BOTTOM:
4832            pinfo.Bottom()
4833        elif direction == wx.LEFT:
4834            pinfo.Left()
4835        elif direction == wx.RIGHT:
4836            pinfo.Right()
4837        elif direction == wx.CENTER:
4838            pinfo.CenterPane()
4839
4840        return self.AddPane(window, pinfo)
4841
4842
4843    def AddPane3(self, window, pane_info, drop_pos):
4844        """ See comments on :meth:`AddPane`. """
4845
4846        if not self.AddPane(window, pane_info):
4847            return False
4848
4849        pane = self.GetPane(window)
4850        indx = self._panes.index(pane)
4851
4852        ret, pane = self.DoDrop(self._docks, self._panes, pane, drop_pos, wx.Point(0, 0))
4853        self._panes[indx] = pane
4854
4855        return True
4856
4857
4858    def AddPane4(self, window, pane_info, target):
4859        """ See comments on :meth:`AddPane`. """
4860
4861        if not self.AddPane(window, pane_info):
4862            return False
4863
4864        paneInfo = self.GetPane(window)
4865
4866        if not paneInfo.IsNotebookDockable():
4867            return self.AddPane1(window, pane_info)
4868        if not target.IsNotebookDockable() and not target.IsNotebookControl():
4869            return self.AddPane1(window, pane_info)
4870
4871        if not target.HasNotebook():
4872            self.CreateNotebookBase(self._panes, target)
4873
4874        # Add new item to notebook
4875        paneInfo.NotebookPage(target.notebook_id)
4876
4877        # we also want to remove our captions sometimes
4878        self.RemoveAutoNBCaption(paneInfo)
4879        self.UpdateNotebook()
4880
4881        return True
4882
4883
4884    def InsertPane(self, window, pane_info, insert_level=AUI_INSERT_PANE):
4885        """
4886        This method is used to insert either a previously unmanaged pane window
4887        into the frame manager, or to insert a currently managed pane somewhere else.
4888        :meth:`InsertPane` will push all panes, rows, or docks aside and insert the window
4889        into the position specified by `pane_info`.
4890
4891        Because `pane_info` can specify either a pane, dock row, or dock layer, the
4892        `insert_level` parameter is used to disambiguate this. The parameter `insert_level`
4893        can take a value of ``AUI_INSERT_PANE``, ``AUI_INSERT_ROW`` or ``AUI_INSERT_DOCK``.
4894
4895        :param wx.Window `window`: the window to be inserted and managed;
4896        :param `pane_info`: the insert location for the new window;
4897        :param integer `insert_level`: the insertion level of the new pane.
4898        """
4899
4900        if not window:
4901            raise Exception("Invalid window passed to InsertPane.")
4902
4903        # shift the panes around, depending on the insert level
4904        if insert_level == AUI_INSERT_PANE:
4905            self._panes = DoInsertPane(self._panes, pane_info.dock_direction,
4906                                       pane_info.dock_layer, pane_info.dock_row,
4907                                       pane_info.dock_pos)
4908
4909        elif insert_level == AUI_INSERT_ROW:
4910            self._panes = DoInsertDockRow(self._panes, pane_info.dock_direction,
4911                                          pane_info.dock_layer, pane_info.dock_row)
4912
4913        elif insert_level == AUI_INSERT_DOCK:
4914            self._panes = DoInsertDockLayer(self._panes, pane_info.dock_direction,
4915                                            pane_info.dock_layer)
4916
4917        # if the window already exists, we are basically just moving/inserting the
4918        # existing window.  If it doesn't exist, we need to add it and insert it
4919        existing_pane = self.GetPane(window)
4920        indx = self._panes.index(existing_pane)
4921
4922        if not existing_pane.IsOk():
4923
4924            return self.AddPane(window, pane_info)
4925
4926        else:
4927
4928            if pane_info.IsFloating():
4929                existing_pane.Float()
4930                if pane_info.floating_pos != wx.Point(-1, -1):
4931                    existing_pane.FloatingPosition(pane_info.floating_pos)
4932                if pane_info.floating_size != wx.Size(-1, -1):
4933                    existing_pane.FloatingSize(pane_info.floating_size)
4934            else:
4935                # if the new pane is docked then we should undo maximize
4936                self.RestoreMaximizedPane()
4937
4938                existing_pane.Direction(pane_info.dock_direction)
4939                existing_pane.Layer(pane_info.dock_layer)
4940                existing_pane.Row(pane_info.dock_row)
4941                existing_pane.Position(pane_info.dock_pos)
4942
4943            self._panes[indx] = existing_pane
4944
4945        return True
4946
4947
4948    def DetachPane(self, window):
4949        """
4950        Tells the :class:`AuiManager` to stop managing the pane specified
4951        by `window`. The window, if in a floated frame, is reparented to the frame
4952        managed by :class:`AuiManager`.
4953
4954        :param wx.Window `window`: the window to be un-managed.
4955        """
4956
4957        for p in self._panes:
4958            if p.window == window:
4959                if p.frame:
4960                    # we have a floating frame which is being detached. We need to
4961                    # reparent it to self._frame and destroy the floating frame
4962
4963                    # reduce flicker
4964                    p.window.SetSize((1, 1))
4965                    if p.frame.IsShown():
4966                        p.frame.Show(False)
4967
4968                    if self._action_window == p.frame:
4969                        self._action_window = None
4970
4971                    # reparent to self._frame and destroy the pane
4972                    p.window.Reparent(self._frame)
4973                    p.frame.SetSizer(None)
4974                    p.frame._mgr.UnInit()
4975                    p.frame.Destroy()
4976                    p.frame = None
4977
4978                elif p.IsNotebookPage():
4979                    notebook = self._notebooks[p.notebook_id]
4980                    id = notebook.GetPageIndex(p.window)
4981                    notebook.RemovePage(id)
4982                    p.window.Reparent(self._frame)
4983
4984                # make sure there are no references to this pane in our uiparts,
4985                # just in case the caller doesn't call Update() immediately after
4986                # the DetachPane() call.  This prevets obscure crashes which would
4987                # happen at window repaint if the caller forgets to call Update()
4988                counter = 0
4989                for pi in range(len(self._uiparts)):
4990                    part = self._uiparts[counter]
4991                    if part.pane == p:
4992                        self._uiparts.pop(counter)
4993                        counter -= 1
4994
4995                    counter += 1
4996
4997                self._panes.remove(p)
4998                return True
4999
5000        return False
5001
5002
5003    def ClosePane(self, pane_info):
5004        """
5005        Destroys or hides the pane depending on its flags.
5006
5007        :param `pane_info`: a :class:`AuiPaneInfo` instance.
5008        """
5009
5010        # if we were maximized, restore
5011        if pane_info.IsMaximized():
5012            self.RestorePane(pane_info)
5013
5014        if pane_info.frame:
5015            if self._agwFlags & AUI_MGR_ANIMATE_FRAMES:
5016                pane_info.frame.FadeOut()
5017
5018        # first, hide the window
5019        if pane_info.window and pane_info.window.IsShown():
5020            pane_info.window.Show(False)
5021
5022        # if we have a frame, destroy it
5023        if pane_info.frame:
5024            # make sure that we are the parent of this window
5025            if pane_info.window and pane_info.window.GetParent() != self._frame:
5026                pane_info.window.Reparent(self._frame)
5027            pane_info.frame.Destroy()
5028            pane_info.frame = None
5029            pane_info.Hide()
5030
5031        elif pane_info.IsNotebookPage():
5032            # if we are a notebook page, remove ourselves...
5033            # the  code would index out of bounds
5034            # if the last page of a sub-notebook was closed
5035            # because the notebook would be deleted, before this
5036            # code is executed.
5037            # This code just prevents an out-of bounds error.
5038            if self._notebooks:
5039                nid = pane_info.notebook_id
5040                if nid >= 0 and nid < len(self._notebooks):
5041                    notebook = self._notebooks[nid]
5042                    page_idx = notebook.GetPageIndex(pane_info.window)
5043                    if page_idx >= 0:
5044                        if not pane_info.IsDestroyOnClose():
5045                            self.ShowPane(pane_info.window, False)
5046
5047        else:
5048            if pane_info.window and pane_info.window.GetParent() != self._frame:
5049                pane_info.window.Reparent(self._frame)
5050            pane_info.Dock().Hide()
5051
5052
5053        # now we need to either destroy or hide the pane
5054        to_destroy = 0
5055        if pane_info.IsDestroyOnClose():
5056            to_destroy = pane_info.window
5057            self.DetachPane(to_destroy)
5058        else:
5059            if isinstance(pane_info.window, auibar.AuiToolBar) and pane_info.IsFloating():
5060                tb = pane_info.window
5061                if pane_info.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]:
5062                    tb.SetAGWWindowStyleFlag(tb.GetAGWWindowStyleFlag() | AUI_TB_VERTICAL)
5063
5064        if pane_info.IsNotebookControl():
5065
5066            notebook = self._notebooks[pane_info.notebook_id]
5067            for idx in range(notebook.GetPageCount()-1, -1, -1):
5068                window = notebook.GetPage(idx)
5069                info = self.GetPane(window)
5070                # close page if its IsDestroyOnClose flag is set
5071                if info.IsDestroyOnClose():
5072                    if info.IsOk():
5073                        # Note: this could change our paneInfo reference ...
5074                        self.ClosePane(info)
5075
5076        if to_destroy:
5077            to_destroy.Destroy()
5078
5079
5080    def MaximizePane(self, pane_info, savesizes=True):
5081        """
5082        Maximizes the input pane.
5083
5084        :param `pane_info`: a :class:`AuiPaneInfo` instance.
5085        :param bool `savesizes`: whether to save previous dock sizes.
5086        """
5087
5088        if savesizes:
5089            self.SavePreviousDockSizes(pane_info)
5090
5091        for p in self._panes:
5092
5093            # save hidden state
5094            p.SetFlag(p.savedHiddenState, p.HasFlag(p.optionHidden))
5095
5096            if not p.IsToolbar() and not p.IsFloating():
5097                p.Restore()
5098
5099                # hide the pane, because only the newly
5100                # maximized pane should show
5101                p.Hide()
5102
5103        pane_info.previousDockPos = pane_info.dock_pos
5104
5105        # mark ourselves maximized
5106        pane_info.Maximize()
5107        pane_info.Show()
5108        self._has_maximized = True
5109
5110        # last, show the window
5111        if pane_info.window and not pane_info.window.IsShown():
5112            pane_info.window.Show(True)
5113
5114
5115    def SavePreviousDockSizes(self, pane_info):
5116        """
5117        Stores the previous dock sizes, to be used in a "restore" action later.
5118
5119        :param `pane_info`: a :class:`AuiPaneInfo` instance.
5120        """
5121
5122        for d in self._docks:
5123            if not d.toolbar:
5124                for p in d.panes:
5125                    p.previousDockSize = d.size
5126                    if pane_info is not p:
5127                        p.SetFlag(p.needsRestore, True)
5128
5129
5130    def RestorePane(self, pane_info):
5131        """
5132        Restores the input pane from a previous maximized or minimized state.
5133
5134        :param `pane_info`: a :class:`AuiPaneInfo` instance.
5135        """
5136
5137        # restore all the panes
5138        for p in self._panes:
5139            if not p.IsToolbar():
5140                p.SetFlag(p.optionHidden, p.HasFlag(p.savedHiddenState))
5141
5142        pane_info.SetFlag(pane_info.needsRestore, True)
5143
5144        # mark ourselves non-maximized
5145        pane_info.Restore()
5146        self._has_maximized = False
5147        self._has_minimized = False
5148
5149        # last, show the window
5150        if pane_info.window and not pane_info.window.IsShown():
5151            pane_info.window.Show(True)
5152
5153
5154    def RestoreMaximizedPane(self):
5155        """ Restores the current maximized pane (if any). """
5156
5157        # restore all the panes
5158        for p in self._panes:
5159            if p.IsMaximized():
5160                self.RestorePane(p)
5161                break
5162
5163
5164    def ActivatePane(self, window):
5165        """
5166        Activates the pane to which `window` is associated.
5167
5168        :param `window`: a :class:`wx.Window` derived window.
5169        """
5170
5171        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
5172            while window:
5173                ret, self._panes = SetActivePane(self._panes, window)
5174                if ret:
5175                    break
5176
5177                window = window.GetParent()
5178
5179            self.RefreshCaptions()
5180            self.FireEvent(wxEVT_AUI_PANE_ACTIVATED, window, canVeto=False)
5181
5182
5183    def CreateNotebook(self):
5184        """
5185        Creates an automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook` when a pane is docked on
5186        top of another pane.
5187        """
5188
5189        notebook = auibook.AuiNotebook(self._frame, -1, wx.Point(0, 0), wx.Size(0, 0), agwStyle=self._autoNBStyle)
5190
5191        # This is so we can get the tab-drag event.
5192        notebook.GetAuiManager().SetMasterManager(self)
5193        notebook.SetArtProvider(self._autoNBTabArt.Clone())
5194        self._notebooks.append(notebook)
5195
5196        return notebook
5197
5198
5199    def SetAutoNotebookTabArt(self, art):
5200        """
5201        Sets the default tab art provider for automatic notebooks.
5202
5203        :param `art`: a tab art provider.
5204        """
5205
5206        for nb in self._notebooks:
5207            nb.SetArtProvider(art.Clone())
5208            nb.Refresh()
5209            nb.Update()
5210
5211        self._autoNBTabArt = art
5212
5213
5214    def GetAutoNotebookTabArt(self):
5215        """ Returns the default tab art provider for automatic notebooks. """
5216
5217        return self._autoNBTabArt
5218
5219
5220    def SetAutoNotebookStyle(self, agwStyle):
5221        """
5222        Sets the default AGW-specific window style for automatic notebooks.
5223
5224        :param integer `agwStyle`: the underlying :class:`~wx.lib.agw.aui.auibook.AuiNotebook` window style.
5225         This can be a combination of the following bits:
5226
5227         ==================================== ==================================
5228         Flag name                            Description
5229         ==================================== ==================================
5230         ``AUI_NB_TOP``                       With this style, tabs are drawn along the top of the notebook
5231         ``AUI_NB_LEFT``                      With this style, tabs are drawn along the left of the notebook. Not implemented yet.
5232         ``AUI_NB_RIGHT``                     With this style, tabs are drawn along the right of the notebook. Not implemented yet.
5233         ``AUI_NB_BOTTOM``                    With this style, tabs are drawn along the bottom of the notebook
5234         ``AUI_NB_TAB_SPLIT``                 Allows the tab control to be split by dragging a tab
5235         ``AUI_NB_TAB_MOVE``                  Allows a tab to be moved horizontally by dragging
5236         ``AUI_NB_TAB_EXTERNAL_MOVE``         Allows a tab to be moved to another tab control
5237         ``AUI_NB_TAB_FIXED_WIDTH``           With this style, all tabs have the same width
5238         ``AUI_NB_SCROLL_BUTTONS``            With this style, left and right scroll buttons are displayed
5239         ``AUI_NB_WINDOWLIST_BUTTON``         With this style, a drop-down list of windows is available
5240         ``AUI_NB_CLOSE_BUTTON``              With this style, a close button is available on the tab bar
5241         ``AUI_NB_CLOSE_ON_ACTIVE_TAB``       With this style, a close button is available on the active tab
5242         ``AUI_NB_CLOSE_ON_ALL_TABS``         With this style, a close button is available on all tabs
5243         ``AUI_NB_MIDDLE_CLICK_CLOSE``        Allows to close :class:`~wx.lib.agw.aui.auibook.AuiNotebook` tabs by mouse middle button click
5244         ``AUI_NB_SUB_NOTEBOOK``              This style is used by :class:`AuiManager` to create automatic AuiNotebooks
5245         ``AUI_NB_HIDE_ON_SINGLE_TAB``        Hides the tab window if only one tab is present
5246         ``AUI_NB_SMART_TABS``                Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
5247         ``AUI_NB_USE_IMAGES_DROPDOWN``       Uses images on dropdown window list menu instead of check items
5248         ``AUI_NB_CLOSE_ON_TAB_LEFT``         Draws the tab close button on the left instead of on the right (a la Camino browser)
5249         ``AUI_NB_TAB_FLOAT``                 Allows the floating of single tabs. Known limitation: when the notebook is more or less
5250                                              full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
5251         ``AUI_NB_DRAW_DND_TAB``              Draws an image representation of a tab while dragging (on by default)
5252         ``AUI_NB_ORDER_BY_ACCESS``           Tab navigation order by last access time for the tabs
5253         ``AUI_NB_NO_TAB_FOCUS``              Don't draw tab focus rectangle
5254         ==================================== ==================================
5255
5256        """
5257
5258        for nb in self._notebooks:
5259            nb.SetAGWWindowStyleFlag(agwStyle)
5260            nb.Refresh()
5261            nb.Update()
5262
5263        self._autoNBStyle = agwStyle
5264
5265
5266    def GetAutoNotebookStyle(self):
5267        """
5268        Returns the default AGW-specific window style for automatic notebooks.
5269
5270        :see: :meth:`SetAutoNotebookStyle` method for a list of possible styles.
5271        """
5272
5273        return self._autoNBStyle
5274
5275
5276    def SavePaneInfo(self, pane):
5277        """
5278        This method is similar to :meth:`SavePerspective`, with the exception
5279        that it only saves information about a single pane. It is used in
5280        combination with :meth:`LoadPaneInfo`.
5281
5282        :param `pane`: a :class:`AuiPaneInfo` instance to save.
5283        """
5284
5285        result = "name=" + EscapeDelimiters(pane.name) + ";"
5286        result += "caption=" + EscapeDelimiters(pane.caption) + ";"
5287
5288        result += "state=%u;"%pane.state
5289        result += "dir=%d;"%pane.dock_direction
5290        result += "layer=%d;"%pane.dock_layer
5291        result += "row=%d;"%pane.dock_row
5292        result += "pos=%d;"%pane.dock_pos
5293        result += "prop=%d;"%pane.dock_proportion
5294        result += "bestw=%d;"%pane.best_size.x
5295        result += "besth=%d;"%pane.best_size.y
5296        result += "minw=%d;"%pane.min_size.x
5297        result += "minh=%d;"%pane.min_size.y
5298        result += "maxw=%d;"%pane.max_size.x
5299        result += "maxh=%d;"%pane.max_size.y
5300        result += "floatx=%d;"%pane.floating_pos.x
5301        result += "floaty=%d;"%pane.floating_pos.y
5302        result += "floatw=%d;"%pane.floating_size.x
5303        result += "floath=%d;"%pane.floating_size.y
5304        result += "notebookid=%d;"%pane.notebook_id
5305        result += "transparent=%d"%pane.transparent
5306
5307        return result
5308
5309
5310    def LoadPaneInfo(self, pane_part, pane):
5311        """
5312        This method is similar to to :meth:`LoadPerspective`, with the exception that
5313        it only loads information about a single pane. It is used in combination
5314        with :meth:`SavePaneInfo`.
5315
5316        :param string `pane_part`: the string to analyze;
5317        :param `pane`: the :class:`AuiPaneInfo` structure in which to load `pane_part`.
5318        """
5319
5320        # replace escaped characters so we can
5321        # split up the string easily
5322        pane_part = pane_part.replace("\\|", "\a")
5323        pane_part = pane_part.replace("\\;", "\b")
5324
5325        options = pane_part.split(";")
5326        for items in options:
5327
5328            val_name, value = items.split("=", 1)
5329            val_name = val_name.strip()
5330
5331            if val_name == "name":
5332                pane.name = value
5333            elif val_name == "caption":
5334                pane.caption = value
5335            elif val_name == "state":
5336                pane.state = int(value)
5337            elif val_name == "dir":
5338                pane.dock_direction = int(value)
5339            elif val_name == "layer":
5340                pane.dock_layer = int(value)
5341            elif val_name == "row":
5342                pane.dock_row = int(value)
5343            elif val_name == "pos":
5344                pane.dock_pos = int(value)
5345            elif val_name == "prop":
5346                pane.dock_proportion = int(value)
5347            elif val_name == "bestw":
5348                pane.best_size.x = int(value)
5349            elif val_name == "besth":
5350                pane.best_size.y = int(value)
5351                pane.best_size = wx.Size(pane.best_size.x, pane.best_size.y)
5352            elif val_name == "minw":
5353                pane.min_size.x = int(value)
5354            elif val_name == "minh":
5355                pane.min_size.y = int(value)
5356                pane.min_size = wx.Size(pane.min_size.x, pane.min_size.y)
5357            elif val_name == "maxw":
5358                pane.max_size.x = int(value)
5359            elif val_name == "maxh":
5360                pane.max_size.y = int(value)
5361                pane.max_size = wx.Size(pane.max_size.x, pane.max_size.y)
5362            elif val_name == "floatx":
5363                pane.floating_pos.x = int(value)
5364            elif val_name == "floaty":
5365                pane.floating_pos.y = int(value)
5366                pane.floating_pos = wx.Point(pane.floating_pos.x, pane.floating_pos.y)
5367            elif val_name == "floatw":
5368                pane.floating_size.x = int(value)
5369            elif val_name == "floath":
5370                pane.floating_size.y = int(value)
5371                pane.floating_size = wx.Size(pane.floating_size.x, pane.floating_size.y)
5372            elif val_name == "notebookid":
5373                pane.notebook_id = int(value)
5374            elif val_name == "transparent":
5375                pane.transparent = int(value)
5376            else:
5377                raise Exception("Bad perspective string")
5378
5379        # replace escaped characters so we can
5380        # split up the string easily
5381        pane.name = pane.name.replace("\a", "|")
5382        pane.name = pane.name.replace("\b", ";")
5383        pane.caption = pane.caption.replace("\a", "|")
5384        pane.caption = pane.caption.replace("\b", ";")
5385        pane_part = pane_part.replace("\a", "|")
5386        pane_part = pane_part.replace("\b", ";")
5387
5388        return pane
5389
5390
5391    def SavePerspective(self):
5392        """
5393        Saves the entire user interface layout into an encoded string, which can then
5394        be stored by the application (probably using :class:`Config <ConfigBase>`).
5395
5396        When a perspective is restored using :meth:`LoadPerspective`, the entire user
5397        interface will return to the state it was when the perspective was saved.
5398        """
5399
5400        result = "layout2|"
5401
5402        for pane in self._panes:
5403            result += self.SavePaneInfo(pane) + "|"
5404
5405        for dock in self._docks:
5406            result = result + ("dock_size(%d,%d,%d)=%d|")%(dock.dock_direction,
5407                                                           dock.dock_layer,
5408                                                           dock.dock_row,
5409                                                           dock.size)
5410        return result
5411
5412
5413    def LoadPerspective(self, layout, update=True, restorecaption=False):
5414        """
5415        Loads a layout which was saved with :meth:`SavePerspective`.
5416
5417        If the `update` flag parameter is ``True``, :meth:`Update` will be
5418        automatically invoked, thus realizing the saved perspective on screen.
5419
5420        :param string `layout`: a string which contains a saved AUI layout;
5421        :param bool `update`: whether to update immediately the window or not;
5422        :param bool `restorecaption`: ``False``, restore from persist storage,
5423         otherwise use the caption defined in code.
5424        """
5425
5426        input = layout
5427
5428        # check layout string version
5429        #    'layout1' = wxAUI 0.9.0 - wxAUI 0.9.2
5430        #    'layout2' = wxAUI 0.9.2 (wxWidgets 2.8)
5431        index = input.find("|")
5432        part = input[0:index].strip()
5433        input = input[index+1:]
5434
5435        if part != "layout2":
5436            return False
5437
5438        # mark all panes currently managed as docked and hidden
5439        saveCapt = {} # see restorecaption param
5440        for pane in self._panes:
5441
5442            # dock the notebook pages
5443            if pane.IsNotebookPage():
5444                notebook = self._notebooks[pane.notebook_id]
5445                idx = notebook.GetPageIndex(pane.window)
5446                notebook.RemovePage(idx)
5447                pane.window.Reparent(self._frame)
5448                nb = self.GetPane(notebook)
5449                pane.notebook_id = -1
5450                pane.Direction(nb.dock_direction)
5451
5452            pane.Dock().Hide()
5453            saveCapt[pane.name] = pane.caption
5454
5455        # clear out the dock array; this will be reconstructed
5456        self._docks = []
5457
5458        # replace escaped characters so we can
5459        # split up the string easily
5460        input = input.replace("\\|", "\a")
5461        input = input.replace("\\;", "\b")
5462
5463        while 1:
5464
5465            pane = AuiPaneInfo()
5466            index = input.find("|")
5467            pane_part = input[0:index].strip()
5468            input = input[index+1:]
5469
5470            # if the string is empty, we're done parsing
5471            if pane_part == "":
5472                break
5473
5474            if pane_part[0:9] == "dock_size":
5475                index = pane_part.find("=")
5476                val_name = pane_part[0:index]
5477                value = pane_part[index+1:]
5478
5479                index = val_name.find("(")
5480                piece = val_name[index+1:]
5481                index = piece.find(")")
5482                piece = piece[0:index]
5483
5484                vals = piece.split(",")
5485                dir = int(vals[0])
5486                layer = int(vals[1])
5487                row = int(vals[2])
5488                size = int(value)
5489
5490                dock = AuiDockInfo()
5491                dock.dock_direction = dir
5492                dock.dock_layer = layer
5493                dock.dock_row = row
5494                dock.size = size
5495                self._docks.append(dock)
5496
5497                continue
5498
5499            # Undo our escaping as LoadPaneInfo needs to take an unescaped
5500            # name so it can be called by external callers
5501            pane_part = pane_part.replace("\a", "|")
5502            pane_part = pane_part.replace("\b", ";")
5503
5504            pane = self.LoadPaneInfo(pane_part, pane)
5505
5506            p = self.GetPane(pane.name)
5507            # restore pane caption from code
5508            if restorecaption:
5509                if pane.name in saveCapt:
5510                    pane.Caption(saveCapt[pane.name])
5511
5512            if not p.IsOk():
5513                if pane.IsNotebookControl():
5514                    # notebook controls - auto add...
5515                    self._panes.append(pane)
5516                    indx = self._panes.index(pane)
5517                else:
5518                    # the pane window couldn't be found
5519                    # in the existing layout -- skip it
5520                    continue
5521
5522            else:
5523                indx = self._panes.index(p)
5524            pane.window = p.window
5525            pane.frame = p.frame
5526            pane.buttons = p.buttons
5527            self._panes[indx] = pane
5528
5529            if isinstance(pane.window, auibar.AuiToolBar) and (pane.IsFloatable() or pane.IsDockable()):
5530                pane.window.SetGripperVisible(True)
5531
5532        for p in self._panes:
5533            if p.IsMinimized():
5534                self.MinimizePane(p, False)
5535
5536        if update:
5537            self.Update()
5538
5539        return True
5540
5541
5542    def GetPanePositionsAndSizes(self, dock):
5543        """
5544        Returns all the panes positions and sizes in a dock.
5545
5546        :param `dock`: a :class:`AuiDockInfo` instance.
5547        """
5548
5549        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
5550        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
5551        gripper_size = self._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
5552
5553        positions = []
5554        sizes = []
5555
5556        action_pane = -1
5557        pane_count = len(dock.panes)
5558
5559        # find the pane marked as our action pane
5560        for pane_i in range(pane_count):
5561            pane = dock.panes[pane_i]
5562            if pane.HasFlag(AuiPaneInfo.actionPane):
5563                if action_pane != -1:
5564                    raise Exception("Too many action panes!")
5565                action_pane = pane_i
5566
5567        # set up each panes default position, and
5568        # determine the size (width or height, depending
5569        # on the dock's orientation) of each pane
5570        for pane in dock.panes:
5571            positions.append(pane.dock_pos)
5572            size = 0
5573
5574            if pane.HasBorder():
5575                size += pane_border_size*2
5576
5577            if dock.IsHorizontal():
5578                if pane.HasGripper() and not pane.HasGripperTop():
5579                    size += gripper_size
5580
5581                if pane.HasCaptionLeft():
5582                    size += caption_size
5583
5584                size += pane.best_size.x
5585
5586            else:
5587                if pane.HasGripper() and pane.HasGripperTop():
5588                    size += gripper_size
5589
5590                if pane.HasCaption() and not pane.HasCaptionLeft():
5591                    size += caption_size
5592
5593                size += pane.best_size.y
5594
5595            sizes.append(size)
5596
5597        # if there is no action pane, just return the default
5598        # positions (as specified in pane.pane_pos)
5599        if action_pane == -1:
5600            return positions, sizes
5601
5602        offset = 0
5603        for pane_i in range(action_pane-1, -1, -1):
5604            amount = positions[pane_i+1] - (positions[pane_i] + sizes[pane_i])
5605            if amount >= 0:
5606                offset += amount
5607            else:
5608                positions[pane_i] -= -amount
5609
5610            offset += sizes[pane_i]
5611
5612        # if the dock mode is fixed, make sure none of the panes
5613        # overlap we will bump panes that overlap
5614        offset = 0
5615        for pane_i in range(action_pane, pane_count):
5616            amount = positions[pane_i] - offset
5617            if amount >= 0:
5618                offset += amount
5619            else:
5620                positions[pane_i] += -amount
5621
5622            offset += sizes[pane_i]
5623
5624        return positions, sizes
5625
5626
5627    def LayoutAddPane(self, cont, dock, pane, uiparts, spacer_only):
5628        """
5629        Adds a pane into the existing layout (in an existing dock).
5630
5631        :param `cont`: a :class:`wx.Sizer` object;
5632        :param `dock`: the :class:`AuiDockInfo` structure in which to add the pane;
5633        :param `pane`: the :class:`AuiPaneInfo` instance to add to the dock;
5634        :param `uiparts`: a list of UI parts in the interface;
5635        :param bool `spacer_only`: whether to add a simple spacer or a real window.
5636        """
5637
5638        #sizer_item = wx.SizerItem()
5639        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
5640        gripper_size = self._art.GetMetric(AUI_DOCKART_GRIPPER_SIZE)
5641        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
5642        pane_button_size = self._art.GetMetric(AUI_DOCKART_PANE_BUTTON_SIZE)
5643
5644        # find out the orientation of the item (orientation for panes
5645        # is the same as the dock's orientation)
5646
5647        if dock.IsHorizontal():
5648            orientation = wx.HORIZONTAL
5649        else:
5650            orientation = wx.VERTICAL
5651
5652        # this variable will store the proportion
5653        # value that the pane will receive
5654        pane_proportion = pane.dock_proportion
5655
5656        horz_pane_sizer = wx.BoxSizer(wx.HORIZONTAL)
5657        vert_pane_sizer = wx.BoxSizer(wx.VERTICAL)
5658
5659        if pane.HasGripper():
5660
5661            part = AuiDockUIPart()
5662            if pane.HasGripperTop():
5663                sizer_item = vert_pane_sizer.Add((1, gripper_size), 0, wx.EXPAND)
5664            else:
5665                sizer_item = horz_pane_sizer.Add((gripper_size, 1), 0, wx.EXPAND)
5666
5667            part.type = AuiDockUIPart.typeGripper
5668            part.dock = dock
5669            part.pane = pane
5670            part.button = None
5671            part.orientation = orientation
5672            part.cont_sizer = horz_pane_sizer
5673            part.sizer_item = sizer_item
5674            uiparts.append(part)
5675
5676        button_count = len(pane.buttons)
5677        button_width_total = button_count*pane_button_size
5678        if button_count >= 1:
5679            button_width_total += 3
5680
5681        caption, captionLeft = pane.HasCaption(), pane.HasCaptionLeft()
5682        button_count = len(pane.buttons)
5683
5684        if captionLeft:
5685            caption_sizer = wx.BoxSizer(wx.VERTICAL)
5686
5687            # add pane buttons to the caption
5688            dummy_parts = []
5689            for btn_id in range(len(pane.buttons)-1, -1, -1):
5690                sizer_item = caption_sizer.Add((caption_size, pane_button_size), 0, wx.EXPAND)
5691                part = AuiDockUIPart()
5692                part.type = AuiDockUIPart.typePaneButton
5693                part.dock = dock
5694                part.pane = pane
5695                part.button = pane.buttons[btn_id]
5696                part.orientation = orientation
5697                part.cont_sizer = caption_sizer
5698                part.sizer_item = sizer_item
5699                dummy_parts.append(part)
5700
5701            sizer_item = caption_sizer.Add((caption_size, 1), 1, wx.EXPAND)
5702            vert_pane_sizer = wx.BoxSizer(wx.HORIZONTAL)
5703
5704            # create the caption sizer
5705            part = AuiDockUIPart()
5706
5707            part.type = AuiDockUIPart.typeCaption
5708            part.dock = dock
5709            part.pane = pane
5710            part.button = None
5711            part.orientation = orientation
5712            part.cont_sizer = vert_pane_sizer
5713            part.sizer_item = sizer_item
5714            caption_part_idx = len(uiparts)
5715            uiparts.append(part)
5716            uiparts.extend(dummy_parts)
5717
5718        elif caption:
5719
5720            caption_sizer = wx.BoxSizer(wx.HORIZONTAL)
5721            sizer_item = caption_sizer.Add((1, caption_size), 1, wx.EXPAND)
5722
5723            # create the caption sizer
5724            part = AuiDockUIPart()
5725
5726            part.type = AuiDockUIPart.typeCaption
5727            part.dock = dock
5728            part.pane = pane
5729            part.button = None
5730            part.orientation = orientation
5731            part.cont_sizer = vert_pane_sizer
5732            part.sizer_item = sizer_item
5733            caption_part_idx = len(uiparts)
5734            uiparts.append(part)
5735
5736            # add pane buttons to the caption
5737            for button in pane.buttons:
5738                sizer_item = caption_sizer.Add((pane_button_size, caption_size), 0, wx.EXPAND)
5739                part = AuiDockUIPart()
5740                part.type = AuiDockUIPart.typePaneButton
5741                part.dock = dock
5742                part.pane = pane
5743                part.button = button
5744                part.orientation = orientation
5745                part.cont_sizer = caption_sizer
5746                part.sizer_item = sizer_item
5747                uiparts.append(part)
5748
5749        if caption or captionLeft:
5750            # if we have buttons, add a little space to the right
5751            # of them to ease visual crowding
5752            if button_count >= 1:
5753                if captionLeft:
5754                    caption_sizer.Add((caption_size, 3), 0, wx.EXPAND)
5755                else:
5756                    caption_sizer.Add((3, caption_size), 0, wx.EXPAND)
5757
5758            # add the caption sizer
5759            sizer_item = vert_pane_sizer.Add(caption_sizer, 0, wx.EXPAND)
5760            uiparts[caption_part_idx].sizer_item = sizer_item
5761
5762        # add the pane window itself
5763        if spacer_only or not pane.window:
5764            sizer_item = vert_pane_sizer.Add((1, 1), 1, wx.EXPAND)
5765        else:
5766            if pane.window.GetContainingSizer():
5767                # Make sure that there is only one sizer to this window
5768                pane.window.GetContainingSizer().Detach(pane.window);
5769            sizer_item = vert_pane_sizer.Add(pane.window, 1, wx.EXPAND)
5770            vert_pane_sizer.SetItemMinSize(pane.window, (1, 1))
5771
5772        part = AuiDockUIPart()
5773        part.type = AuiDockUIPart.typePane
5774        part.dock = dock
5775        part.pane = pane
5776        part.button = None
5777        part.orientation = orientation
5778        part.cont_sizer = vert_pane_sizer
5779        part.sizer_item = sizer_item
5780        uiparts.append(part)
5781
5782        # determine if the pane should have a minimum size if the pane is
5783        # non-resizable (fixed) then we must set a minimum size. Alternatively,
5784        # if the pane.min_size is set, we must use that value as well
5785
5786        min_size = pane.min_size
5787        if pane.IsFixed():
5788            if min_size == wx.Size(-1, -1):
5789                min_size = pane.best_size
5790                pane_proportion = 0
5791
5792        if min_size != wx.Size(-1, -1):
5793            vert_pane_sizer.SetItemMinSize(len(vert_pane_sizer.GetChildren())-1, (min_size.x, min_size.y))
5794
5795        # add the vertical/horizontal sizer (caption, pane window) to the
5796        # horizontal sizer (gripper, vertical sizer)
5797        horz_pane_sizer.Add(vert_pane_sizer, 1, wx.EXPAND)
5798
5799        # finally, add the pane sizer to the dock sizer
5800        if pane.HasBorder():
5801            # allowing space for the pane's border
5802            sizer_item = cont.Add(horz_pane_sizer, pane_proportion,
5803                                  wx.EXPAND | wx.ALL, pane_border_size)
5804            part = AuiDockUIPart()
5805            part.type = AuiDockUIPart.typePaneBorder
5806            part.dock = dock
5807            part.pane = pane
5808            part.button = None
5809            part.orientation = orientation
5810            part.cont_sizer = cont
5811            part.sizer_item = sizer_item
5812            uiparts.append(part)
5813        else:
5814            sizer_item = cont.Add(horz_pane_sizer, pane_proportion, wx.EXPAND)
5815
5816        return uiparts
5817
5818
5819    def LayoutAddDock(self, cont, dock, uiparts, spacer_only):
5820        """
5821        Adds a dock into the existing layout.
5822
5823        :param `cont`: a :class:`wx.Sizer` object;
5824        :param `dock`: the :class:`AuiDockInfo` structure to add to the layout;
5825        :param `uiparts`: a list of UI parts in the interface;
5826        :param bool `spacer_only`: whether to add a simple spacer or a real window.
5827        """
5828
5829#        sizer_item = wx.SizerItem()
5830        part = AuiDockUIPart()
5831
5832        sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
5833        orientation = (dock.IsHorizontal() and [wx.HORIZONTAL] or [wx.VERTICAL])[0]
5834
5835        # resizable bottom and right docks have a sash before them
5836        if not self._has_maximized and not dock.fixed and \
5837           dock.dock_direction in [AUI_DOCK_BOTTOM, AUI_DOCK_RIGHT]:
5838
5839            sizer_item = cont.Add((sash_size, sash_size), 0, wx.EXPAND)
5840
5841            part.type = AuiDockUIPart.typeDockSizer
5842            part.orientation = orientation
5843            part.dock = dock
5844            part.pane = None
5845            part.button = None
5846            part.cont_sizer = cont
5847            part.sizer_item = sizer_item
5848            uiparts.append(part)
5849
5850        # create the sizer for the dock
5851        dock_sizer = wx.BoxSizer(orientation)
5852
5853        # add each pane to the dock
5854        has_maximized_pane = False
5855        pane_count = len(dock.panes)
5856
5857        if dock.fixed:
5858
5859            # figure out the real pane positions we will
5860            # use, without modifying the each pane's pane_pos member
5861            pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock)
5862
5863            offset = 0
5864            for pane_i in range(pane_count):
5865
5866                pane = dock.panes[pane_i]
5867                pane_pos = pane_positions[pane_i]
5868
5869                if pane.IsMaximized():
5870                    has_maximized_pane = True
5871
5872                amount = pane_pos - offset
5873                if amount > 0:
5874
5875                    if dock.IsVertical():
5876                        sizer_item = dock_sizer.Add((1, amount), 0, wx.EXPAND)
5877                    else:
5878                        sizer_item = dock_sizer.Add((amount, 1), 0, wx.EXPAND)
5879
5880                    part = AuiDockUIPart()
5881                    part.type = AuiDockUIPart.typeBackground
5882                    part.dock = dock
5883                    part.pane = None
5884                    part.button = None
5885                    part.orientation = (orientation==wx.HORIZONTAL and \
5886                                        [wx.VERTICAL] or [wx.HORIZONTAL])[0]
5887                    part.cont_sizer = dock_sizer
5888                    part.sizer_item = sizer_item
5889                    uiparts.append(part)
5890
5891                    offset = offset + amount
5892
5893                uiparts = self.LayoutAddPane(dock_sizer, dock, pane, uiparts, spacer_only)
5894
5895                offset = offset + pane_sizes[pane_i]
5896
5897            # at the end add a very small stretchable background area
5898            sizer_item = dock_sizer.Add((0, 0), 1, wx.EXPAND)
5899            part = AuiDockUIPart()
5900            part.type = AuiDockUIPart.typeBackground
5901            part.dock = dock
5902            part.pane = None
5903            part.button = None
5904            part.orientation = orientation
5905            part.cont_sizer = dock_sizer
5906            part.sizer_item = sizer_item
5907            uiparts.append(part)
5908
5909        else:
5910
5911            for pane_i in range(pane_count):
5912
5913                pane = dock.panes[pane_i]
5914
5915                if pane.IsMaximized():
5916                    has_maximized_pane = True
5917
5918                # if this is not the first pane being added,
5919                # we need to add a pane sizer
5920                if not self._has_maximized and pane_i > 0:
5921                    sizer_item = dock_sizer.Add((sash_size, sash_size), 0, wx.EXPAND)
5922                    part = AuiDockUIPart()
5923                    part.type = AuiDockUIPart.typePaneSizer
5924                    part.dock = dock
5925                    part.pane = dock.panes[pane_i-1]
5926                    part.button = None
5927                    part.orientation = (orientation==wx.HORIZONTAL and \
5928                                        [wx.VERTICAL] or [wx.HORIZONTAL])[0]
5929                    part.cont_sizer = dock_sizer
5930                    part.sizer_item = sizer_item
5931                    uiparts.append(part)
5932
5933                uiparts = self.LayoutAddPane(dock_sizer, dock, pane, uiparts, spacer_only)
5934
5935        if dock.dock_direction == AUI_DOCK_CENTER or has_maximized_pane:
5936            sizer_item = cont.Add(dock_sizer, 1, wx.EXPAND)
5937        else:
5938            sizer_item = cont.Add(dock_sizer, 0, wx.EXPAND)
5939
5940        part = AuiDockUIPart()
5941        part.type = AuiDockUIPart.typeDock
5942        part.dock = dock
5943        part.pane = None
5944        part.button = None
5945        part.orientation = orientation
5946        part.cont_sizer = cont
5947        part.sizer_item = sizer_item
5948        uiparts.append(part)
5949
5950        if dock.IsHorizontal():
5951            cont.SetItemMinSize(dock_sizer, (0, dock.size))
5952        else:
5953            cont.SetItemMinSize(dock_sizer, (dock.size, 0))
5954
5955        #  top and left docks have a sash after them
5956        if not self._has_maximized and not dock.fixed and \
5957           dock.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_LEFT]:
5958
5959            sizer_item = cont.Add((sash_size, sash_size), 0, wx.EXPAND)
5960
5961            part = AuiDockUIPart()
5962            part.type = AuiDockUIPart.typeDockSizer
5963            part.dock = dock
5964            part.pane = None
5965            part.button = None
5966            part.orientation = orientation
5967            part.cont_sizer = cont
5968            part.sizer_item = sizer_item
5969            uiparts.append(part)
5970
5971        return uiparts
5972
5973
5974    def LayoutAll(self, panes, docks, uiparts, spacer_only=False, oncheck=True):
5975        """
5976        Layouts all the UI structures in the interface.
5977
5978        :param `panes`: a list of :class:`AuiPaneInfo` instances;
5979        :param `docks`: a list of :class:`AuiDockInfo` classes;
5980        :param `uiparts`: a list of UI parts in the interface;
5981        :param bool `spacer_only`: whether to add a simple spacer or a real window;
5982        :param bool `oncheck`: whether to store the results in a class member or not.
5983        """
5984
5985        container = wx.BoxSizer(wx.VERTICAL)
5986
5987        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
5988        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
5989        cli_size = self._frame.GetClientSize()
5990
5991        # empty all docks out
5992        for dock in docks:
5993            dock.panes = []
5994            if dock.fixed:
5995                # always reset fixed docks' sizes, because
5996                # the contained windows may have been resized
5997                dock.size = 0
5998
5999        dock_count = len(docks)
6000
6001        # iterate through all known panes, filing each
6002        # of them into the appropriate dock. If the
6003        # pane does not exist in the dock, add it
6004        for p in panes:
6005
6006            # don't layout hidden panes.
6007            if p.IsShown():
6008
6009                # find any docks with the same dock direction, dock layer, and
6010                # dock row as the pane we are working on
6011                arr = FindDocks(docks, p.dock_direction, p.dock_layer, p.dock_row)
6012
6013                if arr:
6014                    dock = arr[0]
6015
6016                else:
6017                    # dock was not found, so we need to create a new one
6018                    d = AuiDockInfo()
6019                    d.dock_direction = p.dock_direction
6020                    d.dock_layer = p.dock_layer
6021                    d.dock_row = p.dock_row
6022                    docks.append(d)
6023                    dock = docks[-1]
6024
6025                    if p.HasFlag(p.needsRestore) and not p.HasFlag(p.wasMaximized):
6026
6027                        isHor = dock.IsHorizontal()
6028                        sashSize = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
6029
6030                        # get the sizes of any docks that might
6031                        # overlap with our restored dock
6032
6033                        # make list of widths or heights from the size in the dock rects
6034                        sizes = [d.rect[2:][isHor] for \
6035                                 d in docks if d.IsOk() and \
6036                                 (d.IsHorizontal() == isHor) and \
6037                                 not d.toolbar and \
6038                                 d.dock_direction != AUI_DOCK_CENTER]
6039
6040                        frameRect = GetInternalFrameRect(self._frame, self._docks)
6041
6042                        # set max size allowing for sashes and absolute minimum
6043                        maxsize = frameRect[2:][isHor] - sum(sizes) - (len(sizes)*10) - (sashSize*len(sizes))
6044                        dock.size = min(p.previousDockSize,maxsize)
6045
6046                    else:
6047                        dock.size = 0
6048
6049                if p.HasFlag(p.wasMaximized):
6050                    self.MaximizePane(p, savesizes=False)
6051                    p.SetFlag(p.wasMaximized, False)
6052
6053                if p.HasFlag(p.needsRestore):
6054                    if p.previousDockPos is not None:
6055                        DoInsertPane(dock.panes, dock.dock_direction, dock.dock_layer, dock.dock_row, p.previousDockPos)
6056                        p.dock_pos = p.previousDockPos
6057                        p.previousDockPos = None
6058                    p.SetFlag(p.needsRestore, False)
6059
6060                if p.IsDocked():
6061                    # remove the pane from any existing docks except this one
6062                    docks = RemovePaneFromDocks(docks, p, dock)
6063
6064                    # pane needs to be added to the dock,
6065                    # if it doesn't already exist
6066                    if not FindPaneInDock(dock, p.window):
6067                        dock.panes.append(p)
6068                else:
6069                    # remove the pane from any existing docks
6070                    docks = RemovePaneFromDocks(docks, p)
6071
6072        # remove any empty docks
6073        docks = [dock for dock in docks if dock.panes]
6074
6075        dock_count = len(docks)
6076        # configure the docks further
6077        for ii, dock in enumerate(docks):
6078            # sort the dock pane array by the pane's
6079            # dock position (dock_pos), in ascending order
6080            dock_pane_count = len(dock.panes)
6081            if dock_pane_count > 1:
6082                #~ dock.panes.sort(PaneSortFunc)
6083                dock.panes.sort(key = lambda pane: pane.dock_pos)
6084
6085            # for newly created docks, set up their initial size
6086            if dock.size == 0:
6087                size = 0
6088                for pane in dock.panes:
6089                    pane_size = pane.best_size
6090                    if pane_size == wx.Size(-1, -1):
6091                        pane_size = pane.min_size
6092                    if pane_size == wx.Size(-1, -1) and pane.window:
6093                        pane_size = pane.window.GetSize()
6094                    if dock.IsHorizontal():
6095                        size = max(pane_size.y, size)
6096                    else:
6097                        size = max(pane_size.x, size)
6098
6099                # add space for the border (two times), but only
6100                # if at least one pane inside the dock has a pane border
6101                for pane in dock.panes:
6102                    if pane.HasBorder():
6103                        size = size + pane_border_size*2
6104                        break
6105
6106                # if pane is on the top or bottom, add the caption height,
6107                # but only if at least one pane inside the dock has a caption
6108                if dock.IsHorizontal():
6109                    for pane in dock.panes:
6110                        if pane.HasCaption() and not pane.HasCaptionLeft():
6111                            size = size + caption_size
6112                            break
6113                else:
6114                    for pane in dock.panes:
6115                        if pane.HasCaptionLeft() and not pane.HasCaption():
6116                            size = size + caption_size
6117                            break
6118
6119                # new dock's size may not be more than the dock constraint
6120                # parameter specifies.  See SetDockSizeConstraint()
6121                max_dock_x_size = int(self._dock_constraint_x*float(cli_size.x))
6122                max_dock_y_size = int(self._dock_constraint_y*float(cli_size.y))
6123                if tuple(cli_size) <= tuple(wx.Size(20, 20)):
6124                    max_dock_x_size = 10000
6125                    max_dock_y_size = 10000
6126
6127                if dock.IsHorizontal():
6128                    size = min(size, max_dock_y_size)
6129                else:
6130                    size = min(size, max_dock_x_size)
6131
6132                # absolute minimum size for a dock is 10 pixels
6133                if size < 10:
6134                    size = 10
6135
6136                dock.size = size
6137
6138            # determine the dock's minimum size
6139            plus_border = False
6140            plus_caption = False
6141            plus_caption_left = False
6142            dock_min_size = 0
6143            for pane in dock.panes:
6144                if pane.min_size != wx.Size(-1, -1):
6145                    if pane.HasBorder():
6146                        plus_border = True
6147                    if pane.HasCaption():
6148                        plus_caption = True
6149                    if pane.HasCaptionLeft():
6150                        plus_caption_left = True
6151                    if dock.IsHorizontal():
6152                        if pane.min_size.y > dock_min_size:
6153                            dock_min_size = pane.min_size.y
6154                    else:
6155                        if pane.min_size.x > dock_min_size:
6156                            dock_min_size = pane.min_size.x
6157
6158            if plus_border:
6159                dock_min_size += pane_border_size*2
6160            if plus_caption and dock.IsHorizontal():
6161                dock_min_size += caption_size
6162            if plus_caption_left and dock.IsVertical():
6163                dock_min_size += caption_size
6164
6165            dock.min_size = dock_min_size
6166
6167            # if the pane's current size is less than it's
6168            # minimum, increase the dock's size to it's minimum
6169            if dock.size < dock.min_size:
6170                dock.size = dock.min_size
6171
6172            # determine the dock's mode (fixed or proportional)
6173            # determine whether the dock has only toolbars
6174            action_pane_marked = False
6175            dock.fixed = True
6176            dock.toolbar = True
6177            for pane in dock.panes:
6178                if not pane.IsFixed():
6179                    dock.fixed = False
6180                if not pane.IsToolbar():
6181                    dock.toolbar = False
6182                if pane.HasFlag(AuiPaneInfo.optionDockFixed):
6183                    dock.fixed = True
6184                if pane.HasFlag(AuiPaneInfo.actionPane):
6185                    action_pane_marked = True
6186
6187            # if the dock mode is proportional and not fixed-pixel,
6188            # reassign the dock_pos to the sequential 0, 1, 2, 3
6189            # e.g. remove gaps like 1, 2, 30, 500
6190            if not dock.fixed:
6191                for jj in range(dock_pane_count):
6192                    pane = dock.panes[jj]
6193                    if pane.IsNotebookPage() and pane.notebook_id < len(self._notebooks):
6194                        # update dock_pos to its index in notebook
6195                        notebook = self._notebooks[pane.notebook_id]
6196                        pane.dock_pos = notebook.GetPageIndex(pane.window)
6197                    else:
6198                        pane.dock_pos = jj
6199
6200            # if the dock mode is fixed, and none of the panes
6201            # are being moved right now, make sure the panes
6202            # do not overlap each other.  If they do, we will
6203            # adjust the panes' positions
6204            if dock.fixed and not action_pane_marked:
6205                pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock)
6206                offset = 0
6207                for jj in range(dock_pane_count):
6208                    pane = dock.panes[jj]
6209                    pane.dock_pos = pane_positions[jj]
6210                    amount = pane.dock_pos - offset
6211                    if amount >= 0:
6212                        offset += amount
6213                    else:
6214                        pane.dock_pos += -amount
6215
6216                    offset += pane_sizes[jj]
6217                    dock.panes[jj] = pane
6218
6219            if oncheck:
6220                self._docks[ii] = dock
6221
6222        # shrink docks if needed
6223##        docks = self.SmartShrink(docks, AUI_DOCK_TOP)
6224##        docks = self.SmartShrink(docks, AUI_DOCK_LEFT)
6225
6226        if oncheck:
6227            self._docks = docks
6228
6229        # discover the maximum dock layer
6230        max_layer = 0
6231        dock_count = len(docks)
6232
6233        for ii in range(dock_count):
6234            max_layer = max(max_layer, docks[ii].dock_layer)
6235
6236        # clear out uiparts
6237        uiparts = []
6238
6239        # create a bunch of box sizers,
6240        # from the innermost level outwards.
6241        cont = None
6242        middle = None
6243
6244        if oncheck:
6245            docks = self._docks
6246
6247        for layer in range(max_layer+1):
6248            # find any docks in this layer
6249            arr = FindDocks(docks, -1, layer, -1)
6250            # if there aren't any, skip to the next layer
6251            if not arr:
6252                continue
6253
6254            old_cont = cont
6255
6256            # create a container which will hold this layer's
6257            # docks (top, bottom, left, right)
6258            cont = wx.BoxSizer(wx.VERTICAL)
6259
6260            # find any top docks in this layer
6261            arr = FindDocks(docks, AUI_DOCK_TOP, layer, -1)
6262            for row in arr:
6263                uiparts = self.LayoutAddDock(cont, row, uiparts, spacer_only)
6264
6265            # fill out the middle layer (which consists
6266            # of left docks, content area and right docks)
6267
6268            middle = wx.BoxSizer(wx.HORIZONTAL)
6269
6270            # find any left docks in this layer
6271            arr = FindDocks(docks, AUI_DOCK_LEFT, layer, -1)
6272            for row in arr:
6273                uiparts = self.LayoutAddDock(middle, row, uiparts, spacer_only)
6274
6275            # add content dock (or previous layer's sizer
6276            # to the middle
6277            if not old_cont:
6278                # find any center docks
6279                arr = FindDocks(docks, AUI_DOCK_CENTER, -1, -1)
6280                if arr:
6281                    for row in arr:
6282                       uiparts = self.LayoutAddDock(middle, row, uiparts, spacer_only)
6283
6284                elif not self._has_maximized:
6285                    # there are no center docks, add a background area
6286                    sizer_item = middle.Add((1, 1), 1, wx.EXPAND)
6287                    part = AuiDockUIPart()
6288                    part.type = AuiDockUIPart.typeBackground
6289                    part.pane = None
6290                    part.dock = None
6291                    part.button = None
6292                    part.cont_sizer = middle
6293                    part.sizer_item = sizer_item
6294                    uiparts.append(part)
6295            else:
6296                middle.Add(old_cont, 1, wx.EXPAND)
6297
6298            # find any right docks in this layer
6299            arr = FindDocks(docks, AUI_DOCK_RIGHT, layer, -1, reverse=True)
6300            for row in arr:
6301                uiparts = self.LayoutAddDock(middle, row, uiparts, spacer_only)
6302
6303            if len(middle.GetChildren()) > 0:
6304                cont.Add(middle, 1, wx.EXPAND)
6305
6306            # find any bottom docks in this layer
6307            arr = FindDocks(docks, AUI_DOCK_BOTTOM, layer, -1, reverse=True)
6308            for row in arr:
6309                    uiparts = self.LayoutAddDock(cont, row, uiparts, spacer_only)
6310
6311        if not cont:
6312            # no sizer available, because there are no docks,
6313            # therefore we will create a simple background area
6314            cont = wx.BoxSizer(wx.VERTICAL)
6315            sizer_item = cont.Add((1, 1), 1, wx.EXPAND)
6316            part = AuiDockUIPart()
6317            part.type = AuiDockUIPart.typeBackground
6318            part.pane = None
6319            part.dock = None
6320            part.button = None
6321            part.cont_sizer = middle
6322            part.sizer_item = sizer_item
6323            uiparts.append(part)
6324
6325        if oncheck:
6326            self._uiparts = uiparts
6327            self._docks = docks
6328
6329        container.Add(cont, 1, wx.EXPAND)
6330
6331        if oncheck:
6332            return container
6333        else:
6334            return container, panes, docks, uiparts
6335
6336
6337    def SetDockSizeConstraint(self, width_pct, height_pct):
6338        """
6339        When a user creates a new dock by dragging a window into a docked position,
6340        often times the large size of the window will create a dock that is unwieldly
6341        large.
6342
6343        :class:`AuiManager` by default limits the size of any new dock to 1/3 of the window
6344        size. For horizontal docks, this would be 1/3 of the window height. For vertical
6345        docks, 1/3 of the width. Calling this function will adjust this constraint value.
6346
6347        The numbers must be between 0.0 and 1.0. For instance, calling :meth:`SetDockSizeConstraint`
6348        with (0.5, 0.5) will cause new docks to be limited to half of the size of the entire
6349        managed window.
6350
6351        :param float `width_pct`: a number representing the `x` dock size constraint;
6352        :param float `width_pct`: a number representing the `y` dock size constraint.
6353        """
6354
6355        self._dock_constraint_x = max(0.0, min(1.0, width_pct))
6356        self._dock_constraint_y = max(0.0, min(1.0, height_pct))
6357
6358
6359    def GetDockSizeConstraint(self):
6360        """
6361        Returns the current dock constraint values.
6362
6363        :see: :meth:`SetDockSizeConstraint`
6364        """
6365
6366        return self._dock_constraint_x, self._dock_constraint_y
6367
6368
6369    def Update(self):
6370        if '__WXGTK__' in wx.PlatformInfo:
6371            wx.CallAfter(self.DoUpdate)
6372        else:
6373            self.DoUpdate()
6374
6375    def DoUpdateEvt(self, evt):
6376        self.Unbind(wx.EVT_WINDOW_CREATE)
6377        wx.CallAfter(self.DoUpdate)
6378
6379    def DoUpdate(self):
6380        """
6381        This method is called after any number of changes are made to any of the
6382        managed panes. :meth:`Update` must be invoked after :meth:`AddPane`
6383        or :meth:`InsertPane` are called in order to "realize" or "commit" the changes.
6384
6385        In addition, any number of changes may be made to :class:`AuiManager` structures
6386        (retrieved with :meth:`GetPane`), but to realize the changes, :meth:`Update`
6387        must be called. This construction allows pane flicker to be avoided by updating
6388        the whole layout at one time.
6389        """
6390
6391        if not self.GetManagedWindow():
6392            return
6393
6394        self._hover_button = None
6395        self._action_part = None
6396
6397        # destroy floating panes which have been
6398        # redocked or are becoming non-floating
6399        for p in self._panes:
6400            if p.IsFloating() or not p.frame:
6401                continue
6402
6403            # because the pane is no longer in a floating, we need to
6404            # reparent it to self._frame and destroy the floating frame
6405            # reduce flicker
6406            p.window.SetSize((1, 1))
6407
6408            # the following block is a workaround for bug #1531361
6409            # (see wxWidgets sourceforge page).  On wxGTK (only), when
6410            # a frame is shown/hidden, a move event unfortunately
6411            # also gets fired.  Because we may be dragging around
6412            # a pane, we need to cancel that action here to prevent
6413            # a spurious crash.
6414            if self._action_window == p.frame:
6415                if self._frame.HasCapture():
6416                    self._frame.ReleaseMouse()
6417                self._action = actionNone
6418                self._action_window = None
6419
6420            # hide the frame
6421            if p.frame.IsShown():
6422                p.frame.Show(False)
6423
6424            if self._action_window == p.frame:
6425                self._action_window = None
6426
6427            # reparent to self._frame and destroy the pane
6428            p.window.Reparent(self._frame)
6429            if isinstance(p.window, auibar.AuiToolBar):
6430                p.window.SetAuiManager(self)
6431
6432            if p.frame:
6433                p.frame.SetSizer(None)
6434                while p.frame.GetEventHandler() is not p.frame:
6435                    p.frame.PopEventHandler()
6436                p.frame.Destroy()
6437            p.frame = None
6438
6439        # Only the master manager should create/destroy notebooks...
6440        if not self._masterManager:
6441            self.UpdateNotebook()
6442
6443        # delete old sizer first
6444        self._frame.SetSizer(None)
6445
6446        # create a layout for all of the panes
6447        sizer = self.LayoutAll(self._panes, self._docks, self._uiparts, False)
6448
6449        # hide or show panes as necessary,
6450        # and float panes as necessary
6451
6452        pane_count = len(self._panes)
6453
6454        for ii in range(pane_count):
6455            p = self._panes[ii]
6456            pFrame = p.frame
6457
6458            if p.IsFloating():
6459                if p.IsToolbar():
6460                    bar = p.window
6461                    if isinstance(bar, auibar.AuiToolBar):
6462                        bar.SetGripperVisible(False)
6463                        agwStyle = bar.GetAGWWindowStyleFlag()
6464                        bar.SetAGWWindowStyleFlag(agwStyle & ~AUI_TB_VERTICAL)
6465                        bar.Realize()
6466
6467                    s = p.window.GetMinSize()
6468                    p.BestSize(s)
6469                    p.FloatingSize(wx.DefaultSize)
6470
6471                if pFrame is None:
6472                    # we need to create a frame for this
6473                    # pane, which has recently been floated
6474                    frame = self.CreateFloatingFrame(self._frame, p)
6475
6476                    # on MSW and Mac, if the owner desires transparent dragging, and
6477                    # the dragging is happening right now, then the floating
6478                    # window should have this style by default
6479                    if self._action in [actionDragFloatingPane, actionDragToolbarPane] and \
6480                       self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
6481                        frame.SetTransparent(150)
6482
6483                    frame.SetPaneWindow(p)
6484                    p.needsTransparency = True
6485                    p.frame = pFrame = frame
6486                    if p.IsShown() and not frame.IsShown():
6487                        frame.Show()
6488                        frame.Update()
6489                else:
6490
6491                    # frame already exists, make sure it's position
6492                    # and size reflect the information in AuiPaneInfo
6493                    if pFrame.GetPosition() != p.floating_pos or pFrame.GetSize() != p.floating_size:
6494                        pFrame.SetSize(p.floating_pos.x, p.floating_pos.y,
6495                                       p.floating_size.x, p.floating_size.y, wx.SIZE_USE_EXISTING)
6496
6497                    # update whether the pane is resizable or not
6498                    style = p.frame.GetWindowStyleFlag()
6499                    if p.IsFixed():
6500                        style &= ~wx.RESIZE_BORDER
6501                    else:
6502                        style |= wx.RESIZE_BORDER
6503
6504                    p.frame.SetWindowStyleFlag(style)
6505
6506                    if pFrame.IsShown() != p.IsShown():
6507                        p.needsTransparency = True
6508                        pFrame.Show(p.IsShown())
6509
6510                if pFrame.GetTitle() != p.caption:
6511                    pFrame.SetTitle(p.caption)
6512                if p.icon.IsOk():
6513                    pFrame.SetIcon(wx.Icon(p.icon))
6514
6515            else:
6516
6517                if p.IsToolbar():
6518                    self.SwitchToolBarOrientation(p)
6519                    p.best_size = p.window.GetBestSize()
6520
6521                if p.window and not p.IsNotebookPage() and p.window.IsShown() != p.IsShown():
6522                    p.window.Show(p.IsShown())
6523
6524            if pFrame and p.needsTransparency:
6525                if pFrame.IsShown() and pFrame._transparent != p.transparent:
6526                    pFrame.SetTransparent(p.transparent)
6527                    pFrame._transparent = p.transparent
6528
6529                p.needsTransparency = False
6530
6531            # if "active panes" are no longer allowed, clear
6532            # any optionActive values from the pane states
6533            if self._agwFlags & AUI_MGR_ALLOW_ACTIVE_PANE == 0:
6534                p.state &= ~AuiPaneInfo.optionActive
6535
6536            self._panes[ii] = p
6537
6538        old_pane_rects = []
6539        pane_count = len(self._panes)
6540
6541        for p in self._panes:
6542            r = wx.Rect()
6543            if p.window and p.IsShown() and p.IsDocked():
6544                r = p.rect
6545
6546            old_pane_rects.append(r)
6547
6548        # apply the new sizer
6549        self._frame.SetSizer(sizer)
6550        self._frame.SetAutoLayout(False)
6551        self.DoFrameLayout()
6552
6553        # now that the frame layout is done, we need to check
6554        # the new pane rectangles against the old rectangles that
6555        # we saved a few lines above here.  If the rectangles have
6556        # changed, the corresponding panes must also be updated
6557        for ii in range(pane_count):
6558            p = self._panes[ii]
6559            if p.window and p.IsShown() and p.IsDocked():
6560                if p.rect != old_pane_rects[ii]:
6561                    p.window.Refresh()
6562                    p.window.Update()
6563
6564        if wx.Platform == "__WXMAC__":
6565            self._frame.Refresh()
6566        else:
6567            self.Repaint()
6568
6569        if not self._masterManager:
6570            e = self.FireEvent(wxEVT_AUI_PERSPECTIVE_CHANGED, None, canVeto=False)
6571
6572
6573
6574    def UpdateNotebook(self):
6575        """ Updates the automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook` in the layout (if any exists). """
6576
6577        # Workout how many notebooks we need.
6578        max_notebook = -1
6579
6580        # destroy floating panes which have been
6581        # redocked or are becoming non-floating
6582        for paneInfo in self._panes:
6583            if max_notebook < paneInfo.notebook_id:
6584                max_notebook = paneInfo.notebook_id
6585
6586        # We are the master of our domain
6587        extra_notebook = len(self._notebooks)
6588        max_notebook += 1
6589
6590        for i in range(extra_notebook, max_notebook):
6591            self.CreateNotebook()
6592
6593        # Remove pages from notebooks that no-longer belong there ...
6594        for nb, notebook in enumerate(self._notebooks):
6595            pages = notebook.GetPageCount()
6596            pageCounter, allPages = 0, pages
6597
6598            # Check each tab ...
6599            for page in range(pages):
6600
6601                if page >= allPages:
6602                    break
6603
6604                window = notebook.GetPage(pageCounter)
6605                paneInfo = self.GetPane(window)
6606                if paneInfo.IsOk() and paneInfo.notebook_id != nb:
6607                    notebook.RemovePage(pageCounter)
6608                    window.Hide()
6609                    window.Reparent(self._frame)
6610                    pageCounter -= 1
6611                    allPages -= 1
6612                    paneInfo.Direction(self.GetPane(notebook).dock_direction)
6613
6614                pageCounter += 1
6615
6616            notebook.DoSizing()
6617
6618        # Add notebook pages that aren't there already...
6619        pages_and_panes = {}
6620        for paneInfo in self._panes:
6621            if paneInfo.IsNotebookPage():
6622
6623                title = (paneInfo.caption == "" and [paneInfo.name] or [paneInfo.caption])[0]
6624
6625                notebook = self._notebooks[paneInfo.notebook_id]
6626                page_id = notebook.GetPageIndex(paneInfo.window)
6627
6628                if page_id < 0:
6629
6630                    if paneInfo.notebook_id not in pages_and_panes:
6631                        pages_and_panes[paneInfo.notebook_id] = []
6632                    pages_and_panes[paneInfo.notebook_id].append(paneInfo)
6633
6634                # Update title and icon ...
6635                else:
6636
6637                    notebook.SetPageText(page_id, title)
6638                    notebook.SetPageBitmap(page_id, paneInfo.icon)
6639
6640                notebook.DoSizing()
6641
6642            # Wire-up newly created notebooks
6643            elif paneInfo.IsNotebookControl() and not paneInfo.window:
6644                paneInfo.window = self._notebooks[paneInfo.notebook_id]
6645
6646        for notebook_id, pnp in six.iteritems(pages_and_panes):
6647            # sort the panes with dock_pos
6648            sorted_pnp = sorted(pnp, key=lambda pane: pane.dock_pos)
6649            notebook = self._notebooks[notebook_id]
6650            for pane in sorted_pnp:
6651                title = (pane.caption == "" and [pane.name] or [pane.caption])[0]
6652                pane.window.Reparent(notebook)
6653                notebook.AddPage(pane.window, title, True, pane.icon)
6654            notebook.DoSizing()
6655
6656        # Delete empty notebooks, and convert notebooks with 1 page to
6657        # normal panes...
6658        remap_ids = [-1]*len(self._notebooks)
6659        nb_idx = 0
6660
6661        for nb, notebook in enumerate(self._notebooks):
6662            if notebook.GetPageCount() == 1:
6663
6664                # Convert notebook page to pane...
6665                window = notebook.GetPage(0)
6666                child_pane = self.GetPane(window)
6667                notebook_pane = self.GetPane(notebook)
6668                if child_pane.IsOk() and notebook_pane.IsOk():
6669
6670                    child_pane.SetDockPos(notebook_pane)
6671                    child_pane.Show(notebook_pane.IsShown())
6672                    child_pane.window.Hide()
6673                    child_pane.window.Reparent(self._frame)
6674                    child_pane.frame = None
6675                    child_pane.notebook_id = -1
6676                    if notebook_pane.IsFloating():
6677                        child_pane.Float()
6678
6679                    self.DetachPane(notebook)
6680
6681                    notebook.RemovePage(0)
6682                    notebook.Destroy()
6683
6684                else:
6685
6686                    raise Exception("Odd notebook docking")
6687
6688            elif notebook.GetPageCount() == 0:
6689
6690                self.DetachPane(notebook)
6691                notebook.Destroy()
6692
6693            else:
6694
6695                self._notebooks[nb_idx] = notebook
6696
6697                # It's a keeper.
6698                remap_ids[nb] = nb_idx
6699                nb_idx += 1
6700
6701        # Apply remap...
6702        nb_count = len(self._notebooks)
6703
6704        if nb_count != nb_idx:
6705
6706            self._notebooks = self._notebooks[0:nb_idx]
6707            for p in self._panes:
6708                if p.notebook_id >= 0:
6709                    p.notebook_id = remap_ids[p.notebook_id]
6710                    if p.IsNotebookControl():
6711                        p.SetNameFromNotebookId()
6712
6713        # Make sure buttons are correct ...
6714        for notebook in self._notebooks:
6715            want_max = True
6716            want_min = True
6717            want_close = True
6718
6719            pages = notebook.GetPageCount()
6720            for page in range(pages):
6721
6722                win = notebook.GetPage(page)
6723                pane = self.GetPane(win)
6724                if pane.IsOk():
6725
6726                    if not pane.HasCloseButton():
6727                        want_close = False
6728                    if not pane.HasMaximizeButton():
6729                        want_max = False
6730                    if not pane.HasMinimizeButton():
6731                        want_min = False
6732
6733            notebook_pane = self.GetPane(notebook)
6734            if notebook_pane.IsOk():
6735                if notebook_pane.HasMinimizeButton() != want_min:
6736                    if want_min:
6737                        button = AuiPaneButton(AUI_BUTTON_MINIMIZE)
6738                        notebook_pane.state |= AuiPaneInfo.buttonMinimize
6739                        notebook_pane.buttons.append(button)
6740
6741                    # todo: remove min/max
6742
6743                if notebook_pane.HasMaximizeButton() != want_max:
6744                    if want_max:
6745                        button = AuiPaneButton(AUI_BUTTON_MAXIMIZE_RESTORE)
6746                        notebook_pane.state |= AuiPaneInfo.buttonMaximize
6747                        notebook_pane.buttons.append(button)
6748
6749                    # todo: remove min/max
6750
6751                if notebook_pane.HasCloseButton() != want_close:
6752                    if want_close:
6753                        button = AuiPaneButton(AUI_BUTTON_CLOSE)
6754                        notebook_pane.state |= AuiPaneInfo.buttonClose
6755                        notebook_pane.buttons.append(button)
6756
6757                    # todo: remove close
6758
6759
6760    def SmartShrink(self, docks, direction):
6761        """
6762        Used to intelligently shrink the docks' size (if needed).
6763
6764        :param `docks`: a list of :class:`AuiDockInfo` instances;
6765        :param integer `direction`: the direction in which to shrink.
6766        """
6767
6768        sashSize = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
6769        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
6770        clientSize = self._frame.GetClientSize()
6771        ourDocks = FindDocks(docks, direction, -1, -1)
6772        oppositeDocks = FindOppositeDocks(docks, direction)
6773        oppositeSize = self.GetOppositeDockTotalSize(docks, direction)
6774        ourSize = 0
6775
6776        for dock in ourDocks:
6777            ourSize += dock.size
6778
6779            if not dock.toolbar:
6780                ourSize += sashSize
6781
6782        shrinkSize = ourSize + oppositeSize
6783
6784        if direction == AUI_DOCK_TOP or direction == AUI_DOCK_BOTTOM:
6785            shrinkSize -= clientSize.y
6786        else:
6787            shrinkSize -= clientSize.x
6788
6789        if shrinkSize <= 0:
6790            return docks
6791
6792        # Combine arrays
6793        for dock in oppositeDocks:
6794            ourDocks.append(dock)
6795
6796        oppositeDocks = []
6797
6798        for dock in ourDocks:
6799            if dock.toolbar or not dock.resizable:
6800                continue
6801
6802            dockRange = dock.size - dock.min_size
6803
6804            if dock.min_size == 0:
6805                dockRange -= sashSize
6806                if direction == AUI_DOCK_TOP or direction == AUI_DOCK_BOTTOM:
6807                    dockRange -= caption_size
6808
6809            if dockRange >= shrinkSize:
6810
6811                dock.size -= shrinkSize
6812                return docks
6813
6814            else:
6815
6816                dock.size -= dockRange
6817                shrinkSize -= dockRange
6818
6819        return docks
6820
6821
6822    def UpdateDockingGuides(self, paneInfo):
6823        """
6824        Updates the docking guide windows positions and appearance.
6825
6826        :param `paneInfo`: a :class:`AuiPaneInfo` instance.
6827        """
6828
6829        if len(self._guides) == 0:
6830            self.CreateGuideWindows()
6831
6832        captionSize = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
6833        frameRect = GetInternalFrameRect(self._frame, self._docks)
6834        mousePos = wx.GetMousePosition()
6835
6836        for indx, guide in enumerate(self._guides):
6837
6838            pt = wx.Point()
6839            guide_size = guide.host.GetSize()
6840            if not guide.host:
6841                raise Exception("Invalid docking host")
6842
6843            direction = guide.dock_direction
6844
6845            if direction == AUI_DOCK_LEFT:
6846                pt.x = frameRect.x + guide_size.x / 2 + 16
6847                pt.y = frameRect.y + frameRect.height / 2
6848
6849            elif direction == AUI_DOCK_TOP:
6850                pt.x = frameRect.x + frameRect.width / 2
6851                pt.y = frameRect.y + guide_size.y / 2 + 16
6852
6853            elif direction == AUI_DOCK_RIGHT:
6854                pt.x = frameRect.x + frameRect.width - guide_size.x / 2 - 16
6855                pt.y = frameRect.y + frameRect.height / 2
6856
6857            elif direction == AUI_DOCK_BOTTOM:
6858                pt.x = frameRect.x + frameRect.width / 2
6859                pt.y = frameRect.y + frameRect.height - guide_size.y / 2 - 16
6860
6861            elif direction == AUI_DOCK_CENTER:
6862                rc = paneInfo.window.GetScreenRect()
6863                pt.x = rc.x + rc.width / 2
6864                pt.y = rc.y + rc.height / 2
6865                if paneInfo.HasCaption():
6866                    pt.y -= captionSize / 2
6867                elif paneInfo.HasCaptionLeft():
6868                    pt.x -= captionSize / 2
6869
6870            # guide will be centered around point 'pt'
6871            targetPosition = wx.Point(pt.x - guide_size.x / 2, pt.y - guide_size.y / 2)
6872
6873            if guide.host.GetPosition() != targetPosition:
6874                guide.host.Move(targetPosition)
6875
6876            guide.host.AeroMove(targetPosition)
6877
6878            if guide.dock_direction == AUI_DOCK_CENTER:
6879                guide.host.ValidateNotebookDocking(paneInfo.IsNotebookDockable())
6880
6881            if guide.host.IsShownOnScreen():
6882                guide.host.UpdateDockGuide(mousePos)
6883
6884        paneInfo.window.Lower()
6885
6886
6887    def DoFrameLayout(self):
6888        """
6889        This is an internal function which invokes :meth:`wx.Sizer.Layout() <Sizer.Layout>`
6890        on the frame's main sizer, then measures all the various UI items
6891        and updates their internal rectangles.
6892
6893        :note: This should always be called instead of calling
6894         `self._managed_window.Layout()` directly.
6895        """
6896
6897        self._frame.Layout()
6898
6899        for part in self._uiparts:
6900            # get the rectangle of the UI part
6901            # originally, this code looked like this:
6902            #    part.rect = wx.Rect(part.sizer_item.GetPosition(),
6903            #                       part.sizer_item.GetSize())
6904            # this worked quite well, with one exception: the mdi
6905            # client window had a "deferred" size variable
6906            # that returned the wrong size.  It looks like
6907            # a bug in wx, because the former size of the window
6908            # was being returned.  So, we will retrieve the part's
6909            # rectangle via other means
6910
6911            part.rect = part.sizer_item.GetRect()
6912            flag = part.sizer_item.GetFlag()
6913            border = part.sizer_item.GetBorder()
6914
6915            if flag & wx.TOP:
6916                part.rect.y -= border
6917                part.rect.height += border
6918            if flag & wx.LEFT:
6919                part.rect.x -= border
6920                part.rect.width += border
6921            if flag & wx.BOTTOM:
6922                part.rect.height += border
6923            if flag & wx.RIGHT:
6924                part.rect.width += border
6925
6926            if part.type == AuiDockUIPart.typeDock:
6927                part.dock.rect = part.rect
6928            if part.type == AuiDockUIPart.typePane:
6929                part.pane.rect = part.rect
6930
6931
6932    def GetPanePart(self, wnd):
6933        """
6934        Looks up the pane border UI part of the
6935        pane specified. This allows the caller to get the exact rectangle
6936        of the pane in question, including decorations like caption and border.
6937
6938        :param wx.Window `wnd`: the window to which the pane border belongs to.
6939        """
6940
6941        for part in self._uiparts:
6942            if part.type == AuiDockUIPart.typePaneBorder and \
6943               part.pane and part.pane.window == wnd:
6944                return part
6945
6946        for part in self._uiparts:
6947            if part.type == AuiDockUIPart.typePane and \
6948               part.pane and part.pane.window == wnd:
6949                return part
6950
6951        return None
6952
6953
6954    def GetDockPixelOffset(self, test):
6955        """
6956        This is an internal function which returns a dock's offset in pixels from
6957        the left side of the window (for horizontal docks) or from the top of the
6958        window (for vertical docks).
6959
6960        This value is necessary for calculating fixed-pane/toolbar offsets
6961        when they are dragged.
6962
6963        :param `test`: a fake :class:`AuiPaneInfo` for testing purposes.
6964        """
6965
6966        # the only way to accurately calculate the dock's
6967        # offset is to actually run a theoretical layout
6968        docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
6969        panes.append(test)
6970
6971        sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False)
6972        client_size = self._frame.GetClientSize()
6973        sizer.SetDimension(0, 0, client_size.x, client_size.y)
6974        sizer.Layout()
6975
6976        for part in uiparts:
6977            pos = part.sizer_item.GetPosition()
6978            size = part.sizer_item.GetSize()
6979            part.rect = wx.Rect(pos, size)
6980            if part.type == AuiDockUIPart.typeDock:
6981                part.dock.rect = part.rect
6982
6983        sizer.Destroy()
6984
6985        for dock in docks:
6986            if test.dock_direction == dock.dock_direction and \
6987               test.dock_layer == dock.dock_layer and  \
6988               test.dock_row == dock.dock_row:
6989
6990                if dock.IsVertical():
6991                    return dock.rect.y
6992                else:
6993                    return dock.rect.x
6994
6995        return 0
6996
6997
6998    def GetPartnerDock(self, dock):
6999        """
7000        Returns the partner dock for the input dock.
7001
7002        :param `dock`: a :class:`AuiDockInfo` instance.
7003        """
7004
7005        for layer in range(dock.dock_layer, -1, -1):
7006
7007            bestDock = None
7008
7009            for tmpDock in self._docks:
7010
7011                if tmpDock.dock_layer != layer:
7012                    continue
7013
7014                if tmpDock.dock_direction != dock.dock_direction:
7015                    continue
7016
7017                if tmpDock.dock_layer < dock.dock_layer:
7018
7019                    if not bestDock or tmpDock.dock_row < bestDock.dock_row:
7020                        bestDock = tmpDock
7021
7022                elif tmpDock.dock_row > dock.dock_row:
7023
7024                    if not bestDock or tmpDock.dock_row > bestDock.dock_row:
7025                        bestDock = tmpDock
7026
7027            if bestDock:
7028                return bestDock
7029
7030        return None
7031
7032
7033    def GetPartnerPane(self, dock, pane):
7034        """
7035        Returns the partner pane for the input pane. They both need to live
7036        in the same :class:`AuiDockInfo`.
7037
7038        :param `dock`: a :class:`AuiDockInfo` instance;
7039        :param `pane`: a :class:`AuiPaneInfo` class.
7040        """
7041
7042        panePosition = -1
7043
7044        for i, tmpPane in enumerate(dock.panes):
7045            if tmpPane.window == pane.window:
7046                panePosition = i
7047            elif not tmpPane.IsFixed() and panePosition != -1:
7048                return tmpPane
7049
7050        return None
7051
7052
7053    def GetTotalPixSizeAndProportion(self, dock):
7054        """
7055        Returns the dimensions and proportion of the input dock.
7056
7057        :param `dock`: the :class:`AuiDockInfo` structure to analyze.
7058        """
7059
7060        totalPixsize = 0
7061        totalProportion = 0
7062
7063        # determine the total proportion of all resizable panes,
7064        # and the total size of the dock minus the size of all
7065        # the fixed panes
7066        for tmpPane in dock.panes:
7067
7068            if tmpPane.IsFixed():
7069                continue
7070
7071            totalProportion += tmpPane.dock_proportion
7072
7073            if dock.IsHorizontal():
7074                totalPixsize += tmpPane.rect.width
7075            else:
7076                totalPixsize += tmpPane.rect.height
7077
7078##            if tmpPane.min_size.IsFullySpecified():
7079##
7080##                if dock.IsHorizontal():
7081##                    totalPixsize -= tmpPane.min_size.x
7082##                else:
7083##                    totalPixsize -= tmpPane.min_size.y
7084
7085        return totalPixsize, totalProportion
7086
7087
7088    def GetOppositeDockTotalSize(self, docks, direction):
7089        """
7090        Returns the dimensions of the dock which lives opposite of the input dock.
7091
7092        :param `docks`: a list of :class:`AuiDockInfo` structures to analyze;
7093        :param integer `direction`: the direction in which to look for the opposite dock.
7094        """
7095
7096        sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
7097        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
7098        pane_border_size = self._art.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE)
7099        minSizeMax = 0
7100        result = sash_size
7101        vertical = False
7102
7103        if direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]:
7104            vertical = True
7105
7106        # Get minimum size of the most inner area
7107        for tmpDock in docks:
7108
7109            if tmpDock.dock_layer != 0:
7110                continue
7111
7112            if tmpDock.dock_direction != AUI_DOCK_CENTER and tmpDock.IsVertical() != vertical:
7113                continue
7114
7115            for tmpPane in tmpDock.panes:
7116
7117                minSize = pane_border_size*2 - sash_size
7118
7119                if vertical:
7120                    minSize += tmpPane.min_size.y + caption_size
7121                else:
7122                    minSize += tmpPane.min_size.x
7123
7124                if minSize > minSizeMax:
7125                    minSizeMax = minSize
7126
7127        result += minSizeMax
7128
7129        # Get opposite docks
7130        oppositeDocks = FindOppositeDocks(docks, direction)
7131
7132        # Sum size of the opposite docks and their sashes
7133        for dock in oppositeDocks:
7134            result += dock.size
7135            # if it's not a toolbar add the sash_size too
7136            if not dock.toolbar:
7137                result += sash_size
7138
7139        return result
7140
7141
7142    def CalculateDockSizerLimits(self, dock):
7143        """
7144        Calculates the minimum and maximum sizes allowed for the input dock.
7145
7146        :param `dock`: the :class:`AuiDockInfo` structure to analyze.
7147        """
7148
7149        docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
7150
7151        sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
7152        caption_size = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
7153        opposite_size = self.GetOppositeDockTotalSize(docks, dock.dock_direction)
7154
7155        for tmpDock in docks:
7156
7157            if tmpDock.dock_direction == dock.dock_direction and \
7158               tmpDock.dock_layer == dock.dock_layer and \
7159               tmpDock.dock_row == dock.dock_row:
7160
7161                tmpDock.size = 1
7162                break
7163
7164        sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False)
7165        client_size = self._frame.GetClientSize()
7166        sizer.SetDimension(0, 0, client_size.x, client_size.y)
7167        sizer.Layout()
7168
7169        for part in uiparts:
7170
7171            part.rect = wx.Rect(part.sizer_item.GetPosition(), part.sizer_item.GetSize())
7172            if part.type == AuiDockUIPart.typeDock:
7173                part.dock.rect = part.rect
7174
7175        sizer.Destroy()
7176        new_dock = None
7177
7178        for tmpDock in docks:
7179            if tmpDock.dock_direction == dock.dock_direction and \
7180               tmpDock.dock_layer == dock.dock_layer and \
7181               tmpDock.dock_row == dock.dock_row:
7182
7183                new_dock = tmpDock
7184                break
7185
7186        partnerDock = self.GetPartnerDock(dock)
7187
7188        if partnerDock:
7189            partnerRange = partnerDock.size - partnerDock.min_size
7190            if partnerDock.min_size == 0:
7191                partnerRange -= sash_size
7192                if dock.IsHorizontal():
7193                    partnerRange -= caption_size
7194
7195            direction = dock.dock_direction
7196
7197            if direction == AUI_DOCK_LEFT:
7198                minPix = new_dock.rect.x + new_dock.rect.width
7199                maxPix = dock.rect.x + dock.rect.width
7200                maxPix += partnerRange
7201
7202            elif direction == AUI_DOCK_TOP:
7203                minPix = new_dock.rect.y + new_dock.rect.height
7204                maxPix = dock.rect.y + dock.rect.height
7205                maxPix += partnerRange
7206
7207            elif direction == AUI_DOCK_RIGHT:
7208                minPix = dock.rect.x - partnerRange - sash_size
7209                maxPix = new_dock.rect.x - sash_size
7210
7211            elif direction == AUI_DOCK_BOTTOM:
7212                minPix = dock.rect.y - partnerRange - sash_size
7213                maxPix = new_dock.rect.y - sash_size
7214
7215            return minPix, maxPix
7216
7217        direction = new_dock.dock_direction
7218
7219        if direction == AUI_DOCK_LEFT:
7220            minPix = new_dock.rect.x + new_dock.rect.width
7221            maxPix = client_size.x - opposite_size - sash_size
7222
7223        elif direction == AUI_DOCK_TOP:
7224            minPix = new_dock.rect.y + new_dock.rect.height
7225            maxPix = client_size.y - opposite_size - sash_size
7226
7227        elif direction == AUI_DOCK_RIGHT:
7228            minPix = opposite_size
7229            maxPix = new_dock.rect.x - sash_size
7230
7231        elif direction == AUI_DOCK_BOTTOM:
7232            minPix = opposite_size
7233            maxPix = new_dock.rect.y - sash_size
7234
7235        return minPix, maxPix
7236
7237
7238    def CalculatePaneSizerLimits(self, dock, pane):
7239        """
7240        Calculates the minimum and maximum sizes allowed for the input pane.
7241
7242        :param `dock`: the :class:`AuiDockInfo` structure to which `pane` belongs to;
7243        :param `pane`: a :class:`AuiPaneInfo` class for which calculation are requested.
7244        """
7245
7246        if pane.IsFixed():
7247            if dock.IsHorizontal():
7248                minPix = maxPix = pane.rect.x + 1 + pane.rect.width
7249            else:
7250                minPix = maxPix = pane.rect.y + 1 + pane.rect.height
7251
7252            return minPix, maxPix
7253
7254        totalPixsize, totalProportion = self.GetTotalPixSizeAndProportion(dock)
7255        partnerPane = self.GetPartnerPane(dock, pane)
7256
7257        if dock.IsHorizontal():
7258
7259            minPix = pane.rect.x + 1
7260            maxPix = pane.rect.x + 1 + pane.rect.width
7261
7262            if pane.min_size.IsFullySpecified():
7263                minPix += pane.min_size.x
7264            else:
7265                minPix += 1
7266
7267            if partnerPane:
7268                maxPix += partnerPane.rect.width
7269
7270                if partnerPane.min_size.IsFullySpecified():
7271                    maxPix -= partnerPane.min_size.x - 1
7272
7273            else:
7274                minPix = maxPix
7275
7276        else:
7277
7278            minPix = pane.rect.y + 1
7279            maxPix = pane.rect.y + 1 + pane.rect.height
7280
7281            if pane.min_size.IsFullySpecified():
7282                minPix += pane.min_size.y
7283            else:
7284                minPix += 1
7285
7286            if partnerPane:
7287                maxPix += partnerPane.rect.height
7288
7289                if partnerPane.min_size.IsFullySpecified():
7290                    maxPix -= partnerPane.min_size.y - 1
7291
7292            else:
7293                minPix = maxPix
7294
7295        return minPix, maxPix
7296
7297
7298    def CheckMovableSizer(self, part):
7299        """
7300        Checks if a UI part can be actually resized.
7301
7302        :param `part`: a UI part, an instance of :class:`AuiDockUIPart`.
7303        """
7304
7305        # a dock may not be resized if it has a single
7306        # pane which is not resizable
7307        if part.type == AuiDockUIPart.typeDockSizer and part.dock and \
7308           len(part.dock.panes) == 1 and part.dock.panes[0].IsFixed():
7309
7310            return False
7311
7312        if part.pane:
7313
7314            # panes that may not be resized should be ignored here
7315            minPix, maxPix = self.CalculatePaneSizerLimits(part.dock, part.pane)
7316
7317            if minPix == maxPix:
7318                return False
7319
7320        return True
7321
7322
7323    def PaneFromTabEvent(self, event):
7324        """
7325        Returns a :class:`AuiPaneInfo` from a :class:`~wx.lib.agw.aui.auibook.AuiNotebook` event.
7326
7327        :param `event`: a :class:`~wx.lib.agw.aui.auibook.AuiNotebookEvent` event.
7328        """
7329
7330        obj = event.GetEventObject()
7331
7332        if obj and isinstance(obj, auibook.AuiTabCtrl):
7333
7334            page_idx = obj.GetActivePage()
7335
7336            if page_idx >= 0:
7337                page = obj.GetPage(page_idx)
7338                window = page.window
7339                if window:
7340                    return self.GetPane(window)
7341
7342        elif obj and isinstance(obj, auibook.AuiNotebook):
7343
7344            page_idx = event.GetSelection()
7345
7346            if page_idx >= 0:
7347                window = obj.GetPage(page_idx)
7348                if window:
7349                    return self.GetPane(window)
7350
7351        return NonePaneInfo
7352
7353
7354    def OnTabBeginDrag(self, event):
7355        """
7356        Handles the ``EVT_AUINOTEBOOK_BEGIN_DRAG`` event.
7357
7358        :param `event`: a :class:`~wx.lib.agw.aui.auibook.AuiNotebookEvent` event to be processed.
7359        """
7360
7361        if self._masterManager:
7362            self._masterManager.OnTabBeginDrag(event)
7363
7364        else:
7365            paneInfo = self.PaneFromTabEvent(event)
7366
7367            if paneInfo.IsOk():
7368
7369                # It's one of ours!
7370                self._action = actionDragFloatingPane
7371                mouse = wx.GetMousePosition()
7372
7373                # set initial float position - may have to think about this
7374                # offset a bit more later ...
7375                self._action_offset = wx.Point(20, 10)
7376                self._toolbar_action_offset = wx.Point(20, 10)
7377
7378                paneInfo.floating_pos = mouse - self._action_offset
7379                paneInfo.dock_pos = AUI_DOCK_NONE
7380                paneInfo.notebook_id = -1
7381
7382                tab = event.GetEventObject()
7383
7384                if tab.HasCapture():
7385                    tab.ReleaseMouse()
7386
7387                # float the window
7388                if paneInfo.IsMaximized():
7389                    self.RestorePane(paneInfo)
7390                paneInfo.Float()
7391
7392                # The call to Update may result in
7393                # the notebook that generated this
7394                # event being deleted, so we have
7395                # to do the call asynchronously.
7396                wx.CallAfter(self.Update)
7397
7398                self._action_window = paneInfo.window
7399
7400                self._frame.CaptureMouse()
7401                event.SetDispatched(True)
7402
7403            else:
7404
7405                # not our window
7406                event.Skip()
7407
7408    def OnTabEndDrag(self, event):
7409        """
7410        Handles the ``EVT_AUINOTEBOOK_END_DRAG`` event.
7411
7412        :param `event`: a :class:`~wx.lib.agw.aui.auibook.AuiNotebookEvent` event to be processed.
7413        """
7414
7415        if self._masterManager:
7416            self._masterManager.OnTabEndDrag(event)
7417        else:
7418            self.Update()
7419            event.Skip()
7420
7421    def OnTabPageClose(self, event):
7422        """
7423        Handles the ``EVT_AUINOTEBOOK_PAGE_CLOSE`` event.
7424
7425        :param `event`: a :class:`~wx.lib.agw.aui.auibook.AuiNotebookEvent` event to be processed.
7426        """
7427
7428        if self._masterManager:
7429            self._masterManager.OnTabPageClose(event)
7430
7431        else:
7432
7433            p = self.PaneFromTabEvent(event)
7434            if p.IsOk():
7435
7436                # veto it because we will call "RemovePage" ourselves
7437                event.Veto()
7438
7439                # Now ask the app if they really want to close...
7440                # fire pane close event
7441                e = AuiManagerEvent(wxEVT_AUI_PANE_CLOSE)
7442                e.SetPane(p)
7443                e.SetCanVeto(True)
7444                self.ProcessMgrEvent(e)
7445
7446                if e.GetVeto():
7447                    return
7448
7449                # Close/update asynchronously, because
7450                # the notebook which generated the event
7451                # (and triggered this method call) will
7452                # be deleted.
7453                def close():
7454                    self.ClosePane(p)
7455                    self.Update()
7456
7457                wx.CallAfter(close)
7458            else:
7459                event.Skip()
7460
7461
7462    def OnTabSelected(self, event):
7463        """
7464        Handles the ``EVT_AUINOTEBOOK_PAGE_CHANGED`` event.
7465
7466        :param `event`: a :class:`~wx.lib.agw.aui.auibook.AuiNotebookEvent` event to be processed.
7467        """
7468
7469        if self._masterManager:
7470            self._masterManager.OnTabSelected(event)
7471            return
7472
7473        obj = event.GetEventObject()
7474
7475        if obj and isinstance(obj, auibook.AuiNotebook):
7476
7477            notebook = obj
7478            page = notebook.GetPage(event.GetSelection())
7479            paneInfo = self.GetPane(page)
7480
7481            if paneInfo.IsOk():
7482                notebookRoot = GetNotebookRoot(self._panes, paneInfo.notebook_id)
7483                if notebookRoot:
7484
7485                    notebookRoot.Caption(paneInfo.caption)
7486                    self.RefreshCaptions()
7487
7488        event.Skip()
7489
7490
7491    def GetNotebooks(self):
7492        """ Returns all the automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook` in the :class:`AuiManager`. """
7493
7494        if self._masterManager:
7495            return self._masterManager.GetNotebooks()
7496
7497        return self._notebooks
7498
7499
7500    def SetMasterManager(self, manager):
7501        """
7502        Sets the master manager for an automatic :class:`~wx.lib.agw.aui.auibook.AuiNotebook`.
7503
7504        :param `manager`: an instance of :class:`AuiManager`.
7505        """
7506
7507        self._masterManager = manager
7508
7509
7510    def ProcessDockResult(self, target, new_pos):
7511        """
7512        This is a utility function used by :meth:`DoDrop` - it checks
7513        if a dock operation is allowed, the new dock position is copied into
7514        the target info. If the operation was allowed, the function returns ``True``.
7515
7516        :param `target`: the :class:`AuiPaneInfo` instance to be docked;
7517        :param integer `new_pos`: the new docking position if the docking operation is allowed.
7518        """
7519
7520        allowed = False
7521        direction = new_pos.dock_direction
7522
7523        if direction == AUI_DOCK_TOP:
7524            allowed = target.IsTopDockable()
7525        elif direction == AUI_DOCK_BOTTOM:
7526            allowed = target.IsBottomDockable()
7527        elif direction == AUI_DOCK_LEFT:
7528            allowed = target.IsLeftDockable()
7529        elif direction == AUI_DOCK_RIGHT:
7530            allowed = target.IsRightDockable()
7531
7532        if allowed:
7533            target = new_pos
7534
7535            if target.IsToolbar():
7536                self.SwitchToolBarOrientation(target)
7537
7538        return allowed, target
7539
7540
7541    def SwitchToolBarOrientation(self, pane):
7542        """
7543        Switches the toolbar orientation from vertical to horizontal and vice-versa.
7544        This is especially useful for vertical docked toolbars once they float.
7545
7546        :param `pane`: an instance of :class:`AuiPaneInfo`, which may have a :class:`~wx.lib.agw.aui.auibar.AuiToolBar`
7547         window associated with it.
7548        """
7549
7550        if not isinstance(pane.window, auibar.AuiToolBar):
7551            return pane
7552
7553        if pane.IsFloating():
7554            return pane
7555
7556        toolBar = pane.window
7557        direction = pane.dock_direction
7558        vertical = direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT]
7559
7560        agwStyle = toolBar.GetAGWWindowStyleFlag()
7561        new_agwStyle = agwStyle
7562
7563        if vertical:
7564            new_agwStyle |= AUI_TB_VERTICAL
7565        else:
7566            new_agwStyle &= ~(AUI_TB_VERTICAL)
7567
7568        if agwStyle != new_agwStyle:
7569            toolBar.SetAGWWindowStyleFlag(new_agwStyle)
7570        if not toolBar.GetGripperVisible():
7571            toolBar.SetGripperVisible(True)
7572
7573        s = pane.window.GetMinSize()
7574        pane.BestSize(s)
7575
7576        if new_agwStyle != agwStyle:
7577            toolBar.Realize()
7578
7579        return pane
7580
7581
7582    def DoDrop(self, docks, panes, target, pt, offset=wx.Point(0, 0)):
7583        """
7584        This is an important function. It basically takes a mouse position,
7585        and determines where the panes new position would be. If the pane is to be
7586        dropped, it performs the drop operation using the specified dock and pane
7587        arrays. By specifying copy dock and pane arrays when calling, a "what-if"
7588        scenario can be performed, giving precise coordinates for drop hints.
7589
7590        :param `docks`: a list of :class:`AuiDockInfo` classes;
7591        :param `panes`: a list of :class:`AuiPaneInfo` instances;
7592        :param wx.Point `pt`: a mouse position to check for a drop operation;
7593        :param wx.Point `offset`: a possible offset from the input point `pt`.
7594        """
7595
7596        if target.IsToolbar():
7597            return self.DoDropToolbar(docks, panes, target, pt, offset)
7598        elif target.IsFloating():
7599            return self.DoDropFloatingPane(docks, panes, target, pt)
7600        else:
7601            return self.DoDropNonFloatingPane(docks, panes, target, pt)
7602
7603
7604    def CopyTarget(self, target):
7605        """
7606        Copies all the attributes of the input `target` into another :class:`AuiPaneInfo`.
7607
7608        :param `target`: the source :class:`AuiPaneInfo` from where to copy attributes.
7609        """
7610
7611        drop = AuiPaneInfo()
7612        drop.name = target.name
7613        drop.caption = target.caption
7614        drop.window = target.window
7615        drop.frame = target.frame
7616        drop.state = target.state
7617        drop.dock_direction = target.dock_direction
7618        drop.dock_layer = target.dock_layer
7619        drop.dock_row = target.dock_row
7620        drop.dock_pos = target.dock_pos
7621        drop.best_size = wx.Size(*target.best_size)
7622        drop.min_size = wx.Size(*target.min_size)
7623        drop.max_size = wx.Size(*target.max_size)
7624        drop.floating_pos = wx.Point(*target.floating_pos)
7625        drop.floating_size = wx.Size(*target.floating_size)
7626        drop.dock_proportion = target.dock_proportion
7627        drop.buttons = target.buttons
7628        drop.rect = wx.Rect(*target.rect)
7629        drop.icon = target.icon
7630        drop.notebook_id = target.notebook_id
7631        drop.transparent = target.transparent
7632        drop.snapped = target.snapped
7633        drop.minimize_mode = target.minimize_mode
7634        drop.minimize_target = target.minimize_target
7635
7636        return drop
7637
7638
7639    def DoDropToolbar(self, docks, panes, target, pt, offset):
7640        """
7641        Handles the situation in which the dropped pane contains a toolbar.
7642
7643        :param `docks`: a list of :class:`AuiDockInfo` classes;
7644        :param `panes`: a list of :class:`AuiPaneInfo` instances;
7645        :param `target`: the target pane containing the toolbar, an instance of :class:`AuiPaneInfo`;
7646        :param wx.Point `pt`: a mouse position to check for a drop operation;
7647        :param wx.Point `offset`: a possible offset from the input point `pt`.
7648        """
7649
7650        drop = self.CopyTarget(target)
7651
7652        # The result should always be shown
7653        drop.Show()
7654
7655        # Check to see if the toolbar has been dragged out of the window
7656        if CheckOutOfWindow(self._frame, pt):
7657            if self._agwFlags & AUI_MGR_ALLOW_FLOATING and drop.IsFloatable():
7658                drop.Float()
7659
7660            return self.ProcessDockResult(target, drop)
7661
7662        # Allow directional change when the cursor leaves this rect
7663        safeRect = wx.Rect(*target.rect)
7664        if target.IsHorizontal():
7665            safeRect.Inflate(100, 50)
7666        else:
7667            safeRect.Inflate(50, 100)
7668
7669        # Check to see if the toolbar has been dragged to edge of the frame
7670        dropDir = CheckEdgeDrop(self._frame, docks, pt)
7671
7672        if dropDir != -1:
7673
7674            if dropDir == wx.LEFT:
7675                drop.Dock().Left().Layer(auiToolBarLayer).Row(0). \
7676                    Position(pt.y - self.GetDockPixelOffset(drop) - offset.y)
7677
7678            elif dropDir == wx.RIGHT:
7679                drop.Dock().Right().Layer(auiToolBarLayer).Row(0). \
7680                    Position(pt.y - self.GetDockPixelOffset(drop) - offset.y)
7681
7682            elif dropDir == wx.TOP:
7683                drop.Dock().Top().Layer(auiToolBarLayer).Row(0). \
7684                    Position(pt.x - self.GetDockPixelOffset(drop) - offset.x)
7685
7686            elif dropDir == wx.BOTTOM:
7687                drop.Dock().Bottom().Layer(auiToolBarLayer).Row(0). \
7688                    Position(pt.x - self.GetDockPixelOffset(drop) - offset.x)
7689
7690            if not target.IsFloating() and safeRect.Contains(pt) and \
7691               target.dock_direction != drop.dock_direction:
7692                return False, target
7693
7694            return self.ProcessDockResult(target, drop)
7695
7696        # If the windows is floating and out of the client area, do nothing
7697        if drop.IsFloating() and not self._frame.GetClientRect().Contains(pt):
7698            return False, target
7699
7700        # Ok, can't drop on edge - check internals ...
7701
7702        clientSize = self._frame.GetClientSize()
7703        x = Clip(pt.x, 0, clientSize.x - 1)
7704        y = Clip(pt.y, 0, clientSize.y - 1)
7705        part = self.HitTest(x, y)
7706
7707        if not part or not part.dock:
7708            return False, target
7709
7710        dock = part.dock
7711
7712        # toolbars may only be moved in and to fixed-pane docks,
7713        # otherwise we will try to float the pane.  Also, the pane
7714        # should float if being dragged over center pane windows
7715        if not dock.fixed or dock.dock_direction == AUI_DOCK_CENTER:
7716
7717            if (self._agwFlags & AUI_MGR_ALLOW_FLOATING and drop.IsFloatable()) or \
7718               dock.dock_direction not in [AUI_DOCK_CENTER, AUI_DOCK_NONE]:
7719                if drop.IsFloatable():
7720                    drop.Float()
7721
7722            return self.ProcessDockResult(target, drop)
7723
7724        # calculate the offset from where the dock begins
7725        # to the point where the user dropped the pane
7726        dockDropOffset = 0
7727        if dock.IsHorizontal():
7728            dockDropOffset = pt.x - dock.rect.x - offset.x
7729        else:
7730            dockDropOffset = pt.y - dock.rect.y - offset.y
7731
7732        drop.Dock().Direction(dock.dock_direction).Layer(dock.dock_layer). \
7733            Row(dock.dock_row).Position(dockDropOffset)
7734
7735        if (pt.y <= dock.rect.GetTop() + 2 and dock.IsHorizontal()) or \
7736           (pt.x <= dock.rect.GetLeft() + 2 and dock.IsVertical()):
7737
7738            if dock.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_LEFT]:
7739                row = drop.dock_row
7740                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row)
7741                drop.dock_row = row
7742
7743            else:
7744                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row+1)
7745                drop.dock_row = dock.dock_row + 1
7746
7747        if (pt.y >= dock.rect.GetBottom() - 2 and dock.IsHorizontal()) or \
7748           (pt.x >= dock.rect.GetRight() - 2 and dock.IsVertical()):
7749
7750            if dock.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_LEFT]:
7751                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row+1)
7752                drop.dock_row = dock.dock_row+1
7753
7754            else:
7755                row = drop.dock_row
7756                panes = DoInsertDockRow(panes, dock.dock_direction, dock.dock_layer, dock.dock_row)
7757                drop.dock_row = row
7758
7759        if not target.IsFloating() and safeRect.Contains(pt) and \
7760           target.dock_direction != drop.dock_direction:
7761            return False, target
7762
7763        return self.ProcessDockResult(target, drop)
7764
7765
7766    def DoDropFloatingPane(self, docks, panes, target, pt):
7767        """
7768        Handles the situation in which the dropped pane contains a normal window.
7769
7770        :param `docks`: a list of :class:`AuiDockInfo` classes;
7771        :param `panes`: a list of :class:`AuiPaneInfo` instances;
7772        :param `target`: the target pane containing the window, an instance of
7773         :class:`AuiPaneInfo`;
7774        :param wx.Point `pt`: a mouse position to check for a drop operation.
7775        """
7776
7777        screenPt = self._frame.ClientToScreen(pt)
7778        paneInfo = self.PaneHitTest(panes, pt)
7779
7780        if paneInfo.IsMaximized():
7781            return False, target
7782
7783        if paneInfo.window is None:
7784            return False, target
7785
7786        # search the dock guides.
7787        # reverse order to handle the center first.
7788        for i in range(len(self._guides)-1, -1, -1):
7789            guide = self._guides[i]
7790
7791            # do hit testing on the guide
7792            dir = guide.host.HitTest(screenPt.x, screenPt.y)
7793
7794            if dir == -1:  # point was outside of the dock guide
7795                continue
7796
7797            if dir == wx.ALL:   # target is a single dock guide
7798                return self.DoDropLayer(docks, target, guide.dock_direction)
7799
7800            elif dir == wx.CENTER:
7801
7802                if not target.IsNotebookDockable():
7803                    continue
7804                if not paneInfo.IsNotebookDockable() and not paneInfo.IsNotebookControl():
7805                    continue
7806
7807                if not paneInfo.HasNotebook():
7808
7809                    # Add a new notebook pane with the original as a tab...
7810                    self.CreateNotebookBase(panes, paneInfo)
7811
7812                # Add new item to notebook
7813                target.NotebookPage(paneInfo.notebook_id)
7814
7815            else:
7816
7817                drop_pane = False
7818                drop_row = False
7819
7820                insert_dir = paneInfo.dock_direction
7821                insert_layer = paneInfo.dock_layer
7822                insert_row = paneInfo.dock_row
7823                insert_pos = paneInfo.dock_pos
7824
7825                if insert_dir == AUI_DOCK_CENTER:
7826
7827                    insert_layer = 0
7828                    if dir == wx.LEFT:
7829                        insert_dir = AUI_DOCK_LEFT
7830                    elif dir == wx.UP:
7831                        insert_dir = AUI_DOCK_TOP
7832                    elif dir == wx.RIGHT:
7833                        insert_dir = AUI_DOCK_RIGHT
7834                    elif dir == wx.DOWN:
7835                        insert_dir = AUI_DOCK_BOTTOM
7836
7837                if insert_dir == AUI_DOCK_LEFT:
7838
7839                    drop_pane = (dir == wx.UP   or dir == wx.DOWN)
7840                    drop_row  = (dir == wx.LEFT or dir == wx.RIGHT)
7841                    if dir == wx.RIGHT:
7842                        insert_row += 1
7843                    elif dir == wx.DOWN:
7844                        insert_pos += 1
7845
7846                elif insert_dir == AUI_DOCK_RIGHT:
7847
7848                    drop_pane = (dir == wx.UP   or dir == wx.DOWN)
7849                    drop_row  = (dir == wx.LEFT or dir == wx.RIGHT)
7850                    if dir == wx.LEFT:
7851                        insert_row += 1
7852                    elif dir == wx.DOWN:
7853                        insert_pos += 1
7854
7855                elif insert_dir == AUI_DOCK_TOP:
7856
7857                    drop_pane = (dir == wx.LEFT or dir == wx.RIGHT)
7858                    drop_row  = (dir == wx.UP   or dir == wx.DOWN)
7859                    if dir == wx.DOWN:
7860                        insert_row += 1
7861                    elif dir == wx.RIGHT:
7862                        insert_pos += 1
7863
7864                elif insert_dir == AUI_DOCK_BOTTOM:
7865
7866                    drop_pane = (dir == wx.LEFT or dir == wx.RIGHT)
7867                    drop_row  = (dir == wx.UP   or dir == wx.DOWN)
7868                    if dir == wx.UP:
7869                        insert_row += 1
7870                    elif dir == wx.RIGHT:
7871                        insert_pos += 1
7872
7873                if paneInfo.dock_direction == AUI_DOCK_CENTER:
7874                    insert_row = GetMaxRow(panes, insert_dir, insert_layer) + 1
7875
7876                if drop_pane:
7877                    return self.DoDropPane(panes, target, insert_dir, insert_layer, insert_row, insert_pos)
7878
7879                if drop_row:
7880                    return self.DoDropRow(panes, target, insert_dir, insert_layer, insert_row)
7881
7882            return True, target
7883
7884        return False, target
7885
7886
7887    def DoDropNonFloatingPane(self, docks, panes, target, pt):
7888        """
7889        Handles the situation in which the dropped pane is not floating.
7890
7891        :param `docks`: a list of :class:`AuiDockInfo` classes;
7892        :param `panes`: a list of :class:`AuiPaneInfo` instances;
7893        :param `target`: the target pane containing the toolbar, an instance of :class:`AuiPaneInfo`;
7894        :param wx.Point `pt`: a mouse position to check for a drop operation.
7895        """
7896
7897        screenPt = self._frame.ClientToScreen(pt)
7898        clientSize = self._frame.GetClientSize()
7899        frameRect = GetInternalFrameRect(self._frame, self._docks)
7900
7901        drop = self.CopyTarget(target)
7902
7903        # The result should always be shown
7904        drop.Show()
7905
7906        part = self.HitTest(pt.x, pt.y)
7907
7908        if not part:
7909            return False, target
7910
7911        if part.type == AuiDockUIPart.typeDockSizer:
7912
7913            if len(part.dock.panes) != 1:
7914                return False, target
7915
7916            part = self.GetPanePart(part.dock.panes[0].window)
7917            if not part:
7918                return False, target
7919
7920        if not part.pane:
7921            return False, target
7922
7923        part = self.GetPanePart(part.pane.window)
7924        if not part:
7925            return False, target
7926
7927        insert_dock_row = False
7928        insert_row = part.pane.dock_row
7929        insert_dir = part.pane.dock_direction
7930        insert_layer = part.pane.dock_layer
7931
7932        direction = part.pane.dock_direction
7933
7934        if direction == AUI_DOCK_TOP:
7935            if pt.y >= part.rect.y and pt.y < part.rect.y+auiInsertRowPixels:
7936                insert_dock_row = True
7937
7938        elif direction == AUI_DOCK_BOTTOM:
7939            if pt.y > part.rect.y+part.rect.height-auiInsertRowPixels and \
7940               pt.y <= part.rect.y + part.rect.height:
7941                insert_dock_row = True
7942
7943        elif direction == AUI_DOCK_LEFT:
7944            if pt.x >= part.rect.x and pt.x < part.rect.x+auiInsertRowPixels:
7945                insert_dock_row = True
7946
7947        elif direction == AUI_DOCK_RIGHT:
7948            if pt.x > part.rect.x+part.rect.width-auiInsertRowPixels and \
7949               pt.x <= part.rect.x+part.rect.width:
7950                insert_dock_row = True
7951
7952        elif direction == AUI_DOCK_CENTER:
7953
7954                # "new row pixels" will be set to the default, but
7955                # must never exceed 20% of the window size
7956                new_row_pixels_x = auiNewRowPixels
7957                new_row_pixels_y = auiNewRowPixels
7958
7959                if new_row_pixels_x > (part.rect.width*20)/100:
7960                    new_row_pixels_x = (part.rect.width*20)/100
7961
7962                if new_row_pixels_y > (part.rect.height*20)/100:
7963                    new_row_pixels_y = (part.rect.height*20)/100
7964
7965                # determine if the mouse pointer is in a location that
7966                # will cause a new row to be inserted.  The hot spot positions
7967                # are along the borders of the center pane
7968
7969                insert_layer = 0
7970                insert_dock_row = True
7971                pr = part.rect
7972
7973                if pt.x >= pr.x and pt.x < pr.x + new_row_pixels_x:
7974                    insert_dir = AUI_DOCK_LEFT
7975                elif pt.y >= pr.y and pt.y < pr.y + new_row_pixels_y:
7976                    insert_dir = AUI_DOCK_TOP
7977                elif pt.x >= pr.x + pr.width - new_row_pixels_x and pt.x < pr.x + pr.width:
7978                    insert_dir = AUI_DOCK_RIGHT
7979                elif pt.y >= pr.y+ pr.height - new_row_pixels_y and pt.y < pr.y + pr.height:
7980                    insert_dir = AUI_DOCK_BOTTOM
7981                else:
7982                    return False, target
7983
7984                insert_row = GetMaxRow(panes, insert_dir, insert_layer) + 1
7985
7986        if insert_dock_row:
7987
7988            panes = DoInsertDockRow(panes, insert_dir, insert_layer, insert_row)
7989            drop.Dock().Direction(insert_dir).Layer(insert_layer). \
7990                Row(insert_row).Position(0)
7991
7992            return self.ProcessDockResult(target, drop)
7993
7994        # determine the mouse offset and the pane size, both in the
7995        # direction of the dock itself, and perpendicular to the dock
7996
7997        if part.orientation == wx.VERTICAL:
7998
7999            offset = pt.y - part.rect.y
8000            size = part.rect.GetHeight()
8001
8002        else:
8003
8004            offset = pt.x - part.rect.x
8005            size = part.rect.GetWidth()
8006
8007        drop_position = part.pane.dock_pos
8008
8009        # if we are in the top/left part of the pane,
8010        # insert the pane before the pane being hovered over
8011        if offset <= size/2:
8012
8013            drop_position = part.pane.dock_pos
8014            panes = DoInsertPane(panes,
8015                                 part.pane.dock_direction,
8016                                 part.pane.dock_layer,
8017                                 part.pane.dock_row,
8018                                 part.pane.dock_pos)
8019
8020        # if we are in the bottom/right part of the pane,
8021        # insert the pane before the pane being hovered over
8022        if offset > size/2:
8023
8024            drop_position = part.pane.dock_pos+1
8025            panes = DoInsertPane(panes,
8026                                 part.pane.dock_direction,
8027                                 part.pane.dock_layer,
8028                                 part.pane.dock_row,
8029                                 part.pane.dock_pos+1)
8030
8031
8032        drop.Dock(). \
8033                     Direction(part.dock.dock_direction). \
8034                     Layer(part.dock.dock_layer).Row(part.dock.dock_row). \
8035                     Position(drop_position)
8036
8037        return self.ProcessDockResult(target, drop)
8038
8039
8040    def DoDropLayer(self, docks, target, dock_direction):
8041        """
8042        Handles the situation in which `target` is a single dock guide.
8043
8044        :param `docks`: a list of :class:`AuiDockInfo` classes;
8045        :param `target`: the target pane, an instance of :class:`AuiPaneInfo`;
8046        :param integer `dock_direction`: the docking direction.
8047        """
8048
8049        drop = self.CopyTarget(target)
8050
8051        if dock_direction == AUI_DOCK_LEFT:
8052            drop.Dock().Left()
8053            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_LEFT),
8054                                     GetMaxLayer(docks, AUI_DOCK_BOTTOM)),
8055                                 GetMaxLayer(docks, AUI_DOCK_TOP)) + 1
8056
8057        elif dock_direction == AUI_DOCK_TOP:
8058            drop.Dock().Top()
8059            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_TOP),
8060                                     GetMaxLayer(docks, AUI_DOCK_LEFT)),
8061                                 GetMaxLayer(docks, AUI_DOCK_RIGHT)) + 1
8062
8063        elif dock_direction == AUI_DOCK_RIGHT:
8064            drop.Dock().Right()
8065            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_RIGHT),
8066                                     GetMaxLayer(docks, AUI_DOCK_TOP)),
8067                                 GetMaxLayer(docks, AUI_DOCK_BOTTOM)) + 1
8068
8069        elif dock_direction == AUI_DOCK_BOTTOM:
8070            drop.Dock().Bottom()
8071            drop_new_layer = max(max(GetMaxLayer(docks, AUI_DOCK_BOTTOM),
8072                                     GetMaxLayer(docks, AUI_DOCK_LEFT)),
8073                                 GetMaxLayer(docks, AUI_DOCK_RIGHT)) + 1
8074
8075        else:
8076            return False, target
8077
8078
8079        drop.Dock().Layer(drop_new_layer)
8080        return self.ProcessDockResult(target, drop)
8081
8082
8083    def DoDropPane(self, panes, target, dock_direction, dock_layer, dock_row, dock_pos):
8084        """
8085        Drop a pane in the interface.
8086
8087        :param `panes`: a list of :class:`AuiPaneInfo` classes;
8088        :param `target`: the target pane, an instance of :class:`AuiPaneInfo`;
8089        :param integer `dock_direction`: the docking direction;
8090        :param integer `dock_layer`: the docking layer;
8091        :param integer `dock_row`: the docking row;
8092        :param integer `dock_pos`: the docking position.
8093        """
8094
8095        drop = self.CopyTarget(target)
8096        panes = DoInsertPane(panes, dock_direction, dock_layer, dock_row, dock_pos)
8097
8098        drop.Dock().Direction(dock_direction).Layer(dock_layer).Row(dock_row).Position(dock_pos)
8099        return self.ProcessDockResult(target, drop)
8100
8101
8102    def DoDropRow(self, panes, target, dock_direction, dock_layer, dock_row):
8103        """
8104        Insert a row in the interface before dropping.
8105
8106        :param `panes`: a list of :class:`AuiPaneInfo` classes;
8107        :param `target`: the target pane, an instance of :class:`AuiPaneInfo`;
8108        :param integer `dock_direction`: the docking direction;
8109        :param integer `dock_layer`: the docking layer;
8110        :param integer `dock_row`: the docking row.
8111        """
8112
8113        drop = self.CopyTarget(target)
8114        panes = DoInsertDockRow(panes, dock_direction, dock_layer, dock_row)
8115
8116        drop.Dock().Direction(dock_direction).Layer(dock_layer).Row(dock_row).Position(0)
8117        return self.ProcessDockResult(target, drop)
8118
8119
8120    def ShowHint(self, rect):
8121        """
8122        Shows the AUI hint window.
8123
8124        :param wx.Rect `rect`: the hint rect calculated in advance.
8125        """
8126
8127        if rect == self._last_hint:
8128            return
8129
8130        if self._agwFlags & AUI_MGR_RECTANGLE_HINT and wx.Platform != "__WXMAC__":
8131
8132            if self._last_hint != rect:
8133                # remove the last hint rectangle
8134                self._last_hint = wx.Rect(*rect)
8135                self._frame.Refresh()
8136                self._frame.Update()
8137
8138            screendc = wx.ScreenDC()
8139            clip = wx.Region(1, 1, 10000, 10000)
8140
8141            # clip all floating windows, so we don't draw over them
8142            for pane in self._panes:
8143                if pane.IsFloating() and pane.frame.IsShown():
8144
8145                    rect2 = wx.Rect(*pane.frame.GetRect())
8146                    if wx.Platform == "__WXGTK__":
8147                        # wxGTK returns the client size, not the whole frame size
8148                        rect2.width += 15
8149                        rect2.height += 35
8150                        rect2.Inflate(5, 5)
8151
8152                    clip.Subtract(rect2)
8153
8154            # As we can only hide the hint by redrawing the managed window, we
8155            # need to clip the region to the managed window too or we get
8156            # nasty redrawn problems.
8157            clip.Intersect(self._frame.GetRect())
8158            screendc.SetDeviceClippingRegion(clip)
8159
8160            stipple = PaneCreateStippleBitmap()
8161            brush = wx.Brush(stipple)
8162            screendc.SetBrush(brush)
8163            screendc.SetPen(wx.TRANSPARENT_PEN)
8164            screendc.DrawRectangle(rect.x, rect.y, 5, rect.height)
8165            screendc.DrawRectangle(rect.x+5, rect.y, rect.width-10, 5)
8166            screendc.DrawRectangle(rect.x+rect.width-5, rect.y, 5, rect.height)
8167            screendc.DrawRectangle(rect.x+5, rect.y+rect.height-5, rect.width-10, 5)
8168            RefreshDockingGuides(self._guides)
8169
8170            return
8171
8172        if not self._hint_window:
8173            self.CreateHintWindow()
8174
8175        if self._hint_window:
8176            self._hint_window.SetRect(rect)
8177            self._hint_window.Show()
8178
8179        self._hint_fadeamt = self._hint_fademax
8180
8181        if self._agwFlags & AUI_MGR_HINT_FADE:
8182            self._hint_fadeamt = 0
8183            self._hint_window.SetTransparent(self._hint_fadeamt)
8184
8185        if self._action == actionDragFloatingPane and self._action_window:
8186            self._action_window.SetFocus()
8187
8188        if self._hint_fadeamt != self._hint_fademax: #  Only fade if we need to
8189            # start fade in timer
8190            self._hint_fadetimer.Start(5)
8191
8192        self._last_hint = wx.Rect(*rect)
8193
8194
8195    def HideHint(self):
8196        """ Hides a transparent window hint if there is one. """
8197
8198        # hides a transparent window hint if there is one
8199        if self._hint_window:
8200            self._hint_window.Hide()
8201
8202        self._hint_fadetimer.Stop()
8203        self._last_hint = wx.Rect()
8204
8205
8206    def IsPaneButtonVisible(self, part):
8207        """
8208        Returns whether a pane button in the pane caption is visible.
8209
8210        :param `part`: the UI part to analyze, an instance of :class:`AuiDockUIPart`.
8211        """
8212
8213        captionRect = wx.Rect()
8214
8215        for temp_part in self._uiparts:
8216            if temp_part.pane == part.pane and \
8217               temp_part.type == AuiDockUIPart.typeCaption:
8218                captionRect = temp_part.rect
8219                break
8220
8221        return captionRect.Contains(part.rect)
8222
8223
8224    def DrawPaneButton(self, dc, part, pt):
8225        """
8226        Draws a pane button in the caption (convenience function).
8227
8228        :param `dc`: a :class:`wx.DC` device context object;
8229        :param `part`: the UI part to analyze, an instance of :class:`AuiDockUIPart`;
8230        :param wx.Point `pt`: the mouse location.
8231        """
8232
8233        if not self.IsPaneButtonVisible(part):
8234            return
8235
8236        state = AUI_BUTTON_STATE_NORMAL
8237
8238        if part.rect.Contains(pt):
8239
8240            if wx.GetMouseState().LeftIsDown():
8241                state = AUI_BUTTON_STATE_PRESSED
8242            else:
8243                state = AUI_BUTTON_STATE_HOVER
8244
8245        self._art.DrawPaneButton(dc, self._frame, part.button.button_id,
8246                                 state, part.rect, part.pane)
8247
8248
8249    def RefreshButton(self, part):
8250        """
8251        Refreshes a pane button in the caption.
8252
8253        :param `part`: the UI part to analyze, an instance of :class:`AuiDockUIPart`.
8254        """
8255
8256        rect = wx.Rect(*part.rect)
8257        rect.Inflate(2, 2)
8258        self._frame.Refresh(True, rect)
8259        self._frame.Update()
8260
8261
8262    def RefreshCaptions(self):
8263        """ Refreshes all pane captions. """
8264
8265        for part in self._uiparts:
8266            if part.type == AuiDockUIPart.typeCaption:
8267                self._frame.Refresh(True, part.rect)
8268                self._frame.Update()
8269
8270
8271    def CalculateHintRect(self, pane_window, pt, offset):
8272        """
8273        Calculates the drop hint rectangle.
8274
8275        The method first calls :meth:`DoDrop` to determine the exact position the pane would
8276        be at were if dropped. If the pane would indeed become docked at the
8277        specified drop point, the the rectangle hint will be returned in
8278        screen coordinates. Otherwise, an empty rectangle is returned.
8279
8280        :param wx.Window `pane_window`: it is the window pointer of the pane being dragged;
8281        :param wx.Point `pt`: is the mouse position, in client coordinates;
8282        :param wx.Point `offset`: describes the offset that the mouse is from the upper-left
8283         corner of the item being dragged.
8284        """
8285
8286        # we need to paint a hint rectangle to find out the exact hint rectangle,
8287        # we will create a new temporary layout and then measure the resulting
8288        # rectangle we will create a copy of the docking structures (self._docks)
8289        # so that we don't modify the real thing on screen
8290
8291        rect = wx.Rect()
8292        pane = self.GetPane(pane_window)
8293
8294        attrs = self.GetAttributes(pane)
8295        hint = AuiPaneInfo()
8296        hint = self.SetAttributes(hint, attrs)
8297
8298        if hint.name != "__HINT__":
8299            self._oldname = hint.name
8300
8301        hint.name = "__HINT__"
8302        hint.PaneBorder(True)
8303        hint.Show()
8304
8305        if not hint.IsOk():
8306            hint.name = self._oldname
8307            return rect
8308
8309        docks, panes = CopyDocksAndPanes2(self._docks, self._panes)
8310
8311        # remove any pane already there which bears the same window
8312        # this happens when you are moving a pane around in a dock
8313        for ii in range(len(panes)):
8314            if panes[ii].window == pane_window:
8315                docks = RemovePaneFromDocks(docks, panes[ii])
8316                panes.pop(ii)
8317                break
8318
8319        # find out where the new pane would be
8320        allow, hint = self.DoDrop(docks, panes, hint, pt, offset)
8321
8322        if not allow:
8323            return rect
8324
8325        panes.append(hint)
8326
8327        sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False)
8328
8329        client_size = self._frame.GetClientSize()
8330        sizer.SetDimension(0, 0, client_size.x, client_size.y)
8331        sizer.Layout()
8332
8333        sought = "__HINT__"
8334
8335        # For a notebook page, actually look for the notebook itself.
8336        if hint.IsNotebookPage():
8337            id = hint.notebook_id
8338            for pane in panes:
8339                if pane.IsNotebookControl() and pane.notebook_id==id:
8340                    sought = pane.name
8341                    break
8342
8343        for part in uiparts:
8344            if part.pane and part.pane.name == sought:
8345                rect.Union(wx.Rect(part.sizer_item.GetPosition(),
8346                                     part.sizer_item.GetSize()))
8347
8348        sizer.Destroy()
8349
8350        # check for floating frame ...
8351        if rect.IsEmpty():
8352            for p in panes:
8353                if p.name == sought and p.IsFloating():
8354                    return wx.Rect(p.floating_pos, p.floating_size)
8355
8356        if rect.IsEmpty():
8357            return rect
8358
8359        # actually show the hint rectangle on the screen
8360        rect.x, rect.y = self._frame.ClientToScreen((rect.x, rect.y))
8361        if self._frame.GetLayoutDirection() == wx.Layout_RightToLeft:
8362            # Mirror rectangle in RTL mode
8363            rect.x -= rect.GetWidth()
8364
8365        return rect
8366
8367
8368    def DrawHintRect(self, pane_window, pt, offset):
8369        """
8370        Calculates the hint rectangle by calling :meth:`CalculateHintRect`. If there is a
8371        rectangle, it shows it by calling :meth:`ShowHint`, otherwise it hides any hint
8372        rectangle currently shown.
8373
8374        :param wx.Window `pane_window`: it is the window pointer of the pane being dragged;
8375        :param wx.Point `pt`: is the mouse position, in client coordinates;
8376        :param wx.Point `offset`: describes the offset that the mouse is from the upper-left
8377         corner of the item being dragged.
8378        """
8379
8380        rect = self.CalculateHintRect(pane_window, pt, offset)
8381
8382        if rect.IsEmpty():
8383            self.HideHint()
8384            self._hint_rect = wx.Rect()
8385        else:
8386            self.ShowHint(rect)
8387            self._hint_rect = wx.Rect(*rect)
8388
8389
8390    def GetPartSizerRect(self, uiparts):
8391        """
8392        Returns the rectangle surrounding the specified UI parts.
8393
8394        :param list `uiparts`: list of :class:`AuiDockUIPart` parts.
8395        """
8396
8397        rect = wx.Rect()
8398
8399        for part in self._uiparts:
8400            if part.pane and part.pane.name == "__HINT__":
8401                rect.Union(wx.Rect(part.sizer_item.GetPosition(),
8402                                     part.sizer_item.GetSize()))
8403
8404        return rect
8405
8406
8407    def GetAttributes(self, pane):
8408        """
8409        Returns all the attributes of a :class:`AuiPaneInfo`.
8410
8411        :param `pane`: a :class:`AuiPaneInfo` instance.
8412        """
8413
8414        attrs = []
8415        attrs.extend([pane.window, pane.frame, pane.state, pane.dock_direction,
8416                      pane.dock_layer, pane.dock_pos, pane.dock_row, pane.dock_proportion,
8417                      pane.floating_pos, pane.floating_size, pane.best_size,
8418                      pane.min_size, pane.max_size, pane.caption, pane.name,
8419                      pane.buttons, pane.rect, pane.icon, pane.notebook_id,
8420                      pane.transparent, pane.snapped, pane.minimize_mode, pane.minimize_target])
8421
8422        return attrs
8423
8424
8425    def SetAttributes(self, pane, attrs):
8426        """
8427        Sets all the attributes contained in `attrs` to a :class:`AuiPaneInfo`.
8428
8429        :param `pane`: a :class:`AuiPaneInfo` instance;
8430        :param list `attrs`: a list of attributes.
8431        """
8432
8433        pane.window = attrs[0]
8434        pane.frame = attrs[1]
8435        pane.state = attrs[2]
8436        pane.dock_direction = attrs[3]
8437        pane.dock_layer = attrs[4]
8438        pane.dock_pos = attrs[5]
8439        pane.dock_row = attrs[6]
8440        pane.dock_proportion = attrs[7]
8441        pane.floating_pos = attrs[8]
8442        pane.floating_size = attrs[9]
8443        pane.best_size = attrs[10]
8444        pane.min_size = attrs[11]
8445        pane.max_size = attrs[12]
8446        pane.caption = attrs[13]
8447        pane.name = attrs[14]
8448        pane.buttons = attrs[15]
8449        pane.rect = attrs[16]
8450        pane.icon = attrs[17]
8451        pane.notebook_id = attrs[18]
8452        pane.transparent = attrs[19]
8453        pane.snapped = attrs[20]
8454        pane.minimize_mode = attrs[21]
8455        pane.minimize_target = attrs[22]
8456
8457        return pane
8458
8459
8460    def OnFloatingPaneResized(self, wnd, size):
8461        """
8462        Handles the resizing of a floating pane.
8463
8464        :param wx.Window `wnd`: the window managed by the pane;
8465        :param wx.Size `size`: the new pane floating size.
8466        """
8467
8468        # try to find the pane
8469        pane = self.GetPane(wnd)
8470        if not pane.IsOk():
8471            raise Exception("Pane window not found")
8472
8473        if pane.frame:
8474            indx = self._panes.index(pane)
8475            pane.floating_pos = pane.frame.GetPosition()
8476            pane.floating_size = size
8477            self._panes[indx] = pane
8478            if pane.IsSnappable():
8479                self.SnapPane(pane, pane.floating_pos, pane.floating_size, True)
8480
8481
8482    def OnFloatingPaneClosed(self, wnd, event):
8483        """
8484        Handles the close event of a floating pane.
8485
8486        :param wx.Window `wnd`: the window managed by the pane;
8487        :param `event`: a :class:`CloseEvent` to be processed.
8488        """
8489
8490        # try to find the pane
8491        pane = self.GetPane(wnd)
8492        if not pane.IsOk():
8493            raise Exception("Pane window not found")
8494
8495        # fire pane close event
8496        e = AuiManagerEvent(wxEVT_AUI_PANE_CLOSE)
8497        e.SetPane(pane)
8498        e.SetCanVeto(event.CanVeto())
8499        self.ProcessMgrEvent(e)
8500
8501        if e.GetVeto():
8502            event.Veto()
8503            return
8504        else:
8505            # close the pane, but check that it
8506            # still exists in our pane array first
8507            # (the event handler above might have removed it)
8508
8509            check = self.GetPane(wnd)
8510            if check.IsOk():
8511                self.ClosePane(pane)
8512
8513
8514    def OnFloatingPaneActivated(self, wnd):
8515        """
8516        Handles the activation event of a floating pane.
8517
8518        :param wx.Window `wnd`: the window managed by the pane.
8519        """
8520
8521        pane = self.GetPane(wnd)
8522        if not pane.IsOk():
8523            raise Exception("Pane window not found")
8524
8525        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
8526            ret, self._panes = SetActivePane(self._panes, wnd)
8527            self.RefreshCaptions()
8528            self.FireEvent(wxEVT_AUI_PANE_ACTIVATED, wnd, canVeto=False)
8529
8530
8531    def OnFloatingPaneMoved(self, wnd, eventOrPt):
8532        """
8533        Handles the move event of a floating pane.
8534
8535        :param wx.Window `wnd`: the window managed by the pane;
8536        :param `eventOrPt`: a :class:`MoveEvent` to be processed or an instance of :class:`wx.Point`.
8537        """
8538
8539        pane = self.GetPane(wnd)
8540        if not pane.IsOk():
8541            raise Exception("Pane window not found")
8542
8543        if not pane.IsSnappable():
8544            return
8545
8546        if isinstance(eventOrPt, wx.Point):
8547            pane_pos = wx.Point(*eventOrPt)
8548        else:
8549            pane_pos = eventOrPt.GetPosition()
8550
8551        pane_size = pane.floating_size
8552
8553        self.SnapPane(pane, pane_pos, pane_size, False)
8554
8555
8556    def SnapPane(self, pane, pane_pos, pane_size, toSnap=False):
8557        """
8558        Snaps a floating pane to one of the main frame sides.
8559
8560        :param `pane`: a :class:`AuiPaneInfo` instance;
8561        :param wx.Point `pane_pos`: the new pane floating position;
8562        :param wx.Size `pane_size`: the new pane floating size;
8563        :param bool `toSnap`: a bool variable to check if :meth:`SnapPane` was called from
8564         a move event.
8565        """
8566
8567        if self._from_move:
8568            return
8569
8570        managed_window = self.GetManagedWindow()
8571        wnd_pos = managed_window.GetPosition()
8572        wnd_size = managed_window.GetSize()
8573        snapX, snapY = self._snap_limits
8574
8575        if not toSnap:
8576            pane.snapped = 0
8577            if pane.IsLeftSnappable():
8578                # Check if we can snap to the left
8579                diff = wnd_pos.x - (pane_pos.x + pane_size.x)
8580                if -snapX <= diff <= snapX:
8581                    pane.snapped = wx.LEFT
8582                    pane.floating_pos = wx.Point(wnd_pos.x-pane_size.x, pane_pos.y)
8583            elif pane.IsTopSnappable():
8584                # Check if we can snap to the top
8585                diff = wnd_pos.y - (pane_pos.y + pane_size.y)
8586                if -snapY <= diff <= snapY:
8587                    pane.snapped = wx.TOP
8588                    pane.floating_pos = wx.Point(pane_pos.x, wnd_pos.y-pane_size.y)
8589            elif pane.IsRightSnappable():
8590                # Check if we can snap to the right
8591                diff = pane_pos.x - (wnd_pos.x + wnd_size.x)
8592                if -snapX <= diff <= snapX:
8593                    pane.snapped = wx.RIGHT
8594                    pane.floating_pos = wx.Point(wnd_pos.x + wnd_size.x, pane_pos.y)
8595            elif pane.IsBottomSnappable():
8596                # Check if we can snap to the bottom
8597                diff = pane_pos.y - (wnd_pos.y + wnd_size.y)
8598                if -snapY <= diff <= snapY:
8599                    pane.snapped = wx.BOTTOM
8600                    pane.floating_pos = wx.Point(pane_pos.x, wnd_pos.y + wnd_size.y)
8601
8602        self.RepositionPane(pane, wnd_pos, wnd_size)
8603
8604
8605    def RepositionPane(self, pane, wnd_pos, wnd_size):
8606        """
8607        Repositions a pane after the main frame has been moved/resized.
8608
8609        :param `pane`: a :class:`AuiPaneInfo` instance;
8610        :param wx.Point `wnd_pos`: the main frame position;
8611        :param wx.Size `wnd_size`: the main frame size.
8612        """
8613
8614        pane_pos = pane.floating_pos
8615        pane_size = pane.floating_size
8616
8617        snap = pane.snapped
8618        if snap == wx.LEFT:
8619            floating_pos = wx.Point(wnd_pos.x - pane_size.x, pane_pos.y)
8620        elif snap == wx.TOP:
8621            floating_pos = wx.Point(pane_pos.x, wnd_pos.y - pane_size.y)
8622        elif snap == wx.RIGHT:
8623            floating_pos = wx.Point(wnd_pos.x + wnd_size.x, pane_pos.y)
8624        elif snap == wx.BOTTOM:
8625            floating_pos = wx.Point(pane_pos.x, wnd_pos.y + wnd_size.y)
8626
8627        if snap:
8628            if pane_pos != floating_pos:
8629                pane.floating_pos = floating_pos
8630                self._from_move = True
8631                pane.frame.SetPosition(pane.floating_pos)
8632                self._from_move = False
8633
8634
8635    def OnGripperClicked(self, pane_window, start, offset):
8636        """
8637        Handles the mouse click on the pane gripper.
8638
8639        :param wx.Window `pane_window`: the window managed by the pane;
8640        :param wx.Point `start`: the mouse-click position;
8641        :param wx.Point `offset`: an offset point from the `start` position.
8642        """
8643
8644        # try to find the pane
8645        paneInfo = self.GetPane(pane_window)
8646
8647        if not paneInfo.IsOk():
8648            raise Exception("Pane window not found")
8649
8650        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
8651            # set the caption as active
8652            ret, self._panes = SetActivePane(self._panes, pane_window)
8653            self.RefreshCaptions()
8654            self.FireEvent(wxEVT_AUI_PANE_ACTIVATED, pane_window, canVeto=False)
8655
8656        self._action_part = None
8657        self._action_pane = paneInfo
8658        self._action_window = pane_window
8659        self._action_start = start
8660        self._action_offset = offset
8661        self._toolbar_action_offset = wx.Point(*self._action_offset)
8662
8663        self._frame.CaptureMouse()
8664
8665        if paneInfo.IsDocked():
8666            self._action = actionClickCaption
8667        else:
8668            if paneInfo.IsToolbar():
8669                self._action = actionDragToolbarPane
8670            else:
8671                self._action = actionDragFloatingPane
8672
8673            if paneInfo.frame:
8674
8675                windowPt = paneInfo.frame.GetRect().GetTopLeft()
8676                originPt = paneInfo.frame.ClientToScreen(wx.Point())
8677                self._action_offset += originPt - windowPt
8678                self._toolbar_action_offset = wx.Point(*self._action_offset)
8679
8680                if self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
8681                    paneInfo.frame.SetTransparent(150)
8682
8683            if paneInfo.IsToolbar():
8684                self._frame.SetCursor(wx.Cursor(wx.CURSOR_SIZING))
8685
8686
8687    def OnRender(self, event):
8688        """
8689        Draws all of the pane captions, sashes, backgrounds, captions, grippers, pane borders and buttons.
8690        It renders the entire user interface. It binds the ``EVT_AUI_RENDER`` event.
8691
8692        :param `event`: an instance of :class:`AuiManagerEvent`.
8693        """
8694
8695        # if the frame is about to be deleted, don't bother
8696        if not self._frame or self._frame.IsBeingDeleted():
8697            return
8698
8699        if not self._frame.GetSizer():
8700            return
8701
8702        if not self._frame.IsShownOnScreen():
8703            return
8704
8705        mouse = wx.GetMouseState()
8706        mousePos = wx.Point(mouse.GetX(), mouse.GetY())
8707        point = self._frame.ScreenToClient(mousePos)
8708        art = self._art
8709
8710        dc = event.GetDC()
8711
8712        for part in self._uiparts:
8713
8714            # don't draw hidden pane items or items that aren't windows
8715            if part.sizer_item and ((not part.sizer_item.IsWindow() and \
8716                                     not part.sizer_item.IsSpacer() and \
8717                                     not part.sizer_item.IsSizer()) or \
8718                                    not part.sizer_item.IsShown()):
8719
8720                continue
8721
8722            ptype = part.type
8723
8724            if ptype in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
8725                art.DrawSash(dc, self._frame, part.orientation, part.rect)
8726
8727            elif ptype == AuiDockUIPart.typeBackground:
8728                art.DrawBackground(dc, self._frame, part.orientation, part.rect)
8729
8730            elif ptype == AuiDockUIPart.typeCaption:
8731                art.DrawCaption(dc, self._frame, part.pane.caption, part.rect, part.pane)
8732
8733            elif ptype == AuiDockUIPart.typeGripper:
8734                art.DrawGripper(dc, self._frame, part.rect, part.pane)
8735
8736            elif ptype == AuiDockUIPart.typePaneBorder:
8737                art.DrawBorder(dc, self._frame, part.rect, part.pane)
8738
8739            elif ptype == AuiDockUIPart.typePaneButton:
8740                self.DrawPaneButton(dc, part, point)
8741
8742
8743    def Repaint(self, dc=None):
8744        """
8745        Repaints the entire frame decorations (sashes, borders, buttons and so on).
8746        It renders the entire user interface.
8747
8748        :param `dc`: if not ``None``, an instance of :class:`PaintDC`.
8749        """
8750
8751        w, h = self._frame.GetClientSize()
8752
8753        # Figure out which dc to use; if one
8754        # has been specified, use it, otherwise
8755        # make a client dc
8756        if dc is None:
8757            if not self._frame.IsDoubleBuffered():
8758                client_dc = wx.BufferedDC(wx.ClientDC(self._frame), wx.Size(w, h))
8759            else:
8760                client_dc = wx.ClientDC(self._frame)
8761            dc = client_dc
8762
8763        # If the frame has a toolbar, the client area
8764        # origin will not be (0, 0).
8765        pt = self._frame.GetClientAreaOrigin()
8766        if pt.x != 0 or pt.y != 0:
8767            dc.SetDeviceOrigin(pt.x, pt.y)
8768
8769        # Render all the items
8770        self.Render(dc)
8771
8772
8773    def Render(self, dc):
8774        """
8775        Fires a render event, which is normally handled by :meth:`OnRender`. This allows the
8776        render function to be overridden via the render event.
8777
8778        This can be useful for painting custom graphics in the main window.
8779        Default behavior can be invoked in the overridden function by calling
8780        :meth:`OnRender`.
8781
8782        :param `dc`: a :class:`wx.DC` device context object.
8783        """
8784
8785        e = AuiManagerEvent(wxEVT_AUI_RENDER)
8786        e.SetManager(self)
8787        e.SetDC(dc)
8788        self.ProcessMgrEvent(e)
8789
8790
8791    def OnCaptionDoubleClicked(self, pane_window):
8792        """
8793        Handles the mouse double click on the pane caption.
8794
8795        :param wx.Window `pane_window`: the window managed by the pane.
8796        """
8797
8798        # try to find the pane
8799        paneInfo = self.GetPane(pane_window)
8800        if not paneInfo.IsOk():
8801            raise Exception("Pane window not found")
8802
8803        if not paneInfo.IsFloatable() or not paneInfo.IsDockable() or \
8804           self._agwFlags & AUI_MGR_ALLOW_FLOATING == 0:
8805            return
8806
8807        indx = self._panes.index(paneInfo)
8808        win_rect = None
8809
8810        if paneInfo.IsFloating():
8811            if paneInfo.name.startswith("__floating__"):
8812                # It's a floating tab from a AuiNotebook
8813                notebook = paneInfo.window.__aui_notebook__
8814                notebook.ReDockPage(paneInfo)
8815                self.Update()
8816                return
8817            else:
8818
8819                e = self.FireEvent(wxEVT_AUI_PANE_DOCKING, paneInfo, canVeto=True)
8820                if e.GetVeto():
8821                    self.HideHint()
8822                    ShowDockingGuides(self._guides, False)
8823                    return
8824
8825                win_rect = paneInfo.frame.GetRect()
8826                paneInfo.Dock()
8827                if paneInfo.IsToolbar():
8828                    paneInfo = self.SwitchToolBarOrientation(paneInfo)
8829
8830                e = self.FireEvent(wxEVT_AUI_PANE_DOCKED, paneInfo, canVeto=False)
8831
8832        else:
8833
8834            e = self.FireEvent(wxEVT_AUI_PANE_FLOATING, paneInfo, canVeto=True)
8835            if e.GetVeto():
8836                return
8837
8838            # float the window
8839            if paneInfo.IsMaximized():
8840                self.RestorePane(paneInfo)
8841
8842            if paneInfo.floating_pos == wx.Point(-1, -1):
8843                captionSize = self._art.GetMetric(AUI_DOCKART_CAPTION_SIZE)
8844                paneInfo.floating_pos = pane_window.GetScreenPosition()
8845                paneInfo.floating_pos.y -= captionSize
8846
8847            paneInfo.Float()
8848            e = self.FireEvent(wxEVT_AUI_PANE_FLOATED, paneInfo, canVeto=False)
8849
8850        self._panes[indx] = paneInfo
8851        self.Update()
8852
8853        if win_rect and self._agwFlags & AUI_MGR_ANIMATE_FRAMES:
8854            paneInfo = self.GetPane(pane_window)
8855            pane_rect = paneInfo.window.GetScreenRect()
8856            self.AnimateDocking(win_rect, pane_rect)
8857
8858
8859    def OnPaint(self, event):
8860        """
8861        Handles the ``wx.EVT_PAINT`` event for :class:`AuiManager`.
8862
8863        :param `event`: an instance of :class:`PaintEvent` to be processed.
8864        """
8865
8866        dc = wx.PaintDC(self._frame)
8867        self.Repaint(dc)
8868
8869
8870    def OnEraseBackground(self, event):
8871        """
8872        Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`AuiManager`.
8873
8874        :param `event`: :class:`EraseEvent` to be processed.
8875
8876        :note: This is intentionally empty (excluding wxMAC) to reduce
8877         flickering while drawing.
8878        """
8879
8880        if wx.Platform == "__WXMAC__":
8881            event.Skip()
8882
8883
8884    def OnSize(self, event):
8885        """
8886        Handles the ``wx.EVT_SIZE`` event for :class:`AuiManager`.
8887
8888        :param `event`: a :class:`wx.SizeEvent` to be processed.
8889        """
8890
8891        skipped = False
8892        if isinstance(self._frame, AuiFloatingFrame) and self._frame.IsShownOnScreen():
8893            skipped = True
8894            event.Skip()
8895
8896        if self._frame:
8897
8898            self.DoFrameLayout()
8899            if wx.Platform == "__WXMAC__":
8900                self._frame.Refresh()
8901            else:
8902                self.Repaint()
8903
8904            if isinstance(self._frame, wx.MDIParentFrame) or isinstance(self._frame, tabmdi.AuiMDIClientWindow) \
8905               or isinstance(self._frame, tabmdi.AuiMDIParentFrame):
8906                # for MDI parent frames, this event must not
8907                # be "skipped".  In other words, the parent frame
8908                # must not be allowed to resize the client window
8909                # after we are finished processing sizing changes
8910                return
8911
8912        if not skipped:
8913            event.Skip()
8914
8915        # For the snap to screen...
8916        self.OnMove(None)
8917
8918
8919    def OnFindManager(self, event):
8920        """
8921        Handles the ``EVT_AUI_FIND_MANAGER`` event for :class:`AuiManager`.
8922
8923        :param `event`: a :class:`AuiManagerEvent` event to be processed.
8924        """
8925
8926        # Initialize to None
8927        event.SetManager(None)
8928
8929        if not self._frame:
8930            return
8931
8932        # See it this window wants to overwrite
8933        self._frame.ProcessEvent(event)
8934
8935        # if no, it must be us
8936        if not event.GetManager():
8937           event.SetManager(self)
8938
8939
8940    def OnSetCursor(self, event):
8941        """
8942        Handles the ``wx.EVT_SET_CURSOR`` event for :class:`AuiManager`.
8943
8944        :param `event`: a :class:`SetCursorEvent` to be processed.
8945        """
8946
8947        # determine cursor
8948        part = self.HitTest(event.GetX(), event.GetY())
8949        cursor = wx.NullCursor
8950
8951        if part:
8952            if part.type in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
8953
8954                if not self.CheckMovableSizer(part):
8955                    return
8956
8957                if part.orientation == wx.VERTICAL:
8958                    cursor = wx.Cursor(wx.CURSOR_SIZEWE)
8959                else:
8960                    cursor = wx.Cursor(wx.CURSOR_SIZENS)
8961
8962            elif part.type == AuiDockUIPart.typeGripper:
8963                cursor = wx.Cursor(wx.CURSOR_SIZING)
8964
8965        event.SetCursor(cursor)
8966
8967
8968    def UpdateButtonOnScreen(self, button_ui_part, event):
8969        """
8970        Updates/redraws the UI part containing a pane button.
8971
8972        :param `button_ui_part`: the UI part the button belongs to, an instance of :class:`AuiDockUIPart`.;
8973        :param `event`: a :class:`MouseEvent` to be processed.
8974        """
8975
8976        hit_test = self.HitTest(*event.GetPosition())
8977
8978        if not hit_test or not button_ui_part:
8979            return
8980
8981        state = AUI_BUTTON_STATE_NORMAL
8982
8983        if hit_test == button_ui_part:
8984            if event.LeftDown():
8985                state = AUI_BUTTON_STATE_PRESSED
8986            else:
8987                state = AUI_BUTTON_STATE_HOVER
8988        else:
8989            if event.LeftDown():
8990                state = AUI_BUTTON_STATE_HOVER
8991
8992        # now repaint the button with hover state
8993        cdc = wx.ClientDC(self._frame)
8994
8995        # if the frame has a toolbar, the client area
8996        # origin will not be (0,0).
8997        pt = self._frame.GetClientAreaOrigin()
8998        if pt.x != 0 or pt.y != 0:
8999            cdc.SetDeviceOrigin(pt.x, pt.y)
9000
9001        if hit_test.pane:
9002            self._art.DrawPaneButton(cdc, self._frame,
9003                      button_ui_part.button.button_id,
9004                      state,
9005                      button_ui_part.rect, hit_test.pane)
9006
9007
9008    def OnLeftDown(self, event):
9009        """
9010        Handles the ``wx.EVT_LEFT_DOWN`` event for :class:`AuiManager`.
9011
9012        :param `event`: a :class:`MouseEvent` to be processed.
9013        """
9014
9015        part = self.HitTest(*event.GetPosition())
9016
9017        if not part:
9018            event.Skip()
9019            return
9020
9021        self._currentDragItem = -1
9022
9023        if part.type in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
9024
9025            if not self.CheckMovableSizer(part):
9026                return
9027
9028            self._action = actionResize
9029            self._action_part = part
9030            self._action_pane = None
9031            self._action_rect = wx.Rect()
9032            self._action_start = wx.Point(event.GetX(), event.GetY())
9033            self._action_offset = wx.Point(event.GetX() - part.rect.x,
9034                                           event.GetY() - part.rect.y)
9035
9036            # draw the resize hint
9037            rect = wx.Rect(self._frame.ClientToScreen(part.rect.GetPosition()),
9038                             part.rect.GetSize())
9039
9040            self._action_rect = wx.Rect(*rect)
9041
9042            if not AuiManager_HasLiveResize(self):
9043                if wx.Platform == "__WXMAC__":
9044                    dc = wx.ClientDC(self._frame)
9045                else:
9046                    dc = wx.ScreenDC()
9047
9048                DrawResizeHint(dc, rect)
9049
9050            self._frame.CaptureMouse()
9051
9052        elif part.type == AuiDockUIPart.typePaneButton:
9053            if self.IsPaneButtonVisible(part):
9054                self._action = actionClickButton
9055                self._action_part = part
9056                self._action_pane = None
9057                self._action_start = wx.Point(*event.GetPosition())
9058                self._frame.CaptureMouse()
9059
9060                self.RefreshButton(part)
9061
9062        elif part.type in [AuiDockUIPart.typeCaption, AuiDockUIPart.typeGripper]:
9063
9064            # if we are managing a AuiFloatingFrame window, then
9065            # we are an embedded AuiManager inside the AuiFloatingFrame.
9066            # We want to initiate a toolbar drag in our owner manager
9067            if isinstance(part.pane.window.GetParent(), AuiFloatingFrame):
9068                rootManager = GetManager(part.pane.window)
9069            else:
9070                rootManager = self
9071
9072            offset = wx.Point(event.GetX() - part.rect.x, event.GetY() - part.rect.y)
9073            rootManager.OnGripperClicked(part.pane.window, event.GetPosition(), offset)
9074
9075        if wx.Platform != "__WXMAC__":
9076            event.Skip()
9077
9078
9079    def OnLeftDClick(self, event):
9080        """
9081        Handles the ``wx.EVT_LEFT_DCLICK`` event for :class:`AuiManager`.
9082
9083        :param `event`: a :class:`MouseEvent` to be processed.
9084        """
9085
9086        part = self.HitTest(event.GetX(), event.GetY())
9087
9088        if part and part.type == AuiDockUIPart.typeCaption:
9089            if isinstance(part.pane.window.GetParent(), AuiFloatingFrame):
9090                rootManager = GetManager(part.pane.window)
9091            else:
9092                rootManager = self
9093
9094            rootManager.OnCaptionDoubleClicked(part.pane.window)
9095
9096        elif part and part.type in [AuiDockUIPart.typeDockSizer, AuiDockUIPart.typePaneSizer]:
9097            # Handles double click on AuiNotebook sashes to unsplit
9098            sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
9099            for child in part.cont_sizer.GetChildren():
9100                if child.IsSizer():
9101                    win = child.GetSizer().GetContainingWindow()
9102                    if isinstance(win, auibook.AuiNotebook):
9103                        win.UnsplitDClick(part, sash_size, event.GetPosition())
9104                        break
9105
9106        event.Skip()
9107
9108
9109    def DoEndResizeAction(self, event):
9110        """
9111        Ends a resize action, or for live update, resizes the sash.
9112
9113        :param `event`: a :class:`MouseEvent` to be processed.
9114        """
9115
9116        clientPt = event.GetPosition()
9117        screenPt = self._frame.ClientToScreen(clientPt)
9118
9119        return self.RestrictResize(clientPt, screenPt, createDC=False)
9120
9121
9122    def RestrictResize(self, clientPt, screenPt, createDC):
9123        """ Common method between :meth:`DoEndResizeAction` and :meth:`OnLeftUp_Resize`. """
9124
9125        dock = self._action_part.dock
9126        pane = self._action_part.pane
9127
9128        if createDC:
9129            if wx.Platform == "__WXMAC__":
9130                dc = wx.ClientDC(self._frame)
9131            else:
9132                dc = wx.ScreenDC()
9133
9134            DrawResizeHint(dc, self._action_rect)
9135            self._action_rect = wx.Rect()
9136
9137        newPos = clientPt - self._action_offset
9138
9139        if self._action_part.type == AuiDockUIPart.typeDockSizer:
9140            minPix, maxPix = self.CalculateDockSizerLimits(dock)
9141        else:
9142            if not self._action_part.pane:
9143                return
9144            minPix, maxPix = self.CalculatePaneSizerLimits(dock, pane)
9145
9146        if self._action_part.orientation == wx.HORIZONTAL:
9147            newPos.y = Clip(newPos.y, minPix, maxPix)
9148        else:
9149            newPos.x = Clip(newPos.x, minPix, maxPix)
9150
9151        if self._action_part.type == AuiDockUIPart.typeDockSizer:
9152
9153            partnerDock = self.GetPartnerDock(dock)
9154            sash_size = self._art.GetMetric(AUI_DOCKART_SASH_SIZE)
9155            new_dock_size = 0
9156            direction = dock.dock_direction
9157
9158            if direction == AUI_DOCK_LEFT:
9159                new_dock_size = newPos.x - dock.rect.x
9160
9161            elif direction == AUI_DOCK_TOP:
9162                new_dock_size = newPos.y - dock.rect.y
9163
9164            elif direction == AUI_DOCK_RIGHT:
9165                new_dock_size = dock.rect.x + dock.rect.width - newPos.x - sash_size
9166
9167            elif direction == AUI_DOCK_BOTTOM:
9168                new_dock_size = dock.rect.y + dock.rect.height - newPos.y - sash_size
9169
9170            deltaDockSize = new_dock_size - dock.size
9171
9172            if partnerDock:
9173                if deltaDockSize > partnerDock.size - sash_size:
9174                    deltaDockSize = partnerDock.size - sash_size
9175
9176                partnerDock.size -= deltaDockSize
9177
9178            dock.size += deltaDockSize
9179            self.Update()
9180
9181        else:
9182
9183            # determine the new pixel size that the user wants
9184            # this will help us recalculate the pane's proportion
9185            if dock.IsHorizontal():
9186                oldPixsize = pane.rect.width
9187                newPixsize = oldPixsize + newPos.x - self._action_part.rect.x
9188
9189            else:
9190                oldPixsize = pane.rect.height
9191                newPixsize = oldPixsize + newPos.y - self._action_part.rect.y
9192
9193            totalPixsize, totalProportion = self.GetTotalPixSizeAndProportion(dock)
9194            partnerPane = self.GetPartnerPane(dock, pane)
9195
9196            # prevent division by zero
9197            if totalPixsize <= 0 or totalProportion <= 0 or not partnerPane:
9198                return
9199
9200            # adjust for the surplus
9201            while (oldPixsize > 0 and totalPixsize > 10 and \
9202                  oldPixsize*totalProportion/totalPixsize < pane.dock_proportion):
9203
9204                totalPixsize -= 1
9205
9206            # calculate the new proportion of the pane
9207
9208            newProportion = newPixsize*totalProportion/totalPixsize
9209            newProportion = Clip(newProportion, 1, totalProportion)
9210            deltaProp = newProportion - pane.dock_proportion
9211
9212            if partnerPane.dock_proportion - deltaProp < 1:
9213                deltaProp = partnerPane.dock_proportion - 1
9214                newProportion = pane.dock_proportion + deltaProp
9215
9216            # borrow the space from our neighbor pane to the
9217            # right or bottom (depending on orientation)
9218            partnerPane.dock_proportion -= deltaProp
9219            pane.dock_proportion = newProportion
9220
9221            self.Update()
9222
9223        return True
9224
9225
9226    def OnLeftUp(self, event):
9227        """
9228        Handles the ``wx.EVT_LEFT_UP`` event for :class:`AuiManager`.
9229
9230        :param `event`: a :class:`MouseEvent` to be processed.
9231        """
9232
9233        if self._action == actionResize:
9234##            self._frame.Freeze()
9235            self.OnLeftUp_Resize(event)
9236##            self._frame.Thaw()
9237
9238        elif self._action == actionClickButton:
9239            self.OnLeftUp_ClickButton(event)
9240
9241        elif self._action == actionDragFloatingPane:
9242            self.OnLeftUp_DragFloatingPane(event)
9243
9244        elif self._action == actionDragToolbarPane:
9245            self.OnLeftUp_DragToolbarPane(event)
9246
9247        elif self._action == actionDragMovablePane:
9248            self.OnLeftUp_DragMovablePane(event)
9249
9250        else:
9251            event.Skip()
9252
9253        try:
9254            if self._frame.HasCapture():
9255                self._frame.ReleaseMouse()
9256        except RuntimeError:
9257            pass
9258
9259        self._action = actionNone
9260
9261
9262    def OnMotion(self, event):
9263        """
9264        Handles the ``wx.EVT_MOTION`` event for :class:`AuiManager`.
9265
9266        :param `event`: a :class:`MouseEvent` to be processed.
9267        """
9268
9269        if self._action == actionResize:
9270            self.OnMotion_Resize(event)
9271
9272        elif self._action == actionClickCaption:
9273            self.OnMotion_ClickCaption(event)
9274
9275        elif self._action == actionDragFloatingPane:
9276            self.OnMotion_DragFloatingPane(event)
9277
9278        elif self._action == actionDragToolbarPane:
9279            self.OnMotion_DragToolbarPane(event)
9280
9281        elif self._action == actionDragMovablePane:
9282            self.OnMotion_DragMovablePane(event)
9283
9284        else:
9285            self.OnMotion_Other(event)
9286
9287
9288    def OnLeaveWindow(self, event):
9289        """
9290        Handles the ``wx.EVT_LEAVE_WINDOW`` event for :class:`AuiManager`.
9291
9292        :param `event`: a :class:`MouseEvent` to be processed.
9293        """
9294
9295        if self._hover_button:
9296            self.RefreshButton(self._hover_button)
9297            self._hover_button = None
9298
9299
9300    def OnCaptureLost(self, event):
9301        """
9302        Handles the ``wx.EVT_MOUSE_CAPTURE_LOST`` event for :class:`AuiManager`.
9303
9304        :param `event`: a :class:`MouseCaptureLostEvent` to be processed.
9305        """
9306
9307        # cancel the operation in progress, if any
9308        if self._action != actionNone:
9309            self._action = actionNone
9310            self.HideHint()
9311
9312
9313    def OnHintFadeTimer(self, event):
9314        """
9315        Handles the ``wx.EVT_TIMER`` event for :class:`AuiManager`.
9316
9317        :param `event`: a :class:`TimerEvent` to be processed.
9318        """
9319
9320        if not self._hint_window or self._hint_fadeamt >= self._hint_fademax:
9321            self._hint_fadetimer.Stop()
9322            return
9323
9324        self._hint_fadeamt += 4
9325        self._hint_window.SetTransparent(self._hint_fadeamt)
9326
9327
9328    def OnMove(self, event):
9329        """
9330        Handles the ``wx.EVT_MOVE`` event for :class:`AuiManager`.
9331
9332        :param `event`: a :class:`MoveEvent` to be processed.
9333        """
9334
9335        if event is not None:
9336            event.Skip()
9337
9338        if isinstance(self._frame, AuiFloatingFrame) and self._frame.IsShownOnScreen():
9339            return
9340
9341        docked, hAlign, vAlign, monitor = self._is_docked
9342        if docked:
9343            self.Snap()
9344
9345        for pane in self._panes:
9346            if pane.IsSnappable():
9347                if pane.IsFloating() and pane.IsShown():
9348                    self.SnapPane(pane, pane.floating_pos, pane.floating_size, True)
9349
9350
9351    def OnSysColourChanged(self, event):
9352        """
9353        Handles the ``wx.EVT_SYS_COLOUR_CHANGED`` event for :class:`AuiManager`.
9354
9355        :param `event`: a :class:`SysColourChangedEvent` to be processed.
9356        """
9357
9358        # This event is probably triggered by a theme change
9359        # so we have to re-init the art provider.
9360        if self._art:
9361            self._art.Init()
9362
9363        if self._frame:
9364            self.Update()
9365            self._frame.Refresh()
9366
9367
9368    def OnChildFocus(self, event):
9369        """
9370        Handles the ``wx.EVT_CHILD_FOCUS`` event for :class:`AuiManager`.
9371
9372        :param `event`: a :class:`ChildFocusEvent` to be processed.
9373        """
9374
9375        # when a child pane has it's focus set, we should change the
9376        # pane's active state to reflect this. (this is only true if
9377        # active panes are allowed by the owner)
9378
9379        window = event.GetWindow()
9380        if isinstance(window, wx.Dialog):
9381            # Ignore EVT_CHILD_FOCUS events originating from dialogs not
9382            # managed by AUI
9383            rootManager = None
9384        elif isinstance(window.GetParent(), AuiFloatingFrame):
9385            rootManager = GetManager(window)
9386        else:
9387            rootManager = self
9388
9389        if rootManager:
9390            rootManager.ActivatePane(window)
9391
9392        event.Skip()
9393
9394
9395    def OnMotion_ClickCaption(self, event):
9396        """
9397        Sub-handler for the :meth:`OnMotion` event.
9398
9399        :param `event`: a :class:`MouseEvent` to be processed.
9400        """
9401
9402        clientPt = event.GetPosition()
9403        screenPt = self._frame.ClientToScreen(clientPt)
9404
9405        drag_x_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_X)
9406        drag_y_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_Y)
9407
9408        if not self._action_pane:
9409            return
9410
9411        # we need to check if the mouse is now being dragged
9412        if not (abs(clientPt.x - self._action_start.x) > drag_x_threshold or \
9413                abs(clientPt.y - self._action_start.y) > drag_y_threshold):
9414
9415            return
9416
9417        # dragged -- we need to change the mouse action to 'drag'
9418        if self._action_pane.IsToolbar():
9419            self._action = actionDragToolbarPane
9420            self._action_window = self._action_pane.window
9421
9422        elif self._action_pane.IsFloatable() and self._agwFlags & AUI_MGR_ALLOW_FLOATING:
9423
9424            e = self.FireEvent(wxEVT_AUI_PANE_FLOATING, self._action_pane, canVeto=True)
9425            if e.GetVeto():
9426                return
9427
9428            self._action = actionDragFloatingPane
9429
9430            # set initial float position
9431            self._action_pane.floating_pos = screenPt - self._action_offset
9432
9433            # float the window
9434            if self._action_pane.IsMaximized():
9435                self.RestorePane(self._action_pane)
9436
9437            self._action_pane.Hide()
9438            self._action_pane.Float()
9439            if wx.Platform == "__WXGTK__":
9440                self._action_pane.Show()
9441
9442            e = self.FireEvent(wxEVT_AUI_PANE_FLOATED, self._action_pane, canVeto=False)
9443
9444            if not self._action_pane.frame:
9445                self.DoUpdate()
9446
9447            self._action_window = self._action_pane.window
9448
9449            # adjust action offset for window frame
9450            windowPt = self._action_pane.frame.GetRect().GetTopLeft()
9451            originPt = self._action_pane.frame.ClientToScreen(wx.Point())
9452            self._toolbar_action_offset = originPt - windowPt
9453
9454            if self._agwFlags & AUI_MGR_USE_NATIVE_MINIFRAMES:
9455                originPt = windowPt + wx.Point(3, 3)
9456
9457            self._action_offset += originPt - windowPt
9458
9459            # action offset is used here to make it feel "natural" to the user
9460            # to drag a docked pane and suddenly have it become a floating frame.
9461            # Sometimes, however, the offset where the user clicked on the docked
9462            # caption is bigger than the width of the floating frame itself, so
9463            # in that case we need to set the action offset to a sensible value
9464            frame_size = self._action_pane.frame.GetSize()
9465            if self._action_offset.x > frame_size.x * 2 / 3:
9466                self._action_offset.x = frame_size.x / 2
9467            if self._action_offset.y > frame_size.y * 2 / 3:
9468                self._action_offset.y = frame_size.y / 2
9469
9470            self.OnMotion_DragFloatingPane(event)
9471            if wx.Platform != "__WXGTK__":
9472                self._action_pane.Show()
9473
9474            self.Update()
9475
9476        elif self._action_pane.IsMovable():
9477            self._action = actionDragMovablePane
9478            self._action_window = self._action_pane.window
9479
9480
9481    def OnMotion_Resize(self, event):
9482        """
9483        Sub-handler for the :meth:`OnMotion` event.
9484
9485        :param `event`: a :class:`MouseEvent` to be processed.
9486        """
9487
9488        if AuiManager_HasLiveResize(self):
9489            if self._currentDragItem != -1:
9490                self._action_part = self._uiparts[self._currentDragItem]
9491            else:
9492                self._currentDragItem = self._uiparts.index(self._action_part)
9493
9494            if self._frame.HasCapture():
9495                self._frame.ReleaseMouse()
9496
9497            self.DoEndResizeAction(event)
9498            self._frame.CaptureMouse()
9499            return
9500
9501        if not self._action_part or not self._action_part.dock or not self._action_part.orientation:
9502            return
9503
9504        clientPt = event.GetPosition()
9505        screenPt = self._frame.ClientToScreen(clientPt)
9506
9507        dock = self._action_part.dock
9508        pos = self._action_part.rect.GetPosition()
9509
9510        if self._action_part.type == AuiDockUIPart.typeDockSizer:
9511            minPix, maxPix = self.CalculateDockSizerLimits(dock)
9512        else:
9513            if not self._action_part.pane:
9514                return
9515
9516            pane = self._action_part.pane
9517            minPix, maxPix = self.CalculatePaneSizerLimits(dock, pane)
9518
9519        if self._action_part.orientation == wx.HORIZONTAL:
9520            pos.y = Clip(clientPt.y - self._action_offset.y, minPix, maxPix)
9521        else:
9522            pos.x = Clip(clientPt.x - self._action_offset.x, minPix, maxPix)
9523
9524        hintrect = wx.Rect(self._frame.ClientToScreen(pos), self._action_part.rect.GetSize())
9525
9526        if hintrect != self._action_rect:
9527
9528            if wx.Platform == "__WXMAC__":
9529                dc = wx.ClientDC(self._frame)
9530            else:
9531                dc = wx.ScreenDC()
9532
9533            DrawResizeHint(dc, self._action_rect)
9534            DrawResizeHint(dc, hintrect)
9535            self._action_rect = wx.Rect(*hintrect)
9536
9537
9538    def OnLeftUp_Resize(self, event):
9539        """
9540        Sub-handler for the :meth:`OnLeftUp` event.
9541
9542        :param `event`: a :class:`MouseEvent` to be processed.
9543        """
9544
9545        if self._currentDragItem != -1 and AuiManager_HasLiveResize(self):
9546            self._action_part = self._uiparts[self._currentDragItem]
9547
9548            if self._frame.HasCapture():
9549                self._frame.ReleaseMouse()
9550
9551            self.DoEndResizeAction(event)
9552            self._currentDragItem = -1
9553            return
9554
9555        if not self._action_part or not self._action_part.dock:
9556            return
9557
9558        clientPt = event.GetPosition()
9559        screenPt = self._frame.ClientToScreen(clientPt)
9560
9561        return self.RestrictResize(clientPt, screenPt, createDC=True)
9562
9563
9564    def OnLeftUp_ClickButton(self, event):
9565        """
9566        Sub-handler for the :meth:`OnLeftUp` event.
9567
9568        :param `event`: a :class:`MouseEvent` to be processed.
9569        """
9570
9571        self._hover_button = None
9572
9573        if self._action_part:
9574            self.RefreshButton(self._action_part)
9575
9576            # make sure we're still over the item that was originally clicked
9577            if self._action_part == self.HitTest(*event.GetPosition()):
9578
9579                # fire button-click event
9580                e = AuiManagerEvent(wxEVT_AUI_PANE_BUTTON)
9581                e.SetManager(self)
9582                e.SetPane(self._action_part.pane)
9583                e.SetButton(self._action_part.button.button_id)
9584                self.ProcessMgrEvent(e)
9585
9586
9587    def CheckPaneMove(self, pane):
9588        """
9589        Checks if a pane has moved by a visible amount.
9590
9591        :param `pane`: an instance of :class:`AuiPaneInfo`.
9592        """
9593
9594        win_rect = pane.frame.GetRect()
9595        win_rect.x, win_rect.y = pane.floating_pos
9596
9597        if win_rect == self._last_rect:
9598            return False
9599
9600        # skip the first move event
9601        if self._last_rect.IsEmpty():
9602            self._last_rect = wx.Rect(*win_rect)
9603            return False
9604
9605        # skip if moving too fast to avoid massive redraws and
9606        # jumping hint windows
9607        if abs(win_rect.x - self._last_rect.x) > 10 or \
9608           abs(win_rect.y - self._last_rect.y) > 10:
9609            self._last_rect = wx.Rect(*win_rect)
9610            return False
9611
9612        return True
9613
9614
9615    def OnMotion_DragFloatingPane(self, eventOrPt):
9616        """
9617        Sub-handler for the :meth:`OnMotion` event.
9618
9619        :param `event`: a :class:`MouseEvent` to be processed.
9620        """
9621
9622        isPoint = False
9623        if isinstance(eventOrPt, wx.Point):
9624            clientPt = self._frame.ScreenToClient(eventOrPt)
9625            screenPt = wx.Point(*eventOrPt)
9626            isPoint = True
9627        else:
9628            clientPt = eventOrPt.GetPosition()
9629            screenPt = self._frame.ClientToScreen(clientPt)
9630
9631        framePos = wx.Point()
9632
9633        # try to find the pane
9634        pane = self.GetPane(self._action_window)
9635        if not pane.IsOk():
9636            raise Exception("Pane window not found")
9637
9638        # update floating position
9639        if pane.IsFloating():
9640            diff = pane.floating_pos - (screenPt - self._action_offset)
9641            pane.floating_pos = screenPt - self._action_offset
9642
9643        framePos = pane.floating_pos
9644
9645        # Move the pane window
9646        if pane.frame:
9647
9648            if diff.x != 0 or diff.y != 0:
9649                if wx.Platform == "__WXMSW__" and (self._agwFlags & AUI_MGR_TRANSPARENT_DRAG) == 0: # and not self.CheckPaneMove(pane):
9650                    # return
9651                    # HACK: Terrible hack on wxMSW (!)
9652                    pane.frame.SetTransparent(254)
9653
9654                self._from_move = True
9655                pane.frame.Move(pane.floating_pos)
9656                self._from_move = False
9657
9658            if self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
9659                pane.frame.SetTransparent(150)
9660
9661        # calculate the offset from the upper left-hand corner
9662        # of the frame to the mouse pointer
9663        action_offset = screenPt - framePos
9664
9665        # is the pane dockable?
9666        if not self.CanDockPanel(pane):
9667            self.HideHint()
9668            ShowDockingGuides(self._guides, False)
9669            return
9670
9671        for paneInfo in self._panes:
9672
9673            if not paneInfo.IsDocked() or not paneInfo.IsShown():
9674                continue
9675            if paneInfo.IsToolbar() or paneInfo.IsNotebookControl():
9676                continue
9677            if paneInfo.IsMaximized():
9678                continue
9679
9680            if paneInfo.IsNotebookPage():
9681
9682                notebookRoot = GetNotebookRoot(self._panes, paneInfo.notebook_id)
9683
9684                if not notebookRoot or not notebookRoot.IsDocked():
9685                    continue
9686
9687            rc = paneInfo.window.GetScreenRect()
9688            if rc.Contains(screenPt):
9689                if rc.height < 20 or rc.width < 20:
9690                    return
9691
9692                self.UpdateDockingGuides(paneInfo)
9693                ShowDockingGuides(self._guides, True)
9694                break
9695
9696        wx.CallAfter(self.DrawHintRect, pane.window, clientPt, action_offset)
9697
9698
9699    def OnMotion_DragMovablePane(self, eventOrPt):
9700        """
9701        Sub-handler for the :meth:`OnMotion` event.
9702
9703        :param `event`: a :class:`MouseEvent` to be processed.
9704        """
9705
9706        # Try to find the pane.
9707        pane = self.GetPane(self._action_window)
9708        if not pane.IsOk():
9709            raise Exception("Pane window not found")
9710
9711        # Draw a hint for where the window will be moved.
9712        if isinstance(eventOrPt, wx.Point):
9713            pt = wx.Point(*eventOrPt)
9714        else:
9715            pt = eventOrPt.GetPosition()
9716
9717        self.DrawHintRect(self._action_window, pt, wx.Point(0, 0))
9718
9719        # Reduces flicker.
9720        self._frame.Update()
9721
9722
9723    def OnLeftUp_DragFloatingPane(self, eventOrPt):
9724        """
9725        Sub-handler for the :meth:`OnLeftUp` event.
9726
9727        :param `event`: a :class:`MouseEvent` to be processed.
9728        """
9729
9730        if isinstance(eventOrPt, wx.Point):
9731            clientPt = self._frame.ScreenToClient(eventOrPt)
9732            screenPt = wx.Point(*eventOrPt)
9733        else:
9734            clientPt = eventOrPt.GetPosition()
9735            screenPt = self._frame.ClientToScreen(clientPt)
9736
9737        # try to find the pane
9738        paneInfo = self.GetPane(self._action_window)
9739        if not paneInfo.IsOk():
9740            raise Exception("Pane window not found")
9741
9742        ret = False
9743
9744        if paneInfo.frame:
9745
9746            # calculate the offset from the upper left-hand corner
9747            # of the frame to the mouse pointer
9748            framePos = paneInfo.frame.GetPosition()
9749            action_offset = screenPt - framePos
9750
9751            # is the pane dockable?
9752            if self.CanDockPanel(paneInfo):
9753                # do the drop calculation
9754                indx = self._panes.index(paneInfo)
9755                ret, paneInfo = self.DoDrop(self._docks, self._panes, paneInfo, clientPt, action_offset)
9756
9757                if ret:
9758                    e = self.FireEvent(wxEVT_AUI_PANE_DOCKING, paneInfo, canVeto=True)
9759                    if e.GetVeto():
9760                        self.HideHint()
9761                        ShowDockingGuides(self._guides, False)
9762                        return
9763
9764                    e = self.FireEvent(wxEVT_AUI_PANE_DOCKED, paneInfo, canVeto=False)
9765
9766                    if self._agwFlags & AUI_MGR_SMOOTH_DOCKING:
9767                        self.SmoothDock(paneInfo)
9768
9769                self._panes[indx] = paneInfo
9770
9771        # if the pane is still floating, update it's floating
9772        # position (that we store)
9773        if paneInfo.IsFloating():
9774            paneInfo.floating_pos = paneInfo.frame.GetPosition()
9775            if paneInfo.frame._transparent != paneInfo.transparent or self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
9776                paneInfo.frame.SetTransparent(paneInfo.transparent)
9777                paneInfo.frame._transparent = paneInfo.transparent
9778
9779        elif self._has_maximized:
9780            self.RestoreMaximizedPane()
9781
9782        # reorder for dropping to a new notebook
9783        # (caution: this code breaks the reference!)
9784        tempPaneInfo = self.CopyTarget(paneInfo)
9785        self._panes.remove(paneInfo)
9786        self._panes.append(tempPaneInfo)
9787
9788        if ret:
9789            self.Update()
9790
9791        if tempPaneInfo.IsFloating():
9792            self.SnapPane(tempPaneInfo, tempPaneInfo.floating_pos, tempPaneInfo.floating_size, False)
9793
9794        self.HideHint()
9795        ShowDockingGuides(self._guides, False)
9796
9797
9798    def OnLeftUp_DragMovablePane(self, event):
9799        """
9800        Sub-handler for the :meth:`OnLeftUp` event.
9801
9802        :param `event`: a :class:`MouseEvent` to be processed.
9803        """
9804
9805        # Try to find the pane.
9806        paneInfo = self.GetPane(self._action_window)
9807        if not paneInfo.IsOk():
9808            raise Exception("Pane window not found")
9809
9810        # Hide the hint as it is no longer needed.
9811        self.HideHint()
9812
9813        # is the pane dockable?
9814        if self.CanDockPanel(paneInfo):
9815            # Move the pane to new position.
9816            pt = event.GetPosition()
9817            # do the drop calculation
9818            indx = self._panes.index(paneInfo)
9819            ret, paneInfo = self.DoDrop(self._docks, self._panes, paneInfo, pt, wx.Point(0,0))
9820
9821            if ret:
9822                e = self.FireEvent(wxEVT_AUI_PANE_DOCKING, paneInfo, canVeto=True)
9823                if e.GetVeto():
9824                    self.HideHint()
9825                    ShowDockingGuides(self._guides, False)
9826                    return
9827
9828                e = self.FireEvent(wxEVT_AUI_PANE_DOCKED, paneInfo, canVeto=False)
9829
9830                if self._agwFlags & AUI_MGR_SMOOTH_DOCKING:
9831                    self.SmoothDock(paneInfo)
9832
9833            self._panes[indx] = paneInfo
9834
9835            if ret:
9836                # Update the layout to realize new position and e.g. form notebooks if needed.
9837                self.Update()
9838
9839        if self.GetAGWFlags() & AUI_MGR_ALLOW_ACTIVE_PANE:
9840            # Ensure active before doing actual display.
9841            ret, self._panes = SetActivePane(self._panes, paneInfo.window)
9842
9843        # Make changes visible to user.
9844        self.Repaint()
9845
9846        # Cancel the action and release the mouse.
9847        self._action = actionNone
9848        self._frame.ReleaseMouse()
9849        self._action_window = None
9850
9851
9852    def OnMotion_DragToolbarPane(self, eventOrPt):
9853        """
9854        Sub-handler for the :meth:`OnMotion` event.
9855
9856        :param `event`: a :class:`MouseEvent` to be processed.
9857        """
9858
9859        isPoint = False
9860        if isinstance(eventOrPt, wx.Point):
9861            clientPt = self._frame.ScreenToClient(eventOrPt)
9862            screenPt = wx.Point(*eventOrPt)
9863            isPoint = True
9864        else:
9865            clientPt = eventOrPt.GetPosition()
9866            screenPt = self._frame.ClientToScreen(clientPt)
9867
9868        pane = self.GetPane(self._action_window)
9869        if not pane.IsOk():
9870            raise Exception("Pane window not found")
9871
9872        pane.state |= AuiPaneInfo.actionPane
9873        indx = self._panes.index(pane)
9874
9875        ret = False
9876        wasFloating = pane.IsFloating()
9877        # is the pane dockable?
9878        if self.CanDockPanel(pane):
9879            # do the drop calculation
9880            ret, pane = self.DoDrop(self._docks, self._panes, pane, clientPt, self._action_offset)
9881
9882        # update floating position
9883        if pane.IsFloating():
9884            pane.floating_pos = screenPt - self._toolbar_action_offset
9885
9886        # move the pane window
9887        if pane.frame:
9888            if wx.Platform == "__WXMSW__" and (self._agwFlags & AUI_MGR_TRANSPARENT_DRAG) == 0: # and not self.CheckPaneMove(pane):
9889                # return
9890                # HACK: Terrible hack on wxMSW (!)
9891                pane.frame.SetTransparent(254)
9892
9893            self._from_move = True
9894            pane.frame.Move(pane.floating_pos)
9895            self._from_move = False
9896
9897            if self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
9898                pane.frame.SetTransparent(150)
9899
9900        self._panes[indx] = pane
9901        if ret and wasFloating != pane.IsFloating() or (ret and not wasFloating):
9902            wx.CallAfter(self.Update)
9903
9904        # when release the button out of the window.
9905        # TODO: a better fix is needed.
9906
9907        if not wx.GetMouseState().LeftIsDown():
9908            self._action = actionNone
9909            self.OnLeftUp_DragToolbarPane(eventOrPt)
9910
9911
9912    def OnMotion_Other(self, event):
9913        """
9914        Sub-handler for the :meth:`OnMotion` event.
9915
9916        :param `event`: a :class:`MouseEvent` to be processed.
9917        """
9918
9919        part = self.HitTest(*event.GetPosition())
9920
9921        if part and part.type == AuiDockUIPart.typePaneButton \
9922           and self.IsPaneButtonVisible(part):
9923            if part != self._hover_button:
9924
9925                if self._hover_button:
9926                    self.RefreshButton(self._hover_button)
9927
9928                self._hover_button = part
9929                self.RefreshButton(part)
9930
9931        else:
9932
9933            if self._hover_button:
9934                self.RefreshButton(self._hover_button)
9935            else:
9936                event.Skip()
9937
9938            self._hover_button = None
9939
9940
9941    def OnLeftUp_DragToolbarPane(self, eventOrPt):
9942        """
9943        Sub-handler for the :meth:`OnLeftUp` event.
9944
9945        :param `event`: a :class:`MouseEvent` to be processed.
9946        """
9947
9948        isPoint = False
9949        if isinstance(eventOrPt, wx.Point):
9950            clientPt = self._frame.ScreenToClient(eventOrPt)
9951            screenPt = wx.Point(*eventOrPt)
9952            isPoint = True
9953        else:
9954            clientPt = eventOrPt.GetPosition()
9955            screenPt = self._frame.ClientToScreen(clientPt)
9956
9957        # try to find the pane
9958        pane = self.GetPane(self._action_window)
9959        if not pane.IsOk():
9960            raise Exception("Pane window not found")
9961
9962        if pane.IsFloating() and pane.frame is not None:
9963            pane.floating_pos = pane.frame.GetPosition()
9964            if pane.frame._transparent != pane.transparent or self._agwFlags & AUI_MGR_TRANSPARENT_DRAG:
9965                pane.frame.SetTransparent(pane.transparent)
9966                pane.frame._transparent = pane.transparent
9967
9968        # save the new positions
9969        docks = FindDocks(self._docks, pane.dock_direction, pane.dock_layer, pane.dock_row)
9970        if len(docks) == 1:
9971            dock = docks[0]
9972            pane_positions, pane_sizes = self.GetPanePositionsAndSizes(dock)
9973
9974            for i in range(len(dock.panes)):
9975                dock.panes[i].dock_pos = pane_positions[i]
9976
9977        pane.state &= ~AuiPaneInfo.actionPane
9978        self.Update()
9979
9980
9981    def OnPaneButton(self, event):
9982        """
9983        Handles the ``EVT_AUI_PANE_BUTTON`` event for :class:`AuiManager`.
9984
9985        :param `event`: a :class:`AuiManagerEvent` event to be processed.
9986        """
9987
9988        if not event.pane:
9989            raise Exception("Pane Info passed to AuiManager.OnPaneButton must be non-null")
9990
9991        pane = event.pane
9992
9993        if event.button == AUI_BUTTON_CLOSE:
9994
9995            if isinstance(pane.window.GetParent(), AuiFloatingFrame):
9996                rootManager = GetManager(pane.window)
9997            else:
9998                rootManager = self
9999
10000            if rootManager != self:
10001                self._frame.Close()
10002                return
10003
10004            # fire pane close event
10005            e = AuiManagerEvent(wxEVT_AUI_PANE_CLOSE)
10006            e.SetManager(self)
10007            e.SetPane(event.pane)
10008            self.ProcessMgrEvent(e)
10009
10010            if not e.GetVeto():
10011
10012                # close the pane, but check that it
10013                # still exists in our pane array first
10014                # (the event handler above might have removed it)
10015
10016                check = self.GetPane(pane.window)
10017                if check.IsOk():
10018                    self.ClosePane(pane)
10019
10020                self.Update()
10021
10022        # mn this performs the minimizing of a pane
10023        elif event.button == AUI_BUTTON_MINIMIZE:
10024            e = AuiManagerEvent(wxEVT_AUI_PANE_MINIMIZE)
10025            e.SetManager(self)
10026            e.SetPane(event.pane)
10027            self.ProcessMgrEvent(e)
10028
10029            if not e.GetVeto():
10030                self.MinimizePane(pane)
10031
10032        elif event.button == AUI_BUTTON_MAXIMIZE_RESTORE and not pane.IsMaximized():
10033
10034            # fire pane close event
10035            e = AuiManagerEvent(wxEVT_AUI_PANE_MAXIMIZE)
10036            e.SetManager(self)
10037            e.SetPane(event.pane)
10038            self.ProcessMgrEvent(e)
10039
10040            if not e.GetVeto():
10041
10042                self.MaximizePane(pane)
10043                self.Update()
10044
10045        elif event.button == AUI_BUTTON_MAXIMIZE_RESTORE and pane.IsMaximized():
10046
10047            # fire pane close event
10048            e = AuiManagerEvent(wxEVT_AUI_PANE_RESTORE)
10049            e.SetManager(self)
10050            e.SetPane(event.pane)
10051            self.ProcessMgrEvent(e)
10052
10053            if not e.GetVeto():
10054
10055                self.RestorePane(pane)
10056                self.Update()
10057
10058        elif event.button == AUI_BUTTON_PIN:
10059
10060            if self._agwFlags & AUI_MGR_ALLOW_FLOATING and pane.IsFloatable():
10061                e = self.FireEvent(wxEVT_AUI_PANE_FLOATING, pane, canVeto=True)
10062                if e.GetVeto():
10063                    return
10064
10065                pane.Float()
10066                e = self.FireEvent(wxEVT_AUI_PANE_FLOATED, pane, canVeto=False)
10067
10068            self.Update()
10069
10070
10071    def MinimizePane(self, paneInfo, mgrUpdate=True):
10072        """
10073        Minimizes a pane in a newly and automatically created :class:`~wx.lib.agw.aui.auibar.AuiToolBar`.
10074
10075        Clicking on the minimize button causes a new :class:`~wx.lib.agw.aui.auibar.AuiToolBar` to be created
10076        and added to the frame manager (currently the implementation is such that
10077        panes at West will have a toolbar at the right, panes at South will have
10078        toolbars at the bottom etc...) and the pane is hidden in the manager.
10079
10080        Clicking on the restore button on the newly created toolbar will result in the
10081        toolbar being removed and the original pane being restored.
10082
10083        :param `paneInfo`: a :class:`AuiPaneInfo` instance for the pane to be minimized;
10084        :param bool `mgrUpdate`: ``True`` to call :meth:`Update` to realize the new layout,
10085         ``False`` otherwise.
10086
10087        .. note::
10088
10089           The `mgrUpdate` parameter is currently only used while loading perspectives using
10090           :meth:`LoadPerspective`, as minimized panes were not correctly taken into account before.
10091
10092        """
10093
10094        if not paneInfo.IsToolbar():
10095
10096            if paneInfo.IsMinimized() and mgrUpdate:
10097                # We are already minimized
10098                return
10099
10100            # Basically the idea is this.
10101            #
10102            # 1) create a toolbar, with a restore button
10103            #
10104            # 2) place the new toolbar in the toolbar area representative of the location of the pane
10105            #  (NORTH/SOUTH/EAST/WEST, central area always to the right)
10106            #
10107            # 3) Hide the minimizing pane
10108
10109            # personalize the toolbar style
10110
10111            tbStyle = AUI_TB_DEFAULT_STYLE
10112            posMask = paneInfo.minimize_mode & AUI_MINIMIZE_POS_MASK
10113            captMask = paneInfo.minimize_mode & AUI_MINIMIZE_CAPT_MASK
10114            dockDirection = paneInfo.dock_direction
10115            if captMask != 0:
10116                tbStyle |= AUI_TB_TEXT
10117
10118            if posMask == AUI_MINIMIZE_POS_TOOLBAR:
10119                minimize_toolbar = self.GetPane(paneInfo.minimize_target)
10120                if not minimize_toolbar.IsOk():
10121                    posMask = AUI_MINIMIZE_POS_SMART
10122                    if paneInfo.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]:
10123                        tbStyle |= AUI_TB_HORZ_LAYOUT
10124
10125                    elif paneInfo.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT, AUI_DOCK_CENTER]:
10126                        tbStyle |= AUI_TB_VERTICAL
10127                        if captMask == AUI_MINIMIZE_CAPT_SMART:
10128                            tbStyle |= AUI_TB_CLOCKWISE
10129                else:
10130                    minimize_toolbar = minimize_toolbar.window
10131
10132            elif posMask == AUI_MINIMIZE_POS_SMART:
10133                if paneInfo.dock_direction in [AUI_DOCK_TOP, AUI_DOCK_BOTTOM]:
10134                    tbStyle |= AUI_TB_HORZ_LAYOUT
10135
10136                elif paneInfo.dock_direction in [AUI_DOCK_LEFT, AUI_DOCK_RIGHT, AUI_DOCK_CENTER]:
10137                    tbStyle |= AUI_TB_VERTICAL
10138                    if captMask == AUI_MINIMIZE_CAPT_SMART:
10139                        tbStyle |= AUI_TB_CLOCKWISE
10140
10141            elif posMask in [AUI_MINIMIZE_POS_TOP, AUI_MINIMIZE_POS_BOTTOM]:
10142                tbStyle |= AUI_TB_HORZ_LAYOUT
10143                if posMask == AUI_MINIMIZE_POS_TOP:
10144                    dockDirection = AUI_DOCK_TOP
10145                else:
10146                    dockDirection = AUI_DOCK_BOTTOM
10147
10148            else:
10149                tbStyle |= AUI_TB_VERTICAL
10150                if captMask == AUI_MINIMIZE_CAPT_SMART:
10151                    tbStyle |= AUI_TB_CLOCKWISE
10152                if posMask == AUI_MINIMIZE_POS_LEFT:
10153                    dockDirection = AUI_DOCK_LEFT
10154                elif posMask == AUI_MINIMIZE_POS_RIGHT:
10155                    dockDirection = AUI_DOCK_RIGHT
10156                elif posMask == AUI_MINIMIZE_POS_BOTTOM:
10157                    dockDirection = AUI_DOCK_BOTTOM
10158
10159            # Create a new toolbar
10160            # give it the same name as the minimized pane with _min appended
10161
10162            win_rect = paneInfo.window.GetScreenRect()
10163
10164            if posMask != AUI_MINIMIZE_POS_TOOLBAR:
10165                minimize_toolbar = auibar.AuiToolBar(self.GetManagedWindow(), agwStyle=tbStyle)
10166                minimize_toolbar.Hide()
10167                minimize_toolbar.SetToolBitmapSize(wx.Size(16, 16))
10168
10169            if paneInfo.icon and paneInfo.icon.IsOk():
10170                restore_bitmap = paneInfo.icon
10171            else:
10172                restore_bitmap = self._art._restore_bitmap
10173
10174            if posMask == AUI_MINIMIZE_POS_TOOLBAR:
10175                xsize, ysize = minimize_toolbar.GetToolBitmapSize()
10176                if xsize != restore_bitmap.GetWidth():
10177                    img = restore_bitmap.ConvertToImage()
10178                    img.Rescale(xsize, ysize, wx.IMAGE_QUALITY_HIGH)
10179                    restore_bitmap = img.ConvertToBitmap()
10180
10181            target = None
10182            if posMask == AUI_MINIMIZE_POS_TOOLBAR:
10183                target = paneInfo.name
10184
10185            minimize_toolbar.AddSimpleTool(ID_RESTORE_FRAME, paneInfo.caption, restore_bitmap,
10186                                           _(six.u("Restore %s"))%paneInfo.caption, target=target)
10187            minimize_toolbar.SetAuiManager(self)
10188            minimize_toolbar.Realize()
10189            toolpanelname = paneInfo.name + "_min"
10190
10191            if paneInfo.IsMaximized():
10192                paneInfo.SetFlag(paneInfo.wasMaximized, True)
10193
10194            if posMask != AUI_MINIMIZE_POS_TOOLBAR:
10195
10196                if dockDirection == AUI_DOCK_TOP:
10197                    self.AddPane(minimize_toolbar, AuiPaneInfo(). \
10198                        Name(toolpanelname).Caption(paneInfo.caption). \
10199                        ToolbarPane().Top().BottomDockable(False). \
10200                        LeftDockable(False).RightDockable(False).DestroyOnClose())
10201
10202                elif dockDirection == AUI_DOCK_BOTTOM:
10203                    self.AddPane(minimize_toolbar, AuiPaneInfo(). \
10204                        Name(toolpanelname).Caption(paneInfo.caption). \
10205                        ToolbarPane().Bottom().TopDockable(False). \
10206                        LeftDockable(False).RightDockable(False).DestroyOnClose())
10207
10208                elif dockDirection == AUI_DOCK_LEFT:
10209                    self.AddPane(minimize_toolbar, AuiPaneInfo(). \
10210                        Name(toolpanelname).Caption(paneInfo.caption). \
10211                        ToolbarPane().Left().TopDockable(False). \
10212                        BottomDockable(False).RightDockable(False).DestroyOnClose())
10213
10214                elif dockDirection in [AUI_DOCK_RIGHT, AUI_DOCK_CENTER]:
10215                    self.AddPane(minimize_toolbar, AuiPaneInfo(). \
10216                        Name(toolpanelname).Caption(paneInfo.caption). \
10217                        ToolbarPane().Right().TopDockable(False). \
10218                        LeftDockable(False).BottomDockable(False).DestroyOnClose())
10219
10220            arr = FindDocks(self._docks, paneInfo.dock_direction, paneInfo.dock_layer, paneInfo.dock_row)
10221
10222            if arr:
10223                dock = arr[0]
10224                paneInfo.previousDockSize = dock.size
10225
10226            paneInfo.previousDockPos = paneInfo.dock_pos
10227
10228            # mark ourselves minimized
10229            paneInfo.Minimize()
10230            paneInfo.Show(False)
10231            self._has_minimized = True
10232            # last, hide the window
10233            if paneInfo.window and paneInfo.window.IsShown():
10234                paneInfo.window.Show(False)
10235
10236            minimize_toolbar.Show()
10237
10238            if mgrUpdate:
10239                self.Update()
10240                if self._agwFlags & AUI_MGR_ANIMATE_FRAMES:
10241                    self.AnimateDocking(win_rect, minimize_toolbar.GetScreenRect())
10242
10243
10244    def OnRestoreMinimizedPane(self, event):
10245        """
10246        Handles the ``EVT_AUI_PANE_MIN_RESTORE`` event for :class:`AuiManager`.
10247
10248        :param `event`: an instance of :class:`AuiManagerEvent` to be processed.
10249        """
10250
10251        self.RestoreMinimizedPane(event.pane)
10252
10253
10254    def OnPaneDocked(self, event):
10255        """
10256        Handles the ``EVT_AUI_PANE_DOCKED`` event for :class:`AuiManager`.
10257
10258        :param `event`: an instance of :class:`AuiManagerEvent` to be processed.
10259        """
10260
10261        event.Skip()
10262        self.RemoveAutoNBCaption(event.GetPane())
10263
10264
10265    def CreateNotebookBase(self, panes, paneInfo):
10266        """
10267        Creates an auto-notebook base from a pane, and then add that pane as a page.
10268
10269        :param list `panes`: set of panes to append new notebook base pane to
10270        :param `paneInfo`: the pane to be converted to a new notebook, an instance of
10271         :class:`AuiPaneInfo`.
10272        """
10273
10274        # Create base notebook pane ...
10275        nbid = len(self._notebooks)
10276
10277        baseInfo = AuiPaneInfo()
10278        baseInfo.SetDockPos(paneInfo).NotebookControl(nbid). \
10279            CloseButton(False).SetNameFromNotebookId(). \
10280            NotebookDockable(False).Floatable(paneInfo.IsFloatable())
10281        baseInfo.best_size = paneInfo.best_size
10282        panes.append(baseInfo)
10283
10284        # add original pane as tab ...
10285        paneInfo.NotebookPage(nbid)
10286
10287
10288    def RemoveAutoNBCaption(self, pane):
10289        """
10290        Removes the caption on newly created automatic notebooks.
10291
10292        :param `pane`: an instance of :class:`AuiPaneInfo` (the target notebook).
10293        """
10294
10295        if self._agwFlags & AUI_MGR_AUTONB_NO_CAPTION == 0:
10296            return False
10297
10298        def RemoveCaption():
10299            """ Sub-function used to remove the pane caption on automatic notebooks. """
10300
10301            if pane.HasNotebook():
10302                notebook = self._notebooks[pane.notebook_id]
10303                self.GetPane(notebook).CaptionVisible(False).PaneBorder(False)
10304                self.Update()
10305
10306        # it seems the notebook isnt created by this stage, so remove
10307        # the caption a moment later
10308        wx.CallAfter(RemoveCaption)
10309        return True
10310
10311
10312    def RestoreMinimizedPane(self, paneInfo):
10313        """
10314        Restores a previously minimized pane.
10315
10316        :param `paneInfo`: a :class:`AuiPaneInfo` instance for the pane to be restored.
10317        """
10318
10319        panename = paneInfo.name
10320
10321        if paneInfo.minimize_mode & AUI_MINIMIZE_POS_TOOLBAR:
10322            pane = self.GetPane(panename)
10323            hasTarget = True
10324        else:
10325            panename = panename[0:-4]
10326            hasTarget = False
10327
10328        pane = self.GetPane(panename)
10329        pane.SetFlag(pane.needsRestore, True)
10330
10331        if not pane.IsOk():
10332            panename = paneInfo.name
10333            pane = self.GetPane(panename)
10334            paneInfo = self.GetPane(panename + "_min")
10335            if not paneInfo.IsOk():
10336                # Already minimized
10337                return
10338
10339        if pane.IsOk():
10340            if not pane.IsMinimized():
10341                return
10342
10343
10344            if pane.HasFlag(pane.wasMaximized):
10345                self.SavePreviousDockSizes(pane)
10346
10347            self.ShowPane(pane.window, True)
10348            pane.Show(True)
10349            self._has_minimized = False
10350            pane.SetFlag(pane.optionMinimized, False)
10351
10352            if hasTarget:
10353                targetName = pane.minimize_target
10354                toolbarPane = self.GetPane(targetName)
10355                toolbar = toolbarPane.window
10356                item = toolbar.FindToolByLabel(pane.caption)
10357                toolbar.DeleteTool(item.id)
10358            else:
10359                paneInfo.window.Show(False)
10360                self.DetachPane(paneInfo.window)
10361                paneInfo.Show(False)
10362                paneInfo.Hide()
10363
10364            self.Update()
10365
10366
10367    def AnimateDocking(self, win_rect, pane_rect):
10368        """
10369        Animates the minimization/docking of a pane a la Eclipse, using a :class:`ScreenDC`
10370        to draw a "moving docking rectangle" on the screen.
10371
10372        :param wx.Rect `win_rect`: the original pane screen rectangle;
10373        :param wx.Rect `pane_rect`: the newly created toolbar/pane screen rectangle.
10374
10375        :note: This functionality is not available on wxMAC as this platform doesn't have
10376         the ability to use :class:`ScreenDC` to draw on-screen and on Windows > Vista.
10377        """
10378
10379        if wx.Platform == "__WXMAC__":
10380            # No wx.ScreenDC on the Mac...
10381            return
10382        if wx.Platform == "__WXMSW__" and wx.GetOsVersion()[1] > 5:
10383            # No easy way to handle this on Vista...
10384            return
10385
10386        xstart, ystart = win_rect.x, win_rect.y
10387        xend, yend = pane_rect.x, pane_rect.y
10388
10389        step = self.GetAnimationStep()
10390
10391        wstep = int(abs(win_rect.width - pane_rect.width)/step)
10392        hstep = int(abs(win_rect.height - pane_rect.height)/step)
10393        xstep = int(win_rect.x - pane_rect.x)/step
10394        ystep = int(win_rect.y - pane_rect.y)/step
10395
10396        dc = wx.ScreenDC()
10397        dc.SetLogicalFunction(wx.INVERT)
10398        dc.SetBrush(wx.TRANSPARENT_BRUSH)
10399        dc.SetPen(wx.LIGHT_GREY_PEN)
10400
10401        for i in range(int(step)):
10402            width, height = win_rect.width - i*wstep, win_rect.height - i*hstep
10403            x, y = xstart - i*xstep, ystart - i*ystep
10404            new_rect = wx.Rect(x, y, width, height)
10405            dc.DrawRoundedRectangle(new_rect, 3)
10406            wx.SafeYield()
10407            wx.MilliSleep(10)
10408            dc.DrawRoundedRectangle(new_rect, 3)
10409
10410
10411    def SmoothDock(self, paneInfo):
10412        """
10413        This method implements a smooth docking effect for floating panes, similar to
10414        what the PyQT library does with its floating windows.
10415
10416        :param `paneInfo`: an instance of :class:`AuiPaneInfo`.
10417
10418        :note: The smooth docking effect can only be used if you set the ``AUI_MGR_SMOOTH_DOCKING``
10419         style to :class:`AuiManager`.
10420        """
10421
10422        if paneInfo.IsToolbar():
10423            return
10424
10425        if not paneInfo.frame or self._hint_rect.IsEmpty():
10426            return
10427
10428        hint_rect = self._hint_rect
10429        win_rect = paneInfo.frame.GetScreenRect()
10430
10431        xstart, ystart = win_rect.x, win_rect.y
10432        xend, yend = hint_rect.x, hint_rect.y
10433
10434        step = self.GetAnimationStep()/3
10435
10436        wstep = int((win_rect.width - hint_rect.width)/step)
10437        hstep = int((win_rect.height - hint_rect.height)/step)
10438        xstep = int((win_rect.x - hint_rect.x))/step
10439        ystep = int((win_rect.y - hint_rect.y))/step
10440
10441        for i in range(int(step)):
10442            width, height = win_rect.width - i*wstep, win_rect.height - i*hstep
10443            x, y = xstart - i*xstep, ystart - i*ystep
10444            new_rect = wx.Rect(x, y, width, height)
10445            paneInfo.frame.SetRect(new_rect)
10446            wx.MilliSleep(10)
10447
10448
10449    def SetSnapLimits(self, x, y):
10450        """
10451        Modifies the snap limits used when snapping the `managed_window` to the screen
10452        (using :meth:`SnapToScreen`) or when snapping the floating panes to one side of the
10453        `managed_window` (using :meth:`SnapPane`).
10454
10455        To change the limit after which the `managed_window` or the floating panes are
10456        automatically stickled to the screen border (or to the `managed_window` side),
10457        set these two variables. Default values are 15 pixels.
10458
10459        :param integer `x`: the minimum horizontal distance below which the snap occurs;
10460        :param integer `y`: the minimum vertical distance below which the snap occurs.
10461        """
10462
10463        self._snap_limits = (x, y)
10464        self.Snap()
10465
10466
10467    def Snap(self):
10468        """
10469        Snaps the main frame to specified position on the screen.
10470
10471        :see: :meth:`SnapToScreen`
10472        """
10473
10474        snap, hAlign, vAlign, monitor = self._is_docked
10475        if not snap:
10476            return
10477
10478        managed_window = self.GetManagedWindow()
10479        snap_pos = self.GetSnapPosition()
10480        wnd_pos = managed_window.GetPosition()
10481        snapX, snapY = self._snap_limits
10482
10483        if abs(snap_pos.x - wnd_pos.x) < snapX and abs(snap_pos.y - wnd_pos.y) < snapY:
10484            managed_window.SetPosition(snap_pos)
10485
10486
10487    def SnapToScreen(self, snap=True, monitor=0, hAlign=wx.RIGHT, vAlign=wx.TOP):
10488        """
10489        Snaps the main frame to specified position on the screen.
10490
10491        :param bool `snap`: whether to snap the main frame or not;
10492        :param integer `monitor`: the monitor display in which snapping the window;
10493        :param integer `hAlign`: the horizontal alignment of the snapping position;
10494        :param integer `vAlign`: the vertical alignment of the snapping position.
10495        """
10496
10497        if not snap:
10498            self._is_docked = (False, wx.RIGHT, wx.TOP, 0)
10499            return
10500
10501        displayCount = wx.Display.GetCount()
10502        if monitor > displayCount:
10503            raise Exception("Invalid monitor selected: you only have %d monitors"%displayCount)
10504
10505        self._is_docked = (True, hAlign, vAlign, monitor)
10506        self.GetManagedWindow().SetPosition(self.GetSnapPosition())
10507
10508
10509    def GetSnapPosition(self):
10510        """ Returns the main frame snapping position. """
10511
10512        snap, hAlign, vAlign, monitor = self._is_docked
10513
10514        display = wx.Display(monitor)
10515        area = display.GetClientArea()
10516        size = self.GetManagedWindow().GetSize()
10517
10518        pos = wx.Point()
10519        if hAlign == wx.LEFT:
10520            pos.x = area.x
10521        elif hAlign == wx.CENTER:
10522            pos.x = area.x + (area.width - size.x)/2
10523        else:
10524            pos.x = area.x + area.width - size.x
10525
10526        if vAlign == wx.TOP:
10527            pos.y = area.y
10528        elif vAlign == wx.CENTER:
10529            pos.y = area.y + (area.height - size.y)/2
10530        else:
10531            pos.y = area.y + area.height - size.y
10532
10533        return pos
10534
10535
10536    def GetAnimationStep(self):
10537        """ Returns the animation step speed (a float) to use in :meth:`AnimateDocking`. """
10538
10539        return self._animation_step
10540
10541
10542    def SetAnimationStep(self, step):
10543        """
10544        Sets the animation step speed (a float) to use in :meth:`AnimateDocking`.
10545
10546        :param float `step`: the animation speed.
10547        """
10548
10549        self._animation_step = float(step)
10550
10551
10552    def RequestUserAttention(self, pane_window):
10553        """
10554        Requests the user attention by intermittently highlighting the pane caption.
10555
10556        :param wx.Window `pane_window`: the window managed by the pane;
10557        """
10558
10559        # try to find the pane
10560        paneInfo = self.GetPane(pane_window)
10561        if not paneInfo.IsOk():
10562            raise Exception("Pane window not found")
10563
10564        dc = wx.ClientDC(self._frame)
10565
10566        # if the frame is about to be deleted, don't bother
10567        if not self._frame or self._frame.IsBeingDeleted():
10568            return
10569
10570        if not self._frame.GetSizer():
10571            return
10572
10573        for part in self._uiparts:
10574            if part.pane == paneInfo:
10575                self._art.RequestUserAttention(dc, self._frame, part.pane.caption, part.rect, part.pane)
10576                self._frame.RefreshRect(part.rect, True)
10577                break
10578
10579
10580    def StartPreviewTimer(self, toolbar):
10581        """
10582        Starts a timer for sliding in and out a minimized pane.
10583
10584        :param `toolbar`: the :class:`~wx.lib.agw.aui.auibar.AuiToolBar` containing the minimized pane tool.
10585        """
10586
10587        toolbar_pane = self.GetPane(toolbar)
10588        toolbar_name = toolbar_pane.name
10589
10590        pane_name = toolbar_name[0:-4]
10591
10592        self._sliding_pane = self.GetPane(pane_name)
10593        self._sliding_rect = toolbar.GetScreenRect()
10594        self._sliding_direction = toolbar_pane.dock_direction
10595        self._sliding_frame = None
10596
10597        self._preview_timer.Start(1000, wx.TIMER_ONE_SHOT)
10598
10599
10600    def StopPreviewTimer(self):
10601        """ Stops a timer for sliding in and out a minimized pane. """
10602
10603        if self._preview_timer.IsRunning():
10604            self._preview_timer.Stop()
10605
10606        self.SlideOut()
10607        self._sliding_pane = None
10608
10609
10610    def SlideIn(self, event):
10611        """
10612        Handles the ``wx.EVT_TIMER`` event for :class:`AuiManager`.
10613
10614        :param `event`: a :class:`TimerEvent` to be processed.
10615
10616        :note: This is used solely for sliding in and out minimized panes.
10617        """
10618
10619        window = self._sliding_pane.window
10620        self._sliding_frame = wx.MiniFrame(None, -1, title=_("Pane Preview"),
10621                                           style=wx.FRAME_TOOL_WINDOW | wx.STAY_ON_TOP |
10622                                           wx.FRAME_NO_TASKBAR | wx.CAPTION)
10623        window.Reparent(self._sliding_frame)
10624        self._sliding_frame.SetSize((0, 0))
10625        window.Show()
10626        self._sliding_frame.Show()
10627
10628        size = window.GetBestSize()
10629
10630        startX, startY, stopX, stopY = GetSlidingPoints(self._sliding_rect, size, self._sliding_direction)
10631
10632        step = stopX/10
10633        window_size = 0
10634
10635        for i in range(0, stopX, step):
10636            window_size = i
10637            self._sliding_frame.SetSize(startX, startY, window_size, stopY)
10638            self._sliding_frame.Refresh()
10639            self._sliding_frame.Update()
10640            wx.MilliSleep(10)
10641
10642        self._sliding_frame.SetSize(startX, startY, stopX, stopY)
10643        self._sliding_frame.Refresh()
10644        self._sliding_frame.Update()
10645
10646
10647    def SlideOut(self):
10648        """
10649        Slides out a preview of a minimized pane.
10650
10651        :note: This is used solely for sliding in and out minimized panes.
10652        """
10653
10654        if not self._sliding_frame:
10655            return
10656
10657        window = self._sliding_frame.GetChildren()[0]
10658        size = window.GetBestSize()
10659
10660        startX, startY, stopX, stopY = GetSlidingPoints(self._sliding_rect, size, self._sliding_direction)
10661
10662        step = stopX/10
10663        window_size = 0
10664
10665        for i in range(stopX, 0, -step):
10666            window_size = i
10667            self._sliding_frame.SetSize(startX, startY, window_size, stopY)
10668            self._sliding_frame.Refresh()
10669            self._sliding_frame.Update()
10670            self._frame.RefreshRect(wx.Rect(startX+window_size, startY, step, stopY))
10671            self._frame.Update()
10672            wx.MilliSleep(10)
10673
10674        self._sliding_frame.SetSize(startX, startY, 0, stopY)
10675
10676        window.Hide()
10677        window.Reparent(self._frame)
10678
10679        self._sliding_frame.Hide()
10680        self._sliding_frame.Destroy()
10681        self._sliding_frame = None
10682        self._sliding_pane = None
10683
10684
10685class AuiManager_DCP(AuiManager):
10686    """
10687    A class similar to :class:`AuiManager` but with a Dummy Center Pane (**DCP**).
10688    The code for this class is still flickery due to the call to :func:`CallAfter`
10689    and the double-update call.
10690    """
10691
10692    def __init__(self, *args, **keys):
10693        """ See :meth:`AuiManager.__init__` for the class construction. """
10694
10695        AuiManager.__init__(self, *args, **keys)
10696        self.hasDummyPane = False
10697
10698
10699    def _createDummyPane(self):
10700        """ Creates a Dummy Center Pane (**DCP**). """
10701
10702        if self.hasDummyPane:
10703            return
10704
10705        self.hasDummyPane = True
10706        dummy = wx.Panel(self.GetManagedWindow())
10707        info = AuiPaneInfo().CenterPane().NotebookDockable(True).Name('dummyCenterPane').DestroyOnClose(True)
10708        self.AddPane(dummy, info)
10709
10710
10711    def _destroyDummyPane(self):
10712        """ Destroys the Dummy Center Pane (**DCP**). """
10713
10714        if not self.hasDummyPane:
10715            return
10716
10717        self.hasDummyPane = False
10718        self.ClosePane(self.GetPane('dummyCenterPane'))
10719
10720
10721    def Update(self):
10722        """
10723        This method is called after any number of changes are made to any of the
10724        managed panes. :meth:`Update` must be invoked after :meth:`AuiManager.AddPane` or
10725        :meth:`AuiManager.InsertPane` are called in order to "realize" or "commit" the changes.
10726
10727        In addition, any number of changes may be made to :class:`AuiManager` structures
10728        (retrieved with :meth:`AuiManager.GetPane`), but to realize the changes,
10729        :meth:`Update` must be called. This construction allows pane flicker to
10730        be avoided by updating the whole layout at one time.
10731        """
10732
10733        AuiManager.Update(self)
10734
10735        # check if there's already a center pane (except our dummy pane)
10736        dummyCenterPane = self.GetPane('dummyCenterPane')
10737        haveCenterPane = any((pane != dummyCenterPane) and (pane.dock_direction == AUI_DOCK_CENTER) and
10738                             not pane.IsFloating() and pane.IsShown() for pane in self.GetAllPanes())
10739        if haveCenterPane:
10740            if self.hasDummyPane:
10741                # there's our dummy pane and also another center pane, therefor let's remove our dummy
10742                def do():
10743                    self._destroyDummyPane()
10744                    self.Update()
10745                wx.CallAfter(do)
10746        else:
10747            # if we get here, there's no center pane, create our dummy
10748            if not self.hasDummyPane:
10749                self._createDummyPane()
10750
10751
10752