GRASS Programmer's Manual  6.4.2(2012)
ghelp.py
Go to the documentation of this file.
00001 """!
00002 @package help.py
00003 
00004 @brief Help window
00005 
00006 Classes:
00007  - SearchModuleWindow
00008  - ItemTree
00009  - MenuTreeWindow
00010  - MenuTree
00011  - AboutWindow
00012  - InstallExtensionWindow
00013  - ExtensionTree
00014  - UninstallExtensionWindow
00015  - CheckListExtension
00016  - HelpFrame
00017  - HelpWindow
00018  - HelpPanel
00019 
00020 (C) 2008-2011 by the GRASS Development Team
00021 This program is free software under the GNU General Public License
00022 (>=v2). Read the file COPYING that comes with GRASS for details.
00023 
00024 @author Martin Landa <landa.martin gmail.com>
00025 """
00026 
00027 import os
00028 import codecs
00029 import sys
00030 
00031 import wx
00032 import wx.lib.mixins.listctrl as listmix
00033 try:
00034     import wx.lib.agw.customtreectrl as CT
00035 #    import wx.lib.agw.hyperlink as hl
00036 except ImportError:
00037     import wx.lib.customtreectrl as CT
00038 #    import wx.lib.hyperlink as hl
00039 import wx.lib.flatnotebook as FN
00040 import  wx.lib.scrolledpanel as scrolled
00041 
00042 from grass.script import core as grass
00043 from grass.script import task as gtask
00044 
00045 import menudata
00046 import gcmd
00047 import globalvar
00048 import gdialogs
00049 import utils
00050 import menuform
00051 
00052 class HelpFrame(wx.Frame):
00053     """!GRASS Quickstart help window"""
00054     def __init__(self, parent, id, title, size, file):
00055         wx.Frame.__init__(self, parent = parent, id = id, title = title, size = size)
00056         
00057         sizer = wx.BoxSizer(wx.VERTICAL)
00058         
00059         # text
00060         content = HelpPanel(parent = self)
00061         content.LoadPage(file)
00062         
00063         sizer.Add(item = content, proportion = 1, flag = wx.EXPAND)
00064         
00065         self.SetAutoLayout(True)
00066         self.SetSizer(sizer)
00067         self.Layout()
00068 
00069 class SearchModuleWindow(wx.Panel):
00070     """!Search module window (used in MenuTreeWindow)"""
00071     def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None,
00072                  showChoice = True, showTip = False, **kwargs):
00073         self.showTip    = showTip
00074         self.showChoice = showChoice
00075         self.cmdPrompt  = cmdPrompt
00076         
00077         wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
00078         
00079         self._searchDict = { _('description') : 'description',
00080                              _('command')     : 'command',
00081                              _('keywords')    : 'keywords' }
00082         
00083         self.box = wx.StaticBox(parent = self, id = wx.ID_ANY,
00084                                 label = " %s " % _("Find module(s)"))
00085         
00086         self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
00087                                   choices = [_('description'),
00088                                              _('keywords'),
00089                                              _('command')])
00090         self.searchBy.SetSelection(0)
00091         
00092         self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
00093                                   value = "", size = (-1, 25),
00094                                   style = wx.TE_PROCESS_ENTER)
00095         self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
00096         
00097         if self.showTip:
00098             self.searchTip = gdialogs.StaticWrapText(parent = self, id = wx.ID_ANY,
00099                                                      size = (-1, 35))
00100         
00101         if self.showChoice:
00102             self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY)
00103             if self.cmdPrompt:
00104                 self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
00105             self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)
00106         
00107         self._layout()
00108 
00109     def _layout(self):
00110         """!Do layout"""
00111         sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
00112         gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
00113         gridSizer.AddGrowableCol(1)
00114         
00115         gridSizer.Add(item = self.searchBy,
00116                       flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
00117         gridSizer.Add(item = self.search,
00118                       flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1))
00119         row = 1
00120         if self.showTip:
00121             gridSizer.Add(item = self.searchTip,
00122                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
00123             row += 1
00124         
00125         if self.showChoice:
00126             gridSizer.Add(item = self.searchChoice,
00127                           flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
00128         
00129         sizer.Add(item = gridSizer, proportion = 1)
00130         
00131         self.SetSizer(sizer)
00132         sizer.Fit(self)
00133 
00134     def GetSelection(self):
00135         """!Get selected element"""
00136         selection = self.searchBy.GetStringSelection()
00137         
00138         return self._searchDict[selection]
00139 
00140     def SetSelection(self, i):
00141         """!Set selection element"""
00142         self.searchBy.SetSelection(i)
00143 
00144     def OnSearchModule(self, event):
00145         """!Search module by keywords or description"""
00146         if not self.cmdPrompt:
00147             event.Skip()
00148             return
00149         
00150         text = event.GetString()
00151         if not text:
00152             self.cmdPrompt.SetFilter(None)
00153             mList = self.cmdPrompt.GetCommandItems()
00154             self.searchChoice.SetItems(mList)
00155             if self.showTip:
00156                 self.searchTip.SetLabel(_("%d modules found") % len(mList))
00157             event.Skip()
00158             return
00159         
00160         modules = dict()
00161         iFound = 0
00162         for module, data in self.cmdPrompt.moduleDesc.iteritems():
00163             found = False
00164             sel = self.searchBy.GetSelection()
00165             if sel == 0: # -> description
00166                 if text in data['desc']:
00167                     found = True
00168             elif sel == 1: # keywords
00169                 if text in ','.join(data['keywords']):
00170                     found = True
00171             else: # command
00172                 if module[:len(text)] == text:
00173                     found = True
00174             
00175             if found:
00176                 iFound += 1
00177                 try:
00178                     group, name = module.split('.')
00179                 except ValueError:
00180                     continue # TODO
00181                 
00182                 if group not in modules:
00183                     modules[group] = list()
00184                 modules[group].append(name)
00185                 
00186         self.cmdPrompt.SetFilter(modules)
00187         self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
00188         if self.showTip:
00189             self.searchTip.SetLabel(_("%d modules found") % iFound)
00190         
00191         event.Skip()
00192         
00193     def OnSelectModule(self, event):
00194         """!Module selected from choice, update command prompt"""
00195         cmd  = event.GetString().split(' ', 1)[0]
00196         text = cmd + ' '
00197         pos = len(text)
00198 
00199         if self.cmdPrompt:
00200             self.cmdPrompt.SetText(text)
00201             self.cmdPrompt.SetSelectionStart(pos)
00202             self.cmdPrompt.SetCurrentPos(pos)
00203             self.cmdPrompt.SetFocus()
00204         
00205         desc = self.cmdPrompt.GetCommandDesc(cmd)
00206         if self.showTip:
00207             self.searchTip.SetLabel(desc)
00208     
00209     def Reset(self):
00210         """!Reset widget"""
00211         self.searchBy.SetSelection(0)
00212         self.search.SetValue('')
00213         if self.showTip:
00214             self.searchTip.SetLabel('')
00215         
00216 class MenuTreeWindow(wx.Panel):
00217     """!Show menu tree"""
00218     def __init__(self, parent, id = wx.ID_ANY, **kwargs):
00219         self.parent = parent # LayerManager
00220         
00221         wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
00222         
00223         self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
00224                                     label = " %s " % _("Menu tree (double-click to run command)"))
00225         # tree
00226         self.tree = MenuTree(parent = self, data = menudata.ManagerData())
00227         self.tree.Load()
00228 
00229         # search widget
00230         self.search = SearchModuleWindow(parent = self, showChoice = False)
00231         
00232         # buttons
00233         self.btnRun   = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
00234         self.btnRun.SetToolTipString(_("Run selected command"))
00235         self.btnRun.Enable(False)
00236         
00237         # bindings
00238         self.btnRun.Bind(wx.EVT_BUTTON,            self.OnRun)
00239         self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
00240         self.tree.Bind(wx.EVT_TREE_SEL_CHANGED,    self.OnItemSelected)
00241         self.search.Bind(wx.EVT_TEXT_ENTER,        self.OnShowItem)
00242         self.search.Bind(wx.EVT_TEXT,              self.OnUpdateStatusBar)
00243         
00244         self._layout()
00245         
00246         self.search.SetFocus()
00247         
00248     def _layout(self):
00249         """!Do dialog layout"""
00250         sizer = wx.BoxSizer(wx.VERTICAL)
00251         
00252         # body
00253         dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
00254         dataSizer.Add(item = self.tree, proportion =1,
00255                       flag = wx.EXPAND)
00256         
00257         # buttons
00258         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00259         btnSizer.Add(item = self.btnRun, proportion = 0)
00260         
00261         sizer.Add(item = dataSizer, proportion = 1,
00262                   flag = wx.EXPAND | wx.ALL, border = 5)
00263 
00264         sizer.Add(item = self.search, proportion = 0,
00265                   flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
00266         
00267         sizer.Add(item = btnSizer, proportion = 0,
00268                   flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
00269         
00270         sizer.Fit(self)
00271         sizer.SetSizeHints(self)
00272         
00273         self.SetSizer(sizer)
00274         
00275         self.Fit()
00276         self.SetAutoLayout(True)        
00277         self.Layout()
00278         
00279     def OnCloseWindow(self, event):
00280         """!Close window"""
00281         self.Destroy()
00282         
00283     def OnRun(self, event):
00284         """!Run selected command"""
00285         if not self.tree.GetSelected():
00286             return # should not happen
00287         
00288         data = self.tree.GetPyData(self.tree.GetSelected())
00289         if not data:
00290             return
00291 
00292         handler = 'self.parent.' + data['handler'].lstrip('self.')
00293         if data['handler'] == 'self.OnXTerm':
00294             wx.MessageBox(parent = self,
00295                           message = _('You must run this command from the menu or command line',
00296                                       'This command require an XTerm'),
00297                           caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
00298         elif data['command']:
00299             eval(handler)(event = None, cmd = data['command'].split())
00300         else:
00301             eval(handler)(None)
00302 
00303     def OnShowItem(self, event):
00304         """!Show selected item"""
00305         self.tree.OnShowItem(event)
00306         if self.tree.GetSelected():
00307             self.btnRun.Enable()
00308         else:
00309             self.btnRun.Enable(False)
00310         
00311     def OnItemActivated(self, event):
00312         """!Item activated (double-click)"""
00313         item = event.GetItem()
00314         if not item or not item.IsOk():
00315             return
00316         
00317         data = self.tree.GetPyData(item)
00318         if not data or 'command' not in data:
00319             return
00320         
00321         self.tree.itemSelected = item
00322         
00323         self.OnRun(None)
00324         
00325     def OnItemSelected(self, event):
00326         """!Item selected"""
00327         item = event.GetItem()
00328         if not item or not item.IsOk():
00329             return
00330         
00331         data = self.tree.GetPyData(item)
00332         if not data or 'command' not in data:
00333             return
00334         
00335         if data['command']:
00336             label = data['command'] + ' -- ' + data['description']
00337         else:
00338             label = data['description']
00339         
00340         self.parent.SetStatusText(label, 0)
00341         
00342     def OnUpdateStatusBar(self, event):
00343         """!Update statusbar text"""
00344         element = self.search.GetSelection()
00345         self.tree.SearchItems(element = element,
00346                               value = event.GetString())
00347         
00348         nItems = len(self.tree.itemsMarked)
00349         if event.GetString():
00350             self.parent.SetStatusText(_("%d modules match") % nItems, 0)
00351         else:
00352             self.parent.SetStatusText("", 0)
00353         
00354         event.Skip()
00355         
00356 class ItemTree(CT.CustomTreeCtrl):
00357     def __init__(self, parent, id = wx.ID_ANY,
00358                  ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
00359                  CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
00360         if globalvar.hasAgw:
00361             super(ItemTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs)
00362         else:
00363             super(ItemTree, self).__init__(parent, id, style = ctstyle, **kwargs)
00364         
00365         self.root = self.AddRoot(_("Menu tree"))
00366         self.itemsMarked = [] # list of marked items
00367         self.itemSelected = None
00368 
00369     def SearchItems(self, element, value):
00370         """!Search item 
00371 
00372         @param element element index (see self.searchBy)
00373         @param value
00374 
00375         @return list of found tree items
00376         """
00377         items = list()
00378         if not value:
00379             return items
00380         
00381         item = self.GetFirstChild(self.root)[0]
00382         self._processItem(item, element, value, items)
00383         
00384         self.itemsMarked  = items
00385         self.itemSelected = None
00386         
00387         return items
00388     
00389     def _processItem(self, item, element, value, listOfItems):
00390         """!Search items (used by SearchItems)
00391         
00392         @param item reference item
00393         @param listOfItems list of found items
00394         """
00395         while item and item.IsOk():
00396             subItem = self.GetFirstChild(item)[0]
00397             if subItem:
00398                 self._processItem(subItem, element, value, listOfItems)
00399             data = self.GetPyData(item)
00400             
00401             if data and element in data and \
00402                     value.lower() in data[element].lower():
00403                 listOfItems.append(item)
00404             
00405             item = self.GetNextSibling(item)
00406             
00407     def GetSelected(self):
00408         """!Get selected item"""
00409         return self.itemSelected
00410 
00411     def OnShowItem(self, event):
00412         """!Highlight first found item in menu tree"""
00413         if len(self.itemsMarked) > 0:
00414             if self.GetSelected():
00415                 self.ToggleItemSelection(self.GetSelected())
00416                 idx = self.itemsMarked.index(self.GetSelected()) + 1
00417             else:
00418                 idx = 0
00419             try:
00420                 self.ToggleItemSelection(self.itemsMarked[idx])
00421                 self.itemSelected = self.itemsMarked[idx]
00422                 self.EnsureVisible(self.itemsMarked[idx])
00423             except IndexError:
00424                 self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
00425                 self.EnsureVisible(self.itemsMarked[0])
00426                 self.itemSelected = self.itemsMarked[0]
00427         else:
00428             for item in self.root.GetChildren():
00429                 self.Collapse(item)
00430             itemSelected = self.GetSelection()
00431             if itemSelected:
00432                 self.ToggleItemSelection(itemSelected)
00433             self.itemSelected = None
00434     
00435 class MenuTree(ItemTree):
00436     """!Menu tree class"""
00437     def __init__(self, parent, data, **kwargs):
00438         self.parent   = parent
00439         self.menudata = data
00440 
00441         super(MenuTree, self).__init__(parent, **kwargs)
00442         
00443     def Load(self, data = None):
00444         """!Load menu data tree
00445 
00446         @param data menu data (None to use self.menudata)
00447         """
00448         if not data:
00449             data = self.menudata
00450         
00451         self.itemsMarked = [] # list of marked items
00452         for eachMenuData in data.GetMenu():
00453             for label, items in eachMenuData:
00454                 item = self.AppendItem(parentId = self.root,
00455                                        text = label.replace('&', ''))
00456                 self.__AppendItems(item, items)
00457         
00458     def __AppendItems(self, item, data):
00459         """!Append items into tree (used by Load()
00460         
00461         @param item tree item (parent)
00462         @parent data menu data"""
00463         for eachItem in data:
00464             if len(eachItem) == 2:
00465                 if eachItem[0]:
00466                     itemSub = self.AppendItem(parentId = item,
00467                                     text = eachItem[0])
00468                 self.__AppendItems(itemSub, eachItem[1])
00469             else:
00470                 if eachItem[0]:
00471                     itemNew = self.AppendItem(parentId = item,
00472                                               text = eachItem[0])
00473                     
00474                     data = { 'item'        : eachItem[0],
00475                              'description' : eachItem[1],
00476                              'handler'  : eachItem[2],
00477                              'command'  : eachItem[3],
00478                              'keywords' : eachItem[4] }
00479                     
00480                     self.SetPyData(itemNew, data)
00481         
00482 class AboutWindow(wx.Frame):
00483     """!Create custom About Window
00484 
00485     @todo improve styling
00486     """
00487     def __init__(self, parent, size = (750, 400), 
00488                  title = _('About GRASS GIS'), **kwargs):
00489         wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, size = size, **kwargs)
00490         
00491         panel = wx.Panel(parent = self, id = wx.ID_ANY)
00492         
00493         # icon
00494         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00495 
00496         # get version and web site
00497         vInfo = grass.version()
00498         
00499         infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
00500         infoSizer = wx.BoxSizer(wx.VERTICAL)
00501         infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
00502         infoGridSizer.AddGrowableCol(0)
00503         infoGridSizer.AddGrowableCol(1)
00504         logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
00505         logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
00506                                      bitmap = wx.Bitmap(name = logo,
00507                                                         type = wx.BITMAP_TYPE_ICO))
00508         infoSizer.Add(item = logoBitmap, proportion = 0,
00509                       flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
00510         
00511         info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00512                              label = 'GRASS GIS ' + vInfo['version'] + '\n\n')
00513         info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
00514         infoSizer.Add(item = info, proportion = 0,
00515                           flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
00516 
00517         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00518                                                label = _('Official GRASS site:')),
00519                           pos = (0, 0),
00520                           flag = wx.ALIGN_RIGHT)
00521 
00522         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00523                                                label = 'http://grass.osgeo.org'),
00524                           pos = (0, 1),
00525                           flag = wx.ALIGN_LEFT)
00526         
00527         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00528                                                label = _('SVN Revision:')),
00529                           pos = (2, 0),
00530                           flag = wx.ALIGN_RIGHT)
00531         
00532         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00533                                                label = vInfo['revision']),
00534                           pos = (2, 1),
00535                           flag = wx.ALIGN_LEFT)
00536 
00537         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00538                                                label = _('GIS Library Revision:')),
00539                           pos = (3, 0),
00540                           flag = wx.ALIGN_RIGHT)
00541         
00542         infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
00543                                                label = vInfo['libgis_revision'] + ' (' +
00544                                                vInfo['libgis_date'].split(' ')[0] + ')'),
00545                           pos = (3, 1),
00546                           flag = wx.ALIGN_LEFT)
00547 
00548         infoSizer.Add(item = infoGridSizer,
00549                       proportion = 1,
00550                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
00551                       border = 25)
00552         
00553         # create a flat notebook for displaying information about GRASS
00554         aboutNotebook = menuform.GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 
00555         aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
00556         
00557         for title, win in ((_("Info"), infoTxt),
00558                            (_("Copyright"), self._pageCopyright()),
00559                            (_("License"), self._pageLicense()),
00560                            (_("Authors"), self._pageCredit()),
00561                            (_("Contributors"), self._pageContributors()),
00562                            (_("Extra contributors"), self._pageContributors(extra = True)),
00563                            (_("Translators"), self._pageTranslators())):
00564             aboutNotebook.AddPage(page = win, text = title)
00565         wx.CallAfter(aboutNotebook.SetSelection, 0)
00566         
00567         # buttons
00568         btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
00569         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00570         btnSizer.Add(item = btnClose, proportion = 0,
00571                      flag = wx.ALL | wx.ALIGN_RIGHT,
00572                      border = 5)
00573         # bindings
00574         btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
00575         
00576         infoTxt.SetSizer(infoSizer)
00577         infoSizer.Fit(infoTxt)
00578         
00579         sizer = wx.BoxSizer(wx.VERTICAL)
00580         sizer.Add(item = aboutNotebook, proportion = 1,
00581                   flag = wx.EXPAND | wx.ALL, border = 1)
00582         sizer.Add(item = btnSizer, proportion = 0,
00583                   flag = wx.ALL | wx.ALIGN_RIGHT, border = 1)
00584         panel.SetSizer(sizer)
00585         self.Layout()
00586     
00587     def _pageCopyright(self):
00588         """Copyright information"""
00589         copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
00590         if os.path.exists(copyfile):
00591             copyrightFile = open(copyfile, 'r')
00592             copytext = copyrightFile.read()
00593             copyrightFile.close()
00594         else:
00595             copytext = _('%s file missing') % 'COPYING'
00596         
00597         # put text into a scrolling panel
00598         copyrightwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 
00599                                               size = wx.DefaultSize,
00600                                               style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
00601         copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext)
00602         copyrightwin.SetAutoLayout(True)
00603         copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
00604         copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1,
00605                                flag = wx.EXPAND | wx.ALL, border = 3)
00606         copyrightwin.SetSizer(copyrightwin.sizer)
00607         copyrightwin.Layout()
00608         copyrightwin.SetupScrolling()
00609         
00610         return copyrightwin
00611     
00612     def _pageLicense(self):
00613         """Licence about"""
00614         licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
00615         if os.path.exists(licfile):
00616             licenceFile = open(licfile, 'r')
00617             license = ''.join(licenceFile.readlines())
00618             licenceFile.close()
00619         else:
00620             license = _('%s file missing') % 'GPL.TXT'
00621         # put text into a scrolling panel
00622         licensewin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 
00623                                             style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
00624         licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license)
00625         licensewin.SetAutoLayout(True)
00626         licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
00627         licensewin.sizer.Add(item = licensetxt, proportion = 1,
00628                 flag = wx.EXPAND | wx.ALL, border = 3)
00629         licensewin.SetSizer(licensewin.sizer)
00630         licensewin.Layout()
00631         licensewin.SetupScrolling()
00632         
00633         return licensewin
00634     
00635     def _pageCredit(self):
00636         """Credit about"""
00637                 # credits
00638         authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
00639         if os.path.exists(authfile):
00640             authorsFile = open(authfile, 'r')
00641             authors = unicode(''.join(authorsFile.readlines()), "utf-8")
00642             authorsFile.close()
00643         else:
00644             authors = _('%s file missing') % 'AUTHORS'
00645         authorwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 
00646                                            style  =  wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
00647         authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
00648         authorwin.SetAutoLayout(1)
00649         authorwin.SetupScrolling()
00650         authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
00651         authorwin.sizer.Add(item = authortxt, proportion = 1,
00652                 flag = wx.EXPAND | wx.ALL, border = 3)
00653         authorwin.SetSizer(authorwin.sizer)
00654         authorwin.Layout()      
00655         
00656         return authorwin
00657 
00658     def _pageContributors(self, extra = False):
00659         """Contributors info"""
00660         if extra:
00661             contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
00662         else:
00663             contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
00664         if os.path.exists(contribfile):
00665             contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
00666             contribs = list()
00667             errLines = list()
00668             for line in contribFile.readlines()[1:]:
00669                 line = line.rstrip('\n')
00670                 try:
00671                     if extra:
00672                         name, email, rfc2_agreed = line.split(',')
00673                     else:
00674                         cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
00675                 except ValueError:
00676                     errLines.append(line)
00677                     continue
00678                 if extra:
00679                     contribs.append((name, email))
00680                 else:
00681                     contribs.append((name, email, country, osgeo_id))
00682             
00683             contribFile.close()
00684             
00685             if errLines:
00686                 gcmd.GError(parent = self,
00687                             message = _("Error when reading file '%s'.") % contribfile + \
00688                                 "\n\n" + _("Lines:") + " %s" % \
00689                                 os.linesep.join(map(utils.UnicodeString, errLines)))
00690         else:
00691             contribs = None
00692         
00693         contribwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 
00694                                            style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
00695         contribwin.SetAutoLayout(True)
00696         contribwin.SetupScrolling()
00697         contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
00698         
00699         if not contribs:
00700             contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
00701                                        label = _('%s file missing') % contribfile)
00702             contribwin.sizer.Add(item = contribtxt, proportion = 1,
00703                                  flag = wx.EXPAND | wx.ALL, border = 3)
00704         else:
00705             if extra:
00706                 items = (_('Name'), _('E-mail'))
00707             else:
00708                 items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
00709             contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5)
00710             for item in items:
00711                 contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
00712                                                     label = item))
00713             for vals in contribs:
00714                 for item in vals:
00715                     contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
00716                                                         label = item))
00717             contribwin.sizer.Add(item = contribBox, proportion = 1,
00718                                  flag = wx.EXPAND | wx.ALL, border = 3)
00719         
00720         contribwin.SetSizer(contribwin.sizer)
00721         contribwin.Layout()      
00722         
00723         return contribwin
00724 
00725     def _pageTranslators(self):
00726         """Translators info"""
00727         translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
00728         if os.path.exists(translatorsfile):
00729             translatorsFile = open(translatorsfile, 'r')
00730             translators = dict()
00731             errLines = list()
00732             for line in translatorsFile.readlines()[1:]:
00733                 line = line.rstrip('\n')
00734                 try:
00735                     name, email, languages = line.split(',')
00736                 except ValueError:
00737                     errLines.append(line)
00738                     continue
00739                 for language in languages.split(' '):
00740                     if language not in translators:
00741                         translators[language] = list()
00742                     translators[language].append((name, email))
00743             translatorsFile.close()
00744             
00745             if errLines:
00746                 gcmd.GError(parent = self,
00747                             message = _("Error when reading file '%s'.") % translatorsfile + \
00748                                 "\n\n" + _("Lines:") + " %s" % \
00749                                 os.linesep.join(map(utils.UnicodeString, errLines)))
00750         else:
00751             translators = None
00752         
00753         translatorswin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 
00754                                            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
00755         translatorswin.SetAutoLayout(1)
00756         translatorswin.SetupScrolling()
00757         translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
00758         
00759         if not translators:
00760             translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY,
00761                                            label = _('%s file missing') % 'translators.csv')
00762             translatorswin.sizer.Add(item = translatorstxt, proportion = 1,
00763                                  flag = wx.EXPAND | wx.ALL, border = 3)
00764         else:
00765             translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
00766             languages = translators.keys()
00767             languages.sort()
00768             translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00769                                                     label = _('Name')))
00770             translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00771                                                     label = _('E-mail')))
00772             translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00773                                                     label = _('Language')))
00774             for lang in languages:
00775                 for translator in translators[lang]:
00776                     name, email = translator
00777                     translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00778                                                             label =  unicode(name, "utf-8")))
00779                     translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00780                                                             label = email))
00781                     translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
00782                                                             label = lang))
00783             
00784             translatorswin.sizer.Add(item = translatorsBox, proportion = 1,
00785                                  flag = wx.EXPAND | wx.ALL, border = 3)
00786         
00787         translatorswin.SetSizer(translatorswin.sizer)
00788         translatorswin.Layout()      
00789         
00790         return translatorswin
00791     
00792     def OnCloseWindow(self, event):
00793         """!Close window"""
00794         self.Close()
00795 
00796 class InstallExtensionWindow(wx.Frame):
00797     def __init__(self, parent, id = wx.ID_ANY,
00798                  title = _("Fetch & install extension from GRASS Addons"), **kwargs):
00799         self.parent = parent
00800         self.options = dict() # list of options
00801         
00802         wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
00803         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00804         
00805         self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
00806 
00807         self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
00808                                     label = " %s " % _("Repository"))
00809         self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
00810                                     label = " %s " % _("List of extensions"))
00811         
00812         self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
00813         self.fullDesc = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
00814                                     label = _("Fetch full info including description and keywords"))
00815         self.fullDesc.SetValue(True)
00816         
00817         self.search = SearchModuleWindow(parent = self.panel)
00818         self.search.SetSelection(0) 
00819         
00820         self.tree   = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
00821         
00822         self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
00823                                       label = " %s " % _("Options"))
00824         
00825         task = gtask.parse_interface('g.extension.py')
00826         
00827         ignoreFlags = ['l', 'c', 'g', 'a', 'f', 'quiet', 'verbose']
00828         if sys.platform == 'win32':
00829             ignoreFlags.append('d')
00830             ignoreFlags.append('i')
00831         
00832         for f in task.get_options()['flags']:
00833             name = f.get('name', '')
00834             desc = f.get('label', '')
00835             if not desc:
00836                 desc = f.get('description', '')
00837             if not name and not desc:
00838                 continue
00839             if name in ignoreFlags:
00840                 continue
00841             self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
00842                                              label = desc)
00843         self.repo.SetValue(task.get_param(value = 'svnurl').get('default',
00844                                                                 'http://svn.osgeo.org/grass/grass-addons'))
00845         
00846         self.statusbar = self.CreateStatusBar(number = 1)
00847         
00848         self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
00849                                   label = _("&Fetch"))
00850         self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
00851         self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
00852         self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
00853                                     label = _("&Install"))
00854         self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
00855         self.btnInstall.Enable(False)
00856         self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
00857                                 label = _("Command dialog"))
00858         self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension.py')
00859 
00860         self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
00861         self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
00862         self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
00863         self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
00864         self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
00865         self.tree.Bind(wx.EVT_TREE_SEL_CHANGED,    self.OnItemSelected)
00866         self.search.Bind(wx.EVT_TEXT_ENTER,        self.OnShowItem)
00867         self.search.Bind(wx.EVT_TEXT,              self.OnUpdateStatusBar)
00868 
00869         self._layout()
00870 
00871     def _layout(self):
00872         """!Do layout"""
00873         sizer = wx.BoxSizer(wx.VERTICAL)
00874         repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
00875         repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
00876         repo1Sizer.Add(item = self.repo, proportion = 1,
00877                       flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
00878         repo1Sizer.Add(item = self.btnFetch, proportion = 0,
00879                       flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
00880         repoSizer.Add(item = repo1Sizer,
00881                       flag = wx.EXPAND)
00882         repoSizer.Add(item = self.fullDesc)
00883         
00884         findSizer = wx.BoxSizer(wx.HORIZONTAL)
00885         findSizer.Add(item = self.search, proportion = 1)
00886         
00887         treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
00888         treeSizer.Add(item = self.tree, proportion = 1,
00889                       flag = wx.ALL | wx.EXPAND, border = 1)
00890 
00891         # options
00892         optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
00893         for key in self.options.keys():
00894             optionSizer.Add(item = self.options[key], proportion = 0)
00895         
00896         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
00897         btnSizer.Add(item = self.btnCmd, proportion = 0,
00898                      flag = wx.RIGHT, border = 5)
00899         btnSizer.AddSpacer(10)
00900         btnSizer.Add(item = self.btnClose, proportion = 0,
00901                      flag = wx.RIGHT, border = 5)
00902         btnSizer.Add(item = self.btnInstall, proportion = 0)
00903         
00904         sizer.Add(item = repoSizer, proportion = 0,
00905                   flag = wx.ALL | wx.EXPAND, border = 3)
00906         sizer.Add(item = findSizer, proportion = 0,
00907                   flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
00908         sizer.Add(item = treeSizer, proportion = 1,
00909                   flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
00910         sizer.Add(item = optionSizer, proportion = 0,
00911                         flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
00912         sizer.Add(item = btnSizer, proportion = 0,
00913                   flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
00914         
00915         self.panel.SetSizer(sizer)
00916         sizer.Fit(self.panel)
00917         
00918         self.Layout()
00919 
00920     def _getCmd(self):
00921         item = self.tree.GetSelected()
00922         if not item or not item.IsOk():
00923             return ['g.extension.py']
00924         
00925         name = self.tree.GetItemText(item)
00926         if not name:
00927             gcmd.GError(_("Extension not defined"), parent = self)
00928             return
00929         flags = list()
00930         for key in self.options.keys():
00931             if self.options[key].IsChecked():
00932                 flags.append('-%s' % key)
00933         
00934         return ['g.extension.py'] + flags + ['extension=' + name,
00935                                              'svnurl=' + self.repo.GetValue().strip()]
00936     
00937     def OnUpdateStatusBar(self, event):
00938         """!Update statusbar text"""
00939         element = self.search.GetSelection()
00940         if not self.tree.IsLoaded():
00941             self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
00942             return
00943         
00944         self.tree.SearchItems(element = element,
00945                               value = event.GetString())
00946         
00947         nItems = len(self.tree.itemsMarked)
00948         if event.GetString():
00949             self.SetStatusText(_("%d items match") % nItems, 0)
00950         else:
00951             self.SetStatusText("", 0)
00952         
00953         event.Skip()
00954     
00955     def OnCloseWindow(self, event):
00956         """!Close window"""
00957         self.Destroy()
00958 
00959     def OnFetch(self, event):
00960         """!Fetch list of available extensions"""
00961         wx.BeginBusyCursor()
00962         self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
00963         self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
00964         self.SetStatusText("", 0)
00965         wx.EndBusyCursor()
00966 
00967     def OnItemActivated(self, event):
00968         item = event.GetItem()
00969         data = self.tree.GetPyData(item)
00970         if data and 'command' in data:
00971             self.OnInstall(event = None)
00972         
00973     def OnInstall(self, event):
00974         """!Install selected extension"""
00975         log = self.parent.GetLogWindow()
00976         log.RunCmd(self._getCmd(), onDone = self.OnDone)
00977         
00978     def OnDone(self, cmd, returncode):
00979         item = self.tree.GetSelected()
00980         if not item or not item.IsOk() or \
00981                 returncode != 0 or \
00982                 not os.getenv('GRASS_ADDON_PATH'):
00983             return
00984         
00985         name = self.tree.GetItemText(item)
00986         globalvar.grassCmd['all'].append(name)
00987         
00988     def OnItemSelected(self, event):
00989         """!Item selected"""
00990         item = event.GetItem()
00991         self.tree.itemSelected = item
00992         data = self.tree.GetPyData(item)
00993         if data is None:
00994             self.SetStatusText('', 0)
00995             self.btnInstall.Enable(False)
00996         else:
00997             self.SetStatusText(data.get('description', ''), 0)
00998             self.btnInstall.Enable(True)
00999 
01000     def OnShowItem(self, event):
01001         """!Show selected item"""
01002         self.tree.OnShowItem(event)
01003         if self.tree.GetSelected():
01004             self.btnInstall.Enable()
01005         else:
01006             self.btnInstall.Enable(False)
01007 
01008     def OnCmdDialog(self, event):
01009         """!Shows command dialog"""
01010         menuform.GUI(parent = self).ParseCommand(cmd = self._getCmd())
01011         
01012 class ExtensionTree(ItemTree):
01013     """!List of available extensions"""
01014     def __init__(self, parent, log, id = wx.ID_ANY,
01015                  ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
01016                  CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
01017                  **kwargs):
01018         self.parent = parent # GMFrame
01019         self.log    = log
01020         
01021         super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
01022         
01023         self._initTree()
01024         
01025     def _initTree(self):
01026         for prefix in ('display', 'database',
01027                        'general', 'imagery',
01028                        'misc', 'postscript', 'paint',
01029                        'raster', 'raster3d', 'sites', 'vector', 'wxGUI', 'other'):
01030             self.AppendItem(parentId = self.root,
01031                             text = prefix)
01032         self._loaded = False
01033         
01034     def _expandPrefix(self, c):
01035         name = { 'd'  : 'display',
01036                  'db' : 'database',
01037                  'g'  : 'general',
01038                  'i'  : 'imagery',
01039                  'm'  : 'misc',
01040                  'ps' : 'postscript',
01041                  'p'  : 'paint',
01042                  'r'  : 'raster',
01043                  'r3' : 'raster3d',
01044                  's'  : 'sites',
01045                  'v'  : 'vector',
01046                  'wx' : 'wxGUI',
01047                  ''   : 'other' }
01048         
01049         if c in name:
01050             return name[c]
01051         
01052         return c
01053     
01054     def _findItem(self, text):
01055         """!Find item"""
01056         item = self.GetFirstChild(self.root)[0]
01057         while item and item.IsOk():
01058             if text == self.GetItemText(item):
01059                 return item
01060             
01061             item = self.GetNextSibling(item)
01062         
01063         return None
01064     
01065     def Load(self, url, full = False):
01066         """!Load list of extensions"""
01067         self.DeleteAllItems()
01068         self.root = self.AddRoot(_("Menu tree"))
01069         self._initTree()
01070         
01071         if full:
01072             flags = 'g'
01073         else:
01074             flags = 'l'
01075         ret = gcmd.RunCommand('g.extension.py', read = True, parent = self,
01076                               svnurl = url,
01077                               flags = flags, quiet = True)
01078         if not ret:
01079             return
01080         
01081         mdict = dict()
01082         for line in ret.splitlines():
01083             if full:
01084                 try:
01085                     key, value = line.split('=', 1)
01086                 except ValueError:
01087                     key = 'name'
01088                     value = line
01089                 
01090                 if key == 'name':
01091                     try:
01092                         prefix, name = value.split('.', 1)
01093                     except ValueError:
01094                         prefix = ''
01095                         name = value
01096                     if prefix not in mdict:
01097                         mdict[prefix] = dict()
01098                     mdict[prefix][name] = dict()
01099                 else:
01100                     mdict[prefix][name][key] = value
01101             else:
01102                 try:
01103                     prefix, name = line.strip().split('.', 1)
01104                 except:
01105                     prefix = ''
01106                     name = line.strip()
01107                 
01108                 if self._expandPrefix(prefix) == prefix:
01109                     prefix = ''
01110                     
01111                 if prefix not in mdict:
01112                     mdict[prefix] = dict()
01113                     
01114                 mdict[prefix][name] = { 'command' : prefix + '.' + name }
01115         
01116         for prefix in mdict.keys():
01117             prefixName = self._expandPrefix(prefix)
01118             item = self._findItem(prefixName)
01119             names = mdict[prefix].keys()
01120             names.sort()
01121             for name in names:
01122                 if prefix:
01123                     text = prefix + '.' + name
01124                 else:
01125                     text = name
01126                 new = self.AppendItem(parentId = item,
01127                                       text = text)
01128                 data = dict()
01129                 for key in mdict[prefix][name].keys():
01130                     data[key] = mdict[prefix][name][key]
01131                 
01132                 self.SetPyData(new, data)
01133         
01134         self._loaded = True
01135 
01136     def IsLoaded(self):
01137         """Check if items are loaded"""
01138         return self._loaded
01139 
01140 class UninstallExtensionWindow(wx.Frame):
01141     def __init__(self, parent, id = wx.ID_ANY,
01142                  title = _("Uninstall GRASS Addons extensions"), **kwargs):
01143         self.parent = parent
01144         
01145         wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
01146         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
01147         
01148         self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
01149 
01150         self.extBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
01151                                    label = " %s " % _("List of installed extensions"))
01152         
01153         self.extList = CheckListExtension(parent = self.panel)
01154 
01155         # buttons
01156         self.btnUninstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
01157                                     label = _("&Uninstall"))
01158         self.btnUninstall.SetToolTipString(_("Uninstall selected AddOns extensions"))
01159         self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
01160                                 label = _("Command dialog"))
01161         self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension')
01162         self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
01163         
01164         self.btnUninstall.Bind(wx.EVT_BUTTON, self.OnUninstall)
01165         self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
01166         self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
01167         
01168         self._layout()
01169         
01170     def _layout(self):
01171         """!Do layout"""
01172         sizer = wx.BoxSizer(wx.VERTICAL)
01173         
01174         extSizer = wx.StaticBoxSizer(self.extBox, wx.HORIZONTAL)
01175         extSizer.Add(item = self.extList, proportion = 1,
01176                      flag = wx.ALL | wx.EXPAND, border = 1)
01177         
01178         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
01179         btnSizer.Add(item = self.btnCmd, proportion = 0,
01180                      flag = wx.RIGHT, border = 5)
01181         btnSizer.AddSpacer(10)
01182         btnSizer.Add(item = self.btnClose, proportion = 0,
01183                      flag = wx.RIGHT, border = 5)
01184         btnSizer.Add(item = self.btnUninstall, proportion = 0)
01185         
01186         sizer.Add(item = extSizer, proportion = 1,
01187                   flag = wx.ALL | wx.EXPAND, border = 3)
01188         sizer.Add(item = btnSizer, proportion = 0,
01189                   flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
01190         
01191         self.panel.SetSizer(sizer)
01192         sizer.Fit(self.panel)
01193         
01194         self.Layout()
01195 
01196     def OnCloseWindow(self, event):
01197         """!Close window"""
01198         self.Destroy()
01199 
01200     def OnUninstall(self, event):
01201         """!Uninstall selected extensions"""
01202         log = self.parent.GetLogWindow()
01203         eList = self.extList.GetExtensions()
01204         if not eList:
01205             gcmd.GError(_("No extension selected for removal. "
01206                           "Operation canceled."),
01207                         parent = self)
01208             return
01209         
01210         for ext in eList:
01211             files = gcmd.RunCommand('g.extension.py', parent = self, read = True, quiet = True,
01212                                     extension = ext, operation = 'remove').splitlines()
01213             dlg = wx.MessageDialog(parent = self,
01214                                    message = _("List of files to be removed:\n%(files)s\n\n"
01215                                                "Do you want really to remove <%(ext)s> extension?") % \
01216                                        { 'files' : os.linesep.join(files), 'ext' : ext },
01217                                    caption = _("Remove extension"),
01218                                    style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
01219             
01220             if dlg.ShowModal() ==  wx.ID_YES:
01221                 gcmd.RunCommand('g.extension.py', flags = 'f', parent = self, quiet = True,
01222                                 extension = ext, operation = 'remove')
01223         
01224         self.extList.LoadData()
01225         
01226     def OnCmdDialog(self, event):
01227         """!Shows command dialog"""
01228         menuform.GUI(parent = self).ParseCommand(cmd = ['g.extension.py'])
01229 
01230 class CheckListExtension(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
01231     """!List of mapset/owner/group"""
01232     def __init__(self, parent):
01233         self.parent = parent
01234         
01235         wx.ListCtrl.__init__(self, parent, id = wx.ID_ANY,
01236                              style = wx.LC_REPORT)
01237         listmix.CheckListCtrlMixin.__init__(self)
01238         
01239         # setup mixins
01240         listmix.ListCtrlAutoWidthMixin.__init__(self)
01241 
01242         self.InsertColumn(0, _('Extension'))
01243         self.LoadData()
01244         
01245     def LoadData(self):
01246         """!Load data into list"""
01247         self.DeleteAllItems()
01248         for ext in gcmd.RunCommand('g.extension.py',
01249                                    quiet = True, parent = self, read = True,
01250                                    flags = 'a').splitlines():
01251             if ext:
01252                 self.InsertStringItem(sys.maxint, ext)
01253         
01254     def GetExtensions(self):
01255         """!Get extensions to be un-installed
01256         """
01257         extList = list()
01258         for i in range(self.GetItemCount()):
01259             if self.IsChecked(i):
01260                 name = self.GetItemText(i)
01261                 if name:
01262                     extList.append(name)
01263         
01264         return extList
01265 
01266 class HelpWindow(wx.html.HtmlWindow):
01267     """!This panel holds the text from GRASS docs.
01268     
01269     GISBASE must be set in the environment to find the html docs dir.
01270     The SYNOPSIS section is skipped, since this Panel is supposed to
01271     be integrated into the cmdPanel and options are obvious there.
01272     """
01273     def __init__(self, parent, grass_command, text, skip_description,
01274                  **kwargs):
01275         """!If grass_command is given, the corresponding HTML help
01276         file will be presented, with all links pointing to absolute
01277         paths of local files.
01278 
01279         If 'skip_description' is True, the HTML corresponding to
01280         SYNOPSIS will be skipped, thus only presenting the help file
01281         from the DESCRIPTION section onwards.
01282 
01283         If 'text' is given, it must be the HTML text to be presented
01284         in the Panel.
01285         """
01286         self.parent = parent
01287         wx.InitAllImageHandlers()
01288         wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs)
01289         
01290         gisbase = os.getenv("GISBASE")
01291         self.loaded = False
01292         self.history = list()
01293         self.historyIdx = 0
01294         self.fspath = os.path.join(gisbase, "docs", "html")
01295         
01296         self.SetStandardFonts (size = 10)
01297         self.SetBorders(10)
01298         
01299         if text is None:
01300             if skip_description:
01301                 url = os.path.join(self.fspath, grass_command + ".html")
01302                 self.fillContentsFromFile(url,
01303                                           skip_description = skip_description)
01304                 self.history.append(url)
01305                 self.loaded = True
01306             else:
01307                 ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
01308                 # self.LoadPage(self.fspath + grass_command + ".html")
01309                 self.loaded = False
01310         else:
01311             self.SetPage(text)
01312             self.loaded = True
01313         
01314     def OnLinkClicked(self, linkinfo):
01315         url = linkinfo.GetHref()
01316         if url[:4] != 'http':
01317             url = os.path.join(self.fspath, url)
01318         self.history.append(url)
01319         self.historyIdx += 1
01320         self.parent.OnHistory()
01321         
01322         super(HelpWindow, self).OnLinkClicked(linkinfo)
01323         
01324     def fillContentsFromFile(self, htmlFile, skip_description = True):
01325         """!Load content from file"""
01326         aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
01327         imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
01328         try:
01329             contents = []
01330             skip = False
01331             for l in file(htmlFile, "rb").readlines():
01332                 if "DESCRIPTION" in l:
01333                     skip = False
01334                 if not skip:
01335                     # do skip the options description if requested
01336                     if "SYNOPSIS" in l:
01337                         skip = skip_description
01338                     else:
01339                         # FIXME: find only first item
01340                         findALink = aLink.search(l)
01341                         if findALink is not None: 
01342                             contents.append(aLink.sub(findALink.group(1)+
01343                                                       self.fspath+findALink.group(2),l))
01344                         findImgLink = imgLink.search(l)
01345                         if findImgLink is not None: 
01346                             contents.append(imgLink.sub(findImgLink.group(1)+
01347                                                         self.fspath+findImgLink.group(2),l))
01348                         
01349                         if findALink is None and findImgLink is None:
01350                             contents.append(l)
01351             self.SetPage("".join(contents))
01352             self.loaded = True
01353         except: # The Manual file was not found
01354             self.loaded = False
01355         
01356 class HelpPanel(wx.Panel):
01357     def __init__(self, parent, grass_command = "index", text = None,
01358                  skip_description = False, **kwargs):
01359         self.grass_command = grass_command
01360         wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
01361         
01362         self.content = HelpWindow(self, grass_command, text,
01363                                   skip_description)
01364         
01365         self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
01366                                  label = _("&Next"))
01367         self.btnNext.Enable(False)
01368         self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
01369                                  label = _("&Previous"))
01370         self.btnPrev.Enable(False)
01371         
01372         self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
01373         self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
01374         
01375         self._layout()
01376 
01377     def _layout(self):
01378         """!Do layout"""
01379         sizer = wx.BoxSizer(wx.VERTICAL)
01380         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
01381         
01382         btnSizer.Add(item = self.btnPrev, proportion = 0,
01383                      flag = wx.ALL, border = 5)
01384         btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
01385         btnSizer.Add(item = self.btnNext, proportion = 0,
01386                      flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
01387         
01388         sizer.Add(item = self.content, proportion = 1,
01389                   flag = wx.EXPAND)
01390         sizer.Add(item = btnSizer, proportion = 0,
01391                   flag = wx.EXPAND)
01392         
01393         self.SetSizer(sizer)
01394         sizer.Fit(self)
01395 
01396     def LoadPage(self, path = None):
01397         """!Load page"""
01398         if not path:
01399             path = os.path.join(self.content.fspath, self.grass_command + ".html")
01400         self.content.history.append(path)
01401         self.content.LoadPage(path)
01402         
01403     def IsFile(self):
01404         """!Check if file exists"""
01405         return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html"))
01406 
01407     def IsLoaded(self):
01408         return self.content.loaded
01409 
01410     def OnHistory(self):
01411         """!Update buttons"""
01412         nH = len(self.content.history)
01413         iH = self.content.historyIdx
01414         if iH == nH - 1:
01415             self.btnNext.Enable(False)
01416         elif iH > -1:
01417             self.btnNext.Enable(True)
01418         if iH < 1:
01419             self.btnPrev.Enable(False)
01420         else:
01421             self.btnPrev.Enable(True)
01422 
01423     def OnNext(self, event):
01424         """Load next page"""
01425         self.content.historyIdx += 1
01426         idx = self.content.historyIdx
01427         path = self.content.history[idx]
01428         self.content.LoadPage(path)
01429         self.OnHistory()
01430         
01431         event.Skip()
01432         
01433     def OnPrev(self, event):
01434         """Load previous page"""
01435         self.content.historyIdx -= 1
01436         idx = self.content.historyIdx
01437         path = self.content.history[idx]
01438         self.content.LoadPage(path)
01439         self.OnHistory()
01440         
01441         event.Skip()
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines