menu.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 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"))
  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.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  121. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  122. self._layout()
  123. self.search.SetFocus()
  124. def _layout(self):
  125. """!Do dialog layout"""
  126. sizer = wx.BoxSizer(wx.VERTICAL)
  127. # body
  128. dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
  129. dataSizer.Add(item = self.tree, proportion =1,
  130. flag = wx.EXPAND)
  131. # buttons
  132. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  133. btnSizer.Add(item = self.btnRun, proportion = 0)
  134. sizer.Add(item = dataSizer, proportion = 1,
  135. flag = wx.EXPAND | wx.ALL, border = 5)
  136. sizer.Add(item = self.search, proportion = 0,
  137. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  138. sizer.Add(item = btnSizer, proportion = 0,
  139. flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
  140. sizer.Fit(self)
  141. sizer.SetSizeHints(self)
  142. self.SetSizer(sizer)
  143. self.Fit()
  144. self.SetAutoLayout(True)
  145. self.Layout()
  146. def OnCloseWindow(self, event):
  147. """!Close window"""
  148. self.Destroy()
  149. def OnRun(self, event):
  150. """!Run selected command"""
  151. if not self.tree.GetSelected():
  152. return # should not happen
  153. data = self.tree.GetPyData(self.tree.GetSelected())
  154. if not data:
  155. return
  156. handler = 'self.parent.' + data['handler'].lstrip('self.')
  157. if data['handler'] == 'self.OnXTerm':
  158. wx.MessageBox(parent = self,
  159. message = _('You must run this command from the menu or command line',
  160. 'This command require an XTerm'),
  161. caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
  162. elif data['command']:
  163. eval(handler)(event = None, cmd = data['command'].split())
  164. else:
  165. eval(handler)(None)
  166. def OnShowItem(self, event):
  167. """!Show selected item"""
  168. self.tree.OnShowItem(event)
  169. if self.tree.GetSelected():
  170. self.btnRun.Enable()
  171. else:
  172. self.btnRun.Enable(False)
  173. def OnItemActivated(self, event):
  174. """!Item activated (double-click)"""
  175. item = event.GetItem()
  176. if not item or not item.IsOk():
  177. return
  178. data = self.tree.GetPyData(item)
  179. if not data or 'command' not in data:
  180. return
  181. self.tree.itemSelected = item
  182. self.OnRun(None)
  183. def OnItemSelected(self, event):
  184. """!Item selected"""
  185. item = event.GetItem()
  186. if not item or not item.IsOk():
  187. return
  188. data = self.tree.GetPyData(item)
  189. if not data or 'command' not in data:
  190. return
  191. if data['command']:
  192. label = data['command'] + ' -- ' + data['description']
  193. else:
  194. label = data['description']
  195. self.parent.SetStatusText(label, 0)
  196. def OnUpdateStatusBar(self, event):
  197. """!Update statusbar text"""
  198. element = self.search.GetSelection()
  199. self.tree.SearchItems(element = element,
  200. value = event.GetString())
  201. nItems = len(self.tree.itemsMarked)
  202. if event.GetString():
  203. self.parent.SetStatusText(_("%d modules match") % nItems, 0)
  204. else:
  205. self.parent.SetStatusText("", 0)
  206. event.Skip()
  207. class MenuTree(ItemTree):
  208. """!Menu tree class"""
  209. def __init__(self, parent, data, **kwargs):
  210. self.parent = parent
  211. self.menudata = data
  212. super(MenuTree, self).__init__(parent, **kwargs)
  213. self.menustyle = UserSettings.Get(group = 'appearance', key = 'menustyle', subkey = 'selection')
  214. def Load(self, data = None):
  215. """!Load menu data tree
  216. @param data menu data (None to use self.menudata)
  217. """
  218. if not data:
  219. data = self.menudata
  220. self.itemsMarked = [] # list of marked items
  221. for eachMenuData in data.GetMenu():
  222. for label, items in eachMenuData:
  223. item = self.AppendItem(parentId = self.root,
  224. text = label.replace('&', ''))
  225. self.__AppendItems(item, items)
  226. def __AppendItems(self, item, data):
  227. """!Append items into tree (used by Load()
  228. @param item tree item (parent)
  229. @parent data menu data"""
  230. for eachItem in data:
  231. if len(eachItem) == 2:
  232. if eachItem[0]:
  233. itemSub = self.AppendItem(parentId = item,
  234. text = eachItem[0])
  235. self.__AppendItems(itemSub, eachItem[1])
  236. else:
  237. if eachItem[0]:
  238. label = eachItem[0]
  239. if eachItem[3]:
  240. if self.menustyle == 1:
  241. label += ' [' + eachItem[3] + ']'
  242. elif self.menustyle == 2:
  243. label = '[' + eachItem[3] + ']'
  244. itemNew = self.AppendItem(parentId = item,
  245. text = label)
  246. data = { 'item' : eachItem[0],
  247. 'description' : eachItem[1],
  248. 'handler' : eachItem[2],
  249. 'command' : eachItem[3],
  250. 'keywords' : eachItem[4] }
  251. self.SetPyData(itemNew, data)