menu.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. """!
  2. @package gui_core.menu
  3. @brief Menu classes for wxGUI
  4. Classes:
  5. - menu::Menu
  6. - menu::SearchModuleWindow
  7. - menu::MenuTree
  8. (C) 2010-2012 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Martin Landa <landa.martin gmail.com>
  12. @author Pawel Netzel (menu customization)
  13. @author Milena Nowotarska (menu customization)
  14. @author Robert Szczepanek (menu customization)
  15. @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
  16. """
  17. import wx
  18. from core import globalvar
  19. from core import utils
  20. from core.modulesdata import ModulesData
  21. from core.gcmd import EncodeString
  22. from core.settings import UserSettings
  23. from core.events import EVT_SHOW_NOTIFICATION
  24. from gui_core.widgets import ItemTree, SearchModuleWidget
  25. from lmgr.menudata import LayerManagerMenuData
  26. class Menu(wx.MenuBar):
  27. def __init__(self, parent, data):
  28. """!Creates menubar"""
  29. wx.MenuBar.__init__(self)
  30. self.parent = parent
  31. self.menudata = data
  32. self.menucmd = dict()
  33. self.menustyle = UserSettings.Get(group = 'appearance', key = 'menustyle', subkey = 'selection')
  34. for eachMenuData in self.menudata.GetMenu():
  35. for eachHeading in eachMenuData:
  36. menuLabel = eachHeading[0]
  37. menuItems = eachHeading[1]
  38. self.Append(self._createMenu(menuItems), menuLabel)
  39. def _createMenu(self, menuData):
  40. """!Creates menu"""
  41. menu = wx.Menu()
  42. for eachItem in menuData:
  43. if len(eachItem) == 2:
  44. label = eachItem[0]
  45. subMenu = self._createMenu(eachItem[1])
  46. menu.AppendMenu(wx.ID_ANY, label, subMenu)
  47. else:
  48. self._createMenuItem(menu, self.menustyle, *eachItem)
  49. self.parent.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
  50. return menu
  51. def _createMenuItem(self, menu, menustyle, label, help, handler, gcmd, keywords,
  52. shortcut = '', wxId = wx.ID_ANY, kind = wx.ITEM_NORMAL):
  53. """!Creates menu items
  54. There are three menu styles (menu item text styles).
  55. 1 -- label only, 2 -- label and cmd name, 3 -- cmd name only
  56. """
  57. if not label:
  58. menu.AppendSeparator()
  59. return
  60. if gcmd:
  61. helpString = gcmd + ' -- ' + help
  62. if menustyle == 1:
  63. label += ' [' + gcmd + ']'
  64. elif menustyle == 2:
  65. label = ' [' + gcmd + ']'
  66. else:
  67. helpString = help
  68. if shortcut:
  69. label += '\t' + shortcut
  70. menuItem = menu.Append(wxId, label, helpString, kind)
  71. self.menucmd[menuItem.GetId()] = gcmd
  72. if gcmd:
  73. try:
  74. cmd = utils.split(str(gcmd))
  75. except UnicodeError:
  76. cmd = utils.split(EncodeString((gcmd)))
  77. if cmd and cmd[0] not in globalvar.grassCmd:
  78. menuItem.Enable(False)
  79. rhandler = eval('self.parent.' + handler)
  80. self.parent.Bind(wx.EVT_MENU, rhandler, menuItem)
  81. def GetData(self):
  82. """!Get menu data"""
  83. return self.menudata
  84. def GetCmd(self):
  85. """!Get list of commands
  86. @return list of commands
  87. """
  88. return self.menucmd
  89. def OnMenuHighlight(self, event):
  90. """
  91. Default menu help handler
  92. """
  93. # Show how to get menu item info from this event handler
  94. id = event.GetMenuId()
  95. item = self.FindItemById(id)
  96. if item:
  97. text = item.GetText()
  98. help = item.GetHelp()
  99. # but in this case just call Skip so the default is done
  100. event.Skip()
  101. class SearchModuleWindow(wx.Panel):
  102. """!Show menu tree"""
  103. def __init__(self, parent, id = wx.ID_ANY, **kwargs):
  104. self.parent = parent # LayerManager
  105. wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
  106. self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  107. label = " %s " % _("Menu tree (double-click or Ctrl-Enter to run command)"))
  108. # tree
  109. menuData = LayerManagerMenuData()
  110. self.tree = MenuTree(parent = self, data = menuData)
  111. self.tree.Load()
  112. # search widget
  113. self.search = SearchModuleWidget(parent = self,
  114. modulesData = ModulesData(menuData.GetModules()),
  115. showChoice = False)
  116. # buttons
  117. self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
  118. self.btnRun.SetToolTipString(_("Run selected command from the menu tree"))
  119. self.btnRun.Enable(False)
  120. # bindings
  121. self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
  122. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  123. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  124. self.search.GetCtrl().Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  125. self.search.GetCtrl().Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  126. # stop propagation of event
  127. # because number of matched items differs
  128. # from number of matched items in tree
  129. # TODO: find the reason for this difference
  130. # TODO: use this event for updating statusbar (i.e., don't do this bind)
  131. self.Bind(EVT_SHOW_NOTIFICATION, lambda event: None)
  132. self._layout()
  133. self.search.SetFocus()
  134. def _layout(self):
  135. """!Do dialog layout"""
  136. sizer = wx.BoxSizer(wx.VERTICAL)
  137. # body
  138. dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
  139. dataSizer.Add(item = self.tree, proportion =1,
  140. flag = wx.EXPAND)
  141. # buttons
  142. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  143. btnSizer.Add(item = self.btnRun, proportion = 0)
  144. sizer.Add(item = dataSizer, proportion = 1,
  145. flag = wx.EXPAND | wx.ALL, border = 5)
  146. sizer.Add(item = self.search, proportion = 0,
  147. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  148. sizer.Add(item = btnSizer, proportion = 0,
  149. flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
  150. sizer.Fit(self)
  151. sizer.SetSizeHints(self)
  152. self.SetSizer(sizer)
  153. self.Fit()
  154. self.SetAutoLayout(True)
  155. self.Layout()
  156. def OnCloseWindow(self, event):
  157. """!Close window"""
  158. self.Destroy()
  159. def OnRun(self, event):
  160. """!Run selected command"""
  161. if not self.tree.GetSelected():
  162. return # should not happen
  163. data = self.tree.GetPyData(self.tree.GetSelected())
  164. if not data:
  165. return
  166. handler = 'self.parent.' + data['handler'].lstrip('self.')
  167. if data['handler'] == 'self.OnXTerm':
  168. wx.MessageBox(parent = self,
  169. message = _('You must run this command from the menu or command line',
  170. 'This command require an XTerm'),
  171. caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
  172. elif data['command']:
  173. eval(handler)(event = None, cmd = data['command'].split())
  174. else:
  175. eval(handler)(None)
  176. def OnKeyUp(self, event):
  177. if event.GetKeyCode() == wx.WXK_RETURN:
  178. if event.ControlDown():
  179. self.OnRun(event)
  180. else:
  181. self.OnShowItem(event)
  182. def OnShowItem(self, event):
  183. """!Show selected item"""
  184. self.tree.OnShowItem(event)
  185. if self.tree.GetSelected():
  186. self.btnRun.Enable()
  187. else:
  188. self.btnRun.Enable(False)
  189. def OnItemActivated(self, event):
  190. """!Item activated (double-click)"""
  191. item = event.GetItem()
  192. if not item or not item.IsOk():
  193. return
  194. data = self.tree.GetPyData(item)
  195. if not data or 'command' not in data:
  196. return
  197. self.tree.itemSelected = item
  198. self.OnRun(None)
  199. def OnItemSelected(self, event):
  200. """!Item selected"""
  201. item = event.GetItem()
  202. if not item or not item.IsOk():
  203. return
  204. data = self.tree.GetPyData(item)
  205. if not data or 'command' not in data:
  206. return
  207. if data['command']:
  208. label = data['command'] + ' -- ' + data['description']
  209. else:
  210. label = data['description']
  211. self.parent.SetStatusText(label, 0)
  212. def OnUpdateStatusBar(self, event):
  213. """!Update statusbar text"""
  214. element = self.search.GetSelection()
  215. value = event.GetEventObject().GetValue()
  216. self.tree.SearchItems(element = element, value = value)
  217. nItems = len(self.tree.itemsMarked)
  218. if value:
  219. self.parent.SetStatusText(_("%d modules match") % nItems, 0)
  220. else:
  221. self.parent.SetStatusText("", 0)
  222. event.Skip()
  223. class MenuTree(ItemTree):
  224. """!Menu tree class"""
  225. def __init__(self, parent, data, **kwargs):
  226. self.parent = parent
  227. self.menudata = data
  228. super(MenuTree, self).__init__(parent, **kwargs)
  229. self.menustyle = UserSettings.Get(group = 'appearance', key = 'menustyle', subkey = 'selection')
  230. def Load(self, data = None):
  231. """!Load menu data tree
  232. @param data menu data (None to use self.menudata)
  233. """
  234. if not data:
  235. data = self.menudata
  236. self.itemsMarked = [] # list of marked items
  237. for eachMenuData in data.GetMenu():
  238. for label, items in eachMenuData:
  239. item = self.AppendItem(parentId = self.root,
  240. text = label.replace('&', ''))
  241. self.__AppendItems(item, items)
  242. def __AppendItems(self, item, data):
  243. """!Append items into tree (used by Load()
  244. @param item tree item (parent)
  245. @parent data menu data"""
  246. for eachItem in data:
  247. if len(eachItem) == 2:
  248. if eachItem[0]:
  249. itemSub = self.AppendItem(parentId = item,
  250. text = eachItem[0])
  251. self.__AppendItems(itemSub, eachItem[1])
  252. else:
  253. if eachItem[0]:
  254. label = eachItem[0]
  255. if eachItem[3]:
  256. if self.menustyle == 1:
  257. label += ' [' + eachItem[3] + ']'
  258. elif self.menustyle == 2:
  259. label = '[' + eachItem[3] + ']'
  260. itemNew = self.AppendItem(parentId = item,
  261. text = label)
  262. data = { 'item' : eachItem[0],
  263. 'description' : eachItem[1],
  264. 'handler' : eachItem[2],
  265. 'command' : eachItem[3],
  266. 'keywords' : eachItem[4] }
  267. self.SetPyData(itemNew, data)