ghelp.py 32 KB

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