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)
2686 wx.PyPanel.__init__(self, parent, id, pos, size, style|wx.BORDER_NONE|wx.TAB_TRAVERSAL)
2687 self._mgr = framemanager.AuiManager()
2688 self._tabs = AuiTabContainer(self)
2690 self.InitNotebook(agwStyle)
2693 def GetTabContainer(self):
2694 """ Returns the instance of L{AuiTabContainer}. """
2699 def InitNotebook(self, agwStyle):
2701 Contains common initialization code called by all constructors.
2703 :param `agwStyle`: the notebook style.
2708 self.SetName("AuiNotebook")
2709 self._agwFlags = agwStyle
2711 self._popupWin = None
2712 self._naviIcon = None
2713 self._imageList = None
2714 self._last_drag_x = 0
2716 self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
2717 self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
2718 self._selected_font.SetWeight(wx.BOLD)
2720 self.SetArtProvider(TA.AuiDefaultTabArt())
2722 self._dummy_wnd = wx.Window(self, wx.ID_ANY, wx.Point(0, 0), wx.Size(0, 0))
2723 self._dummy_wnd.SetSize((200, 200))
2724 self._dummy_wnd.Show(False)
2726 self._mgr.SetManagedWindow(self)
2727 self._mgr.SetAGWFlags(AUI_MGR_DEFAULT)
2728 self._mgr.SetDockSizeConstraint(1.0, 1.0) # no dock size constraint
2730 self._mgr.AddPane(self._dummy_wnd, framemanager.AuiPaneInfo().Name("dummy").Bottom().CaptionVisible(False).Show(False))
2733 self.Bind(wx.EVT_SIZE, self.OnSize)
2734 self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocusNotebook)
2735 self.Bind(EVT_AUINOTEBOOK_PAGE_CHANGING, self.OnTabClicked,
2736 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2737 self.Bind(EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnTabBeginDrag,
2738 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2739 self.Bind(EVT_AUINOTEBOOK_END_DRAG, self.OnTabEndDrag,
2740 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2741 self.Bind(EVT_AUINOTEBOOK_DRAG_MOTION, self.OnTabDragMotion,
2742 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2743 self.Bind(EVT_AUINOTEBOOK_CANCEL_DRAG, self.OnTabCancelDrag,
2744 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2745 self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnTabButton,
2746 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2747 self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.OnTabMiddleDown,
2748 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2749 self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_UP, self.OnTabMiddleUp,
2750 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2751 self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.OnTabRightDown,
2752 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2753 self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_UP, self.OnTabRightUp,
2754 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2755 self.Bind(EVT_AUINOTEBOOK_BG_DCLICK, self.OnTabBgDClick,
2756 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2757 self.Bind(EVT_AUINOTEBOOK_TAB_DCLICK, self.OnTabDClick,
2758 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2760 self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKeyNotebook)
2763 def SetArtProvider(self, art):
2765 Sets the art provider to be used by the notebook.
2767 :param `art`: an art provider.
2770 self._tabs.SetArtProvider(art)
2771 self.UpdateTabCtrlHeight(force=True)
2774 def SavePerspective(self):
2776 Saves the entire user interface layout into an encoded string, which can then
2777 be stored by the application (probably using `wx.Config`). When a perspective
2778 is restored using L{LoadPerspective}, the entire user interface will return
2779 to the state it was when the perspective was saved.
2782 # Build list of panes/tabs
2784 all_panes = self._mgr.GetAllPanes()
2786 for pane in all_panes:
2788 if pane.name == "dummy":
2791 tabframe = pane.window
2796 tabs += pane.name + "="
2799 page_count = tabframe._tabs.GetPageCount()
2801 for p in xrange(page_count):
2803 page = tabframe._tabs.GetPage(p)
2804 page_idx = self._tabs.GetIdxFromWindow(page.window)
2809 if p == tabframe._tabs.GetActivePage():
2811 elif page_idx == self._curpage:
2814 tabs += "%u"%page_idx
2818 # Add frame perspective
2819 tabs += self._mgr.SavePerspective()
2824 def LoadPerspective(self, layout):
2826 Loads a layout which was saved with L{SavePerspective}.
2828 :param `layout`: a string which contains a saved L{AuiNotebook} layout.
2831 # Remove all tab ctrls (but still keep them in main index)
2832 tab_count = self._tabs.GetPageCount()
2833 for i in xrange(tab_count):
2834 wnd = self._tabs.GetWindowFromIdx(i)
2836 # find out which onscreen tab ctrl owns this tab
2837 ctrl, ctrl_idx = self.FindTab(wnd)
2841 # remove the tab from ctrl
2842 if not ctrl.RemovePage(wnd):
2845 self.RemoveEmptyTabFrames()
2848 tabs = layout[0:layout.index("@")]
2857 tab_part = tabs[0:tabs.index('|')]
2859 if "=" not in tab_part:
2860 # No pages in this perspective...
2864 pane_name = tab_part[0:tab_part.index("=")]
2866 # create a new tab frame
2867 new_tabs = TabFrame(self)
2868 self._tab_id_counter += 1
2869 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
2870 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
2871 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
2872 new_tabs._tabs.SetAGWFlags(self._agwFlags)
2873 dest_tabs = new_tabs._tabs
2875 # create a pane info structure with the information
2876 # about where the pane should be added
2877 pane_info = framemanager.AuiPaneInfo().Name(pane_name).Bottom().CaptionVisible(False)
2878 self._mgr.AddPane(new_tabs, pane_info)
2880 # Get list of tab id's and move them to pane
2881 tab_list = tab_part[tab_part.index("=")+1:]
2882 to_break2, active_found = False, False
2885 if "," not in tab_list:
2889 tab = tab_list[0:tab_list.index(",")]
2890 tab_list = tab_list[tab_list.index(",")+1:]
2892 # Check if this page has an 'active' marker
2898 if tab_idx >= self.GetPageCount():
2903 page = self._tabs.GetPage(tab_idx)
2904 newpage_idx = dest_tabs.GetPageCount()
2905 dest_tabs.InsertPage(page.window, page, newpage_idx)
2908 dest_tabs.SetActivePage(newpage_idx)
2916 if not active_found:
2917 dest_tabs.SetActivePage(0)
2920 dest_tabs.DoShowHide()
2926 tabs = tabs[tabs.index('|')+1:]
2928 # Load the frame perspective
2929 frames = layout[layout.index('@')+1:]
2930 self._mgr.LoadPerspective(frames)
2932 # Force refresh of selection
2934 self.SetSelection(sel_page)
2939 def SetTabCtrlHeight(self, height):
2941 Sets the tab height.
2943 By default, the tab control height is calculated by measuring the text
2944 height and bitmap sizes on the tab captions.
2946 Calling this method will override that calculation and set the tab control
2947 to the specified height parameter. A call to this method will override
2948 any call to L{SetUniformBitmapSize}. Specifying -1 as the height will
2949 return the control to its default auto-sizing behaviour.
2951 :param `height`: the tab control area height.
2954 self._requested_tabctrl_height = height
2956 # if window is already initialized, recalculate the tab height
2958 self.UpdateTabCtrlHeight()
2961 def SetUniformBitmapSize(self, size):
2963 Ensures that all tabs will have the same height, even if some tabs
2964 don't have bitmaps. Passing ``wx.DefaultSize`` to this
2965 function will instruct the control to use dynamic tab height, which is
2966 the default behaviour. Under the default behaviour, when a tab with a
2967 large bitmap is added, the tab control's height will automatically
2968 increase to accommodate the larger bitmap.
2970 :param `size`: an instance of `wx.Size` specifying the tab bitmap size.
2973 self._requested_bmp_size = wx.Size(*size)
2975 # if window is already initialized, recalculate the tab height
2977 self.UpdateTabCtrlHeight()
2980 def UpdateTabCtrlHeight(self, force=False):
2982 UpdateTabCtrlHeight() does the actual tab resizing. It's meant
2983 to be used interally.
2985 :param `force`: ``True`` to force the tab art to repaint.
2988 # get the tab ctrl height we will use
2989 height = self.CalculateTabCtrlHeight()
2991 # if the tab control height needs to change, update
2992 # all of our tab controls with the new height
2993 if self._tab_ctrl_height != height or force:
2994 art = self._tabs.GetArtProvider()
2996 self._tab_ctrl_height = height
2998 all_panes = self._mgr.GetAllPanes()
2999 for pane in all_panes:
3001 if pane.name == "dummy":
3004 tab_frame = pane.window
3005 tabctrl = tab_frame._tabs
3006 tab_frame.SetTabCtrlHeight(self._tab_ctrl_height)
3007 tabctrl.SetArtProvider(art.Clone())
3008 tab_frame.DoSizing()
3011 def UpdateHintWindowSize(self):
3012 """ Updates the L{AuiManager} hint window size. """
3014 size = self.CalculateNewSplitSize()
3016 # the placeholder hint window should be set to this size
3017 info = self._mgr.GetPane("dummy")
3022 self._dummy_wnd.SetSize(size)
3025 def CalculateNewSplitSize(self):
3026 """ Calculates the size of the new split. """
3028 # count number of tab controls
3030 all_panes = self._mgr.GetAllPanes()
3032 for pane in all_panes:
3033 if pane.name == "dummy":
3038 # if there is only one tab control, the first split
3039 # should happen around the middle
3040 if tab_ctrl_count < 2:
3041 new_split_size = self.GetClientSize()
3042 new_split_size.x /= 2
3043 new_split_size.y /= 2
3047 # this is in place of a more complicated calculation
3048 # that needs to be implemented
3049 new_split_size = wx.Size(180, 180)
3051 return new_split_size
3054 def CalculateTabCtrlHeight(self):
3055 """ Calculates the tab control area height. """
3057 # if a fixed tab ctrl height is specified,
3058 # just return that instead of calculating a
3060 if self._requested_tabctrl_height != -1:
3061 return self._requested_tabctrl_height
3063 # find out new best tab height
3064 art = self._tabs.GetArtProvider()
3066 return art.GetBestTabCtrlSize(self, self._tabs.GetPages(), self._requested_bmp_size)
3069 def GetArtProvider(self):
3070 """ Returns the associated art provider. """
3072 return self._tabs.GetArtProvider()
3075 def SetAGWWindowStyleFlag(self, agwStyle):
3077 Sets the AGW-specific style of the window.
3079 :param `agwStyle`: the new window style. This can be a combination of the following bits:
3081 ==================================== ==================================
3082 Flag name Description
3083 ==================================== ==================================
3084 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
3085 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
3086 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
3087 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
3088 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
3089 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
3090 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
3091 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
3092 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
3093 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
3094 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
3095 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
3096 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
3097 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
3098 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
3099 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
3100 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
3101 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
3102 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
3103 ``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
3104 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
3105 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
3106 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
3107 ==================================== ==================================
3109 :note: Please note that some styles cannot be changed after the window
3110 creation and that `Refresh` might need to be be called after changing the
3111 others for the change to take place immediately.
3113 :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
3116 self._agwFlags = agwStyle
3118 # if the control is already initialized
3119 if self._mgr.GetManagedWindow() == self:
3121 # let all of the tab children know about the new style
3123 all_panes = self._mgr.GetAllPanes()
3124 for pane in all_panes:
3125 if pane.name == "dummy":
3128 tabframe = pane.window
3129 tabctrl = tabframe._tabs
3130 tabctrl.SetAGWFlags(self._agwFlags)
3136 def GetAGWWindowStyleFlag(self):
3138 Returns the AGW-specific style of the window.
3140 :see: L{SetAGWWindowStyleFlag} for a list of possible AGW-specific window styles.
3143 return self._agwFlags
3146 def AddPage(self, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap, control=None):
3148 Adds a page. If the `select` parameter is ``True``, calling this will generate a
3151 :param `page`: the page to be added;
3152 :param `caption`: specifies the text for the new page;
3153 :param `select`: specifies whether the page should be selected;
3154 :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
3155 :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
3156 :param `control`: a `wx.Window` instance inside a tab (or ``None``).
3159 return self.InsertPage(self.GetPageCount(), page, caption, select, bitmap, disabled_bitmap, control)
3162 def InsertPage(self, page_idx, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap,
3165 This is similar to L{AddPage}, but allows the ability to specify the insert location.
3167 :param `page_idx`: specifies the position for the new page;
3168 :param `page`: the page to be added;
3169 :param `caption`: specifies the text for the new page;
3170 :param `select`: specifies whether the page should be selected;
3171 :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
3172 :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
3173 :param `control`: a `wx.Window` instance inside a tab (or ``None``).
3180 info = AuiNotebookPage()
3182 info.caption = caption
3183 info.bitmap = bitmap
3185 info.control = control
3187 originalPaneMgr = framemanager.GetManager(page)
3189 originalPane = originalPaneMgr.GetPane(page)
3192 info.hasCloseButton = originalPane.HasCloseButton()
3194 if bitmap.IsOk() and not disabled_bitmap.IsOk():
3195 disabled_bitmap = MakeDisabledBitmap(bitmap)
3196 info.dis_bitmap = disabled_bitmap
3198 # if there are currently no tabs, the first added
3199 # tab must be active
3200 if self._tabs.GetPageCount() == 0:
3203 self._tabs.InsertPage(page, info, page_idx)
3205 # if that was the first page added, even if
3206 # select is False, it must become the "current page"
3207 # (though no select events will be fired)
3208 if not select and self._tabs.GetPageCount() == 1:
3211 active_tabctrl = self.GetActiveTabCtrl()
3212 if page_idx >= active_tabctrl.GetPageCount():
3213 active_tabctrl.AddPage(page, info)
3215 active_tabctrl.InsertPage(page, info, page_idx)
3220 control.Reparent(active_tabctrl)
3223 self.UpdateTabCtrlHeight(force=force)
3225 active_tabctrl.DoShowHide()
3227 # adjust selected index
3228 if self._curpage >= page_idx:
3232 self.SetSelectionToWindow(page)
3237 def DeletePage(self, page_idx):
3239 Deletes a page at the given index. Calling this method will generate a page
3242 :param `page_idx`: the page index to be deleted.
3244 :note: L{DeletePage} removes a tab from the multi-notebook, and destroys the window as well.
3249 if page_idx >= self._tabs.GetPageCount():
3252 wnd = self._tabs.GetWindowFromIdx(page_idx)
3253 # hide the window in advance, as this will
3257 self.RemoveControlFromPage(page_idx)
3259 if not self.RemovePage(page_idx):
3267 def RemovePage(self, page_idx):
3269 Removes a page, without deleting the window pointer.
3271 :param `page_idx`: the page index to be removed.
3273 :note: L{RemovePage} removes a tab from the multi-notebook, but does not destroy the window.
3278 # save active window pointer
3280 if self._curpage >= 0:
3281 active_wnd = self._tabs.GetWindowFromIdx(self._curpage)
3283 # save pointer of window being deleted
3284 wnd = self._tabs.GetWindowFromIdx(page_idx)
3287 # make sure we found the page
3291 # find out which onscreen tab ctrl owns this tab
3292 ctrl, ctrl_idx = self.FindTab(wnd)
3296 currentPage = ctrl.GetPage(ctrl_idx)
3297 is_curpage = (self._curpage == page_idx)
3298 is_active_in_split = currentPage.active
3300 # remove the tab from main catalog
3301 if not self._tabs.RemovePage(wnd):
3304 # remove the tab from the onscreen tab ctrl
3305 ctrl.RemovePage(wnd)
3307 if is_active_in_split:
3309 ctrl_new_page_count = ctrl.GetPageCount()
3311 if ctrl_idx >= ctrl_new_page_count:
3312 ctrl_idx = ctrl_new_page_count - 1
3314 if ctrl_idx >= 0 and ctrl_idx < ctrl.GetPageCount():
3316 ctrl_idx = self.FindNextActiveTab(ctrl_idx, ctrl)
3318 # set new page as active in the tab split
3319 ctrl.SetActivePage(ctrl_idx)
3321 # if the page deleted was the current page for the
3322 # entire tab control, then record the window
3323 # pointer of the new active page for activation
3325 new_active = ctrl.GetWindowFromIdx(ctrl_idx)
3329 # we are not deleting the active page, so keep it the same
3330 new_active = active_wnd
3334 # we haven't yet found a new page to active,
3335 # so select the next page from the main tab
3338 if 0 <= page_idx < self._tabs.GetPageCount():
3339 new_active = self._tabs.GetPage(page_idx).window
3340 if not new_active and self._tabs.GetPageCount() > 0:
3341 new_active = self._tabs.GetPage(0).window
3343 self.RemoveEmptyTabFrames()
3345 # set new active pane
3347 if not self.IsBeingDeleted():
3349 self.SetSelectionToWindow(new_active)
3352 self._tabs.SetNoneActive()
3357 def FindNextActiveTab(self, ctrl_idx, ctrl):
3359 Finds the next active tab (used mainly when L{AuiNotebook} has inactive/disabled
3362 :param `ctrl_idx`: the index of the first (most obvious) tab to check for active status;
3363 :param `ctrl`: an instance of L{AuiTabCtrl}.
3366 if self.GetEnabled(ctrl_idx):
3369 for indx in xrange(ctrl_idx, ctrl.GetPageCount()):
3370 if self.GetEnabled(indx):
3373 for indx in xrange(ctrl_idx, -1, -1):
3374 if self.GetEnabled(indx):
3380 def HideAllTabs(self, hidden=True):
3382 Hides all tabs on the L{AuiNotebook} control.
3384 :param `hidden`: if ``True`` hides all tabs.
3387 self._hide_tabs = hidden
3390 def SetSashDClickUnsplit(self, unsplit=True):
3392 Sets whether to unsplit a splitted L{AuiNotebook} when double-clicking on a sash.
3394 :param `unsplit`: ``True`` to unsplit on sash double-clicking, ``False`` otherwise.
3397 self._sash_dclick_unsplit = unsplit
3400 def GetSashDClickUnsplit(self):
3402 Returns whether a splitted L{AuiNotebook} can be unsplitted by double-clicking
3403 on the splitter sash.
3406 return self._sash_dclick_unsplit
3409 def SetMinMaxTabWidth(self, minTabWidth, maxTabWidth):
3411 Sets the minimum and/or the maximum tab widths for L{AuiNotebook} when the
3412 ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
3414 Pass -1 to either `minTabWidth` or `maxTabWidth` to reset to the default tab
3415 width behaviour for L{AuiNotebook}.
3417 :param `minTabWidth`: the minimum allowed tab width, in pixels;
3418 :param `maxTabWidth`: the maximum allowed tab width, in pixels.
3420 :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
3424 if minTabWidth > maxTabWidth:
3425 raise Exception("Minimum tab width must be less or equal than maximum tab width")
3427 self._tabBounds = (minTabWidth, maxTabWidth)
3428 self.SetAGWWindowStyleFlag(self._agwFlags)
3431 def GetMinMaxTabWidth(self):
3433 Returns the minimum and the maximum tab widths for L{AuiNotebook} when the
3434 ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
3436 :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
3439 :see: L{SetMinMaxTabWidth} for more information.
3442 return self._tabBounds
3445 def GetPageIndex(self, page_wnd):
3447 Returns the page index for the specified window. If the window is not
3448 found in the notebook, ``wx.NOT_FOUND`` is returned.
3451 return self._tabs.GetIdxFromWindow(page_wnd)
3454 def SetPageText(self, page_idx, text):
3456 Sets the tab label for the page.
3458 :param `page_idx`: the page index;
3459 :param `text`: the new tab label.
3462 if page_idx >= self._tabs.GetPageCount():
3465 # update our own tab catalog
3466 page_info = self._tabs.GetPage(page_idx)
3467 should_refresh = page_info.caption != text
3468 page_info.caption = text
3470 # update what's on screen
3471 ctrl, ctrl_idx = self.FindTab(page_info.window)
3475 info = ctrl.GetPage(ctrl_idx)
3476 should_refresh = should_refresh or info.caption != text
3483 self.UpdateTabCtrlHeight(force=True)
3488 def GetPageText(self, page_idx):
3490 Returns the tab label for the page.
3492 :param `page_idx`: the page index.
3495 if page_idx >= self._tabs.GetPageCount():
3498 # update our own tab catalog
3499 page_info = self._tabs.GetPage(page_idx)
3500 return page_info.caption
3503 def SetPageBitmap(self, page_idx, bitmap):
3505 Sets the tab bitmap for the page.
3507 :param `page_idx`: the page index;
3508 :param `bitmap`: an instance of `wx.Bitmap`.
3511 if page_idx >= self._tabs.GetPageCount():
3514 # update our own tab catalog
3515 page_info = self._tabs.GetPage(page_idx)
3516 should_refresh = page_info.bitmap is not bitmap
3517 page_info.bitmap = bitmap
3518 if bitmap.IsOk() and not page_info.dis_bitmap.IsOk():
3519 page_info.dis_bitmap = MakeDisabledBitmap(bitmap)
3521 # tab height might have changed
3522 self.UpdateTabCtrlHeight()
3524 # update what's on screen
3525 ctrl, ctrl_idx = self.FindTab(page_info.window)
3529 info = ctrl.GetPage(ctrl_idx)
3530 should_refresh = should_refresh or info.bitmap is not bitmap
3531 info.bitmap = bitmap
3532 info.dis_bitmap = page_info.dis_bitmap
3540 def GetPageBitmap(self, page_idx):
3542 Returns the tab bitmap for the page.
3544 :param `page_idx`: the page index.
3547 if page_idx >= self._tabs.GetPageCount():
3548 return wx.NullBitmap
3550 # update our own tab catalog
3551 page_info = self._tabs.GetPage(page_idx)
3552 return page_info.bitmap
3555 def SetImageList(self, imageList):
3557 Sets the image list for the L{AuiNotebook} control.
3559 :param `imageList`: an instance of `wx.ImageList`.
3562 self._imageList = imageList
3565 def AssignImageList(self, imageList):
3567 Sets the image list for the L{AuiNotebook} control.
3569 :param `imageList`: an instance of `wx.ImageList`.
3572 self.SetImageList(imageList)
3575 def GetImageList(self):
3576 """ Returns the associated image list (if any). """
3578 return self._imageList
3581 def SetPageImage(self, page, image):
3583 Sets the image index for the given page.
3585 :param `page`: the page index;
3586 :param `image`: an index into the image list which was set with L{SetImageList}.
3589 if page >= self._tabs.GetPageCount():
3592 if not isinstance(image, types.IntType):
3593 raise Exception("The image parameter must be an integer, you passed " \
3596 if not self._imageList:
3597 raise Exception("To use SetPageImage you need to associate an image list " \
3598 "Using SetImageList or AssignImageList")
3600 if image >= self._imageList.GetImageCount():
3601 raise Exception("Invalid image index (%d), the image list contains only" \
3602 " (%d) bitmaps"%(image, self._imageList.GetImageCount()))
3605 self.SetPageBitmap(page, wx.NullBitmap)
3608 bitmap = self._imageList.GetBitmap(image)
3609 self.SetPageBitmap(page, bitmap)
3612 def GetPageImage(self, page):
3614 Returns the image index for the given page.
3616 :param `page`: the given page for which to retrieve the image index.
3619 if page >= self._tabs.GetPageCount():
3622 bitmap = self.GetPageBitmap(page)
3623 for indx in xrange(self._imageList.GetImageCount()):
3624 imgListBmp = self._imageList.GetBitmap(indx)
3625 if imgListBmp == bitmap:
3631 def SetPageTextColour(self, page_idx, colour):
3633 Sets the tab text colour for the page.
3635 :param `page_idx`: the page index;
3636 :param `colour`: an instance of `wx.Colour`.
3639 if page_idx >= self._tabs.GetPageCount():
3642 # update our own tab catalog
3643 page_info = self._tabs.GetPage(page_idx)
3644 should_refresh = page_info.text_colour != colour
3645 page_info.text_colour = colour
3647 # update what's on screen
3648 ctrl, ctrl_idx = self.FindTab(page_info.window)
3652 info = ctrl.GetPage(ctrl_idx)
3653 should_refresh = should_refresh or info.text_colour != colour
3654 info.text_colour = page_info.text_colour
3663 def GetPageTextColour(self, page_idx):
3665 Returns the tab text colour for the page.
3667 :param `page_idx`: the page index.
3670 if page_idx >= self._tabs.GetPageCount():
3671 return wx.NullColour
3673 # update our own tab catalog
3674 page_info = self._tabs.GetPage(page_idx)
3675 return page_info.text_colour
3678 def AddControlToPage(self, page_idx, control):
3680 Adds a control inside a tab (not in the tab area).
3682 :param `page_idx`: the page index;
3683 :param `control`: an instance of `wx.Window`.
3686 if page_idx >= self._tabs.GetPageCount():
3689 # update our own tab catalog
3690 page_info = self._tabs.GetPage(page_idx)
3691 page_info.control = control
3693 # tab height might have changed
3694 self.UpdateTabCtrlHeight(force=True)
3696 # update what's on screen
3697 ctrl, ctrl_idx = self.FindTab(page_info.window)
3701 control.Reparent(ctrl)
3703 info = ctrl.GetPage(ctrl_idx)
3704 info.control = control
3711 def RemoveControlFromPage(self, page_idx):
3713 Removes a control from a tab (not from the tab area).
3715 :param `page_idx`: the page index.
3718 if page_idx >= self._tabs.GetPageCount():
3721 page_info = self._tabs.GetPage(page_idx)
3722 if page_info.control is None:
3725 page_info.control.Destroy()
3726 page_info.control = None
3728 # tab height might have changed
3729 self.UpdateTabCtrlHeight(force=True)
3731 # update what's on screen
3732 ctrl, ctrl_idx = self.FindTab(page_info.window)
3736 info = ctrl.GetPage(ctrl_idx)
3744 def SetCloseButton(self, page_idx, hasCloseButton):
3746 Sets whether a tab should display a close button or not.
3748 :param `page_idx`: the page index;
3749 :param `hasCloseButton`: ``True`` if the page displays a close button.
3751 :note: This can only be called if ``AUI_NB_CLOSE_ON_ALL_TABS`` is specified.
3754 if page_idx >= self._tabs.GetPageCount():
3757 if self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS == 0:
3758 raise Exception("SetCloseButton can only be used with AUI_NB_CLOSE_ON_ALL_TABS style.")
3760 # update our own tab catalog
3761 page_info = self._tabs.GetPage(page_idx)
3762 page_info.hasCloseButton = hasCloseButton
3764 # update what's on screen
3765 ctrl, ctrl_idx = self.FindTab(page_info.window)
3769 info = ctrl.GetPage(ctrl_idx)
3770 info.hasCloseButton = page_info.hasCloseButton
3777 def HasCloseButton(self, page_idx):
3779 Returns whether a tab displays a close button or not.
3781 :param `page_idx`: the page index.
3783 :note: This can only be called if ``AUI_NB_CLOSE_ON_ALL_TABS`` is specified.
3786 if page_idx >= self._tabs.GetPageCount():
3789 page_info = self._tabs.GetPage(page_idx)
3790 return page_info.hasCloseButton
3793 def GetSelection(self):
3794 """ Returns the index of the currently active page, or -1 if none was selected. """
3796 return self._curpage
3799 def GetCurrentPage(self):
3800 """ Returns the currently active page (not the index), or ``None`` if none was selected. """
3802 if self._curpage >= 0 and self._curpage < self._tabs.GetPageCount():
3803 return self.GetPage(self._curpage)
3808 def EnsureVisible(self, indx):
3810 Ensures the input page index `indx` is visible.
3812 :param `indx`: the page index.
3815 self._tabs.MakeTabVisible(indx, self)
3818 def SetSelection(self, new_page, force=False):
3820 Sets the page selection. Calling this method will generate a page change event.
3822 :param `new_page`: the index of the new selection;
3823 :param `force`: whether to force the selection or not.
3825 wnd = self._tabs.GetWindowFromIdx(new_page)
3827 #Update page access time
3828 self._tabs.GetPages()[new_page].access_time = datetime.datetime.now()
3830 if not wnd or not self.GetEnabled(new_page):
3831 return self._curpage
3833 # don't change the page unless necessary
3834 # however, clicking again on a tab should give it the focus.
3835 if new_page == self._curpage and not force:
3837 ctrl, ctrl_idx = self.FindTab(wnd)
3838 if wx.Window.FindFocus() != ctrl:
3841 return self._curpage
3843 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
3844 evt.SetSelection(new_page)
3845 evt.SetOldSelection(self._curpage)
3846 evt.SetEventObject(self)
3848 if not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed():
3850 old_curpage = self._curpage
3851 self._curpage = new_page
3853 # program allows the page change
3854 evt.SetEventType(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED)
3855 self.GetEventHandler().ProcessEvent(evt)
3857 if not evt.IsAllowed(): # event is no longer allowed after handler
3858 return self._curpage
3860 ctrl, ctrl_idx = self.FindTab(wnd)
3863 self._tabs.SetActivePage(wnd)
3864 ctrl.SetActivePage(ctrl_idx)
3867 ctrl.MakeTabVisible(ctrl_idx, ctrl)
3870 all_panes = self._mgr.GetAllPanes()
3871 for pane in all_panes:
3872 if pane.name == "dummy":
3875 tabctrl = pane.window._tabs
3877 tabctrl.SetSelectedFont(self._normal_font)
3879 tabctrl.SetSelectedFont(self._selected_font)
3884 # Set the focus to the page if we're not currently focused on the tab.
3885 # This is Firefox-like behaviour.
3886 if wnd.IsShownOnScreen() and wx.Window.FindFocus() != ctrl:
3891 return self._curpage
3894 def SetSelectionToWindow(self, win):
3896 Sets the selection based on the input window `win`.
3898 :param `win`: a `wx.Window` derived window.
3901 idx = self._tabs.GetIdxFromWindow(win)
3903 if idx == wx.NOT_FOUND:
3904 raise Exception("invalid notebook page")
3906 if not self.GetEnabled(idx):
3909 # since a tab was clicked, let the parent know that we received
3910 # the focus, even if we will assign that focus immediately
3911 # to the child tab in the SetSelection call below
3912 # (the child focus event will also let AuiManager, if any,
3913 # know that the notebook control has been activated)
3915 parent = self.GetParent()
3917 eventFocus = wx.ChildFocusEvent(self)
3918 parent.GetEventHandler().ProcessEvent(eventFocus)
3920 self.SetSelection(idx)
3923 def SetSelectionToPage(self, page):
3925 Sets the selection based on the input page.
3927 :param `page`: an instance of L{AuiNotebookPage}.
3930 self.SetSelectionToWindow(page.window)
3933 def GetPageCount(self):
3934 """ Returns the number of pages in the notebook. """
3936 return self._tabs.GetPageCount()
3939 def GetPage(self, page_idx):
3941 Returns the page specified by the given index.
3943 :param `page_idx`: the page index.
3946 if page_idx >= self._tabs.GetPageCount():
3947 raise Exception("invalid notebook page")
3949 return self._tabs.GetWindowFromIdx(page_idx)
3952 def GetPageInfo(self, page_idx):
3954 Returns the L{AuiNotebookPage} info structure specified by the given index.
3956 :param `page_idx`: the page index.
3959 if page_idx >= self._tabs.GetPageCount():
3960 raise Exception("invalid notebook page")
3962 return self._tabs.GetPage(page_idx)
3965 def GetEnabled(self, page_idx):
3967 Returns whether the page specified by the index `page_idx` is enabled.
3969 :param `page_idx`: the page index.
3972 return self._tabs.GetEnabled(page_idx)
3975 def EnableTab(self, page_idx, enable=True):
3977 Enables/disables a page in the notebook.
3979 :param `page_idx`: the page index;
3980 :param `enable`: ``True`` to enable the page, ``False`` to disable it.
3983 self._tabs.EnableTab(page_idx, enable)
3988 """ Performs all sizing operations in each tab control. """
3990 all_panes = self._mgr.GetAllPanes()
3991 for pane in all_panes:
3992 if pane.name == "dummy":
3995 tabframe = pane.window
3999 def GetAuiManager(self):
4000 """ Returns the associated L{AuiManager}. """
4005 def GetActiveTabCtrl(self):
4007 Returns the active tab control. It is called to determine which control
4008 gets new windows being added.
4011 if self._curpage >= 0 and self._curpage < self._tabs.GetPageCount():
4013 # find the tab ctrl with the current page
4014 ctrl, idx = self.FindTab(self._tabs.GetPage(self._curpage).window)
4018 # no current page, just find the first tab ctrl
4019 all_panes = self._mgr.GetAllPanes()
4020 for pane in all_panes:
4021 if pane.name == "dummy":
4024 tabframe = pane.window
4025 return tabframe._tabs
4027 # If there is no tabframe at all, create one
4028 tabframe = TabFrame(self)
4029 tabframe.SetTabCtrlHeight(self._tab_ctrl_height)
4030 self._tab_id_counter += 1
4031 tabframe._tabs = AuiTabCtrl(self, self._tab_id_counter)
4033 tabframe._tabs.SetAGWFlags(self._agwFlags)
4034 tabframe._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
4035 self._mgr.AddPane(tabframe, framemanager.AuiPaneInfo().Center().CaptionVisible(False).
4036 PaneBorder((self._agwFlags & AUI_NB_SUB_NOTEBOOK) == 0))
4040 return tabframe._tabs
4043 def FindTab(self, page):
4045 Finds the tab control that currently contains the window as well
4046 as the index of the window in the tab control. It returns ``True`` if the
4047 window was found, otherwise ``False``.
4049 :param `page`: an instance of L{AuiNotebookPage}.
4052 all_panes = self._mgr.GetAllPanes()
4053 for pane in all_panes:
4054 if pane.name == "dummy":
4057 tabframe = pane.window
4059 page_idx = tabframe._tabs.GetIdxFromWindow(page)
4063 ctrl = tabframe._tabs
4067 return None, wx.NOT_FOUND
4070 def Split(self, page, direction):
4072 Performs a split operation programmatically.
4074 :param `page`: indicates the page that will be split off. This page will also become
4075 the active page after the split.
4076 :param `direction`: specifies where the pane should go, it should be one of the
4077 following: ``wx.TOP``, ``wx.BOTTOM``, ``wx.LEFT``, or ``wx.RIGHT``.
4080 cli_size = self.GetClientSize()
4082 # get the page's window pointer
4083 wnd = self.GetPage(page)
4087 # notebooks with 1 or less pages can't be split
4088 if self.GetPageCount() < 2:
4091 # find out which tab control the page currently belongs to
4093 src_tabs, src_idx = self.FindTab(wnd)
4097 # choose a split size
4098 if self.GetPageCount() > 2:
4099 split_size = self.CalculateNewSplitSize()
4101 # because there are two panes, always split them
4103 split_size = self.GetClientSize()
4107 # create a new tab frame
4108 new_tabs = TabFrame(self)
4109 new_tabs._rect = wx.RectPS(wx.Point(0, 0), split_size)
4110 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
4111 self._tab_id_counter += 1
4112 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
4114 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
4115 new_tabs._tabs.SetAGWFlags(self._agwFlags)
4116 dest_tabs = new_tabs._tabs
4118 page_info = src_tabs.GetPage(src_idx)
4119 if page_info.control:
4120 self.ReparentControl(page_info.control, dest_tabs)
4122 # create a pane info structure with the information
4123 # about where the pane should be added
4124 pane_info = framemanager.AuiPaneInfo().Bottom().CaptionVisible(False)
4126 if direction == wx.LEFT:
4129 mouse_pt = wx.Point(0, cli_size.y/2)
4131 elif direction == wx.RIGHT:
4134 mouse_pt = wx.Point(cli_size.x, cli_size.y/2)
4136 elif direction == wx.TOP:
4139 mouse_pt = wx.Point(cli_size.x/2, 0)
4141 elif direction == wx.BOTTOM:
4144 mouse_pt = wx.Point(cli_size.x/2, cli_size.y)
4146 self._mgr.AddPane(new_tabs, pane_info, mouse_pt)
4149 # remove the page from the source tabs
4150 page_info.active = False
4152 src_tabs.RemovePage(page_info.window)
4154 if src_tabs.GetPageCount() > 0:
4155 src_tabs.SetActivePage(0)
4156 src_tabs.DoShowHide()
4159 # add the page to the destination tabs
4160 dest_tabs.InsertPage(page_info.window, page_info, 0)
4162 if src_tabs.GetPageCount() == 0:
4163 self.RemoveEmptyTabFrames()
4166 dest_tabs.DoShowHide()
4169 # force the set selection function reset the selection
4172 # set the active page to the one we just split off
4173 self.SetSelectionToPage(page_info)
4175 self.UpdateHintWindowSize()
4179 """ Restores original view after a tab split. """
4183 # remember the tab now selected
4184 nowSelected = self.GetSelection()
4185 # select first tab as destination
4186 self.SetSelection(0)
4187 # iterate all other tabs
4188 for idx in xrange(1, self.GetPageCount()):
4190 win = self.GetPage(idx)
4192 title = self.GetPageText(idx)
4194 bmp = self.GetPageBitmap(idx)
4195 # remove from notebook
4196 self.RemovePage(idx)
4197 # re-add in the same position so it will tab
4198 self.InsertPage(idx, win, title, False, bmp)
4199 # restore orignial selected tab
4200 self.SetSelection(nowSelected)
4205 def ReparentControl(self, control, dest_tabs):
4207 Reparents a control added inside a tab.
4209 :param `control`: an instance of `wx.Window`;
4210 :param `dest_tabs`: the destination L{AuiTabCtrl}.
4214 control.Reparent(dest_tabs)
4217 def UnsplitDClick(self, part, sash_size, pos):
4219 Unsplit the L{AuiNotebook} on sash double-click.
4221 :param `part`: an UI part representing the sash;
4222 :param `sash_size`: the sash size;
4223 :param `pos`: the double-click mouse position.
4225 :warning: Due to a bug on MSW, for disabled pages `wx.FindWindowAtPoint`
4226 returns the wrong window. See http://trac.wxwidgets.org/ticket/2942
4229 if not self._sash_dclick_unsplit:
4230 # Unsplit not allowed
4233 pos1 = wx.Point(*pos)
4234 pos2 = wx.Point(*pos)
4235 if part.orientation == wx.HORIZONTAL:
4236 pos1.y -= 2*sash_size
4237 pos2.y += 2*sash_size + self.GetTabCtrlHeight()
4238 elif part.orientation == wx.VERTICAL:
4239 pos1.x -= 2*sash_size
4240 pos2.x += 2*sash_size
4242 raise Exception("Invalid UI part orientation")
4244 pos1, pos2 = self.ClientToScreen(pos1), self.ClientToScreen(pos2)
4245 win1, win2 = wx.FindWindowAtPoint(pos1), wx.FindWindowAtPoint(pos2)
4247 if isinstance(win1, wx.ScrollBar):
4248 # Hopefully it will work
4249 pos1 = wx.Point(*pos)
4250 shift = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + 2*(sash_size+1)
4251 if part.orientation == wx.HORIZONTAL:
4256 pos1 = self.ClientToScreen(pos1)
4257 win1 = wx.FindWindowAtPoint(pos1)
4259 if isinstance(win2, wx.ScrollBar):
4260 pos2 = wx.Point(*pos)
4261 shift = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + 2*(sash_size+1)
4262 if part.orientation == wx.HORIZONTAL:
4267 pos2 = self.ClientToScreen(pos2)
4268 win2 = wx.FindWindowAtPoint(pos2)
4270 if not win1 or not win2:
4271 # How did we get here?
4274 if isinstance(win1, AuiNotebook) or isinstance(win2, AuiNotebook):
4275 # This is a bug on MSW, for disabled pages wx.FindWindowAtPoint
4276 # returns the wrong window.
4277 # See http://trac.wxwidgets.org/ticket/2942
4280 tab_frame1, tab_frame2 = self.GetTabFrameFromWindow(win1), self.GetTabFrameFromWindow(win2)
4282 if not tab_frame1 or not tab_frame2:
4285 tab_ctrl_1, tab_ctrl_2 = tab_frame1._tabs, tab_frame2._tabs
4287 if tab_ctrl_1.GetPageCount() > tab_ctrl_2.GetPageCount():
4288 src_tabs = tab_ctrl_2
4289 dest_tabs = tab_ctrl_1
4291 src_tabs = tab_ctrl_1
4292 dest_tabs = tab_ctrl_2
4295 page_count = dest_tabs.GetPageCount()
4297 for page in xrange(src_tabs.GetPageCount()-1, -1, -1):
4298 # remove the page from the source tabs
4299 page_info = src_tabs.GetPage(page)
4300 if page_info.active:
4301 selection = page_count + page
4302 src_tabs.RemovePage(page_info.window)
4304 # add the page to the destination tabs
4305 dest_tabs.AddPage(page_info.window, page_info)
4306 if page_info.control:
4307 self.ReparentControl(page_info.control, dest_tabs)
4309 self.RemoveEmptyTabFrames()
4311 dest_tabs.DoShowHide()
4316 wx.CallAfter(dest_tabs.MakeTabVisible, selection, self)
4319 def OnSize(self, event):
4321 Handles the ``wx.EVT_SIZE`` event for L{AuiNotebook}.
4323 :param `event`: a `wx.SizeEvent` event to be processed.
4326 self.UpdateHintWindowSize()
4330 def OnTabClicked(self, event):
4332 Handles the ``EVT_AUINOTEBOOK_PAGE_CHANGING`` event for L{AuiNotebook}.
4334 :param `event`: a L{AuiNotebookEvent} event to be processed.
4337 if self._textCtrl is not None:
4338 self._textCtrl.StopEditing()
4340 ctrl = event.GetEventObject()
4343 wnd = ctrl.GetWindowFromIdx(event.GetSelection())
4346 self.SetSelectionToWindow(wnd)
4349 def OnTabBgDClick(self, event):
4351 Handles the ``EVT_AUINOTEBOOK_BG_DCLICK`` event for L{AuiNotebook}.
4353 :param `event`: a L{AuiNotebookEvent} event to be processed.
4356 if self._textCtrl is not None:
4357 self._textCtrl.StopEditing()
4359 # notify owner that the tabbar background has been double-clicked
4360 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, self.GetId())
4361 e.SetEventObject(self)
4362 self.GetEventHandler().ProcessEvent(e)
4365 def OnTabDClick(self, event):
4367 Handles the ``EVT_AUINOTEBOOK_TAB_DCLICK`` event for L{AuiNotebook}.
4369 :param `event`: a L{AuiNotebookEvent} event to be processed.
4372 # notify owner that the tabbar background has been double-clicked
4373 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, self.GetId())
4374 e.SetEventObject(self)
4375 self.GetEventHandler().ProcessEvent(e)
4377 tabs = event.GetEventObject()
4378 if not tabs.GetEnabled(event.GetSelection()):
4381 if not self.IsRenamable(event.GetSelection()):
4384 self.EditTab(event.GetSelection())
4387 def OnTabBeginDrag(self, event):
4389 Handles the ``EVT_AUINOTEBOOK_BEGIN_DRAG`` event for L{AuiNotebook}.
4391 :param `event`: a L{AuiNotebookEvent} event to be processed.
4394 tabs = event.GetEventObject()
4395 if not tabs.GetEnabled(event.GetSelection()):
4398 self._last_drag_x = 0
4401 def OnTabDragMotion(self, event):
4403 Handles the ``EVT_AUINOTEBOOK_DRAG_MOTION`` event for L{AuiNotebook}.
4405 :param `event`: a L{AuiNotebookEvent} event to be processed.
4408 tabs = event.GetEventObject()
4409 if not tabs.GetEnabled(event.GetSelection()):
4412 if self._textCtrl is not None:
4413 self._textCtrl.StopEditing()
4415 screen_pt = wx.GetMousePosition()
4416 client_pt = self.ScreenToClient(screen_pt)
4417 zero = wx.Point(0, 0)
4419 src_tabs = event.GetEventObject()
4420 dest_tabs = self.GetTabCtrlFromPoint(client_pt)
4422 if dest_tabs == src_tabs:
4424 # always hide the hint for inner-tabctrl drag
4425 self._mgr.HideHint()
4427 # if tab moving is not allowed, leave
4428 if not self._agwFlags & AUI_NB_TAB_MOVE:
4431 pt = dest_tabs.ScreenToClient(screen_pt)
4433 # this is an inner-tab drag/reposition
4434 dest_location_tab = dest_tabs.TabHitTest(pt.x, pt.y)
4436 if dest_location_tab:
4438 src_idx = event.GetSelection()
4439 dest_idx = dest_tabs.GetIdxFromWindow(dest_location_tab)
4441 # prevent jumpy drag
4442 if (src_idx == dest_idx) or dest_idx == -1 or \
4443 (src_idx > dest_idx and self._last_drag_x <= pt.x) or \
4444 (src_idx < dest_idx and self._last_drag_x >= pt.x):
4446 self._last_drag_x = pt.x
4449 src_tab = dest_tabs.GetWindowFromIdx(src_idx)
4450 dest_tabs.MovePage(src_tab, dest_idx)
4451 self._tabs.MovePage(self._tabs.GetPage(src_idx).window, dest_idx)
4452 dest_tabs.SetActivePage(dest_idx)
4453 dest_tabs.DoShowHide()
4455 self._last_drag_x = pt.x
4459 # if external drag is allowed, check if the tab is being dragged
4460 # over a different AuiNotebook control
4461 if self._agwFlags & AUI_NB_TAB_EXTERNAL_MOVE:
4463 tab_ctrl = wx.FindWindowAtPoint(screen_pt)
4465 # if we aren't over any window, stop here
4467 if self._agwFlags & AUI_NB_TAB_FLOAT:
4468 if self.IsMouseWellOutsideWindow():
4469 hintRect = wx.RectPS(screen_pt, (400, 300))
4470 # Use CallAfter so we overwrite the hint that might be
4471 # shown by our superclass:
4472 wx.CallAfter(self._mgr.ShowHint, hintRect)
4475 # make sure we are not over the hint window
4476 if not isinstance(tab_ctrl, wx.Frame):
4478 if isinstance(tab_ctrl, AuiTabCtrl):
4481 tab_ctrl = tab_ctrl.GetParent()
4484 nb = tab_ctrl.GetParent()
4488 hint_rect = tab_ctrl.GetClientRect()
4489 hint_rect.x, hint_rect.y = tab_ctrl.ClientToScreenXY(hint_rect.x, hint_rect.y)
4490 self._mgr.ShowHint(hint_rect)
4496 # we are either over a hint window, or not over a tab
4497 # window, and there is no where to drag to, so exit
4500 if self._agwFlags & AUI_NB_TAB_FLOAT:
4501 if self.IsMouseWellOutsideWindow():
4502 hintRect = wx.RectPS(screen_pt, (400, 300))
4503 # Use CallAfter so we overwrite the hint that might be
4504 # shown by our superclass:
4505 wx.CallAfter(self._mgr.ShowHint, hintRect)
4508 # if there are less than two panes, split can't happen, so leave
4509 if self._tabs.GetPageCount() < 2:
4512 # if tab moving is not allowed, leave
4513 if not self._agwFlags & AUI_NB_TAB_SPLIT:
4518 hint_rect = dest_tabs.GetRect()
4519 hint_rect.x, hint_rect.y = self.ClientToScreenXY(hint_rect.x, hint_rect.y)
4520 self._mgr.ShowHint(hint_rect)
4523 rect = self._mgr.CalculateHintRect(self._dummy_wnd, client_pt, zero)
4525 self._mgr.HideHint()
4528 hit_wnd = wx.FindWindowAtPoint(screen_pt)
4529 if hit_wnd and not isinstance(hit_wnd, AuiNotebook):
4530 tab_frame = self.GetTabFrameFromWindow(hit_wnd)
4532 hint_rect = wx.Rect(*tab_frame._rect)
4533 hint_rect.x, hint_rect.y = self.ClientToScreenXY(hint_rect.x, hint_rect.y)
4534 rect.Intersect(hint_rect)
4535 self._mgr.ShowHint(rect)
4537 self._mgr.DrawHintRect(self._dummy_wnd, client_pt, zero)
4539 self._mgr.DrawHintRect(self._dummy_wnd, client_pt, zero)
4542 def OnTabEndDrag(self, event):
4544 Handles the ``EVT_AUINOTEBOOK_END_DRAG`` event for L{AuiNotebook}.
4546 :param `event`: a L{AuiNotebookEvent} event to be processed.
4549 tabs = event.GetEventObject()
4550 if not tabs.GetEnabled(event.GetSelection()):
4553 self._mgr.HideHint()
4555 src_tabs = event.GetEventObject()
4557 raise Exception("no source object?")
4559 # get the mouse position, which will be used to determine the drop point
4560 mouse_screen_pt = wx.GetMousePosition()
4561 mouse_client_pt = self.ScreenToClient(mouse_screen_pt)
4563 # check for an external move
4564 if self._agwFlags & AUI_NB_TAB_EXTERNAL_MOVE:
4565 tab_ctrl = wx.FindWindowAtPoint(mouse_screen_pt)
4569 if isinstance(tab_ctrl, AuiTabCtrl):
4572 tab_ctrl = tab_ctrl.GetParent()
4576 nb = tab_ctrl.GetParent()
4580 # find out from the destination control
4581 # if it's ok to drop this tab here
4582 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, self.GetId())
4583 e.SetSelection(event.GetSelection())
4584 e.SetOldSelection(event.GetSelection())
4585 e.SetEventObject(self)
4586 e.SetDragSource(self)
4587 e.Veto() # dropping must be explicitly approved by control owner
4589 nb.GetEventHandler().ProcessEvent(e)
4591 if not e.IsAllowed():
4593 # no answer or negative answer
4594 self._mgr.HideHint()
4598 src_idx = event.GetSelection()
4599 src_page = src_tabs.GetWindowFromIdx(src_idx)
4601 # Check that it's not an impossible parent relationship
4603 while p and not p.IsTopLevel():
4609 # get main index of the page
4610 main_idx = self._tabs.GetIdxFromWindow(src_page)
4611 if main_idx == wx.NOT_FOUND:
4612 raise Exception("no source page?")
4614 # make a copy of the page info
4615 page_info = self._tabs.GetPage(main_idx)
4617 # remove the page from the source notebook
4618 self.RemovePage(main_idx)
4621 src_page.Reparent(nb)
4623 # Reparent the control in a tab (if any)
4624 if page_info.control:
4625 self.ReparentControl(page_info.control, tab_ctrl)
4627 # find out the insert idx
4628 dest_tabs = tab_ctrl
4629 pt = dest_tabs.ScreenToClient(mouse_screen_pt)
4631 target = dest_tabs.TabHitTest(pt.x, pt.y)
4634 insert_idx = dest_tabs.GetIdxFromWindow(target)
4636 # add the page to the new notebook
4637 if insert_idx == -1:
4638 insert_idx = dest_tabs.GetPageCount()
4640 dest_tabs.InsertPage(page_info.window, page_info, insert_idx)
4641 nb._tabs.AddPage(page_info.window, page_info)
4644 dest_tabs.DoShowHide()
4647 # set the selection in the destination tab control
4648 nb.SetSelectionToPage(page_info)
4650 # notify owner that the tab has been dragged
4651 e2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, self.GetId())
4652 e2.SetSelection(event.GetSelection())
4653 e2.SetOldSelection(event.GetSelection())
4654 e2.SetEventObject(self)
4655 self.GetEventHandler().ProcessEvent(e2)
4657 # notify the target notebook that the tab has been dragged
4658 e3 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, nb.GetId())
4659 e3.SetSelection(insert_idx)
4660 e3.SetOldSelection(insert_idx)
4661 e3.SetEventObject(nb)
4662 nb.GetEventHandler().ProcessEvent(e3)
4666 if self._agwFlags & AUI_NB_TAB_FLOAT:
4667 self._mgr.HideHint()
4668 if self.IsMouseWellOutsideWindow():
4669 # Use CallAfter so we our superclass can deal with the event first
4670 wx.CallAfter(self.FloatPage, self.GetSelection())
4674 # only perform a tab split if it's allowed
4677 if self._agwFlags & AUI_NB_TAB_SPLIT and self._tabs.GetPageCount() >= 2:
4679 # If the pointer is in an existing tab frame, do a tab insert
4680 hit_wnd = wx.FindWindowAtPoint(mouse_screen_pt)
4681 tab_frame = self.GetTabFrameFromTabCtrl(hit_wnd)
4686 dest_tabs = tab_frame._tabs
4688 if dest_tabs == src_tabs:
4691 pt = dest_tabs.ScreenToClient(mouse_screen_pt)
4692 target = dest_tabs.TabHitTest(pt.x, pt.y)
4695 insert_idx = dest_tabs.GetIdxFromWindow(target)
4699 zero = wx.Point(0, 0)
4700 rect = self._mgr.CalculateHintRect(self._dummy_wnd, mouse_client_pt, zero)
4703 # there is no suitable drop location here, exit out
4706 # If there is no tabframe at all, create one
4707 new_tabs = TabFrame(self)
4708 new_tabs._rect = wx.RectPS(wx.Point(0, 0), self.CalculateNewSplitSize())
4709 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
4710 self._tab_id_counter += 1
4711 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
4712 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
4713 new_tabs._tabs.SetAGWFlags(self._agwFlags)
4715 self._mgr.AddPane(new_tabs, framemanager.AuiPaneInfo().Bottom().CaptionVisible(False), mouse_client_pt)
4717 dest_tabs = new_tabs._tabs
4719 # remove the page from the source tabs
4720 page_info = src_tabs.GetPage(event.GetSelection())
4722 if page_info.control:
4723 self.ReparentControl(page_info.control, dest_tabs)
4725 page_info.active = False
4726 src_tabs.RemovePage(page_info.window)
4728 if src_tabs.GetPageCount() > 0:
4729 src_tabs.SetActivePage(0)
4730 src_tabs.DoShowHide()
4733 # add the page to the destination tabs
4734 if insert_idx == -1:
4735 insert_idx = dest_tabs.GetPageCount()
4737 dest_tabs.InsertPage(page_info.window, page_info, insert_idx)
4739 if src_tabs.GetPageCount() == 0:
4740 self.RemoveEmptyTabFrames()
4743 dest_tabs.DoShowHide()
4746 # force the set selection function reset the selection
4749 # set the active page to the one we just split off
4750 self.SetSelectionToPage(page_info)
4752 self.UpdateHintWindowSize()
4754 # notify owner that the tab has been dragged
4755 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, self.GetId())
4756 e.SetSelection(event.GetSelection())
4757 e.SetOldSelection(event.GetSelection())
4758 e.SetEventObject(self)
4759 self.GetEventHandler().ProcessEvent(e)
4762 def OnTabCancelDrag(self, event):
4764 Handles the ``EVT_AUINOTEBOOK_CANCEL_DRAG`` event for L{AuiNotebook}.
4766 :param `event`: a L{AuiNotebookEvent} event to be processed.
4769 tabs = event.GetEventObject()
4770 if not tabs.GetEnabled(event.GetSelection()):
4773 self._mgr.HideHint()
4775 src_tabs = event.GetEventObject()
4777 raise Exception("no source object?")
4780 def IsMouseWellOutsideWindow(self):
4781 """ Returns whether the mouse is well outside the L{AuiNotebook} screen rectangle. """
4783 screen_rect = self.GetScreenRect()
4784 screen_rect.Inflate(50, 50)
4786 return not screen_rect.Contains(wx.GetMousePosition())
4789 def FloatPage(self, page_index):
4791 Float the page in `page_index` by reparenting it to a floating frame.
4793 :param `page_index`: the index of the page to be floated.
4795 :warning: When the notebook is more or less full screen, tabs cannot be dragged far
4796 enough outside of the notebook to become floating pages.
4799 root_manager = framemanager.GetManager(self)
4800 page_title = self.GetPageText(page_index)
4801 page_contents = self.GetPage(page_index)
4802 page_bitmap = self.GetPageBitmap(page_index)
4803 text_colour = self.GetPageTextColour(page_index)
4804 info = self.GetPageInfo(page_index)
4806 if root_manager and root_manager != self._mgr:
4807 root_manager = framemanager.GetManager(self)
4809 if hasattr(page_contents, "__floating_size__"):
4810 floating_size = wx.Size(*page_contents.__floating_size__)
4812 floating_size = page_contents.GetBestSize()
4813 if floating_size == wx.DefaultSize:
4814 floating_size = wx.Size(300, 200)
4816 page_contents.__page_index__ = page_index
4817 page_contents.__aui_notebook__ = self
4818 page_contents.__text_colour__ = text_colour
4819 page_contents.__control__ = info.control
4822 info.control.Reparent(page_contents)
4826 self.RemovePage(page_index)
4827 self.RemoveEmptyTabFrames()
4829 pane_info = framemanager.AuiPaneInfo().Float().FloatingPosition(wx.GetMousePosition()). \
4830 FloatingSize(floating_size).BestSize(floating_size).Name("__floating__%s"%page_title). \
4831 Caption(page_title).Icon(page_bitmap)
4832 root_manager.AddPane(page_contents, pane_info)
4833 root_manager.Bind(framemanager.EVT_AUI_PANE_CLOSE, self.OnCloseFloatingPage)
4834 self.GetActiveTabCtrl().DoShowHide()
4836 root_manager.Update()
4839 frame = wx.Frame(self, title=page_title,
4840 style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_TOOL_WINDOW|
4841 wx.FRAME_FLOAT_ON_PARENT | wx.FRAME_NO_TASKBAR)
4844 info.control.Reparent(frame)
4847 frame.bitmap = page_bitmap
4848 frame.page_index = page_index
4849 frame.text_colour = text_colour
4850 frame.control = info.control
4851 page_contents.Reparent(frame)
4852 frame.Bind(wx.EVT_CLOSE, self.OnCloseFloatingPage)
4853 frame.Move(wx.GetMousePosition())
4855 self.RemovePage(page_index)
4857 self.RemoveEmptyTabFrames()
4859 wx.CallAfter(self.RemoveEmptyTabFrames)
4862 def OnCloseFloatingPage(self, event):
4864 Handles the ``wx.EVT_CLOSE`` event for a floating page in L{AuiNotebook}.
4866 :param `event`: a `wx.CloseEvent` event to be processed.
4869 root_manager = framemanager.GetManager(self)
4870 if root_manager and root_manager != self._mgr:
4872 if pane.name.startswith("__floating__"):
4873 self.ReDockPage(pane)
4879 frame = event.GetEventObject()
4880 page_title = frame.GetTitle()
4881 page_contents = list(frame.GetChildren())[-1]
4882 page_contents.Reparent(self)
4883 self.InsertPage(frame.page_index, page_contents, page_title, select=True, bitmap=frame.bitmap, control=frame.control)
4886 src_tabs, idx = self.FindTab(page_contents)
4887 frame.control.Reparent(src_tabs)
4888 frame.control.Hide()
4889 frame.control = None
4891 self.SetPageTextColour(frame.page_index, frame.text_colour)
4894 def ReDockPage(self, pane):
4896 Re-docks a floating L{AuiNotebook} tab in the original position, when possible.
4898 :param `pane`: an instance of L{framemanager.AuiPaneInfo}.
4901 root_manager = framemanager.GetManager(self)
4903 pane.window.__floating_size__ = wx.Size(*pane.floating_size)
4904 page_index = pane.window.__page_index__
4905 text_colour = pane.window.__text_colour__
4906 control = pane.window.__control__
4908 root_manager.DetachPane(pane.window)
4909 self.InsertPage(page_index, pane.window, pane.caption, True, pane.icon, control=control)
4911 self.SetPageTextColour(page_index, text_colour)
4912 self.GetActiveTabCtrl().DoShowHide()
4915 self.UpdateTabCtrlHeight(force=True)
4918 root_manager.Update()
4921 def GetTabCtrlFromPoint(self, pt):
4923 Returns the tab control at the specified point.
4925 :param `pt`: a `wx.Point` object.
4928 # if we've just removed the last tab from the source
4929 # tab set, the remove the tab control completely
4930 all_panes = self._mgr.GetAllPanes()
4931 for pane in all_panes:
4932 if pane.name == "dummy":
4935 tabframe = pane.window
4936 if tabframe._tab_rect.Contains(pt):
4937 return tabframe._tabs
4942 def GetTabFrameFromTabCtrl(self, tab_ctrl):
4944 Returns the tab frame associated with a tab control.
4946 :param `tab_ctrl`: an instance of L{AuiTabCtrl}.
4949 # if we've just removed the last tab from the source
4950 # tab set, the remove the tab control completely
4951 all_panes = self._mgr.GetAllPanes()
4952 for pane in all_panes:
4953 if pane.name == "dummy":
4956 tabframe = pane.window
4957 if tabframe._tabs == tab_ctrl:
4963 def GetTabFrameFromWindow(self, wnd):
4965 Returns the tab frame associated with a window.
4967 :param `wnd`: an instance of `wx.Window`.
4970 all_panes = self._mgr.GetAllPanes()
4971 for pane in all_panes:
4972 if pane.name == "dummy":
4975 tabframe = pane.window
4976 for page in tabframe._tabs.GetPages():
4977 if wnd == page.window:
4983 def RemoveEmptyTabFrames(self):
4984 """ Removes all the empty tab frames. """
4986 # if we've just removed the last tab from the source
4987 # tab set, the remove the tab control completely
4988 all_panes = self._mgr.GetAllPanes()
4990 for indx in xrange(len(all_panes)-1, -1, -1):
4991 pane = all_panes[indx]
4992 if pane.name == "dummy":
4995 tab_frame = pane.window
4996 if tab_frame._tabs.GetPageCount() == 0:
4997 self._mgr.DetachPane(tab_frame)
4998 tab_frame._tabs.Destroy()
4999 tab_frame._tabs = None
5002 # check to see if there is still a center pane
5003 # if there isn't, make a frame the center pane
5005 center_found = False
5007 all_panes = self._mgr.GetAllPanes()
5008 for pane in all_panes:
5009 if pane.name == "dummy":
5012 if pane.dock_direction == AUI_DOCK_CENTRE:
5015 first_good = pane.window
5017 if not center_found and first_good:
5018 self._mgr.GetPane(first_good).Centre()
5020 if not self.IsBeingDeleted():
5024 def OnChildFocusNotebook(self, event):
5026 Handles the ``wx.EVT_CHILD_FOCUS`` event for L{AuiNotebook}.
5028 :param `event`: a `wx.ChildFocusEvent` event to be processed.
5031 # if we're dragging a tab, don't change the current selection.
5032 # This code prevents a bug that used to happen when the hint window
5033 # was hidden. In the bug, the focus would return to the notebook
5034 # child, which would then enter this handler and call
5035 # SetSelection, which is not desired turn tab dragging.
5039 all_panes = self._mgr.GetAllPanes()
5040 for pane in all_panes:
5041 if pane.name == "dummy":
5043 tabframe = pane.window
5044 if tabframe._tabs.IsDragging():
5047 ## # change the tab selection to the child
5048 ## # which was focused
5049 ## idx = self._tabs.GetIdxFromWindow(event.GetWindow())
5050 ## if idx != -1 and idx != self._curpage:
5051 ## self.SetSelection(idx)
5054 def SetNavigatorIcon(self, bmp):
5056 Sets the icon used by the L{TabNavigatorWindow}.
5058 :param `bmp`: an instance of `wx.Bitmap`.
5061 if isinstance(bmp, wx.Bitmap) and bmp.IsOk():
5062 # Make sure image is proper size
5063 if bmp.GetSize() != (16, 16):
5064 img = bmp.ConvertToImage()
5065 img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
5066 bmp = wx.BitmapFromImage(img)
5067 self._naviIcon = bmp
5069 raise TypeError, "SetNavigatorIcon requires a valid bitmap"
5072 def OnNavigationKeyNotebook(self, event):
5074 Handles the ``wx.EVT_NAVIGATION_KEY`` event for L{AuiNotebook}.
5076 :param `event`: a `wx.NavigationKeyEvent` event to be processed.
5079 if event.IsWindowChange():
5080 if self._agwFlags & AUI_NB_SMART_TABS:
5081 if not self._popupWin:
5082 self._popupWin = TabNavigatorWindow(self, self._naviIcon)
5083 self._popupWin.SetReturnCode(wx.ID_OK)
5084 self._popupWin.ShowModal()
5085 idx = self._popupWin.GetSelectedPage()
5086 self._popupWin.Destroy()
5087 self._popupWin = None
5088 # Need to do CallAfter so that the selection and its
5089 # associated events get processed outside the context of
5090 # this key event. Not doing so causes odd issues with the
5091 # window focus under certain use cases on Windows.
5092 wx.CallAfter(self.SetSelection, idx, True)
5094 # a dialog is already opened
5095 self._popupWin.OnNavigationKey(event)
5099 # FIXME: the problem with this is that if we have a split notebook,
5100 # we selection may go all over the place.
5101 self.AdvanceSelection(event.GetDirection())
5104 # we get this event in 3 cases
5106 # a) one of our pages might have generated it because the user TABbed
5107 # out from it in which case we should propagate the event upwards and
5108 # our parent will take care of setting the focus to prev/next sibling
5112 # b) the parent panel wants to give the focus to us so that we
5113 # forward it to our selected page. We can't deal with this in
5114 # OnSetFocus() because we don't know which direction the focus came
5115 # from in this case and so can't choose between setting the focus to
5116 # first or last panel child
5120 # c) we ourselves (see MSWTranslateMessage) generated the event
5122 parent = self.GetParent()
5124 # the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
5125 isFromParent = event.GetEventObject() == parent
5126 isFromSelf = event.GetEventObject() == self
5128 if isFromParent or isFromSelf:
5130 # no, it doesn't come from child, case (b) or (c): forward to a
5131 # page but only if direction is backwards (TAB) or from ourselves,
5132 if self.GetSelection() != wx.NOT_FOUND and (not event.GetDirection() or isFromSelf):
5134 # so that the page knows that the event comes from it's parent
5135 # and is being propagated downwards
5136 event.SetEventObject(self)
5138 page = self.GetPage(self.GetSelection())
5139 if not page.GetEventHandler().ProcessEvent(event):
5142 #else: page manages focus inside it itself
5144 else: # otherwise set the focus to the notebook itself
5150 # send this event back for the 'wraparound' focus.
5151 winFocus = event.GetCurrentFocus()
5154 event.SetEventObject(self)
5155 winFocus.GetEventHandler().ProcessEvent(event)
5158 def OnTabButton(self, event):
5160 Handles the ``EVT_AUINOTEBOOK_BUTTON`` event for L{AuiNotebook}.
5162 :param `event`: a L{AuiNotebookEvent} event to be processed.
5165 tabs = event.GetEventObject()
5166 button_id = event.GetInt()
5168 if button_id == AUI_BUTTON_CLOSE:
5170 selection = event.GetSelection()
5174 # if the close button is to the right, use the active
5175 # page selection to determine which page to close
5176 selection = tabs.GetActivePage()
5178 if selection == -1 or not tabs.GetEnabled(selection):
5183 close_wnd = tabs.GetWindowFromIdx(selection)
5185 if close_wnd.GetName() == "__fake__page__":
5186 # This is a notebook preview
5187 previous_active, page_status = close_wnd.__previousStatus
5188 for page, status in zip(tabs.GetPages(), page_status):
5189 page.enabled = status
5191 main_idx = self._tabs.GetIdxFromWindow(close_wnd)
5192 self.DeletePage(main_idx)
5194 if previous_active >= 0:
5195 tabs.SetActivePage(previous_active)
5196 page_count = tabs.GetPageCount()
5199 for page in xrange(page_count):
5200 # remove the page from the source tabs
5201 page_info = tabs.GetPage(page)
5202 if page_info.active:
5211 wx.CallAfter(tabs.MakeTabVisible, selection, self)
5213 # Don't fire the event
5216 # ask owner if it's ok to close the tab
5217 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, self.GetId())
5218 idx = self._tabs.GetIdxFromWindow(close_wnd)
5220 e.SetOldSelection(event.GetSelection())
5221 e.SetEventObject(self)
5222 self.GetEventHandler().ProcessEvent(e)
5223 if not e.IsAllowed():
5226 if repr(close_wnd.__class__).find("AuiMDIChildFrame") >= 0:
5230 main_idx = self._tabs.GetIdxFromWindow(close_wnd)
5231 self.DeletePage(main_idx)
5233 # notify owner that the tab has been closed
5234 e2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, self.GetId())
5235 e2.SetSelection(idx)
5236 e2.SetEventObject(self)
5237 self.GetEventHandler().ProcessEvent(e2)
5239 if self.GetPageCount() == 0:
5240 mgr = self.GetAuiManager()
5241 win = mgr.GetManagedWindow()
5245 def OnTabMiddleDown(self, event):
5247 Handles the ``EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN`` event for L{AuiNotebook}.
5249 :param `event`: a L{AuiNotebookEvent} event to be processed.
5252 tabs = event.GetEventObject()
5253 if not tabs.GetEnabled(event.GetSelection()):
5256 # patch event through to owner
5257 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5259 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.GetId())
5260 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5261 e.SetEventObject(self)
5262 self.GetEventHandler().ProcessEvent(e)
5265 def OnTabMiddleUp(self, event):
5267 Handles the ``EVT_AUINOTEBOOK_TAB_MIDDLE_UP`` event for L{AuiNotebook}.
5269 :param `event`: a L{AuiNotebookEvent} event to be processed.
5272 tabs = event.GetEventObject()
5273 if not tabs.GetEnabled(event.GetSelection()):
5276 # if the AUI_NB_MIDDLE_CLICK_CLOSE is specified, middle
5277 # click should act like a tab close action. However, first
5278 # give the owner an opportunity to handle the middle up event
5281 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5283 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, self.GetId())
5284 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5285 e.SetEventObject(self)
5286 if self.GetEventHandler().ProcessEvent(e):
5288 if not e.IsAllowed():
5291 # check if we are supposed to close on middle-up
5292 if self._agwFlags & AUI_NB_MIDDLE_CLICK_CLOSE == 0:
5295 # simulate the user pressing the close button on the tab
5296 event.SetInt(AUI_BUTTON_CLOSE)
5297 self.OnTabButton(event)
5300 def OnTabRightDown(self, event):
5302 Handles the ``EVT_AUINOTEBOOK_TAB_RIGHT_DOWN`` event for L{AuiNotebook}.
5304 :param `event`: a L{AuiNotebookEvent} event to be processed.
5307 tabs = event.GetEventObject()
5308 if not tabs.GetEnabled(event.GetSelection()):
5311 # patch event through to owner
5312 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5314 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, self.GetId())
5315 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5316 e.SetEventObject(self)
5317 self.GetEventHandler().ProcessEvent(e)
5320 def OnTabRightUp(self, event):
5322 Handles the ``EVT_AUINOTEBOOK_TAB_RIGHT_UP`` event for L{AuiNotebook}.
5324 :param `event`: a L{AuiNotebookEvent} event to be processed.
5327 tabs = event.GetEventObject()
5328 if not tabs.GetEnabled(event.GetSelection()):
5331 # patch event through to owner
5332 wnd = tabs.GetWindowFromIdx(event.GetSelection())
5334 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, self.GetId())
5335 e.SetSelection(self._tabs.GetIdxFromWindow(wnd))
5336 e.SetEventObject(self)
5337 self.GetEventHandler().ProcessEvent(e)
5340 def SetNormalFont(self, font):
5342 Sets the normal font for drawing tab labels.
5344 :param `font`: a `wx.Font` object.
5347 self._normal_font = font
5348 self.GetArtProvider().SetNormalFont(font)
5351 def SetSelectedFont(self, font):
5353 Sets the selected tab font for drawing tab labels.
5355 :param `font`: a `wx.Font` object.
5358 self._selected_font = font
5359 self.GetArtProvider().SetSelectedFont(font)
5362 def SetMeasuringFont(self, font):
5364 Sets the font for calculating text measurements.
5366 :param `font`: a `wx.Font` object.
5369 self.GetArtProvider().SetMeasuringFont(font)
5372 def SetFont(self, font):
5376 :param `font`: a `wx.Font` object.
5378 :note: Overridden from `wx.PyPanel`.
5381 wx.PyPanel.SetFont(self, font)
5383 selectedFont = wx.Font(font.GetPointSize(), font.GetFamily(),
5384 font.GetStyle(), wx.BOLD, font.GetUnderlined(),
5385 font.GetFaceName(), font.GetEncoding())
5387 self.SetNormalFont(font)
5388 self.SetSelectedFont(selectedFont)
5389 self.SetMeasuringFont(selectedFont)
5391 # Recalculate tab container size based on new font
5392 self.UpdateTabCtrlHeight(force=False)
5398 def GetTabCtrlHeight(self):
5399 """ Returns the tab control height. """
5401 return self._tab_ctrl_height
5404 def GetHeightForPageHeight(self, pageHeight):
5406 Gets the height of the notebook for a given page height.
5408 :param `pageHeight`: the given page height.
5411 self.UpdateTabCtrlHeight()
5413 tabCtrlHeight = self.GetTabCtrlHeight()
5415 return tabCtrlHeight + pageHeight + decorHeight
5418 def AdvanceSelection(self, forward=True, wrap=True):
5420 Cycles through the tabs.
5422 :param `forward`: whether to advance forward or backward;
5423 :param `wrap`: ``True`` to return to the first tab if we reach the last tab.
5425 :note: The call to this function generates the page changing events.
5428 tabCtrl = self.GetActiveTabCtrl()
5431 focusWin = tabCtrl.FindFocus()
5432 activePage = tabCtrl.GetActivePage()
5433 lenPages = len(tabCtrl.GetPages())
5441 if activePage == -1 or activePage == lenPages - 1:
5447 elif activePage < lenPages - 1:
5448 newPage = activePage + 1
5453 if activePage == -1 or activePage == 0:
5457 newPage = lenPages - 1
5459 elif activePage > 0:
5460 newPage = activePage - 1
5464 if not self.GetEnabled(newPage):
5467 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, tabCtrl.GetId())
5468 e.SetSelection(newPage)
5469 e.SetOldSelection(activePage)
5470 e.SetEventObject(tabCtrl)
5471 self.GetEventHandler().ProcessEvent(e)
5474 ## focusWin.SetFocus()
5479 def ShowWindowMenu(self):
5481 Shows the window menu for the active tab control associated with this
5482 notebook, and returns ``True`` if a selection was made.
5485 tabCtrl = self.GetActiveTabCtrl()
5486 idx = tabCtrl.GetArtProvider().ShowDropDown(tabCtrl, tabCtrl.GetPages(), tabCtrl.GetActivePage())
5488 if not self.GetEnabled(idx):
5492 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, tabCtrl.GetId())
5494 e.SetOldSelection(tabCtrl.GetActivePage())
5495 e.SetEventObject(tabCtrl)
5496 self.GetEventHandler().ProcessEvent(e)
5505 def AddTabAreaButton(self, id, location, normal_bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap):
5507 Adds a button in the tab area.
5509 :param `id`: the button identifier. This can be one of the following:
5511 ============================== =================================
5512 Button Identifier Description
5513 ============================== =================================
5514 ``AUI_BUTTON_CLOSE`` Shows a close button on the tab area
5515 ``AUI_BUTTON_WINDOWLIST`` Shows a window list button on the tab area
5516 ``AUI_BUTTON_LEFT`` Shows a left button on the tab area
5517 ``AUI_BUTTON_RIGHT`` Shows a right button on the tab area
5518 ============================== =================================
5520 :param `location`: the button location. Can be ``wx.LEFT`` or ``wx.RIGHT``;
5521 :param `normal_bitmap`: the bitmap for an enabled tab;
5522 :param `disabled_bitmap`: the bitmap for a disabled tab.
5525 active_tabctrl = self.GetActiveTabCtrl()
5526 active_tabctrl.AddButton(id, location, normal_bitmap, disabled_bitmap)
5529 def RemoveTabAreaButton(self, id):
5531 Removes a button from the tab area.
5533 :param `id`: the button identifier. See L{AddTabAreaButton} for a list of button identifiers.
5535 :see: L{AddTabAreaButton}
5538 active_tabctrl = self.GetActiveTabCtrl()
5539 active_tabctrl.RemoveButton(id)
5542 def HasMultiplePages(self):
5544 This method should be overridden to return ``True`` if this window has multiple pages. All
5545 standard class with multiple pages such as `wx.Notebook`, `wx.Listbook` and `wx.Treebook`
5546 already override it to return ``True`` and user-defined classes with similar behaviour
5547 should do it as well to allow the library to handle such windows appropriately.
5549 :note: Overridden from `wx.PyPanel`.
5555 def GetDefaultBorder(self):
5556 """ Returns the default border style for L{AuiNotebook}. """
5558 return wx.BORDER_NONE
5561 def NotebookPreview(self, thumbnail_size=200):
5563 Generates a preview of all the pages in the notebook (MSW and GTK only).
5565 :param `thumbnail_size`: the maximum size of every page thumbnail.
5567 :note: this functionality is currently unavailable on wxMac.
5570 if wx.Platform == "__WXMAC__":
5573 tabCtrl = self.GetActiveTabCtrl()
5574 activePage = tabCtrl.GetActivePage()
5575 pages = tabCtrl.GetPages()
5577 pageStatus, pageText = [], []
5579 for indx, page in enumerate(pages):
5581 pageStatus.append(page.enabled)
5583 if not page.enabled:
5586 self.SetSelectionToPage(page)
5587 pageText.append(page.caption)
5589 rect = page.window.GetScreenRect()
5590 bmp = RescaleScreenShot(TakeScreenShot(rect), thumbnail_size)
5592 page.enabled = False
5594 il = wx.ImageList(bmp.GetWidth(), bmp.GetHeight(), True)
5598 # create the list control
5599 listCtrl = wx.ListCtrl(self, style=wx.LC_ICON|wx.LC_AUTOARRANGE|wx.LC_HRULES|wx.LC_VRULES,
5600 name="__fake__page__")
5602 # assign the image list to it
5603 listCtrl.AssignImageList(il, wx.IMAGE_LIST_NORMAL)
5604 listCtrl.__previousStatus = [activePage, pageStatus]
5606 # create some items for the list
5607 for indx, text in enumerate(pageText):
5608 listCtrl.InsertImageStringItem(10000, text, indx)
5610 self.AddPage(listCtrl, "AuiNotebook Preview", True, bitmap=auinotebook_preview.GetBitmap(), disabled_bitmap=wx.NullBitmap)
5614 def SetRenamable(self, page_idx, renamable):
5616 Sets whether a tab can be renamed via a left double-click or not.
5618 :param `page_idx`: the page index;
5619 :param `renamable`: ``True`` if the page can be renamed.
5622 if page_idx >= self._tabs.GetPageCount():
5625 # update our own tab catalog
5626 page_info = self._tabs.GetPage(page_idx)
5627 page_info.renamable = renamable
5629 # update what's on screen
5630 ctrl, ctrl_idx = self.FindTab(page_info.window)
5634 info = ctrl.GetPage(ctrl_idx)
5635 info.renamable = page_info.renamable
5640 def IsRenamable(self, page_idx):
5642 Returns whether a tab can be renamed or not.
5644 :param `page_idx`: the page index.
5646 :returns: ``True`` is a page can be renamed, ``False`` otherwise.
5649 if page_idx >= self._tabs.GetPageCount():
5652 page_info = self._tabs.GetPage(page_idx)
5653 return page_info.renamable
5656 def OnRenameCancelled(self, page_index):
5658 Called by L{TabTextCtrl}, to cancel the changes and to send the
5659 `EVT_AUINOTEBOOK_END_LABEL_EDIT` event.
5661 :param `page_index`: the page index in the notebook.
5664 # let owner know that the edit was cancelled
5665 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, self.GetId())
5667 evt.SetSelection(page_index)
5668 evt.SetEventObject(self)
5670 evt.SetEditCanceled(True)
5671 self.GetEventHandler().ProcessEvent(evt)
5674 def OnRenameAccept(self, page_index, value):
5676 Called by L{TabTextCtrl}, to accept the changes and to send the
5677 `EVT_AUINOTEBOOK_END_LABEL_EDIT` event.
5679 :param `page_index`: the page index in the notebook;
5680 :param `value`: the new label for the tab.
5683 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, self.GetId())
5684 evt.SetSelection(page_index)
5685 evt.SetEventObject(self)
5687 evt.SetEditCanceled(False)
5689 return not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed()
5692 def ResetTextControl(self):
5693 """ Called by L{TabTextCtrl} when it marks itself for deletion. """
5695 if not self._textCtrl:
5698 self._textCtrl.Destroy()
5699 self._textCtrl = None
5701 # tab height might have changed
5702 self.UpdateTabCtrlHeight(force=True)
5705 def EditTab(self, page_index):
5707 Starts the editing of an item label, sending a `EVT_AUINOTEBOOK_BEGIN_LABEL_EDIT` event.
5709 :param `page_index`: the page index we want to edit.
5712 if page_index >= self._tabs.GetPageCount():
5715 if not self.IsRenamable(page_index):
5718 page_info = self._tabs.GetPage(page_index)
5719 ctrl, ctrl_idx = self.FindTab(page_info.window)
5723 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT, self.GetId())
5724 evt.SetSelection(page_index)
5725 evt.SetEventObject(self)
5726 if self.GetEventHandler().ProcessEvent(evt) and not evt.IsAllowed():
5730 if self._textCtrl is not None and page_info != self._textCtrl.item():
5731 self._textCtrl.StopEditing()
5733 self._textCtrl = TabTextCtrl(ctrl, page_info, page_index)
5734 self._textCtrl.SetFocus()