ghelp.py 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  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. sizer.Add(item = dataSizer, proportion = 1,
  198. flag = wx.EXPAND | wx.ALL, border = 5)
  199. sizer.Add(item = self.search, proportion=0,
  200. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  201. sizer.Add(item = btnSizer, proportion=0,
  202. flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
  203. sizer.Fit(self)
  204. sizer.SetSizeHints(self)
  205. self.SetSizer(sizer)
  206. self.Fit()
  207. self.SetAutoLayout(True)
  208. self.Layout()
  209. def OnCloseWindow(self, event):
  210. """!Close window"""
  211. self.Destroy()
  212. def OnRun(self, event):
  213. """!Run selected command"""
  214. if not self.tree.GetSelected():
  215. return # should not happen
  216. data = self.tree.GetPyData(self.tree.GetSelected())
  217. if not data:
  218. return
  219. handler = 'self.parent.' + data['handler'].lstrip('self.')
  220. if data['handler'] == 'self.OnXTerm':
  221. wx.MessageBox(parent = self,
  222. message = _('You must run this command from the menu or command line',
  223. 'This command require an XTerm'),
  224. caption = _('Message'), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  225. elif data['command']:
  226. eval(handler)(event = None, cmd = data['command'].split())
  227. else:
  228. eval(handler)(None)
  229. def OnShowItem(self, event):
  230. """!Show selected item"""
  231. self.tree.OnShowItem(event)
  232. if self.tree.GetSelected():
  233. self.btnRun.Enable()
  234. else:
  235. self.btnRun.Enable(False)
  236. def OnItemActivated(self, event):
  237. """!Item activated (double-click)"""
  238. item = event.GetItem()
  239. if not item or not item.IsOk():
  240. return
  241. data = self.tree.GetPyData(item)
  242. if not data or not data.has_key('command'):
  243. return
  244. self.tree.itemSelected = item
  245. self.OnRun(None)
  246. def OnItemSelected(self, event):
  247. """!Item selected"""
  248. item = event.GetItem()
  249. if not item or not item.IsOk():
  250. return
  251. data = self.tree.GetPyData(item)
  252. if not data or not data.has_key('command'):
  253. return
  254. if data['command']:
  255. label = data['command'] + ' -- ' + data['description']
  256. else:
  257. label = data['description']
  258. self.parent.SetStatusText(label, 0)
  259. def OnUpdateStatusBar(self, event):
  260. """!Update statusbar text"""
  261. element = self.search.GetSelection()
  262. self.tree.SearchItems(element = element,
  263. value = event.GetString())
  264. nItems = len(self.tree.itemsMarked)
  265. if event.GetString():
  266. self.parent.SetStatusText(_("%d modules match") % nItems, 0)
  267. else:
  268. self.parent.SetStatusText("", 0)
  269. event.Skip()
  270. class ItemTree(CT.CustomTreeCtrl):
  271. def __init__(self, parent, id = wx.ID_ANY,
  272. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  273. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
  274. super(ItemTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  275. self.root = self.AddRoot(_("Menu tree"))
  276. self.itemsMarked = [] # list of marked items
  277. self.itemSelected = None
  278. def SearchItems(self, element, value):
  279. """!Search item
  280. @param element element index (see self.searchBy)
  281. @param value
  282. @return list of found tree items
  283. """
  284. items = list()
  285. if not value:
  286. return items
  287. item = self.GetFirstChild(self.root)[0]
  288. self._processItem(item, element, value, items)
  289. self.itemsMarked = items
  290. self.itemSelected = None
  291. return items
  292. def _processItem(self, item, element, value, listOfItems):
  293. """!Search items (used by SearchItems)
  294. @param item reference item
  295. @param listOfItems list of found items
  296. """
  297. while item and item.IsOk():
  298. subItem = self.GetFirstChild(item)[0]
  299. if subItem:
  300. self._processItem(subItem, element, value, listOfItems)
  301. data = self.GetPyData(item)
  302. if data and data.has_key(element) and \
  303. value.lower() in data[element].lower():
  304. listOfItems.append(item)
  305. item = self.GetNextSibling(item)
  306. def GetSelected(self):
  307. """!Get selected item"""
  308. return self.itemSelected
  309. def OnShowItem(self, event):
  310. """!Highlight first found item in menu tree"""
  311. if len(self.itemsMarked) > 0:
  312. if self.GetSelected():
  313. self.ToggleItemSelection(self.GetSelected())
  314. idx = self.itemsMarked.index(self.GetSelected()) + 1
  315. else:
  316. idx = 0
  317. try:
  318. self.ToggleItemSelection(self.itemsMarked[idx])
  319. self.itemSelected = self.itemsMarked[idx]
  320. self.EnsureVisible(self.itemsMarked[idx])
  321. except IndexError:
  322. self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
  323. self.EnsureVisible(self.itemsMarked[0])
  324. self.itemSelected = self.itemsMarked[0]
  325. else:
  326. for item in self.root.GetChildren():
  327. self.Collapse(item)
  328. itemSelected = self.GetSelection()
  329. if itemSelected:
  330. self.ToggleItemSelection(itemSelected)
  331. self.itemSelected = None
  332. class MenuTree(ItemTree):
  333. """!Menu tree class"""
  334. def __init__(self, parent, data, **kwargs):
  335. self.parent = parent
  336. self.menudata = data
  337. super(MenuTree, self).__init__(parent, **kwargs)
  338. def Load(self, data = None):
  339. """!Load menu data tree
  340. @param data menu data (None to use self.menudata)
  341. """
  342. if not data:
  343. data = self.menudata
  344. self.itemsMarked = [] # list of marked items
  345. for eachMenuData in data.GetMenu():
  346. for label, items in eachMenuData:
  347. item = self.AppendItem(parentId = self.root,
  348. text = label.replace('&', ''))
  349. self.__AppendItems(item, items)
  350. def __AppendItems(self, item, data):
  351. """!Append items into tree (used by Load()
  352. @param item tree item (parent)
  353. @parent data menu data"""
  354. for eachItem in data:
  355. if len(eachItem) == 2:
  356. if eachItem[0]:
  357. itemSub = self.AppendItem(parentId = item,
  358. text = eachItem[0])
  359. self.__AppendItems(itemSub, eachItem[1])
  360. else:
  361. if eachItem[0]:
  362. itemNew = self.AppendItem(parentId = item,
  363. text = eachItem[0])
  364. data = { 'item' : eachItem[0],
  365. 'description' : eachItem[1],
  366. 'handler' : eachItem[2],
  367. 'command' : eachItem[3],
  368. 'keywords' : eachItem[4] }
  369. self.SetPyData(itemNew, data)
  370. class AboutWindow(wx.Frame):
  371. def __init__(self, parent):
  372. """!Create custom About Window
  373. @todo improve styling
  374. """
  375. wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400),
  376. title=_('About GRASS GIS'))
  377. panel = wx.Panel(parent = self, id = wx.ID_ANY)
  378. # icon
  379. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  380. # get version and web site
  381. version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
  382. flags = 'r',
  383. read = True).splitlines()
  384. infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
  385. infoSizer = wx.BoxSizer(wx.VERTICAL)
  386. infoGridSizer = wx.GridBagSizer(vgap=5, hgap=5)
  387. infoGridSizer.AddGrowableCol(0)
  388. infoGridSizer.AddGrowableCol(1)
  389. logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
  390. logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
  391. bitmap = wx.Bitmap(name = logo,
  392. type = wx.BITMAP_TYPE_ICO))
  393. infoSizer.Add(item = logoBitmap, proportion = 0,
  394. flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
  395. info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  396. label = version.replace('GRASS', 'GRASS GIS').strip() + '\n\n')
  397. info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  398. infoSizer.Add(item = info, proportion = 0,
  399. flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
  400. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  401. label = _('Official GRASS site:')),
  402. pos = (0, 0),
  403. flag = wx.ALIGN_RIGHT)
  404. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  405. label = 'http://grass.osgeo.org'),
  406. pos = (0, 1),
  407. flag = wx.ALIGN_LEFT)
  408. # infoGridSizer.Add(item = hl.HyperLinkCtrl(parent = self, id = wx.ID_ANY,
  409. # label = 'http://grass.osgeo.org',
  410. # URL = 'http://grass.osgeo.org'),
  411. # pos = (0, 1),
  412. # flag = wx.LEFT)
  413. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  414. label = _('GIS Library Revision:')),
  415. pos = (2, 0),
  416. flag = wx.ALIGN_RIGHT)
  417. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  418. label = svn_gis_h_rev.split(' ')[1] + ' (' +
  419. svn_gis_h_date.split(' ')[1] + ')'),
  420. pos = (2, 1),
  421. flag = wx.ALIGN_LEFT)
  422. infoSizer.Add(item = infoGridSizer,
  423. proportion = 1,
  424. flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
  425. border = 25)
  426. #
  427. # create pages
  428. #
  429. copyrightwin = self.PageCopyright()
  430. licensewin = self.PageLicense()
  431. authorwin = self.PageCredit()
  432. contribwin = self.PageContributors()
  433. transwin = self.PageTranslators()
  434. # create a flat notebook for displaying information about GRASS
  435. nbstyle = FN.FNB_VC8 | \
  436. FN.FNB_BACKGROUND_GRADIENT | \
  437. FN.FNB_TABS_BORDER_SIMPLE | \
  438. FN.FNB_NO_X_BUTTON
  439. aboutNotebook = FN.FlatNotebook(panel, id=wx.ID_ANY, style=nbstyle)
  440. aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
  441. # make pages for About GRASS notebook
  442. pg1 = aboutNotebook.AddPage(infoTxt, text=_("Info"))
  443. pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright"))
  444. pg3 = aboutNotebook.AddPage(licensewin, text=_("License"))
  445. pg4 = aboutNotebook.AddPage(authorwin, text=_("Authors"))
  446. pg5 = aboutNotebook.AddPage(contribwin, text=_("Contributors"))
  447. pg5 = aboutNotebook.AddPage(transwin, text=_("Translators"))
  448. wx.CallAfter(aboutNotebook.SetSelection, 0)
  449. # buttons
  450. btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
  451. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  452. btnSizer.Add(item = btnClose, proportion = 0,
  453. flag = wx.ALL | wx.ALIGN_RIGHT,
  454. border = 5)
  455. # bindings
  456. # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
  457. btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  458. infoTxt.SetSizer(infoSizer)
  459. infoSizer.Fit(infoTxt)
  460. sizer = wx.BoxSizer(wx.VERTICAL)
  461. sizer.Add(item=aboutNotebook, proportion=1,
  462. flag=wx.EXPAND | wx.ALL, border=1)
  463. sizer.Add(item=btnSizer, proportion=0,
  464. flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
  465. panel.SetSizer(sizer)
  466. self.Layout()
  467. def PageCopyright(self):
  468. """Copyright information"""
  469. copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
  470. if os.path.exists(copyfile):
  471. copyrightFile = open(copyfile, 'r')
  472. copytext = copyrightFile.read()
  473. copyrightFile.close()
  474. else:
  475. copytext = _('%s file missing') % 'COPYING'
  476. # put text into a scrolling panel
  477. copyrightwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  478. size=wx.DefaultSize,
  479. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  480. copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext)
  481. copyrightwin.SetAutoLayout(True)
  482. copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
  483. copyrightwin.sizer.Add(item=copyrighttxt, proportion=1,
  484. flag=wx.EXPAND | wx.ALL, border=3)
  485. copyrightwin.SetSizer(copyrightwin.sizer)
  486. copyrightwin.Layout()
  487. copyrightwin.SetupScrolling()
  488. return copyrightwin
  489. def PageLicense(self):
  490. """Licence about"""
  491. licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
  492. if os.path.exists(licfile):
  493. licenceFile = open(licfile, 'r')
  494. license = ''.join(licenceFile.readlines())
  495. licenceFile.close()
  496. else:
  497. license = _('%s file missing') % 'GPL.TXT'
  498. # put text into a scrolling panel
  499. licensewin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  500. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  501. licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license)
  502. licensewin.SetAutoLayout(True)
  503. licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
  504. licensewin.sizer.Add(item=licensetxt, proportion=1,
  505. flag=wx.EXPAND | wx.ALL, border=3)
  506. licensewin.SetSizer(licensewin.sizer)
  507. licensewin.Layout()
  508. licensewin.SetupScrolling()
  509. return licensewin
  510. def PageCredit(self):
  511. """Credit about"""
  512. # credits
  513. authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
  514. if os.path.exists(authfile):
  515. authorsFile = open(authfile, 'r')
  516. authors = unicode(''.join(authorsFile.readlines()), "utf-8")
  517. authorsFile.close()
  518. else:
  519. authors = _('%s file missing') % 'AUTHORS'
  520. authorwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  521. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  522. authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=authors)
  523. authorwin.SetAutoLayout(1)
  524. authorwin.SetupScrolling()
  525. authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
  526. authorwin.sizer.Add(item=authortxt, proportion=1,
  527. flag=wx.EXPAND | wx.ALL, border=3)
  528. authorwin.SetSizer(authorwin.sizer)
  529. authorwin.Layout()
  530. return authorwin
  531. def PageContributors(self):
  532. """Contributors info"""
  533. contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
  534. if os.path.exists(contribfile):
  535. contribFile = open(contribfile, 'r')
  536. contribs = list()
  537. for line in contribFile.readlines():
  538. cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
  539. contribs.append((name, email, country, osgeo_id))
  540. contribs[0] = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
  541. contribFile.close()
  542. else:
  543. contribs = None
  544. contribwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  545. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  546. contribwin.SetAutoLayout(1)
  547. contribwin.SetupScrolling()
  548. contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
  549. if not contribs:
  550. contribtxt = wx.StaticText(contribwin, id=wx.ID_ANY,
  551. label=_('%s file missing') % 'contibutors.csv')
  552. contribwin.sizer.Add(item=contribtxt, proportion=1,
  553. flag=wx.EXPAND | wx.ALL, border=3)
  554. else:
  555. contribBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
  556. for developer in contribs:
  557. for item in developer:
  558. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  559. label = item))
  560. contribwin.sizer.Add(item=contribBox, proportion=1,
  561. flag=wx.EXPAND | wx.ALL, border=3)
  562. contribwin.SetSizer(contribwin.sizer)
  563. contribwin.Layout()
  564. return contribwin
  565. def PageTranslators(self):
  566. """Translators info"""
  567. translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
  568. if os.path.exists(translatorsfile):
  569. translatorsFile = open(translatorsfile, 'r')
  570. translators = dict()
  571. for line in translatorsFile.readlines()[1:]:
  572. name, email, languages = line.rstrip('\n').split(',')
  573. for language in languages.split(' '):
  574. if not translators.has_key(language):
  575. translators[language] = list()
  576. translators[language].append((name, email))
  577. translatorsFile.close()
  578. else:
  579. translators = None
  580. translatorswin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  581. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  582. translatorswin.SetAutoLayout(1)
  583. translatorswin.SetupScrolling()
  584. translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
  585. if not translators:
  586. translatorstxt = wx.StaticText(translatorswin, id=wx.ID_ANY,
  587. label=_('%s file missing') % 'translators.csv')
  588. translatorswin.sizer.Add(item=translatorstxt, proportion=1,
  589. flag=wx.EXPAND | wx.ALL, border=3)
  590. else:
  591. translatorsBox = wx.FlexGridSizer(cols=3, vgap=5, hgap=5)
  592. languages = translators.keys()
  593. languages.sort()
  594. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  595. label = _('Name')))
  596. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  597. label = _('E-mail')))
  598. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  599. label = _('Language')))
  600. for lang in languages:
  601. for translator in translators[lang]:
  602. name, email = translator
  603. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  604. label = unicode(name, "utf-8")))
  605. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  606. label = email))
  607. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  608. label = lang))
  609. translatorswin.sizer.Add(item=translatorsBox, proportion=1,
  610. flag=wx.EXPAND | wx.ALL, border=3)
  611. translatorswin.SetSizer(translatorswin.sizer)
  612. translatorswin.Layout()
  613. return translatorswin
  614. def OnCloseWindow(self, event):
  615. """!Close window"""
  616. self.Close()
  617. class InstallExtensionWindow(wx.Frame):
  618. def __init__(self, parent, id = wx.ID_ANY,
  619. title = _("Fetch & install new extension from GRASS Addons"), **kwargs):
  620. self.parent = parent
  621. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  622. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  623. self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  624. label=" %s " % _("Repository"))
  625. self.findBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  626. label=" %s " % _("Find extension by"))
  627. self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  628. label=" %s " % _("List of extensions"))
  629. self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  630. value = 'https://svn.osgeo.org/grass/grass-addons')
  631. self.fullDesc = wx.CheckBox(parent = self.panel, id=wx.ID_ANY,
  632. label = _("Fetch full info including description and keywords (takes time)"))
  633. self.fullDesc.SetValue(False)
  634. self.search = SearchModuleWindow(parent = self.panel, showLabel = False)
  635. self.search.SetSelection(2)
  636. self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
  637. self.statusbar = self.CreateStatusBar(0)
  638. self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
  639. label = _("&Fetch"))
  640. self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
  641. self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  642. self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
  643. label = _("&Install"))
  644. self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
  645. self.btnInstall.Enable(False)
  646. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  647. self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
  648. self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
  649. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  650. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  651. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  652. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  653. self._layout()
  654. def _layout(self):
  655. """!Do layout"""
  656. sizer = wx.BoxSizer(wx.VERTICAL)
  657. repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
  658. repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
  659. repo1Sizer.Add(item = self.repo, proportion = 1,
  660. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  661. repo1Sizer.Add(item = self.btnFetch, proportion = 0,
  662. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  663. repoSizer.Add(item = repo1Sizer,
  664. flag = wx.EXPAND)
  665. repoSizer.Add(item = self.fullDesc)
  666. findSizer = wx.StaticBoxSizer(self.findBox, wx.HORIZONTAL)
  667. findSizer.Add(item = self.search, proportion = 1)
  668. treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
  669. treeSizer.Add(item = self.tree, proportion = 1,
  670. flag = wx.ALL | wx.EXPAND, border = 1)
  671. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  672. btnSizer.Add(item = self.btnClose, proportion = 0,
  673. flag = wx.LEFT | wx.RIGHT, border = 5)
  674. btnSizer.Add(item = self.btnInstall, proportion = 0,
  675. flag = wx.LEFT | wx.RIGHT, border = 5)
  676. sizer.Add(item = repoSizer, proportion = 0,
  677. flag = wx.ALL | wx.EXPAND, border = 3)
  678. sizer.Add(item = findSizer, proportion = 0,
  679. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  680. sizer.Add(item = treeSizer, proportion = 1,
  681. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  682. sizer.Add(item = btnSizer, proportion=0,
  683. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  684. self.panel.SetSizer(sizer)
  685. sizer.Fit(self.panel)
  686. self.Layout()
  687. def _install(self, name):
  688. if not name:
  689. return
  690. log = self.parent.GetLogWindow()
  691. log.RunCmd(['g.extension', 'extension=' + name,
  692. 'svnurl=' + self.repo.GetValue().strip()])
  693. self.OnCloseWindow(None)
  694. def OnUpdateStatusBar(self, event):
  695. """!Update statusbar text"""
  696. element = self.search.GetSelection()
  697. if not self.tree.IsLoaded():
  698. self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
  699. return
  700. self.tree.SearchItems(element = element,
  701. value = event.GetString())
  702. nItems = len(self.tree.itemsMarked)
  703. if event.GetString():
  704. self.SetStatusText(_("%d items match") % nItems, 0)
  705. else:
  706. self.SetStatusText("", 0)
  707. event.Skip()
  708. def OnCloseWindow(self, event):
  709. """!Close window"""
  710. self.Destroy()
  711. def OnFetch(self, event):
  712. """!Fetch list of available extensions"""
  713. self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
  714. self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
  715. self.SetStatusText("", 0)
  716. def OnItemActivated(self, event):
  717. item = event.GetItem()
  718. data = self.tree.GetPyData(item)
  719. if data and data.has_key('command'):
  720. self._install(data['command'])
  721. def OnInstall(self, event):
  722. """!Install selected extension"""
  723. item = self.tree.GetSelected()
  724. if not item.IsOk():
  725. return
  726. self._install(self.tree.GetItemText(item))
  727. def OnItemSelected(self, event):
  728. """!Item selected"""
  729. item = event.GetItem()
  730. self.tree.itemSelected = item
  731. data = self.tree.GetPyData(item)
  732. if not data:
  733. self.SetStatusText('', 0)
  734. self.btnInstall.Enable(False)
  735. else:
  736. self.SetStatusText(data.get('description', ''), 0)
  737. self.btnInstall.Enable(True)
  738. def OnShowItem(self, event):
  739. """!Show selected item"""
  740. self.tree.OnShowItem(event)
  741. if self.tree.GetSelected():
  742. self.btnInstall.Enable()
  743. else:
  744. self.btnInstall.Enable(False)
  745. class ExtensionTree(ItemTree):
  746. """!List of available extensions"""
  747. def __init__(self, parent, log, id = wx.ID_ANY,
  748. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  749. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
  750. **kwargs):
  751. self.parent = parent # GMFrame
  752. self.log = log
  753. super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  754. self._initTree()
  755. def _initTree(self):
  756. for prefix in ('display', 'database',
  757. 'general', 'imagery',
  758. 'misc', 'postscript', 'paint',
  759. 'raster', 'raster3D', 'sites', 'vector'):
  760. self.AppendItem(parentId = self.root,
  761. text = prefix)
  762. self._loaded = False
  763. def _expandPrefix(self, c):
  764. name = { 'd' : 'display',
  765. 'db' : 'database',
  766. 'g' : 'general',
  767. 'i' : 'imagery',
  768. 'm' : 'misc',
  769. 'ps' : 'postscript',
  770. 'p' : 'paint',
  771. 'r' : 'raster',
  772. 'r3' : 'raster3D',
  773. 's' : 'sites',
  774. 'v' : 'vector' }
  775. if name.has_key(c):
  776. return name[c]
  777. return c
  778. def _findItem(self, text):
  779. """!Find item"""
  780. item = self.GetFirstChild(self.root)[0]
  781. while item and item.IsOk():
  782. if text == self.GetItemText(item):
  783. return item
  784. item = self.GetNextSibling(item)
  785. return None
  786. def Load(self, url, full = False):
  787. """!Load list of extensions"""
  788. self.DeleteAllItems()
  789. self.root = self.AddRoot(_("Menu tree"))
  790. self._initTree()
  791. if full:
  792. flags = 'g'
  793. else:
  794. flags = 'l'
  795. ret = gcmd.RunCommand('g.extension', read = True,
  796. svnurl = url,
  797. flags = flags, quiet = True)
  798. if not ret:
  799. return
  800. mdict = dict()
  801. for line in ret.splitlines():
  802. if full:
  803. key, value = line.split('=', 1)
  804. if key == 'name':
  805. prefix, name = value.split('.', 1)
  806. if not mdict.has_key(prefix):
  807. mdict[prefix] = dict()
  808. mdict[prefix][name] = dict()
  809. else:
  810. mdict[prefix][name][key] = value
  811. else:
  812. prefix, name = line.strip().split('.', 1)
  813. if not mdict.has_key(prefix):
  814. mdict[prefix] = dict()
  815. mdict[prefix][name] = { 'command' : prefix + '.' + name }
  816. for prefix in mdict.keys():
  817. prefixName = self._expandPrefix(prefix)
  818. item = self._findItem(prefixName)
  819. names = mdict[prefix].keys()
  820. names.sort()
  821. for name in names:
  822. new = self.AppendItem(parentId = item,
  823. text = prefix + '.' + name)
  824. data = dict()
  825. for key in mdict[prefix][name].keys():
  826. data[key] = mdict[prefix][name][key]
  827. self.SetPyData(new, data)
  828. self._loaded = True
  829. def IsLoaded(self):
  830. """Check if items are loaded"""
  831. return self._loaded