help.py 35 KB

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