ghelp.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. """!
  2. @package gui_core.ghelp
  3. @brief Help/about window, menu tree, search module tree
  4. Classes:
  5. - ghelp::AboutWindow
  6. - ghelp::HelpFrame
  7. - ghelp::HelpWindow
  8. - ghelp::HelpPanel
  9. (C) 2008-2012 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Martin Landa <landa.martin gmail.com>
  13. """
  14. import os
  15. import codecs
  16. import platform
  17. import re
  18. import wx
  19. from wx.html import HtmlWindow
  20. try:
  21. from wx.lib.agw.hyperlink import HyperLinkCtrl
  22. except ImportError:
  23. from wx.lib.hyperlink import HyperLinkCtrl
  24. import grass.script as grass
  25. from core import globalvar
  26. from core.gcmd import GError, DecodeString
  27. from gui_core.widgets import FormListbook, ScrolledPanel
  28. from core.debug import Debug
  29. class AboutWindow(wx.Frame):
  30. """!Create custom About Window
  31. """
  32. def __init__(self, parent, size = (650, 460),
  33. title = _('About GRASS GIS'), **kwargs):
  34. wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, title = title, size = size, **kwargs)
  35. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  36. # icon
  37. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  38. # notebook
  39. self.aboutNotebook = FormListbook(self.panel, style = wx.BK_LEFT)
  40. for title, win in ((_("Info"), self._pageInfo()),
  41. (_("Copyright"), self._pageCopyright()),
  42. (_("License"), self._pageLicense()),
  43. (_("Authors"), self._pageCredit()),
  44. (_("Contributors"), self._pageContributors()),
  45. (_("Extra contributors"), self._pageContributors(extra = True)),
  46. (_("Translators"), self._pageTranslators()),
  47. (_("Translation status"), self._pageStats())):
  48. self.aboutNotebook.AddPage(page = win, text = title)
  49. self.aboutNotebook.Refresh()
  50. wx.CallAfter(self.aboutNotebook.SetSelection, 0)
  51. # buttons
  52. self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  53. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  54. self._doLayout()
  55. def _doLayout(self):
  56. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  57. btnSizer.Add(item = self.btnClose, proportion = 0,
  58. flag = wx.ALL | wx.ALIGN_RIGHT,
  59. border = 5)
  60. sizer = wx.BoxSizer(wx.VERTICAL)
  61. sizer.Add(item = self.aboutNotebook, proportion = 1,
  62. flag = wx.EXPAND | wx.ALL, border = 1)
  63. sizer.Add(item = btnSizer, proportion = 0,
  64. flag = wx.ALL | wx.ALIGN_RIGHT, border = 1)
  65. self.SetMinSize((400, 400))
  66. self.panel.SetSizer(sizer)
  67. sizer.Fit(self.panel)
  68. self.Layout()
  69. def _pageInfo(self):
  70. """!Info page"""
  71. # get version and web site
  72. vInfo = grass.version()
  73. infoTxt = ScrolledPanel(self.aboutNotebook)
  74. infoTxt.SetBackgroundColour('WHITE')
  75. infoTxt.SetupScrolling()
  76. infoSizer = wx.BoxSizer(wx.VERTICAL)
  77. infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
  78. infoGridSizer.AddGrowableCol(0)
  79. infoGridSizer.AddGrowableCol(1)
  80. logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass-64x64.png")
  81. logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
  82. bitmap = wx.Bitmap(name = logo,
  83. type = wx.BITMAP_TYPE_PNG))
  84. infoSizer.Add(item = logoBitmap, proportion = 0,
  85. flag = wx.ALL | wx.ALIGN_CENTER, border = 20)
  86. info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  87. label = 'GRASS GIS ' + vInfo['version'] + '\n\n')
  88. info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  89. info.SetForegroundColour(wx.Colour(35, 142, 35))
  90. infoSizer.Add(item = info, proportion = 0,
  91. flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 1)
  92. row = 0
  93. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  94. label = _('Official GRASS site:')),
  95. pos = (row, 0),
  96. flag = wx.ALIGN_RIGHT)
  97. infoGridSizer.Add(item = HyperLinkCtrl(parent = infoTxt, id = wx.ID_ANY,
  98. label = 'http://grass.osgeo.org'),
  99. pos = (row, 1),
  100. flag = wx.ALIGN_LEFT)
  101. row += 2
  102. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  103. label = '%s:' % _('SVN Revision')),
  104. pos = (row, 0),
  105. flag = wx.ALIGN_RIGHT)
  106. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  107. label = vInfo['revision']),
  108. pos = (row, 1),
  109. flag = wx.ALIGN_LEFT)
  110. row += 1
  111. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  112. label = '%s:' % _('GIS Library Revision')),
  113. pos = (row, 0),
  114. flag = wx.ALIGN_RIGHT)
  115. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  116. label = vInfo['libgis_revision'] + ' (' +
  117. vInfo['libgis_date'].split(' ')[0] + ')'),
  118. pos = (row, 1),
  119. flag = wx.ALIGN_LEFT)
  120. row += 2
  121. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  122. label = 'Python:'),
  123. pos = (row, 0),
  124. flag = wx.ALIGN_RIGHT)
  125. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  126. label = platform.python_version()),
  127. pos = (row, 1),
  128. flag = wx.ALIGN_LEFT)
  129. row += 1
  130. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  131. label = 'wxPython:'),
  132. pos = (row, 0),
  133. flag = wx.ALIGN_RIGHT)
  134. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  135. label = wx.__version__),
  136. pos = (row, 1),
  137. flag = wx.ALIGN_LEFT)
  138. infoSizer.Add(item = infoGridSizer,
  139. proportion = 1,
  140. flag = wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
  141. row += 2
  142. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  143. label = "%s:" % _('Language')),
  144. pos = (row, 0),
  145. flag = wx.ALIGN_RIGHT)
  146. self.langUsed = grass.gisenv().get('LANG', None)
  147. if not self.langUsed:
  148. import locale
  149. loc = locale.getdefaultlocale()
  150. if loc == (None, None):
  151. self.langUsed = _('unknown')
  152. else:
  153. self.langUsed = u'%s.%s' % (loc[0], loc[1])
  154. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  155. label = self.langUsed),
  156. pos = (row, 1),
  157. flag = wx.ALIGN_LEFT)
  158. infoTxt.SetSizer(infoSizer)
  159. infoSizer.Fit(infoTxt)
  160. return infoTxt
  161. def _pageCopyright(self):
  162. """Copyright information"""
  163. copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
  164. if os.path.exists(copyfile):
  165. copyrightFile = open(copyfile, 'r')
  166. copytext = copyrightFile.read()
  167. copyrightFile.close()
  168. else:
  169. copytext = _('%s file missing') % 'COPYING'
  170. # put text into a scrolling panel
  171. copyrightwin = ScrolledPanel(self.aboutNotebook)
  172. copyrightwin.SetBackgroundColour('WHITE')
  173. copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext)
  174. copyrightwin.SetAutoLayout(True)
  175. copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
  176. copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1,
  177. flag = wx.EXPAND | wx.ALL, border = 3)
  178. copyrightwin.SetSizer(copyrightwin.sizer)
  179. copyrightwin.Layout()
  180. copyrightwin.SetupScrolling()
  181. return copyrightwin
  182. def _pageLicense(self):
  183. """Licence about"""
  184. licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
  185. if os.path.exists(licfile):
  186. licenceFile = open(licfile, 'r')
  187. license = ''.join(licenceFile.readlines())
  188. licenceFile.close()
  189. else:
  190. license = _('%s file missing') % 'GPL.TXT'
  191. # put text into a scrolling panel
  192. licensewin = ScrolledPanel(self.aboutNotebook)
  193. licensewin.SetBackgroundColour('WHITE')
  194. licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license)
  195. licensewin.SetAutoLayout(True)
  196. licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
  197. licensewin.sizer.Add(item = licensetxt, proportion = 1,
  198. flag = wx.EXPAND | wx.ALL, border = 3)
  199. licensewin.SetSizer(licensewin.sizer)
  200. licensewin.Layout()
  201. licensewin.SetupScrolling()
  202. return licensewin
  203. def _pageCredit(self):
  204. """Credit about"""
  205. # credits
  206. authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
  207. if os.path.exists(authfile):
  208. authorsFile = open(authfile, 'r')
  209. authors = unicode(''.join(authorsFile.readlines()), "utf-8")
  210. authorsFile.close()
  211. else:
  212. authors = _('%s file missing') % 'AUTHORS'
  213. authorwin = ScrolledPanel(self.aboutNotebook)
  214. authorwin.SetBackgroundColour('WHITE')
  215. authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
  216. authorwin.SetAutoLayout(True)
  217. authorwin.SetupScrolling()
  218. authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
  219. authorwin.sizer.Add(item = authortxt, proportion = 1,
  220. flag = wx.EXPAND | wx.ALL, border = 3)
  221. authorwin.SetSizer(authorwin.sizer)
  222. authorwin.Layout()
  223. return authorwin
  224. def _pageContributors(self, extra = False):
  225. """Contributors info"""
  226. if extra:
  227. contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
  228. else:
  229. contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
  230. if os.path.exists(contribfile):
  231. contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
  232. contribs = list()
  233. errLines = list()
  234. for line in contribFile.readlines()[1:]:
  235. line = line.rstrip('\n')
  236. try:
  237. if extra:
  238. name, email, country, rfc2_agreed = line.split(',')
  239. else:
  240. cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
  241. except ValueError:
  242. errLines.append(line)
  243. continue
  244. if extra:
  245. contribs.append((name, email, country))
  246. else:
  247. contribs.append((name, email, country, osgeo_id))
  248. contribFile.close()
  249. if errLines:
  250. GError(parent = self,
  251. message = _("Error when reading file '%s'.") % contribfile + \
  252. "\n\n" + _("Lines:") + " %s" % \
  253. os.linesep.join(map(DecodeString, errLines)))
  254. else:
  255. contribs = None
  256. contribwin = ScrolledPanel(self.aboutNotebook)
  257. contribwin.SetBackgroundColour('WHITE')
  258. contribwin.SetAutoLayout(True)
  259. contribwin.SetupScrolling()
  260. contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
  261. if not contribs:
  262. contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
  263. label = _('%s file missing') % contribfile)
  264. contribwin.sizer.Add(item = contribtxt, proportion = 1,
  265. flag = wx.EXPAND | wx.ALL, border = 3)
  266. else:
  267. if extra:
  268. items = (_('Name'), _('E-mail'), _('Country'))
  269. else:
  270. items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
  271. contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5)
  272. for item in items:
  273. text = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  274. label = item)
  275. text.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  276. contribBox.Add(item = text)
  277. for vals in sorted(contribs, key = lambda x: x[0]):
  278. for item in vals:
  279. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  280. label = item))
  281. contribwin.sizer.Add(item = contribBox, proportion = 1,
  282. flag = wx.EXPAND | wx.ALL, border = 3)
  283. contribwin.SetSizer(contribwin.sizer)
  284. contribwin.Layout()
  285. return contribwin
  286. def _pageTranslators(self):
  287. """Translators info"""
  288. translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
  289. if os.path.exists(translatorsfile):
  290. translatorsFile = open(translatorsfile, 'r')
  291. translators = dict()
  292. errLines = list()
  293. for line in translatorsFile.readlines()[1:]:
  294. line = line.rstrip('\n')
  295. try:
  296. name, email, languages = line.split(',')
  297. except ValueError:
  298. errLines.append(line)
  299. continue
  300. for language in languages.split(' '):
  301. if language not in translators:
  302. translators[language] = list()
  303. translators[language].append((name, email))
  304. translatorsFile.close()
  305. if errLines:
  306. GError(parent = self,
  307. message = _("Error when reading file '%s'.") % translatorsfile + \
  308. "\n\n" + _("Lines:") + " %s" % \
  309. os.linesep.join(map(DecodeString, errLines)))
  310. else:
  311. translators = None
  312. translatorswin = ScrolledPanel(self.aboutNotebook)
  313. translatorswin.SetBackgroundColour('WHITE')
  314. translatorswin.SetAutoLayout(True)
  315. translatorswin.SetupScrolling()
  316. translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
  317. if not translators:
  318. translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY,
  319. label = _('%s file missing') % 'translators.csv')
  320. translatorswin.sizer.Add(item = translatorstxt, proportion = 1,
  321. flag = wx.EXPAND | wx.ALL, border = 3)
  322. else:
  323. translatorsBox = wx.FlexGridSizer(cols = 4, vgap = 5, hgap = 5)
  324. languages = translators.keys()
  325. languages.sort()
  326. tname = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  327. label = _('Name'))
  328. tname.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  329. translatorsBox.Add(item = tname)
  330. temail = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  331. label = _('E-mail'))
  332. temail.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  333. translatorsBox.Add(item = temail)
  334. tlang = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  335. label = _('Language'))
  336. tlang.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  337. translatorsBox.Add(item = tlang)
  338. tnat = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  339. label = _('Nation'))
  340. tnat.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  341. translatorsBox.Add(item = tnat)
  342. for lang in languages:
  343. for translator in translators[lang]:
  344. name, email = translator
  345. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  346. label = unicode(name, "utf-8")))
  347. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  348. label = email))
  349. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  350. label = lang))
  351. flag = os.path.join(os.getenv("GISBASE"), "etc", "gui",
  352. "icons", "flags", "%s.png" % lang.lower())
  353. if os.path.exists(flag):
  354. flagBitmap = wx.StaticBitmap(parent = translatorswin, id = wx.ID_ANY,
  355. bitmap = wx.Bitmap(name = flag,
  356. type = wx.BITMAP_TYPE_PNG))
  357. translatorsBox.Add(item = flagBitmap)
  358. else:
  359. translatorsBox.Add(item = wx.StaticText(parent = translatorswin,
  360. id = wx.ID_ANY, label = lang))
  361. translatorswin.sizer.Add(item = translatorsBox, proportion = 1,
  362. flag = wx.EXPAND | wx.ALL, border = 3)
  363. translatorswin.SetSizer(translatorswin.sizer)
  364. translatorswin.Layout()
  365. return translatorswin
  366. def _langString(self, k, v):
  367. """Return string for the status of translation"""
  368. allStr = "%s :" % k.upper()
  369. try:
  370. allStr += _(" %d translated" % v['good'])
  371. except:
  372. pass
  373. try:
  374. allStr += _(" %d fuzzy" % v['fuzzy'])
  375. except:
  376. pass
  377. try:
  378. allStr += _(" %d untranslated" % v['bad'])
  379. except:
  380. pass
  381. return allStr
  382. def _langBox(self, par, k, v):
  383. """Return box"""
  384. langBox = wx.FlexGridSizer(cols = 4, vgap = 5, hgap = 5)
  385. tkey = wx.StaticText(parent = par, id = wx.ID_ANY,
  386. label = k.upper())
  387. langBox.Add(item = tkey)
  388. try:
  389. tgood = wx.StaticText(parent = par, id = wx.ID_ANY,
  390. label = _("%d translated" % v['good']))
  391. tgood.SetForegroundColour(wx.Colour(35, 142, 35))
  392. langBox.Add(item = tgood)
  393. except:
  394. tgood = wx.StaticText(parent = par, id = wx.ID_ANY,
  395. label = "")
  396. langBox.Add(item = tgood)
  397. try:
  398. tfuzzy = wx.StaticText(parent = par, id = wx.ID_ANY,
  399. label = _(" %d fuzzy" % v['fuzzy']))
  400. tfuzzy.SetForegroundColour(wx.Colour(255, 142, 0))
  401. langBox.Add(item = tfuzzy)
  402. except:
  403. tfuzzy = wx.StaticText(parent = par, id = wx.ID_ANY,
  404. label = "")
  405. langBox.Add(item = tfuzzy)
  406. try:
  407. tbad = wx.StaticText(parent = par, id = wx.ID_ANY,
  408. label = _(" %d untranslated" % v['bad']))
  409. tbad.SetForegroundColour(wx.Colour(255, 0, 0))
  410. langBox.Add(item = tbad)
  411. except:
  412. tbad = wx.StaticText(parent = par, id = wx.ID_ANY,
  413. label = "")
  414. langBox.Add(item = tbad)
  415. return langBox
  416. def _langPanel(self, lang, js):
  417. """Create panel for each languages"""
  418. text = self._langString(lang, js['total'])
  419. panel = wx.CollapsiblePane(self.statswin, -1, label=text, style=wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE)
  420. panel.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged)
  421. win = panel.GetPane()
  422. # TODO IT DOESN'T WORK
  423. # TO ADD ONLY WHEN TAB IS OPENED
  424. #if lang == self.langUsed.split('_')[0]:
  425. #panel.Collapse(False)
  426. #else:
  427. #panel.Collapse(True)
  428. pageSizer = wx.BoxSizer(wx.VERTICAL)
  429. for k,v in js.iteritems():
  430. if k != 'total' and k!= 'name':
  431. box = self._langBox(win, k,v)
  432. pageSizer.Add(item = box, proportion = 1,
  433. flag = wx.EXPAND | wx.ALL, border = 3)
  434. win.SetSizer(pageSizer)
  435. pageSizer.SetSizeHints(win)
  436. return panel
  437. def OnPaneChanged(self, evt):
  438. """Redo the layout"""
  439. # TODO better to test on Windows
  440. self.statswin.SetupScrolling(scrollToTop = False)
  441. def _pageStats(self):
  442. """Translation statistics info"""
  443. fname = "translation_status.json"
  444. statsfile = os.path.join(os.getenv("GISBASE"), fname)
  445. if os.path.exists(statsfile):
  446. statsFile = open(statsfile)
  447. import json
  448. jsStats = json.load(statsFile)
  449. else:
  450. jsStats = None
  451. self.statswin = ScrolledPanel(self.aboutNotebook)
  452. self.statswin.SetBackgroundColour('WHITE')
  453. self.statswin.SetAutoLayout(True)
  454. if not jsStats:
  455. Debug.msg(5, _("File <%s> not found") % fname)
  456. statsSizer = wx.BoxSizer(wx.VERTICAL)
  457. statstext = wx.StaticText(self.statswin, id = wx.ID_ANY,
  458. label = _('%s file missing') % fname)
  459. statsSizer.Add(item = statstext, proportion = 1,
  460. flag = wx.EXPAND | wx.ALL, border = 3)
  461. else:
  462. languages = jsStats['langs'].keys()
  463. languages.sort()
  464. statsSizer = wx.BoxSizer(wx.VERTICAL)
  465. for lang in languages:
  466. v = jsStats['langs'][lang]
  467. panel = self._langPanel(lang, v)
  468. statsSizer.Add(panel)
  469. self.statswin.SetSizer(statsSizer)
  470. self.statswin.SetupScrolling(scroll_x = False, scroll_y = True)
  471. self.statswin.Layout()
  472. self.statswin.Fit()
  473. return self.statswin
  474. def OnCloseWindow(self, event):
  475. """!Close window"""
  476. self.Close()
  477. class HelpFrame(wx.Dialog):
  478. """!GRASS Quickstart help window
  479. As a base class wx.Dialog is used, because of not working
  480. close button with wx.Frame when dialog is called from wizard.
  481. If parent is None, application TopLevelWindow is used (wxPython standard behaviour).
  482. Currently not used (was in location wizard before)
  483. due to unsolved problems - window sometimes does not respond.
  484. """
  485. def __init__(self, parent, id, title, size, file):
  486. wx.Dialog.__init__(self, parent = parent, id = id, title = title,
  487. size = size, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MINIMIZE_BOX)
  488. sizer = wx.BoxSizer(wx.VERTICAL)
  489. # text
  490. content = HelpPanel(parent = self)
  491. content.LoadPage(file)
  492. sizer.Add(item = content, proportion = 1, flag = wx.EXPAND)
  493. self.SetAutoLayout(True)
  494. self.SetSizer(sizer)
  495. self.Layout()
  496. class HelpWindow(HtmlWindow):
  497. """!This panel holds the text from GRASS docs.
  498. GISBASE must be set in the environment to find the html docs dir.
  499. The SYNOPSIS section is skipped, since this Panel is supposed to
  500. be integrated into the cmdPanel and options are obvious there.
  501. """
  502. def __init__(self, parent, command, text, skipDescription,
  503. **kwargs):
  504. """!If command is given, the corresponding HTML help
  505. file will be presented, with all links pointing to absolute
  506. paths of local files.
  507. If 'skipDescription' is True, the HTML corresponding to
  508. SYNOPSIS will be skipped, thus only presenting the help file
  509. from the DESCRIPTION section onwards.
  510. If 'text' is given, it must be the HTML text to be presented
  511. in the Panel.
  512. """
  513. self.parent = parent
  514. wx.InitAllImageHandlers()
  515. HtmlWindow.__init__(self, parent = parent, **kwargs)
  516. self.loaded = False
  517. self.history = list()
  518. self.historyIdx = 0
  519. self.fspath = os.path.join(os.getenv("GISBASE"), "docs", "html")
  520. self.SetStandardFonts (size = 10)
  521. self.SetBorders(10)
  522. if text is None:
  523. if skipDescription:
  524. url = os.path.join(self.fspath, command + ".html")
  525. self.fillContentsFromFile(url,
  526. skipDescription = skipDescription)
  527. self.history.append(url)
  528. self.loaded = True
  529. else:
  530. ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
  531. # self.LoadPage(self.fspath + command + ".html")
  532. self.loaded = False
  533. else:
  534. self.SetPage(text)
  535. self.loaded = True
  536. def OnLinkClicked(self, linkinfo):
  537. url = linkinfo.GetHref()
  538. if url[:4] != 'http':
  539. url = os.path.join(self.fspath, url)
  540. self.history.append(url)
  541. self.historyIdx += 1
  542. self.parent.OnHistory()
  543. super(HelpWindow, self).OnLinkClicked(linkinfo)
  544. def fillContentsFromFile(self, htmlFile, skipDescription = True):
  545. """!Load content from file.
  546. Currently not used.
  547. """
  548. aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
  549. imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
  550. try:
  551. contents = []
  552. skip = False
  553. for l in file(htmlFile, "rb").readlines():
  554. if "DESCRIPTION" in l:
  555. skip = False
  556. if not skip:
  557. # do skip the options description if requested
  558. if "SYNOPSIS" in l:
  559. skip = skipDescription
  560. else:
  561. # FIXME: find only first item
  562. findALink = aLink.search(l)
  563. if findALink is not None:
  564. contents.append(aLink.sub(findALink.group(1)+
  565. self.fspath+findALink.group(2),l))
  566. findImgLink = imgLink.search(l)
  567. if findImgLink is not None:
  568. contents.append(imgLink.sub(findImgLink.group(1)+
  569. self.fspath+findImgLink.group(2),l))
  570. if findALink is None and findImgLink is None:
  571. contents.append(l)
  572. self.SetPage("".join(contents))
  573. self.loaded = True
  574. except: # The Manual file was not found
  575. self.loaded = False
  576. class HelpPanel(wx.Panel):
  577. def __init__(self, parent, command = "index", text = None,
  578. skipDescription = False, **kwargs):
  579. self.command = command
  580. wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
  581. self.content = HelpWindow(self, command, text, skipDescription)
  582. self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
  583. label = _("&Next"))
  584. self.btnNext.Enable(False)
  585. self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
  586. label = _("&Previous"))
  587. self.btnPrev.Enable(False)
  588. self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
  589. self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
  590. self._layout()
  591. def _layout(self):
  592. """!Do layout"""
  593. sizer = wx.BoxSizer(wx.VERTICAL)
  594. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  595. btnSizer.Add(item = self.btnPrev, proportion = 0,
  596. flag = wx.ALL, border = 5)
  597. btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
  598. btnSizer.Add(item = self.btnNext, proportion = 0,
  599. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  600. sizer.Add(item = self.content, proportion = 1,
  601. flag = wx.EXPAND)
  602. sizer.Add(item = btnSizer, proportion = 0,
  603. flag = wx.EXPAND)
  604. self.SetSizer(sizer)
  605. sizer.Fit(self)
  606. def LoadPage(self, path = None):
  607. """!Load page"""
  608. if not path:
  609. path = self.GetFile()
  610. self.content.history.append(path)
  611. self.content.LoadPage(path)
  612. def GetFile(self):
  613. """!Get HTML file"""
  614. fMan = os.path.join(self.content.fspath, self.command + ".html")
  615. if os.path.isfile(fMan):
  616. return fMan
  617. # check also addons
  618. faMan = os.path.join(os.getenv('GRASS_ADDON_BASE'), "docs", "html",
  619. self.command + ".html")
  620. if os.getenv('GRASS_ADDON_BASE') and \
  621. os.path.isfile(faMan):
  622. return faMan
  623. return None
  624. def IsLoaded(self):
  625. return self.content.loaded
  626. def OnHistory(self):
  627. """!Update buttons"""
  628. nH = len(self.content.history)
  629. iH = self.content.historyIdx
  630. if iH == nH - 1:
  631. self.btnNext.Enable(False)
  632. elif iH > -1:
  633. self.btnNext.Enable(True)
  634. if iH < 1:
  635. self.btnPrev.Enable(False)
  636. else:
  637. self.btnPrev.Enable(True)
  638. def OnNext(self, event):
  639. """Load next page"""
  640. self.content.historyIdx += 1
  641. idx = self.content.historyIdx
  642. path = self.content.history[idx]
  643. self.content.LoadPage(path)
  644. self.OnHistory()
  645. event.Skip()
  646. def OnPrev(self, event):
  647. """Load previous page"""
  648. self.content.historyIdx -= 1
  649. idx = self.content.historyIdx
  650. path = self.content.history[idx]
  651. self.content.LoadPage(path)
  652. self.OnHistory()
  653. event.Skip()