menu.py 11 KB

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