ghelp.py 31 KB

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