GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
ghelp.py
Go to the documentation of this file.
00001 """!
00002 @package gui_core.ghelp
00003 
00004 @brief Help window
00005 
00006 Classes:
00007  - ghelp::SearchModuleWindow
00008  - ghelp::MenuTreeWindow
00009  - ghelp::MenuTree
00010  - ghelp::AboutWindow
00011  - ghelp::HelpFrame
00012  - ghelp::HelpWindow
00013  - ghelp::HelpPanel
00014 
00015 (C) 2008-2011 by the GRASS Development Team
00016 
00017 This program is free software under the GNU General Public License
00018 (>=v2). Read the file COPYING that comes with GRASS for details.
00019 
00020 @author Martin Landa <landa.martin gmail.com>
00021 """
00022 
00023 import os
00024 import sys
00025 import codecs
00026 import platform
00027 
00028 import wx
00029 from wx.html import HtmlWindow
00030 try:
00031     import wx.lib.agw.customtreectrl as CT
00032     from wx.lib.agw.hyperlink import HyperLinkCtrl
00033 except ImportError:
00034     import wx.lib.customtreectrl as CT
00035     from wx.lib.hyperlink import HyperLinkCtrl
00036 import wx.lib.flatnotebook as FN
00037 
00038 import grass.script as grass
00039 
00040 from core             import globalvar
00041 from core             import utils
00042 from lmgr.menudata    import ManagerData
00043 from core.gcmd        import GError, DecodeString
00044 from gui_core.widgets import GNotebook, StaticWrapText, ItemTree, ScrolledPanel
00045 
00046 class SearchModuleWindow(wx.Panel):
00047     """!Search module window (used in MenuTreeWindow)"""
00048     def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None,
00049                  showChoice = True, showTip = False, **kwargs):
00050         self.showTip    = showTip
00051         self.showChoice = showChoice
00052         self.cmdPrompt  = cmdPrompt
00053         
00054         wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
00055         
00056         self._searchDict = { _('description') : 'description',
00057                              _('command')     : 'command',
00058                              _('keywords')    : 'keywords' }
00059         
00060         self.box = wx.StaticBox(parent = self, id = wx.ID_ANY,
00061                                 label = " %s " % _("Find module(s)"))
00062         
00063         self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
00064                                   choices = [_('description'),
00065                                              _('keywords'),
00066                                              _('command')])
00067         self.searchBy.SetSelection(0)
00068         
00069         self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
00070                                   value = "", size = (-1, 25),
00071                                   style = wx.TE_PROCESS_ENTER)
00072         self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
00073         
00074         if self.showTip:
00075             self.searchTip = StaticWrapText(parent = self, id = wx.ID_ANY,
00076                                             size = (-1, 35))
00077         
00078         if self.showChoice:
00079             self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY)
00080             if self.cmdPrompt:
00081                 self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
00082             self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)
00083         
00084         self._layout()
00085 
00086     def _layout(self):
00087         """!Do layout"""
00088         sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
00089         gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
00090         gridSizer.AddGrowableCol(1)
00091         
00092         gridSizer.Add(item = self.searchBy,
00093                       flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
00094         gridSizer.Add(item = self.search,
00095                       flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1))
00096         row = 1
00097         if self.showTip:
00098             gridSizer.Add(item = self.searchTip,
00099                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
00100             row += 1
00101         
00102         if self.showChoice:
00103             gridSizer.Add(item = self.searchChoice,
00104                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
00105         
00106         sizer.Add(item = gridSizer, proportion = 1)
00107         
00108         self.SetSizer(sizer)
00109         sizer.Fit(self)
00110 
00111     def GetSelection(self):
00112         """!Get selected element"""
00113         selection = self.searchBy.GetStringSelection()
00114         
00115         return self._searchDict[selection]
00116 
00117     def SetSelection(self, i):
00118         """!Set selection element"""
00119         self.searchBy.SetSelection(i)
00120 
00121     def OnSearchModule(self, event):
00122         """!Search module by keywords or description"""
00123         if not self.cmdPrompt:
00124             event.Skip()
00125             return
00126         
00127         text = event.GetString()
00128         if not text:
00129             self.cmdPrompt.SetFilter(None)
00130             mList = self.cmdPrompt.GetCommandItems()
00131             self.searchChoice.SetItems(mList)
00132             if self.showTip:
00133                 self.searchTip.SetLabel(_("%d modules found") % len(mList))
00134             event.Skip()
00135             return
00136         
00137         modules = dict()
00138         iFound = 0
00139         for module, data in self.cmdPrompt.moduleDesc.iteritems():
00140             found = False
00141             sel = self.searchBy.GetSelection()
00142             if sel == 0: # -> description
00143                 if text in data['desc']:
00144                     found = True
00145             elif sel == 1: # keywords
00146                 if text in ','.join(data['keywords']):
00147                     found = True
00148             else: # command
00149                 if module[:len(text)] == text:
00150                     found = True
00151             
00152             if found:
00153                 iFound += 1
00154                 try:
00155                     group, name = module.split('.')
00156                 except ValueError:
00157                     continue # TODO
00158                 
00159                 if group not in modules:
00160                     modules[group] = list()
00161                 modules[group].append(name)
00162                 
00163         self.cmdPrompt.SetFilter(modules)
00164         self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
00165         if self.showTip:
00166             self.searchTip.SetLabel(_("%d modules found") % iFound)
00167         
00168         event.Skip()
00169         
00170     def OnSelectModule(self, event):
00171         """!Module selected from choice, update command prompt"""
00172         cmd  = event.GetString().split(' ', 1)[0]
00173         text = cmd + ' '
00174         pos = len(text)
00175 
00176         if self.cmdPrompt:
00177             self.cmdPrompt.SetText(text)
00178             self.cmdPrompt.SetSelectionStart(pos)
00179             self.cmdPrompt.SetCurrentPos(pos)
00180             self.cmdPrompt.SetFocus()
00181         
00182         desc = self.cmdPrompt.GetCommandDesc(cmd)
00183         if self.showTip:
00184             self.searchTip.SetLabel(desc)
00185     
00186     def Reset(self):
00187         """!Reset widget"""
00188         self.searchBy.SetSelection(0)
00189         self.search.SetValue('')
00190         if self.showTip:
00191             self.searchTip.SetLabel('')
00192         
00193 class MenuTreeWindow(wx.Panel):
00194     """!Show menu tree"""
00195     def __init__(self, parent, id = wx.ID_ANY, **kwargs):
00196         self.parent = parent # LayerManager
00197         
00198         wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
00199         
00200         self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
00201                                     label = " %s " % _("Menu tree (double-click to run command)"))
00202         # tree
00203         self.tree = MenuTree(parent = self, data = ManagerData())
00204         self.tree.Load()
00205 
00206         # search widget
00207         self.search = SearchModuleWindow(parent = self, showChoice = False)
00208         
00209         # buttons
00210         self.btnRun   = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
00211         self.btnRun.SetToolTipString(_("Run selected command"))
00212         self.btnRun.Enable(False)
00213         
00214         # bindings
00215         self.btnRun.Bind(wx.EVT_BUTTON,            self.OnRun)
00216         self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
00217         self.tree.Bind(wx.EVT_TREE_SEL_CHANGED,    self.OnItemSelected)
00218         self.search.Bind(wx.EVT_TEXT_ENTER,        self.OnShowItem)
00219         self.search.Bind(wx.EVT_TEXT,              self.OnUpdateStatusBar)
00220         
00221         self._layout()
00222         
00223         self.search.SetFocus()
00224         
00225     def _layout(self):
00226         """!Do dialog layout"""
00227         sizer = wx.BoxSizer(wx.VERTICAL)
00228         
00229         # body
00230         dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
00231         dataSizer.Add(item = self.tree, proportion =1,
00232                       flag = wx.EXPAND)
00233         
00234         # buttons
00235         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00236         btnSizer.Add(item = self.btnRun, proportion = 0)
00237         
00238         sizer.Add(item = dataSizer, proportion = 1,
00239                   flag = wx.EXPAND | wx.ALL, border = 5)
00240 
00241         sizer.Add(item = self.search, proportion = 0,
00242                   flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
00243         
00244         sizer.Add(item = btnSizer, proportion = 0,
00245                   flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
00246         
00247         sizer.Fit(self)
00248         sizer.SetSizeHints(self)
00249         
00250         self.SetSizer(sizer)
00251         
00252         self.Fit()
00253         self.SetAutoLayout(True)        
00254         self.Layout()
00255         
00256     def OnCloseWindow(self, event):
00257         """!Close window"""
00258         self.Destroy()
00259         
00260     def OnRun(self, event):
00261         """!Run selected command"""
00262         if not self.tree.GetSelected():
00263             return # should not happen
00264         
00265         data = self.tree.GetPyData(self.tree.GetSelected())
00266         if not data:
00267             return
00268 
00269         handler = 'self.parent.' + data['handler'].lstrip('self.')
00270         if data['handler'] == 'self.OnXTerm':
00271             wx.MessageBox(parent = self,
00272                           message = _('You must run this command from the menu or command line',
00273                                       'This command require an XTerm'),
00274                           caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00275         elif data['command']:
00276             eval(handler)(event = None, cmd = data['command'].split())
00277         else:
00278             eval(handler)(None)
00279 
00280     def OnShowItem(self, event):
00281         """!Show selected item"""
00282         self.tree.OnShowItem(event)
00283         if self.tree.GetSelected():
00284             self.btnRun.Enable()
00285         else:
00286             self.btnRun.Enable(False)
00287         
00288     def OnItemActivated(self, event):
00289         """!Item activated (double-click)"""
00290         item = event.GetItem()
00291         if not item or not item.IsOk():
00292             return
00293         
00294         data = self.tree.GetPyData(item)
00295         if not data or 'command' not in data:
00296             return
00297         
00298         self.tree.itemSelected = item
00299         
00300         self.OnRun(None)
00301         
00302     def OnItemSelected(self, event):
00303         """!Item selected"""
00304         item = event.GetItem()
00305         if not item or not item.IsOk():
00306             return
00307         
00308         data = self.tree.GetPyData(item)
00309         if not data or 'command' not in data:
00310             return
00311         
00312         if data['command']:
00313             label = data['command'] + ' -- ' + data['description']
00314         else:
00315             label = data['description']
00316         
00317         self.parent.SetStatusText(label, 0)
00318         
00319     def OnUpdateStatusBar(self, event):
00320         """!Update statusbar text"""
00321         element = self.search.GetSelection()
00322         self.tree.SearchItems(element = element,
00323                               value = event.GetString())
00324         
00325         nItems = len(self.tree.itemsMarked)
00326         if event.GetString():
00327             self.parent.SetStatusText(_("%d modules match") % nItems, 0)
00328         else:
00329             self.parent.SetStatusText("", 0)
00330         
00331         event.Skip()
00332 
00333 class MenuTree(ItemTree):
00334     """!Menu tree class"""
00335     def __init__(self, parent, data, **kwargs):
00336         self.parent   = parent
00337         self.menudata = data
00338 
00339         super(MenuTree, self).__init__(parent, **kwargs)
00340         
00341     def Load(self, data = None):
00342         """!Load menu data tree
00343 
00344         @param data menu data (None to use self.menudata)
00345         """
00346         if not data:
00347             data = self.menudata
00348         
00349         self.itemsMarked = [] # list of marked items
00350         for eachMenuData in data.GetMenu():
00351             for label, items in eachMenuData:
00352                 item = self.AppendItem(parentId = self.root,
00353                                        text = label.replace('&', ''))
00354                 self.__AppendItems(item, items)
00355         
00356     def __AppendItems(self, item, data):
00357         """!Append items into tree (used by Load()
00358         
00359         @param item tree item (parent)
00360         @parent data menu data"""
00361         for eachItem in data:
00362             if len(eachItem) == 2:
00363                 if eachItem[0]:
00364                     itemSub = self.AppendItem(parentId = item,
00365                                     text = eachItem[0])
00366                 self.__AppendItems(itemSub, eachItem[1])
00367             else:
00368                 if eachItem[0]:
00369                     itemNew = self.AppendItem(parentId = item,
00370                                               text = eachItem[0])
00371                     
00372                     data = { 'item'        : eachItem[0],
00373                              'description' : eachItem[1],
00374                              'handler'  : eachItem[2],
00375                              'command'  : eachItem[3],
00376                              'keywords' : eachItem[4] }
00377                     
00378                     self.SetPyData(itemNew, data)
00379         
00380 class AboutWindow(wx.Frame):
00381     """!Create custom About Window
00382 
00383     @todo improve styling
00384     """
00385     def __init__(self, parent, size = (750, 450), 
00386                  title = _('About GRASS GIS'), **kwargs):
00387         wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, title = title, size = size, **kwargs)
00388         
00389         panel = wx.Panel(parent = self, id = wx.ID_ANY)
00390         
00391         # icon
00392         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00393 
00394         # get version and web site
00395         vInfo = grass.version()
00396         
00397         infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
00398         infoSizer = wx.BoxSizer(wx.VERTICAL)
00399         infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
00400         infoGridSizer.AddGrowableCol(0)
00401         infoGridSizer.AddGrowableCol(1)
00402         logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass-64x64.png")
00403         logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
00404                                      bitmap = wx.Bitmap(name = logo,
00405                                                         type = wx.BITMAP_TYPE_PNG))
00406         infoSizer.Add(item = logoBitmap, proportion = 0,
00407                       flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
00408         
00409         info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00410                              label = 'GRASS GIS ' + vInfo['version'] + '\n\n')
00411         info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
00412         info.SetForegroundColour(wx.Colour(35, 142, 35))
00413         infoSizer.Add(item = info, proportion = 0,
00414                       flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
00415         
00416         row = 0
00417         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00418                                                label = _('Official GRASS site:')),
00419                           pos = (row, 0),
00420                           flag = wx.ALIGN_RIGHT)
00421 
00422         infoGridSizer.Add(item = HyperLinkCtrl(parent = infoTxt, id = wx.ID_ANY,
00423                                                label = 'http://grass.osgeo.org'),
00424                           pos = (row, 1),
00425                           flag = wx.ALIGN_LEFT)
00426 
00427         row += 2
00428         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00429                                                label = _('SVN Revision:')),
00430                           pos = (row, 0),
00431                           flag = wx.ALIGN_RIGHT)
00432         
00433         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00434                                                label = vInfo['revision']),
00435                           pos = (row, 1),
00436                           flag = wx.ALIGN_LEFT)
00437         
00438         row += 1
00439         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00440                                                label = _('GIS Library Revision:')),
00441                           pos = (row, 0),
00442                           flag = wx.ALIGN_RIGHT)
00443         
00444         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00445                                                label = vInfo['libgis_revision'] + ' (' +
00446                                                vInfo['libgis_date'].split(' ')[0] + ')'),
00447                           pos = (row, 1),
00448                           flag = wx.ALIGN_LEFT)
00449 
00450         row += 2
00451         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00452                                                label = _('Python:')),
00453                           pos = (row, 0),
00454                           flag = wx.ALIGN_RIGHT)
00455         
00456         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00457                                                label = platform.python_version()),
00458                           pos = (row, 1),
00459                           flag = wx.ALIGN_LEFT)
00460 
00461         row += 1
00462         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00463                                                label = _('wxPython:')),
00464                           pos = (row, 0),
00465                           flag = wx.ALIGN_RIGHT)
00466         
00467         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00468                                                label = wx.__version__),
00469                           pos = (row, 1),
00470                           flag = wx.ALIGN_LEFT)
00471         
00472         infoSizer.Add(item = infoGridSizer,
00473                       proportion = 1,
00474                       flag = wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
00475         
00476         # create a flat notebook for displaying information about GRASS
00477         aboutNotebook = GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 
00478         aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
00479         
00480         for title, win in ((_("Info"), infoTxt),
00481                            (_("Copyright"), self._pageCopyright()),
00482                            (_("License"), self._pageLicense()),
00483                            (_("Authors"), self._pageCredit()),
00484                            (_("Contributors"), self._pageContributors()),
00485                            (_("Extra contributors"), self._pageContributors(extra = True)),
00486                            (_("Translators"), self._pageTranslators())):
00487             aboutNotebook.AddPage(page = win, text = title)
00488         wx.CallAfter(aboutNotebook.SetSelection, 0)
00489         
00490         # buttons
00491         btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
00492         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00493         btnSizer.Add(item = btnClose, proportion = 0,
00494                      flag = wx.ALL | wx.ALIGN_RIGHT,
00495                      border = 5)
00496         # bindings
00497         btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
00498         
00499         infoTxt.SetSizer(infoSizer)
00500         infoSizer.Fit(infoTxt)
00501         
00502         sizer = wx.BoxSizer(wx.VERTICAL)
00503         sizer.Add(item = aboutNotebook, proportion = 1,
00504                   flag = wx.EXPAND | wx.ALL, border = 1)
00505         sizer.Add(item = btnSizer, proportion = 0,
00506                   flag = wx.ALL | wx.ALIGN_RIGHT, border = 1)
00507         panel.SetSizer(sizer)
00508         
00509         self.Layout()
00510         self.SetMinSize((500, 400))
00511         
00512     def _pageCopyright(self):
00513         """Copyright information"""
00514         copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
00515         if os.path.exists(copyfile):
00516             copyrightFile = open(copyfile, 'r')
00517             copytext = copyrightFile.read()
00518             copyrightFile.close()
00519         else:
00520             copytext = _('%s file missing') % 'COPYING'
00521         
00522         # put text into a scrolling panel
00523         copyrightwin = ScrolledPanel(self)
00524                                      
00525         copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext)
00526         copyrightwin.SetAutoLayout(True)
00527         copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
00528         copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1,
00529                                flag = wx.EXPAND | wx.ALL, border = 3)
00530         copyrightwin.SetSizer(copyrightwin.sizer)
00531         copyrightwin.Layout()
00532         copyrightwin.SetupScrolling()
00533         
00534         return copyrightwin
00535     
00536     def _pageLicense(self):
00537         """Licence about"""
00538         licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
00539         if os.path.exists(licfile):
00540             licenceFile = open(licfile, 'r')
00541             license = ''.join(licenceFile.readlines())
00542             licenceFile.close()
00543         else:
00544             license = _('%s file missing') % 'GPL.TXT'
00545         # put text into a scrolling panel
00546         licensewin = ScrolledPanel(self)
00547         licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license)
00548         licensewin.SetAutoLayout(True)
00549         licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
00550         licensewin.sizer.Add(item = licensetxt, proportion = 1,
00551                 flag = wx.EXPAND | wx.ALL, border = 3)
00552         licensewin.SetSizer(licensewin.sizer)
00553         licensewin.Layout()
00554         licensewin.SetupScrolling()
00555         
00556         return licensewin
00557     
00558     def _pageCredit(self):
00559         """Credit about"""
00560                 # credits
00561         authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
00562         if os.path.exists(authfile):
00563             authorsFile = open(authfile, 'r')
00564             authors = unicode(''.join(authorsFile.readlines()), "utf-8")
00565             authorsFile.close()
00566         else:
00567             authors = _('%s file missing') % 'AUTHORS'
00568         authorwin = ScrolledPanel(self)
00569         authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
00570         authorwin.SetAutoLayout(True)
00571         authorwin.SetupScrolling()
00572         authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
00573         authorwin.sizer.Add(item = authortxt, proportion = 1,
00574                 flag = wx.EXPAND | wx.ALL, border = 3)
00575         authorwin.SetSizer(authorwin.sizer)
00576         authorwin.Layout()      
00577         
00578         return authorwin
00579 
00580     def _pageContributors(self, extra = False):
00581         """Contributors info"""
00582         if extra:
00583             contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
00584         else:
00585             contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
00586         if os.path.exists(contribfile):
00587             contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
00588             contribs = list()
00589             errLines = list()
00590             for line in contribFile.readlines()[1:]:
00591                 line = line.rstrip('\n')
00592                 try:
00593                     if extra:
00594                         name, email, rfc2_agreed = line.split(',')
00595                     else:
00596                         cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
00597                 except ValueError:
00598                     errLines.append(line)
00599                     continue
00600                 if extra:
00601                     contribs.append((name, email))
00602                 else:
00603                     contribs.append((name, email, country, osgeo_id))
00604             
00605             contribFile.close()
00606             
00607             if errLines:
00608                 GError(parent = self,
00609                        message = _("Error when reading file '%s'.") % contribfile + \
00610                            "\n\n" + _("Lines:") + " %s" % \
00611                            os.linesep.join(map(DecodeString, errLines)))
00612         else:
00613             contribs = None
00614         
00615         contribwin = ScrolledPanel(self)
00616         contribwin.SetAutoLayout(True)
00617         contribwin.SetupScrolling()
00618         contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
00619         
00620         if not contribs:
00621             contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
00622                                        label = _('%s file missing') % contribfile)
00623             contribwin.sizer.Add(item = contribtxt, proportion = 1,
00624                                  flag = wx.EXPAND | wx.ALL, border = 3)
00625         else:
00626             if extra:
00627                 items = (_('Name'), _('E-mail'))
00628             else:
00629                 items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
00630             contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5)
00631             for item in items:
00632                 contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
00633                                                     label = item))
00634             for vals in sorted(contribs, key = lambda x: x[0]):
00635                 for item in vals:
00636                     contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
00637                                                         label = item))
00638             contribwin.sizer.Add(item = contribBox, proportion = 1,
00639                                  flag = wx.EXPAND | wx.ALL, border = 3)
00640         
00641         contribwin.SetSizer(contribwin.sizer)
00642         contribwin.Layout()      
00643         
00644         return contribwin
00645 
00646     def _pageTranslators(self):
00647         """Translators info"""
00648         translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
00649         if os.path.exists(translatorsfile):
00650             translatorsFile = open(translatorsfile, 'r')
00651             translators = dict()
00652             errLines = list()
00653             for line in translatorsFile.readlines()[1:]:
00654                 line = line.rstrip('\n')
00655                 try:
00656                     name, email, languages = line.split(',')
00657                 except ValueError:
00658                     errLines.append(line)
00659                     continue
00660                 for language in languages.split(' '):
00661                     if language not in translators:
00662                         translators[language] = list()
00663                     translators[language].append((name, email))
00664             translatorsFile.close()
00665             
00666             if errLines:
00667                 GError(parent = self,
00668                        message = _("Error when reading file '%s'.") % translatorsfile + \
00669                            "\n\n" + _("Lines:") + " %s" % \
00670                            os.linesep.join(map(DecodeString, errLines)))
00671         else:
00672             translators = None
00673         
00674         translatorswin = ScrolledPanel(self)
00675         translatorswin.SetAutoLayout(True)
00676         translatorswin.SetupScrolling()
00677         translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
00678         
00679         if not translators:
00680             translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY,
00681                                            label = _('%s file missing') % 'translators.csv')
00682             translatorswin.sizer.Add(item = translatorstxt, proportion = 1,
00683                                  flag = wx.EXPAND | wx.ALL, border = 3)
00684         else:
00685             translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
00686             languages = translators.keys()
00687             languages.sort()
00688             translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00689                                                     label = _('Name')))
00690             translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00691                                                     label = _('E-mail')))
00692             translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00693                                                     label = _('Language')))
00694             for lang in languages:
00695                 for translator in translators[lang]:
00696                     name, email = translator
00697                     translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00698                                                             label =  unicode(name, "utf-8")))
00699                     translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00700                                                             label = email))
00701                     translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00702                                                             label = lang))
00703             
00704             translatorswin.sizer.Add(item = translatorsBox, proportion = 1,
00705                                  flag = wx.EXPAND | wx.ALL, border = 3)
00706         
00707         translatorswin.SetSizer(translatorswin.sizer)
00708         translatorswin.Layout()      
00709         
00710         return translatorswin
00711     
00712     def OnCloseWindow(self, event):
00713         """!Close window"""
00714         self.Close()
00715 
00716 class HelpFrame(wx.Frame):
00717     """!GRASS Quickstart help window"""
00718     def __init__(self, parent, id, title, size, file):
00719         wx.Frame.__init__(self, parent = parent, id = id, title = title, size = size)
00720         
00721         sizer = wx.BoxSizer(wx.VERTICAL)
00722         
00723         # text
00724         content = HelpPanel(parent = self)
00725         content.LoadPage(file)
00726         
00727         sizer.Add(item = content, proportion = 1, flag = wx.EXPAND)
00728         
00729         self.SetAutoLayout(True)
00730         self.SetSizer(sizer)
00731         self.Layout()
00732 
00733 class HelpWindow(wx.html.HtmlWindow):
00734     """!This panel holds the text from GRASS docs.
00735     
00736     GISBASE must be set in the environment to find the html docs dir.
00737     The SYNOPSIS section is skipped, since this Panel is supposed to
00738     be integrated into the cmdPanel and options are obvious there.
00739     """
00740     def __init__(self, parent, grass_command, text, skip_description,
00741                  **kwargs):
00742         """!If grass_command is given, the corresponding HTML help
00743         file will be presented, with all links pointing to absolute
00744         paths of local files.
00745 
00746         If 'skip_description' is True, the HTML corresponding to
00747         SYNOPSIS will be skipped, thus only presenting the help file
00748         from the DESCRIPTION section onwards.
00749 
00750         If 'text' is given, it must be the HTML text to be presented
00751         in the Panel.
00752         """
00753         self.parent = parent
00754         wx.InitAllImageHandlers()
00755         wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs)
00756         
00757         gisbase = os.getenv("GISBASE")
00758         self.loaded = False
00759         self.history = list()
00760         self.historyIdx = 0
00761         self.fspath = os.path.join(gisbase, "docs", "html")
00762         
00763         self.SetStandardFonts (size = 10)
00764         self.SetBorders(10)
00765         
00766         if text is None:
00767             if skip_description:
00768                 url = os.path.join(self.fspath, grass_command + ".html")
00769                 self.fillContentsFromFile(url,
00770                                           skip_description = skip_description)
00771                 self.history.append(url)
00772                 self.loaded = True
00773             else:
00774                 ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
00775                 # self.LoadPage(self.fspath + grass_command + ".html")
00776                 self.loaded = False
00777         else:
00778             self.SetPage(text)
00779             self.loaded = True
00780         
00781     def OnLinkClicked(self, linkinfo):
00782         url = linkinfo.GetHref()
00783         if url[:4] != 'http':
00784             url = os.path.join(self.fspath, url)
00785         self.history.append(url)
00786         self.historyIdx += 1
00787         self.parent.OnHistory()
00788         
00789         super(HelpWindow, self).OnLinkClicked(linkinfo)
00790         
00791     def fillContentsFromFile(self, htmlFile, skip_description = True):
00792         """!Load content from file"""
00793         aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
00794         imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
00795         try:
00796             contents = []
00797             skip = False
00798             for l in file(htmlFile, "rb").readlines():
00799                 if "DESCRIPTION" in l:
00800                     skip = False
00801                 if not skip:
00802                     # do skip the options description if requested
00803                     if "SYNOPSIS" in l:
00804                         skip = skip_description
00805                     else:
00806                         # FIXME: find only first item
00807                         findALink = aLink.search(l)
00808                         if findALink is not None: 
00809                             contents.append(aLink.sub(findALink.group(1)+
00810                                                       self.fspath+findALink.group(2),l))
00811                         findImgLink = imgLink.search(l)
00812                         if findImgLink is not None: 
00813                             contents.append(imgLink.sub(findImgLink.group(1)+
00814                                                         self.fspath+findImgLink.group(2),l))
00815                         
00816                         if findALink is None and findImgLink is None:
00817                             contents.append(l)
00818             self.SetPage("".join(contents))
00819             self.loaded = True
00820         except: # The Manual file was not found
00821             self.loaded = False
00822         
00823 class HelpPanel(wx.Panel):
00824     def __init__(self, parent, grass_command = "index", text = None,
00825                  skip_description = False, **kwargs):
00826         self.grass_command = grass_command
00827         wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
00828         
00829         self.content = HelpWindow(self, grass_command, text,
00830                                   skip_description)
00831         
00832         self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
00833                                  label = _("&Next"))
00834         self.btnNext.Enable(False)
00835         self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
00836                                  label = _("&Previous"))
00837         self.btnPrev.Enable(False)
00838         
00839         self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
00840         self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
00841         
00842         self._layout()
00843 
00844     def _layout(self):
00845         """!Do layout"""
00846         sizer = wx.BoxSizer(wx.VERTICAL)
00847         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00848         
00849         btnSizer.Add(item = self.btnPrev, proportion = 0,
00850                      flag = wx.ALL, border = 5)
00851         btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
00852         btnSizer.Add(item = self.btnNext, proportion = 0,
00853                      flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
00854         
00855         sizer.Add(item = self.content, proportion = 1,
00856                   flag = wx.EXPAND)
00857         sizer.Add(item = btnSizer, proportion = 0,
00858                   flag = wx.EXPAND)
00859         
00860         self.SetSizer(sizer)
00861         sizer.Fit(self)
00862 
00863     def LoadPage(self, path = None):
00864         """!Load page"""
00865         if not path:
00866             path = os.path.join(self.content.fspath, self.grass_command + ".html")
00867         self.content.history.append(path)
00868         self.content.LoadPage(path)
00869         
00870     def IsFile(self):
00871         """!Check if file exists"""
00872         return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html"))
00873 
00874     def IsLoaded(self):
00875         return self.content.loaded
00876 
00877     def OnHistory(self):
00878         """!Update buttons"""
00879         nH = len(self.content.history)
00880         iH = self.content.historyIdx
00881         if iH == nH - 1:
00882             self.btnNext.Enable(False)
00883         elif iH > -1:
00884             self.btnNext.Enable(True)
00885         if iH < 1:
00886             self.btnPrev.Enable(False)
00887         else:
00888             self.btnPrev.Enable(True)
00889 
00890     def OnNext(self, event):
00891         """Load next page"""
00892         self.content.historyIdx += 1
00893         idx = self.content.historyIdx
00894         path = self.content.history[idx]
00895         self.content.LoadPage(path)
00896         self.OnHistory()
00897         
00898         event.Skip()
00899         
00900     def OnPrev(self, event):
00901         """Load previous page"""
00902         self.content.historyIdx -= 1
00903         idx = self.content.historyIdx
00904         path = self.content.history[idx]
00905         self.content.LoadPage(path)
00906         self.OnHistory()
00907         
00908         event.Skip()