2 auibook contains a notebook control which implements many features common in
3 applications with dockable panes. Specifically, L{AuiNotebook} implements functionality
4 which allows the user to rearrange tab order via drag-and-drop, split the tab window
5 into many different splitter configurations, and toggle through different themes to
6 customize the control's look and feel.
8 An effort has been made to try to maintain an API as similar to that of `wx.Notebook`.
10 The default theme that is used is L{AuiDefaultTabArt}, which provides a modern, glossy
11 look and feel. The theme can be changed by calling L{AuiNotebook.SetArtProvider}.
14 __author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
15 __date__ = "31 March 2009"
22 from wx.lib.expando import ExpandoTextCtrl
27 from aui_utilities import LightColour, MakeDisabledBitmap, TabDragImage
28 from aui_utilities import TakeScreenShot, RescaleScreenShot
30 from aui_constants import *
33 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE = wx.NewEventType()
34 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED = wx.NewEventType()
35 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED = wx.NewEventType()
36 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING = wx.NewEventType()
37 wxEVT_COMMAND_AUINOTEBOOK_BUTTON = wx.NewEventType()
38 wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG = wx.NewEventType()
39 wxEVT_COMMAND_AUINOTEBOOK_END_DRAG = wx.NewEventType()
40 wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION = wx.NewEventType()
41 wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND = wx.NewEventType()
42 wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE = wx.NewEventType()
43 wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN = wx.NewEventType()
44 wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP = wx.NewEventType()
45 wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN = wx.NewEventType()
46 wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP = wx.NewEventType()
47 wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK = wx.NewEventType()
48 wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN = wx.NewEventType()
49 wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP = wx.NewEventType()
50 wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN = wx.NewEventType()
51 wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP = wx.NewEventType()
52 wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK = wx.NewEventType()
54 # Define a new event for a drag cancelled
55 wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG = wx.NewEventType()
57 # Define events for editing a tab label
58 wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT = wx.NewEventType()
59 wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT = wx.NewEventType()
61 # Create event binders
62 EVT_AUINOTEBOOK_PAGE_CLOSE = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 1)
63 """ A tab in `AuiNotebook` is being closed. Can be vetoed by calling `Veto()`. """
64 EVT_AUINOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 1)
65 """ A tab in `AuiNotebook` has been closed. """
66 EVT_AUINOTEBOOK_PAGE_CHANGED = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 1)
67 """ The page selection was changed. """
68 EVT_AUINOTEBOOK_PAGE_CHANGING = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 1)
69 """ The page selection is being changed. """
70 EVT_AUINOTEBOOK_BUTTON = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 1)
71 """ The user clicked on a button in the `AuiNotebook` tab area. """
72 EVT_AUINOTEBOOK_BEGIN_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 1)
73 """ A drag-and-drop operation on a notebook tab has started. """
74 EVT_AUINOTEBOOK_END_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 1)
75 """ A drag-and-drop operation on a notebook tab has finished. """
76 EVT_AUINOTEBOOK_DRAG_MOTION = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 1)
77 """ A drag-and-drop operation on a notebook tab is ongoing. """
78 EVT_AUINOTEBOOK_ALLOW_DND = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 1)
79 """ Fires an event asking if it is OK to drag and drop a tab. """
80 EVT_AUINOTEBOOK_DRAG_DONE = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 1)
81 """ A drag-and-drop operation on a notebook tab has finished. """
82 EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 1)
83 """ The user clicked with the middle mouse button on a tab. """
84 EVT_AUINOTEBOOK_TAB_MIDDLE_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 1)
85 """ The user clicked with the middle mouse button on a tab. """
86 EVT_AUINOTEBOOK_TAB_RIGHT_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 1)
87 """ The user clicked with the right mouse button on a tab. """
88 EVT_AUINOTEBOOK_TAB_RIGHT_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 1)
89 """ The user clicked with the right mouse button on a tab. """
90 EVT_AUINOTEBOOK_BG_MIDDLE_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN, 1)
91 """ The user middle-clicked in the tab area but not over a tab or a button. """
92 EVT_AUINOTEBOOK_BG_MIDDLE_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP, 1)
93 """ The user middle-clicked in the tab area but not over a tab or a button. """
94 EVT_AUINOTEBOOK_BG_RIGHT_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN, 1)
95 """ The user right-clicked in the tab area but not over a tab or a button. """
96 EVT_AUINOTEBOOK_BG_RIGHT_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP, 1)
97 """ The user right-clicked in the tab area but not over a tab or a button. """
98 EVT_AUINOTEBOOK_BG_DCLICK = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 1)
99 """ The user left-clicked on the tab area not occupied by `AuiNotebook` tabs. """
100 EVT_AUINOTEBOOK_CANCEL_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG, 1)
101 """ A drag and drop operation has been cancelled. """
102 EVT_AUINOTEBOOK_TAB_DCLICK = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, 1)
103 """ The user double-clicked with the left mouse button on a tab. """
104 EVT_AUINOTEBOOK_BEGIN_LABEL_EDIT = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT, 1)
105 """ The user double-clicked with the left mouse button on a tab which text is editable. """
106 EVT_AUINOTEBOOK_END_LABEL_EDIT = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, 1)
107 """ The user finished editing a tab label. """
110 # -----------------------------------------------------------------------------
111 # Auxiliary class: TabTextCtrl
112 # This is the temporary ExpandoTextCtrl created when you edit the text of a tab
113 # -----------------------------------------------------------------------------
115 class TabTextCtrl(ExpandoTextCtrl):
116 """ Control used for in-place edit. """
118 def __init__(self, owner, tab, page_index):
120 Default class constructor.
121 For internal use: do not call it in your code!
123 :param `owner`: the L{AuiTabCtrl} owning the tab;
124 :param `tab`: the actual L{AuiNotebookPage} tab;
125 :param `page_index`: the L{AuiNotebook} page index for the tab.
129 self._tabEdited = tab
130 self._pageIndex = page_index
131 self._startValue = tab.caption
132 self._finished = False
133 self._aboutToFinish = False
134 self._currentValue = self._startValue
136 x, y, w, h = self._tabEdited.rect
138 wnd = self._tabEdited.control
140 x += wnd.GetSize()[0] + 2
149 image_w, image_h = image.GetWidth(), image.GetHeight()
152 dc = wx.ClientDC(self._owner)
153 h = max(image_h, dc.GetMultiLineTextExtent(tab.caption)[1])
156 # FIXME: what are all these hardcoded 4, 8 and 11s really?
160 y = (self._tabEdited.rect.height - h)/2 + 1
162 expandoStyle = wx.WANTS_CHARS
163 if wx.Platform in ["__WXGTK__", "__WXMAC__"]:
164 expandoStyle |= wx.SIMPLE_BORDER
165 xSize, ySize = w + 2, h
167 expandoStyle |= wx.SUNKEN_BORDER
168 xSize, ySize = w + 2, h+2
170 ExpandoTextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue,
171 wx.Point(x, y), wx.Size(xSize, ySize),
174 if wx.Platform == "__WXMAC__":
175 self.SetFont(owner.GetFont())
176 bs = self.GetBestSize()
177 self.SetSize((-1, bs.height))
179 self.Bind(wx.EVT_CHAR, self.OnChar)
180 self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
181 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
184 def AcceptChanges(self):
185 """ Accepts/refuses the changes made by the user. """
187 value = self.GetValue()
188 notebook = self._owner.GetParent()
190 if value == self._startValue:
191 # nothing changed, always accept
192 # when an item remains unchanged, the owner
193 # needs to be notified that the user decided
194 # not to change the tree item label, and that
195 # the edit has been cancelled
196 notebook.OnRenameCancelled(self._pageIndex)
199 if not notebook.OnRenameAccept(self._pageIndex, value):
203 # accepted, do rename the item
204 notebook.SetPageText(self._pageIndex, value)
210 """ Finish editing. """
212 if not self._finished:
214 notebook = self._owner.GetParent()
216 self._finished = True
217 self._owner.SetFocus()
218 notebook.ResetTextControl()
221 def OnChar(self, event):
223 Handles the ``wx.EVT_CHAR`` event for L{TabTextCtrl}.
225 :param `event`: a `wx.KeyEvent` event to be processed.
228 keycode = event.GetKeyCode()
229 shiftDown = event.ShiftDown()
231 if keycode == wx.WXK_RETURN:
232 if shiftDown and self._tabEdited.IsMultiline():
235 self._aboutToFinish = True
236 self.SetValue(self._currentValue)
237 # Notify the owner about the changes
239 # Even if vetoed, close the control (consistent with MSW)
240 wx.CallAfter(self.Finish)
242 elif keycode == wx.WXK_ESCAPE:
249 def OnKeyUp(self, event):
251 Handles the ``wx.EVT_KEY_UP`` event for L{TabTextCtrl}.
253 :param `event`: a `wx.KeyEvent` event to be processed.
256 if not self._finished:
258 # auto-grow the textctrl:
259 mySize = self.GetSize()
261 dc = wx.ClientDC(self)
262 sx, sy, dummy = dc.GetMultiLineTextExtent(self.GetValue() + "M")
264 self.SetSize((sx, -1))
265 self._currentValue = self.GetValue()
270 def OnKillFocus(self, event):
272 Handles the ``wx.EVT_KILL_FOCUS`` event for L{TabTextCtrl}.
274 :param `event`: a `wx.FocusEvent` event to be processed.
277 if not self._finished and not self._aboutToFinish:
279 # We must finish regardless of success, otherwise we'll get
281 if not self.AcceptChanges():
282 self._owner.GetParent().OnRenameCancelled(self._pageIndex)
284 # We must let the native text control handle focus, too, otherwise
285 # it could have problems with the cursor (e.g., in wxGTK).
287 wx.CallAfter(self._owner.GetParent().ResetTextControl)
290 def StopEditing(self):
291 """ Suddenly stops the editing. """
293 self._owner.GetParent().OnRenameCancelled(self._pageIndex)
298 """ Returns the item currently edited. """
300 return self._tabEdited
303 # ----------------------------------------------------------------------
305 class AuiNotebookPage(object):
307 A simple class which holds information about tab captions, bitmaps and
313 Default class constructor.
314 Used internally, do not call it in your code!
317 self.window = None # page's associated window
318 self.caption = "" # caption displayed on the tab
319 self.bitmap = wx.NullBitmap # tab's bitmap
320 self.dis_bitmap = wx.NullBitmap # tab's disabled bitmap
321 self.rect = wx.Rect() # tab's hit rectangle
322 self.active = False # True if the page is currently active
323 self.enabled = True # True if the page is currently enabled
324 self.hasCloseButton = True # True if the page has a close button using the style
325 # AUI_NB_CLOSE_ON_ALL_TABS
326 self.control = None # A control can now be inside a tab
327 self.renamable = False # If True, a tab can be renamed by a left double-click
329 self.text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT)
331 self.access_time = datetime.datetime.now() # Last time this page was selected
334 def IsMultiline(self):
335 """ Returns whether the tab contains multiline text. """
337 return "\n" in self.caption
340 # ----------------------------------------------------------------------
342 class AuiTabContainerButton(object):
344 A simple class which holds information about tab buttons and their state.
349 Default class constructor.
350 Used internally, do not call it in your code!
353 self.id = -1 # button's id
354 self.cur_state = AUI_BUTTON_STATE_NORMAL # current state (normal, hover, pressed, etc.)
355 self.location = wx.LEFT # buttons location (wxLEFT, wxRIGHT, or wxCENTER)
356 self.bitmap = wx.NullBitmap # button's hover bitmap
357 self.dis_bitmap = wx.NullBitmap # button's disabled bitmap
358 self.rect = wx.Rect() # button's hit rectangle
361 # ----------------------------------------------------------------------
363 class CommandNotebookEvent(wx.PyCommandEvent):
364 """ A specialized command event class for events sent by L{AuiNotebook} . """
366 def __init__(self, command_type=None, win_id=0):
368 Default class constructor.
370 :param `command_type`: the event kind or an instance of `wx.PyCommandEvent`.
371 :param `win_id`: the window identification number.
374 if type(command_type) == types.IntType:
375 wx.PyCommandEvent.__init__(self, command_type, win_id)
377 wx.PyCommandEvent.__init__(self, command_type.GetEventType(), command_type.GetId())
379 self.old_selection = -1
381 self.drag_source = None
384 self.editCancelled = False
387 def SetSelection(self, s):
389 Sets the selection member variable.
391 :param `s`: the new selection.
398 def GetSelection(self):
399 """ Returns the currently selected page, or -1 if none was selected. """
401 return self.selection
404 def SetOldSelection(self, s):
406 Sets the id of the page selected before the change.
408 :param `s`: the old selection.
411 self.old_selection = s
414 def GetOldSelection(self):
416 Returns the page that was selected before the change, or -1 if none was
420 return self.old_selection
423 def SetDragSource(self, s):
425 Sets the drag and drop source.
427 :param `s`: the drag source.
433 def GetDragSource(self):
434 """ Returns the drag and drop source. """
436 return self.drag_source
439 def SetDispatched(self, b):
441 Sets the event as dispatched (used for automatic L{AuiNotebook} ).
443 :param `b`: whether the event was dispatched or not.
449 def GetDispatched(self):
450 """ Returns whether the event was dispatched (used for automatic L{AuiNotebook} ). """
452 return self.dispatched
455 def IsEditCancelled(self):
456 """ Returns the edit cancel flag (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only)."""
458 return self.editCancelled
461 def SetEditCanceled(self, editCancelled):
463 Sets the edit cancel flag (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only).
465 :param `editCancelled`: whether the editing action has been cancelled or not.
468 self.editCancelled = editCancelled
472 """Returns the label-itemtext (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only)."""
477 def SetLabel(self, label):
479 Sets the label. Useful only for ``EVT_AUINOTEBOOK_END_LABEL_EDIT``.
481 :param `label`: the new label.
487 # ----------------------------------------------------------------------
489 class AuiNotebookEvent(CommandNotebookEvent):
490 """ A specialized command event class for events sent by L{AuiNotebook}. """
492 def __init__(self, command_type=None, win_id=0):
494 Default class constructor.
496 :param `command_type`: the event kind or an instance of `wx.PyCommandEvent`.
497 :param `win_id`: the window identification number.
500 CommandNotebookEvent.__init__(self, command_type, win_id)
502 if type(command_type) == types.IntType:
503 self.notify = wx.NotifyEvent(command_type, win_id)
505 self.notify = wx.NotifyEvent(command_type.GetEventType(), command_type.GetId())
508 def GetNotifyEvent(self):
509 """ Returns the actual `wx.NotifyEvent`. """
515 """ Returns whether the event is allowed or not. """
517 return self.notify.IsAllowed()
522 Prevents the change announced by this event from happening.
524 It is in general a good idea to notify the user about the reasons for
525 vetoing the change because otherwise the applications behaviour (which
526 just refuses to do what the user wants) might be quite surprising.
534 This is the opposite of L{Veto}: it explicitly allows the event to be
535 processed. For most events it is not necessary to call this method as the
536 events are allowed anyhow but some are forbidden by default (this will
537 be mentioned in the corresponding event description).
543 # ---------------------------------------------------------------------------- #
544 # Class TabNavigatorWindow
545 # ---------------------------------------------------------------------------- #
547 class TabNavigatorWindow(wx.Dialog):
549 This class is used to create a modal dialog that enables "Smart Tabbing",
550 similar to what you would get by hitting ``Alt`` + ``Tab`` on Windows.
553 def __init__(self, parent=None, icon=None):
555 Default class constructor. Used internally.
557 :param `parent`: the L{TabNavigatorWindow} parent;
558 :param `icon`: the L{TabNavigatorWindow} icon.
561 wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)
563 self._selectedItem = -1
567 self._bmp = Mondrian.GetBitmap()
571 if self._bmp.GetSize() != (16, 16):
572 img = self._bmp.ConvertToImage()
573 img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
574 self._bmp = wx.BitmapFromImage(img)
576 sz = wx.BoxSizer(wx.VERTICAL)
578 self._listBox = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, 150), [], wx.LB_SINGLE | wx.NO_BORDER)
580 mem_dc = wx.MemoryDC()
581 mem_dc.SelectObject(wx.EmptyBitmap(1,1))
582 font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
583 font.SetWeight(wx.BOLD)
586 panelHeight = mem_dc.GetCharHeight()
587 panelHeight += 4 # Place a spacer of 2 pixels
589 # Out signpost bitmap is 24 pixels
593 self._panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, panelHeight))
596 sz.Add(self._listBox, 1, wx.EXPAND)
600 # Connect events to the list box
601 self._listBox.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
602 self._listBox.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
603 self._listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnItemSelected)
605 # Connect paint event to the panel
606 self._panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)
607 self._panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnPanelEraseBg)
609 self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
610 self._listBox.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
611 self.PopulateListControl(parent)
613 self.GetSizer().Fit(self)
614 self.GetSizer().SetSizeHints(self)
615 self.GetSizer().Layout()
618 # Set focus on the list box to avoid having to click on it to change
619 # the tab selection under GTK.
620 self._listBox.SetFocus()
623 def OnKeyUp(self, event):
625 Handles the ``wx.EVT_KEY_UP`` for the L{TabNavigatorWindow}.
627 :param `event`: a `wx.KeyEvent` event to be processed.
630 if event.GetKeyCode() == wx.WXK_CONTROL:
634 def OnNavigationKey(self, event):
636 Handles the ``wx.EVT_NAVIGATION_KEY`` for the L{TabNavigatorWindow}.
638 :param `event`: a `wx.NavigationKeyEvent` event to be processed.
641 selected = self._listBox.GetSelection()
642 bk = self.GetParent()
643 maxItems = bk.GetPageCount()
645 if event.GetDirection():
648 if selected == maxItems - 1:
651 itemToSelect = selected + 1
657 itemToSelect = maxItems - 1
659 itemToSelect = selected - 1
661 self._listBox.SetSelection(itemToSelect)
664 def PopulateListControl(self, book):
666 Populates the L{TabNavigatorWindow} listbox with a list of tabs.
668 :param `book`: the actual L{AuiNotebook}.
670 # Index of currently selected page
671 selection = book.GetSelection()
672 # Total number of pages
673 count = book.GetPageCount()
674 # List of (index, AuiNotebookPage)
675 pages = list(enumerate(book.GetTabContainer().GetPages()))
676 if book.GetAGWWindowStyleFlag() & AUI_NB_ORDER_BY_ACCESS:
677 # Sort pages using last access time. Most recently used is the
680 key = lambda element: element[1].access_time,
684 # Manually add the current selection as first item
685 # Remaining ones are added in the next loop
687 self._listBox.Append(book.GetPageText(selection))
688 self._indexMap.append(selection)
690 for (index, page) in pages:
691 self._listBox.Append(book.GetPageText(index))
692 self._indexMap.append(index)
694 # Select the next entry after the current selection
695 self._listBox.SetSelection(0)
696 dummy = wx.NavigationKeyEvent()
697 dummy.SetDirection(True)
698 self.OnNavigationKey(dummy)
701 def OnItemSelected(self, event):
703 Handles the ``wx.EVT_LISTBOX_DCLICK`` event for the wx.ListBox inside L{TabNavigatorWindow}.
705 :param `event`: a `wx.ListEvent` event to be processed.
711 def CloseDialog(self):
712 """ Closes the L{TabNavigatorWindow} dialog, setting selection in L{AuiNotebook}. """
714 bk = self.GetParent()
715 self._selectedItem = self._listBox.GetSelection()
716 self.EndModal(wx.ID_OK)
719 def GetSelectedPage(self):
720 """ Gets the page index that was selected when the dialog was closed. """
722 return self._indexMap[self._selectedItem]
725 def OnPanelPaint(self, event):
727 Handles the ``wx.EVT_PAINT`` event for L{TabNavigatorWindow} top panel.
729 :param `event`: a `wx.PaintEvent` event to be processed.
732 dc = wx.PaintDC(self._panel)
733 rect = self._panel.GetClientRect()
735 bmp = wx.EmptyBitmap(rect.width, rect.height)
737 mem_dc = wx.MemoryDC()
738 mem_dc.SelectObject(bmp)
740 endColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
741 startColour = LightColour(endColour, 50)
742 mem_dc.GradientFillLinear(rect, startColour, endColour, wx.SOUTH)
744 # Draw the caption title and place the bitmap
745 # get the bitmap optimal position, and draw it
746 bmpPt, txtPt = wx.Point(), wx.Point()
747 bmpPt.y = (rect.height - self._bmp.GetHeight())/2
749 mem_dc.DrawBitmap(self._bmp, bmpPt.x, bmpPt.y, True)
751 # get the text position, and draw it
752 font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
753 font.SetWeight(wx.BOLD)
755 fontHeight = mem_dc.GetCharHeight()
757 txtPt.x = bmpPt.x + self._bmp.GetWidth() + 4
758 txtPt.y = (rect.height - fontHeight)/2
759 mem_dc.SetTextForeground(wx.WHITE)
760 mem_dc.DrawText("Opened tabs:", txtPt.x, txtPt.y)
761 mem_dc.SelectObject(wx.NullBitmap)
763 dc.DrawBitmap(bmp, 0, 0)
766 def OnPanelEraseBg(self, event):
768 Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{TabNavigatorWindow} top panel.
770 :param `event`: a `wx.EraseEvent` event to be processed.
772 :note: This is intentionally empty, to reduce flicker.
778 # ----------------------------------------------------------------------
779 # -- AuiTabContainer class implementation --
781 class AuiTabContainer(object):
783 AuiTabContainer is a class which contains information about each
784 tab. It also can render an entire tab control to a specified DC.
785 It's not a window class itself, because this code will be used by
786 the L{AuiManager}, where it is disadvantageous to have separate
787 windows for each tab control in the case of "docked tabs".
789 A derived class, L{AuiTabCtrl}, is an actual `wx.Window`-derived window
790 which can be used as a tab control in the normal sense.
793 def __init__(self, auiNotebook):
795 Default class constructor.
796 Used internally, do not call it in your code!
798 :param `auiNotebook`: the parent L{AuiNotebook} window.
803 self._art = TA.AuiDefaultTabArt()
807 self._tab_close_buttons = []
809 self._rect = wx.Rect()
810 self._auiNotebook = auiNotebook
812 self.AddButton(AUI_BUTTON_LEFT, wx.LEFT)
813 self.AddButton(AUI_BUTTON_RIGHT, wx.RIGHT)
814 self.AddButton(AUI_BUTTON_WINDOWLIST, wx.RIGHT)
815 self.AddButton(AUI_BUTTON_CLOSE, wx.RIGHT)
818 def SetArtProvider(self, art):
820 Instructs L{AuiTabContainer} to use art provider specified by parameter `art`
821 for all drawing calls. This allows plugable look-and-feel features.
823 :param `art`: an art provider.
825 :note: The previous art provider object, if any, will be deleted by L{AuiTabContainer}.
832 self._art.SetAGWFlags(self._agwFlags)
835 def GetArtProvider(self):
836 """ Returns the current art provider being used. """
841 def SetAGWFlags(self, agwFlags):
843 Sets the tab art flags.
845 :param `agwFlags`: a combination of the following values:
847 ==================================== ==================================
848 Flag name Description
849 ==================================== ==================================
850 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
851 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet
852 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet
853 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
854 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
855 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
856 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
857 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
858 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
859 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
860 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
861 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
862 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
863 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
864 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
865 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
866 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
867 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
868 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
869 ``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
870 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
871 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
872 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
873 ==================================== ==================================
875 :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
879 self._agwFlags = agwFlags
881 # check for new close button settings
882 self.RemoveButton(AUI_BUTTON_LEFT)
883 self.RemoveButton(AUI_BUTTON_RIGHT)
884 self.RemoveButton(AUI_BUTTON_WINDOWLIST)
885 self.RemoveButton(AUI_BUTTON_CLOSE)
887 if agwFlags & AUI_NB_SCROLL_BUTTONS:
888 self.AddButton(AUI_BUTTON_LEFT, wx.LEFT)
889 self.AddButton(AUI_BUTTON_RIGHT, wx.RIGHT)
891 if agwFlags & AUI_NB_WINDOWLIST_BUTTON:
892 self.AddButton(AUI_BUTTON_WINDOWLIST, wx.RIGHT)
894 if agwFlags & AUI_NB_CLOSE_BUTTON:
895 self.AddButton(AUI_BUTTON_CLOSE, wx.RIGHT)
898 self._art.SetAGWFlags(self._agwFlags)
901 def GetAGWFlags(self):
903 Returns the tab art flags.
905 See L{SetAGWFlags} for a list of possible return values.
910 return self._agwFlags
913 def SetNormalFont(self, font):
915 Sets the normal font for drawing tab labels.
917 :param `font`: a `wx.Font` object.
920 self._art.SetNormalFont(font)
923 def SetSelectedFont(self, font):
925 Sets the selected tab font for drawing tab labels.
927 :param `font`: a `wx.Font` object.
930 self._art.SetSelectedFont(font)
933 def SetMeasuringFont(self, font):
935 Sets the font for calculating text measurements.
937 :param `font`: a `wx.Font` object.
940 self._art.SetMeasuringFont(font)
943 def SetTabRect(self, rect):
945 Sets the tab area rectangle.
947 :param `rect`: an instance of `wx.Rect`, specifying the available area for L{AuiTabContainer}.
953 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
954 self._art.SetSizingInfo(rect.GetSize(), len(self._pages), minMaxTabWidth)
957 def AddPage(self, page, info):
959 Adds a page to the tab control.
961 :param `page`: the window associated with this tab;
962 :param `info`: an instance of L{AuiNotebookPage}.
966 page_info.window = page
968 self._pages.append(page_info)
970 # let the art provider know how many pages we have
972 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
973 self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
978 def InsertPage(self, page, info, idx):
980 Inserts a page in the tab control in the position specified by `idx`.
982 :param `page`: the window associated with this tab;
983 :param `info`: an instance of L{AuiNotebookPage};
984 :param `idx`: the page insertion index.
988 page_info.window = page
990 if idx >= len(self._pages):
991 self._pages.append(page_info)
993 self._pages.insert(idx, page_info)
995 # let the art provider know how many pages we have
997 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
998 self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
1003 def MovePage(self, page, new_idx):
1005 Moves a page in a new position specified by `new_idx`.
1007 :param `page`: the window associated with this tab;
1008 :param `new_idx`: the new page position.
1011 idx = self.GetIdxFromWindow(page)
1015 # get page entry, make a copy of it
1016 p = self.GetPage(idx)
1018 # remove old page entry
1019 self.RemovePage(page)
1021 # insert page where it should be
1022 self.InsertPage(page, p, new_idx)
1027 def RemovePage(self, wnd):
1029 Removes a page from the tab control.
1031 :param `wnd`: an instance of `wx.Window`, a window associated with this tab.
1034 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
1036 for page in self._pages:
1037 if page.window == wnd:
1038 self._pages.remove(page)
1039 self._tab_offset = min(self._tab_offset, len(self._pages) - 1)
1041 # let the art provider know how many pages we have
1043 self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
1050 def SetActivePage(self, wndOrInt):
1052 Sets the L{AuiTabContainer} active page.
1054 :param `wndOrInt`: an instance of `wx.Window` or an integer specifying a tab index.
1057 if type(wndOrInt) == types.IntType:
1059 if wndOrInt >= len(self._pages):
1062 wnd = self._pages[wndOrInt].window
1069 for indx, page in enumerate(self._pages):
1070 if page.window == wnd:
1079 def SetNoneActive(self):
1080 """ Sets all the tabs as inactive (non-selected). """
1082 for page in self._pages:
1086 def GetActivePage(self):
1087 """ Returns the current selected tab or ``wx.NOT_FOUND`` if none is selected. """
1089 for indx, page in enumerate(self._pages):
1096 def GetWindowFromIdx(self, idx):
1098 Returns the window associated with the tab with index `idx`.
1100 :param `idx`: the tab index.
1103 if idx >= len(self._pages):
1106 return self._pages[idx].window
1109 def GetIdxFromWindow(self, wnd):
1111 Returns the tab index based on the window `wnd` associated with it.
1113 :param `wnd`: an instance of `wx.Window`.
1116 for indx, page in enumerate(self._pages):
1117 if page.window == wnd:
1123 def GetPage(self, idx):
1125 Returns the page specified by the given index.
1127 :param `idx`: the tab index.
1130 if idx < 0 or idx >= len(self._pages):
1131 raise Exception("Invalid Page index")
1133 return self._pages[idx]
1137 """ Returns a list of all the pages in this L{AuiTabContainer}. """
1142 def GetPageCount(self):
1143 """ Returns the number of pages in the L{AuiTabContainer}. """
1145 return len(self._pages)
1148 def GetEnabled(self, idx):
1150 Returns whether a tab is enabled or not.
1152 :param `idx`: the tab index.
1155 if idx < 0 or idx >= len(self._pages):
1158 return self._pages[idx].enabled
1161 def EnableTab(self, idx, enable=True):
1163 Enables/disables a tab in the L{AuiTabContainer}.
1165 :param `idx`: the tab index;
1166 :param `enable`: ``True`` to enable a tab, ``False`` to disable it.
1169 if idx < 0 or idx >= len(self._pages):
1170 raise Exception("Invalid Page index")
1172 self._pages[idx].enabled = enable
1173 wnd = self.GetWindowFromIdx(idx)
1177 def AddButton(self, id, location, normal_bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap):
1179 Adds a button in the tab area.
1181 :param `id`: the button identifier. This can be one of the following:
1183 ============================== =================================
1184 Button Identifier Description
1185 ============================== =================================
1186 ``AUI_BUTTON_CLOSE`` Shows a close button on the tab area
1187 ``AUI_BUTTON_WINDOWLIST`` Shows a window list button on the tab area
1188 ``AUI_BUTTON_LEFT`` Shows a left button on the tab area
1189 ``AUI_BUTTON_RIGHT`` Shows a right button on the tab area
1190 ============================== =================================
1192 :param `location`: the button location. Can be ``wx.LEFT`` or ``wx.RIGHT``;
1193 :param `normal_bitmap`: the bitmap for an enabled tab;
1194 :param `disabled_bitmap`: the bitmap for a disabled tab.
1197 button = AuiTabContainerButton()
1199 button.bitmap = normal_bitmap
1200 button.dis_bitmap = disabled_bitmap
1201 button.location = location
1202 button.cur_state = AUI_BUTTON_STATE_NORMAL
1204 self._buttons.append(button)
1207 def RemoveButton(self, id):
1209 Removes a button from the tab area.
1211 :param `id`: the button identifier. See L{AddButton} for a list of button identifiers.
1216 for button in self._buttons:
1218 self._buttons.remove(button)
1222 def GetTabOffset(self):
1223 """ Returns the tab offset. """
1225 return self._tab_offset
1228 def SetTabOffset(self, offset):
1230 Sets the tab offset.
1232 :param `offset`: the tab offset.
1235 self._tab_offset = offset
1238 def MinimizeTabOffset(self, dc, wnd, max_width):
1240 Minimize `self._tab_offset` to fit as many tabs as possible in the available space.
1242 :param `dc`: a `wx.DC` device context;
1243 :param `wnd`: an instance of `wx.Window`;
1244 :param `max_width`: the maximum available width for the tabs.
1249 for i, page in reversed(list(enumerate(self._pages))):
1251 tab_button = self._tab_close_buttons[i]
1252 (tab_width, tab_height), x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active, tab_button.cur_state, page.control)
1253 total_width += tab_width
1255 if total_width > max_width:
1259 if tab_offset < self._tab_offset and tab_offset < len(self._pages):
1260 self._tab_offset = tab_offset
1265 self._tab_offset = 0
1268 def Render(self, raw_dc, wnd):
1270 Renders the tab catalog to the specified `wx.DC`.
1272 It is a virtual function and can be overridden to provide custom drawing
1275 :param `raw_dc`: a `wx.DC` device context;
1276 :param `wnd`: an instance of `wx.Window`.
1279 if not raw_dc or not raw_dc.IsOk():
1284 # use the same layout direction as the window DC uses to ensure that the
1285 # text is rendered correctly
1286 dc.SetLayoutDirection(raw_dc.GetLayoutDirection())
1288 page_count = len(self._pages)
1289 button_count = len(self._buttons)
1291 # create off-screen bitmap
1292 bmp = wx.EmptyBitmap(self._rect.GetWidth(), self._rect.GetHeight())
1293 dc.SelectObject(bmp)
1298 # find out if size of tabs is larger than can be
1299 # afforded on screen
1300 total_width = visible_width = 0
1302 for i in xrange(page_count):
1303 page = self._pages[i]
1305 # determine if a close button is on this tab
1306 close_button = False
1307 if (self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS and page.hasCloseButton) or \
1308 (self._agwFlags & AUI_NB_CLOSE_ON_ACTIVE_TAB and page.active and page.hasCloseButton):
1312 control = page.control
1316 except wx.PyDeadObjectError:
1319 size, x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
1320 (close_button and [AUI_BUTTON_STATE_NORMAL] or \
1321 [AUI_BUTTON_STATE_HIDDEN])[0], page.control)
1323 if i+1 < page_count:
1324 total_width += x_extent
1326 total_width += size[0]
1328 if i >= self._tab_offset:
1329 if i+1 < page_count:
1330 visible_width += x_extent
1332 visible_width += size[0]
1334 if total_width > self._rect.GetWidth() or self._tab_offset != 0:
1336 # show left/right buttons
1337 for button in self._buttons:
1338 if button.id == AUI_BUTTON_LEFT or \
1339 button.id == AUI_BUTTON_RIGHT:
1341 button.cur_state &= ~AUI_BUTTON_STATE_HIDDEN
1345 # hide left/right buttons
1346 for button in self._buttons:
1347 if button.id == AUI_BUTTON_LEFT or \
1348 button.id == AUI_BUTTON_RIGHT:
1350 button.cur_state |= AUI_BUTTON_STATE_HIDDEN
1352 # determine whether left button should be enabled
1353 for button in self._buttons:
1354 if button.id == AUI_BUTTON_LEFT:
1355 if self._tab_offset == 0:
1356 button.cur_state |= AUI_BUTTON_STATE_DISABLED
1358 button.cur_state &= ~AUI_BUTTON_STATE_DISABLED
1360 if button.id == AUI_BUTTON_RIGHT:
1361 if visible_width < self._rect.GetWidth() - 16*button_count:
1362 button.cur_state |= AUI_BUTTON_STATE_DISABLED
1364 button.cur_state &= ~AUI_BUTTON_STATE_DISABLED
1367 self._art.DrawBackground(dc, wnd, self._rect)
1370 left_buttons_width = 0
1371 right_buttons_width = 0
1373 # draw the buttons on the right side
1374 offset = self._rect.x + self._rect.width
1376 for i in xrange(button_count):
1377 button = self._buttons[button_count - i - 1]
1379 if button.location != wx.RIGHT:
1381 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1384 button_rect = wx.Rect(*self._rect)
1386 button_rect.SetWidth(offset)
1388 button.rect = self._art.DrawButton(dc, wnd, button_rect, button, wx.RIGHT)
1390 offset -= button.rect.GetWidth()
1391 right_buttons_width += button.rect.GetWidth()
1395 # draw the buttons on the left side
1396 for i in xrange(button_count):
1397 button = self._buttons[button_count - i - 1]
1399 if button.location != wx.LEFT:
1401 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1404 button_rect = wx.Rect(offset, 1, 1000, self._rect.height)
1406 button.rect = self._art.DrawButton(dc, wnd, button_rect, button, wx.LEFT)
1408 offset += button.rect.GetWidth()
1409 left_buttons_width += button.rect.GetWidth()
1411 offset = left_buttons_width
1414 offset += self._art.GetIndentSize()
1416 # prepare the tab-close-button array
1417 # make sure tab button entries which aren't used are marked as hidden
1418 for i in xrange(page_count, len(self._tab_close_buttons)):
1419 self._tab_close_buttons[i].cur_state = AUI_BUTTON_STATE_HIDDEN
1421 # make sure there are enough tab button entries to accommodate all tabs
1422 while len(self._tab_close_buttons) < page_count:
1423 tempbtn = AuiTabContainerButton()
1424 tempbtn.id = AUI_BUTTON_CLOSE
1425 tempbtn.location = wx.CENTER
1426 tempbtn.cur_state = AUI_BUTTON_STATE_HIDDEN
1427 self._tab_close_buttons.append(tempbtn)
1429 # buttons before the tab offset must be set to hidden
1430 for i in xrange(self._tab_offset):
1431 self._tab_close_buttons[i].cur_state = AUI_BUTTON_STATE_HIDDEN
1432 if self._pages[i].control:
1433 if self._pages[i].control.IsShown():
1434 self._pages[i].control.Hide()
1436 self.MinimizeTabOffset(dc, wnd, self._rect.GetWidth() - right_buttons_width - offset - 2)
1442 rect = wx.Rect(*self._rect)
1444 rect.height = self._rect.height
1446 for i in xrange(self._tab_offset, page_count):
1448 page = self._pages[i]
1449 tab_button = self._tab_close_buttons[i]
1451 # determine if a close button is on this tab
1452 if (self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS and page.hasCloseButton) or \
1453 (self._agwFlags & AUI_NB_CLOSE_ON_ACTIVE_TAB and page.active and page.hasCloseButton):
1455 if tab_button.cur_state == AUI_BUTTON_STATE_HIDDEN:
1457 tab_button.id = AUI_BUTTON_CLOSE
1458 tab_button.cur_state = AUI_BUTTON_STATE_NORMAL
1459 tab_button.location = wx.CENTER
1463 tab_button.cur_state = AUI_BUTTON_STATE_HIDDEN
1466 rect.width = self._rect.width - right_buttons_width - offset - 2
1471 page.rect, tab_button.rect, x_extent = self._art.DrawTab(dc, wnd, page, rect, tab_button.cur_state)
1475 active_offset = offset
1476 active_rect = wx.Rect(*rect)
1480 lenPages = len(self._pages)
1481 # make sure to deactivate buttons which are off the screen to the right
1482 for j in xrange(i+1, len(self._tab_close_buttons)):
1483 self._tab_close_buttons[j].cur_state = AUI_BUTTON_STATE_HIDDEN
1484 if j > 0 and j <= lenPages:
1485 if self._pages[j-1].control:
1486 if self._pages[j-1].control.IsShown():
1487 self._pages[j-1].control.Hide()
1489 # draw the active tab again so it stands in the foreground
1490 if active >= self._tab_offset and active < len(self._pages):
1492 page = self._pages[active]
1493 tab_button = self._tab_close_buttons[active]
1495 rect.x = active_offset
1496 dummy = self._art.DrawTab(dc, wnd, page, active_rect, tab_button.cur_state)
1498 raw_dc.Blit(self._rect.x, self._rect.y, self._rect.GetWidth(), self._rect.GetHeight(), dc, 0, 0)
1501 def IsTabVisible(self, tabPage, tabOffset, dc, wnd):
1503 Returns whether a tab is visible or not.
1505 :param `tabPage`: the tab index;
1506 :param `tabOffset`: the tab offset;
1507 :param `dc`: a `wx.DC` device context;
1508 :param `wnd`: an instance of `wx.Window` derived window.
1511 if not dc or not dc.IsOk():
1514 page_count = len(self._pages)
1515 button_count = len(self._buttons)
1516 self.Render(dc, wnd)
1518 # Hasn't been rendered yet assume it's visible
1519 if len(self._tab_close_buttons) < page_count:
1522 if self._agwFlags & AUI_NB_SCROLL_BUTTONS:
1523 # First check if both buttons are disabled - if so, there's no need to
1524 # check further for visibility.
1525 arrowButtonVisibleCount = 0
1526 for i in xrange(button_count):
1528 button = self._buttons[i]
1529 if button.id == AUI_BUTTON_LEFT or \
1530 button.id == AUI_BUTTON_RIGHT:
1532 if button.cur_state & AUI_BUTTON_STATE_HIDDEN == 0:
1533 arrowButtonVisibleCount += 1
1535 # Tab must be visible
1536 if arrowButtonVisibleCount == 0:
1539 # If tab is less than the given offset, it must be invisible by definition
1540 if tabPage < tabOffset:
1544 left_buttons_width = 0
1545 right_buttons_width = 0
1549 # calculate size of the buttons on the right side
1550 offset = self._rect.x + self._rect.width
1552 for i in xrange(button_count):
1553 button = self._buttons[button_count - i - 1]
1555 if button.location != wx.RIGHT:
1557 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1560 offset -= button.rect.GetWidth()
1561 right_buttons_width += button.rect.GetWidth()
1565 # calculate size of the buttons on the left side
1566 for i in xrange(button_count):
1567 button = self._buttons[button_count - i - 1]
1569 if button.location != wx.LEFT:
1571 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1574 offset += button.rect.GetWidth()
1575 left_buttons_width += button.rect.GetWidth()
1577 offset = left_buttons_width
1580 offset += self._art.GetIndentSize()
1582 rect = wx.Rect(*self._rect)
1584 rect.height = self._rect.height
1586 # See if the given page is visible at the given tab offset (effectively scroll position)
1587 for i in xrange(tabOffset, page_count):
1589 page = self._pages[i]
1590 tab_button = self._tab_close_buttons[i]
1593 rect.width = self._rect.width - right_buttons_width - offset - 2
1596 return False # haven't found the tab, and we've run out of space, so return False
1598 size, x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active, tab_button.cur_state, page.control)
1603 # If not all of the tab is visible, and supposing there's space to display it all,
1604 # we could do better so we return False.
1605 if (self._rect.width - right_buttons_width - offset - 2) <= 0 and (self._rect.width - right_buttons_width - left_buttons_width) > x_extent:
1610 # Shouldn't really get here, but if it does, assume the tab is visible to prevent
1611 # further looping in calling code.
1615 def MakeTabVisible(self, tabPage, win):
1617 Make the tab visible if it wasn't already.
1619 :param `tabPage`: the tab index;
1620 :param `win`: an instance of `wx.Window` derived window.
1623 dc = wx.ClientDC(win)
1625 if not self.IsTabVisible(tabPage, self.GetTabOffset(), dc, win):
1626 for i in xrange(len(self._pages)):
1627 if self.IsTabVisible(tabPage, i, dc, win):
1628 self.SetTabOffset(i)
1633 def TabHitTest(self, x, y):
1635 TabHitTest() tests if a tab was hit, passing the window pointer
1636 back if that condition was fulfilled.
1638 :param `x`: the mouse `x` position;
1639 :param `y`: the mouse `y` position.
1642 if not self._rect.Contains((x,y)):
1645 btn = self.ButtonHitTest(x, y)
1647 if btn in self._buttons:
1650 for i in xrange(self._tab_offset, len(self._pages)):
1651 page = self._pages[i]
1652 if page.rect.Contains((x,y)):
1658 def ButtonHitTest(self, x, y):
1660 Tests if a button was hit.
1662 :param `x`: the mouse `x` position;
1663 :param `y`: the mouse `y` position.
1665 :returns: and instance of L{AuiTabContainerButton} if a button was hit, ``None`` otherwise.
1668 if not self._rect.Contains((x,y)):
1671 for button in self._buttons:
1672 if button.rect.Contains((x,y)) and \
1673 (button.cur_state & (AUI_BUTTON_STATE_HIDDEN|AUI_BUTTON_STATE_DISABLED)) == 0:
1676 for button in self._tab_close_buttons:
1677 if button.rect.Contains((x,y)) and \
1678 (button.cur_state & (AUI_BUTTON_STATE_HIDDEN|AUI_BUTTON_STATE_DISABLED)) == 0:
1684 def DoShowHide(self):
1686 This function shows the active window, then hides all of the other windows
1690 pages = self.GetPages()
1692 # show new active page first
1695 page.window.Show(True)
1698 # hide all other pages
1701 page.window.Show(False)
1704 # ----------------------------------------------------------------------
1705 # -- AuiTabCtrl class implementation --
1707 class AuiTabCtrl(wx.PyControl, AuiTabContainer):
1709 This is an actual `wx.Window`-derived window which can be used as a tab
1710 control in the normal sense.
1713 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
1714 style=wx.NO_BORDER|wx.WANTS_CHARS|wx.TAB_TRAVERSAL):
1716 Default class constructor.
1717 Used internally, do not call it in your code!
1719 :param `parent`: the L{AuiTabCtrl} parent;
1720 :param `id`: an identifier for the control: a value of -1 is taken to mean a default;
1721 :param `pos`: the control position. A value of (-1, -1) indicates a default position,
1722 chosen by either the windowing system or wxPython, depending on platform;
1723 :param `size`: the control size. A value of (-1, -1) indicates a default size,
1724 chosen by either the windowing system or wxPython, depending on platform;
1725 :param `style`: the window style.
1728 wx.PyControl.__init__(self, parent, id, pos, size, style, name="AuiTabCtrl")
1729 AuiTabContainer.__init__(self, parent)
1731 self._click_pt = wx.Point(-1, -1)
1732 self._is_dragging = False
1733 self._hover_button = None
1734 self._pressed_button = None
1735 self._drag_image = None
1736 self._drag_img_offset = (0, 0)
1737 self._on_button = False
1739 self.Bind(wx.EVT_PAINT, self.OnPaint)
1740 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
1741 self.Bind(wx.EVT_SIZE, self.OnSize)
1742 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
1743 self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
1744 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
1745 self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
1746 self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
1747 self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
1748 self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
1749 self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
1750 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
1751 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
1752 self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost)
1753 self.Bind(wx.EVT_MOTION, self.OnMotion)
1754 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
1755 self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnButton)
1758 def IsDragging(self):
1759 """ Returns whether the user is dragging a tab with the mouse or not. """
1761 return self._is_dragging
1764 def GetDefaultBorder(self):
1765 """ Returns the default border style for L{AuiTabCtrl}. """
1767 return wx.BORDER_NONE
1770 def OnPaint(self, event):
1772 Handles the ``wx.EVT_PAINT`` event for L{AuiTabCtrl}.
1774 :param `event`: a `wx.PaintEvent` event to be processed.
1777 dc = wx.PaintDC(self)
1778 dc.SetFont(self.GetFont())
1780 if self.GetPageCount() > 0:
1781 self.Render(dc, self)
1784 def OnEraseBackground(self, event):
1786 Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiTabCtrl}.
1788 :param `event`: a `wx.EraseEvent` event to be processed.
1790 :note: This is intentionally empty, to reduce flicker.
1796 def DoGetBestSize(self):
1798 Gets the size which best suits the window: for a control, it would be the
1799 minimal size which doesn't truncate the control, for a panel - the same
1800 size as it would have after a call to `Fit()`.
1802 :note: Overridden from `wx.PyControl`.
1805 return wx.Size(self._rect.width, self._rect.height)
1808 def OnSize(self, event):
1810 Handles the ``wx.EVT_SIZE`` event for L{AuiTabCtrl}.
1812 :param `event`: a `wx.SizeEvent` event to be processed.
1816 self.SetTabRect(wx.Rect(0, 0, s.GetWidth(), s.GetHeight()))
1819 def OnLeftDown(self, event):
1821 Handles the ``wx.EVT_LEFT_DOWN`` event for L{AuiTabCtrl}.
1823 :param `event`: a `wx.MouseEvent` event to be processed.
1827 self._click_pt = wx.Point(-1, -1)
1828 self._is_dragging = False
1829 self._click_tab = None
1830 self._pressed_button = None
1832 wnd = self.TabHitTest(event.GetX(), event.GetY())
1835 new_selection = self.GetIdxFromWindow(wnd)
1837 # AuiNotebooks always want to receive this event
1838 # even if the tab is already active, because they may
1839 # have multiple tab controls
1840 if new_selection != self.GetActivePage() or isinstance(self.GetParent(), AuiNotebook):
1842 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
1843 e.SetSelection(new_selection)
1844 e.SetOldSelection(self.GetActivePage())
1845 e.SetEventObject(self)
1846 self.GetEventHandler().ProcessEvent(e)
1848 self._click_pt.x = event.GetX()
1849 self._click_pt.y = event.GetY()
1850 self._click_tab = wnd
1852 page_index = self.GetActivePage()
1853 if page_index != wx.NOT_FOUND:
1854 self.GetWindowFromIdx(page_index).SetFocus()
1856 if self._hover_button:
1857 self._pressed_button = self._hover_button
1858 self._pressed_button.cur_state = AUI_BUTTON_STATE_PRESSED
1859 self._on_button = True
1864 def OnCaptureLost(self, event):
1866 Handles the ``wx.EVT_MOUSE_CAPTURE_LOST`` event for L{AuiTabCtrl}.
1868 :param `event`: a `wx.MouseCaptureLostEvent` event to be processed.
1871 if self._is_dragging:
1872 self._is_dragging = False
1873 self._on_button = False
1875 if self._drag_image:
1876 self._drag_image.EndDrag()
1877 del self._drag_image
1878 self._drag_image = None
1880 event = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG, self.GetId())
1881 event.SetSelection(self.GetIdxFromWindow(self._click_tab))
1882 event.SetOldSelection(event.GetSelection())
1883 event.SetEventObject(self)
1884 self.GetEventHandler().ProcessEvent(event)
1887 def OnLeftUp(self, event):
1889 Handles the ``wx.EVT_LEFT_UP`` event for L{AuiTabCtrl}.
1891 :param `event`: a `wx.MouseEvent` event to be processed.
1894 self._on_button = False
1896 if self._is_dragging:
1898 if self.HasCapture():
1901 self._is_dragging = False
1902 if self._drag_image:
1903 self._drag_image.EndDrag()
1904 del self._drag_image
1905 self._drag_image = None
1906 self.GetParent().Refresh()
1908 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, self.GetId())
1909 evt.SetSelection(self.GetIdxFromWindow(self._click_tab))
1910 evt.SetOldSelection(evt.GetSelection())
1911 evt.SetEventObject(self)
1912 self.GetEventHandler().ProcessEvent(evt)
1916 self.GetParent()._mgr.HideHint()
1918 if self.HasCapture():
1921 if self._hover_button:
1922 self._pressed_button = self._hover_button
1924 if self._pressed_button:
1926 # make sure we're still clicking the button
1927 button = self.ButtonHitTest(event.GetX(), event.GetY())
1932 if button != self._pressed_button:
1933 self._pressed_button = None
1939 if self._pressed_button.cur_state & AUI_BUTTON_STATE_DISABLED == 0:
1941 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, self.GetId())
1942 evt.SetSelection(self.GetIdxFromWindow(self._click_tab))
1943 evt.SetInt(self._pressed_button.id)
1944 evt.SetEventObject(self)
1945 eventHandler = self.GetEventHandler()
1947 if eventHandler is not None:
1948 eventHandler.ProcessEvent(evt)
1950 self._pressed_button = None
1952 self._click_pt = wx.Point(-1, -1)
1953 self._is_dragging = False
1954 self._click_tab = None
1957 def OnMiddleUp(self, event):
1959 Handles the ``wx.EVT_MIDDLE_UP`` event for L{AuiTabCtrl}.
1961 :param `event`: a `wx.MouseEvent` event to be processed.
1964 eventHandler = self.GetEventHandler()
1965 if not isinstance(eventHandler, AuiTabCtrl):
1969 x, y = event.GetX(), event.GetY()
1970 wnd = self.TabHitTest(x, y)
1973 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, self.GetId())
1974 e.SetEventObject(self)
1975 e.SetSelection(self.GetIdxFromWindow(wnd))
1976 self.GetEventHandler().ProcessEvent(e)
1977 elif not self.ButtonHitTest(x, y):
1978 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP, self.GetId())
1979 e.SetEventObject(self)
1980 self.GetEventHandler().ProcessEvent(e)
1983 def OnMiddleDown(self, event):
1985 Handles the ``wx.EVT_MIDDLE_DOWN`` event for L{AuiTabCtrl}.
1987 :param `event`: a `wx.MouseEvent` event to be processed.
1990 eventHandler = self.GetEventHandler()
1991 if not isinstance(eventHandler, AuiTabCtrl):
1995 x, y = event.GetX(), event.GetY()
1996 wnd = self.TabHitTest(x, y)
1999 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.GetId())
2000 e.SetEventObject(self)
2001 e.SetSelection(self.GetIdxFromWindow(wnd))
2002 self.GetEventHandler().ProcessEvent(e)
2003 elif not self.ButtonHitTest(x, y):
2004 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN, self.GetId())
2005 e.SetEventObject(self)
2006 self.GetEventHandler().ProcessEvent(e)
2009 def OnRightUp(self, event):
2011 Handles the ``wx.EVT_RIGHT_UP`` event for L{AuiTabCtrl}.
2013 :param `event`: a `wx.MouseEvent` event to be processed.
2016 x, y = event.GetX(), event.GetY()
2017 wnd = self.TabHitTest(x, y)
2020 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, self.GetId())
2021 e.SetEventObject(self)
2022 e.SetSelection(self.GetIdxFromWindow(wnd))
2023 self.GetEventHandler().ProcessEvent(e)
2024 elif not self.ButtonHitTest(x, y):
2025 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP, self.GetId())
2026 e.SetEventObject(self)
2027 self.GetEventHandler().ProcessEvent(e)
2030 def OnRightDown(self, event):
2032 Handles the ``wx.EVT_RIGHT_DOWN`` event for L{AuiTabCtrl}.
2034 :param `event`: a `wx.MouseEvent` event to be processed.
2037 x, y = event.GetX(), event.GetY()
2038 wnd = self.TabHitTest(x, y)
2041 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, self.GetId())
2042 e.SetEventObject(self)
2043 e.SetSelection(self.GetIdxFromWindow(wnd))
2044 self.GetEventHandler().ProcessEvent(e)
2045 elif not self.ButtonHitTest(x, y):
2046 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN, self.GetId())
2047 e.SetEventObject(self)
2048 self.GetEventHandler().ProcessEvent(e)
2051 def OnLeftDClick(self, event):
2053 Handles the ``wx.EVT_LEFT_DCLICK`` event for L{AuiTabCtrl}.
2055 :param `event`: a `wx.MouseEvent` event to be processed.
2058 x, y = event.GetX(), event.GetY()
2059 wnd = self.TabHitTest(x, y)
2062 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, self.GetId())
2063 e.SetEventObject(self)
2064 e.SetSelection(self.GetIdxFromWindow(wnd))
2065 self.GetEventHandler().ProcessEvent(e)
2066 elif not self.ButtonHitTest(x, y):
2067 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, self.GetId())
2068 e.SetEventObject(self)
2069 self.GetEventHandler().ProcessEvent(e)
2072 def OnMotion(self, event):
2074 Handles the ``wx.EVT_MOTION`` event for L{AuiTabCtrl}.
2076 :param `event`: a `wx.MouseEvent` event to be processed.
2079 pos = event.GetPosition()
2081 # check if the mouse is hovering above a button
2083 button = self.ButtonHitTest(pos.x, pos.y)
2084 wnd = self.TabHitTest(pos.x, pos.y)
2087 mouse_tab = self.GetIdxFromWindow(wnd)
2088 if not self._pages[mouse_tab].enabled:
2089 self._hover_button = None
2097 if self._hover_button and button != self._hover_button:
2098 self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
2099 self._hover_button = None
2103 if button.cur_state != AUI_BUTTON_STATE_HOVER:
2104 button.cur_state = AUI_BUTTON_STATE_HOVER
2107 self._hover_button = button
2112 if self._hover_button:
2113 self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
2114 self._hover_button = None
2118 if not event.LeftIsDown() or self._click_pt == wx.Point(-1, -1):
2121 if not self.HasCapture():
2124 wnd = self.TabHitTest(pos.x, pos.y)
2126 if not self._is_dragging:
2128 drag_x_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_X)
2129 drag_y_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_Y)
2131 if abs(pos.x - self._click_pt.x) > drag_x_threshold or \
2132 abs(pos.y - self._click_pt.y) > drag_y_threshold:
2134 self._is_dragging = True
2136 if self._drag_image:
2137 self._drag_image.EndDrag()
2138 del self._drag_image
2139 self._drag_image = None
2141 if self._agwFlags & AUI_NB_DRAW_DND_TAB:
2142 # Create the custom draw image from the icons and the text of the item
2143 mouse_tab = self.GetIdxFromWindow(wnd)
2144 page = self._pages[mouse_tab]
2145 tab_button = self._tab_close_buttons[mouse_tab]
2146 self._drag_image = TabDragImage(self, page, tab_button.cur_state, self._art)
2148 if self._agwFlags & AUI_NB_TAB_FLOAT:
2149 self._drag_image.BeginDrag(wx.Point(0,0), self, fullScreen=True)
2151 self._drag_image.BeginDragBounded(wx.Point(0,0), self, self.GetParent())
2153 # Capture the mouse cursor position offset relative to
2154 # The tab image location
2155 self._drag_img_offset = (pos[0] - page.rect.x,
2156 pos[1] - page.rect.y)
2158 self._drag_image.Show()
2161 evt2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, self.GetId())
2162 evt2.SetSelection(self.GetIdxFromWindow(self._click_tab))
2163 evt2.SetOldSelection(evt2.GetSelection())
2164 evt2.SetEventObject(self)
2165 self.GetEventHandler().ProcessEvent(evt2)
2166 if evt2.GetDispatched():
2169 evt3 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, self.GetId())
2170 evt3.SetSelection(self.GetIdxFromWindow(self._click_tab))
2171 evt3.SetOldSelection(evt3.GetSelection())
2172 evt3.SetEventObject(self)
2173 self.GetEventHandler().ProcessEvent(evt3)
2175 if self._drag_image:
2176 # Apply the drag images offset
2177 pos -= self._drag_img_offset
2178 self._drag_image.Move(pos)
2181 def OnLeaveWindow(self, event):
2183 Handles the ``wx.EVT_LEAVE_WINDOW`` event for L{AuiTabCtrl}.
2185 :param `event`: a `wx.MouseEvent` event to be processed.
2188 if self._hover_button:
2189 self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
2190 self._hover_button = None
2195 def OnButton(self, event):
2197 Handles the ``EVT_AUINOTEBOOK_BUTTON`` event for L{AuiTabCtrl}.
2199 :param `event`: a L{AuiNotebookEvent} event to be processed.
2202 button = event.GetInt()
2204 if button == AUI_BUTTON_LEFT or button == AUI_BUTTON_RIGHT:
2205 if button == AUI_BUTTON_LEFT:
2206 if self.GetTabOffset() > 0:
2208 self.SetTabOffset(self.GetTabOffset()-1)
2212 self.SetTabOffset(self.GetTabOffset()+1)
2216 elif button == AUI_BUTTON_WINDOWLIST:
2217 idx = self.GetArtProvider().ShowDropDown(self, self._pages, self.GetActivePage())
2221 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
2223 e.SetOldSelection(self.GetActivePage())
2224 e.SetEventObject(self)
2225 self.GetEventHandler().ProcessEvent(e)
2231 def OnSetFocus(self, event):
2233 Handles the ``wx.EVT_SET_FOCUS`` event for L{AuiTabCtrl}.
2235 :param `event`: a `wx.FocusEvent` event to be processed.
2241 def OnKillFocus(self, event):
2243 Handles the ``wx.EVT_KILL_FOCUS`` event for L{AuiTabCtrl}.
2245 :param `event`: a `wx.FocusEvent` event to be processed.
2251 def OnKeyDown(self, event):
2253 Handles the ``wx.EVT_KEY_DOWN`` event for L{AuiTabCtrl}.
2255 :param `event`: a `wx.KeyEvent` event to be processed.
2258 key = event.GetKeyCode()
2259 nb = self.GetParent()
2261 if key == wx.WXK_LEFT:
2262 nb.AdvanceSelection(False)
2265 elif key == wx.WXK_RIGHT:
2266 nb.AdvanceSelection(True)
2269 elif key == wx.WXK_HOME:
2271 nb.SetSelection(newPage)
2274 elif key == wx.WXK_END:
2275 newPage = nb.GetPageCount() - 1
2276 nb.SetSelection(newPage)
2279 elif key == wx.WXK_TAB:
2280 if not event.ControlDown():
2282 if not event.ShiftDown(): flags |= wx.NavigationKeyEvent.IsForward
2283 if event.CmdDown(): flags |= wx.NavigationKeyEvent.WinChange
2284 self.Navigate(flags)
2287 if not nb or not isinstance(nb, AuiNotebook):
2291 bForward = bWindowChange = 0
2292 if not event.ShiftDown(): bForward |= wx.NavigationKeyEvent.IsForward
2293 if event.CmdDown(): bWindowChange |= wx.NavigationKeyEvent.WinChange
2295 keyEvent = wx.NavigationKeyEvent()
2296 keyEvent.SetDirection(bForward)
2297 keyEvent.SetWindowChange(bWindowChange)
2298 keyEvent.SetFromTab(True)
2299 keyEvent.SetEventObject(nb)
2301 if not nb.GetEventHandler().ProcessEvent(keyEvent):
2303 # Not processed? Do an explicit tab into the page.
2304 win = self.GetWindowFromIdx(self.GetActivePage())
2316 def OnKeyDown2(self, event):
2320 Handles the ``wx.EVT_KEY_DOWN`` event for L{AuiTabCtrl}.
2322 :param `event`: a `wx.KeyEvent` event to be processed.
2324 :warning: This method implementation is now deprecated. Refer to L{OnKeyDown}
2325 for the correct one.
2328 if self.GetActivePage() == -1:
2332 # We can't leave tab processing to the system on Windows, tabs and keys
2333 # get eaten by the system and not processed properly if we specify both
2334 # wxTAB_TRAVERSAL and wxWANTS_CHARS. And if we specify just wxTAB_TRAVERSAL,
2335 # we don't key arrow key events.
2337 key = event.GetKeyCode()
2339 if key == wx.WXK_NUMPAD_PAGEUP:
2341 if key == wx.WXK_NUMPAD_PAGEDOWN:
2342 key = wx.WXK_PAGEDOWN
2343 if key == wx.WXK_NUMPAD_HOME:
2345 if key == wx.WXK_NUMPAD_END:
2347 if key == wx.WXK_NUMPAD_LEFT:
2349 if key == wx.WXK_NUMPAD_RIGHT:
2352 if key == wx.WXK_TAB or key == wx.WXK_PAGEUP or key == wx.WXK_PAGEDOWN:
2354 bCtrlDown = event.ControlDown()
2355 bShiftDown = event.ShiftDown()
2357 bForward = (key == wx.WXK_TAB and not bShiftDown) or (key == wx.WXK_PAGEDOWN)
2358 bWindowChange = (key == wx.WXK_PAGEUP) or (key == wx.WXK_PAGEDOWN) or bCtrlDown
2359 bFromTab = (key == wx.WXK_TAB)
2361 nb = self.GetParent()
2362 if not nb or not isinstance(nb, AuiNotebook):
2366 keyEvent = wx.NavigationKeyEvent()
2367 keyEvent.SetDirection(bForward)
2368 keyEvent.SetWindowChange(bWindowChange)
2369 keyEvent.SetFromTab(bFromTab)
2370 keyEvent.SetEventObject(nb)
2372 if not nb.GetEventHandler().ProcessEvent(keyEvent):
2374 # Not processed? Do an explicit tab into the page.
2375 win = self.GetWindowFromIdx(self.GetActivePage())
2381 if len(self._pages) < 2:
2387 if self.GetLayoutDirection() == wx.Layout_RightToLeft:
2388 forwardKey = wx.WXK_LEFT
2389 backwardKey = wx.WXK_RIGHT
2391 forwardKey = wx.WXK_RIGHT
2392 backwardKey = wx.WXK_LEFT
2394 if key == forwardKey:
2395 if self.GetActivePage() == -1:
2397 elif self.GetActivePage() < len(self._pages) - 1:
2398 newPage = self.GetActivePage() + 1
2400 elif key == backwardKey:
2401 if self.GetActivePage() == -1:
2402 newPage = len(self._pages) - 1
2403 elif self.GetActivePage() > 0:
2404 newPage = self.GetActivePage() - 1
2406 elif key == wx.WXK_HOME:
2409 elif key == wx.WXK_END:
2410 newPage = len(self._pages) - 1
2416 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
2417 e.SetSelection(newPage)
2418 e.SetOldSelection(newPage)
2419 e.SetEventObject(self)
2420 self.GetEventHandler().ProcessEvent(e)
2426 # ----------------------------------------------------------------------
2428 class TabFrame(wx.PyWindow):
2430 TabFrame is an interesting case. It's important that all child pages
2431 of the multi-notebook control are all actually children of that control
2432 (and not grandchildren). TabFrame facilitates this. There is one
2433 instance of TabFrame for each tab control inside the multi-notebook.
2435 It's important to know that TabFrame is not a real window, but it merely
2436 used to capture the dimensions/positioning of the internal tab control and
2437 it's managed page windows.
2440 def __init__(self, parent):
2442 Default class constructor.
2443 Used internally, do not call it in your code!
2446 pre = wx.PrePyWindow()
2449 self._rect = wx.Rect(0, 0, 200, 200)
2450 self._tab_ctrl_height = 20
2451 self._tab_rect = wx.Rect()
2452 self._parent = parent
2454 self.PostCreate(pre)
2457 def SetTabCtrlHeight(self, h):
2459 Sets the tab control height.
2461 :param `h`: the tab area height.
2464 self._tab_ctrl_height = h
2467 def DoSetSize(self, x, y, width, height, flags=wx.SIZE_AUTO):
2469 Sets the position and size of the window in pixels. The `flags`
2470 parameter indicates the interpretation of the other params if they are
2473 :param `x`: the window `x` position;
2474 :param `y`: the window `y` position;
2475 :param `width`: the window width;
2476 :param `height`: the window height;
2477 :param `flags`: may have one of this bit set:
2479 =================================== ======================================
2480 Size Flags Description
2481 =================================== ======================================
2482 ``wx.SIZE_AUTO`` A -1 indicates that a class-specific default should be used.
2483 ``wx.SIZE_AUTO_WIDTH`` A -1 indicates that a class-specific default should be used for the width.
2484 ``wx.SIZE_AUTO_HEIGHT`` A -1 indicates that a class-specific default should be used for the height.
2485 ``wx.SIZE_USE_EXISTING`` Existing dimensions should be used if -1 values are supplied.
2486 ``wx.SIZE_ALLOW_MINUS_ONE`` Allow dimensions of -1 and less to be interpreted as real dimensions, not default values.
2487 ``wx.SIZE_FORCE`` Normally, if the position and the size of the window are already the same as the parameters of this function, nothing is done. but with this flag a window resize may be forced even in this case (supported in wx 2.6.2 and later and only implemented for MSW and ignored elsewhere currently)
2488 =================================== ======================================
2490 :note: Overridden from `wx.PyControl`.
2493 self._rect = wx.Rect(x, y, max(1, width), max(1, height))
2497 def DoGetSize(self):
2499 Returns the window size.
2501 :note: Overridden from `wx.PyControl`.
2504 return self._rect.width, self._rect.height
2507 def DoGetClientSize(self):
2509 Returns the window client size.
2511 :note: Overridden from `wx.PyControl`.
2514 return self._rect.width, self._rect.height
2517 def Show(self, show=True):
2519 Shows/hides the window.
2521 :param `show`: ``True`` to show the window, ``False`` otherwise.
2523 :note: Overridden from `wx.PyControl`, this method always returns ``False`` as
2524 L{TabFrame} should never be phisically shown on screen.
2531 """ Does the actual sizing of the tab control. """
2536 hideOnSingle = ((self._tabs.GetAGWFlags() & AUI_NB_HIDE_ON_SINGLE_TAB) and \
2537 self._tabs.GetPageCount() <= 1)
2539 if not hideOnSingle and not self._parent._hide_tabs:
2540 tab_height = self._tab_ctrl_height
2542 self._tab_rect = wx.Rect(self._rect.x, self._rect.y, self._rect.width, self._tab_ctrl_height)
2544 if self._tabs.GetAGWFlags() & AUI_NB_BOTTOM:
2545 self._tab_rect = wx.Rect(self._rect.x, self._rect.y + self._rect.height - tab_height,
2546 self._rect.width, tab_height)
2547 self._tabs.SetDimensions(self._rect.x, self._rect.y + self._rect.height - tab_height,
2548 self._rect.width, tab_height)
2549 self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
2553 self._tab_rect = wx.Rect(self._rect.x, self._rect.y, self._rect.width, tab_height)
2554 self._tabs.SetDimensions(self._rect.x, self._rect.y, self._rect.width, tab_height)
2555 self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
2557 # TODO: elif (GetAGWFlags() & AUI_NB_LEFT)
2558 # TODO: elif (GetAGWFlags() & AUI_NB_RIGHT)
2560 self._tabs.Refresh()
2566 self._tabs.SetDimensions(self._rect.x, self._rect.y, self._rect.width, tab_height)
2567 self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
2569 pages = self._tabs.GetPages()
2573 height = self._rect.height - tab_height
2576 # avoid passing negative height to wx.Window.SetSize(), this
2577 # results in assert failures/GTK+ warnings
2580 if self._tabs.GetAGWFlags() & AUI_NB_BOTTOM:
2581 page.window.SetDimensions(self._rect.x, self._rect.y, self._rect.width, height)
2584 page.window.SetDimensions(self._rect.x, self._rect.y + tab_height,
2585 self._rect.width, height)
2587 # TODO: elif (GetAGWFlags() & AUI_NB_LEFT)
2588 # TODO: elif (GetAGWFlags() & AUI_NB_RIGHT)
2590 if repr(page.window.__class__).find("AuiMDIChildFrame") >= 0:
2591 page.window.ApplyMDIChildFrameRect()
2596 Calling this method immediately repaints the invalidated area of the window
2597 and all of its children recursively while this would usually only happen when
2598 the flow of control returns to the event loop.
2600 :note: Notice that this function doesn't invalidate any area of the window so
2601 nothing happens if nothing has been invalidated (i.e. marked as requiring a redraw).
2602 Use `Refresh` first if you want to immediately redraw the window unconditionally.
2604 :note: Overridden from `wx.PyControl`.
2611 # ----------------------------------------------------------------------
2612 # -- AuiNotebook class implementation --
2614 class AuiNotebook(wx.PyPanel):
2616 AuiNotebook is a notebook control which implements many features common in
2617 applications with dockable panes. Specifically, AuiNotebook implements functionality
2618 which allows the user to rearrange tab order via drag-and-drop, split the tab window
2619 into many different splitter configurations, and toggle through different themes to
2620 customize the control's look and feel.
2622 An effort has been made to try to maintain an API as similar to that of `wx.Notebook`.
2624 The default theme that is used is L{AuiDefaultTabArt}, which provides a modern, glossy
2625 look and feel. The theme can be changed by calling L{AuiNotebook.SetArtProvider}.
2628 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
2629 style=0, agwStyle=AUI_NB_DEFAULT_STYLE):
2631 Default class constructor.
2633 :param `parent`: the L{AuiNotebook} parent;
2634 :param `id`: an identifier for the control: a value of -1 is taken to mean a default;
2635 :param `pos`: the control position. A value of (-1, -1) indicates a default position,
2636 chosen by either the windowing system or wxPython, depending on platform;
2637 :param `size`: the control size. A value of (-1, -1) indicates a default size,
2638 chosen by either the windowing system or wxPython, depending on platform;
2639 :param `style`: the underlying `wx.PyPanel` window style;
2640 :param `agwStyle`: the AGW-specific window style. This can be a combination of the following bits:
2642 ==================================== ==================================
2643 Flag name Description
2644 ==================================== ==================================
2645 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
2646 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
2647 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
2648 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
2649 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
2650 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
2651 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
2652 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
2653 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
2654 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
2655 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
2656 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
2657 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
2658 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
2659 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
2660 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
2661 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
2662 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
2663 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
2664 ``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
2665 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
2666 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
2667 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
2668 ==================================== ==================================
2670 Default value for `agwStyle` is:
2671 ``AUI_NB_DEFAULT_STYLE`` = ``AUI_NB_TOP`` | ``AUI_NB_TAB_SPLIT`` | ``AUI_NB_TAB_MOVE`` | ``AUI_NB_SCROLL_BUTTONS`` | ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` | ``AUI_NB_MIDDLE_CLICK_CLOSE`` | ``AUI_NB_DRAW_DND_TAB``
2676 self._tab_id_counter = AuiBaseTabCtrlId
2677 self._dummy_wnd = None
2678 self._hide_tabs = False
2679 self._sash_dclick_unsplit = False
2680 self._tab_ctrl_height = 20
2681 self._requested_bmp_size = wx.Size(-1, -1)
2682 self._requested_tabctrl_height = -1
2683 self._textCtrl = None
2684 self._tabBounds = (-1, -1)
2685 self._click_tab = None
2687 wx.PyPanel.__init__(self, parent, id, pos, size, style|wx.BORDER_NONE|wx.TAB_TRAVERSAL)
2688 self._mgr = framemanager.AuiManager()
2689 self._tabs = AuiTabContainer(self)
2691 self.InitNotebook(agwStyle)
2694 def GetTabContainer(self):
2695 """ Returns the instance of L{AuiTabContainer}. """
2700 def InitNotebook(self, agwStyle):
2702 Contains common initialization code called by all constructors.
2704 :param `agwStyle`: the notebook style.
2709 self.SetName("AuiNotebook")
2710 self._agwFlags = agwStyle
2712 self._popupWin = None
2713 self._naviIcon = None
2714 self._imageList = None
2715 self._last_drag_x = 0
2717 self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
2718 self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
2719 self._selected_font.SetWeight(wx.BOLD)
2721 self.SetArtProvider(TA.AuiDefaultTabArt())
2723 self._dummy_wnd = wx.Window(self, wx.ID_ANY, wx.Point(0, 0), wx.Size(0, 0))
2724 self._dummy_wnd.SetSize((200, 200))
2725 self._dummy_wnd.Show(False)
2727 self._mgr.SetManagedWindow(self)
2728 self._mgr.SetAGWFlags(AUI_MGR_DEFAULT)
2729 self._mgr.SetDockSizeConstraint(1.0, 1.0) # no dock size constraint
2731 self._mgr.AddPane(self._dummy_wnd, framemanager.AuiPaneInfo().Name("dummy").Bottom().CaptionVisible(False).Show(False))
2734 self.Bind(wx.EVT_SIZE, self.OnSize)
2735 self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocusNotebook)
2736 self.Bind(EVT_AUINOTEBOOK_PAGE_CHANGING, self.OnTabClicked,
2737 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2738 self.Bind(EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnTabBeginDrag,
2739 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2740 self.Bind(EVT_AUINOTEBOOK_END_DRAG, self.OnTabEndDrag,
2741 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2742 self.Bind(EVT_AUINOTEBOOK_DRAG_MOTION, self.OnTabDragMotion,
2743 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2744 self.Bind(EVT_AUINOTEBOOK_CANCEL_DRAG, self.OnTabCancelDrag,
2745 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2746 self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnTabButton,
2747 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2748 self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.OnTabMiddleDown,
2749 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2750 self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_UP, self.OnTabMiddleUp,
2751 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2752 self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.OnTabRightDown,
2753 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2754 self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_UP, self.OnTabRightUp,
2755 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2756 self.Bind(EVT_AUINOTEBOOK_BG_DCLICK, self.OnTabBgDClick,
2757 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2758 self.Bind(EVT_AUINOTEBOOK_TAB_DCLICK, self.OnTabDClick,
2759 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2761 self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKeyNotebook)
2764 def SetArtProvider(self, art):
2766 Sets the art provider to be used by the notebook.
2768 :param `art`: an art provider.
2771 self._tabs.SetArtProvider(art)
2772 self.UpdateTabCtrlHeight(force=True)
2775 def SavePerspective(self):
2777 Saves the entire user interface layout into an encoded string, which can then
2778 be stored by the application (probably using `wx.Config`). When a perspective
2779 is restored using L{LoadPerspective}, the entire user interface will return
2780 to the state it was when the perspective was saved.
2783 # Build list of panes/tabs
2785 all_panes = self._mgr.GetAllPanes()
2787 for pane in all_panes:
2789 if pane.name == "dummy":
2792 tabframe = pane.window
2797 tabs += pane.name + "="
2800 page_count = tabframe._tabs.GetPageCount()
2802 for p in xrange(page_count):
2804 page = tabframe._tabs.GetPage(p)
2805 page_idx = self._tabs.GetIdxFromWindow(page.window)
2810 if p == tabframe._tabs.GetActivePage():
2812 elif page_idx == self._curpage:
2815 tabs += "%u"%page_idx
2819 # Add frame perspective
2820 tabs += self._mgr.SavePerspective()
2825 def LoadPerspective(self, layout):
2827 Loads a layout which was saved with L{SavePerspective}.
2829 :param `layout`: a string which contains a saved L{AuiNotebook} layout.
2832 # Remove all tab ctrls (but still keep them in main index)
2833 tab_count = self._tabs.GetPageCount()
2834 for i in xrange(tab_count):
2835 wnd = self._tabs.GetWindowFromIdx(i)
2837 # find out which onscreen tab ctrl owns this tab
2838 ctrl, ctrl_idx = self.FindTab(wnd)
2842 # remove the tab from ctrl
2843 if not ctrl.RemovePage(wnd):
2846 self.RemoveEmptyTabFrames()
2849 tabs = layout[0:layout.index("@")]
2858 tab_part = tabs[0:tabs.index('|')]
2860 if "=" not in tab_part:
2861 # No pages in this perspective...
2865 pane_name = tab_part[0:tab_part.index("=")]
2867 # create a new tab frame
2868 new_tabs = TabFrame(self)
2869 self._tab_id_counter += 1
2870 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
2871 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
2872 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
2873 new_tabs._tabs.SetAGWFlags(self._agwFlags)
2874 dest_tabs = new_tabs._tabs
2876 # create a pane info structure with the information
2877 # about where the pane should be added
2878 pane_info = framemanager.AuiPaneInfo().Name(pane_name).Bottom().CaptionVisible(False)
2879 self._mgr.AddPane(new_tabs, pane_info)
2881 # Get list of tab id's and move them to pane
2882 tab_list = tab_part[tab_part.index("=")+1:]
2883 to_break2, active_found = False, False
2886 if "," not in tab_list:
2890 tab = tab_list[0:tab_list.index(",")]
2891 tab_list = tab_list[tab_list.index(",")+1:]
2893 # Check if this page has an 'active' marker
2899 if tab_idx >= self.GetPageCount():
2904 page = self._tabs.GetPage(tab_idx)
2905 newpage_idx = dest_tabs.GetPageCount()
2906 dest_tabs.InsertPage(page.window, page, newpage_idx)
2909 dest_tabs.SetActivePage(newpage_idx)
2917 if not active_found:
2918 dest_tabs.SetActivePage(0)
2921 dest_tabs.DoShowHide()
2927 tabs = tabs[tabs.index('|')+1:]
2929 # Load the frame perspective
2930 frames = layout[layout.index('@')+1:]
2931 self._mgr.LoadPerspective(frames)
2933 # Force refresh of selection
2935 self.SetSelection(sel_page)
2940 def SetTabCtrlHeight(self, height):
2942 Sets the tab height.
2944 By default, the tab control height is calculated by measuring the text
2945 height and bitmap sizes on the tab captions.
2947 Calling this method will override that calculation and set the tab control
2948 to the specified height parameter. A call to this method will override
2949 any call to L{SetUniformBitmapSize}. Specifying -1 as the height will
2950 return the control to its default auto-sizing behaviour.
2952 :param `height`: the tab control area height.
2955 self._requested_tabctrl_height = height
2957 # if window is already initialized, recalculate the tab height
2959 self.UpdateTabCtrlHeight()
2962 def SetUniformBitmapSize(self, size):
2964 Ensures that all tabs will have the same height, even if some tabs
2965 don't have bitmaps. Passing ``wx.DefaultSize`` to this
2966 function will instruct the control to use dynamic tab height, which is
2967 the default behaviour. Under the default behaviour, when a tab with a
2968 large bitmap is added, the tab control's height will automatically
2969 increase to accommodate the larger bitmap.
2971 :param `size`: an instance of `wx.Size` specifying the tab bitmap size.
2974 self._requested_bmp_size = wx.Size(*size)
2976 # if window is already initialized, recalculate the tab height
2978 self.UpdateTabCtrlHeight()
2981 def UpdateTabCtrlHeight(self, force=False):
2983 UpdateTabCtrlHeight() does the actual tab resizing. It's meant
2984 to be used interally.
2986 :param `force`: ``True`` to force the tab art to repaint.
2989 # get the tab ctrl height we will use
2990 height = self.CalculateTabCtrlHeight()
2992 # if the tab control height needs to change, update
2993 # all of our tab controls with the new height
2994 if self._tab_ctrl_height != height or force:
2995 art = self._tabs.GetArtProvider()
2997 self._tab_ctrl_height = height
2999 all_panes = self._mgr.GetAllPanes()
3000 for pane in all_panes:
3002 if pane.name == "dummy":
3005 tab_frame = pane.window
3006 tabctrl = tab_frame._tabs
3007 tab_frame.SetTabCtrlHeight(self._tab_ctrl_height)
3008 tabctrl.SetArtProvider(art.Clone())
3009 tab_frame.DoSizing()
3012 def UpdateHintWindowSize(self):
3013 """ Updates the L{AuiManager} hint window size. """
3015 size = self.CalculateNewSplitSize()
3017 # the placeholder hint window should be set to this size
3018 info = self._mgr.GetPane("dummy")
3023 self._dummy_wnd.SetSize(size)
3026 def CalculateNewSplitSize(self):
3027 """ Calculates the size of the new split. """
3029 # count number of tab controls
3031 all_panes = self._mgr.GetAllPanes()
3033 for pane in all_panes:
3034 if pane.name == "dummy":
3039 # if there is only one tab control, the first split
3040 # should happen around the middle
3041 if tab_ctrl_count < 2:
3042 new_split_size = self.GetClientSize()
3043 new_split_size.x /= 2
3044 new_split_size.y /= 2
3048 # this is in place of a more complicated calculation
3049 # that needs to be implemented
3050 new_split_size = wx.Size(180, 180)
3052 return new_split_size
3055 def CalculateTabCtrlHeight(self):
3056 """ Calculates the tab control area height. """
3058 # if a fixed tab ctrl height is specified,
3059 # just return that instead of calculating a
3061 if self._requested_tabctrl_height != -1:
3062 return self._requested_tabctrl_height
3064 # find out new best tab height
3065 art = self._tabs.GetArtProvider()
3067 return art.GetBestTabCtrlSize(self, self._tabs.GetPages(), self._requested_bmp_size)
3070 def GetArtProvider(self):
3071 """ Returns the associated art provider. """
3073 return self._tabs.GetArtProvider()
3076 def SetAGWWindowStyleFlag(self, agwStyle):
3078 Sets the AGW-specific style of the window.
3080 :param `agwStyle`: the new window style. This can be a combination of the following bits:
3082 ==================================== ==================================
3083 Flag name Description
3084 ==================================== ==================================
3085 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
3086 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
3087 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
3088 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
3089 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
3090 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
3091 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
3092 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
3093 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
3094 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
3095 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
3096 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
3097 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
3098 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
3099 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
3100 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
3101 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
3102 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
3103 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
3104 ``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
3105 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
3106 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
3107 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
3108 ==================================== ==================================
3110 :note: Please note that some styles cannot be changed after the window
3111 creation and that `Refresh` might need to be be called after changing the
3112 others for the change to take place immediately.
3114 :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
3117 self._agwFlags = agwStyle
3119 # if the control is already initialized
3120 if self._mgr.GetManagedWindow() == self:
3122 # let all of the tab children know about the new style
3124 all_panes = self._mgr.GetAllPanes()
3125 for pane in all_panes:
3126 if pane.name == "dummy":
3129 tabframe = pane.window
3130 tabctrl = tabframe._tabs
3131 tabctrl.SetAGWFlags(self._agwFlags)
3137 def GetAGWWindowStyleFlag(self):
3139 Returns the AGW-specific style of the window.
3141 :see: L{SetAGWWindowStyleFlag} for a list of possible AGW-specific window styles.
3144 return self._agwFlags
3147 def AddPage(self, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap, control=None):
3149 Adds a page. If the `select` parameter is ``True``, calling this will generate a
3152 :param `page`: the page to be added;
3153 :param `caption`: specifies the text for the new page;
3154 :param `select`: specifies whether the page should be selected;
3155 :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
3156 :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
3157 :param `control`: a `wx.Window` instance inside a tab (or ``None``).
3160 return self.InsertPage(self.GetPageCount(), page, caption, select, bitmap, disabled_bitmap, control)
3163 def InsertPage(self, page_idx, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap,
3166 This is similar to L{AddPage}, but allows the ability to specify the insert location.
3168 :param `page_idx`: specifies the position for the new page;
3169 :param `page`: the page to be added;
3170 :param `caption`: specifies the text for the new page;
3171 :param `select`: specifies whether the page should be selected;
3172 :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
3173 :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
3174 :param `control`: a `wx.Window` instance inside a tab (or ``None``).
3181 info = AuiNotebookPage()
3183 info.caption = caption
3184 info.bitmap = bitmap
3186 info.control = control
3188 originalPaneMgr = framemanager.GetManager(page)
3190 originalPane = originalPaneMgr.GetPane(page)
3193 info.hasCloseButton = originalPane.HasCloseButton()
3195 if bitmap.IsOk() and not disabled_bitmap.IsOk():
3196 disabled_bitmap = MakeDisabledBitmap(bitmap)
3197 info.dis_bitmap = disabled_bitmap
3199 # if there are currently no tabs, the first added
3200 # tab must be active
3201 if self._tabs.GetPageCount() == 0:
3204 self._tabs.InsertPage(page, info, page_idx)
3206 # if that was the first page added, even if
3207 # select is False, it must become the "current page"
3208 # (though no select events will be fired)
3209 if not select and self._tabs.GetPageCount() == 1:
3212 active_tabctrl = self.GetActiveTabCtrl()
3213 if page_idx >= active_tabctrl.GetPageCount():
3214 active_tabctrl.AddPage(page, info)
3216 active_tabctrl.InsertPage(page, info, page_idx)
3221 control.Reparent(active_tabctrl)
3224 self.UpdateTabCtrlHeight(force=force)
3226 active_tabctrl.DoShowHide()
3228 # adjust selected index
3229 if self._curpage >= page_idx:
3233 self.SetSelectionToWindow(page)
3238 def DeletePage(self, page_idx):
3240 Deletes a page at the given index. Calling this method will generate a page
3243 :param `page_idx`: the page index to be deleted.
3245 :note: L{DeletePage} removes a tab from the multi-notebook, and destroys the window as well.
3250 if page_idx >= self._tabs.GetPageCount():
3253 wnd = self._tabs.GetWindowFromIdx(page_idx)
3254 # hide the window in advance, as this will
3258 self.RemoveControlFromPage(page_idx)
3260 if not self.RemovePage(page_idx):
3268 def RemovePage(self, page_idx):
3270 Removes a page, without deleting the window pointer.
3272 :param `page_idx`: the page index to be removed.
3274 :note: L{RemovePage} removes a tab from the multi-notebook, but does not destroy the window.
3279 # save active window pointer
3281 if self._curpage >= 0:
3282 active_wnd = self._tabs.GetWindowFromIdx(self._curpage)
3284 # save pointer of window being deleted
3285 wnd = self._tabs.GetWindowFromIdx(page_idx)
3288 # make sure we found the page
3292 # find out which onscreen tab ctrl owns this tab
3293 ctrl, ctrl_idx = self.FindTab(wnd)
3297 currentPage = ctrl.GetPage(ctrl_idx)
3298 is_curpage = (self._curpage == page_idx)
3299 is_active_in_split = currentPage.active
3301 # remove the tab from main catalog
3302 if not self._tabs.RemovePage(wnd):
3305 # remove the tab from the onscreen tab ctrl
3306 ctrl.RemovePage(wnd)
3308 if is_active_in_split:
3310 ctrl_new_page_count = ctrl.GetPageCount()
3312 if ctrl_idx >= ctrl_new_page_count:
3313 ctrl_idx = ctrl_new_page_count - 1
3315 if ctrl_idx >= 0 and ctrl_idx < ctrl.GetPageCount():
3317 ctrl_idx = self.FindNextActiveTab(ctrl_idx, ctrl)
3319 # set new page as active in the tab split
3320 ctrl.SetActivePage(ctrl_idx)
3322 # if the page deleted was the current page for the
3323 # entire tab control, then record the window
3324 # pointer of the new active page for activation
3326 new_active = ctrl.GetWindowFromIdx(ctrl_idx)
3330 # we are not deleting the active page, so keep it the same
3331 new_active = active_wnd
3335 # we haven't yet found a new page to active,
3336 # so select the next page from the main tab
3339 if 0 <= page_idx < self._tabs.GetPageCount():
3340 new_active = self._tabs.GetPage(page_idx).window
3341 if not new_active and self._tabs.GetPageCount() > 0:
3342 new_active = self._tabs.GetPage(0).window
3344 self.RemoveEmptyTabFrames()
3346 # set new active pane
3348 if not self.IsBeingDeleted():
3350 self.SetSelectionToWindow(new_active)
3353 self._tabs.SetNoneActive()
3358 def FindNextActiveTab(self, ctrl_idx, ctrl):
3360 Finds the next active tab (used mainly when L{AuiNotebook} has inactive/disabled
3363 :param `ctrl_idx`: the index of the first (most obvious) tab to check for active status;
3364 :param `ctrl`: an instance of L{AuiTabCtrl}.
3367 if self.GetEnabled(ctrl_idx):
3370 for indx in xrange(ctrl_idx, ctrl.GetPageCount()):
3371 if self.GetEnabled(indx):
3374 for indx in xrange(ctrl_idx, -1, -1):
3375 if self.GetEnabled(indx):
3381 def HideAllTabs(self, hidden=True):
3383 Hides all tabs on the L{AuiNotebook} control.
3385 :param `hidden`: if ``True`` hides all tabs.
3388 self._hide_tabs = hidden
3391 def SetSashDClickUnsplit(self, unsplit=True):
3393 Sets whether to unsplit a splitted L{AuiNotebook} when double-clicking on a sash.
3395 :param `unsplit`: ``True`` to unsplit on sash double-clicking, ``False`` otherwise.
3398 self._sash_dclick_unsplit = unsplit
3401 def GetSashDClickUnsplit(self):
3403 Returns whether a splitted L{AuiNotebook} can be unsplitted by double-clicking
3404 on the splitter sash.
3407 return self._sash_dclick_unsplit
3410 def SetMinMaxTabWidth(self, minTabWidth, maxTabWidth):
3412 Sets the minimum and/or the maximum tab widths for L{AuiNotebook} when the
3413 ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
3415 Pass -1 to either `minTabWidth` or `maxTabWidth` to reset to the default tab
3416 width behaviour for L{AuiNotebook}.
3418 :param `minTabWidth`: the minimum allowed tab width, in pixels;
3419 :param `maxTabWidth`: the maximum allowed tab width, in pixels.
3421 :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
3425 if minTabWidth > maxTabWidth:
3426 raise Exception("Minimum tab width must be less or equal than maximum tab width")
3428 self._tabBounds = (minTabWidth, maxTabWidth)
3429 self.SetAGWWindowStyleFlag(self._agwFlags)
3432 def GetMinMaxTabWidth(self):
3434 Returns the minimum and the maximum tab widths for L{AuiNotebook} when the
3435 ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
3437 :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
3440 :see: L{SetMinMaxTabWidth} for more information.
3443 return self._tabBounds
3446 def GetPageIndex(self, page_wnd):
3448 Returns the page index for the specified window. If the window is not
3449 found in the notebook, ``wx.NOT_FOUND`` is returned.
3452 return self._tabs.GetIdxFromWindow(page_wnd)
3455 def SetPageText(self, page_idx, text):
3457 Sets the tab label for the page.
3459 :param `page_idx`: the page index;
3460 :param `text`: the new tab label.
3463 if page_idx >= self._tabs.GetPageCount():
3466 # update our own tab catalog
3467 page_info = self._tabs.GetPage(page_idx)
3468 should_refresh = page_info.caption != text
3469 page_info.caption = text
3471 # update what's on screen
3472 ctrl, ctrl_idx = self.FindTab(page_info.window)
3476 info = ctrl.GetPage(ctrl_idx)
3477 should_refresh = should_refresh or info.caption != text
3484 self.UpdateTabCtrlHeight(force=True)
3489 def GetPageText(self, page_idx):
3491 Returns the tab label for the page.
3493 :param `page_idx`: the page index.
3496 if page_idx >= self._tabs.GetPageCount():
3499 # update our own tab catalog
3500 page_info = self._tabs.GetPage(page_idx)
3501 return page_info.caption
3504 def SetPageBitmap(self, page_idx, bitmap):
3506 Sets the tab bitmap for the page.
3508 :param `page_idx`: the page index;
3509 :param `bitmap`: an instance of `wx.Bitmap`.
3512 if page_idx >= self._tabs.GetPageCount():
3515 # update our own tab catalog
3516 page_info = self._tabs.GetPage(page_idx)
3517 should_refresh = page_info.bitmap is not bitmap
3518 page_info.bitmap = bitmap
3519 if bitmap.IsOk() and not page_info.dis_bitmap.IsOk():
3520 page_info.dis_bitmap = MakeDisabledBitmap(bitmap)
3522 # tab height might have changed
3523 self.UpdateTabCtrlHeight()
3525 # update what's on screen
3526 ctrl, ctrl_idx = self.FindTab(page_info.window)
3530 info = ctrl.GetPage(ctrl_idx)
3531 should_refresh = should_refresh or info.bitmap is not bitmap
3532 info.bitmap = bitmap
3533 info.dis_bitmap = page_info.dis_bitmap
3541 def GetPageBitmap(self, page_idx):
3543 Returns the tab bitmap for the page.
3545 :param `page_idx`: the page index.
3548 if page_idx >= self._tabs.GetPageCount():
3549 return wx.NullBitmap
3551 # update our own tab catalog
3552 page_info = self._tabs.GetPage(page_idx)
3553 return page_info.bitmap
3556 def SetImageList(self, imageList):
3558 Sets the image list for the L{AuiNotebook} control.
3560 :param `imageList`: an instance of `wx.ImageList`.
3563 self._imageList = imageList
3566 def AssignImageList(self, imageList):
3568 Sets the image list for the L{AuiNotebook} control.
3570 :param `imageList`: an instance of `wx.ImageList`.
3573 self.SetImageList(imageList)
3576 def GetImageList(self):
3577 """ Returns the associated image list (if any). """
3579 return self._imageList
3582 def SetPageImage(self, page, image):
3584 Sets the image index for the given page.
3586 :param `page`: the page index;
3587 :param `image`: an index into the image list which was set with L{SetImageList}.
3590 if page >= self._tabs.GetPageCount():
3593 if not isinstance(image, types.IntType):
3594 raise Exception("The image parameter must be an integer, you passed " \
3597 if not self._imageList:
3598 raise Exception("To use SetPageImage you need to associate an image list " \
3599 "Using SetImageList or AssignImageList")
3601 if image >= self._imageList.GetImageCount():
3602 raise Exception("Invalid image index (%d), the image list contains only" \
3603 " (%d) bitmaps"%(image, self._imageList.GetImageCount()))
3606 self.SetPageBitmap(page, wx.NullBitmap)
3609 bitmap = self._imageList.GetBitmap(image)
3610 self.SetPageBitmap(page, bitmap)
3613 def GetPageImage(self, page):
3615 Returns the image index for the given page.
3617 :param `page`: the given page for which to retrieve the image index.
3620 if page >= self._tabs.GetPageCount():
3623 bitmap = self.GetPageBitmap(page)
3624 for indx in xrange(self._imageList.GetImageCount()):
3625 imgListBmp = self._imageList.GetBitmap(indx)
3626 if imgListBmp == bitmap:
3632 def SetPageTextColour(self, page_idx, colour):
3634 Sets the tab text colour for the page.
3636 :param `page_idx`: the page index;
3637 :param `colour`: an instance of `wx.Colour`.
3640 if page_idx >= self._tabs.GetPageCount():
3643 # update our own tab catalog
3644 page_info = self._tabs.GetPage(page_idx)
3645 should_refresh = page_info.text_colour != colour
3646 page_info.text_colour = colour
3648 # update what's on screen
3649 ctrl, ctrl_idx = self.FindTab(page_info.window)
3653 info = ctrl.GetPage(ctrl_idx)
3654 should_refresh = should_refresh or info.text_colour != colour
3655 info.text_colour = page_info.text_colour
3664 def GetPageTextColour(self, page_idx):
3666 Returns the tab text colour for the page.
3668 :param `page_idx`: the page index.
3671 if page_idx >= self._tabs.GetPageCount():
3672 return wx.NullColour
3674 # update our own tab catalog
3675 page_info = self._tabs.GetPage(page_idx)
3676 return page_info.text_colour
3679 def AddControlToPage(self, page_idx, control):
3681 Adds a control inside a tab (not in the tab area).
3683 :param `page_idx`: the page index;
3684 :param `control`: an instance of `wx.Window`.
3687 if page_idx >= self._tabs.GetPageCount():
3690 # update our own tab catalog
3691 page_info = self._tabs.GetPage(page_idx)
3692 page_info.control = control
3694 # tab height might have changed
3695 self.UpdateTabCtrlHeight(force=True)
3697 # update what's on screen
3698 ctrl, ctrl_idx = self.FindTab(page_info.window)
3702 control.Reparent(ctrl)
3704 info = ctrl.GetPage(ctrl_idx)
3705 info.control = control
3712 def RemoveControlFromPage(self, page_idx):
3714 Removes a control from a tab (not from the tab area).
3716 :param `page_idx`: the page index.
3719 if page_idx >= self._tabs.GetPageCount():
3722 page_info = self._tabs.GetPage(page_idx)
3723 if page_info.control is None:
3726 page_info.control.Destroy()
3727 page_info.control = None
3729 # tab height might have changed
3730 self.UpdateTabCtrlHeight(force=True)
3732 # update what's on screen
3733 ctrl, ctrl_idx = self.FindTab(page_info.window)
3737 info = ctrl.GetPage(ctrl_idx)
3745 def SetCloseButton(self, page_idx, hasCloseButton):
3747 Sets whether a tab should display a close button or not.
3749 :param `page_idx`: the page index;
3750 :param `hasCloseButton`: ``True`` if the page displays a close button.
3752 :note: This can only be called if ``AUI_NB_CLOSE_ON_ALL_TABS`` is specified.
3755 if page_idx >= self._tabs.GetPageCount():
3758 if self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS == 0:
3759 raise Exception("SetCloseButton can only be used with AUI_NB_CLOSE_ON_ALL_TABS style.")
3761 # update our own tab catalog
3762 page_info = self._tabs.GetPage(page_idx)
3763 page_info.hasCloseButton = hasCloseButton
3765 # update what's on screen
3766 ctrl, ctrl_idx = self.FindTab(page_info.window)
3770 info = ctrl.GetPage(ctrl_idx)
3771 info.hasCloseButton = page_info.hasCloseButton
3778 def HasCloseButton(self, page_idx):
3780 Returns whether a tab displays a close button or not.
3782 :param `page_idx`: the page index.
3784 :note: This can only be called if ``AUI_NB_CLOSE_ON_ALL_TABS`` is specified.
3787 if page_idx >= self._tabs.GetPageCount():
3790 page_info = self._tabs.GetPage(page_idx)
3791 return page_info.hasCloseButton
3794 def GetSelection(self):
3795 """ Returns the index of the currently active page, or -1 if none was selected. """
3797 return self._curpage
3800 def GetCurrentPage(self):
3801 """ Returns the currently active page (not the index), or ``None`` if none was selected. """
3803 if self._curpage >= 0 and self._curpage < self._tabs.GetPageCount():
3804 return self.GetPage(self._curpage)
3809 def EnsureVisible(self, indx):
3811 Ensures the input page index `indx` is visible.
3813 :param `indx`: the page index.
3816 self._tabs.MakeTabVisible(indx, self)
3819 def SetSelection(self, new_page, force=False):
3821 Sets the page selection. Calling this method will generate a page change event.
3823 :param `new_page`: the index of the new selection;
3824 :param `force`: whether to force the selection or not.
3826 wnd = self._tabs.GetWindowFromIdx(new_page)
3828 #Update page access time
3829 self._tabs.GetPages()[new_page].access_time = datetime.datetime.now()
3831 if not wnd or not self.GetEnabled(new_page):
3832 return self._curpage
3834 # don't change the page unless necessary
3835 # however, clicking again on a tab should give it the focus.
3836 if new_page == self._curpage and not force:
3838 ctrl, ctrl_idx = self.FindTab(wnd)
3839 if wx.Window.FindFocus() != ctrl:
3842 return self._curpage
3844 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
3845 evt.SetSelection(new_page)
3846 evt.SetOldSelection(self._curpage)
3847 evt.SetEventObject(self)
3849 if not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed():
3851 old_curpage = self._curpage
3852 self._curpage = new_page
3854 # program allows the page change
3855 evt.SetEventType(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED)
3856 self.GetEventHandler().ProcessEvent(evt)
3858 if not evt.IsAllowed(): # event is no longer allowed after handler
3859 return self._curpage
3861 ctrl, ctrl_idx = self.FindTab(wnd)
3864 self._tabs.SetActivePage(wnd)
3865 ctrl.SetActivePage(ctrl_idx)
3868 ctrl.MakeTabVisible(ctrl_idx, ctrl)
3871 all_panes = self._mgr.GetAllPanes()
3872 for pane in all_panes:
3873 if pane.name == "dummy":
3876 tabctrl = pane.window._tabs
3878 tabctrl.SetSelectedFont(self._normal_font)
3880 tabctrl.SetSelectedFont(self._selected_font)
3885 # Set the focus to the page if we're not currently focused on the tab.
3886 # This is Firefox-like behaviour.
3887 if wnd.IsShownOnScreen() and wx.Window.FindFocus() != ctrl:
3892 return self._curpage
3895 def SetSelectionToWindow(self, win):
3897 Sets the selection based on the input window `win`.
3899 :param `win`: a `wx.Window` derived window.
3902 idx = self._tabs.GetIdxFromWindow(win)
3904 if idx == wx.NOT_FOUND:
3905 raise Exception("invalid notebook page")
3907 if not self.GetEnabled(idx):
3910 # since a tab was clicked, let the parent know that we received
3911 # the focus, even if we will assign that focus immediately
3912 # to the child tab in the SetSelection call below
3913 # (the child focus event will also let AuiManager, if any,
3914 # know that the notebook control has been activated)
3916 parent = self.GetParent()
3918 eventFocus = wx.ChildFocusEvent(self)
3919 parent.GetEventHandler().ProcessEvent(eventFocus)
3921 self.SetSelection(idx)
3924 def SetSelectionToPage(self, page):
3926 Sets the selection based on the input page.
3928 :param `page`: an instance of L{AuiNotebookPage}.
3931 self.SetSelectionToWindow(page.window)
3934 def GetPageCount(self):
3935 """ Returns the number of pages in the notebook. """
3937 return self._tabs.GetPageCount()
3940 def GetPage(self, page_idx):
3942 Returns the page specified by the given index.
3944 :param `page_idx`: the page index.
3947 if page_idx >= self._tabs.GetPageCount():
3948 raise Exception("invalid notebook page")
3950 return self._tabs.GetWindowFromIdx(page_idx)
3953 def GetPageInfo(self, page_idx):
3955 Returns the L{AuiNotebookPage} info structure specified by the given index.
3957 :param `page_idx`: the page index.
3960 if page_idx >= self._tabs.GetPageCount():
3961 raise Exception("invalid notebook page")
3963 return self._tabs.GetPage(page_idx)
3966 def GetEnabled(self, page_idx):
3968 Returns whether the page specified by the index `page_idx` is enabled.
3970 :param `page_idx`: the page index.
3973 return self._tabs.GetEnabled(page_idx)
3976 def EnableTab(self, page_idx, enable=True):
3978 Enables/disables a page in the notebook.
3980 :param `page_idx`: the page index;
3981 :param `enable`: ``True`` to enable the page, ``False`` to disable it.
3984 self._tabs.EnableTab(page_idx, enable)
3989 """ Performs all sizing operations in each tab control. """
3991 all_panes = self._mgr.GetAllPanes()
3992 for pane in all_panes:
3993 if pane.name == "dummy":
3996 tabframe = pane.window
4000 def GetAuiManager(self):
4001 """ Returns the associated L{AuiManager}. """
4006 def GetActiveTabCtrl(self):
4008 Returns the active tab control. It is called to determine which control
4009 gets new windows being added.
4012 if self._curpage >= 0 and self._curpage < self._tabs.GetPageCount():
4014 # find the tab ctrl with the current page
4015 ctrl, idx = self.FindTab(self._tabs.GetPage(self._curpage).window)
4019 # no current page, just find the first tab ctrl
4020 all_panes = self._mgr.GetAllPanes()
4021 for pane in all_panes:
4022 if pane.name == "dummy":
4025 tabframe = pane.window
4026 return tabframe._tabs
4028 # If there is no tabframe at all, create one
4029 tabframe = TabFrame(self)
4030 tabframe.SetTabCtrlHeight(self._tab_ctrl_height)
4031 self._tab_id_counter += 1
4032 tabframe._tabs = AuiTabCtrl(self, self._tab_id_counter)
4034 tabframe._tabs.SetAGWFlags(self._agwFlags)
4035 tabframe._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
4036 self._mgr.AddPane(tabframe, framemanager.AuiPaneInfo().Center().CaptionVisible(False).
4037 PaneBorder((self._agwFlags & AUI_NB_SUB_NOTEBOOK) == 0))
4041 return tabframe._tabs
4044 def FindTab(self, page):
4046 Finds the tab control that currently contains the window as well
4047 as the index of the window in the tab control. It returns ``True`` if the
4048 window was found, otherwise ``False``.
4050 :param `page`: an instance of L{AuiNotebookPage}.
4053 all_panes = self._mgr.GetAllPanes()
4054 for pane in all_panes:
4055 if pane.name == "dummy":
4058 tabframe = pane.window
4060 page_idx = tabframe._tabs.GetIdxFromWindow(page)
4064 ctrl = tabframe._tabs
4068 return None, wx.NOT_FOUND
4071 def Split(self, page, direction):
4073 Performs a split operation programmatically.
4075 :param `page`: indicates the page that will be split off. This page will also become
4076 the active page after the split.
4077 :param `direction`: specifies where the pane should go, it should be one of the
4078 following: ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, or ``wx.RIGHT``.
4081 cli_size = self.GetClientSize()
4083 # get the page's window pointer
4084 wnd = self.GetPage(page)
4088 # notebooks with 1 or less pages can't be split
4089 if self.GetPageCount() < 2:
4092 # find out which tab control the page currently belongs to
4094 src_tabs, src_idx = self.FindTab(wnd)
4098 # choose a split size
4099 if self.GetPageCount() > 2:
4100 split_size = self.CalculateNewSplitSize()
4102 # because there are two panes, always split them
4104 split_size = self.GetClientSize()
4108 # create a new tab frame
4109 new_tabs = TabFrame(self)
4110 new_tabs._rect = wx.RectPS(wx.Point(0, 0), split_size)
4111 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
4112 self._tab_id_counter += 1
4113 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
4115 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
4116 new_tabs._tabs.SetAGWFlags(self._agwFlags)
4117 dest_tabs = new_tabs._tabs
4119 page_info = src_tabs.GetPage(src_idx)
4120 if page_info.control:
4121 self.ReparentControl(page_info.control, dest_tabs)
4123 # create a pane info structure with the information
4124 # about where the pane should be added
4125 pane_info = framemanager.AuiPaneInfo().Bottom().CaptionVisible(False)
4127 if direction == wx.LEFT:
4130 mouse_pt = wx.Point(0, cli_size.y/2)
4132 elif direction == wx.RIGHT:
4135 mouse_pt = wx.Point(cli_size.x, cli_size.y/2)
4137 elif direction == wx.TOP:
4140 mouse_pt = wx.Point(cli_size.x/2, 0)
4142 elif direction == wx.BOTTOM:
4145 mouse_pt = wx.Point(cli_size.x/2, cli_size.y)
4147 self._mgr.AddPane(new_tabs, pane_info, mouse_pt)
4150 # remove the page from the source tabs
4151 page_info.active = False
4153 src_tabs.RemovePage(page_info.window)
4155 if src_tabs.GetPageCount() > 0:
4156 src_tabs.SetActivePage(0)
4157 src_tabs.DoShowHide()
4160 # add the page to the destination tabs
4161 dest_tabs.InsertPage(page_info.window, page_info, 0)
4163 if src_tabs.GetPageCount() == 0:
4164 self.RemoveEmptyTabFrames()
4167 dest_tabs.DoShowHide()
4170 # force the set selection function reset the selection
4173 # set the active page to the one we just split off
4174 self.SetSelectionToPage(page_info)
4176 self.UpdateHintWindowSize()
4180 """ Restores original view after a tab split. """
4184 # remember the tab now selected
4185 nowSelected = self.GetSelection()
4186 # select first tab as destination
4187 self.SetSelection(0)
4188 # iterate all other tabs
4189 for idx in xrange(1, self.GetPageCount()):
4191 win = self.GetPage(idx)
4193 title = self.GetPageText(idx)
4195 bmp = self.GetPageBitmap(idx)
4196 # remove from notebook
4197 self.RemovePage(idx)
4198 # re-add in the same position so it will tab
4199 self.InsertPage(idx, win, title, False, bmp)
4200 # restore orignial selected tab
4201 self.SetSelection(nowSelected)
4206 def ReparentControl(self, control, dest_tabs):
4208 Reparents a control added inside a tab.
4210 :param `control`: an instance of `wx.Window`;
4211 :param `dest_tabs`: the destination L{AuiTabCtrl}.
4215 control.Reparent(dest_tabs)
4218 def UnsplitDClick(self, part, sash_size, pos):
4220 Unsplit the L{AuiNotebook} on sash double-click.
4222 :param `part`: an UI part representing the sash;
4223 :param `sash_size`: the sash size;
4224 :param `pos`: the double-click mouse position.
4226 :warning: Due to a bug on MSW, for disabled pages `wx.FindWindowAtPoint`
4227 returns the wrong window. See http://trac.wxwidgets.org/ticket/2942
4230 if not self._sash_dclick_unsplit:
4231 # Unsplit not allowed
4234 pos1 = wx.Point(*pos)
4235 pos2 = wx.Point(*pos)
4236 if part.orientation == wx.HORIZONTAL:
4237 pos1.y -= 2*sash_size
4238 pos2.y += 2*sash_size + self.GetTabCtrlHeight()
4239 elif part.orientation == wx.VERTICAL:
4240 pos1.x -= 2*sash_size
4241 pos2.x += 2*sash_size
4243 raise Exception("Invalid UI part orientation")
4245 pos1, pos2 = self.ClientToScreen(pos1), self.ClientToScreen(pos2)
4246 win1, win2 = wx.FindWindowAtPoint(pos1), wx.FindWindowAtPoint(pos2)
4248 if isinstance(win1, wx.ScrollBar):
4249 # Hopefully it will work
4250 pos1 = wx.Point(*pos)
4251 shift = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + 2*(sash_size+1)
4252 if part.orientation == wx.HORIZONTAL:
4257 pos1 = self.ClientToScreen(pos1)
4258 win1 = wx.FindWindowAtPoint(pos1)
4260 if isinstance(win2, wx.ScrollBar):
4261 pos2 = wx.Point(*pos)
4262 shift = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + 2*(sash_size+1)
4263 if part.orientation == wx.HORIZONTAL:
4268 pos2 = self.ClientToScreen(pos2)
4269 win2 = wx.FindWindowAtPoint(pos2)
4271 if not win1 or not win2:
4272 # How did we get here?
4275 if isinstance(win1, AuiNotebook) or isinstance(win2, AuiNotebook):
4276 # This is a bug on MSW, for disabled pages wx.FindWindowAtPoint
4277 # returns the wrong window.
4278 # See http://trac.wxwidgets.org/ticket/2942
4281 tab_frame1, tab_frame2 = self.GetTabFrameFromWindow(win1), self.GetTabFrameFromWindow(win2)
4283 if not tab_frame1 or not tab_frame2:
4286 tab_ctrl_1, tab_ctrl_2 = tab_frame1._tabs, tab_frame2._tabs
4288 if tab_ctrl_1.GetPageCount() > tab_ctrl_2.GetPageCount():
4289 src_tabs = tab_ctrl_2
4290 dest_tabs = tab_ctrl_1
4292 src_tabs = tab_ctrl_1
4293 dest_tabs = tab_ctrl_2
4296 page_count = dest_tabs.GetPageCount()
4298 for page in xrange(src_tabs.GetPageCount()-1, -1, -1):
4299 # remove the page from the source tabs
4300 page_info = src_tabs.GetPage(page)
4301 if page_info.active:
4302 selection = page_count + page
4303 src_tabs.RemovePage(page_info.window)
4305 # add the page to the destination tabs
4306 dest_tabs.AddPage(page_info.window, page_info)
4307 if page_info.control:
4308 self.ReparentControl(page_info.control, dest_tabs)
4310 self.RemoveEmptyTabFrames()
4312 dest_tabs.DoShowHide()
4317 wx.CallAfter(dest_tabs.MakeTabVisible, selection, self)
4320 def OnSize(self, event):
4322 Handles the ``wx.EVT_SIZE`` event for L{AuiNotebook}.
4324 :param `event`: a `wx.SizeEvent` event to be processed.
4327 self.UpdateHintWindowSize()
4331 def OnTabClicked(self, event):
4333 Handles the ``EVT_AUINOTEBOOK_PAGE_CHANGING`` event for L{AuiNotebook}.
4335 :param `event`: a L{AuiNotebookEvent} event to be processed.
4338 if self._textCtrl is not None:
4339 self._textCtrl.StopEditing()
4341 ctrl = event.GetEventObject()
4344 wnd = ctrl.GetWindowFromIdx(event.GetSelection())
4347 self.SetSelectionToWindow(wnd)
4350 def OnTabBgDClick(self, event):
4352 Handles the ``EVT_AUINOTEBOOK_BG_DCLICK`` event for L{AuiNotebook}.
4354 :param `event`: a L{AuiNotebookEvent} event to be processed.
4357 if self._textCtrl is not None:
4358 self._textCtrl.StopEditing()
4360 # notify owner that the tabbar background has been double-clicked
4361 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, self.GetId())
4362 e.SetEventObject(self)
4363 self.GetEventHandler().ProcessEvent(e)
4366 def OnTabDClick(self, event):
4368 Handles the ``EVT_AUINOTEBOOK_TAB_DCLICK`` event for L{AuiNotebook}.
4370 :param `event`: a L{AuiNotebookEvent} event to be processed.
4373 # notify owner that the tabbar background has been double-clicked
4374 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, self.GetId())
4375 e.SetEventObject(self)
4376 self.GetEventHandler().ProcessEvent(e)
4378 tabs = event.GetEventObject()
4379 if not tabs.GetEnabled(event.GetSelection()):
4382 if not self.IsRenamable(event.GetSelection()):
4385 self.EditTab(event.GetSelection())
4388 def OnTabBeginDrag(self, event):
4390 Handles the ``EVT_AUINOTEBOOK_BEGIN_DRAG`` event for L{AuiNotebook}.
4392 :param `event`: a L{AuiNotebookEvent} event to be processed.
4395 tabs = event.GetEventObject()
4396 if not tabs.GetEnabled(event.GetSelection()):
4399 self._last_drag_x = 0
4402 def OnTabDragMotion(self, event):
4404 Handles the ``EVT_AUINOTEBOOK_DRAG_MOTION`` event for L{AuiNotebook}.
4406 :param `event`: a L{AuiNotebookEvent} event to be processed.
4409 tabs = event.GetEventObject()
4410 if not tabs.GetEnabled(event.GetSelection()):
4413 if self._textCtrl is not None:
4414 self._textCtrl.StopEditing()
4416 screen_pt = wx.GetMousePosition()
4417 client_pt = self.ScreenToClient(screen_pt)
4418 zero = wx.Point(0, 0)
4420 src_tabs = event.GetEventObject()
4421 dest_tabs = self.GetTabCtrlFromPoint(client_pt)
4423 if dest_tabs == src_tabs:
4425 # always hide the hint for inner-tabctrl drag
4426 self._mgr.HideHint()
4428 # if tab moving is not allowed, leave
4429 if not self._agwFlags & AUI_NB_TAB_MOVE:
4432 pt = dest_tabs.ScreenToClient(screen_pt)
4434 # this is an inner-tab drag/reposition
4435 dest_location_tab = dest_tabs.TabHitTest(pt.x, pt.y)
4437 if dest_location_tab:
4439 src_idx = event.GetSelection()
4440 dest_idx = dest_tabs.GetIdxFromWindow(dest_location_tab)
4442 # prevent jumpy drag
4443 if (src_idx == dest_idx) or dest_idx == -1 or \
4444 (src_idx > dest_idx and self._last_drag_x <= pt.x) or \
4445 (src_idx < dest_idx and self._last_drag_x >= pt.x):
4447 self._last_drag_x = pt.x
4450 src_tab = dest_tabs.GetWindowFromIdx(src_idx)
4451 dest_tabs.MovePage(src_tab, dest_idx)
4452 self._tabs.MovePage(self._tabs.GetPage(src_idx).window, dest_idx)
4453 dest_tabs.SetActivePage(dest_idx)
4454 dest_tabs.DoShowHide()
4456 self._last_drag_x = pt.x
4460 # if external drag is allowed, check if the tab is being dragged
4461 # over a different AuiNotebook control
4462 if self._agwFlags & AUI_NB_TAB_EXTERNAL_MOVE:
4464 tab_ctrl = wx.FindWindowAtPoint(screen_pt)
4466 # if we aren't over any window, stop here
4468 if self._agwFlags & AUI_NB_TAB_FLOAT:
4469 if self.IsMouseWellOutsideWindow():
4470 hintRect = wx.RectPS(screen_pt, (400, 300))
4471 # Use CallAfter so we overwrite the hint that might be
4472 # shown by our superclass:
4473 wx.CallAfter(self._mgr.ShowHint, hintRect)
4476 # make sure we are not over the hint window
4477 if not isinstance(tab_ctrl, wx.Frame):
4479 if isinstance(tab_ctrl, AuiTabCtrl):
4482 tab_ctrl = tab_ctrl.GetParent()
4485 nb = tab_ctrl.GetParent()
4489 hint_rect = tab_ctrl.GetClientRect()
4490 hint_rect.x, hint_rect.y = tab_ctrl.ClientToScreenXY(hint_rect.x, hint_rect.y)
4491 self._mgr.ShowHint(hint_rect)
4497 # we are either over a hint window, or not over a tab
4498 # window, and there is no where to drag to, so exit
4501 if self._agwFlags & AUI_NB_TAB_FLOAT:
4502 if self.IsMouseWellOutsideWindow():
4503 hintRect = wx.RectPS(screen_pt, (400, 300))
4504 # Use CallAfter so we overwrite the hint that might be
4505 # shown by our superclass:
4506 wx.CallAfter(self._mgr.ShowHint, hintRect)
4509 # if there are less than two panes, split can't happen, so leave
4510 if self._tabs.GetPageCount() < 2:
4513 # if tab moving is not allowed, leave
4514 if not self._agwFlags & AUI_NB_TAB_SPLIT:
4519 hint_rect = dest_tabs.GetRect()
4520 hint_rect.x, hint_rect.y = self.ClientToScreenXY(hint_rect.x, hint_rect.y)
4521 self._mgr.ShowHint(hint_rect)
4524 rect = self._mgr.CalculateHintRect(self._dummy_wnd, client_pt, zero)
4526 self._mgr.HideHint()
4529 hit_wnd = wx.FindWindowAtPoint(screen_pt)
4530 if hit_wnd and not isinstance(hit_wnd, AuiNotebook):
4531 tab_frame = self.GetTabFrameFromWindow(hit_wnd)
4533 hint_rect = wx.Rect(*tab_frame._rect)
4534 hint_rect.x, hint_rect.y = self.ClientToScreenXY(hint_rect.x, hint_rect.y)
4535 rect.Intersect(hint_rect)
4536 self._mgr.ShowHint(rect)
4538 self._mgr.DrawHintRect(self._dummy_wnd, client_pt, zero)
4540 self._mgr.DrawHintRect(self._dummy_wnd, client_pt, zero)
4543 def OnTabEndDrag(self, event):
4545 Handles the ``EVT_AUINOTEBOOK_END_DRAG`` event for L{AuiNotebook}.
4547 :param `event`: a L{AuiNotebookEvent} event to be processed.
4550 tabs = event.GetEventObject()
4551 if not tabs.GetEnabled(event.GetSelection()):
4554 self._mgr.HideHint()
4556 src_tabs = event.GetEventObject()
4558 raise Exception("no source object?")
4560 # get the mouse position, which will be used to determine the drop point
4561 mouse_screen_pt = wx.GetMousePosition()
4562 mouse_client_pt = self.ScreenToClient(mouse_screen_pt)
4564 # check for an external move
4565 if self._agwFlags & AUI_NB_TAB_EXTERNAL_MOVE:
4566 tab_ctrl = wx.FindWindowAtPoint(mouse_screen_pt)
4570 if isinstance(tab_ctrl, AuiTabCtrl):
4573 tab_ctrl = tab_ctrl.GetParent()
4577 nb = tab_ctrl.GetParent()
4581 # find out from the destination control
4582 # if it's ok to drop this tab here
4583 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, self.GetId())
4584 e.SetSelection(event.GetSelection())
4585 e.SetOldSelection(event.GetSelection())
4586 e.SetEventObject(self)
4587 e.SetDragSource(self)
4588 e.Veto() # dropping must be explicitly approved by control owner
4590 nb.GetEventHandler().ProcessEvent(e)
4592 if not e.IsAllowed():
4594 # no answer or negative answer
4595 self._mgr.HideHint()
4599 src_idx = event.GetSelection()
4600 src_page = src_tabs.GetWindowFromIdx(src_idx)
4602 # Check that it's not an impossible parent relationship
4604 while p and not p.IsTopLevel():
4610 # get main index of the page
4611 main_idx = self._tabs.GetIdxFromWindow(src_page)
4612 if main_idx == wx.NOT_FOUND:
4613 raise Exception("no source page?")
4615 # make a copy of the page info
4616 page_info = self._tabs.GetPage(main_idx)
4618 # remove the page from the source notebook
4619 self.RemovePage(main_idx)
4622 src_page.Reparent(nb)
4624 # Reparent the control in a tab (if any)
4625 if page_info.control:
4626 self.ReparentControl(page_info.control, tab_ctrl)
4628 # find out the insert idx
4629 dest_tabs = tab_ctrl
4630 pt = dest_tabs.ScreenToClient(mouse_screen_pt)
4632 target = dest_tabs.TabHitTest(pt.x, pt.y)
4635 insert_idx = dest_tabs.GetIdxFromWindow(target)
4637 # add the page to the new notebook
4638 if insert_idx == -1:
4639 insert_idx = dest_tabs.GetPageCount()
4641 dest_tabs.InsertPage(page_info.window, page_info, insert_idx)
4642 nb._tabs.AddPage(page_info.window, page_info)
4645 dest_tabs.DoShowHide()
4648 # set the selection in the destination tab control
4649 nb.SetSelectionToPage(page_info)
4651 # notify owner that the tab has been dragged
4652 e2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, self.GetId())
4653 e2.SetSelection(event.GetSelection())
4654 e2.SetOldSelection(event.GetSelection())
4655 e2.SetEventObject(self)
4656 self.GetEventHandler().ProcessEvent(e2)
4658 # notify the target notebook that the tab has been dragged
4659 e3 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, nb.GetId())
4660 e3.SetSelection(insert_idx)
4661 e3.SetOldSelection(insert_idx)
4662 e3.SetEventObject(nb)
4663 nb.GetEventHandler().ProcessEvent(e3)
4667 if self._agwFlags & AUI_NB_TAB_FLOAT:
4668 self._mgr.HideHint()
4669 if self.IsMouseWellOutsideWindow():
4670 # Use CallAfter so we our superclass can deal with the event first
4671 wx.CallAfter(self.FloatPage, self.GetSelection())
4675 # only perform a tab split if it's allowed
4678 if self._agwFlags & AUI_NB_TAB_SPLIT and self._tabs.GetPageCount() >= 2:
4680 # If the pointer is in an existing tab frame, do a tab insert
4681 hit_wnd = wx.FindWindowAtPoint(mouse_screen_pt)
4682 tab_frame = self.GetTabFrameFromTabCtrl(hit_wnd)
4687 dest_tabs = tab_frame._tabs
4689 if dest_tabs == src_tabs:
4692 pt = dest_tabs.ScreenToClient(mouse_screen_pt)
4693 target = dest_tabs.TabHitTest(pt.x, pt.y)
4696 insert_idx = dest_tabs.GetIdxFromWindow(target)
4700 zero = wx.Point(0, 0)
4701 rect = self._mgr.CalculateHintRect(self._dummy_wnd, mouse_client_pt, zero)
4704 # there is no suitable drop location here, exit out
4707 # If there is no tabframe at all, create one
4708 new_tabs = TabFrame(self)
4709 new_tabs._rect = wx.RectPS(wx.Point(0, 0), self.CalculateNewSplitSize())
4710 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
4711 self._tab_id_counter += 1
4712 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
4713 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
4714 new_tabs._tabs.SetAGWFlags(self._agwFlags)
4716 self._mgr.AddPane(new_tabs, framemanager.AuiPaneInfo().Bottom().CaptionVisible(False), mouse_client_pt)
4718 dest_tabs = new_tabs._tabs
4720 # remove the page from the source tabs
4721 page_info = src_tabs.GetPage(event.GetSelection())
4723 if page_info.control:
4724 self.ReparentControl(page_info.control, dest_tabs)
4726 page_info.active = False
4727 src_tabs.RemovePage(page_info.window)
4729 if src_tabs.GetPageCount() > 0:
4730 src_tabs.SetActivePage(0)
4731 src_tabs.DoShowHide()
4734 # add the page to the destination tabs
4735 if insert_idx == -1:
4736 insert_idx = dest_tabs.GetPageCount()
4738 dest_tabs.InsertPage(page_info.window, page_info, insert_idx)
4740 if src_tabs.GetPageCount() == 0:
4741 self.RemoveEmptyTabFrames()
4744 dest_tabs.DoShowHide()
4747 # force the set selection function reset the selection
4750 # set the active page to the one we just split off
4751 self.SetSelectionToPage(page_info)
4753 self.UpdateHintWindowSize()
4755 # notify owner that the tab has been dragged
4756 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, self.GetId())
4757 e.SetSelection(event.GetSelection())
4758 e.SetOldSelection(event.GetSelection())
4759 e.SetEventObject(self)
4760 self.GetEventHandler().ProcessEvent(e)
4763 def OnTabCancelDrag(self, event):
4765 Handles the ``EVT_AUINOTEBOOK_CANCEL_DRAG`` event for L{AuiNotebook}.
4767 :param `event`: a L{AuiNotebookEvent} event to be processed.
4770 tabs = event.GetEventObject()
4771 if not tabs.GetEnabled(event.GetSelection()):
4774 self._mgr.HideHint()
4776 src_tabs = event.GetEventObject()
4778 raise Exception("no source object?")
4781 def IsMouseWellOutsideWindow(self):
4782 """ Returns whether the mouse is well outside the L{AuiNotebook} screen rectangle. """
4784 screen_rect = self.GetScreenRect()
4785 screen_rect.Inflate(50, 50)
4787 return not screen_rect.Contains(wx.GetMousePosition())
4790 def FloatPage(self, page_index):
4792 Float the page in `page_index` by reparenting it to a floating frame.
4794 :param `page_index`: the index of the page to be floated.
4796 :warning: When the notebook is more or less full screen, tabs cannot be dragged far
4797 enough outside of the notebook to become floating pages.
4800 root_manager = framemanager.GetManager(self)
4801 page_title = self.GetPageText(page_index)
4802 page_contents = self.GetPage(page_index)
4803 page_bitmap = self.GetPageBitmap(page_index)
4804 text_colour = self.GetPageTextColour(page_index)
4805 info = self.GetPageInfo(page_index)
4807 if root_manager and root_manager != self._mgr:
4808 root_manager = framemanager.GetManager(self)
4810 if hasattr(page_contents, "__floating_size__"):
4811 floating_size = wx.Size(*page_contents.__floating_size__)
4813 floating_size = page_contents.GetBestSize()
4814 if floating_size == wx.DefaultSize:
4815 floating_size = wx.Size(300, 200)
4817 page_contents.__page_index__ = page_index
4818 page_contents.__aui_notebook__ = self
4819 page_contents.__text_colour__ = text_colour
4820 page_contents.__control__ = info.control
4823 info.control.Reparent(page_contents)
4827 self.RemovePage(page_index)
4828 self.RemoveEmptyTabFrames()
4830 pane_info = framemanager.AuiPaneInfo().Float().FloatingPosition(wx.GetMousePosition()). \
4831 FloatingSize(floating_size).BestSize(floating_size).Name("__floating__%s"%page_title). \
4832 Caption(page_title).Icon(page_bitmap)
4833 root_manager.AddPane(page_contents, pane_info)
4834 root_manager.Bind(framemanager.EVT_AUI_PANE_CLOSE, self.OnCloseFloatingPage)
4835 self.GetActiveTabCtrl().DoShowHide()
4837 root_manager.Update()
4840 frame = wx.Frame(self, title=page_title,
4841 style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_TOOL_WINDOW|
4842 wx.FRAME_FLOAT_ON_PARENT | wx.FRAME_NO_TASKBAR)
4845 info.control.Reparent(frame)
4848 frame.bitmap = page_bitmap
4849 frame.page_index = page_index
4850 frame.text_colour = text_colour
4851 frame.control = info.control
4852 page_contents.Reparent(frame)
4853 frame.Bind(wx.EVT_CLOSE, self.OnCloseFloatingPage)
4854 frame.Move(wx.GetMousePosition())
4856 self.RemovePage(page_index)
4858 self.RemoveEmptyTabFrames()
4860 wx.CallAfter(self.RemoveEmptyTabFrames)
4863 def OnCloseFloatingPage(self, event):
4865 Handles the ``wx.EVT_CLOSE`` event for a floating page in L{AuiNotebook}.
4867 :param `event`: a `wx.CloseEvent` event to be processed.
4870 root_manager = framemanager.GetManager(self)
4871 if root_manager and root_manager != self._mgr:
4873 if pane.name.startswith("__floating__"):
4874 self.ReDockPage(pane)
4880 frame = event.GetEventObject()
4881 page_title = frame.GetTitle()
4882 page_contents = list(frame.GetChildren())[-1]
4883 page_contents.Reparent(self)
4884 self.InsertPage(frame.page_index, page_contents, page_title, select=True, bitmap=frame.bitmap, control=frame.control)
4887 src_tabs, idx = self.FindTab(page_contents)
4888 frame.control.Reparent(src_tabs)
4889 frame.control.Hide()
4890 frame.control = None
4892 self.SetPageTextColour(frame.page_index, frame.text_colour)
4895 def ReDockPage(self, pane):
4897 Re-docks a floating L{AuiNotebook} tab in the original position, when possible.
4899 :param `pane`: an instance of L{framemanager.AuiPaneInfo}.
4902 root_manager = framemanager.GetManager(self)
4904 pane.window.__floating_size__ = wx.Size(*pane.floating_size)
4905 page_index = pane.window.__page_index__
4906 text_colour = pane.window.__text_colour__
4907 control = pane.window.__control__
4909 root_manager.DetachPane(pane.window)
4910 self.InsertPage(page_index, pane.window, pane.caption, True, pane.icon, control=control)
4912 self.SetPageTextColour(page_index, text_colour)
4913 self.GetActiveTabCtrl().DoShowHide()
4916 self.UpdateTabCtrlHeight(force=True)
4919 root_manager.Update()
4922 def GetTabCtrlFromPoint(self, pt):
4924 Returns the tab control at the specified point.
4926 :param `pt`: a `wx.Point` object.
4929 # if we've just removed the last tab from the source
4930 # tab set, the remove the tab control completely
4931 all_panes = self._mgr.GetAllPanes()
4932 for pane in all_panes:
4933 if pane.name == "dummy":
4936 tabframe = pane.window
4937 if tabframe._tab_rect.Contains(pt):
4938 return tabframe._tabs
4943 def GetTabFrameFromTabCtrl(self, tab_ctrl):
4945 Returns the tab frame associated with a tab control.
4947 :param `tab_ctrl`: an instance of L{AuiTabCtrl}.
4950 # if we've just removed the last tab from the source
4951 # tab set, the remove the tab control completely
4952 all_panes = self._mgr.GetAllPanes()
4953 for pane in all_panes:
4954 if pane.name == "dummy":
4957 tabframe = pane.window
4958 if tabframe._tabs == tab_ctrl:
4964 def GetTabFrameFromWindow(self, wnd):
4966 Returns the tab frame associated with a window.
4968 :param `wnd`: an instance of `wx.Window`.
4971 all_panes = self._mgr.GetAllPanes()
4972 for pane in all_panes:
4973 if pane.name == "dummy":
4976 tabframe = pane.window
4977 for page in tabframe._tabs.GetPages():
4978 if wnd == page.window:
4984 def RemoveEmptyTabFrames(self):
4985 """ Removes all the empty tab frames. """
4987 # if we've just removed the last tab from the source
4988 # tab set, the remove the tab control completely
4989 all_panes = self._mgr.GetAllPanes()
4991 for indx in xrange(len(all_panes)-1, -1, -1):
4992 pane = all_panes[indx]
4993 if pane.name == "dummy":
4996 tab_frame = pane.window
4997 if tab_frame._tabs.GetPageCount() == 0:
4998 self._mgr.DetachPane(tab_frame)
4999 tab_frame._tabs.Destroy()
5000 tab_frame._tabs = None
5003 # check to see if there is still a center pane
5004 # if there isn't, make a frame the center pane
5006 center_found = False
5008 all_panes = self._mgr.GetAllPanes()
5009 for pane in all_panes:
5010 if pane.name == "dummy":
5013 if pane.dock_direction == AUI_DOCK_CENTRE:
5016 first_good = pane.window
5018 if not center_found and first_good:
5019 self._mgr.GetPane(first_good).Centre()
5021 if not self.IsBeingDeleted():
5025 def OnChildFocusNotebook(self, event):
5027 Handles the ``wx.EVT_CHILD_FOCUS`` event for L{AuiNotebook}.
5029 :param `event`: a `wx.ChildFocusEvent` event to be processed.
5032 # if we're dragging a tab, don't change the current selection.
5033 # This code prevents a bug that used to happen when the hint window
5034 # was hidden. In the bug, the focus would return to the notebook
5035 # child, which would then enter this handler and call
5036 # SetSelection, which is not desired turn tab dragging.
5040 all_panes = self._mgr.GetAllPanes()
5041 for pane in all_panes:
5042 if pane.name == "dummy":
5044 tabframe = pane.window
5045 if tabframe._tabs.IsDragging():
5048 ## # change the tab selection to the child
5049 ## # which was focused
5050 ## idx = self._tabs.GetIdxFromWindow(event.GetWindow())
5051 ## if idx != -1 and idx != self._curpage:
5052 ## self.SetSelection(idx)
5055 def SetNavigatorIcon(self, bmp):
5057 Sets the icon used by the L{TabNavigatorWindow}.
5059 :param `bmp`: an instance of `wx.Bitmap`.
5062 if isinstance(bmp, wx.Bitmap) and bmp.IsOk():
5063 # Make sure image is proper size
5064 if bmp.GetSize() != (16, 16):
5065 img = bmp.ConvertToImage()
5066 img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
5067 bmp = wx.BitmapFromImage(img)
5068 self._naviIcon = bmp
5070 raise TypeError, "SetNavigatorIcon requires a valid bitmap"
5073 def OnNavigationKeyNotebook(self, event):
5075 Handles the ``wx.EVT_NAVIGATION_KEY`` event for L{AuiNotebook}.
5077 :param `event`: a `wx.NavigationKeyEvent` event to be processed.
5080 if event.IsWindowChange():
5081 if self._agwFlags & AUI_NB_SMART_TABS:
5082 if not self._popupWin:
5083 self._popupWin = TabNavigatorWindow(self, self._naviIcon)
5084 self._popupWin.SetReturnCode(wx.ID_OK)
5085 self._popupWin.ShowModal()
5086 idx = self._popupWin.GetSelectedPage()
5087 self._popupWin.Destroy()
5088 self._popupWin = None
5089 # Need to do CallAfter so that the selection and its
5090 # associated events get processed outside the context of
5091 # this key event. Not doing so causes odd issues with the
5092 # window focus under certain use cases on Windows.
5093 wx.CallAfter(self.SetSelection, idx, True)
5095 # a dialog is already opened
5096 self._popupWin.OnNavigationKey(event)
5100 # FIXME: the problem with this is that if we have a split notebook,
5101 # we selection may go all over the place.
5102 self.AdvanceSelection(event.GetDirection())
5105 # we get this event in 3 cases
5107 # a) one of our pages might have generated it because the user TABbed
5108 # out from it in which case we should propagate the event upwards and
5109 # our parent will take care of setting the focus to prev/next sibling
5113 # b) the parent panel wants to give the focus to us so that we
5114 # forward it to our selected page. We can't deal with this in
5115 # OnSetFocus() because we don't know which direction the focus came
5116 # from in this case and so can't choose between setting the focus to
5117 # first or last panel child
5121 # c) we ourselves (see MSWTranslateMessage) generated the event
5123 parent = self.GetParent()
5125 # the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
5126 isFromParent = event.GetEventObject() == parent
5127 isFromSelf = event.GetEventObject() == self
5129 if isFromParent or isFromSelf:
5131 # no, it doesn't come from child, case (b) or (c): forward to a
5132 # page but only if direction is backwards (TAB) or from ourselves,
5133 if self.GetSelection() != wx.NOT_FOUND and (not event.GetDirection() or isFromSelf):
5135 # so that the page knows that the event comes from it's parent
5136 # and is being propagated downwards
5137 event.SetEventObject(self)
5139 page = self.GetPage(self.GetSelection())
5140 if not page.GetEventHandler().ProcessEvent(event):
5143 #else: page manages focus inside it itself
5145 else: # otherwise set the focus to the notebook itself
5151 # send this event back for the 'wraparound' focus.
5152 winFocus = event.GetCurrentFocus()
5155 event.SetEventObject(self)
5156 winFocus.GetEventHandler().ProcessEvent(event)
5159 def OnTabButton(self, event):
5161 Handles the ``EVT_AUINOTEBOOK_BUTTON`` event for L{AuiNotebook}.
5163 :param `event`: a L{AuiNotebookEvent} event to be processed.
5166 tabs = event.GetEventObject()
5167 button_id = event.GetInt()
5169 if button_id == AUI_BUTTON_CLOSE:
5171 selection = event.GetSelection()
5175 # if the close button is to the right, use the active
5176 # page selection to determine which page to close
5177 selection = tabs.GetActivePage()
5179 if selection == -1 or not tabs.GetEnabled(selection):
5184 close_wnd = tabs.GetWindowFromIdx(selection)
5186 if close_wnd.GetName() == "__fake__page__":
5187 # This is a notebook preview
5188 previous_active, page_status = close_wnd.__previousStatus
5189 for page, status in zip(tabs.GetPages(), page_status):
5190 page.enabled = status
5192 main_idx = self._tabs.GetIdxFromWindow(close_wnd)
5193 self.DeletePage(main_idx)
5195 if previous_active >= 0:
5196 tabs.SetActivePage(previous_active)
5197 page_count = tabs.GetPageCount()
5200 for page in xrange(page_count):
5201 # remove the page from the source tabs
5202 page_info = tabs.GetPage(page)
5203 if page_info.active:
5212 wx.CallAfter(tabs.MakeTabVisible, selection, self)
5214 # Don't fire the event
5217 # ask owner if it's ok to close the tab
5218 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, self.GetId())
5219 idx = self._tabs.GetIdxFromWindow(close_wnd)
5221 e.SetOldSelection(event.GetSelection())
5222 e.SetEventObject(self)
5223 self.GetEventHandler().ProcessEvent(e)
5224 if not e.IsAllowed():
5227 if repr(close_wnd.__class__).find("AuiMDIChildFrame") >= 0:
5231 main_idx = self._tabs.GetIdxFromWindow(close_wnd)
5232 self.DeletePage(main_idx)
5234 # notify owner that the tab has been closed
5235 e2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, self.GetId())
5236 e2.SetSelection(idx)
5237 e2.SetEventObject(self)
5238 self.GetEventHandler().ProcessEvent(e2)
5240 if self.GetPageCount() == 0:
5241 mgr = self.GetAuiManager()
5242 win = mgr.GetManagedWindow()
5246 def OnTabMiddleDown(self, event):
5248 Handles the ``EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN`` event for L{AuiNotebook}.
5250 :param `event`: a L{AuiNotebookEvent} event to be processed.
5253 tabs = event.GetEventObject()
5254 if not tabs.GetEnabled(event.GetSelection()):
5257 # patch event through to owner
5258 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5260 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.GetId())
5261 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5262 e.SetEventObject(self)
5263 self.GetEventHandler().ProcessEvent(e)
5266 def OnTabMiddleUp(self, event):
5268 Handles the ``EVT_AUINOTEBOOK_TAB_MIDDLE_UP`` event for L{AuiNotebook}.
5270 :param `event`: a L{AuiNotebookEvent} event to be processed.
5273 tabs = event.GetEventObject()
5274 if not tabs.GetEnabled(event.GetSelection()):
5277 # if the AUI_NB_MIDDLE_CLICK_CLOSE is specified, middle
5278 # click should act like a tab close action. However, first
5279 # give the owner an opportunity to handle the middle up event
5282 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5284 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, self.GetId())
5285 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5286 e.SetEventObject(self)
5287 if self.GetEventHandler().ProcessEvent(e):
5289 if not e.IsAllowed():
5292 # check if we are supposed to close on middle-up
5293 if self._agwFlags & AUI_NB_MIDDLE_CLICK_CLOSE == 0:
5296 # simulate the user pressing the close button on the tab
5297 event.SetInt(AUI_BUTTON_CLOSE)
5298 self.OnTabButton(event)
5301 def OnTabRightDown(self, event):
5303 Handles the ``EVT_AUINOTEBOOK_TAB_RIGHT_DOWN`` event for L{AuiNotebook}.
5305 :param `event`: a L{AuiNotebookEvent} event to be processed.
5308 tabs = event.GetEventObject()
5309 if not tabs.GetEnabled(event.GetSelection()):
5312 # patch event through to owner
5313 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5315 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, self.GetId())
5316 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5317 e.SetEventObject(self)
5318 self.GetEventHandler().ProcessEvent(e)
5321 def OnTabRightUp(self, event):
5323 Handles the ``EVT_AUINOTEBOOK_TAB_RIGHT_UP`` event for L{AuiNotebook}.
5325 :param `event`: a L{AuiNotebookEvent} event to be processed.
5328 tabs = event.GetEventObject()
5329 if not tabs.GetEnabled(event.GetSelection()):
5332 # patch event through to owner
5333 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5335 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, self.GetId())
5336 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5337 e.SetEventObject(self)
5338 self.GetEventHandler().ProcessEvent(e)
5341 def SetNormalFont(self, font):
5343 Sets the normal font for drawing tab labels.
5345 :param `font`: a `wx.Font` object.
5348 self._normal_font = font
5349 self.GetArtProvider().SetNormalFont(font)
5352 def SetSelectedFont(self, font):
5354 Sets the selected tab font for drawing tab labels.
5356 :param `font`: a `wx.Font` object.
5359 self._selected_font = font
5360 self.GetArtProvider().SetSelectedFont(font)
5363 def SetMeasuringFont(self, font):
5365 Sets the font for calculating text measurements.
5367 :param `font`: a `wx.Font` object.
5370 self.GetArtProvider().SetMeasuringFont(font)
5373 def SetFont(self, font):
5377 :param `font`: a `wx.Font` object.
5379 :note: Overridden from `wx.PyPanel`.
5382 wx.PyPanel.SetFont(self, font)
5384 selectedFont = wx.Font(font.GetPointSize(), font.GetFamily(),
5385 font.GetStyle(), wx.BOLD, font.GetUnderlined(),
5386 font.GetFaceName(), font.GetEncoding())
5388 self.SetNormalFont(font)
5389 self.SetSelectedFont(selectedFont)
5390 self.SetMeasuringFont(selectedFont)
5392 # Recalculate tab container size based on new font
5393 self.UpdateTabCtrlHeight(force=False)
5399 def GetTabCtrlHeight(self):
5400 """ Returns the tab control height. """
5402 return self._tab_ctrl_height
5405 def GetHeightForPageHeight(self, pageHeight):
5407 Gets the height of the notebook for a given page height.
5409 :param `pageHeight`: the given page height.
5412 self.UpdateTabCtrlHeight()
5414 tabCtrlHeight = self.GetTabCtrlHeight()
5416 return tabCtrlHeight + pageHeight + decorHeight
5419 def AdvanceSelection(self, forward=True, wrap=True):
5421 Cycles through the tabs.
5423 :param `forward`: whether to advance forward or backward;
5424 :param `wrap`: ``True`` to return to the first tab if we reach the last tab.
5426 :note: The call to this function generates the page changing events.
5429 tabCtrl = self.GetActiveTabCtrl()
5432 focusWin = tabCtrl.FindFocus()
5433 activePage = tabCtrl.GetActivePage()
5434 lenPages = len(tabCtrl.GetPages())
5442 if activePage == -1 or activePage == lenPages - 1:
5448 elif activePage < lenPages - 1:
5449 newPage = activePage + 1
5454 if activePage == -1 or activePage == 0:
5458 newPage = lenPages - 1
5460 elif activePage > 0:
5461 newPage = activePage - 1
5465 if not self.GetEnabled(newPage):
5468 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, tabCtrl.GetId())
5469 e.SetSelection(newPage)
5470 e.SetOldSelection(activePage)
5471 e.SetEventObject(tabCtrl)
5472 self.GetEventHandler().ProcessEvent(e)
5475 ## focusWin.SetFocus()
5480 def ShowWindowMenu(self):
5482 Shows the window menu for the active tab control associated with this
5483 notebook, and returns ``True`` if a selection was made.
5486 tabCtrl = self.GetActiveTabCtrl()
5487 idx = tabCtrl.GetArtProvider().ShowDropDown(tabCtrl, tabCtrl.GetPages(), tabCtrl.GetActivePage())
5489 if not self.GetEnabled(idx):
5493 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, tabCtrl.GetId())
5495 e.SetOldSelection(tabCtrl.GetActivePage())
5496 e.SetEventObject(tabCtrl)
5497 self.GetEventHandler().ProcessEvent(e)
5506 def AddTabAreaButton(self, id, location, normal_bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap):
5508 Adds a button in the tab area.
5510 :param `id`: the button identifier. This can be one of the following:
5512 ============================== =================================
5513 Button Identifier Description
5514 ============================== =================================
5515 ``AUI_BUTTON_CLOSE`` Shows a close button on the tab area
5516 ``AUI_BUTTON_WINDOWLIST`` Shows a window list button on the tab area
5517 ``AUI_BUTTON_LEFT`` Shows a left button on the tab area
5518 ``AUI_BUTTON_RIGHT`` Shows a right button on the tab area
5519 ============================== =================================
5521 :param `location`: the button location. Can be ``wx.LEFT`` or ``wx.RIGHT``;
5522 :param `normal_bitmap`: the bitmap for an enabled tab;
5523 :param `disabled_bitmap`: the bitmap for a disabled tab.
5526 active_tabctrl = self.GetActiveTabCtrl()
5527 active_tabctrl.AddButton(id, location, normal_bitmap, disabled_bitmap)
5530 def RemoveTabAreaButton(self, id):
5532 Removes a button from the tab area.
5534 :param `id`: the button identifier. See L{AddTabAreaButton} for a list of button identifiers.
5536 :see: L{AddTabAreaButton}
5539 active_tabctrl = self.GetActiveTabCtrl()
5540 active_tabctrl.RemoveButton(id)
5543 def HasMultiplePages(self):
5545 This method should be overridden to return ``True`` if this window has multiple pages. All
5546 standard class with multiple pages such as `wx.Notebook`, `wx.Listbook` and `wx.Treebook`
5547 already override it to return ``True`` and user-defined classes with similar behaviour
5548 should do it as well to allow the library to handle such windows appropriately.
5550 :note: Overridden from `wx.PyPanel`.
5556 def GetDefaultBorder(self):
5557 """ Returns the default border style for L{AuiNotebook}. """
5559 return wx.BORDER_NONE
5562 def NotebookPreview(self, thumbnail_size=200):
5564 Generates a preview of all the pages in the notebook (MSW and GTK only).
5566 :param `thumbnail_size`: the maximum size of every page thumbnail.
5568 :note: this functionality is currently unavailable on wxMac.
5571 if wx.Platform == "__WXMAC__":
5574 tabCtrl = self.GetActiveTabCtrl()
5575 activePage = tabCtrl.GetActivePage()
5576 pages = tabCtrl.GetPages()
5578 pageStatus, pageText = [], []
5580 for indx, page in enumerate(pages):
5582 pageStatus.append(page.enabled)
5584 if not page.enabled:
5587 self.SetSelectionToPage(page)
5588 pageText.append(page.caption)
5590 rect = page.window.GetScreenRect()
5591 bmp = RescaleScreenShot(TakeScreenShot(rect), thumbnail_size)
5593 page.enabled = False
5595 il = wx.ImageList(bmp.GetWidth(), bmp.GetHeight(), True)
5599 # create the list control
5600 listCtrl = wx.ListCtrl(self, style=wx.LC_ICON|wx.LC_AUTOARRANGE|wx.LC_HRULES|wx.LC_VRULES,
5601 name="__fake__page__")
5603 # assign the image list to it
5604 listCtrl.AssignImageList(il, wx.IMAGE_LIST_NORMAL)
5605 listCtrl.__previousStatus = [activePage, pageStatus]
5607 # create some items for the list
5608 for indx, text in enumerate(pageText):
5609 listCtrl.InsertImageStringItem(10000, text, indx)
5611 self.AddPage(listCtrl, "AuiNotebook Preview", True, bitmap=auinotebook_preview.GetBitmap(), disabled_bitmap=wx.NullBitmap)
5615 def SetRenamable(self, page_idx, renamable):
5617 Sets whether a tab can be renamed via a left double-click or not.
5619 :param `page_idx`: the page index;
5620 :param `renamable`: ``True`` if the page can be renamed.
5623 if page_idx >= self._tabs.GetPageCount():
5626 # update our own tab catalog
5627 page_info = self._tabs.GetPage(page_idx)
5628 page_info.renamable = renamable
5630 # update what's on screen
5631 ctrl, ctrl_idx = self.FindTab(page_info.window)
5635 info = ctrl.GetPage(ctrl_idx)
5636 info.renamable = page_info.renamable
5641 def IsRenamable(self, page_idx):
5643 Returns whether a tab can be renamed or not.
5645 :param `page_idx`: the page index.
5647 :returns: ``True`` is a page can be renamed, ``False`` otherwise.
5650 if page_idx >= self._tabs.GetPageCount():
5653 page_info = self._tabs.GetPage(page_idx)
5654 return page_info.renamable
5657 def OnRenameCancelled(self, page_index):
5659 Called by L{TabTextCtrl}, to cancel the changes and to send the
5660 `EVT_AUINOTEBOOK_END_LABEL_EDIT` event.
5662 :param `page_index`: the page index in the notebook.
5665 # let owner know that the edit was cancelled
5666 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, self.GetId())
5668 evt.SetSelection(page_index)
5669 evt.SetEventObject(self)
5671 evt.SetEditCanceled(True)
5672 self.GetEventHandler().ProcessEvent(evt)
5675 def OnRenameAccept(self, page_index, value):
5677 Called by L{TabTextCtrl}, to accept the changes and to send the
5678 `EVT_AUINOTEBOOK_END_LABEL_EDIT` event.
5680 :param `page_index`: the page index in the notebook;
5681 :param `value`: the new label for the tab.
5684 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, self.GetId())
5685 evt.SetSelection(page_index)
5686 evt.SetEventObject(self)
5688 evt.SetEditCanceled(False)
5690 return not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed()
5693 def ResetTextControl(self):
5694 """ Called by L{TabTextCtrl} when it marks itself for deletion. """
5696 if not self._textCtrl:
5699 self._textCtrl.Destroy()
5700 self._textCtrl = None
5702 # tab height might have changed
5703 self.UpdateTabCtrlHeight(force=True)
5706 def EditTab(self, page_index):
5708 Starts the editing of an item label, sending a `EVT_AUINOTEBOOK_BEGIN_LABEL_EDIT` event.
5710 :param `page_index`: the page index we want to edit.
5713 if page_index >= self._tabs.GetPageCount():
5716 if not self.IsRenamable(page_index):
5719 page_info = self._tabs.GetPage(page_index)
5720 ctrl, ctrl_idx = self.FindTab(page_info.window)
5724 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT, self.GetId())
5725 evt.SetSelection(page_index)
5726 evt.SetEventObject(self)
5727 if self.GetEventHandler().ProcessEvent(evt) and not evt.IsAllowed():
5731 if self._textCtrl is not None and page_info != self._textCtrl.item():
5732 self._textCtrl.StopEditing()
5734 self._textCtrl = TabTextCtrl(ctrl, page_info, page_index)
5735 self._textCtrl.SetFocus()