ghelp.py 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280
  1. """!
  2. @package help.py
  3. @brief Help window
  4. Classes:
  5. - SearchModuleWindow
  6. - ItemTree
  7. - MenuTreeWindow
  8. - MenuTree
  9. - AboutWindow
  10. - InstallExtensionWindow
  11. - ExtensionTree
  12. - HelpFrame
  13. - HelpWindow
  14. - HelpPanel
  15. (C) 2008-2011 by the GRASS Development Team
  16. This program is free software under the GNU General Public License
  17. (>=v2). Read the file COPYING that comes with GRASS for details.
  18. @author Martin Landa <landa.martin gmail.com>
  19. """
  20. import os
  21. import codecs
  22. import wx
  23. try:
  24. import wx.lib.agw.customtreectrl as CT
  25. # import wx.lib.agw.hyperlink as hl
  26. except ImportError:
  27. import wx.lib.customtreectrl as CT
  28. # import wx.lib.hyperlink as hl
  29. import wx.lib.flatnotebook as FN
  30. import wx.lib.scrolledpanel as scrolled
  31. from grass.script import task as gtask
  32. import menudata
  33. import gcmd
  34. import globalvar
  35. import gdialogs
  36. import utils
  37. import menuform
  38. class HelpFrame(wx.Frame):
  39. """!GRASS Quickstart help window"""
  40. def __init__(self, parent, id, title, size, file):
  41. wx.Frame.__init__(self, parent = parent, id = id, title = title, size = size)
  42. sizer = wx.BoxSizer(wx.VERTICAL)
  43. # text
  44. content = HelpPanel(parent = self)
  45. content.LoadPage(file)
  46. sizer.Add(item = content, proportion = 1, flag = wx.EXPAND)
  47. self.SetAutoLayout(True)
  48. self.SetSizer(sizer)
  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 = gdialogs.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 module[:len(text)] == text:
  135. found = True
  136. if found:
  137. iFound += 1
  138. try:
  139. group, name = module.split('.')
  140. except ValueError:
  141. continue # TODO
  142. if group not in modules:
  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. def Reset(self):
  164. """!Reset widget"""
  165. self.searchBy.SetSelection(0)
  166. self.search.SetValue('')
  167. class MenuTreeWindow(wx.Panel):
  168. """!Show menu tree"""
  169. def __init__(self, parent, id = wx.ID_ANY, **kwargs):
  170. self.parent = parent # LayerManager
  171. wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
  172. self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  173. label = " %s " % _("Menu tree (double-click to run command)"))
  174. # tree
  175. self.tree = MenuTree(parent = self, data = menudata.ManagerData())
  176. self.tree.Load()
  177. # search widget
  178. self.search = SearchModuleWindow(parent = self, showChoice = False)
  179. # buttons
  180. self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
  181. self.btnRun.SetToolTipString(_("Run selected command"))
  182. self.btnRun.Enable(False)
  183. # bindings
  184. self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
  185. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  186. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  187. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  188. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  189. self._layout()
  190. self.search.SetFocus()
  191. def _layout(self):
  192. """!Do dialog layout"""
  193. sizer = wx.BoxSizer(wx.VERTICAL)
  194. # body
  195. dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
  196. dataSizer.Add(item = self.tree, proportion =1,
  197. flag = wx.EXPAND)
  198. # buttons
  199. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  200. btnSizer.Add(item = self.btnRun, proportion = 0)
  201. sizer.Add(item = dataSizer, proportion = 1,
  202. flag = wx.EXPAND | wx.ALL, border = 5)
  203. sizer.Add(item = self.search, proportion = 0,
  204. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  205. sizer.Add(item = btnSizer, proportion = 0,
  206. flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
  207. sizer.Fit(self)
  208. sizer.SetSizeHints(self)
  209. self.SetSizer(sizer)
  210. self.Fit()
  211. self.SetAutoLayout(True)
  212. self.Layout()
  213. def OnCloseWindow(self, event):
  214. """!Close window"""
  215. self.Destroy()
  216. def OnRun(self, event):
  217. """!Run selected command"""
  218. if not self.tree.GetSelected():
  219. return # should not happen
  220. data = self.tree.GetPyData(self.tree.GetSelected())
  221. if not data:
  222. return
  223. handler = 'self.parent.' + data['handler'].lstrip('self.')
  224. if data['handler'] == 'self.OnXTerm':
  225. wx.MessageBox(parent = self,
  226. message = _('You must run this command from the menu or command line',
  227. 'This command require an XTerm'),
  228. caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
  229. elif data['command']:
  230. eval(handler)(event = None, cmd = data['command'].split())
  231. else:
  232. eval(handler)(None)
  233. def OnShowItem(self, event):
  234. """!Show selected item"""
  235. self.tree.OnShowItem(event)
  236. if self.tree.GetSelected():
  237. self.btnRun.Enable()
  238. else:
  239. self.btnRun.Enable(False)
  240. def OnItemActivated(self, event):
  241. """!Item activated (double-click)"""
  242. item = event.GetItem()
  243. if not item or not item.IsOk():
  244. return
  245. data = self.tree.GetPyData(item)
  246. if not data or 'command' not in data:
  247. return
  248. self.tree.itemSelected = item
  249. self.OnRun(None)
  250. def OnItemSelected(self, event):
  251. """!Item selected"""
  252. item = event.GetItem()
  253. if not item or not item.IsOk():
  254. return
  255. data = self.tree.GetPyData(item)
  256. if not data or 'command' not in data:
  257. return
  258. if data['command']:
  259. label = data['command'] + ' -- ' + data['description']
  260. else:
  261. label = data['description']
  262. self.parent.SetStatusText(label, 0)
  263. def OnUpdateStatusBar(self, event):
  264. """!Update statusbar text"""
  265. element = self.search.GetSelection()
  266. self.tree.SearchItems(element = element,
  267. value = event.GetString())
  268. nItems = len(self.tree.itemsMarked)
  269. if event.GetString():
  270. self.parent.SetStatusText(_("%d modules match") % nItems, 0)
  271. else:
  272. self.parent.SetStatusText("", 0)
  273. event.Skip()
  274. class ItemTree(CT.CustomTreeCtrl):
  275. def __init__(self, parent, id = wx.ID_ANY,
  276. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  277. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
  278. if globalvar.hasAgw:
  279. super(ItemTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs)
  280. else:
  281. super(ItemTree, self).__init__(parent, id, style = ctstyle, **kwargs)
  282. self.root = self.AddRoot(_("Menu tree"))
  283. self.itemsMarked = [] # list of marked items
  284. self.itemSelected = None
  285. def SearchItems(self, element, value):
  286. """!Search item
  287. @param element element index (see self.searchBy)
  288. @param value
  289. @return list of found tree items
  290. """
  291. items = list()
  292. if not value:
  293. return items
  294. item = self.GetFirstChild(self.root)[0]
  295. self._processItem(item, element, value, items)
  296. self.itemsMarked = items
  297. self.itemSelected = None
  298. return items
  299. def _processItem(self, item, element, value, listOfItems):
  300. """!Search items (used by SearchItems)
  301. @param item reference item
  302. @param listOfItems list of found items
  303. """
  304. while item and item.IsOk():
  305. subItem = self.GetFirstChild(item)[0]
  306. if subItem:
  307. self._processItem(subItem, element, value, listOfItems)
  308. data = self.GetPyData(item)
  309. if data and element in data and \
  310. value.lower() in data[element].lower():
  311. listOfItems.append(item)
  312. item = self.GetNextSibling(item)
  313. def GetSelected(self):
  314. """!Get selected item"""
  315. return self.itemSelected
  316. def OnShowItem(self, event):
  317. """!Highlight first found item in menu tree"""
  318. if len(self.itemsMarked) > 0:
  319. if self.GetSelected():
  320. self.ToggleItemSelection(self.GetSelected())
  321. idx = self.itemsMarked.index(self.GetSelected()) + 1
  322. else:
  323. idx = 0
  324. try:
  325. self.ToggleItemSelection(self.itemsMarked[idx])
  326. self.itemSelected = self.itemsMarked[idx]
  327. self.EnsureVisible(self.itemsMarked[idx])
  328. except IndexError:
  329. self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
  330. self.EnsureVisible(self.itemsMarked[0])
  331. self.itemSelected = self.itemsMarked[0]
  332. else:
  333. for item in self.root.GetChildren():
  334. self.Collapse(item)
  335. itemSelected = self.GetSelection()
  336. if itemSelected:
  337. self.ToggleItemSelection(itemSelected)
  338. self.itemSelected = None
  339. class MenuTree(ItemTree):
  340. """!Menu tree class"""
  341. def __init__(self, parent, data, **kwargs):
  342. self.parent = parent
  343. self.menudata = data
  344. super(MenuTree, self).__init__(parent, **kwargs)
  345. def Load(self, data = None):
  346. """!Load menu data tree
  347. @param data menu data (None to use self.menudata)
  348. """
  349. if not data:
  350. data = self.menudata
  351. self.itemsMarked = [] # list of marked items
  352. for eachMenuData in data.GetMenu():
  353. for label, items in eachMenuData:
  354. item = self.AppendItem(parentId = self.root,
  355. text = label.replace('&', ''))
  356. self.__AppendItems(item, items)
  357. def __AppendItems(self, item, data):
  358. """!Append items into tree (used by Load()
  359. @param item tree item (parent)
  360. @parent data menu data"""
  361. for eachItem in data:
  362. if len(eachItem) == 2:
  363. if eachItem[0]:
  364. itemSub = self.AppendItem(parentId = item,
  365. text = eachItem[0])
  366. self.__AppendItems(itemSub, eachItem[1])
  367. else:
  368. if eachItem[0]:
  369. itemNew = self.AppendItem(parentId = item,
  370. text = eachItem[0])
  371. data = { 'item' : eachItem[0],
  372. 'description' : eachItem[1],
  373. 'handler' : eachItem[2],
  374. 'command' : eachItem[3],
  375. 'keywords' : eachItem[4] }
  376. self.SetPyData(itemNew, data)
  377. class AboutWindow(wx.Frame):
  378. def __init__(self, parent):
  379. """!Create custom About Window
  380. @todo improve styling
  381. """
  382. wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, size = (550,400),
  383. title = _('About GRASS GIS'))
  384. panel = wx.Panel(parent = self, id = wx.ID_ANY)
  385. # icon
  386. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  387. # get version and web site
  388. version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
  389. flags = 'r',
  390. read = True).splitlines()
  391. infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
  392. infoSizer = wx.BoxSizer(wx.VERTICAL)
  393. infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
  394. infoGridSizer.AddGrowableCol(0)
  395. infoGridSizer.AddGrowableCol(1)
  396. logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
  397. logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
  398. bitmap = wx.Bitmap(name = logo,
  399. type = wx.BITMAP_TYPE_ICO))
  400. infoSizer.Add(item = logoBitmap, proportion = 0,
  401. flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
  402. info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  403. label = version.replace('GRASS', 'GRASS GIS').strip() + '\n\n')
  404. info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  405. infoSizer.Add(item = info, proportion = 0,
  406. flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
  407. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  408. label = _('Official GRASS site:')),
  409. pos = (0, 0),
  410. flag = wx.ALIGN_RIGHT)
  411. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  412. label = 'http://grass.osgeo.org'),
  413. pos = (0, 1),
  414. flag = wx.ALIGN_LEFT)
  415. # infoGridSizer.Add(item = hl.HyperLinkCtrl(parent = self, id = wx.ID_ANY,
  416. # label = 'http://grass.osgeo.org',
  417. # URL = 'http://grass.osgeo.org'),
  418. # pos = (0, 1),
  419. # flag = wx.LEFT)
  420. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  421. label = _('GIS Library Revision:')),
  422. pos = (2, 0),
  423. flag = wx.ALIGN_RIGHT)
  424. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  425. label = svn_gis_h_rev.split(' ')[2] + ' (' +
  426. svn_gis_h_date.split(' ')[2] + ')'),
  427. pos = (2, 1),
  428. flag = wx.ALIGN_LEFT)
  429. infoSizer.Add(item = infoGridSizer,
  430. proportion = 1,
  431. flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
  432. border = 25)
  433. #
  434. # create pages
  435. #
  436. copyrightwin = self.PageCopyright()
  437. licensewin = self.PageLicense()
  438. authorwin = self.PageCredit()
  439. contribwin = self.PageContributors()
  440. transwin = self.PageTranslators()
  441. # create a flat notebook for displaying information about GRASS
  442. nbstyle = FN.FNB_VC8 | \
  443. FN.FNB_BACKGROUND_GRADIENT | \
  444. FN.FNB_TABS_BORDER_SIMPLE | \
  445. FN.FNB_NO_X_BUTTON
  446. if globalvar.hasAgw:
  447. aboutNotebook = FN.FlatNotebook(panel, id = wx.ID_ANY, agwStyle = nbstyle)
  448. else:
  449. aboutNotebook = FN.FlatNotebook(panel, id = wx.ID_ANY, style = nbstyle)
  450. aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
  451. # make pages for About GRASS notebook
  452. pg1 = aboutNotebook.AddPage(infoTxt, text = _("Info"))
  453. pg2 = aboutNotebook.AddPage(copyrightwin, text = _("Copyright"))
  454. pg3 = aboutNotebook.AddPage(licensewin, text = _("License"))
  455. pg4 = aboutNotebook.AddPage(authorwin, text = _("Authors"))
  456. pg5 = aboutNotebook.AddPage(contribwin, text = _("Contributors"))
  457. pg5 = aboutNotebook.AddPage(transwin, text = _("Translators"))
  458. wx.CallAfter(aboutNotebook.SetSelection, 0)
  459. # buttons
  460. btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
  461. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  462. btnSizer.Add(item = btnClose, proportion = 0,
  463. flag = wx.ALL | wx.ALIGN_RIGHT,
  464. border = 5)
  465. # bindings
  466. # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
  467. btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  468. infoTxt.SetSizer(infoSizer)
  469. infoSizer.Fit(infoTxt)
  470. sizer = wx.BoxSizer(wx.VERTICAL)
  471. sizer.Add(item = aboutNotebook, proportion = 1,
  472. flag = wx.EXPAND | wx.ALL, border = 1)
  473. sizer.Add(item = btnSizer, proportion = 0,
  474. flag = wx.ALL | wx.ALIGN_RIGHT, border = 1)
  475. panel.SetSizer(sizer)
  476. self.Layout()
  477. def PageCopyright(self):
  478. """Copyright information"""
  479. copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
  480. if os.path.exists(copyfile):
  481. copyrightFile = open(copyfile, 'r')
  482. copytext = copyrightFile.read()
  483. copyrightFile.close()
  484. else:
  485. copytext = _('%s file missing') % 'COPYING'
  486. # put text into a scrolling panel
  487. copyrightwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
  488. size = wx.DefaultSize,
  489. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  490. copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext)
  491. copyrightwin.SetAutoLayout(True)
  492. copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
  493. copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1,
  494. flag = wx.EXPAND | wx.ALL, border = 3)
  495. copyrightwin.SetSizer(copyrightwin.sizer)
  496. copyrightwin.Layout()
  497. copyrightwin.SetupScrolling()
  498. return copyrightwin
  499. def PageLicense(self):
  500. """Licence about"""
  501. licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
  502. if os.path.exists(licfile):
  503. licenceFile = open(licfile, 'r')
  504. license = ''.join(licenceFile.readlines())
  505. licenceFile.close()
  506. else:
  507. license = _('%s file missing') % 'GPL.TXT'
  508. # put text into a scrolling panel
  509. licensewin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
  510. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  511. licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license)
  512. licensewin.SetAutoLayout(True)
  513. licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
  514. licensewin.sizer.Add(item = licensetxt, proportion = 1,
  515. flag = wx.EXPAND | wx.ALL, border = 3)
  516. licensewin.SetSizer(licensewin.sizer)
  517. licensewin.Layout()
  518. licensewin.SetupScrolling()
  519. return licensewin
  520. def PageCredit(self):
  521. """Credit about"""
  522. # credits
  523. authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
  524. if os.path.exists(authfile):
  525. authorsFile = open(authfile, 'r')
  526. authors = unicode(''.join(authorsFile.readlines()), "utf-8")
  527. authorsFile.close()
  528. else:
  529. authors = _('%s file missing') % 'AUTHORS'
  530. authorwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
  531. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  532. authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
  533. authorwin.SetAutoLayout(1)
  534. authorwin.SetupScrolling()
  535. authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
  536. authorwin.sizer.Add(item = authortxt, proportion = 1,
  537. flag = wx.EXPAND | wx.ALL, border = 3)
  538. authorwin.SetSizer(authorwin.sizer)
  539. authorwin.Layout()
  540. return authorwin
  541. def PageContributors(self):
  542. """Contributors info"""
  543. contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
  544. if os.path.exists(contribfile):
  545. contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
  546. contribs = dict()
  547. errLines = list()
  548. for line in contribFile.readlines():
  549. line = line.rstrip('\n')
  550. try:
  551. cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
  552. except ValueError:
  553. errLines.append(line)
  554. continue
  555. contribs[osgeo_id] = [name, email, country]
  556. contribFile.close()
  557. if errLines:
  558. gcmd.GError(parent = self,
  559. message = _("Error when reading file '%s'.") % translatorsfile + \
  560. "\n\n" + _("Lines:") + " %s" % \
  561. os.linesep.join(map(utils.DecodeString, errLines)))
  562. else:
  563. contribs = None
  564. contribwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
  565. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  566. contribwin.SetAutoLayout(1)
  567. contribwin.SetupScrolling()
  568. contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
  569. if not contribs:
  570. contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
  571. label = _('%s file missing') % 'contibutors.csv')
  572. contribwin.sizer.Add(item = contribtxt, proportion = 1,
  573. flag = wx.EXPAND | wx.ALL, border = 3)
  574. else:
  575. contribBox = wx.FlexGridSizer(cols = 4, vgap = 5, hgap = 5)
  576. for item in (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID')):
  577. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  578. label = item))
  579. for osgeo_id in sorted(contribs.keys()):
  580. for item in contribs[osgeo_id] + [osgeo_id]:
  581. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  582. label = item))
  583. contribwin.sizer.Add(item = contribBox, proportion = 1,
  584. flag = wx.EXPAND | wx.ALL, border = 3)
  585. contribwin.SetSizer(contribwin.sizer)
  586. contribwin.Layout()
  587. return contribwin
  588. def PageTranslators(self):
  589. """Translators info"""
  590. translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
  591. if os.path.exists(translatorsfile):
  592. translatorsFile = open(translatorsfile, 'r')
  593. translators = dict()
  594. errLines = list()
  595. for line in translatorsFile.readlines()[1:]:
  596. line = line.rstrip('\n')
  597. try:
  598. name, email, languages = line.split(',')
  599. except ValueError:
  600. errLines.append(line)
  601. continue
  602. for language in languages.split(' '):
  603. if language not in translators:
  604. translators[language] = list()
  605. translators[language].append((name, email))
  606. translatorsFile.close()
  607. if errLines:
  608. gcmd.GError(parent = self,
  609. message = _("Error when reading file '%s'.") % translatorsfile + \
  610. "\n\n" + _("Lines:") + " %s" % \
  611. os.linesep.join(map(utils.DecodeString, errLines)))
  612. else:
  613. translators = None
  614. translatorswin = scrolled.ScrolledPanel(self, id = wx.ID_ANY,
  615. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  616. translatorswin.SetAutoLayout(1)
  617. translatorswin.SetupScrolling()
  618. translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
  619. if not translators:
  620. translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY,
  621. label = _('%s file missing') % 'translators.csv')
  622. translatorswin.sizer.Add(item = translatorstxt, proportion = 1,
  623. flag = wx.EXPAND | wx.ALL, border = 3)
  624. else:
  625. translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
  626. languages = translators.keys()
  627. languages.sort()
  628. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  629. label = _('Name')))
  630. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  631. label = _('E-mail')))
  632. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  633. label = _('Language')))
  634. for lang in languages:
  635. for translator in translators[lang]:
  636. name, email = translator
  637. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  638. label = unicode(name, "utf-8")))
  639. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  640. label = email))
  641. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  642. label = lang))
  643. translatorswin.sizer.Add(item = translatorsBox, proportion = 1,
  644. flag = wx.EXPAND | wx.ALL, border = 3)
  645. translatorswin.SetSizer(translatorswin.sizer)
  646. translatorswin.Layout()
  647. return translatorswin
  648. def OnCloseWindow(self, event):
  649. """!Close window"""
  650. self.Close()
  651. class InstallExtensionWindow(wx.Frame):
  652. def __init__(self, parent, id = wx.ID_ANY,
  653. title = _("Fetch & install extension from GRASS Addons"), **kwargs):
  654. self.parent = parent
  655. self.options = dict() # list of options
  656. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  657. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  658. self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  659. label = " %s " % _("Repository"))
  660. self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  661. label = " %s " % _("List of extensions"))
  662. self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
  663. self.fullDesc = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
  664. label = _("Fetch full info including description and keywords (takes time)"))
  665. self.fullDesc.SetValue(False)
  666. self.search = SearchModuleWindow(parent = self.panel)
  667. self.search.SetSelection(2)
  668. self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
  669. self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  670. label = " %s " % _("Options"))
  671. task = gtask.parse_interface('g.extension')
  672. for f in task.get_options()['flags']:
  673. name = f.get('name', '')
  674. desc = f.get('label', '')
  675. if not desc:
  676. desc = f.get('description', '')
  677. if not name and not desc:
  678. continue
  679. if name in ('l', 'f', 'g', 'quiet', 'verbose'):
  680. continue
  681. self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
  682. label = desc)
  683. self.repo.SetValue(task.get_param(value = 'svnurl').get('default',
  684. 'https://svn.osgeo.org/grass/grass-addons'))
  685. self.statusbar = self.CreateStatusBar(number = 1)
  686. self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
  687. label = _("&Fetch"))
  688. self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
  689. self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  690. self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
  691. label = _("&Install"))
  692. self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
  693. self.btnInstall.Enable(False)
  694. self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
  695. label = _("Command dialog"))
  696. self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension')
  697. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  698. self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
  699. self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
  700. self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
  701. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  702. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  703. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  704. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  705. self._layout()
  706. def _layout(self):
  707. """!Do layout"""
  708. sizer = wx.BoxSizer(wx.VERTICAL)
  709. repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
  710. repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
  711. repo1Sizer.Add(item = self.repo, proportion = 1,
  712. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  713. repo1Sizer.Add(item = self.btnFetch, proportion = 0,
  714. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  715. repoSizer.Add(item = repo1Sizer,
  716. flag = wx.EXPAND)
  717. repoSizer.Add(item = self.fullDesc)
  718. findSizer = wx.BoxSizer(wx.HORIZONTAL)
  719. findSizer.Add(item = self.search, proportion = 1)
  720. treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
  721. treeSizer.Add(item = self.tree, proportion = 1,
  722. flag = wx.ALL | wx.EXPAND, border = 1)
  723. # options
  724. optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
  725. for key in self.options.keys():
  726. optionSizer.Add(item = self.options[key], proportion = 0)
  727. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  728. btnSizer.Add(item = self.btnCmd, proportion = 0,
  729. flag = wx.RIGHT, border = 5)
  730. btnSizer.AddSpacer(10)
  731. btnSizer.Add(item = self.btnClose, proportion = 0,
  732. flag = wx.RIGHT, border = 5)
  733. btnSizer.Add(item = self.btnInstall, proportion = 0)
  734. sizer.Add(item = repoSizer, proportion = 0,
  735. flag = wx.ALL | wx.EXPAND, border = 3)
  736. sizer.Add(item = findSizer, proportion = 0,
  737. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  738. sizer.Add(item = treeSizer, proportion = 1,
  739. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  740. sizer.Add(item = optionSizer, proportion = 0,
  741. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  742. sizer.Add(item = btnSizer, proportion = 0,
  743. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  744. self.panel.SetSizer(sizer)
  745. sizer.Fit(self.panel)
  746. self.Layout()
  747. def _getCmd(self):
  748. item = self.tree.GetSelected()
  749. if not item or not item.IsOk():
  750. return ['g.extension']
  751. name = self.tree.GetItemText(item)
  752. if not name:
  753. gcmd.GError(_("Extension not defined"), parent = self)
  754. return
  755. flags = list()
  756. for key in self.options.keys():
  757. if self.options[key].IsChecked():
  758. flags.append('-%s' % key)
  759. return ['g.extension'] + flags + ['extension=' + name,
  760. 'svnurl=' + self.repo.GetValue().strip()]
  761. def OnUpdateStatusBar(self, event):
  762. """!Update statusbar text"""
  763. element = self.search.GetSelection()
  764. if not self.tree.IsLoaded():
  765. self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
  766. return
  767. self.tree.SearchItems(element = element,
  768. value = event.GetString())
  769. nItems = len(self.tree.itemsMarked)
  770. if event.GetString():
  771. self.SetStatusText(_("%d items match") % nItems, 0)
  772. else:
  773. self.SetStatusText("", 0)
  774. event.Skip()
  775. def OnCloseWindow(self, event):
  776. """!Close window"""
  777. self.Destroy()
  778. def OnFetch(self, event):
  779. """!Fetch list of available extensions"""
  780. self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
  781. self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
  782. self.SetStatusText("", 0)
  783. def OnItemActivated(self, event):
  784. item = event.GetItem()
  785. data = self.tree.GetPyData(item)
  786. if data and 'command' in data:
  787. self.OnInstall(event = None)
  788. def OnInstall(self, event):
  789. """!Install selected extension"""
  790. log = self.parent.GetLogWindow()
  791. log.RunCmd(self._getCmd())
  792. ### self.OnCloseWindow(None)
  793. def OnItemSelected(self, event):
  794. """!Item selected"""
  795. item = event.GetItem()
  796. self.tree.itemSelected = item
  797. data = self.tree.GetPyData(item)
  798. if not data:
  799. self.SetStatusText('', 0)
  800. self.btnInstall.Enable(False)
  801. else:
  802. self.SetStatusText(data.get('description', ''), 0)
  803. self.btnInstall.Enable(True)
  804. def OnShowItem(self, event):
  805. """!Show selected item"""
  806. self.tree.OnShowItem(event)
  807. if self.tree.GetSelected():
  808. self.btnInstall.Enable()
  809. else:
  810. self.btnInstall.Enable(False)
  811. def OnCmdDialog(self, event):
  812. """!Shows command dialog"""
  813. menuform.GUI(parent = self).ParseCommand(cmd = self._getCmd())
  814. class ExtensionTree(ItemTree):
  815. """!List of available extensions"""
  816. def __init__(self, parent, log, id = wx.ID_ANY,
  817. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  818. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
  819. **kwargs):
  820. self.parent = parent # GMFrame
  821. self.log = log
  822. super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  823. self._initTree()
  824. def _initTree(self):
  825. for prefix in ('display', 'database',
  826. 'general', 'imagery',
  827. 'misc', 'postscript', 'paint',
  828. 'raster', 'raster3D', 'sites', 'vector', 'wxGUI'):
  829. self.AppendItem(parentId = self.root,
  830. text = prefix)
  831. self._loaded = False
  832. def _expandPrefix(self, c):
  833. name = { 'd' : 'display',
  834. 'db' : 'database',
  835. 'g' : 'general',
  836. 'i' : 'imagery',
  837. 'm' : 'misc',
  838. 'ps' : 'postscript',
  839. 'p' : 'paint',
  840. 'r' : 'raster',
  841. 'r3' : 'raster3D',
  842. 's' : 'sites',
  843. 'v' : 'vector',
  844. 'wx' : 'wxGUI' }
  845. if c in name:
  846. return name[c]
  847. return c
  848. def _findItem(self, text):
  849. """!Find item"""
  850. item = self.GetFirstChild(self.root)[0]
  851. while item and item.IsOk():
  852. if text == self.GetItemText(item):
  853. return item
  854. item = self.GetNextSibling(item)
  855. return None
  856. def Load(self, url, full = False):
  857. """!Load list of extensions"""
  858. self.DeleteAllItems()
  859. self.root = self.AddRoot(_("Menu tree"))
  860. self._initTree()
  861. if full:
  862. flags = 'g'
  863. else:
  864. flags = 'l'
  865. ret = gcmd.RunCommand('g.extension', read = True,
  866. svnurl = url,
  867. flags = flags, quiet = True)
  868. if not ret:
  869. return
  870. mdict = dict()
  871. for line in ret.splitlines():
  872. if full:
  873. key, value = line.split('=', 1)
  874. if key == 'name':
  875. prefix, name = value.split('.', 1)
  876. if prefix not in mdict:
  877. mdict[prefix] = dict()
  878. mdict[prefix][name] = dict()
  879. else:
  880. mdict[prefix][name][key] = value
  881. else:
  882. try:
  883. prefix, name = line.strip().split('.', 1)
  884. except:
  885. prefix = 'unknown'
  886. name = line.strip()
  887. if self._expandPrefix(prefix) == prefix:
  888. prefix = 'unknown'
  889. if prefix not in mdict:
  890. mdict[prefix] = dict()
  891. mdict[prefix][name] = { 'command' : prefix + '.' + name }
  892. for prefix in mdict.keys():
  893. prefixName = self._expandPrefix(prefix)
  894. item = self._findItem(prefixName)
  895. names = mdict[prefix].keys()
  896. names.sort()
  897. for name in names:
  898. new = self.AppendItem(parentId = item,
  899. text = prefix + '.' + name)
  900. data = dict()
  901. for key in mdict[prefix][name].keys():
  902. data[key] = mdict[prefix][name][key]
  903. self.SetPyData(new, data)
  904. self._loaded = True
  905. def IsLoaded(self):
  906. """Check if items are loaded"""
  907. return self._loaded
  908. class HelpWindow(wx.html.HtmlWindow):
  909. """!This panel holds the text from GRASS docs.
  910. GISBASE must be set in the environment to find the html docs dir.
  911. The SYNOPSIS section is skipped, since this Panel is supposed to
  912. be integrated into the cmdPanel and options are obvious there.
  913. """
  914. def __init__(self, parent, grass_command, text, skip_description,
  915. **kwargs):
  916. """!If grass_command is given, the corresponding HTML help
  917. file will be presented, with all links pointing to absolute
  918. paths of local files.
  919. If 'skip_description' is True, the HTML corresponding to
  920. SYNOPSIS will be skipped, thus only presenting the help file
  921. from the DESCRIPTION section onwards.
  922. If 'text' is given, it must be the HTML text to be presented
  923. in the Panel.
  924. """
  925. self.parent = parent
  926. wx.InitAllImageHandlers()
  927. wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs)
  928. gisbase = os.getenv("GISBASE")
  929. self.loaded = False
  930. self.history = list()
  931. self.historyIdx = 0
  932. self.fspath = os.path.join(gisbase, "docs", "html")
  933. self.SetStandardFonts (size = 10)
  934. self.SetBorders(10)
  935. if text is None:
  936. if skip_description:
  937. url = os.path.join(self.fspath, grass_command + ".html")
  938. self.fillContentsFromFile(url,
  939. skip_description = skip_description)
  940. self.history.append(url)
  941. self.loaded = True
  942. else:
  943. ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
  944. # self.LoadPage(self.fspath + grass_command + ".html")
  945. self.loaded = False
  946. else:
  947. self.SetPage(text)
  948. self.loaded = True
  949. def OnLinkClicked(self, linkinfo):
  950. url = linkinfo.GetHref()
  951. if url[:4] != 'http':
  952. url = os.path.join(self.fspath, url)
  953. self.history.append(url)
  954. self.historyIdx += 1
  955. self.parent.OnHistory()
  956. super(HelpWindow, self).OnLinkClicked(linkinfo)
  957. def fillContentsFromFile(self, htmlFile, skip_description = True):
  958. """!Load content from file"""
  959. aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
  960. imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
  961. try:
  962. contents = []
  963. skip = False
  964. for l in file(htmlFile, "rb").readlines():
  965. if "DESCRIPTION" in l:
  966. skip = False
  967. if not skip:
  968. # do skip the options description if requested
  969. if "SYNOPSIS" in l:
  970. skip = skip_description
  971. else:
  972. # FIXME: find only first item
  973. findALink = aLink.search(l)
  974. if findALink is not None:
  975. contents.append(aLink.sub(findALink.group(1)+
  976. self.fspath+findALink.group(2),l))
  977. findImgLink = imgLink.search(l)
  978. if findImgLink is not None:
  979. contents.append(imgLink.sub(findImgLink.group(1)+
  980. self.fspath+findImgLink.group(2),l))
  981. if findALink is None and findImgLink is None:
  982. contents.append(l)
  983. self.SetPage("".join(contents))
  984. self.loaded = True
  985. except: # The Manual file was not found
  986. self.loaded = False
  987. class HelpPanel(wx.Panel):
  988. def __init__(self, parent, grass_command = "index", text = None,
  989. skip_description = False, **kwargs):
  990. self.grass_command = grass_command
  991. wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
  992. self.content = HelpWindow(self, grass_command, text,
  993. skip_description)
  994. self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
  995. label = _("&Next"))
  996. self.btnNext.Enable(False)
  997. self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
  998. label = _("&Previous"))
  999. self.btnPrev.Enable(False)
  1000. self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
  1001. self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
  1002. self._layout()
  1003. def _layout(self):
  1004. """!Do layout"""
  1005. sizer = wx.BoxSizer(wx.VERTICAL)
  1006. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  1007. btnSizer.Add(item = self.btnPrev, proportion = 0,
  1008. flag = wx.ALL, border = 5)
  1009. btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
  1010. btnSizer.Add(item = self.btnNext, proportion = 0,
  1011. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  1012. sizer.Add(item = self.content, proportion = 1,
  1013. flag = wx.EXPAND)
  1014. sizer.Add(item = btnSizer, proportion = 0,
  1015. flag = wx.EXPAND)
  1016. self.SetSizer(sizer)
  1017. sizer.Fit(self)
  1018. def LoadPage(self, path = None):
  1019. """!Load page"""
  1020. if not path:
  1021. path = os.path.join(self.content.fspath, self.grass_command + ".html")
  1022. self.content.history.append(path)
  1023. self.content.LoadPage(path)
  1024. def IsFile(self):
  1025. """!Check if file exists"""
  1026. return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html"))
  1027. def IsLoaded(self):
  1028. return self.content.loaded
  1029. def OnHistory(self):
  1030. """!Update buttons"""
  1031. nH = len(self.content.history)
  1032. iH = self.content.historyIdx
  1033. if iH == nH - 1:
  1034. self.btnNext.Enable(False)
  1035. elif iH > -1:
  1036. self.btnNext.Enable(True)
  1037. if iH < 1:
  1038. self.btnPrev.Enable(False)
  1039. else:
  1040. self.btnPrev.Enable(True)
  1041. def OnNext(self, event):
  1042. """Load next page"""
  1043. self.content.historyIdx += 1
  1044. idx = self.content.historyIdx
  1045. path = self.content.history[idx]
  1046. self.content.LoadPage(path)
  1047. self.OnHistory()
  1048. event.Skip()
  1049. def OnPrev(self, event):
  1050. """Load previous page"""
  1051. self.content.historyIdx -= 1
  1052. idx = self.content.historyIdx
  1053. path = self.content.history[idx]
  1054. self.content.LoadPage(path)
  1055. self.OnHistory()
  1056. event.Skip()