2 auibook contains a notebook control which implements many features common in
3 applications with dockable panes. Specifically, L{AuiNotebook} implements functionality
4 which allows the user to rearrange tab order via drag-and-drop, split the tab window
5 into many different splitter configurations, and toggle through different themes to
6 customize the control's look and feel.
8 An effort has been made to try to maintain an API as similar to that of `wx.Notebook`.
10 The default theme that is used is L{AuiDefaultTabArt}, which provides a modern, glossy
11 look and feel. The theme can be changed by calling L{AuiNotebook.SetArtProvider}.
14 __author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
15 __date__ = "31 March 2009"
22 from wx.lib.expando import ExpandoTextCtrl
27 from aui_utilities import LightColour, MakeDisabledBitmap, TabDragImage
28 from aui_utilities import TakeScreenShot, RescaleScreenShot
30 from aui_constants import *
33 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE = wx.NewEventType()
34 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED = wx.NewEventType()
35 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED = wx.NewEventType()
36 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING = wx.NewEventType()
37 wxEVT_COMMAND_AUINOTEBOOK_BUTTON = wx.NewEventType()
38 wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG = wx.NewEventType()
39 wxEVT_COMMAND_AUINOTEBOOK_END_DRAG = wx.NewEventType()
40 wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION = wx.NewEventType()
41 wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND = wx.NewEventType()
42 wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE = wx.NewEventType()
43 wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN = wx.NewEventType()
44 wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP = wx.NewEventType()
45 wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN = wx.NewEventType()
46 wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP = wx.NewEventType()
47 wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK = wx.NewEventType()
48 wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN = wx.NewEventType()
49 wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP = wx.NewEventType()
50 wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN = wx.NewEventType()
51 wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP = wx.NewEventType()
52 wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK = wx.NewEventType()
54 # Define a new event for a drag cancelled
55 wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG = wx.NewEventType()
57 # Define events for editing a tab label
58 wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT = wx.NewEventType()
59 wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT = wx.NewEventType()
61 # Create event binders
62 EVT_AUINOTEBOOK_PAGE_CLOSE = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 1)
63 """ A tab in `AuiNotebook` is being closed. Can be vetoed by calling `Veto()`. """
64 EVT_AUINOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 1)
65 """ A tab in `AuiNotebook` has been closed. """
66 EVT_AUINOTEBOOK_PAGE_CHANGED = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 1)
67 """ The page selection was changed. """
68 EVT_AUINOTEBOOK_PAGE_CHANGING = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 1)
69 """ The page selection is being changed. """
70 EVT_AUINOTEBOOK_BUTTON = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 1)
71 """ The user clicked on a button in the `AuiNotebook` tab area. """
72 EVT_AUINOTEBOOK_BEGIN_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 1)
73 """ A drag-and-drop operation on a notebook tab has started. """
74 EVT_AUINOTEBOOK_END_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 1)
75 """ A drag-and-drop operation on a notebook tab has finished. """
76 EVT_AUINOTEBOOK_DRAG_MOTION = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 1)
77 """ A drag-and-drop operation on a notebook tab is ongoing. """
78 EVT_AUINOTEBOOK_ALLOW_DND = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 1)
79 """ Fires an event asking if it is OK to drag and drop a tab. """
80 EVT_AUINOTEBOOK_DRAG_DONE = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 1)
81 """ A drag-and-drop operation on a notebook tab has finished. """
82 EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 1)
83 """ The user clicked with the middle mouse button on a tab. """
84 EVT_AUINOTEBOOK_TAB_MIDDLE_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 1)
85 """ The user clicked with the middle mouse button on a tab. """
86 EVT_AUINOTEBOOK_TAB_RIGHT_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 1)
87 """ The user clicked with the right mouse button on a tab. """
88 EVT_AUINOTEBOOK_TAB_RIGHT_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 1)
89 """ The user clicked with the right mouse button on a tab. """
90 EVT_AUINOTEBOOK_BG_MIDDLE_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN, 1)
91 """ The user middle-clicked in the tab area but not over a tab or a button. """
92 EVT_AUINOTEBOOK_BG_MIDDLE_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP, 1)
93 """ The user middle-clicked in the tab area but not over a tab or a button. """
94 EVT_AUINOTEBOOK_BG_RIGHT_DOWN = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN, 1)
95 """ The user right-clicked in the tab area but not over a tab or a button. """
96 EVT_AUINOTEBOOK_BG_RIGHT_UP = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP, 1)
97 """ The user right-clicked in the tab area but not over a tab or a button. """
98 EVT_AUINOTEBOOK_BG_DCLICK = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 1)
99 """ The user left-clicked on the tab area not occupied by `AuiNotebook` tabs. """
100 EVT_AUINOTEBOOK_CANCEL_DRAG = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG, 1)
101 """ A drag and drop operation has been cancelled. """
102 EVT_AUINOTEBOOK_TAB_DCLICK = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, 1)
103 """ The user double-clicked with the left mouse button on a tab. """
104 EVT_AUINOTEBOOK_BEGIN_LABEL_EDIT = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_LABEL_EDIT, 1)
105 """ The user double-clicked with the left mouse button on a tab which text is editable. """
106 EVT_AUINOTEBOOK_END_LABEL_EDIT = wx.PyEventBinder(wxEVT_COMMAND_AUINOTEBOOK_END_LABEL_EDIT, 1)
107 """ The user finished editing a tab label. """
110 # -----------------------------------------------------------------------------
111 # Auxiliary class: TabTextCtrl
112 # This is the temporary ExpandoTextCtrl created when you edit the text of a tab
113 # -----------------------------------------------------------------------------
115 class TabTextCtrl(ExpandoTextCtrl):
116 """ Control used for in-place edit. """
118 def __init__(self, owner, tab, page_index):
120 Default class constructor.
121 For internal use: do not call it in your code!
123 :param `owner`: the L{AuiTabCtrl} owning the tab;
124 :param `tab`: the actual L{AuiNotebookPage} tab;
125 :param `page_index`: the L{AuiNotebook} page index for the tab.
129 self._tabEdited = tab
130 self._pageIndex = page_index
131 self._startValue = tab.caption
132 self._finished = False
133 self._aboutToFinish = False
134 self._currentValue = self._startValue
136 x, y, w, h = self._tabEdited.rect
138 wnd = self._tabEdited.control
140 x += wnd.GetSize()[0] + 2
149 image_w, image_h = image.GetWidth(), image.GetHeight()
152 dc = wx.ClientDC(self._owner)
153 h = max(image_h, dc.GetMultiLineTextExtent(tab.caption)[1])
156 # FIXME: what are all these hardcoded 4, 8 and 11s really?
160 y = (self._tabEdited.rect.height - h)/2 + 1
162 expandoStyle = wx.WANTS_CHARS
163 if wx.Platform in ["__WXGTK__", "__WXMAC__"]:
164 expandoStyle |= wx.SIMPLE_BORDER
165 xSize, ySize = w + 2, h
167 expandoStyle |= wx.SUNKEN_BORDER
168 xSize, ySize = w + 2, h+2
170 ExpandoTextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue,
171 wx.Point(x, y), wx.Size(xSize, ySize),
174 if wx.Platform == "__WXMAC__":
175 self.SetFont(owner.GetFont())
176 bs = self.GetBestSize()
177 self.SetSize((-1, bs.height))
179 self.Bind(wx.EVT_CHAR, self.OnChar)
180 self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
181 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
184 def AcceptChanges(self):
185 """ Accepts/refuses the changes made by the user. """
187 value = self.GetValue()
188 notebook = self._owner.GetParent()
190 if value == self._startValue:
191 # nothing changed, always accept
192 # when an item remains unchanged, the owner
193 # needs to be notified that the user decided
194 # not to change the tree item label, and that
195 # the edit has been cancelled
196 notebook.OnRenameCancelled(self._pageIndex)
199 if not notebook.OnRenameAccept(self._pageIndex, value):
203 # accepted, do rename the item
204 notebook.SetPageText(self._pageIndex, value)
210 """ Finish editing. """
212 if not self._finished:
214 notebook = self._owner.GetParent()
216 self._finished = True
217 self._owner.SetFocus()
218 notebook.ResetTextControl()
221 def OnChar(self, event):
223 Handles the ``wx.EVT_CHAR`` event for L{TabTextCtrl}.
225 :param `event`: a `wx.KeyEvent` event to be processed.
228 keycode = event.GetKeyCode()
229 shiftDown = event.ShiftDown()
231 if keycode == wx.WXK_RETURN:
232 if shiftDown and self._tabEdited.IsMultiline():
235 self._aboutToFinish = True
236 self.SetValue(self._currentValue)
237 # Notify the owner about the changes
239 # Even if vetoed, close the control (consistent with MSW)
240 wx.CallAfter(self.Finish)
242 elif keycode == wx.WXK_ESCAPE:
249 def OnKeyUp(self, event):
251 Handles the ``wx.EVT_KEY_UP`` event for L{TabTextCtrl}.
253 :param `event`: a `wx.KeyEvent` event to be processed.
256 if not self._finished:
258 # auto-grow the textctrl:
259 mySize = self.GetSize()
261 dc = wx.ClientDC(self)
262 sx, sy, dummy = dc.GetMultiLineTextExtent(self.GetValue() + "M")
264 self.SetSize((sx, -1))
265 self._currentValue = self.GetValue()
270 def OnKillFocus(self, event):
272 Handles the ``wx.EVT_KILL_FOCUS`` event for L{TabTextCtrl}.
274 :param `event`: a `wx.FocusEvent` event to be processed.
277 if not self._finished and not self._aboutToFinish:
279 # We must finish regardless of success, otherwise we'll get
281 if not self.AcceptChanges():
282 self._owner.GetParent().OnRenameCancelled(self._pageIndex)
284 # We must let the native text control handle focus, too, otherwise
285 # it could have problems with the cursor (e.g., in wxGTK).
287 wx.CallAfter(self._owner.GetParent().ResetTextControl)
290 def StopEditing(self):
291 """ Suddenly stops the editing. """
293 self._owner.GetParent().OnRenameCancelled(self._pageIndex)
298 """ Returns the item currently edited. """
300 return self._tabEdited
303 # ----------------------------------------------------------------------
305 class AuiNotebookPage(object):
307 A simple class which holds information about tab captions, bitmaps and
313 Default class constructor.
314 Used internally, do not call it in your code!
317 self.window = None # page's associated window
318 self.caption = "" # caption displayed on the tab
319 self.bitmap = wx.NullBitmap # tab's bitmap
320 self.dis_bitmap = wx.NullBitmap # tab's disabled bitmap
321 self.rect = wx.Rect() # tab's hit rectangle
322 self.active = False # True if the page is currently active
323 self.enabled = True # True if the page is currently enabled
324 self.hasCloseButton = True # True if the page has a close button using the style
325 # AUI_NB_CLOSE_ON_ALL_TABS
326 self.control = None # A control can now be inside a tab
327 self.renamable = False # If True, a tab can be renamed by a left double-click
329 self.text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT)
331 self.access_time = datetime.datetime.now() # Last time this page was selected
334 def IsMultiline(self):
335 """ Returns whether the tab contains multiline text. """
337 return "\n" in self.caption
340 # ----------------------------------------------------------------------
342 class AuiTabContainerButton(object):
344 A simple class which holds information about tab buttons and their state.
349 Default class constructor.
350 Used internally, do not call it in your code!
353 self.id = -1 # button's id
354 self.cur_state = AUI_BUTTON_STATE_NORMAL # current state (normal, hover, pressed, etc.)
355 self.location = wx.LEFT # buttons location (wxLEFT, wxRIGHT, or wxCENTER)
356 self.bitmap = wx.NullBitmap # button's hover bitmap
357 self.dis_bitmap = wx.NullBitmap # button's disabled bitmap
358 self.rect = wx.Rect() # button's hit rectangle
361 # ----------------------------------------------------------------------
363 class CommandNotebookEvent(wx.PyCommandEvent):
364 """ A specialized command event class for events sent by L{AuiNotebook} . """
366 def __init__(self, command_type=None, win_id=0):
368 Default class constructor.
370 :param `command_type`: the event kind or an instance of `wx.PyCommandEvent`.
371 :param `win_id`: the window identification number.
374 if type(command_type) == types.IntType:
375 wx.PyCommandEvent.__init__(self, command_type, win_id)
377 wx.PyCommandEvent.__init__(self, command_type.GetEventType(), command_type.GetId())
379 self.old_selection = -1
381 self.drag_source = None
384 self.editCancelled = False
387 def SetSelection(self, s):
389 Sets the selection member variable.
391 :param `s`: the new selection.
398 def GetSelection(self):
399 """ Returns the currently selected page, or -1 if none was selected. """
401 return self.selection
404 def SetOldSelection(self, s):
406 Sets the id of the page selected before the change.
408 :param `s`: the old selection.
411 self.old_selection = s
414 def GetOldSelection(self):
416 Returns the page that was selected before the change, or -1 if none was
420 return self.old_selection
423 def SetDragSource(self, s):
425 Sets the drag and drop source.
427 :param `s`: the drag source.
433 def GetDragSource(self):
434 """ Returns the drag and drop source. """
436 return self.drag_source
439 def SetDispatched(self, b):
441 Sets the event as dispatched (used for automatic L{AuiNotebook} ).
443 :param `b`: whether the event was dispatched or not.
449 def GetDispatched(self):
450 """ Returns whether the event was dispatched (used for automatic L{AuiNotebook} ). """
452 return self.dispatched
455 def IsEditCancelled(self):
456 """ Returns the edit cancel flag (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only)."""
458 return self.editCancelled
461 def SetEditCanceled(self, editCancelled):
463 Sets the edit cancel flag (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only).
465 :param `editCancelled`: whether the editing action has been cancelled or not.
468 self.editCancelled = editCancelled
472 """Returns the label-itemtext (for ``EVT_AUINOTEBOOK_BEGIN`` | ``END_LABEL_EDIT`` only)."""
477 def SetLabel(self, label):
479 Sets the label. Useful only for ``EVT_AUINOTEBOOK_END_LABEL_EDIT``.
481 :param `label`: the new label.
487 # ----------------------------------------------------------------------
489 class AuiNotebookEvent(CommandNotebookEvent):
490 """ A specialized command event class for events sent by L{AuiNotebook}. """
492 def __init__(self, command_type=None, win_id=0):
494 Default class constructor.
496 :param `command_type`: the event kind or an instance of `wx.PyCommandEvent`.
497 :param `win_id`: the window identification number.
500 CommandNotebookEvent.__init__(self, command_type, win_id)
502 if type(command_type) == types.IntType:
503 self.notify = wx.NotifyEvent(command_type, win_id)
505 self.notify = wx.NotifyEvent(command_type.GetEventType(), command_type.GetId())
508 def GetNotifyEvent(self):
509 """ Returns the actual `wx.NotifyEvent`. """
515 """ Returns whether the event is allowed or not. """
517 return self.notify.IsAllowed()
522 Prevents the change announced by this event from happening.
524 It is in general a good idea to notify the user about the reasons for
525 vetoing the change because otherwise the applications behaviour (which
526 just refuses to do what the user wants) might be quite surprising.
534 This is the opposite of L{Veto}: it explicitly allows the event to be
535 processed. For most events it is not necessary to call this method as the
536 events are allowed anyhow but some are forbidden by default (this will
537 be mentioned in the corresponding event description).
543 # ---------------------------------------------------------------------------- #
544 # Class TabNavigatorWindow
545 # ---------------------------------------------------------------------------- #
547 class TabNavigatorWindow(wx.Dialog):
549 This class is used to create a modal dialog that enables "Smart Tabbing",
550 similar to what you would get by hitting ``Alt`` + ``Tab`` on Windows.
553 def __init__(self, parent=None, icon=None):
555 Default class constructor. Used internally.
557 :param `parent`: the L{TabNavigatorWindow} parent;
558 :param `icon`: the L{TabNavigatorWindow} icon.
561 wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)
563 self._selectedItem = -1
567 self._bmp = Mondrian.GetBitmap()
571 if self._bmp.GetSize() != (16, 16):
572 img = self._bmp.ConvertToImage()
573 img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
574 self._bmp = wx.BitmapFromImage(img)
576 sz = wx.BoxSizer(wx.VERTICAL)
578 self._listBox = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, 150), [], wx.LB_SINGLE | wx.NO_BORDER)
580 mem_dc = wx.MemoryDC()
581 mem_dc.SelectObject(wx.EmptyBitmap(1,1))
582 font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
583 font.SetWeight(wx.BOLD)
586 panelHeight = mem_dc.GetCharHeight()
587 panelHeight += 4 # Place a spacer of 2 pixels
589 # Out signpost bitmap is 24 pixels
593 self._panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, panelHeight))
596 sz.Add(self._listBox, 1, wx.EXPAND)
600 # Connect events to the list box
601 self._listBox.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
602 self._listBox.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
603 self._listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnItemSelected)
605 # Connect paint event to the panel
606 self._panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)
607 self._panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnPanelEraseBg)
609 self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
610 self._listBox.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
611 self.PopulateListControl(parent)
613 self.GetSizer().Fit(self)
614 self.GetSizer().SetSizeHints(self)
615 self.GetSizer().Layout()
618 # Set focus on the list box to avoid having to click on it to change
619 # the tab selection under GTK.
620 self._listBox.SetFocus()
623 def OnKeyUp(self, event):
625 Handles the ``wx.EVT_KEY_UP`` for the L{TabNavigatorWindow}.
627 :param `event`: a `wx.KeyEvent` event to be processed.
630 if event.GetKeyCode() == wx.WXK_CONTROL:
634 def OnNavigationKey(self, event):
636 Handles the ``wx.EVT_NAVIGATION_KEY`` for the L{TabNavigatorWindow}.
638 :param `event`: a `wx.NavigationKeyEvent` event to be processed.
641 selected = self._listBox.GetSelection()
642 bk = self.GetParent()
643 maxItems = bk.GetPageCount()
645 if event.GetDirection():
648 if selected == maxItems - 1:
651 itemToSelect = selected + 1
657 itemToSelect = maxItems - 1
659 itemToSelect = selected - 1
661 self._listBox.SetSelection(itemToSelect)
664 def PopulateListControl(self, book):
666 Populates the L{TabNavigatorWindow} listbox with a list of tabs.
668 :param `book`: the actual L{AuiNotebook}.
670 # Index of currently selected page
671 selection = book.GetSelection()
672 # Total number of pages
673 count = book.GetPageCount()
674 # List of (index, AuiNotebookPage)
675 pages = list(enumerate(book.GetTabContainer().GetPages()))
676 if book.GetAGWWindowStyleFlag() & AUI_NB_ORDER_BY_ACCESS:
677 # Sort pages using last access time. Most recently used is the
680 key = lambda element: element[1].access_time,
684 # Manually add the current selection as first item
685 # Remaining ones are added in the next loop
687 self._listBox.Append(book.GetPageText(selection))
688 self._indexMap.append(selection)
690 for (index, page) in pages:
691 self._listBox.Append(book.GetPageText(index))
692 self._indexMap.append(index)
694 # Select the next entry after the current selection
695 self._listBox.SetSelection(0)
696 dummy = wx.NavigationKeyEvent()
697 dummy.SetDirection(True)
698 self.OnNavigationKey(dummy)
701 def OnItemSelected(self, event):
703 Handles the ``wx.EVT_LISTBOX_DCLICK`` event for the wx.ListBox inside L{TabNavigatorWindow}.
705 :param `event`: a `wx.ListEvent` event to be processed.
711 def CloseDialog(self):
712 """ Closes the L{TabNavigatorWindow} dialog, setting selection in L{AuiNotebook}. """
714 bk = self.GetParent()
715 self._selectedItem = self._listBox.GetSelection()
716 self.EndModal(wx.ID_OK)
719 def GetSelectedPage(self):
720 """ Gets the page index that was selected when the dialog was closed. """
722 return self._indexMap[self._selectedItem]
725 def OnPanelPaint(self, event):
727 Handles the ``wx.EVT_PAINT`` event for L{TabNavigatorWindow} top panel.
729 :param `event`: a `wx.PaintEvent` event to be processed.
732 dc = wx.PaintDC(self._panel)
733 rect = self._panel.GetClientRect()
735 bmp = wx.EmptyBitmap(rect.width, rect.height)
737 mem_dc = wx.MemoryDC()
738 mem_dc.SelectObject(bmp)
740 endColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
741 startColour = LightColour(endColour, 50)
742 mem_dc.GradientFillLinear(rect, startColour, endColour, wx.SOUTH)
744 # Draw the caption title and place the bitmap
745 # get the bitmap optimal position, and draw it
746 bmpPt, txtPt = wx.Point(), wx.Point()
747 bmpPt.y = (rect.height - self._bmp.GetHeight())/2
749 mem_dc.DrawBitmap(self._bmp, bmpPt.x, bmpPt.y, True)
751 # get the text position, and draw it
752 font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
753 font.SetWeight(wx.BOLD)
755 fontHeight = mem_dc.GetCharHeight()
757 txtPt.x = bmpPt.x + self._bmp.GetWidth() + 4
758 txtPt.y = (rect.height - fontHeight)/2
759 mem_dc.SetTextForeground(wx.WHITE)
760 mem_dc.DrawText("Opened tabs:", txtPt.x, txtPt.y)
761 mem_dc.SelectObject(wx.NullBitmap)
763 dc.DrawBitmap(bmp, 0, 0)
766 def OnPanelEraseBg(self, event):
768 Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{TabNavigatorWindow} top panel.
770 :param `event`: a `wx.EraseEvent` event to be processed.
772 :note: This is intentionally empty, to reduce flicker.
778 # ----------------------------------------------------------------------
779 # -- AuiTabContainer class implementation --
781 class AuiTabContainer(object):
783 AuiTabContainer is a class which contains information about each
784 tab. It also can render an entire tab control to a specified DC.
785 It's not a window class itself, because this code will be used by
786 the L{AuiManager}, where it is disadvantageous to have separate
787 windows for each tab control in the case of "docked tabs".
789 A derived class, L{AuiTabCtrl}, is an actual `wx.Window`-derived window
790 which can be used as a tab control in the normal sense.
793 def __init__(self, auiNotebook):
795 Default class constructor.
796 Used internally, do not call it in your code!
798 :param `auiNotebook`: the parent L{AuiNotebook} window.
803 self._art = TA.AuiDefaultTabArt()
807 self._tab_close_buttons = []
809 self._rect = wx.Rect()
810 self._auiNotebook = auiNotebook
812 self.AddButton(AUI_BUTTON_LEFT, wx.LEFT)
813 self.AddButton(AUI_BUTTON_RIGHT, wx.RIGHT)
814 self.AddButton(AUI_BUTTON_WINDOWLIST, wx.RIGHT)
815 self.AddButton(AUI_BUTTON_CLOSE, wx.RIGHT)
818 def SetArtProvider(self, art):
820 Instructs L{AuiTabContainer} to use art provider specified by parameter `art`
821 for all drawing calls. This allows plugable look-and-feel features.
823 :param `art`: an art provider.
825 :note: The previous art provider object, if any, will be deleted by L{AuiTabContainer}.
832 self._art.SetAGWFlags(self._agwFlags)
835 def GetArtProvider(self):
836 """ Returns the current art provider being used. """
841 def SetAGWFlags(self, agwFlags):
843 Sets the tab art flags.
845 :param `agwFlags`: a combination of the following values:
847 ==================================== ==================================
848 Flag name Description
849 ==================================== ==================================
850 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
851 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet
852 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet
853 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
854 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
855 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
856 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
857 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
858 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
859 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
860 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
861 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
862 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
863 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
864 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
865 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
866 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
867 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
868 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
869 ``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
870 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
871 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
872 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
873 ==================================== ==================================
875 :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
879 self._agwFlags = agwFlags
881 # check for new close button settings
882 self.RemoveButton(AUI_BUTTON_LEFT)
883 self.RemoveButton(AUI_BUTTON_RIGHT)
884 self.RemoveButton(AUI_BUTTON_WINDOWLIST)
885 self.RemoveButton(AUI_BUTTON_CLOSE)
887 if agwFlags & AUI_NB_SCROLL_BUTTONS:
888 self.AddButton(AUI_BUTTON_LEFT, wx.LEFT)
889 self.AddButton(AUI_BUTTON_RIGHT, wx.RIGHT)
891 if agwFlags & AUI_NB_WINDOWLIST_BUTTON:
892 self.AddButton(AUI_BUTTON_WINDOWLIST, wx.RIGHT)
894 if agwFlags & AUI_NB_CLOSE_BUTTON:
895 self.AddButton(AUI_BUTTON_CLOSE, wx.RIGHT)
898 self._art.SetAGWFlags(self._agwFlags)
901 def GetAGWFlags(self):
903 Returns the tab art flags.
905 See L{SetAGWFlags} for a list of possible return values.
910 return self._agwFlags
913 def SetNormalFont(self, font):
915 Sets the normal font for drawing tab labels.
917 :param `font`: a `wx.Font` object.
920 self._art.SetNormalFont(font)
923 def SetSelectedFont(self, font):
925 Sets the selected tab font for drawing tab labels.
927 :param `font`: a `wx.Font` object.
930 self._art.SetSelectedFont(font)
933 def SetMeasuringFont(self, font):
935 Sets the font for calculating text measurements.
937 :param `font`: a `wx.Font` object.
940 self._art.SetMeasuringFont(font)
943 def SetTabRect(self, rect):
945 Sets the tab area rectangle.
947 :param `rect`: an instance of `wx.Rect`, specifying the available area for L{AuiTabContainer}.
953 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
954 self._art.SetSizingInfo(rect.GetSize(), len(self._pages), minMaxTabWidth)
957 def AddPage(self, page, info):
959 Adds a page to the tab control.
961 :param `page`: the window associated with this tab;
962 :param `info`: an instance of L{AuiNotebookPage}.
966 page_info.window = page
968 self._pages.append(page_info)
970 # let the art provider know how many pages we have
972 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
973 self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
978 def InsertPage(self, page, info, idx):
980 Inserts a page in the tab control in the position specified by `idx`.
982 :param `page`: the window associated with this tab;
983 :param `info`: an instance of L{AuiNotebookPage};
984 :param `idx`: the page insertion index.
988 page_info.window = page
990 if idx >= len(self._pages):
991 self._pages.append(page_info)
993 self._pages.insert(idx, page_info)
995 # let the art provider know how many pages we have
997 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
998 self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
1003 def MovePage(self, page, new_idx):
1005 Moves a page in a new position specified by `new_idx`.
1007 :param `page`: the window associated with this tab;
1008 :param `new_idx`: the new page position.
1011 idx = self.GetIdxFromWindow(page)
1015 # get page entry, make a copy of it
1016 p = self.GetPage(idx)
1018 # remove old page entry
1019 self.RemovePage(page)
1021 # insert page where it should be
1022 self.InsertPage(page, p, new_idx)
1027 def RemovePage(self, wnd):
1029 Removes a page from the tab control.
1031 :param `wnd`: an instance of `wx.Window`, a window associated with this tab.
1034 minMaxTabWidth = self._auiNotebook.GetMinMaxTabWidth()
1036 for page in self._pages:
1037 if page.window == wnd:
1038 self._pages.remove(page)
1039 self._tab_offset = min(self._tab_offset, len(self._pages) - 1)
1041 # let the art provider know how many pages we have
1043 self._art.SetSizingInfo(self._rect.GetSize(), len(self._pages), minMaxTabWidth)
1050 def SetActivePage(self, wndOrInt):
1052 Sets the L{AuiTabContainer} active page.
1054 :param `wndOrInt`: an instance of `wx.Window` or an integer specifying a tab index.
1057 if type(wndOrInt) == types.IntType:
1059 if wndOrInt >= len(self._pages):
1062 wnd = self._pages[wndOrInt].window
1069 for indx, page in enumerate(self._pages):
1070 if page.window == wnd:
1079 def SetNoneActive(self):
1080 """ Sets all the tabs as inactive (non-selected). """
1082 for page in self._pages:
1086 def GetActivePage(self):
1087 """ Returns the current selected tab or ``wx.NOT_FOUND`` if none is selected. """
1089 for indx, page in enumerate(self._pages):
1096 def GetWindowFromIdx(self, idx):
1098 Returns the window associated with the tab with index `idx`.
1100 :param `idx`: the tab index.
1103 if idx >= len(self._pages):
1106 return self._pages[idx].window
1109 def GetIdxFromWindow(self, wnd):
1111 Returns the tab index based on the window `wnd` associated with it.
1113 :param `wnd`: an instance of `wx.Window`.
1116 for indx, page in enumerate(self._pages):
1117 if page.window == wnd:
1123 def GetPage(self, idx):
1125 Returns the page specified by the given index.
1127 :param `idx`: the tab index.
1130 if idx < 0 or idx >= len(self._pages):
1131 raise Exception("Invalid Page index")
1133 return self._pages[idx]
1137 """ Returns a list of all the pages in this L{AuiTabContainer}. """
1142 def GetPageCount(self):
1143 """ Returns the number of pages in the L{AuiTabContainer}. """
1145 return len(self._pages)
1148 def GetEnabled(self, idx):
1150 Returns whether a tab is enabled or not.
1152 :param `idx`: the tab index.
1155 if idx < 0 or idx >= len(self._pages):
1158 return self._pages[idx].enabled
1161 def EnableTab(self, idx, enable=True):
1163 Enables/disables a tab in the L{AuiTabContainer}.
1165 :param `idx`: the tab index;
1166 :param `enable`: ``True`` to enable a tab, ``False`` to disable it.
1169 if idx < 0 or idx >= len(self._pages):
1170 raise Exception("Invalid Page index")
1172 self._pages[idx].enabled = enable
1173 wnd = self.GetWindowFromIdx(idx)
1177 def AddButton(self, id, location, normal_bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap):
1179 Adds a button in the tab area.
1181 :param `id`: the button identifier. This can be one of the following:
1183 ============================== =================================
1184 Button Identifier Description
1185 ============================== =================================
1186 ``AUI_BUTTON_CLOSE`` Shows a close button on the tab area
1187 ``AUI_BUTTON_WINDOWLIST`` Shows a window list button on the tab area
1188 ``AUI_BUTTON_LEFT`` Shows a left button on the tab area
1189 ``AUI_BUTTON_RIGHT`` Shows a right button on the tab area
1190 ============================== =================================
1192 :param `location`: the button location. Can be ``wx.LEFT`` or ``wx.RIGHT``;
1193 :param `normal_bitmap`: the bitmap for an enabled tab;
1194 :param `disabled_bitmap`: the bitmap for a disabled tab.
1197 button = AuiTabContainerButton()
1199 button.bitmap = normal_bitmap
1200 button.dis_bitmap = disabled_bitmap
1201 button.location = location
1202 button.cur_state = AUI_BUTTON_STATE_NORMAL
1204 self._buttons.append(button)
1207 def RemoveButton(self, id):
1209 Removes a button from the tab area.
1211 :param `id`: the button identifier. See L{AddButton} for a list of button identifiers.
1216 for button in self._buttons:
1218 self._buttons.remove(button)
1222 def GetTabOffset(self):
1223 """ Returns the tab offset. """
1225 return self._tab_offset
1228 def SetTabOffset(self, offset):
1230 Sets the tab offset.
1232 :param `offset`: the tab offset.
1235 self._tab_offset = offset
1238 def MinimizeTabOffset(self, dc, wnd, max_width):
1240 Minimize `self._tab_offset` to fit as many tabs as possible in the available space.
1242 :param `dc`: a `wx.DC` device context;
1243 :param `wnd`: an instance of `wx.Window`;
1244 :param `max_width`: the maximum available width for the tabs.
1249 for i, page in reversed(list(enumerate(self._pages))):
1251 tab_button = self._tab_close_buttons[i]
1252 (tab_width, tab_height), x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active, tab_button.cur_state, page.control)
1253 total_width += tab_width
1255 if total_width > max_width:
1259 if tab_offset < self._tab_offset and tab_offset < len(self._pages):
1260 self._tab_offset = tab_offset
1265 self._tab_offset = 0
1268 def Render(self, raw_dc, wnd):
1270 Renders the tab catalog to the specified `wx.DC`.
1272 It is a virtual function and can be overridden to provide custom drawing
1275 :param `raw_dc`: a `wx.DC` device context;
1276 :param `wnd`: an instance of `wx.Window`.
1279 if not raw_dc or not raw_dc.IsOk():
1284 # use the same layout direction as the window DC uses to ensure that the
1285 # text is rendered correctly
1286 dc.SetLayoutDirection(raw_dc.GetLayoutDirection())
1288 page_count = len(self._pages)
1289 button_count = len(self._buttons)
1291 # create off-screen bitmap
1292 bmp = wx.EmptyBitmap(self._rect.GetWidth(), self._rect.GetHeight())
1293 dc.SelectObject(bmp)
1298 # find out if size of tabs is larger than can be
1299 # afforded on screen
1300 total_width = visible_width = 0
1302 for i in xrange(page_count):
1303 page = self._pages[i]
1305 # determine if a close button is on this tab
1306 close_button = False
1307 if (self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS and page.hasCloseButton) or \
1308 (self._agwFlags & AUI_NB_CLOSE_ON_ACTIVE_TAB and page.active and page.hasCloseButton):
1312 control = page.control
1316 except wx.PyDeadObjectError:
1319 size, x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
1320 (close_button and [AUI_BUTTON_STATE_NORMAL] or \
1321 [AUI_BUTTON_STATE_HIDDEN])[0], page.control)
1323 if i+1 < page_count:
1324 total_width += x_extent
1326 total_width += size[0]
1328 if i >= self._tab_offset:
1329 if i+1 < page_count:
1330 visible_width += x_extent
1332 visible_width += size[0]
1334 if total_width > self._rect.GetWidth() or self._tab_offset != 0:
1336 # show left/right buttons
1337 for button in self._buttons:
1338 if button.id == AUI_BUTTON_LEFT or \
1339 button.id == AUI_BUTTON_RIGHT:
1341 button.cur_state &= ~AUI_BUTTON_STATE_HIDDEN
1345 # hide left/right buttons
1346 for button in self._buttons:
1347 if button.id == AUI_BUTTON_LEFT or \
1348 button.id == AUI_BUTTON_RIGHT:
1350 button.cur_state |= AUI_BUTTON_STATE_HIDDEN
1352 # determine whether left button should be enabled
1353 for button in self._buttons:
1354 if button.id == AUI_BUTTON_LEFT:
1355 if self._tab_offset == 0:
1356 button.cur_state |= AUI_BUTTON_STATE_DISABLED
1358 button.cur_state &= ~AUI_BUTTON_STATE_DISABLED
1360 if button.id == AUI_BUTTON_RIGHT:
1361 if visible_width < self._rect.GetWidth() - 16*button_count:
1362 button.cur_state |= AUI_BUTTON_STATE_DISABLED
1364 button.cur_state &= ~AUI_BUTTON_STATE_DISABLED
1367 self._art.DrawBackground(dc, wnd, self._rect)
1370 left_buttons_width = 0
1371 right_buttons_width = 0
1373 # draw the buttons on the right side
1374 offset = self._rect.x + self._rect.width
1376 for i in xrange(button_count):
1377 button = self._buttons[button_count - i - 1]
1379 if button.location != wx.RIGHT:
1381 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1384 button_rect = wx.Rect(*self._rect)
1386 button_rect.SetWidth(offset)
1388 button.rect = self._art.DrawButton(dc, wnd, button_rect, button, wx.RIGHT)
1390 offset -= button.rect.GetWidth()
1391 right_buttons_width += button.rect.GetWidth()
1395 # draw the buttons on the left side
1396 for i in xrange(button_count):
1397 button = self._buttons[button_count - i - 1]
1399 if button.location != wx.LEFT:
1401 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1404 button_rect = wx.Rect(offset, 1, 1000, self._rect.height)
1406 button.rect = self._art.DrawButton(dc, wnd, button_rect, button, wx.LEFT)
1408 offset += button.rect.GetWidth()
1409 left_buttons_width += button.rect.GetWidth()
1411 offset = left_buttons_width
1414 offset += self._art.GetIndentSize()
1416 # prepare the tab-close-button array
1417 # make sure tab button entries which aren't used are marked as hidden
1418 for i in xrange(page_count, len(self._tab_close_buttons)):
1419 self._tab_close_buttons[i].cur_state = AUI_BUTTON_STATE_HIDDEN
1421 # make sure there are enough tab button entries to accommodate all tabs
1422 while len(self._tab_close_buttons) < page_count:
1423 tempbtn = AuiTabContainerButton()
1424 tempbtn.id = AUI_BUTTON_CLOSE
1425 tempbtn.location = wx.CENTER
1426 tempbtn.cur_state = AUI_BUTTON_STATE_HIDDEN
1427 self._tab_close_buttons.append(tempbtn)
1429 # buttons before the tab offset must be set to hidden
1430 for i in xrange(self._tab_offset):
1431 self._tab_close_buttons[i].cur_state = AUI_BUTTON_STATE_HIDDEN
1432 if self._pages[i].control:
1433 if self._pages[i].control.IsShown():
1434 self._pages[i].control.Hide()
1436 self.MinimizeTabOffset(dc, wnd, self._rect.GetWidth() - right_buttons_width - offset - 2)
1442 rect = wx.Rect(*self._rect)
1444 rect.height = self._rect.height
1446 for i in xrange(self._tab_offset, page_count):
1448 page = self._pages[i]
1449 tab_button = self._tab_close_buttons[i]
1451 # determine if a close button is on this tab
1452 if (self._agwFlags & AUI_NB_CLOSE_ON_ALL_TABS and page.hasCloseButton) or \
1453 (self._agwFlags & AUI_NB_CLOSE_ON_ACTIVE_TAB and page.active and page.hasCloseButton):
1455 if tab_button.cur_state == AUI_BUTTON_STATE_HIDDEN:
1457 tab_button.id = AUI_BUTTON_CLOSE
1458 tab_button.cur_state = AUI_BUTTON_STATE_NORMAL
1459 tab_button.location = wx.CENTER
1463 tab_button.cur_state = AUI_BUTTON_STATE_HIDDEN
1466 rect.width = self._rect.width - right_buttons_width - offset - 2
1471 page.rect, tab_button.rect, x_extent = self._art.DrawTab(dc, wnd, page, rect, tab_button.cur_state)
1475 active_offset = offset
1476 active_rect = wx.Rect(*rect)
1480 lenPages = len(self._pages)
1481 # make sure to deactivate buttons which are off the screen to the right
1482 for j in xrange(i+1, len(self._tab_close_buttons)):
1483 self._tab_close_buttons[j].cur_state = AUI_BUTTON_STATE_HIDDEN
1484 if j > 0 and j <= lenPages:
1485 if self._pages[j-1].control:
1486 if self._pages[j-1].control.IsShown():
1487 self._pages[j-1].control.Hide()
1489 # draw the active tab again so it stands in the foreground
1490 if active >= self._tab_offset and active < len(self._pages):
1492 page = self._pages[active]
1493 tab_button = self._tab_close_buttons[active]
1495 rect.x = active_offset
1496 dummy = self._art.DrawTab(dc, wnd, page, active_rect, tab_button.cur_state)
1498 raw_dc.Blit(self._rect.x, self._rect.y, self._rect.GetWidth(), self._rect.GetHeight(), dc, 0, 0)
1501 def IsTabVisible(self, tabPage, tabOffset, dc, wnd):
1503 Returns whether a tab is visible or not.
1505 :param `tabPage`: the tab index;
1506 :param `tabOffset`: the tab offset;
1507 :param `dc`: a `wx.DC` device context;
1508 :param `wnd`: an instance of `wx.Window` derived window.
1511 if not dc or not dc.IsOk():
1514 page_count = len(self._pages)
1515 button_count = len(self._buttons)
1516 self.Render(dc, wnd)
1518 # Hasn't been rendered yet assume it's visible
1519 if len(self._tab_close_buttons) < page_count:
1522 if self._agwFlags & AUI_NB_SCROLL_BUTTONS:
1523 # First check if both buttons are disabled - if so, there's no need to
1524 # check further for visibility.
1525 arrowButtonVisibleCount = 0
1526 for i in xrange(button_count):
1528 button = self._buttons[i]
1529 if button.id == AUI_BUTTON_LEFT or \
1530 button.id == AUI_BUTTON_RIGHT:
1532 if button.cur_state & AUI_BUTTON_STATE_HIDDEN == 0:
1533 arrowButtonVisibleCount += 1
1535 # Tab must be visible
1536 if arrowButtonVisibleCount == 0:
1539 # If tab is less than the given offset, it must be invisible by definition
1540 if tabPage < tabOffset:
1544 left_buttons_width = 0
1545 right_buttons_width = 0
1549 # calculate size of the buttons on the right side
1550 offset = self._rect.x + self._rect.width
1552 for i in xrange(button_count):
1553 button = self._buttons[button_count - i - 1]
1555 if button.location != wx.RIGHT:
1557 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1560 offset -= button.rect.GetWidth()
1561 right_buttons_width += button.rect.GetWidth()
1565 # calculate size of the buttons on the left side
1566 for i in xrange(button_count):
1567 button = self._buttons[button_count - i - 1]
1569 if button.location != wx.LEFT:
1571 if button.cur_state & AUI_BUTTON_STATE_HIDDEN:
1574 offset += button.rect.GetWidth()
1575 left_buttons_width += button.rect.GetWidth()
1577 offset = left_buttons_width
1580 offset += self._art.GetIndentSize()
1582 rect = wx.Rect(*self._rect)
1584 rect.height = self._rect.height
1586 # See if the given page is visible at the given tab offset (effectively scroll position)
1587 for i in xrange(tabOffset, page_count):
1589 page = self._pages[i]
1590 tab_button = self._tab_close_buttons[i]
1593 rect.width = self._rect.width - right_buttons_width - offset - 2
1596 return False # haven't found the tab, and we've run out of space, so return False
1598 size, x_extent = self._art.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active, tab_button.cur_state, page.control)
1603 # If not all of the tab is visible, and supposing there's space to display it all,
1604 # we could do better so we return False.
1605 if (self._rect.width - right_buttons_width - offset - 2) <= 0 and (self._rect.width - right_buttons_width - left_buttons_width) > x_extent:
1610 # Shouldn't really get here, but if it does, assume the tab is visible to prevent
1611 # further looping in calling code.
1615 def MakeTabVisible(self, tabPage, win):
1617 Make the tab visible if it wasn't already.
1619 :param `tabPage`: the tab index;
1620 :param `win`: an instance of `wx.Window` derived window.
1623 dc = wx.ClientDC(win)
1625 if not self.IsTabVisible(tabPage, self.GetTabOffset(), dc, win):
1626 for i in xrange(len(self._pages)):
1627 if self.IsTabVisible(tabPage, i, dc, win):
1628 self.SetTabOffset(i)
1633 def TabHitTest(self, x, y):
1635 TabHitTest() tests if a tab was hit, passing the window pointer
1636 back if that condition was fulfilled.
1638 :param `x`: the mouse `x` position;
1639 :param `y`: the mouse `y` position.
1642 if not self._rect.Contains((x,y)):
1645 btn = self.ButtonHitTest(x, y)
1647 if btn in self._buttons:
1650 for i in xrange(self._tab_offset, len(self._pages)):
1651 page = self._pages[i]
1652 if page.rect.Contains((x,y)):
1658 def ButtonHitTest(self, x, y):
1660 Tests if a button was hit.
1662 :param `x`: the mouse `x` position;
1663 :param `y`: the mouse `y` position.
1665 :returns: and instance of L{AuiTabContainerButton} if a button was hit, ``None`` otherwise.
1668 if not self._rect.Contains((x,y)):
1671 for button in self._buttons:
1672 if button.rect.Contains((x,y)) and \
1673 (button.cur_state & (AUI_BUTTON_STATE_HIDDEN|AUI_BUTTON_STATE_DISABLED)) == 0:
1676 for button in self._tab_close_buttons:
1677 if button.rect.Contains((x,y)) and \
1678 (button.cur_state & (AUI_BUTTON_STATE_HIDDEN|AUI_BUTTON_STATE_DISABLED)) == 0:
1684 def DoShowHide(self):
1686 This function shows the active window, then hides all of the other windows
1690 pages = self.GetPages()
1692 # show new active page first
1695 page.window.Show(True)
1698 # hide all other pages
1701 page.window.Show(False)
1704 # ----------------------------------------------------------------------
1705 # -- AuiTabCtrl class implementation --
1707 class AuiTabCtrl(wx.PyControl, AuiTabContainer):
1709 This is an actual `wx.Window`-derived window which can be used as a tab
1710 control in the normal sense.
1713 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
1714 style=wx.NO_BORDER|wx.WANTS_CHARS|wx.TAB_TRAVERSAL):
1716 Default class constructor.
1717 Used internally, do not call it in your code!
1719 :param `parent`: the L{AuiTabCtrl} parent;
1720 :param `id`: an identifier for the control: a value of -1 is taken to mean a default;
1721 :param `pos`: the control position. A value of (-1, -1) indicates a default position,
1722 chosen by either the windowing system or wxPython, depending on platform;
1723 :param `size`: the control size. A value of (-1, -1) indicates a default size,
1724 chosen by either the windowing system or wxPython, depending on platform;
1725 :param `style`: the window style.
1728 wx.PyControl.__init__(self, parent, id, pos, size, style, name="AuiTabCtrl")
1729 AuiTabContainer.__init__(self, parent)
1731 self._click_pt = wx.Point(-1, -1)
1732 self._is_dragging = False
1733 self._hover_button = None
1734 self._pressed_button = None
1735 self._drag_image = None
1736 self._drag_img_offset = (0, 0)
1737 self._on_button = False
1739 self.Bind(wx.EVT_PAINT, self.OnPaint)
1740 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
1741 self.Bind(wx.EVT_SIZE, self.OnSize)
1742 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
1743 self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
1744 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
1745 self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
1746 self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
1747 self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
1748 self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
1749 self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
1750 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
1751 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
1752 self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost)
1753 self.Bind(wx.EVT_MOTION, self.OnMotion)
1754 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
1755 self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnButton)
1758 def IsDragging(self):
1759 """ Returns whether the user is dragging a tab with the mouse or not. """
1761 return self._is_dragging
1764 def GetDefaultBorder(self):
1765 """ Returns the default border style for L{AuiTabCtrl}. """
1767 return wx.BORDER_NONE
1770 def OnPaint(self, event):
1772 Handles the ``wx.EVT_PAINT`` event for L{AuiTabCtrl}.
1774 :param `event`: a `wx.PaintEvent` event to be processed.
1777 dc = wx.PaintDC(self)
1778 dc.SetFont(self.GetFont())
1780 if self.GetPageCount() > 0:
1781 self.Render(dc, self)
1784 def OnEraseBackground(self, event):
1786 Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{AuiTabCtrl}.
1788 :param `event`: a `wx.EraseEvent` event to be processed.
1790 :note: This is intentionally empty, to reduce flicker.
1796 def DoGetBestSize(self):
1798 Gets the size which best suits the window: for a control, it would be the
1799 minimal size which doesn't truncate the control, for a panel - the same
1800 size as it would have after a call to `Fit()`.
1802 :note: Overridden from `wx.PyControl`.
1805 return wx.Size(self._rect.width, self._rect.height)
1808 def OnSize(self, event):
1810 Handles the ``wx.EVT_SIZE`` event for L{AuiTabCtrl}.
1812 :param `event`: a `wx.SizeEvent` event to be processed.
1816 self.SetTabRect(wx.Rect(0, 0, s.GetWidth(), s.GetHeight()))
1819 def OnLeftDown(self, event):
1821 Handles the ``wx.EVT_LEFT_DOWN`` event for L{AuiTabCtrl}.
1823 :param `event`: a `wx.MouseEvent` event to be processed.
1827 self._click_pt = wx.Point(-1, -1)
1828 self._is_dragging = False
1829 self._click_tab = None
1830 self._pressed_button = None
1832 wnd = self.TabHitTest(event.GetX(), event.GetY())
1835 new_selection = self.GetIdxFromWindow(wnd)
1837 # AuiNotebooks always want to receive this event
1838 # even if the tab is already active, because they may
1839 # have multiple tab controls
1840 if new_selection != self.GetActivePage() or isinstance(self.GetParent(), AuiNotebook):
1842 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
1843 e.SetSelection(new_selection)
1844 e.SetOldSelection(self.GetActivePage())
1845 e.SetEventObject(self)
1846 self.GetEventHandler().ProcessEvent(e)
1848 self._click_pt.x = event.GetX()
1849 self._click_pt.y = event.GetY()
1850 self._click_tab = wnd
1852 page_index = self.GetActivePage()
1853 if page_index != wx.NOT_FOUND:
1854 self.GetWindowFromIdx(page_index).SetFocus()
1856 if self._hover_button:
1857 self._pressed_button = self._hover_button
1858 self._pressed_button.cur_state = AUI_BUTTON_STATE_PRESSED
1859 self._on_button = True
1864 def OnCaptureLost(self, event):
1866 Handles the ``wx.EVT_MOUSE_CAPTURE_LOST`` event for L{AuiTabCtrl}.
1868 :param `event`: a `wx.MouseCaptureLostEvent` event to be processed.
1871 if self._is_dragging:
1872 self._is_dragging = False
1873 self._on_button = False
1875 if self._drag_image:
1876 self._drag_image.EndDrag()
1877 del self._drag_image
1878 self._drag_image = None
1880 event = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_CANCEL_DRAG, self.GetId())
1881 event.SetSelection(self.GetIdxFromWindow(self._click_tab))
1882 event.SetOldSelection(event.GetSelection())
1883 event.SetEventObject(self)
1884 self.GetEventHandler().ProcessEvent(event)
1887 def OnLeftUp(self, event):
1889 Handles the ``wx.EVT_LEFT_UP`` event for L{AuiTabCtrl}.
1891 :param `event`: a `wx.MouseEvent` event to be processed.
1894 self._on_button = False
1896 if self._is_dragging:
1898 if self.HasCapture():
1901 self._is_dragging = False
1902 if self._drag_image:
1903 self._drag_image.EndDrag()
1904 del self._drag_image
1905 self._drag_image = None
1906 self.GetParent().Refresh()
1908 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, self.GetId())
1909 evt.SetSelection(self.GetIdxFromWindow(self._click_tab))
1910 evt.SetOldSelection(evt.GetSelection())
1911 evt.SetEventObject(self)
1912 self.GetEventHandler().ProcessEvent(evt)
1916 self.GetParent()._mgr.HideHint()
1918 if self.HasCapture():
1921 if self._hover_button:
1922 self._pressed_button = self._hover_button
1924 if self._pressed_button:
1926 # make sure we're still clicking the button
1927 button = self.ButtonHitTest(event.GetX(), event.GetY())
1932 if button != self._pressed_button:
1933 self._pressed_button = None
1939 if self._pressed_button.cur_state & AUI_BUTTON_STATE_DISABLED == 0:
1941 evt = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, self.GetId())
1942 evt.SetSelection(self.GetIdxFromWindow(self._click_tab))
1943 evt.SetInt(self._pressed_button.id)
1944 evt.SetEventObject(self)
1945 eventHandler = self.GetEventHandler()
1947 if eventHandler is not None:
1948 eventHandler.ProcessEvent(evt)
1950 self._pressed_button = None
1952 self._click_pt = wx.Point(-1, -1)
1953 self._is_dragging = False
1954 self._click_tab = None
1957 def OnMiddleUp(self, event):
1959 Handles the ``wx.EVT_MIDDLE_UP`` event for L{AuiTabCtrl}.
1961 :param `event`: a `wx.MouseEvent` event to be processed.
1964 eventHandler = self.GetEventHandler()
1965 if not isinstance(eventHandler, AuiTabCtrl):
1969 x, y = event.GetX(), event.GetY()
1970 wnd = self.TabHitTest(x, y)
1973 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, self.GetId())
1974 e.SetEventObject(self)
1975 e.SetSelection(self.GetIdxFromWindow(wnd))
1976 self.GetEventHandler().ProcessEvent(e)
1977 elif not self.ButtonHitTest(x, y):
1978 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_UP, self.GetId())
1979 e.SetEventObject(self)
1980 self.GetEventHandler().ProcessEvent(e)
1983 def OnMiddleDown(self, event):
1985 Handles the ``wx.EVT_MIDDLE_DOWN`` event for L{AuiTabCtrl}.
1987 :param `event`: a `wx.MouseEvent` event to be processed.
1990 eventHandler = self.GetEventHandler()
1991 if not isinstance(eventHandler, AuiTabCtrl):
1995 x, y = event.GetX(), event.GetY()
1996 wnd = self.TabHitTest(x, y)
1999 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.GetId())
2000 e.SetEventObject(self)
2001 e.SetSelection(self.GetIdxFromWindow(wnd))
2002 self.GetEventHandler().ProcessEvent(e)
2003 elif not self.ButtonHitTest(x, y):
2004 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_MIDDLE_DOWN, self.GetId())
2005 e.SetEventObject(self)
2006 self.GetEventHandler().ProcessEvent(e)
2009 def OnRightUp(self, event):
2011 Handles the ``wx.EVT_RIGHT_UP`` event for L{AuiTabCtrl}.
2013 :param `event`: a `wx.MouseEvent` event to be processed.
2016 x, y = event.GetX(), event.GetY()
2017 wnd = self.TabHitTest(x, y)
2020 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, self.GetId())
2021 e.SetEventObject(self)
2022 e.SetSelection(self.GetIdxFromWindow(wnd))
2023 self.GetEventHandler().ProcessEvent(e)
2024 elif not self.ButtonHitTest(x, y):
2025 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_UP, self.GetId())
2026 e.SetEventObject(self)
2027 self.GetEventHandler().ProcessEvent(e)
2030 def OnRightDown(self, event):
2032 Handles the ``wx.EVT_RIGHT_DOWN`` event for L{AuiTabCtrl}.
2034 :param `event`: a `wx.MouseEvent` event to be processed.
2037 x, y = event.GetX(), event.GetY()
2038 wnd = self.TabHitTest(x, y)
2041 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, self.GetId())
2042 e.SetEventObject(self)
2043 e.SetSelection(self.GetIdxFromWindow(wnd))
2044 self.GetEventHandler().ProcessEvent(e)
2045 elif not self.ButtonHitTest(x, y):
2046 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_RIGHT_DOWN, self.GetId())
2047 e.SetEventObject(self)
2048 self.GetEventHandler().ProcessEvent(e)
2051 def OnLeftDClick(self, event):
2053 Handles the ``wx.EVT_LEFT_DCLICK`` event for L{AuiTabCtrl}.
2055 :param `event`: a `wx.MouseEvent` event to be processed.
2058 x, y = event.GetX(), event.GetY()
2059 wnd = self.TabHitTest(x, y)
2062 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_TAB_DCLICK, self.GetId())
2063 e.SetEventObject(self)
2064 e.SetSelection(self.GetIdxFromWindow(wnd))
2065 self.GetEventHandler().ProcessEvent(e)
2066 elif not self.ButtonHitTest(x, y):
2067 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, self.GetId())
2068 e.SetEventObject(self)
2069 self.GetEventHandler().ProcessEvent(e)
2072 def OnMotion(self, event):
2074 Handles the ``wx.EVT_MOTION`` event for L{AuiTabCtrl}.
2076 :param `event`: a `wx.MouseEvent` event to be processed.
2079 pos = event.GetPosition()
2081 # check if the mouse is hovering above a button
2083 button = self.ButtonHitTest(pos.x, pos.y)
2084 wnd = self.TabHitTest(pos.x, pos.y)
2087 mouse_tab = self.GetIdxFromWindow(wnd)
2088 if not self._pages[mouse_tab].enabled:
2089 self._hover_button = None
2097 if self._hover_button and button != self._hover_button:
2098 self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
2099 self._hover_button = None
2103 if button.cur_state != AUI_BUTTON_STATE_HOVER:
2104 button.cur_state = AUI_BUTTON_STATE_HOVER
2107 self._hover_button = button
2112 if self._hover_button:
2113 self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
2114 self._hover_button = None
2118 if not event.LeftIsDown() or self._click_pt == wx.Point(-1, -1):
2121 if not self.HasCapture():
2124 wnd = self.TabHitTest(pos.x, pos.y)
2126 if not self._is_dragging:
2128 drag_x_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_X)
2129 drag_y_threshold = wx.SystemSettings.GetMetric(wx.SYS_DRAG_Y)
2131 if abs(pos.x - self._click_pt.x) > drag_x_threshold or \
2132 abs(pos.y - self._click_pt.y) > drag_y_threshold:
2134 self._is_dragging = True
2136 if self._drag_image:
2137 self._drag_image.EndDrag()
2138 del self._drag_image
2139 self._drag_image = None
2141 if self._agwFlags & AUI_NB_DRAW_DND_TAB:
2142 # Create the custom draw image from the icons and the text of the item
2143 mouse_tab = self.GetIdxFromWindow(wnd)
2144 page = self._pages[mouse_tab]
2145 tab_button = self._tab_close_buttons[mouse_tab]
2146 self._drag_image = TabDragImage(self, page, tab_button.cur_state, self._art)
2148 if self._agwFlags & AUI_NB_TAB_FLOAT:
2149 self._drag_image.BeginDrag(wx.Point(0,0), self, fullScreen=True)
2151 self._drag_image.BeginDragBounded(wx.Point(0,0), self, self.GetParent())
2153 # Capture the mouse cursor position offset relative to
2154 # The tab image location
2155 self._drag_img_offset = (pos[0] - page.rect.x,
2156 pos[1] - page.rect.y)
2158 self._drag_image.Show()
2161 evt2 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, self.GetId())
2162 evt2.SetSelection(self.GetIdxFromWindow(self._click_tab))
2163 evt2.SetOldSelection(evt2.GetSelection())
2164 evt2.SetEventObject(self)
2165 self.GetEventHandler().ProcessEvent(evt2)
2166 if evt2.GetDispatched():
2169 evt3 = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, self.GetId())
2170 evt3.SetSelection(self.GetIdxFromWindow(self._click_tab))
2171 evt3.SetOldSelection(evt3.GetSelection())
2172 evt3.SetEventObject(self)
2173 self.GetEventHandler().ProcessEvent(evt3)
2175 if self._drag_image:
2176 # Apply the drag images offset
2177 pos -= self._drag_img_offset
2178 self._drag_image.Move(pos)
2181 def OnLeaveWindow(self, event):
2183 Handles the ``wx.EVT_LEAVE_WINDOW`` event for L{AuiTabCtrl}.
2185 :param `event`: a `wx.MouseEvent` event to be processed.
2188 if self._hover_button:
2189 self._hover_button.cur_state = AUI_BUTTON_STATE_NORMAL
2190 self._hover_button = None
2195 def OnButton(self, event):
2197 Handles the ``EVT_AUINOTEBOOK_BUTTON`` event for L{AuiTabCtrl}.
2199 :param `event`: a L{AuiNotebookEvent} event to be processed.
2202 button = event.GetInt()
2204 if button == AUI_BUTTON_LEFT or button == AUI_BUTTON_RIGHT:
2205 if button == AUI_BUTTON_LEFT:
2206 if self.GetTabOffset() > 0:
2208 self.SetTabOffset(self.GetTabOffset()-1)
2212 self.SetTabOffset(self.GetTabOffset()+1)
2216 elif button == AUI_BUTTON_WINDOWLIST:
2217 idx = self.GetArtProvider().ShowDropDown(self, self._pages, self.GetActivePage())
2221 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
2223 e.SetOldSelection(self.GetActivePage())
2224 e.SetEventObject(self)
2225 self.GetEventHandler().ProcessEvent(e)
2231 def OnSetFocus(self, event):
2233 Handles the ``wx.EVT_SET_FOCUS`` event for L{AuiTabCtrl}.
2235 :param `event`: a `wx.FocusEvent` event to be processed.
2241 def OnKillFocus(self, event):
2243 Handles the ``wx.EVT_KILL_FOCUS`` event for L{AuiTabCtrl}.
2245 :param `event`: a `wx.FocusEvent` event to be processed.
2251 def OnKeyDown(self, event):
2253 Handles the ``wx.EVT_KEY_DOWN`` event for L{AuiTabCtrl}.
2255 :param `event`: a `wx.KeyEvent` event to be processed.
2258 key = event.GetKeyCode()
2259 nb = self.GetParent()
2261 if key == wx.WXK_LEFT:
2262 nb.AdvanceSelection(False)
2265 elif key == wx.WXK_RIGHT:
2266 nb.AdvanceSelection(True)
2269 elif key == wx.WXK_HOME:
2271 nb.SetSelection(newPage)
2274 elif key == wx.WXK_END:
2275 newPage = nb.GetPageCount() - 1
2276 nb.SetSelection(newPage)
2279 elif key == wx.WXK_TAB:
2280 if not event.ControlDown():
2282 if not event.ShiftDown(): flags |= wx.NavigationKeyEvent.IsForward
2283 if event.CmdDown(): flags |= wx.NavigationKeyEvent.WinChange
2284 self.Navigate(flags)
2287 if not nb or not isinstance(nb, AuiNotebook):
2291 bForward = bWindowChange = 0
2292 if not event.ShiftDown(): bForward |= wx.NavigationKeyEvent.IsForward
2293 if event.CmdDown(): bWindowChange |= wx.NavigationKeyEvent.WinChange
2295 keyEvent = wx.NavigationKeyEvent()
2296 keyEvent.SetDirection(bForward)
2297 keyEvent.SetWindowChange(bWindowChange)
2298 keyEvent.SetFromTab(True)
2299 keyEvent.SetEventObject(nb)
2301 if not nb.GetEventHandler().ProcessEvent(keyEvent):
2303 # Not processed? Do an explicit tab into the page.
2304 win = self.GetWindowFromIdx(self.GetActivePage())
2316 def OnKeyDown2(self, event):
2320 Handles the ``wx.EVT_KEY_DOWN`` event for L{AuiTabCtrl}.
2322 :param `event`: a `wx.KeyEvent` event to be processed.
2324 :warning: This method implementation is now deprecated. Refer to L{OnKeyDown}
2325 for the correct one.
2328 if self.GetActivePage() == -1:
2332 # We can't leave tab processing to the system on Windows, tabs and keys
2333 # get eaten by the system and not processed properly if we specify both
2334 # wxTAB_TRAVERSAL and wxWANTS_CHARS. And if we specify just wxTAB_TRAVERSAL,
2335 # we don't key arrow key events.
2337 key = event.GetKeyCode()
2339 if key == wx.WXK_NUMPAD_PAGEUP:
2341 if key == wx.WXK_NUMPAD_PAGEDOWN:
2342 key = wx.WXK_PAGEDOWN
2343 if key == wx.WXK_NUMPAD_HOME:
2345 if key == wx.WXK_NUMPAD_END:
2347 if key == wx.WXK_NUMPAD_LEFT:
2349 if key == wx.WXK_NUMPAD_RIGHT:
2352 if key == wx.WXK_TAB or key == wx.WXK_PAGEUP or key == wx.WXK_PAGEDOWN:
2354 bCtrlDown = event.ControlDown()
2355 bShiftDown = event.ShiftDown()
2357 bForward = (key == wx.WXK_TAB and not bShiftDown) or (key == wx.WXK_PAGEDOWN)
2358 bWindowChange = (key == wx.WXK_PAGEUP) or (key == wx.WXK_PAGEDOWN) or bCtrlDown
2359 bFromTab = (key == wx.WXK_TAB)
2361 nb = self.GetParent()
2362 if not nb or not isinstance(nb, AuiNotebook):
2366 keyEvent = wx.NavigationKeyEvent()
2367 keyEvent.SetDirection(bForward)
2368 keyEvent.SetWindowChange(bWindowChange)
2369 keyEvent.SetFromTab(bFromTab)
2370 keyEvent.SetEventObject(nb)
2372 if not nb.GetEventHandler().ProcessEvent(keyEvent):
2374 # Not processed? Do an explicit tab into the page.
2375 win = self.GetWindowFromIdx(self.GetActivePage())
2381 if len(self._pages) < 2:
2387 if self.GetLayoutDirection() == wx.Layout_RightToLeft:
2388 forwardKey = wx.WXK_LEFT
2389 backwardKey = wx.WXK_RIGHT
2391 forwardKey = wx.WXK_RIGHT
2392 backwardKey = wx.WXK_LEFT
2394 if key == forwardKey:
2395 if self.GetActivePage() == -1:
2397 elif self.GetActivePage() < len(self._pages) - 1:
2398 newPage = self.GetActivePage() + 1
2400 elif key == backwardKey:
2401 if self.GetActivePage() == -1:
2402 newPage = len(self._pages) - 1
2403 elif self.GetActivePage() > 0:
2404 newPage = self.GetActivePage() - 1
2406 elif key == wx.WXK_HOME:
2409 elif key == wx.WXK_END:
2410 newPage = len(self._pages) - 1
2416 e = AuiNotebookEvent(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, self.GetId())
2417 e.SetSelection(newPage)
2418 e.SetOldSelection(newPage)
2419 e.SetEventObject(self)
2420 self.GetEventHandler().ProcessEvent(e)
2426 # ----------------------------------------------------------------------
2428 class TabFrame(wx.PyWindow):
2430 TabFrame is an interesting case. It's important that all child pages
2431 of the multi-notebook control are all actually children of that control
2432 (and not grandchildren). TabFrame facilitates this. There is one
2433 instance of TabFrame for each tab control inside the multi-notebook.
2435 It's important to know that TabFrame is not a real window, but it merely
2436 used to capture the dimensions/positioning of the internal tab control and
2437 it's managed page windows.
2440 def __init__(self, parent):
2442 Default class constructor.
2443 Used internally, do not call it in your code!
2446 pre = wx.PrePyWindow()
2449 self._rect = wx.Rect(0, 0, 200, 200)
2450 self._tab_ctrl_height = 20
2451 self._tab_rect = wx.Rect()
2452 self._parent = parent
2454 self.PostCreate(pre)
2457 def SetTabCtrlHeight(self, h):
2459 Sets the tab control height.
2461 :param `h`: the tab area height.
2464 self._tab_ctrl_height = h
2467 def DoSetSize(self, x, y, width, height, flags=wx.SIZE_AUTO):
2469 Sets the position and size of the window in pixels. The `flags`
2470 parameter indicates the interpretation of the other params if they are
2473 :param `x`: the window `x` position;
2474 :param `y`: the window `y` position;
2475 :param `width`: the window width;
2476 :param `height`: the window height;
2477 :param `flags`: may have one of this bit set:
2479 =================================== ======================================
2480 Size Flags Description
2481 =================================== ======================================
2482 ``wx.SIZE_AUTO`` A -1 indicates that a class-specific default should be used.
2483 ``wx.SIZE_AUTO_WIDTH`` A -1 indicates that a class-specific default should be used for the width.
2484 ``wx.SIZE_AUTO_HEIGHT`` A -1 indicates that a class-specific default should be used for the height.
2485 ``wx.SIZE_USE_EXISTING`` Existing dimensions should be used if -1 values are supplied.
2486 ``wx.SIZE_ALLOW_MINUS_ONE`` Allow dimensions of -1 and less to be interpreted as real dimensions, not default values.
2487 ``wx.SIZE_FORCE`` Normally, if the position and the size of the window are already the same as the parameters of this function, nothing is done. but with this flag a window resize may be forced even in this case (supported in wx 2.6.2 and later and only implemented for MSW and ignored elsewhere currently)
2488 =================================== ======================================
2490 :note: Overridden from `wx.PyControl`.
2493 self._rect = wx.Rect(x, y, max(1, width), max(1, height))
2497 def DoGetSize(self):
2499 Returns the window size.
2501 :note: Overridden from `wx.PyControl`.
2504 return self._rect.width, self._rect.height
2507 def DoGetClientSize(self):
2509 Returns the window client size.
2511 :note: Overridden from `wx.PyControl`.
2514 return self._rect.width, self._rect.height
2517 def Show(self, show=True):
2519 Shows/hides the window.
2521 :param `show`: ``True`` to show the window, ``False`` otherwise.
2523 :note: Overridden from `wx.PyControl`, this method always returns ``False`` as
2524 L{TabFrame} should never be phisically shown on screen.
2531 """ Does the actual sizing of the tab control. """
2536 hideOnSingle = ((self._tabs.GetAGWFlags() & AUI_NB_HIDE_ON_SINGLE_TAB) and \
2537 self._tabs.GetPageCount() <= 1)
2539 if not hideOnSingle and not self._parent._hide_tabs:
2540 tab_height = self._tab_ctrl_height
2542 self._tab_rect = wx.Rect(self._rect.x, self._rect.y, self._rect.width, self._tab_ctrl_height)
2544 if self._tabs.GetAGWFlags() & AUI_NB_BOTTOM:
2545 self._tab_rect = wx.Rect(self._rect.x, self._rect.y + self._rect.height - tab_height,
2546 self._rect.width, tab_height)
2547 self._tabs.SetDimensions(self._rect.x, self._rect.y + self._rect.height - tab_height,
2548 self._rect.width, tab_height)
2549 self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
2553 self._tab_rect = wx.Rect(self._rect.x, self._rect.y, self._rect.width, tab_height)
2554 self._tabs.SetDimensions(self._rect.x, self._rect.y, self._rect.width, tab_height)
2555 self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
2557 # TODO: elif (GetAGWFlags() & AUI_NB_LEFT)
2558 # TODO: elif (GetAGWFlags() & AUI_NB_RIGHT)
2560 self._tabs.Refresh()
2566 self._tabs.SetDimensions(self._rect.x, self._rect.y, self._rect.width, tab_height)
2567 self._tabs.SetTabRect(wx.Rect(0, 0, self._rect.width, tab_height))
2569 pages = self._tabs.GetPages()
2573 height = self._rect.height - tab_height
2576 # avoid passing negative height to wx.Window.SetSize(), this
2577 # results in assert failures/GTK+ warnings
2580 if self._tabs.GetAGWFlags() & AUI_NB_BOTTOM:
2581 page.window.SetDimensions(self._rect.x, self._rect.y, self._rect.width, height)
2584 page.window.SetDimensions(self._rect.x, self._rect.y + tab_height,
2585 self._rect.width, height)
2587 # TODO: elif (GetAGWFlags() & AUI_NB_LEFT)
2588 # TODO: elif (GetAGWFlags() & AUI_NB_RIGHT)
2590 if repr(page.window.__class__).find("AuiMDIChildFrame") >= 0:
2591 page.window.ApplyMDIChildFrameRect()
2596 Calling this method immediately repaints the invalidated area of the window
2597 and all of its children recursively while this would usually only happen when
2598 the flow of control returns to the event loop.
2600 :note: Notice that this function doesn't invalidate any area of the window so
2601 nothing happens if nothing has been invalidated (i.e. marked as requiring a redraw).
2602 Use `Refresh` first if you want to immediately redraw the window unconditionally.
2604 :note: Overridden from `wx.PyControl`.
2611 # ----------------------------------------------------------------------
2612 # -- AuiNotebook class implementation --
2614 class AuiNotebook(wx.PyPanel):
2616 AuiNotebook is a notebook control which implements many features common in
2617 applications with dockable panes. Specifically, AuiNotebook implements functionality
2618 which allows the user to rearrange tab order via drag-and-drop, split the tab window
2619 into many different splitter configurations, and toggle through different themes to
2620 customize the control's look and feel.
2622 An effort has been made to try to maintain an API as similar to that of `wx.Notebook`.
2624 The default theme that is used is L{AuiDefaultTabArt}, which provides a modern, glossy
2625 look and feel. The theme can be changed by calling L{AuiNotebook.SetArtProvider}.
2628 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
2629 style=0, agwStyle=AUI_NB_DEFAULT_STYLE):
2631 Default class constructor.
2633 :param `parent`: the L{AuiNotebook} parent;
2634 :param `id`: an identifier for the control: a value of -1 is taken to mean a default;
2635 :param `pos`: the control position. A value of (-1, -1) indicates a default position,
2636 chosen by either the windowing system or wxPython, depending on platform;
2637 :param `size`: the control size. A value of (-1, -1) indicates a default size,
2638 chosen by either the windowing system or wxPython, depending on platform;
2639 :param `style`: the underlying `wx.PyPanel` window style;
2640 :param `agwStyle`: the AGW-specific window style. This can be a combination of the following bits:
2642 ==================================== ==================================
2643 Flag name Description
2644 ==================================== ==================================
2645 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
2646 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
2647 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
2648 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
2649 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
2650 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
2651 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
2652 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
2653 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
2654 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
2655 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
2656 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
2657 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
2658 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
2659 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
2660 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
2661 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
2662 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
2663 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
2664 ``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
2665 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
2666 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
2667 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
2668 ==================================== ==================================
2670 Default value for `agwStyle` is:
2671 ``AUI_NB_DEFAULT_STYLE`` = ``AUI_NB_TOP`` | ``AUI_NB_TAB_SPLIT`` | ``AUI_NB_TAB_MOVE`` | ``AUI_NB_SCROLL_BUTTONS`` | ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` | ``AUI_NB_MIDDLE_CLICK_CLOSE`` | ``AUI_NB_DRAW_DND_TAB``
2676 self._tab_id_counter = AuiBaseTabCtrlId
2677 self._dummy_wnd = None
2678 self._hide_tabs = False
2679 self._sash_dclick_unsplit = False
2680 self._tab_ctrl_height = 20
2681 self._requested_bmp_size = wx.Size(-1, -1)
2682 self._requested_tabctrl_height = -1
2683 self._textCtrl = None
2684 self._tabBounds = (-1, -1)
2685 self._click_tab = None
2687 wx.PyPanel.__init__(self, parent, id, pos, size, style|wx.BORDER_NONE|wx.TAB_TRAVERSAL)
2688 self._mgr = framemanager.AuiManager()
2689 self._tabs = AuiTabContainer(self)
2691 self.InitNotebook(agwStyle)
2694 def GetTabContainer(self):
2695 """ Returns the instance of L{AuiTabContainer}. """
2700 def InitNotebook(self, agwStyle):
2702 Contains common initialization code called by all constructors.
2704 :param `agwStyle`: the notebook style.
2709 self.SetName("AuiNotebook")
2710 self._agwFlags = agwStyle
2712 self._popupWin = None
2713 self._naviIcon = None
2714 self._imageList = None
2715 self._last_drag_x = 0
2717 self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
2718 self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
2719 self._selected_font.SetWeight(wx.BOLD)
2721 self.SetArtProvider(TA.AuiDefaultTabArt())
2723 self._dummy_wnd = wx.Window(self, wx.ID_ANY, wx.Point(0, 0), wx.Size(0, 0))
2724 self._dummy_wnd.SetSize((200, 200))
2725 self._dummy_wnd.Show(False)
2727 self._mgr.SetManagedWindow(self)
2728 self._mgr.SetAGWFlags(AUI_MGR_DEFAULT)
2729 self._mgr.SetDockSizeConstraint(1.0, 1.0) # no dock size constraint
2731 self._mgr.AddPane(self._dummy_wnd, framemanager.AuiPaneInfo().Name("dummy").Bottom().CaptionVisible(False).Show(False))
2734 self.Bind(wx.EVT_SIZE, self.OnSize)
2735 self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocusNotebook)
2736 self.Bind(EVT_AUINOTEBOOK_PAGE_CHANGING, self.OnTabClicked,
2737 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2738 self.Bind(EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnTabBeginDrag,
2739 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2740 self.Bind(EVT_AUINOTEBOOK_END_DRAG, self.OnTabEndDrag,
2741 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2742 self.Bind(EVT_AUINOTEBOOK_DRAG_MOTION, self.OnTabDragMotion,
2743 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2744 self.Bind(EVT_AUINOTEBOOK_CANCEL_DRAG, self.OnTabCancelDrag,
2745 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2746 self.Bind(EVT_AUINOTEBOOK_BUTTON, self.OnTabButton,
2747 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2748 self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, self.OnTabMiddleDown,
2749 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2750 self.Bind(EVT_AUINOTEBOOK_TAB_MIDDLE_UP, self.OnTabMiddleUp,
2751 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2752 self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.OnTabRightDown,
2753 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2754 self.Bind(EVT_AUINOTEBOOK_TAB_RIGHT_UP, self.OnTabRightUp,
2755 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2756 self.Bind(EVT_AUINOTEBOOK_BG_DCLICK, self.OnTabBgDClick,
2757 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2758 self.Bind(EVT_AUINOTEBOOK_TAB_DCLICK, self.OnTabDClick,
2759 id=AuiBaseTabCtrlId, id2=AuiBaseTabCtrlId+500)
2761 self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKeyNotebook)
2764 def SetArtProvider(self, art):
2766 Sets the art provider to be used by the notebook.
2768 :param `art`: an art provider.
2771 self._tabs.SetArtProvider(art)
2772 self.UpdateTabCtrlHeight(force=True)
2775 def SavePerspective(self):
2777 Saves the entire user interface layout into an encoded string, which can then
2778 be stored by the application (probably using `wx.Config`). When a perspective
2779 is restored using L{LoadPerspective}, the entire user interface will return
2780 to the state it was when the perspective was saved.
2783 # Build list of panes/tabs
2785 all_panes = self._mgr.GetAllPanes()
2787 for pane in all_panes:
2789 if pane.name == "dummy":
2792 tabframe = pane.window
2797 tabs += pane.name + "="
2800 page_count = tabframe._tabs.GetPageCount()
2802 for p in xrange(page_count):
2804 page = tabframe._tabs.GetPage(p)
2805 page_idx = self._tabs.GetIdxFromWindow(page.window)
2810 if p == tabframe._tabs.GetActivePage():
2812 elif page_idx == self._curpage:
2815 tabs += "%u"%page_idx
2819 # Add frame perspective
2820 tabs += self._mgr.SavePerspective()
2825 def LoadPerspective(self, layout):
2827 Loads a layout which was saved with L{SavePerspective}.
2829 :param `layout`: a string which contains a saved L{AuiNotebook} layout.
2832 # Remove all tab ctrls (but still keep them in main index)
2833 tab_count = self._tabs.GetPageCount()
2834 for i in xrange(tab_count):
2835 wnd = self._tabs.GetWindowFromIdx(i)
2837 # find out which onscreen tab ctrl owns this tab
2838 ctrl, ctrl_idx = self.FindTab(wnd)
2842 # remove the tab from ctrl
2843 if not ctrl.RemovePage(wnd):
2846 self.RemoveEmptyTabFrames()
2849 tabs = layout[0:layout.index("@")]
2858 tab_part = tabs[0:tabs.index('|')]
2860 if "=" not in tab_part:
2861 # No pages in this perspective...
2865 pane_name = tab_part[0:tab_part.index("=")]
2867 # create a new tab frame
2868 new_tabs = TabFrame(self)
2869 self._tab_id_counter += 1
2870 new_tabs._tabs = AuiTabCtrl(self, self._tab_id_counter)
2871 new_tabs._tabs.SetArtProvider(self._tabs.GetArtProvider().Clone())
2872 new_tabs.SetTabCtrlHeight(self._tab_ctrl_height)
2873 new_tabs._tabs.SetAGWFlags(self._agwFlags)
2874 dest_tabs = new_tabs._tabs
2876 # create a pane info structure with the information
2877 # about where the pane should be added
2878 pane_info = framemanager.AuiPaneInfo().Name(pane_name).Bottom().CaptionVisible(False)
2879 self._mgr.AddPane(new_tabs, pane_info)
2881 # Get list of tab id's and move them to pane
2882 tab_list = tab_part[tab_part.index("=")+1:]
2883 to_break2, active_found = False, False
2886 if "," not in tab_list:
2890 tab = tab_list[0:tab_list.index(",")]
2891 tab_list = tab_list[tab_list.index(",")+1:]
2893 # Check if this page has an 'active' marker
2899 if tab_idx >= self.GetPageCount():
2904 page = self._tabs.GetPage(tab_idx)
2905 newpage_idx = dest_tabs.GetPageCount()
2906 dest_tabs.InsertPage(page.window, page, newpage_idx)
2909 dest_tabs.SetActivePage(newpage_idx)
2917 if not active_found:
2918 dest_tabs.SetActivePage(0)
2921 dest_tabs.DoShowHide()
2927 tabs = tabs[tabs.index('|')+1:]
2929 # Load the frame perspective
2930 frames = layout[layout.index('@')+1:]
2931 self._mgr.LoadPerspective(frames)
2933 # Force refresh of selection
2935 self.SetSelection(sel_page)
2940 def SetTabCtrlHeight(self, height):
2942 Sets the tab height.
2944 By default, the tab control height is calculated by measuring the text
2945 height and bitmap sizes on the tab captions.
2947 Calling this method will override that calculation and set the tab control
2948 to the specified height parameter. A call to this method will override
2949 any call to L{SetUniformBitmapSize}. Specifying -1 as the height will
2950 return the control to its default auto-sizing behaviour.
2952 :param `height`: the tab control area height.
2955 self._requested_tabctrl_height = height
2957 # if window is already initialized, recalculate the tab height
2959 self.UpdateTabCtrlHeight()
2962 def SetUniformBitmapSize(self, size):
2964 Ensures that all tabs will have the same height, even if some tabs
2965 don't have bitmaps. Passing ``wx.DefaultSize`` to this
2966 function will instruct the control to use dynamic tab height, which is
2967 the default behaviour. Under the default behaviour, when a tab with a
2968 large bitmap is added, the tab control's height will automatically
2969 increase to accommodate the larger bitmap.
2971 :param `size`: an instance of `wx.Size` specifying the tab bitmap size.
2974 self._requested_bmp_size = wx.Size(*size)
2976 # if window is already initialized, recalculate the tab height
2978 self.UpdateTabCtrlHeight()
2981 def UpdateTabCtrlHeight(self, force=False):
2983 UpdateTabCtrlHeight() does the actual tab resizing. It's meant
2984 to be used interally.
2986 :param `force`: ``True`` to force the tab art to repaint.
2989 # get the tab ctrl height we will use
2990 height = self.CalculateTabCtrlHeight()
2992 # if the tab control height needs to change, update
2993 # all of our tab controls with the new height
2994 if self._tab_ctrl_height != height or force:
2995 art = self._tabs.GetArtProvider()
2997 self._tab_ctrl_height = height
2999 all_panes = self._mgr.GetAllPanes()
3000 for pane in all_panes:
3002 if pane.name == "dummy":
3005 tab_frame = pane.window
3006 tabctrl = tab_frame._tabs
3007 tab_frame.SetTabCtrlHeight(self._tab_ctrl_height)
3008 tabctrl.SetArtProvider(art.Clone())
3009 tab_frame.DoSizing()
3012 def UpdateHintWindowSize(self):
3013 """ Updates the L{AuiManager} hint window size. """
3015 size = self.CalculateNewSplitSize()
3017 # the placeholder hint window should be set to this size
3018 info = self._mgr.GetPane("dummy")
3023 self._dummy_wnd.SetSize(size)
3026 def CalculateNewSplitSize(self):
3027 """ Calculates the size of the new split. """
3029 # count number of tab controls
3031 all_panes = self._mgr.GetAllPanes()
3033 for pane in all_panes:
3034 if pane.name == "dummy":
3039 # if there is only one tab control, the first split
3040 # should happen around the middle
3041 if tab_ctrl_count < 2:
3042 new_split_size = self.GetClientSize()
3043 new_split_size.x /= 2
3044 new_split_size.y /= 2
3048 # this is in place of a more complicated calculation
3049 # that needs to be implemented
3050 new_split_size = wx.Size(180, 180)
3052 return new_split_size
3055 def CalculateTabCtrlHeight(self):
3056 """ Calculates the tab control area height. """
3058 # if a fixed tab ctrl height is specified,
3059 # just return that instead of calculating a
3061 if self._requested_tabctrl_height != -1:
3062 return self._requested_tabctrl_height
3064 # find out new best tab height
3065 art = self._tabs.GetArtProvider()
3067 return art.GetBestTabCtrlSize(self, self._tabs.GetPages(), self._requested_bmp_size)
3070 def GetArtProvider(self):
3071 """ Returns the associated art provider. """
3073 return self._tabs.GetArtProvider()
3076 def SetAGWWindowStyleFlag(self, agwStyle):
3078 Sets the AGW-specific style of the window.
3080 :param `agwStyle`: the new window style. This can be a combination of the following bits:
3082 ==================================== ==================================
3083 Flag name Description
3084 ==================================== ==================================
3085 ``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
3086 ``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
3087 ``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
3088 ``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
3089 ``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
3090 ``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
3091 ``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
3092 ``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
3093 ``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
3094 ``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
3095 ``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
3096 ``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
3097 ``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
3098 ``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close L{AuiNotebook} tabs by mouse middle button click
3099 ``AUI_NB_SUB_NOTEBOOK`` This style is used by L{AuiManager} to create automatic AuiNotebooks
3100 ``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
3101 ``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
3102 ``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
3103 ``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
3104 ``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
3105 ``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
3106 ``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
3107 ``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
3108 ==================================== ==================================
3110 :note: Please note that some styles cannot be changed after the window
3111 creation and that `Refresh` might need to be be called after changing the
3112 others for the change to take place immediately.
3114 :todo: Implementation of flags ``AUI_NB_RIGHT`` and ``AUI_NB_LEFT``.
3117 self._agwFlags = agwStyle
3119 # if the control is already initialized
3120 if self._mgr.GetManagedWindow() == self:
3122 # let all of the tab children know about the new style
3124 all_panes = self._mgr.GetAllPanes()
3125 for pane in all_panes:
3126 if pane.name == "dummy":
3129 tabframe = pane.window
3130 tabctrl = tabframe._tabs
3131 tabctrl.SetAGWFlags(self._agwFlags)
3137 def GetAGWWindowStyleFlag(self):
3139 Returns the AGW-specific style of the window.
3141 :see: L{SetAGWWindowStyleFlag} for a list of possible AGW-specific window styles.
3144 return self._agwFlags
3147 def AddPage(self, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap, control=None):
3149 Adds a page. If the `select` parameter is ``True``, calling this will generate a
3152 :param `page`: the page to be added;
3153 :param `caption`: specifies the text for the new page;
3154 :param `select`: specifies whether the page should be selected;
3155 :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
3156 :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
3157 :param `control`: a `wx.Window` instance inside a tab (or ``None``).
3160 return self.InsertPage(self.GetPageCount(), page, caption, select, bitmap, disabled_bitmap, control)
3163 def InsertPage(self, page_idx, page, caption, select=False, bitmap=wx.NullBitmap, disabled_bitmap=wx.NullBitmap,
3166 This is similar to L{AddPage}, but allows the ability to specify the insert location.
3168 :param `page_idx`: specifies the position for the new page;
3169 :param `page`: the page to be added;
3170 :param `caption`: specifies the text for the new page;
3171 :param `select`: specifies whether the page should be selected;
3172 :param `bitmap`: the `wx.Bitmap` to display in the enabled tab;
3173 :param `disabled_bitmap`: the `wx.Bitmap` to display in the disabled tab;
3174 :param `control`: a `wx.Window` instance inside a tab (or ``None``).
3181 info = AuiNotebookPage()
3183 info.caption = caption
3184 info.bitmap = bitmap
3186 info.control = control
3188 originalPaneMgr = framemanager.GetManager(page)
3190 originalPane = originalPaneMgr.GetPane(page)
3193 info.hasCloseButton = originalPane.HasCloseButton()
3195 if bitmap.IsOk() and not disabled_bitmap.IsOk():
3196 disabled_bitmap = MakeDisabledBitmap(bitmap)
3197 info.dis_bitmap = disabled_bitmap
3199 # if there are currently no tabs, the first added
3200 # tab must be active
3201 if self._tabs.GetPageCount() == 0:
3204 self._tabs.InsertPage(page, info, page_idx)
3206 # if that was the first page added, even if
3207 # select is False, it must become the "current page"
3208 # (though no select events will be fired)
3209 if not select and self._tabs.GetPageCount() == 1:
3212 active_tabctrl = self.GetActiveTabCtrl()
3213 if page_idx >= active_tabctrl.GetPageCount():
3214 active_tabctrl.AddPage(page, info)
3216 active_tabctrl.InsertPage(page, info, page_idx)
3221 control.Reparent(active_tabctrl)
3224 self.UpdateTabCtrlHeight(force=force)
3226 active_tabctrl.DoShowHide()
3228 # adjust selected index
3229 if self._curpage >= page_idx:
3233 self.SetSelectionToWindow(page)
3238 def DeletePage(self, page_idx):
3240 Deletes a page at the given index. Calling this method will generate a page
3243 :param `page_idx`: the page index to be deleted.
3245 :note: L{DeletePage} removes a tab from the multi-notebook, and destroys the window as well.
3250 if page_idx >= self._tabs.GetPageCount():
3253 wnd = self._tabs.GetWindowFromIdx(page_idx)
3254 # hide the window in advance, as this will
3258 self.RemoveControlFromPage(page_idx)
3260 if not self.RemovePage(page_idx):
3268 def RemovePage(self, page_idx):
3270 Removes a page, without deleting the window pointer.
3272 :param `page_idx`: the page index to be removed.
3274 :note: L{RemovePage} removes a tab from the multi-notebook, but does not destroy the window.
3279 # save active window pointer
3281 if self._curpage >= 0:
3282 active_wnd = self._tabs.GetWindowFromIdx(self._curpage)
3284 # save pointer of window being deleted
3285 wnd = self._tabs.GetWindowFromIdx(page_idx)
3288 # make sure we found the page
3292 # find out which onscreen tab ctrl owns this tab
3293 ctrl, ctrl_idx = self.FindTab(wnd)
3297 currentPage = ctrl.GetPage(ctrl_idx)
3298 is_curpage = (self._curpage == page_idx)
3299 is_active_in_split = currentPage.active
3301 # remove the tab from main catalog
3302 if not self._tabs.RemovePage(wnd):
3305 # remove the tab from the onscreen tab ctrl
3306 ctrl.RemovePage(wnd)
3308 if is_active_in_split:
3310 ctrl_new_page_count = ctrl.GetPageCount()
3312 if ctrl_idx >= ctrl_new_page_count:
3313 ctrl_idx = ctrl_new_page_count - 1
3315 if ctrl_idx >= 0 and ctrl_idx < ctrl.GetPageCount():
3317 ctrl_idx = self.FindNextActiveTab(ctrl_idx, ctrl)
3319 # set new page as active in the tab split
3320 ctrl.SetActivePage(ctrl_idx)
3322 # if the page deleted was the current page for the
3323 # entire tab control, then record the window
3324 # pointer of the new active page for activation
3326 new_active = ctrl.GetWindowFromIdx(ctrl_idx)
3330 # we are not deleting the active page, so keep it the same
3331 new_active = active_wnd
3335 # we haven't yet found a new page to active,
3336 # so select the next page from the main tab
3339 if 0 <= page_idx < self._tabs.GetPageCount():
3340 new_active = self._tabs.GetPage(page_idx).window
3341 if not new_active and self._tabs.GetPageCount() > 0:
3342 new_active = self._tabs.GetPage(0).window
3344 self.RemoveEmptyTabFrames()
3346 # set new active pane
3348 if not self.IsBeingDeleted():
3350 self.SetSelectionToWindow(new_active)
3353 self._tabs.SetNoneActive()
3358 def FindNextActiveTab(self, ctrl_idx, ctrl):
3360 Finds the next active tab (used mainly when L{AuiNotebook} has inactive/disabled
3363 :param `ctrl_idx`: the index of the first (most obvious) tab to check for active status;
3364 :param `ctrl`: an instance of L{AuiTabCtrl}.
3367 if self.GetEnabled(ctrl_idx):
3370 for indx in xrange(ctrl_idx, ctrl.GetPageCount()):
3371 if self.GetEnabled(indx):
3374 for indx in xrange(ctrl_idx, -1, -1):
3375 if self.GetEnabled(indx):
3381 def HideAllTabs(self, hidden=True):
3383 Hides all tabs on the L{AuiNotebook} control.
3385 :param `hidden`: if ``True`` hides all tabs.
3388 self._hide_tabs = hidden
3391 def SetSashDClickUnsplit(self, unsplit=True):
3393 Sets whether to unsplit a splitted L{AuiNotebook} when double-clicking on a sash.
3395 :param `unsplit`: ``True`` to unsplit on sash double-clicking, ``False`` otherwise.
3398 self._sash_dclick_unsplit = unsplit
3401 def GetSashDClickUnsplit(self):
3403 Returns whether a splitted L{AuiNotebook} can be unsplitted by double-clicking
3404 on the splitter sash.
3407 return self._sash_dclick_unsplit
3410 def SetMinMaxTabWidth(self, minTabWidth, maxTabWidth):
3412 Sets the minimum and/or the maximum tab widths for L{AuiNotebook} when the
3413 ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
3415 Pass -1 to either `minTabWidth` or `maxTabWidth` to reset to the default tab
3416 width behaviour for L{AuiNotebook}.
3418 :param `minTabWidth`: the minimum allowed tab width, in pixels;
3419 :param `maxTabWidth`: the maximum allowed tab width, in pixels.
3421 :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
3425 if minTabWidth > maxTabWidth:
3426 raise Exception("Minimum tab width must be less or equal than maximum tab width")
3428 self._tabBounds = (minTabWidth, maxTabWidth)
3429 self.SetAGWWindowStyleFlag(self._agwFlags)
3432 def GetMinMaxTabWidth(self):
3434 Returns the minimum and the maximum tab widths for L{AuiNotebook} when the
3435 ``AUI_NB_TAB_FIXED_WIDTH`` style is defined.
3437 :note: Minimum and maximum tabs widths are used only when the ``AUI_NB_TAB_FIXED_WIDTH``
3440 :see: L{SetMinMaxTabWidth} for more information.
3443 return self._tabBounds
3446 def GetPageIndex(self, page_wnd):
3448 Returns the page index for the specified window. If the window is not
3449 found in the notebook, ``wx.NOT_FOUND`` is returned.
3452 return self._tabs.GetIdxFromWindow(page_wnd)
3455 def SetPageText(self, page_idx, text):
3457 Sets the tab label for the page.
3459 :param `page_idx`: the page index;
3460 :param `text`: the new tab label.
3463 if page_idx >= self._tabs.GetPageCount():
3466 # update our own tab catalog
3467 page_info = self._tabs.GetPage(page_idx)
3468 should_refresh = page_info.caption != text
3469 page_info.caption = text
3471 # update what's on screen
3472 ctrl, ctrl_idx = self.FindTab(page_info.window)
3476 info = ctrl.GetPage(ctrl_idx)
3477 should_refresh = should_refresh or info.caption != text
3484 self.UpdateTabCtrlHeight(force=True)
3489 def GetPageText(self, page_idx):
3491 Returns the tab label for the page.
3493 :param `page_idx`: the page index.
3496 if page_idx >= self._tabs.GetPageCount():
3499 # update our own tab catalog
3500 page_info = self._tabs.GetPage(page_idx)
3501 return page_info.caption
3504 def SetPageBitmap(self, page_idx, bitmap):
3506 Sets the tab bitmap for the page.
3508 :param `page_idx`: the page index;
3509 :param `bitmap`: an instance of `wx.Bitmap`.
3512 if page_idx >= self._tabs.GetPageCount():
3515 # update our own tab catalog
3516 page_info = self._tabs.GetPage(page_idx)
3517 should_refresh = page_info.bitmap is not bitmap
3518 page_info.bitmap = bitmap
3519 if bitmap.IsOk() and not page_info.dis_bitmap.IsOk():
3520 page_info.dis_bitmap = MakeDisabledBitmap(bitmap)
3522 # tab height might have changed
3523 self.UpdateTabCtrlHeight()
3525 # update what's on screen
3526 ctrl, ctrl_idx = self.FindTab(page_info.window)
3530 info = ctrl.GetPage(ctrl_idx)
3531 should_refresh = should_refresh or info.bitmap is not bitmap
3532 info.bitmap = bitmap
3533 info.dis_bitmap = page_info.dis_bitmap
3541 def GetPageBitmap(self, page_idx):
3543 Returns the tab bitmap for the page.
3545 :param `page_idx`: the page index.
3548 if page_idx >= self._tabs.GetPageCount():
3549 return wx.NullBitmap
3551 # update our own tab catalog
3552 page_info = self._tabs.GetPage(page_idx)
3553 return page_info.bitmap
3556 def SetImageList(self, imageList):
3558 Sets the image list for the L{AuiNotebook} control.
3560 :param `imageList`: an instance of `wx.ImageList`.
3563 self._imageList = imageList
3566 def AssignImageList(self, imageList):
3568 Sets the image list for the L{AuiNotebook} control.
3570 :param `imageList`: an instance of `wx.ImageList`.
3573 self.SetImageList(imageList)
3576 def GetImageList(self):
3577 """ Returns the associated image list (if any). """
3579 return self._imageList
3582 def SetPageImage(self, page, image):
3584 Sets the image index for the given page.
3586 :param `page`: the page index;
3587 :param `image`: an index into the image list which was set with L{SetImageList}.
3590 if page >= self._tabs.GetPageCount():
3593 if not isinstance(image, types.IntType):
3594 raise Exception("The image parameter must be an integer, you passed " \
3597 if not self._imageList:
3598 raise Exception("To use SetPageImage you need to associate an image list " \
3599 "Using SetImageList or AssignImageList")
3601 if image >= self._imageList.GetImageCount():
3602 raise Exception("Invalid image index (%d), the image list contains only" \
3603 " (%d) bitmaps"%(image, self._imageList.GetImageCount()))
3606 self.SetPageBitmap(page, wx.NullBitmap)
3609 bitmap = self._imageList.GetBitmap(image)
3610 self.SetPageBitmap(page, bitmap)
3613 def GetPageImage(self, page):
3615 Returns the image index for the given page.
3617 :param `page`: the given page for which to retrieve the image index.
3620 if page >= self._tabs.GetPageCount():
3623 bitmap = self.GetPageBitmap(page)
3624 for indx in xrange(self._imageList.GetImageCount()):
3625 imgListBmp = self._imageList.GetBitmap(indx)
3626 if imgListBmp == bitmap:
3632 def SetPageTextColour(self, page_idx, colour):
3634 Sets the tab text colour for the page.
3636 :param `page_idx`: the page index;
3637 :param `colour`: an instance of `wx.Colour`.
3640 if page_idx >= self._tabs.GetPageCount():
3643 # update our own tab catalog
3644 page_info = self._tabs.GetPage(page_idx)
3645 should_refresh = page_info.text_colour != colour
3646 page_info.text_colour = colour
3648 # update what's on screen
3649 ctrl, ctrl_idx = self.FindTab(page_info.window)
3653 info = ctrl.GetPage(ctrl_idx)
3654 should_refresh = should_refresh or info.text_colour != colour
3655 info.text_colour = page_info.text_colour
3664 def GetPageTextColour(self, page_idx):
3666 Returns the tab text colour for the page.
3668 :param `page_idx`: the page index.
3671 if page_idx >= self._tabs.GetPageCount():
3672 return wx.NullColour
3674 # update our own tab catalog
3675 page_info = self._tabs.GetPage(page_idx)
3676 return page_info.text_colour
3679 def AddControlToPage(self, page_idx, control):
3681 Adds a control inside a tab (not in the tab area).
3683 :param `page_idx`: the page index;
3684 :param `control`: an instance of `wx.Window`.