+"""
+Description
+===========
+
+The idea of `SwitcherDialog` is to make it easier to implement keyboard
+navigation in AUI and other applications that have multiple panes and
+tabs.
+
+A key combination with a modifier (such as ``Ctrl`` + ``Tab``) shows the
+dialog, and the user holds down the modifier whilst navigating with
+``Tab`` and arrow keys before releasing the modifier to dismiss the dialog
+and activate the selected pane.
+
+The switcher dialog is a multi-column menu with no scrolling, implemented
+by the `MultiColumnListCtrl` class. You can have headings for your items
+for logical grouping, and you can force a column break if you need to.
+
+The modifier used for invoking and dismissing the dialog can be customised,
+as can the colours, number of rows, and the key used for cycling through
+the items. So you can use different keys on different platforms if
+required (especially since ``Ctrl`` + ``Tab`` is reserved on some platforms).
+
+Items are shown as names and optional 16x16 images.
+
+
+Base Functionalities
+====================
+
+To use the dialog, you set up the items in a `SwitcherItems` object,
+before passing this to the `SwitcherDialog` instance.
+
+Call L{SwitcherItems.AddItem} and optionally L{SwitcherItems.AddGroup} to add items and headings. These
+functions take a label (to be displayed to the user), an identifying name,
+an integer id, and a bitmap. The name and id are purely for application-defined
+identification. You may also set a description to be displayed when each
+item is selected; and you can set a window pointer for convenience when
+activating the desired window after the dialog returns.
+
+Have created the dialog, you call `ShowModal()`, and if the return value is
+``wx.ID_OK``, retrieve the selection from the dialog and activate the pane.
+
+The sample code below shows a generic method of finding panes and notebook
+tabs within the current L{AuiManager}, and using the pane name or notebook
+tab position to display the pane.
+
+The only other code to add is a menu item with the desired accelerator,
+whose modifier matches the one you pass to L{SwitcherDialog.SetModifierKey}
+(the default being ``wx.WXK_CONTROL``).
+
+
+Usage
+=====
+
+Menu item::
+
+ if wx.Platform == "__WXMAC__":
+ switcherAccel = "Alt+Tab"
+ elif wx.Platform == "__WXGTK__":
+ switcherAccel = "Ctrl+/"
+ else:
+ switcherAccel = "Ctrl+Tab"
+
+ view_menu.Append(ID_SwitchPane, _("S&witch Window...") + "\t" + switcherAccel)
+
+
+Event handler::
+
+ def OnSwitchPane(self, event):
+
+ items = SwitcherItems()
+ items.SetRowCount(12)
+
+ # Add the main windows and toolbars, in two separate columns
+ # We'll use the item 'id' to store the notebook selection, or -1 if not a page
+
+ for k in xrange(2):
+ if k == 0:
+ items.AddGroup(_("Main Windows"), "mainwindows")
+ else:
+ items.AddGroup(_("Toolbars"), "toolbars").BreakColumn()
+
+ for pane in self._mgr.GetAllPanes():
+ name = pane.name
+ caption = pane.caption
+
+ toolbar = isinstance(info.window, wx.ToolBar) or isinstance(info.window, aui.AuiToolBar)
+ if caption and (toolBar and k == 1) or (not toolBar and k == 0):
+ items.AddItem(caption, name, -1).SetWindow(pane.window)
+
+ # Now add the wxAuiNotebook pages
+
+ items.AddGroup(_("Notebook Pages"), "pages").BreakColumn()
+
+ for pane in self._mgr.GetAllPanes():
+ nb = pane.window
+ if isinstance(nb, aui.AuiNotebook):
+ for j in xrange(nb.GetPageCount()):
+
+ name = nb.GetPageText(j)
+ win = nb.GetPage(j)
+
+ items.AddItem(name, name, j, nb.GetPageBitmap(j)).SetWindow(win)
+
+ # Select the focused window
+
+ idx = items.GetIndexForFocus()
+ if idx != wx.NOT_FOUND:
+ items.SetSelection(idx)
+
+ if wx.Platform == "__WXMAC__":
+ items.SetBackgroundColour(wx.WHITE)
+
+ # Show the switcher dialog
+
+ dlg = SwitcherDialog(items, wx.GetApp().GetTopWindow())
+
+ # In GTK+ we can't use Ctrl+Tab; we use Ctrl+/ instead and tell the switcher
+ # to treat / in the same was as tab (i.e. cycle through the names)
+
+ if wx.Platform == "__WXGTK__":
+ dlg.SetExtraNavigationKey(wxT('/'))
+
+ if wx.Platform == "__WXMAC__":
+ dlg.SetBackgroundColour(wx.WHITE)
+ dlg.SetModifierKey(wx.WXK_ALT)
+
+ ans = dlg.ShowModal()
+
+ if ans == wx.ID_OK and dlg.GetSelection() != -1:
+ item = items.GetItem(dlg.GetSelection())
+
+ if item.GetId() == -1:
+ info = self._mgr.GetPane(item.GetName())
+ info.Show()
+ self._mgr.Update()
+ info.window.SetFocus()
+
+ else:
+ nb = item.GetWindow().GetParent()
+ win = item.GetWindow();
+ if isinstance(nb, aui.AuiNotebook):
+ nb.SetSelection(item.GetId())
+ win.SetFocus()
+
+
+"""
+
+import wx
+
+import auibook
+from aui_utilities import FindFocusDescendant
+from aui_constants import SWITCHER_TEXT_MARGIN_X, SWITCHER_TEXT_MARGIN_Y
+
+
+# Define a translation function
+_ = wx.GetTranslation
+
+
+class SwitcherItem(object):
+ """ An object containing information about one item. """
+
+ def __init__(self, item=None):
+ """ Default class constructor. """
+
+ self._id = 0
+ self._isGroup = False
+ self._breakColumn = False
+ self._rowPos = 0
+ self._colPos = 0
+ self._window = None
+ self._description = ""
+
+ self._textColour = wx.NullColour
+ self._bitmap = wx.NullBitmap
+ self._font = wx.NullFont
+
+ if item:
+ self.Copy(item)
+
+
+ def Copy(self, item):
+ """
+ Copy operator between 2 L{SwitcherItem} instances.
+
+ :param `item`: another instance of L{SwitcherItem}.
+ """
+
+ self._id = item._id
+ self._name = item._name
+ self._title = item._title
+ self._isGroup = item._isGroup
+ self._breakColumn = item._breakColumn
+ self._rect = item._rect
+ self._font = item._font
+ self._textColour = item._textColour
+ self._bitmap = item._bitmap
+ self._description = item._description
+ self._rowPos = item._rowPos
+ self._colPos = item._colPos
+ self._window = item._window
+
+
+ def SetTitle(self, title):
+
+ self._title = title
+ return self
+
+
+ def GetTitle(self):
+
+ return self._title
+
+
+ def SetName(self, name):
+
+ self._name = name
+ return self
+
+
+ def GetName(self):
+
+ return self._name
+
+
+ def SetDescription(self, descr):
+
+ self._description = descr
+ return self
+
+
+ def GetDescription(self):
+
+ return self._description
+
+
+ def SetId(self, id):
+
+ self._id = id
+ return self
+
+
+ def GetId(self):
+
+ return self._id
+
+
+ def SetIsGroup(self, isGroup):
+
+ self._isGroup = isGroup
+ return self
+
+
+ def GetIsGroup(self):
+
+ return self._isGroup
+
+
+ def BreakColumn(self, breakCol=True):
+
+ self._breakColumn = breakCol
+ return self
+
+
+ def GetBreakColumn(self):
+
+ return self._breakColumn
+
+
+ def SetRect(self, rect):
+
+ self._rect = rect
+ return self
+
+
+ def GetRect(self):
+
+ return self._rect
+
+
+ def SetTextColour(self, colour):
+
+ self._textColour = colour
+ return self
+
+
+ def GetTextColour(self):
+
+ return self._textColour
+
+
+ def SetFont(self, font):
+
+ self._font = font
+ return self
+
+
+ def GetFont(self):
+
+ return self._font
+
+
+ def SetBitmap(self, bitmap):
+
+ self._bitmap = bitmap
+ return self
+
+
+ def GetBitmap(self):
+
+ return self._bitmap
+
+
+ def SetRowPos(self, pos):
+
+ self._rowPos = pos
+ return self
+
+
+ def GetRowPos(self):
+
+ return self._rowPos
+
+
+ def SetColPos(self, pos):
+
+ self._colPos = pos
+ return self
+
+
+ def GetColPos(self):
+
+ return self._colPos
+
+
+ def SetWindow(self, win):
+
+ self._window = win
+ return self
+
+
+ def GetWindow(self):
+
+ return self._window
+
+
+class SwitcherItems(object):
+ """ An object containing switcher items. """
+
+ def __init__(self, items=None):
+ """ Default class constructor. """
+
+ self._selection = -1
+ self._rowCount = 10
+ self._columnCount = 0
+
+ self._backgroundColour = wx.NullColour
+ self._textColour = wx.NullColour
+ self._selectionColour = wx.NullColour
+ self._selectionOutlineColour = wx.NullColour
+ self._itemFont = wx.NullFont
+
+ self._items = []
+
+ if wx.Platform == "__WXMSW__":
+ # If on Windows XP/Vista, use more appropriate colours
+ self.SetSelectionOutlineColour(wx.Colour(49, 106, 197))
+ self.SetSelectionColour(wx.Colour(193, 210, 238))
+
+ if items:
+ self.Copy(items)
+
+
+ def Copy(self, items):
+ """
+ Copy operator between 2 L{SwitcherItems}.
+
+ :param `items`: another instance of L{SwitcherItems}.
+ """
+
+ self.Clear()
+
+ for item in items._items:
+ self._items.append(item)
+
+ self._selection = items._selection
+ self._rowCount = items._rowCount
+ self._columnCount = items._columnCount
+
+ self._backgroundColour = items._backgroundColour
+ self._textColour = items._textColour
+ self._selectionColour = items._selectionColour
+ self._selectionOutlineColour = items._selectionOutlineColour
+ self._itemFont = items._itemFont
+
+
+ def AddItem(self, titleOrItem, name=None, id=0, bitmap=wx.NullBitmap):
+
+ if isinstance(titleOrItem, SwitcherItem):
+ self._items.append(titleOrItem)
+ return self._items[-1]
+
+ item = SwitcherItem()
+ item.SetTitle(titleOrItem)
+ item.SetName(name)
+ item.SetId(id)
+ item.SetBitmap(bitmap)
+
+ self._items.append(item)
+ return self._items[-1]
+
+
+ def AddGroup(self, title, name, id=0, bitmap=wx.NullBitmap):
+
+ item = self.AddItem(title, name, id, bitmap)
+ item.SetIsGroup(True)
+
+ return item
+
+
+ def Clear(self):
+
+ self._items = []
+
+
+ def FindItemByName(self, name):
+
+ for i in xrange(len(self._items)):
+ if self._items[i].GetName() == name:
+ return i
+
+ return wx.NOT_FOUND
+
+
+ def FindItemById(self, id):
+
+ for i in xrange(len(self._items)):
+ if self._items[i].GetId() == id:
+ return i
+
+ return wx.NOT_FOUND
+
+
+ def SetSelection(self, sel):
+
+ self._selection = sel
+
+
+ def SetSelectionByName(self, name):
+
+ idx = self.FindItemByName(name)
+ if idx != wx.NOT_FOUND:
+ self.SetSelection(idx)
+
+
+ def GetSelection(self):
+
+ return self._selection
+
+
+ def GetItem(self, i):
+
+ return self._items[i]
+
+
+ def GetItemCount(self):
+
+ return len(self._items)
+
+
+ def SetRowCount(self, rows):
+
+ self._rowCount = rows
+
+
+ def GetRowCount(self):
+
+ return self._rowCount
+
+
+ def SetColumnCount(self, cols):
+
+ self._columnCount = cols
+
+
+ def GetColumnCount(self):
+
+ return self._columnCount
+
+
+ def SetBackgroundColour(self, colour):
+
+ self._backgroundColour = colour
+
+
+ def GetBackgroundColour(self):
+
+ return self._backgroundColour
+
+
+ def SetTextColour(self, colour):
+
+ self._textColour = colour
+
+
+ def GetTextColour(self):
+
+ return self._textColour
+
+
+ def SetSelectionColour(self, colour):
+
+ self._selectionColour = colour
+
+
+ def GetSelectionColour(self):
+
+ return self._selectionColour
+
+
+ def SetSelectionOutlineColour(self, colour):
+
+ self._selectionOutlineColour = colour
+
+
+ def GetSelectionOutlineColour(self):
+
+ return self._selectionOutlineColour
+
+
+ def SetItemFont(self, font):
+
+ self._itemFont = font
+
+
+ def GetItemFont(self):
+
+ return self._itemFont
+
+
+ def PaintItems(self, dc, win):
+
+ backgroundColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
+ standardTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
+ selectionColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+ selectionOutlineColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
+ standardFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+ groupFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+ groupFont.SetWeight(wx.BOLD)
+
+ if self.GetBackgroundColour().IsOk():
+ backgroundColour = self.GetBackgroundColour()
+
+ if self.GetTextColour().IsOk():
+ standardTextColour = self.GetTextColour()
+
+ if self.GetSelectionColour().IsOk():
+ selectionColour = self.GetSelectionColour()
+
+ if self.GetSelectionOutlineColour().IsOk():
+ selectionOutlineColour = self.GetSelectionOutlineColour()
+
+ if self.GetItemFont().IsOk():
+
+ standardFont = self.GetItemFont()
+ groupFont = wx.Font(standardFont.GetPointSize(), standardFont.GetFamily(), standardFont.GetStyle(),
+ wx.BOLD, standardFont.GetUnderlined(), standardFont.GetFaceName())
+
+ textMarginX = SWITCHER_TEXT_MARGIN_X
+
+ dc.SetLogicalFunction(wx.COPY)
+ dc.SetBrush(wx.Brush(backgroundColour))
+ dc.SetPen(wx.TRANSPARENT_PEN)
+ dc.DrawRectangleRect(win.GetClientRect())
+ dc.SetBackgroundMode(wx.TRANSPARENT)
+
+ for i in xrange(len(self._items)):
+ item = self._items[i]
+ if i == self._selection:
+ dc.SetPen(wx.Pen(selectionOutlineColour))
+ dc.SetBrush(wx.Brush(selectionColour))
+ dc.DrawRectangleRect(item.GetRect())
+
+ clippingRect = wx.Rect(*item.GetRect())
+ clippingRect.Deflate(1, 1)
+
+ dc.SetClippingRect(clippingRect)
+
+ if item.GetTextColour().IsOk():
+ dc.SetTextForeground(item.GetTextColour())
+ else:
+ dc.SetTextForeground(standardTextColour)
+
+ if item.GetFont().IsOk():
+ dc.SetFont(item.GetFont())
+ else:
+ if item.GetIsGroup():
+ dc.SetFont(groupFont)
+ else:
+ dc.SetFont(standardFont)
+
+ w, h = dc.GetTextExtent(item.GetTitle())
+ x = item.GetRect().x
+
+ x += textMarginX
+
+ if not item.GetIsGroup():
+ if item.GetBitmap().IsOk() and item.GetBitmap().GetWidth() <= 16 \
+ and item.GetBitmap().GetHeight() <= 16:
+ x -= textMarginX
+ dc.DrawBitmap(item.GetBitmap(), x, item.GetRect().y + \
+ (item.GetRect().height - item.GetBitmap().GetHeight())/2,
+ True)
+ x += 16 + textMarginX
+ #x += textMarginX
+
+ y = item.GetRect().y + (item.GetRect().height - h)/2
+ dc.DrawText(item.GetTitle(), x, y)
+ dc.DestroyClippingRegion()
+
+
+ def CalculateItemSize(self, dc):
+
+ # Start off allowing for an icon
+ sz = wx.Size(150, 16)
+ standardFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+ groupFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
+ groupFont.SetWeight(wx.BOLD)
+
+ textMarginX = SWITCHER_TEXT_MARGIN_X
+ textMarginY = SWITCHER_TEXT_MARGIN_Y
+ maxWidth = 300
+ maxHeight = 40
+
+ if self.GetItemFont().IsOk():
+ standardFont = self.GetItemFont()
+
+ for item in self._items:
+ if item.GetFont().IsOk():
+ dc.SetFont(item.GetFont())
+ else:
+ if item.GetIsGroup():
+ dc.SetFont(groupFont)
+ else:
+ dc.SetFont(standardFont)
+
+ w, h = dc.GetTextExtent(item.GetTitle())
+ w += 16 + 2*textMarginX
+
+ if w > sz.x:
+ sz.x = min(w, maxWidth)
+ if h > sz.y:
+ sz.y = min(h, maxHeight)
+
+ if sz == wx.Size(16, 16):
+ sz = wx.Size(100, 25)
+ else:
+ sz.x += textMarginX*2
+ sz.y += textMarginY*2
+
+ return sz
+
+
+ def GetIndexForFocus(self):
+
+ for i, item in enumerate(self._items):
+ if item.GetWindow():
+
+ if FindFocusDescendant(item.GetWindow()):
+ return i
+
+ return wx.NOT_FOUND
+
+
+class MultiColumnListCtrl(wx.PyControl):
+ """ A control for displaying several columns (not scrollable). """
+
+ def __init__(self, parent, aui_manager, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
+ style=0, validator=wx.DefaultValidator, name="MultiColumnListCtrl"):
+
+ wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
+
+ self._overallSize = wx.Size(200, 100)
+ self._modifierKey = wx.WXK_CONTROL
+ self._extraNavigationKey = 0
+ self._aui_manager = aui_manager
+
+ self.SetInitialSize(size)
+ self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+ self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+ self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
+ self.Bind(wx.EVT_CHAR, self.OnChar)
+ self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
+ self.Bind(wx.EVT_KEY_UP, self.OnKey)
+
+
+ def __del__(self):
+
+ self._aui_manager.HideHint()
+
+
+ def DoGetBestSize(self):
+
+ return self._overallSize
+
+
+ def OnEraseBackground(self, event):
+
+ pass
+
+
+ def OnPaint(self, event):
+
+ dc = wx.AutoBufferedPaintDC(self)
+ rect = self.GetClientRect()
+
+ if self._items.GetColumnCount() == 0:
+ self.CalculateLayout(dc)
+
+ if self._items.GetColumnCount() == 0:
+ return
+
+ self._items.PaintItems(dc, self)
+
+
+ def OnMouseEvent(self, event):
+
+ if event.LeftDown():
+ self.SetFocus()
+
+
+ def OnChar(self, event):
+
+ event.Skip()
+
+
+ def OnKey(self, event):
+
+ if event.GetEventType() == wx.wxEVT_KEY_UP:
+ if event.GetKeyCode() == self.GetModifierKey():
+ topLevel = wx.GetTopLevelParent(self)
+ closeEvent = wx.CloseEvent(wx.wxEVT_CLOSE_WINDOW, topLevel.GetId())
+ closeEvent.SetEventObject(topLevel)
+ closeEvent.SetCanVeto(False)
+
+ topLevel.GetEventHandler().ProcessEvent(closeEvent)
+ return
+
+ event.Skip()
+ return
+
+ keyCode = event.GetKeyCode()
+
+ if keyCode in [wx.WXK_ESCAPE, wx.WXK_RETURN]:
+ if keyCode == wx.WXK_ESCAPE:
+ self._items.SetSelection(-1)
+
+ topLevel = wx.GetTopLevelParent(self)
+ closeEvent = wx.CloseEvent(wx.wxEVT_CLOSE_WINDOW, topLevel.GetId())
+ closeEvent.SetEventObject(topLevel)
+ closeEvent.SetCanVeto(False)
+
+ topLevel.GetEventHandler().ProcessEvent(closeEvent)
+ return
+
+ elif keyCode in [wx.WXK_TAB, self.GetExtraNavigationKey()]:
+ if event.ShiftDown():
+
+ self._items.SetSelection(self._items.GetSelection() - 1)
+ if self._items.GetSelection() < 0:
+ self._items.SetSelection(self._items.GetItemCount() - 1)
+
+ self.AdvanceToNextSelectableItem(-1)
+
+ else:
+
+ self._items.SetSelection(self._items.GetSelection() + 1)
+ if self._items.GetSelection() >= self._items.GetItemCount():
+ self._items.SetSelection(0)
+
+ self.AdvanceToNextSelectableItem(1)
+
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ elif keyCode in [wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN]:
+ self._items.SetSelection(self._items.GetSelection() + 1)
+ if self._items.GetSelection() >= self._items.GetItemCount():
+ self._items.SetSelection(0)
+
+ self.AdvanceToNextSelectableItem(1)
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ elif keyCode in [wx.WXK_UP, wx.WXK_NUMPAD_UP]:
+ self._items.SetSelection(self._items.GetSelection() - 1)
+ if self._items.GetSelection() < 0:
+ self._items.SetSelection(self._items.GetItemCount() - 1)
+
+ self.AdvanceToNextSelectableItem(-1)
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ elif keyCode in [wx.WXK_HOME, wx.WXK_NUMPAD_HOME]:
+ self._items.SetSelection(0)
+ self.AdvanceToNextSelectableItem(1)
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ elif keyCode in [wx.WXK_END, wx.WXK_NUMPAD_END]:
+ self._items.SetSelection(self._items.GetItemCount() - 1)
+ self.AdvanceToNextSelectableItem(-1)
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ elif keyCode in [wx.WXK_LEFT, wx.WXK_NUMPAD_LEFT]:
+ item = self._items.GetItem(self._items.GetSelection())
+
+ row = item.GetRowPos()
+ newCol = item.GetColPos() - 1
+ if newCol < 0:
+ newCol = self._items.GetColumnCount() - 1
+
+ # Find the first item from the end whose row matches and whose column is equal or lower
+ for i in xrange(self._items.GetItemCount()-1, -1, -1):
+ item2 = self._items.GetItem(i)
+ if item2.GetColPos() == newCol and item2.GetRowPos() <= row:
+ self._items.SetSelection(i)
+ break
+
+ self.AdvanceToNextSelectableItem(-1)
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ elif keyCode in [wx.WXK_RIGHT, wx.WXK_NUMPAD_RIGHT]:
+ item = self._items.GetItem(self._items.GetSelection())
+
+ row = item.GetRowPos()
+ newCol = item.GetColPos() + 1
+ if newCol >= self._items.GetColumnCount():
+ newCol = 0
+
+ # Find the first item from the end whose row matches and whose column is equal or lower
+ for i in xrange(self._items.GetItemCount()-1, -1, -1):
+ item2 = self._items.GetItem(i)
+ if item2.GetColPos() == newCol and item2.GetRowPos() <= row:
+ self._items.SetSelection(i)
+ break
+
+ self.AdvanceToNextSelectableItem(1)
+ self.GenerateSelectionEvent()
+ self.Refresh()
+
+ else:
+ event.Skip()
+
+
+ def AdvanceToNextSelectableItem(self, direction):
+
+ if self._items.GetItemCount() < 2:
+ return
+
+ if self._items.GetSelection() == -1:
+ self._items.SetSelection(0)
+
+ oldSel = self._items.GetSelection()
+
+ while 1:
+
+ if self._items.GetItem(self._items.GetSelection()).GetIsGroup():
+
+ self._items.SetSelection(self._items.GetSelection() + direction)
+ if self._items.GetSelection() == -1:
+ self._items.SetSelection(self._items.GetItemCount()-1)
+ elif self._items.GetSelection() == self._items.GetItemCount():
+ self._items.SetSelection(0)
+ if self._items.GetSelection() == oldSel:
+ break
+
+ else:
+ break
+
+ self.SetTransparency()
+ selection = self._items.GetItem(self._items.GetSelection()).GetWindow()
+ pane = self._aui_manager.GetPane(selection)
+
+ if not pane.IsOk():
+ if isinstance(selection.GetParent(), auibook.AuiNotebook):
+ self.SetTransparency(selection)
+ self._aui_manager.ShowHint(selection.GetScreenRect())
+ wx.CallAfter(self.SetFocus)
+ self.SetFocus()
+ return
+ else:
+ self._aui_manager.HideHint()
+ return
+ if not pane.IsShown():
+ self._aui_manager.HideHint()
+ return
+
+ self.SetTransparency(selection)
+ self._aui_manager.ShowHint(selection.GetScreenRect())
+ # NOTE: this is odd but it is the only way for the focus to
+ # work correctly on wxMac...
+ wx.CallAfter(self.SetFocus)
+ self.SetFocus()
+
+
+ def SetTransparency(self, selection=None):
+
+ if not self.GetParent().CanSetTransparent():
+ return
+
+ if selection is not None:
+ intersects = False
+ if selection.GetScreenRect().Intersects(self.GetParent().GetScreenRect()):
+ intersects = True
+ self.GetParent().SetTransparent(200)
+ return
+
+ self.GetParent().SetTransparent(255)
+
+
+ def GenerateSelectionEvent(self):
+
+ event = wx.CommandEvent(wx.wxEVT_COMMAND_LISTBOX_SELECTED, self.GetId())
+ event.SetEventObject(self)
+ event.SetInt(self._items.GetSelection())
+ self.GetEventHandler().ProcessEvent(event)
+
+
+ def CalculateLayout(self, dc=None):
+
+ if dc is None:
+ dc = wx.ClientDC(self)
+
+ if self._items.GetSelection() == -1:
+ self._items.SetSelection(0)
+
+ columnCount = 1
+
+ # Spacing between edge of window or between columns
+ xMargin = 4
+ yMargin = 4
+
+ # Inter-row spacing
+ rowSpacing = 2
+
+ itemSize = self._items.CalculateItemSize(dc)
+ self._overallSize = wx.Size(350, 200)
+
+ currentRow = 0
+ x = xMargin
+ y = yMargin
+
+ breaking = False
+ i = 0
+
+ while 1:
+
+ oldOverallSize = self._overallSize
+ item = self._items.GetItem(i)
+
+ item.SetRect(wx.Rect(x, y, itemSize.x, itemSize.y))
+ item.SetColPos(columnCount-1)
+ item.SetRowPos(currentRow)
+
+ if item.GetRect().GetBottom() > self._overallSize.y:
+ self._overallSize.y = item.GetRect().GetBottom() + yMargin
+
+ if item.GetRect().GetRight() > self._overallSize.x:
+ self._overallSize.x = item.GetRect().GetRight() + xMargin
+
+ currentRow += 1
+
+ y += rowSpacing + itemSize.y
+ stopBreaking = breaking
+
+ if currentRow > self._items.GetRowCount() or (item.GetBreakColumn() and not breaking and currentRow != 1):
+ currentRow = 0
+ columnCount += 1
+ x += xMargin + itemSize.x
+ y = yMargin
+
+ # Make sure we don't orphan a group
+ if item.GetIsGroup() or (item.GetBreakColumn() and not breaking):
+ self._overallSize = oldOverallSize
+
+ if item.GetBreakColumn():
+ breaking = True
+
+ # Repeat the last item, in the next column
+ i -= 1
+
+ if stopBreaking:
+ breaking = False
+
+ i += 1
+
+ if i >= self._items.GetItemCount():
+ break
+
+ self._items.SetColumnCount(columnCount)
+ self.InvalidateBestSize()
+
+
+ def SetItems(self, items):
+
+ self._items = items
+
+
+ def GetItems(self):
+
+ return self._items
+
+
+ def SetExtraNavigationKey(self, keyCode):
+ """
+ Set an extra key that can be used to cycle through items,
+ in case not using the ``Ctrl`` + ``Tab`` combination.
+ """
+
+ self._extraNavigationKey = keyCode
+
+
+ def GetExtraNavigationKey(self):
+
+ return self._extraNavigationKey
+
+
+ def SetModifierKey(self, modifierKey):
+ """
+ Set the modifier used to invoke the dialog, and therefore to test for
+ release.
+ """
+
+ self._modifierKey = modifierKey
+
+
+ def GetModifierKey(self):
+
+ return self._modifierKey
+
+
+
+class SwitcherDialog(wx.Dialog):
+ """
+ SwitcherDialog shows a L{MultiColumnListCtrl} with a list of panes
+ and tabs for the user to choose. ``Ctrl`` + ``Tab`` cycles through them.
+ """
+
+ def __init__(self, items, parent, aui_manager, id=wx.ID_ANY, title=_("Pane Switcher"), pos=wx.DefaultPosition,
+ size=wx.DefaultSize, style=wx.STAY_ON_TOP|wx.DIALOG_NO_PARENT|wx.BORDER_SIMPLE):
+ """ Default class constructor. """
+
+ self._switcherBorderStyle = (style & wx.BORDER_MASK)
+ if self._switcherBorderStyle == wx.BORDER_NONE:
+ self._switcherBorderStyle = wx.BORDER_SIMPLE
+
+ style &= wx.BORDER_MASK
+ style |= wx.BORDER_NONE
+
+ wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+
+ self._listCtrl = MultiColumnListCtrl(self, aui_manager,
+ style=wx.WANTS_CHARS|wx.NO_BORDER)
+ self._listCtrl.SetItems(items)
+ self._listCtrl.CalculateLayout()
+
+ self._descriptionCtrl = wx.html.HtmlWindow(self, size=(-1, 100), style=wx.BORDER_NONE)
+ self._descriptionCtrl.SetBackgroundColour(self.GetBackgroundColour())
+
+ if wx.Platform == "__WXGTK__":
+ fontSize = 11
+ self._descriptionCtrl.SetStandardFonts(fontSize)
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ self.SetSizer(sizer)
+ sizer.Add(self._listCtrl, 1, wx.ALL|wx.EXPAND, 10)
+ sizer.Add(self._descriptionCtrl, 0, wx.ALL|wx.EXPAND, 10)
+ sizer.SetSizeHints(self)
+
+ self._listCtrl.SetFocus()
+
+ self.Centre(wx.BOTH)
+
+ if self._listCtrl.GetItems().GetSelection() == -1:
+ self._listCtrl.GetItems().SetSelection(0)
+
+ self._listCtrl.AdvanceToNextSelectableItem(1)
+
+ self.ShowDescription(self._listCtrl.GetItems().GetSelection())
+
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+ self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
+ self.Bind(wx.EVT_LISTBOX, self.OnSelectItem)
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+ # Attributes
+ self._closing = False
+ if wx.Platform == "__WXMSW__":
+ self._borderColour = wx.Colour(49, 106, 197)
+ else:
+ self._borderColour = wx.BLACK
+
+ self._aui_manager = aui_manager
+
+
+ def OnCloseWindow(self, event):
+
+ if self._closing:
+ return
+
+ if self.IsModal():
+ self._closing = True
+
+ if self.GetSelection() == -1:
+ self.EndModal(wx.ID_CANCEL)
+ else:
+ self.EndModal(wx.ID_OK)
+
+ self._aui_manager.HideHint()
+
+
+ def GetSelection(self):
+
+ return self._listCtrl.GetItems().GetSelection()
+
+
+ def OnActivate(self, event):
+
+ if not event.GetActive():
+ if not self._closing:
+ self._closing = True
+ self.EndModal(wx.ID_CANCEL)
+
+
+ def OnPaint(self, event):
+
+ dc = wx.PaintDC(self)
+
+ if self._switcherBorderStyle == wx.BORDER_SIMPLE:
+
+ dc.SetPen(wx.Pen(self._borderColour))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+
+ rect = self.GetClientRect()
+ dc.DrawRectangleRect(rect)
+
+ # Draw border around the HTML control
+ rect = wx.Rect(*self._descriptionCtrl.GetRect())
+ rect.Inflate(1, 1)
+ dc.DrawRectangleRect(rect)
+
+
+ def OnSelectItem(self, event):
+
+ self.ShowDescription(event.GetSelection())
+
+
+# Convert a colour to a 6-digit hex string
+ def ColourToHexString(self, col):
+
+ hx = '%02x%02x%02x' % tuple([int(c) for c in col])
+ return hx
+
+
+ def ShowDescription(self, i):
+
+ item = self._listCtrl.GetItems().GetItem(i)
+ colour = self._listCtrl.GetItems().GetBackgroundColour()
+
+ if not colour.IsOk():
+ colour = self.GetBackgroundColour()
+
+ backgroundColourHex = self.ColourToHexString(colour)
+ html = _("<body bgcolor=\"#") + backgroundColourHex + _("\"><b>") + item.GetTitle() + _("</b>")
+
+ if item.GetDescription():
+ html += _("<p>")
+ html += item.GetDescription()
+
+ html += _("</body>")
+ self._descriptionCtrl.SetPage(html)
+
+
+ def SetExtraNavigationKey(self, keyCode):
+
+ self._extraNavigationKey = keyCode
+ if self._listCtrl:
+ self._listCtrl.SetExtraNavigationKey(keyCode)
+
+
+ def GetExtraNavigationKey(self):
+
+ return self._extraNavigationKey
+
+
+ def SetModifierKey(self, modifierKey):
+
+ self._modifierKey = modifierKey
+ if self._listCtrl:
+ self._listCtrl.SetModifierKey(modifierKey)
+
+
+ def GetModifierKey(self):
+
+ return self._modifierKey
+
+
+ def SetBorderColour(self, colour):
+
+ self._borderColour = colour
+
+
\ No newline at end of file