ghelp.py 38 KB

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