ghelp.py 46 KB

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