"""! @package gui_core.menu @brief Menu classes for wxGUI Classes: - menu::Menu - menu::SearchModuleWindow - menu::MenuTree (C) 2010-2012 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Martin Landa @author Pawel Netzel (menu customization) @author Milena Nowotarska (menu customization) @author Robert Szczepanek (menu customization) @author Vaclav Petras (menu customization) """ import wx from core import globalvar from core import utils from core.modulesdata import ModulesData from core.gcmd import EncodeString from core.settings import UserSettings from core.events import EVT_SHOW_NOTIFICATION from gui_core.widgets import ItemTree, SearchModuleWidget from lmgr.menudata import LayerManagerMenuData class Menu(wx.MenuBar): def __init__(self, parent, data): """!Creates menubar""" wx.MenuBar.__init__(self) self.parent = parent self.menudata = data self.menucmd = dict() self.menustyle = UserSettings.Get(group = 'appearance', key = 'menustyle', subkey = 'selection') for eachMenuData in self.menudata.GetMenu(): for eachHeading in eachMenuData: menuLabel = eachHeading[0] menuItems = eachHeading[1] self.Append(self._createMenu(menuItems), menuLabel) def _createMenu(self, menuData): """!Creates menu""" menu = wx.Menu() for eachItem in menuData: if len(eachItem) == 2: label = eachItem[0] subMenu = self._createMenu(eachItem[1]) menu.AppendMenu(wx.ID_ANY, label, subMenu) else: self._createMenuItem(menu, self.menustyle, *eachItem) self.parent.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight) return menu def _createMenuItem(self, menu, menustyle, label, help, handler, gcmd, keywords, shortcut = '', wxId = wx.ID_ANY, kind = wx.ITEM_NORMAL): """!Creates menu items There are three menu styles (menu item text styles). 1 -- label only, 2 -- label and cmd name, 3 -- cmd name only """ if not label: menu.AppendSeparator() return if gcmd: helpString = gcmd + ' -- ' + help if menustyle == 1: label += ' [' + gcmd + ']' elif menustyle == 2: label = ' [' + gcmd + ']' else: helpString = help if shortcut: label += '\t' + shortcut menuItem = menu.Append(wxId, label, helpString, kind) self.menucmd[menuItem.GetId()] = gcmd if gcmd: try: cmd = utils.split(str(gcmd)) except UnicodeError: cmd = utils.split(EncodeString((gcmd))) if cmd and cmd[0] not in globalvar.grassCmd: menuItem.Enable(False) rhandler = eval('self.parent.' + handler) self.parent.Bind(wx.EVT_MENU, rhandler, menuItem) def GetData(self): """!Get menu data""" return self.menudata def GetCmd(self): """!Get list of commands @return list of commands """ return self.menucmd def OnMenuHighlight(self, event): """ Default menu help handler """ # Show how to get menu item info from this event handler id = event.GetMenuId() item = self.FindItemById(id) if item: text = item.GetText() help = item.GetHelp() # but in this case just call Skip so the default is done event.Skip() class SearchModuleWindow(wx.Panel): """!Show menu tree""" def __init__(self, parent, id = wx.ID_ANY, **kwargs): self.parent = parent # LayerManager wx.Panel.__init__(self, parent = parent, id = id, **kwargs) self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY, label = " %s " % _("Menu tree (double-click or Ctrl-Enter to run command)")) # tree menuData = LayerManagerMenuData() self.tree = MenuTree(parent = self, data = menuData) self.tree.Load() # search widget self.search = SearchModuleWidget(parent = self, modulesData = ModulesData(menuData.GetModules()), showChoice = False) # buttons self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run")) self.btnRun.SetToolTipString(_("Run selected command from the menu tree")) self.btnRun.Enable(False) # bindings self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun) self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected) self.search.GetCtrl().Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) self.search.GetCtrl().Bind(wx.EVT_KEY_UP, self.OnKeyUp) # stop propagation of event # because number of matched items differs # from number of matched items in tree # TODO: find the reason for this difference # TODO: use this event for updating statusbar (i.e., don't do this bind) self.Bind(EVT_SHOW_NOTIFICATION, lambda event: None) self._layout() self.search.SetFocus() def _layout(self): """!Do dialog layout""" sizer = wx.BoxSizer(wx.VERTICAL) # body dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL) dataSizer.Add(item = self.tree, proportion =1, flag = wx.EXPAND) # buttons btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(item = self.btnRun, proportion = 0) sizer.Add(item = dataSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5) sizer.Add(item = self.search, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) sizer.Add(item = btnSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5) sizer.Fit(self) sizer.SetSizeHints(self) self.SetSizer(sizer) self.Fit() self.SetAutoLayout(True) self.Layout() def OnCloseWindow(self, event): """!Close window""" self.Destroy() def OnRun(self, event): """!Run selected command""" if not self.tree.GetSelected(): return # should not happen data = self.tree.GetPyData(self.tree.GetSelected()) if not data: return handler = 'self.parent.' + data['handler'].lstrip('self.') if data['handler'] == 'self.OnXTerm': wx.MessageBox(parent = self, message = _('You must run this command from the menu or command line', 'This command require an XTerm'), caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE) elif data['command']: eval(handler)(event = None, cmd = data['command'].split()) else: eval(handler)(None) def OnKeyUp(self, event): if event.GetKeyCode() == wx.WXK_RETURN: if event.ControlDown(): self.OnRun(event) else: self.OnShowItem(event) def OnShowItem(self, event): """!Show selected item""" self.tree.OnShowItem(event) if self.tree.GetSelected(): self.btnRun.Enable() else: self.btnRun.Enable(False) def OnItemActivated(self, event): """!Item activated (double-click)""" item = event.GetItem() if not item or not item.IsOk(): return data = self.tree.GetPyData(item) if not data or 'command' not in data: return self.tree.itemSelected = item self.OnRun(None) def OnItemSelected(self, event): """!Item selected""" item = event.GetItem() if not item or not item.IsOk(): return data = self.tree.GetPyData(item) if not data or 'command' not in data: return if data['command']: label = data['command'] + ' -- ' + data['description'] else: label = data['description'] self.parent.SetStatusText(label, 0) def OnUpdateStatusBar(self, event): """!Update statusbar text""" element = self.search.GetSelection() value = event.GetEventObject().GetValue() self.tree.SearchItems(element = element, value = value) nItems = len(self.tree.itemsMarked) if value: self.parent.SetStatusText(_("%d modules match") % nItems, 0) else: self.parent.SetStatusText("", 0) event.Skip() class MenuTree(ItemTree): """!Menu tree class""" def __init__(self, parent, data, **kwargs): self.parent = parent self.menudata = data super(MenuTree, self).__init__(parent, **kwargs) self.menustyle = UserSettings.Get(group = 'appearance', key = 'menustyle', subkey = 'selection') def Load(self, data = None): """!Load menu data tree @param data menu data (None to use self.menudata) """ if not data: data = self.menudata self.itemsMarked = [] # list of marked items for eachMenuData in data.GetMenu(): for label, items in eachMenuData: item = self.AppendItem(parentId = self.root, text = label.replace('&', '')) self.__AppendItems(item, items) def __AppendItems(self, item, data): """!Append items into tree (used by Load() @param item tree item (parent) @parent data menu data""" for eachItem in data: if len(eachItem) == 2: if eachItem[0]: itemSub = self.AppendItem(parentId = item, text = eachItem[0]) self.__AppendItems(itemSub, eachItem[1]) else: if eachItem[0]: label = eachItem[0] if eachItem[3]: if self.menustyle == 1: label += ' [' + eachItem[3] + ']' elif self.menustyle == 2: label = '[' + eachItem[3] + ']' itemNew = self.AppendItem(parentId = item, text = label) data = { 'item' : eachItem[0], 'description' : eachItem[1], 'handler' : eachItem[2], 'command' : eachItem[3], 'keywords' : eachItem[4] } self.SetPyData(itemNew, data)