ghelp.py 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. """!
  2. @package help.py
  3. @brief Help window
  4. Classes:
  5. - HelpWindow
  6. - SearchModuleWindow
  7. - ItemTree
  8. - MenuTreeWindow
  9. - MenuTree
  10. - AboutWindow
  11. - InstallExtensionWindow
  12. - ExtensionTree
  13. (C) 2008-2010 by the GRASS Development Team
  14. This program is free software under the GNU General Public License
  15. (>=v2). Read the file COPYING that comes with GRASS for details.
  16. @author Martin Landa <landa.martin gmail.com>
  17. """
  18. import os
  19. import wx
  20. try:
  21. import wx.lib.agw.customtreectrl as CT
  22. # import wx.lib.agw.hyperlink as hl
  23. except ImportError:
  24. import wx.lib.customtreectrl as CT
  25. # import wx.lib.hyperlink as hl
  26. import wx.lib.flatnotebook as FN
  27. import wx.lib.scrolledpanel as scrolled
  28. import menudata
  29. import gcmd
  30. import globalvar
  31. import menuform
  32. class HelpWindow(wx.Frame):
  33. """!GRASS Quickstart help window"""
  34. def __init__(self, parent, id, title, size, file):
  35. wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
  36. sizer = wx.BoxSizer(wx.VERTICAL)
  37. # text
  38. helpFrame = wx.html.HtmlWindow(parent=self, id=wx.ID_ANY)
  39. helpFrame.SetStandardFonts (size = 10)
  40. helpFrame.SetBorders(10)
  41. wx.InitAllImageHandlers()
  42. helpFrame.LoadFile(file)
  43. self.Ok = True
  44. sizer.Add(item=helpFrame, proportion=1, flag=wx.EXPAND)
  45. self.SetAutoLayout(True)
  46. self.SetSizer(sizer)
  47. # sizer.Fit(self)
  48. # sizer.SetSizeHints(self)
  49. self.Layout()
  50. class SearchModuleWindow(wx.Panel):
  51. """!Search module window (used in MenuTreeWindow)"""
  52. def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None,
  53. showChoice = True, showTip = False, **kwargs):
  54. self.showTip = showTip
  55. self.showChoice = showChoice
  56. self.cmdPrompt = cmdPrompt
  57. wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
  58. self._searchDict = { _('description') : 'description',
  59. _('command') : 'command',
  60. _('keywords') : 'keywords' }
  61. self.box = wx.StaticBox(parent = self, id = wx.ID_ANY,
  62. label=" %s " % _("Find module(s)"))
  63. self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
  64. choices = [_('description'),
  65. _('keywords'),
  66. _('command')])
  67. self.searchBy.SetSelection(0)
  68. self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
  69. value = "", size = (-1, 25),
  70. style = wx.TE_PROCESS_ENTER)
  71. self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
  72. if self.showTip:
  73. self.searchTip = menuform.StaticWrapText(parent = self, id = wx.ID_ANY,
  74. size = (-1, 35))
  75. if self.showChoice:
  76. self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY)
  77. if self.cmdPrompt:
  78. self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
  79. self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)
  80. self._layout()
  81. def _layout(self):
  82. """!Do layout"""
  83. sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
  84. gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
  85. gridSizer.AddGrowableCol(1)
  86. gridSizer.Add(item = self.searchBy,
  87. flag=wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
  88. gridSizer.Add(item = self.search,
  89. flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1))
  90. row = 1
  91. if self.showTip:
  92. gridSizer.Add(item = self.searchTip,
  93. flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
  94. row += 1
  95. if self.showChoice:
  96. gridSizer.Add(item = self.searchChoice,
  97. flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
  98. sizer.Add(item = gridSizer, proportion = 1)
  99. self.SetSizer(sizer)
  100. sizer.Fit(self)
  101. def GetSelection(self):
  102. """!Get selected element"""
  103. selection = self.searchBy.GetStringSelection()
  104. return self._searchDict[selection]
  105. def SetSelection(self, i):
  106. """!Set selection element"""
  107. self.searchBy.SetSelection(i)
  108. def OnSearchModule(self, event):
  109. """!Search module by keywords or description"""
  110. if not self.cmdPrompt:
  111. event.Skip()
  112. return
  113. text = event.GetString()
  114. if not text:
  115. self.cmdPrompt.SetFilter(None)
  116. mList = self.cmdPrompt.GetCommandItems()
  117. self.searchChoice.SetItems(mList)
  118. if self.showTip:
  119. self.searchTip.SetLabel(_("%d modules found") % len(mList))
  120. event.Skip()
  121. return
  122. modules = dict()
  123. iFound = 0
  124. for module, data in self.cmdPrompt.moduleDesc.iteritems():
  125. found = False
  126. sel = self.searchBy.GetSelection()
  127. if sel == 0: # -> description
  128. if text in data['desc']:
  129. found = True
  130. elif sel == 1: # keywords
  131. if self.cmdPrompt.CheckKey(text, data['keywords']):
  132. found = True
  133. else: # command
  134. if text in module:
  135. found = True
  136. if found:
  137. iFound += 1
  138. try:
  139. group, name = module.split('.')
  140. except ValueError:
  141. continue # TODO
  142. if not modules.has_key(group):
  143. modules[group] = list()
  144. modules[group].append(name)
  145. self.cmdPrompt.SetFilter(modules)
  146. self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
  147. if self.showTip:
  148. self.searchTip.SetLabel(_("%d modules found") % iFound)
  149. event.Skip()
  150. def OnSelectModule(self, event):
  151. """!Module selected from choice, update command prompt"""
  152. cmd = event.GetString().split(' ', 1)[0]
  153. text = cmd + ' '
  154. pos = len(text)
  155. if self.cmdPrompt:
  156. self.cmdPrompt.SetText(text)
  157. self.cmdPrompt.SetSelectionStart(pos)
  158. self.cmdPrompt.SetCurrentPos(pos)
  159. self.cmdPrompt.SetFocus()
  160. desc = self.cmdPrompt.GetCommandDesc(cmd)
  161. if self.showTip:
  162. self.searchTip.SetLabel(desc)
  163. class MenuTreeWindow(wx.Panel):
  164. """!Show menu tree"""
  165. def __init__(self, parent, id = wx.ID_ANY, **kwargs):
  166. self.parent = parent # LayerManager
  167. wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
  168. self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  169. label=" %s " % _("Menu tree (double-click to run command)"))
  170. # tree
  171. self.tree = MenuTree(parent = self, data = menudata.ManagerData())
  172. self.tree.Load()
  173. # search widget
  174. self.search = SearchModuleWindow(parent = self, showChoice = False)
  175. # buttons
  176. self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("Run"))
  177. self.btnRun.SetToolTipString(_("Run selected command"))
  178. self.btnRun.Enable(False)
  179. # bindings
  180. self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
  181. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  182. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  183. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  184. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  185. self._layout()
  186. self.search.SetFocus()
  187. def _layout(self):
  188. """!Do dialog layout"""
  189. sizer = wx.BoxSizer(wx.VERTICAL)
  190. # body
  191. dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
  192. dataSizer.Add(item = self.tree, proportion =1,
  193. flag = wx.EXPAND)
  194. # buttons
  195. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  196. btnSizer.Add(item = self.btnRun, proportion = 0,
  197. flag = wx.LEFT | wx.RIGHT, border = 5)
  198. sizer.Add(item = dataSizer, proportion = 1,
  199. flag = wx.EXPAND | wx.ALL, border = 5)
  200. sizer.Add(item = self.search, proportion=0,
  201. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  202. sizer.Add(item = btnSizer, proportion=0,
  203. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  204. sizer.Fit(self)
  205. sizer.SetSizeHints(self)
  206. self.SetSizer(sizer)
  207. self.Fit()
  208. self.SetAutoLayout(True)
  209. self.Layout()
  210. def OnCloseWindow(self, event):
  211. """!Close window"""
  212. self.Destroy()
  213. def OnRun(self, event):
  214. """!Run selected command"""
  215. if not self.tree.GetSelected():
  216. return # should not happen
  217. data = self.tree.GetPyData(self.tree.GetSelected())
  218. if not data:
  219. return
  220. handler = 'self.parent.' + data['handler'].lstrip('self.')
  221. if data['handler'] == 'self.OnXTerm':
  222. wx.MessageBox(parent = self,
  223. message = _('You must run this command from the menu or command line',
  224. 'This command require an XTerm'),
  225. caption = _('Message'), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  226. elif data['command']:
  227. eval(handler)(event = None, cmd = data['command'].split())
  228. else:
  229. eval(handler)(None)
  230. def OnShowItem(self, event):
  231. """!Show selected item"""
  232. self.tree.OnShowItem(event)
  233. if self.tree.GetSelected():
  234. self.btnRun.Enable()
  235. else:
  236. self.btnRun.Enable(False)
  237. def OnItemActivated(self, event):
  238. """!Item activated (double-click)"""
  239. item = event.GetItem()
  240. if not item or not item.IsOk():
  241. return
  242. data = self.tree.GetPyData(item)
  243. if not data or not data.has_key('command'):
  244. return
  245. self.tree.itemSelected = item
  246. self.OnRun(None)
  247. def OnItemSelected(self, event):
  248. """!Item selected"""
  249. item = event.GetItem()
  250. if not item or not item.IsOk():
  251. return
  252. data = self.tree.GetPyData(item)
  253. if not data or not data.has_key('command'):
  254. return
  255. if data['command']:
  256. label = data['command'] + ' -- ' + data['description']
  257. else:
  258. label = data['description']
  259. self.parent.SetStatusText(label, 0)
  260. def OnUpdateStatusBar(self, event):
  261. """!Update statusbar text"""
  262. element = self.search.GetSelection()
  263. self.tree.SearchItems(element = element,
  264. value = event.GetString())
  265. nItems = len(self.tree.itemsMarked)
  266. if event.GetString():
  267. self.parent.SetStatusText(_("%d modules match") % nItems, 0)
  268. else:
  269. self.parent.SetStatusText("", 0)
  270. event.Skip()
  271. class ItemTree(CT.CustomTreeCtrl):
  272. def __init__(self, parent, id = wx.ID_ANY,
  273. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  274. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
  275. super(ItemTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  276. self.root = self.AddRoot(_("Menu tree"))
  277. self.itemsMarked = [] # list of marked items
  278. self.itemSelected = None
  279. def SearchItems(self, element, value):
  280. """!Search item
  281. @param element element index (see self.searchBy)
  282. @param value
  283. @return list of found tree items
  284. """
  285. items = list()
  286. if not value:
  287. return items
  288. item = self.GetFirstChild(self.root)[0]
  289. self._processItem(item, element, value, items)
  290. self.itemsMarked = items
  291. self.itemSelected = None
  292. return items
  293. def _processItem(self, item, element, value, listOfItems):
  294. """!Search items (used by SearchItems)
  295. @param item reference item
  296. @param listOfItems list of found items
  297. """
  298. while item and item.IsOk():
  299. subItem = self.GetFirstChild(item)[0]
  300. if subItem:
  301. self._processItem(subItem, element, value, listOfItems)
  302. data = self.GetPyData(item)
  303. if data and data.has_key(element) and \
  304. value.lower() in data[element].lower():
  305. listOfItems.append(item)
  306. item = self.GetNextSibling(item)
  307. def GetSelected(self):
  308. """!Get selected item"""
  309. return self.itemSelected
  310. def OnShowItem(self, event):
  311. """!Highlight first found item in menu tree"""
  312. if len(self.itemsMarked) > 0:
  313. if self.GetSelected():
  314. self.ToggleItemSelection(self.GetSelected())
  315. idx = self.itemsMarked.index(self.GetSelected()) + 1
  316. else:
  317. idx = 0
  318. try:
  319. self.ToggleItemSelection(self.itemsMarked[idx])
  320. self.itemSelected = self.itemsMarked[idx]
  321. self.EnsureVisible(self.itemsMarked[idx])
  322. except IndexError:
  323. self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
  324. self.EnsureVisible(self.itemsMarked[0])
  325. self.itemSelected = self.itemsMarked[0]
  326. else:
  327. for item in self.root.GetChildren():
  328. self.Collapse(item)
  329. itemSelected = self.GetSelection()
  330. if itemSelected:
  331. self.ToggleItemSelection(itemSelected)
  332. self.itemSelected = None
  333. class MenuTree(ItemTree):
  334. """!Menu tree class"""
  335. def __init__(self, parent, data, **kwargs):
  336. self.parent = parent
  337. self.menudata = data
  338. super(MenuTree, self).__init__(parent, **kwargs)
  339. def Load(self, data = None):
  340. """!Load menu data tree
  341. @param data menu data (None to use self.menudata)
  342. """
  343. if not data:
  344. data = self.menudata
  345. self.itemsMarked = [] # list of marked items
  346. for eachMenuData in data.GetMenu():
  347. for label, items in eachMenuData:
  348. item = self.AppendItem(parentId = self.root,
  349. text = label.replace('&', ''))
  350. self.__AppendItems(item, items)
  351. def __AppendItems(self, item, data):
  352. """!Append items into tree (used by Load()
  353. @param item tree item (parent)
  354. @parent data menu data"""
  355. for eachItem in data:
  356. if len(eachItem) == 2:
  357. if eachItem[0]:
  358. itemSub = self.AppendItem(parentId = item,
  359. text = eachItem[0])
  360. self.__AppendItems(itemSub, eachItem[1])
  361. else:
  362. if eachItem[0]:
  363. itemNew = self.AppendItem(parentId = item,
  364. text = eachItem[0])
  365. data = { 'item' : eachItem[0],
  366. 'description' : eachItem[1],
  367. 'handler' : eachItem[2],
  368. 'command' : eachItem[3],
  369. 'keywords' : eachItem[4] }
  370. self.SetPyData(itemNew, data)
  371. class AboutWindow(wx.Frame):
  372. def __init__(self, parent):
  373. """!Create custom About Window
  374. @todo improve styling
  375. """
  376. wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400),
  377. title=_('About GRASS GIS'))
  378. panel = wx.Panel(parent = self, id = wx.ID_ANY)
  379. # icon
  380. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  381. # get version and web site
  382. version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
  383. flags = 'r',
  384. read = True).splitlines()
  385. infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
  386. infoSizer = wx.BoxSizer(wx.VERTICAL)
  387. infoGridSizer = wx.GridBagSizer(vgap=5, hgap=5)
  388. infoGridSizer.AddGrowableCol(0)
  389. infoGridSizer.AddGrowableCol(1)
  390. logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
  391. logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
  392. bitmap = wx.Bitmap(name = logo,
  393. type = wx.BITMAP_TYPE_ICO))
  394. infoSizer.Add(item = logoBitmap, proportion = 0,
  395. flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
  396. info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  397. label = version.replace('GRASS', 'GRASS GIS').strip() + '\n\n')
  398. info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  399. infoSizer.Add(item = info, proportion = 0,
  400. flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
  401. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  402. label = _('Official GRASS site:')),
  403. pos = (0, 0),
  404. flag = wx.ALIGN_RIGHT)
  405. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  406. label = 'http://grass.osgeo.org'),
  407. pos = (0, 1),
  408. flag = wx.ALIGN_LEFT)
  409. # infoGridSizer.Add(item = hl.HyperLinkCtrl(parent = self, id = wx.ID_ANY,
  410. # label = 'http://grass.osgeo.org',
  411. # URL = 'http://grass.osgeo.org'),
  412. # pos = (0, 1),
  413. # flag = wx.LEFT)
  414. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  415. label = _('GIS Library Revision:')),
  416. pos = (2, 0),
  417. flag = wx.ALIGN_RIGHT)
  418. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  419. label = svn_gis_h_rev.split(' ')[1] + ' (' +
  420. svn_gis_h_date.split(' ')[1] + ')'),
  421. pos = (2, 1),
  422. flag = wx.ALIGN_LEFT)
  423. infoSizer.Add(item = infoGridSizer,
  424. proportion = 1,
  425. flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
  426. border = 25)
  427. #
  428. # create pages
  429. #
  430. copyrightwin = self.PageCopyright()
  431. licensewin = self.PageLicense()
  432. authorwin = self.PageCredit()
  433. contribwin = self.PageContributors()
  434. transwin = self.PageTranslators()
  435. # create a flat notebook for displaying information about GRASS
  436. nbstyle = FN.FNB_VC8 | \
  437. FN.FNB_BACKGROUND_GRADIENT | \
  438. FN.FNB_TABS_BORDER_SIMPLE | \
  439. FN.FNB_NO_X_BUTTON
  440. aboutNotebook = FN.FlatNotebook(panel, id=wx.ID_ANY, style=nbstyle)
  441. aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
  442. # make pages for About GRASS notebook
  443. pg1 = aboutNotebook.AddPage(infoTxt, text=_("Info"))
  444. pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright"))
  445. pg3 = aboutNotebook.AddPage(licensewin, text=_("License"))
  446. pg4 = aboutNotebook.AddPage(authorwin, text=_("Authors"))
  447. pg5 = aboutNotebook.AddPage(contribwin, text=_("Contributors"))
  448. pg5 = aboutNotebook.AddPage(transwin, text=_("Translators"))
  449. wx.CallAfter(aboutNotebook.SetSelection, 0)
  450. # buttons
  451. btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
  452. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  453. btnSizer.Add(item = btnClose, proportion = 0,
  454. flag = wx.ALL | wx.ALIGN_RIGHT,
  455. border = 5)
  456. # bindings
  457. # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
  458. btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  459. infoTxt.SetSizer(infoSizer)
  460. infoSizer.Fit(infoTxt)
  461. sizer = wx.BoxSizer(wx.VERTICAL)
  462. sizer.Add(item=aboutNotebook, proportion=1,
  463. flag=wx.EXPAND | wx.ALL, border=1)
  464. sizer.Add(item=btnSizer, proportion=0,
  465. flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
  466. panel.SetSizer(sizer)
  467. self.Layout()
  468. def PageCopyright(self):
  469. """Copyright information"""
  470. copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
  471. if os.path.exists(copyfile):
  472. copyrightFile = open(copyfile, 'r')
  473. copytext = copyrightFile.read()
  474. copyrightFile.close()
  475. else:
  476. copytext = _('%s file missing') % 'COPYING'
  477. # put text into a scrolling panel
  478. copyrightwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  479. size=wx.DefaultSize,
  480. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  481. copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext)
  482. copyrightwin.SetAutoLayout(True)
  483. copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
  484. copyrightwin.sizer.Add(item=copyrighttxt, proportion=1,
  485. flag=wx.EXPAND | wx.ALL, border=3)
  486. copyrightwin.SetSizer(copyrightwin.sizer)
  487. copyrightwin.Layout()
  488. copyrightwin.SetupScrolling()
  489. return copyrightwin
  490. def PageLicense(self):
  491. """Licence about"""
  492. licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
  493. if os.path.exists(licfile):
  494. licenceFile = open(licfile, 'r')
  495. license = ''.join(licenceFile.readlines())
  496. licenceFile.close()
  497. else:
  498. license = _('%s file missing') % 'GPL.TXT'
  499. # put text into a scrolling panel
  500. licensewin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  501. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  502. licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license)
  503. licensewin.SetAutoLayout(True)
  504. licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
  505. licensewin.sizer.Add(item=licensetxt, proportion=1,
  506. flag=wx.EXPAND | wx.ALL, border=3)
  507. licensewin.SetSizer(licensewin.sizer)
  508. licensewin.Layout()
  509. licensewin.SetupScrolling()
  510. return licensewin
  511. def PageCredit(self):
  512. """Credit about"""
  513. # credits
  514. authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
  515. if os.path.exists(authfile):
  516. authorsFile = open(authfile, 'r')
  517. authors = unicode(''.join(authorsFile.readlines()), "utf-8")
  518. authorsFile.close()
  519. else:
  520. authors = _('%s file missing') % 'AUTHORS'
  521. authorwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  522. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  523. authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=authors)
  524. authorwin.SetAutoLayout(1)
  525. authorwin.SetupScrolling()
  526. authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
  527. authorwin.sizer.Add(item=authortxt, proportion=1,
  528. flag=wx.EXPAND | wx.ALL, border=3)
  529. authorwin.SetSizer(authorwin.sizer)
  530. authorwin.Layout()
  531. return authorwin
  532. def PageContributors(self):
  533. """Contributors info"""
  534. contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
  535. if os.path.exists(contribfile):
  536. contribFile = open(contribfile, 'r')
  537. contribs = list()
  538. for line in contribFile.readlines():
  539. cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
  540. contribs.append((name, email, country, osgeo_id))
  541. contribs[0] = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
  542. contribFile.close()
  543. else:
  544. contribs = None
  545. contribwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  546. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  547. contribwin.SetAutoLayout(1)
  548. contribwin.SetupScrolling()
  549. contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
  550. if not contribs:
  551. contribtxt = wx.StaticText(contribwin, id=wx.ID_ANY,
  552. label=_('%s file missing') % 'contibutors.csv')
  553. contribwin.sizer.Add(item=contribtxt, proportion=1,
  554. flag=wx.EXPAND | wx.ALL, border=3)
  555. else:
  556. contribBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
  557. for developer in contribs:
  558. for item in developer:
  559. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  560. label = item))
  561. contribwin.sizer.Add(item=contribBox, proportion=1,
  562. flag=wx.EXPAND | wx.ALL, border=3)
  563. contribwin.SetSizer(contribwin.sizer)
  564. contribwin.Layout()
  565. return contribwin
  566. def PageTranslators(self):
  567. """Translators info"""
  568. translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
  569. if os.path.exists(translatorsfile):
  570. translatorsFile = open(translatorsfile, 'r')
  571. translators = dict()
  572. for line in translatorsFile.readlines()[1:]:
  573. name, email, languages = line.rstrip('\n').split(',')
  574. for language in languages.split(' '):
  575. if not translators.has_key(language):
  576. translators[language] = list()
  577. translators[language].append((name, email))
  578. translatorsFile.close()
  579. else:
  580. translators = None
  581. translatorswin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  582. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  583. translatorswin.SetAutoLayout(1)
  584. translatorswin.SetupScrolling()
  585. translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
  586. if not translators:
  587. translatorstxt = wx.StaticText(translatorswin, id=wx.ID_ANY,
  588. label=_('%s file missing') % 'translators.csv')
  589. translatorswin.sizer.Add(item=translatorstxt, proportion=1,
  590. flag=wx.EXPAND | wx.ALL, border=3)
  591. else:
  592. translatorsBox = wx.FlexGridSizer(cols=3, vgap=5, hgap=5)
  593. languages = translators.keys()
  594. languages.sort()
  595. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  596. label = _('Name')))
  597. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  598. label = _('E-mail')))
  599. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  600. label = _('Language')))
  601. for lang in languages:
  602. for translator in translators[lang]:
  603. name, email = translator
  604. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  605. label = unicode(name, "utf-8")))
  606. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  607. label = email))
  608. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  609. label = lang))
  610. translatorswin.sizer.Add(item=translatorsBox, proportion=1,
  611. flag=wx.EXPAND | wx.ALL, border=3)
  612. translatorswin.SetSizer(translatorswin.sizer)
  613. translatorswin.Layout()
  614. return translatorswin
  615. def OnCloseWindow(self, event):
  616. """!Close window"""
  617. self.Close()
  618. class InstallExtensionWindow(wx.Frame):
  619. def __init__(self, parent, id = wx.ID_ANY,
  620. title = _("Fetch & install new extension from GRASS Addons"), **kwargs):
  621. self.parent = parent
  622. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  623. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  624. self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  625. label=" %s " % _("Repository"))
  626. self.findBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  627. label=" %s " % _("Find extension by"))
  628. self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  629. label=" %s " % _("List of extensions"))
  630. self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  631. value = 'https://svn.osgeo.org/grass/grass-addons')
  632. self.fullDesc = wx.CheckBox(parent = self.panel, id=wx.ID_ANY,
  633. label = _("Fetch full info including description and keywords (takes time)"))
  634. self.fullDesc.SetValue(False)
  635. self.search = SearchModuleWindow(parent = self.panel, showLabel = False)
  636. self.search.SetSelection(2)
  637. self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
  638. self.statusbar = self.CreateStatusBar(0)
  639. self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
  640. label = _("&Fetch"))
  641. self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
  642. self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  643. self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
  644. label = _("&Install"))
  645. self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
  646. self.btnInstall.Enable(False)
  647. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  648. self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
  649. self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
  650. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  651. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  652. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  653. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  654. self._layout()
  655. def _layout(self):
  656. """!Do layout"""
  657. sizer = wx.BoxSizer(wx.VERTICAL)
  658. repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
  659. repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
  660. repo1Sizer.Add(item = self.repo, proportion = 1,
  661. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  662. repo1Sizer.Add(item = self.btnFetch, proportion = 0,
  663. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  664. repoSizer.Add(item = repo1Sizer,
  665. flag = wx.EXPAND)
  666. repoSizer.Add(item = self.fullDesc)
  667. findSizer = wx.StaticBoxSizer(self.findBox, wx.HORIZONTAL)
  668. findSizer.Add(item = self.search, proportion = 1)
  669. treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
  670. treeSizer.Add(item = self.tree, proportion = 1,
  671. flag = wx.ALL | wx.EXPAND, border = 1)
  672. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  673. btnSizer.Add(item = self.btnClose, proportion = 0,
  674. flag = wx.LEFT | wx.RIGHT, border = 5)
  675. btnSizer.Add(item = self.btnInstall, proportion = 0,
  676. flag = wx.LEFT | wx.RIGHT, border = 5)
  677. sizer.Add(item = repoSizer, proportion = 0,
  678. flag = wx.ALL | wx.EXPAND, border = 3)
  679. sizer.Add(item = findSizer, proportion = 0,
  680. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  681. sizer.Add(item = treeSizer, proportion = 1,
  682. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  683. sizer.Add(item = btnSizer, proportion=0,
  684. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  685. self.panel.SetSizer(sizer)
  686. sizer.Fit(self.panel)
  687. self.Layout()
  688. def _install(self, name):
  689. if not name:
  690. return
  691. log = self.parent.GetLogWindow()
  692. log.RunCmd(['g.extension', 'extension=' + name,
  693. 'svnurl=' + self.repo.GetValue().strip()])
  694. self.OnCloseWindow(None)
  695. def OnUpdateStatusBar(self, event):
  696. """!Update statusbar text"""
  697. element = self.search.GetSelection()
  698. if not self.tree.IsLoaded():
  699. self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
  700. return
  701. self.tree.SearchItems(element = element,
  702. value = event.GetString())
  703. nItems = len(self.tree.itemsMarked)
  704. if event.GetString():
  705. self.SetStatusText(_("%d items match") % nItems, 0)
  706. else:
  707. self.SetStatusText("", 0)
  708. event.Skip()
  709. def OnCloseWindow(self, event):
  710. """!Close window"""
  711. self.Destroy()
  712. def OnFetch(self, event):
  713. """!Fetch list of available extensions"""
  714. self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
  715. self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
  716. self.SetStatusText("", 0)
  717. def OnItemActivated(self, event):
  718. item = event.GetItem()
  719. data = self.tree.GetPyData(item)
  720. if data and data.has_key('command'):
  721. self._install(data['command'])
  722. def OnInstall(self, event):
  723. """!Install selected extension"""
  724. item = self.tree.GetSelected()
  725. if not item.IsOk():
  726. return
  727. self._install(self.tree.GetItemText(item))
  728. def OnItemSelected(self, event):
  729. """!Item selected"""
  730. item = event.GetItem()
  731. self.tree.itemSelected = item
  732. data = self.tree.GetPyData(item)
  733. if not data:
  734. self.SetStatusText('', 0)
  735. self.btnInstall.Enable(False)
  736. else:
  737. self.SetStatusText(data.get('description', ''), 0)
  738. self.btnInstall.Enable(True)
  739. def OnShowItem(self, event):
  740. """!Show selected item"""
  741. self.tree.OnShowItem(event)
  742. if self.tree.GetSelected():
  743. self.btnInstall.Enable()
  744. else:
  745. self.btnInstall.Enable(False)
  746. class ExtensionTree(ItemTree):
  747. """!List of available extensions"""
  748. def __init__(self, parent, log, id = wx.ID_ANY,
  749. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  750. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
  751. **kwargs):
  752. self.parent = parent # GMFrame
  753. self.log = log
  754. super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  755. self._initTree()
  756. def _initTree(self):
  757. for prefix in ('display', 'database',
  758. 'general', 'imagery',
  759. 'misc', 'postscript', 'paint',
  760. 'raster', 'raster3D', 'sites', 'vector'):
  761. self.AppendItem(parentId = self.root,
  762. text = prefix)
  763. self._loaded = False
  764. def _expandPrefix(self, c):
  765. name = { 'd' : 'display',
  766. 'db' : 'database',
  767. 'g' : 'general',
  768. 'i' : 'imagery',
  769. 'm' : 'misc',
  770. 'ps' : 'postscript',
  771. 'p' : 'paint',
  772. 'r' : 'raster',
  773. 'r3' : 'raster3D',
  774. 's' : 'sites',
  775. 'v' : 'vector' }
  776. if name.has_key(c):
  777. return name[c]
  778. return c
  779. def _findItem(self, text):
  780. """!Find item"""
  781. item = self.GetFirstChild(self.root)[0]
  782. while item and item.IsOk():
  783. if text == self.GetItemText(item):
  784. return item
  785. item = self.GetNextSibling(item)
  786. return None
  787. def Load(self, url, full = False):
  788. """!Load list of extensions"""
  789. self.DeleteAllItems()
  790. self.root = self.AddRoot(_("Menu tree"))
  791. self._initTree()
  792. if full:
  793. flags = 'g'
  794. else:
  795. flags = 'l'
  796. ret = gcmd.RunCommand('g.extension', read = True,
  797. svnurl = url,
  798. flags = flags, quiet = True)
  799. if not ret:
  800. return
  801. mdict = dict()
  802. for line in ret.splitlines():
  803. if full:
  804. key, value = line.split('=', 1)
  805. if key == 'name':
  806. prefix, name = value.split('.', 1)
  807. if not mdict.has_key(prefix):
  808. mdict[prefix] = dict()
  809. mdict[prefix][name] = dict()
  810. else:
  811. mdict[prefix][name][key] = value
  812. else:
  813. prefix, name = line.strip().split('.', 1)
  814. if not mdict.has_key(prefix):
  815. mdict[prefix] = dict()
  816. mdict[prefix][name] = { 'command' : prefix + '.' + name }
  817. for prefix in mdict.keys():
  818. prefixName = self._expandPrefix(prefix)
  819. item = self._findItem(prefixName)
  820. names = mdict[prefix].keys()
  821. names.sort()
  822. for name in names:
  823. new = self.AppendItem(parentId = item,
  824. text = prefix + '.' + name)
  825. data = dict()
  826. for key in mdict[prefix][name].keys():
  827. data[key] = mdict[prefix][name][key]
  828. self.SetPyData(new, data)
  829. self._loaded = True
  830. def IsLoaded(self):
  831. """Check if items are loaded"""
  832. return self._loaded