""" 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 = _("") + item.GetTitle() + _("") if item.GetDescription(): html += _("

") html += item.GetDescription() html += _("") 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