[GRASS-SVN] r49738 - in grass/branches/releasebranch_6_4/gui: scripts wxpython wxpython/gui_modules wxpython/xml

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Dec 14 06:57:31 EST 2011


Author: martinl
Date: 2011-12-14 03:57:31 -0800 (Wed, 14 Dec 2011)
New Revision: 49738

Modified:
   grass/branches/releasebranch_6_4/gui/scripts/g.extension.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py
   grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py
   grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml
Log:
wxGUI: backport improved extension management from devbr6
       new tool for uninstalling extensions


Modified: grass/branches/releasebranch_6_4/gui/scripts/g.extension.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/scripts/g.extension.py	2011-12-14 11:38:11 UTC (rev 49737)
+++ grass/branches/releasebranch_6_4/gui/scripts/g.extension.py	2011-12-14 11:57:31 UTC (rev 49738)
@@ -28,6 +28,7 @@
 #% type: string
 #% key_desc: name
 #% description: Name of extension to install/remove
+#% guisection: Required
 #%end
 #%option
 #% key: operation
@@ -42,7 +43,6 @@
 #% type: string
 #% key_desc: url
 #% description: SVN Addons repository URL
-#% required: yes
 #% answer: http://svn.osgeo.org/grass/grass-addons/grass6
 #%end
 #%option
@@ -56,34 +56,43 @@
 
 #%flag
 #% key: l
-#% description: List available modules in the GRASS Addons SVN repository
+#% description: List available extensions in the GRASS Addons SVN repository
 #% guisection: Print
 #%end
 #%flag
 #% key: c
-#% description: List available modules in the GRASS Addons SVN repository including module description
+#% description: List available extensions in the GRASS Addons SVN repository including module description
 #% guisection: Print
 #%end
 #%flag
 #% key: g
-#% description: List available modules in the GRASS Addons SVN repository (shell script style)
+#% description: List available extensions in the GRASS Addons SVN repository (shell script style)
 #% guisection: Print
 #%end
 #%flag
+#% key: a
+#% description: List locally installed extension
+#% guisection: Print
+#%end
+#%flag
 #% key: s
 #% description: Install system-wide (may need system administrator rights)
+#% guisection: Install
 #%end
 #%flag
 #% key: d
 #% description: Download source code and exit
+#% guisection: Install
 #%end
 #%flag
 #% key: i
 #% description: Don't install new extension, just compile it
+#% guisection: Install
 #%end
 #%flag
 #% key: f
 #% description: Force removal when uninstalling extension (operation=remove)
+#% guisection: Remove
 #%end
 
 import os
@@ -135,8 +144,35 @@
     
     return c
 
-# list modules (read XML file from grass.osgeo.org/addons)
-def list_available_modules():
+
+# list installed extensions
+def get_installed_extensions(force = False):
+    fXML = os.path.join(options['prefix'], 'modules.xml')
+    if not os.path.exists(fXML):
+        if force:
+            write_xml_modules(fXML)
+        else:
+            grass.warning(_("No metadata file available"))
+        return []
+    
+    # read XML file
+    fo = open(fXML, 'r')
+    try:
+        tree = etree.fromstring(fo.read())
+    except:
+        os.remove(fXML)
+        write_xml_modules(fXML)
+        return []
+    fo.close()
+    
+    ret = list()
+    for tnode in tree.findall('task'):
+        ret.append(tnode.get('name'))
+    
+    return ret
+
+# list extensions (read XML file from grass.osgeo.org/addons)
+def list_available_extensions():
     mlist = list()
 
     # try to download XML metadata file first
@@ -163,14 +199,14 @@
             else:
                 print name
     except HTTPError:
-        return list_available_modules_svn()
+        return list_available_extensions_svn()
     
     return mlist
 
-# list modules (scan SVN repo)
-def list_available_modules_svn():
+# list extensions (scan SVN repo)
+def list_available_extensions_svn():
     mlist = list()
-    grass.message(_('Fetching list of modules from GRASS-Addons SVN (be patient)...'))
+    grass.message(_('Fetching list of extensions from GRASS-Addons SVN (be patient)...'))
     pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
 
     if flags['c']:
@@ -194,7 +230,7 @@
             continue
         
         for line in f.readlines():
-            # list modules
+            # list extensions
             sline = pattern.search(line)
             if not sline:
                 continue
@@ -222,7 +258,7 @@
         return
         
     for line in f.readlines():
-        # list modules
+        # list extensions
         sline = pattern.search(line)
         if not sline:
             continue
@@ -241,6 +277,147 @@
         grass.message(_("Path to the source code:"))
         sys.stderr.write('%s\n' % os.path.join(tmpdir, options['extension']))
 
+# write out meta-file
+def write_xml_modules(name, tree = None):
+    fo = open(name, 'w')
+    version = grass.version()['version'].split('.')[0]
+    fo.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+    fo.write('<!DOCTYPE task SYSTEM "grass-addons.dtd">\n')
+    fo.write('<addons version="%s">\n' % version)
+    
+    if tree is not None:
+        for tnode in tree.findall('task'):
+            indent = 4
+            fo.write('%s<task name="%s">\n' % (' ' * indent, tnode.get('name')))
+            indent += 4
+            fo.write('%s<description>%s</description>\n' % \
+                         (' ' * indent, tnode.find('description').text))
+            fo.write('%s<keywords>%s</keywords>\n' % \
+                         (' ' * indent, tnode.find('keywords').text))
+            bnode = tnode.find('binary')
+            if bnode is not None:
+                fo.write('%s<binary>\n' % (' ' * indent))
+                indent += 4
+                for fnode in bnode.findall('file'):
+                    fpath = fnode.text.split(os.path.sep)
+                    if not flags['s']: # tidy citizen hacks
+                        if fpath[0] in ('bin', 'scripts'):
+                            del fpath[0]
+                        if fpath[0] == 'man':
+                            fpath.insert(0, 'docs')
+                    
+                    fo.write('%s<file>%s</file>\n' % \
+                                 (' ' * indent, os.path.join(options['prefix'],
+                                                             os.path.sep.join(fpath))))
+                indent -= 4 
+                fo.write('%s</binary>\n' % (' ' * indent))
+            libgisRev = grass.version()['libgis_revision']
+            fo.write('%s<libgis revision="%s" />\n' % \
+                         (' ' * indent, libgisRev))
+            indent -= 4
+            fo.write('%s</task>\n' % (' ' * indent))
+    
+    fo.write('</addons>\n')
+    fo.close()
+    
+# update local meta-file when installing new extension
+def install_extension_xml():
+    # read metadata from remote server
+    url = "http://grass.osgeo.org/addons/grass%s.xml" % grass.version()['version'].split('.')[0]
+    data = None
+    try:
+        f = urlopen(url)
+        tree = etree.fromstring(f.read())
+        for mnode in tree.findall('task'):
+            name = mnode.get('name')
+            if name != options['extension']:
+                continue
+            
+            fList = list()
+            bnode = mnode.find('binary')
+            windows = sys.platform == 'win32'
+            if bnode is not None:
+                for fnode in bnode.findall('file'):
+                    path = fnode.text.split('/')
+                    if windows:
+                        if path[0] == 'bin':
+                            path[-1] += '.exe'
+                        if path[0] == 'scripts':
+                            path[-1] += '.py'
+                    fList.append(os.path.sep.join(path))
+            
+            desc = mnode.find('description').text
+            if not desc:
+                desc = ''
+            keyw = mnode.find('keywords').text
+            if not keyw:
+                keyw = ''
+            
+            data = { 'name'  : name,
+                     'desc'  : desc,
+                     'keyw'  : keyw,
+                     'files' : fList,
+                     }
+    except HTTPError:
+        grass.error(_("Unable to read metadata file from the remote server"))
+    
+    if not data:
+        grass.warning(_("No metadata available"))
+        return
+
+    fXML = os.path.join(options['prefix'], 'modules.xml')
+    # create an empty file if not exists
+    if not os.path.exists(fXML):
+        write_xml_modules(fXML)
+    
+    # read XML file
+    fo = open(fXML, 'r')
+    tree = etree.fromstring(fo.read())
+    fo.close()
+    
+    # update tree
+    tnode = None
+    for node in tree.findall('task'):
+        if node.get('name') == options['extension']:
+            tnode = node
+            break
+    
+    if tnode is not None:
+        # update existing node
+        dnode = tnode.find('description')
+        if dnode is not None:
+            dnode.text = data['desc']
+        knode = tnode.find('keywords')
+        if knode is not None:
+            knode.text = data['keyw']
+        bnode = tnode.find('binary')
+        if bnode is not None:
+            tnode.remove(bnode)
+        bnode = etree.Element('binary')
+        for f in data['files']:
+            fnode = etree.Element('file')
+            fnode.text = f
+            bnode.append(fnode)
+        tnode.append(bnode)
+    else:
+        # create new node for task
+        tnode = etree.Element('task', attrib = { 'name' : data['name'] })
+        dnode = etree.Element('description')
+        dnode.text = data['desc']
+        tnode.append(dnode)
+        knode = etree.Element('keywords')
+        knode.text = data['keyw']
+        tnode.append(knode)
+        bnode = etree.Element('binary')
+        for f in data['files']:
+            fnode = etree.Element('file')
+            fnode.text = f
+            bnode.append(fnode)
+        tnode.append(bnode)
+        tree.append(tnode)
+    
+    write_xml_modules(fXML, tree)
+
 # install extension on MS Windows
 def install_extension_win():
     ### TODO: do not use hardcoded url
@@ -271,23 +448,30 @@
         fo.close()
     except HTTPError:
         grass.fatal(_("GRASS Addons <%s> not found") % options['extension'])
+    
+    return 0
 
-    grass.message(_("Installation of <%s> successfully finished.") % options['extension'])
-
 # install extension
 def install_extension():
     gisbase = os.getenv('GISBASE')
     if not gisbase:
         grass.fatal(_('$GISBASE not defined'))
     
-    if grass.find_program(options['extension'], ['--help']):
-        grass.warning(_("Extension <%s> already installed. Will be updated...") % options['extension'])
+    if options['extension'] in get_installed_extensions(force = True):
+        grass.warning(_("Extension <%s> already installed. Re-installing...") % options['extension'])
     
     if sys.platform == "win32":
-        install_extension_win()
+        ret = install_extension_win()
     else:
-        install_extension_other()
+        ret = install_extension_other()
     
+    if ret != 0:
+        grass.warning(_('Installation failed, sorry. Please check above error messages.'))
+    else:
+        grass.message(_("Updating metadata file..."))
+        install_extension_xml()
+        grass.message(_("Installation of <%s> successfully finished") % options['extension'])
+    
     # cleanup build cruft
     if not flags['s']:
         tidy_citizen()
@@ -296,30 +480,6 @@
             not os.environ['GRASS_ADDON_PATH']:
         grass.warning(_('This add-on module will not function until you set the '
                         'GRASS_ADDON_PATH environment variable (see "g.manual variables")'))
-    
-    # manual page: fix href
-    if os.getenv('GRASS_ADDON_PATH'):
-        html_man = os.path.join(os.getenv('GRASS_ADDON_PATH'), 'docs', 'html', 'description.html')
-        if os.path.exists(html_man):
-            fd = open(html_man)
-            html_str = '\n'.join(fd.readlines())
-            fd.close()
-            for rep in ('grassdocs.css', 'grass_logo.png'):
-                patt = re.compile(rep, re.IGNORECASE)
-                html_str = patt.sub(os.path.join(gisbase, 'docs', 'html', rep),
-                                    html_str)
-                
-            patt = re.compile(r'(<a href=")(d|db|g|i|m|p|ps|r|r3|s|v|wxGUI)(\.)(.+)(.html">)', re.IGNORECASE)
-            while True:
-                m = patt.search(html_str)
-                if not m:
-                    break
-                html_str = patt.sub(m.group(1) + os.path.join(gisbase, 'docs', 'html',
-                                                              m.group(2) + m.group(3) + m.group(4)) + m.group(5),
-                                    html_str, count = 1)
-            fd = open(html_man, "w")
-            fd.write(html_str)
-            fd.close()
 
 # install extension on other plaforms
 def install_extension_other():
@@ -398,14 +558,9 @@
     
     grass.message(_("Installing <%s>...") % options['extension'])
     
-    ret = grass.call(installCmd,
-                     stdout = outdev)
-    
-    if ret != 0:
-        grass.warning(_('Installation failed, sorry. Please check above error messages.'))
-    else:
-        grass.message(_("Installation of <%s> successfully finished.") % options['extension'])
-    
+    return grass.call(installCmd,
+                      stdout = outdev)
+
 # remove dir if empty
 def remove_empty_dir(path):
     if os.path.exists(path) and not os.listdir(path):
@@ -444,41 +599,56 @@
     # if empty, rmdir bin, etc, man, scripts
     for d in ('bin', 'etc', os.path.join('man', 'man1'), 'man', 'scripts'):
         remove_empty_dir(os.path.join(options['prefix'], d))
+
+# update local meta-file when removing existing extension
+def remove_extension_xml():
+    fXML = os.path.join(options['prefix'], 'modules.xml')
+    if not os.path.exists(fXML):
+        return
     
+    # read XML file
+    fo = open(fXML, 'r')
+    tree = etree.fromstring(fo.read())
+    fo.close()
+
+    tnode = None
+    for node in tree.findall('task'):
+        if node.get('name') == options['extension']:
+            tnode = node
+            break
+
+    if tnode is not None:
+        tree.remove(tnode)
+        
+    write_xml_modules(fXML, tree)
+    
 # remove existing extension (reading XML file)
 def remove_extension(force = False):
-    # try to download XML metadata file first
-    url = "http://grass.osgeo.org/addons/grass%s.xml" % grass.version()['version'].split('.')[0]
+    # try to read XML metadata file first
+    fXML = os.path.join(options['prefix'], 'modules.xml')
     name = options['extension']
+    if name not in get_installed_extensions():
+        grass.warning(_("Extension <%s> not found") % name)
+    
     if force:
         grass.verbose(_("List of removed files:"))
     else:
         grass.info(_("Files to be removed (use flag 'f' to force removal):"))
     
-    try:
-        f = urlopen(url)
+    if os.path.exists(fXML):
+        f = open(fXML, 'r')
         tree = etree.fromstring(f.read())
         flist = []
         for task in tree.findall('task'):
             if name == task.get('name', default = '') and \
                     task.find('binary') is not None:
                 for f in task.find('binary').findall('file'):
-                    fname = f.text
-                    if fname:
-                        fpath = fname.split('/')
-                        if sys.platform == 'win32':
-                            if fpath[0] == 'bin':
-                                fpath[-1] += '.exe'
-                            if fpath[0] == 'scripts':
-                                fpath[-1] += '.py'
-                        
-                        flist.append(fpath)
+                    flist.append(f.text)
         
         if flist:
             removed = False
             err = list()
-            for f in flist:
-                fpath = os.path.join(options['prefix'], os.path.sep.join(f))
+            for fpath in flist:
                 try:
                     if force:
                         grass.verbose(fpath)
@@ -496,13 +666,15 @@
                     grass.error(e)
         else:
             remove_extension_std(force)
-    except HTTPError:
+    else:
         remove_extension_std(force)
 
     if force:
+        grass.message(_("Updating metadata file..."))
+        remove_extension_xml()
         grass.message(_("Extension <%s> successfully uninstalled.") % options['extension'])
     else:
-        grass.warning(_("Extension <%s> not removed. "
+        grass.warning(_("Extension <%s> not removed.\n"
                         "Re-run '%s' with 'f' flag to force removal") % (options['extension'], 'g.extension'))
     
 # remove exising extension (using standard files layout)
@@ -524,20 +696,17 @@
     
 # check links in CSS
 def check_style_files(fil):
-    # check the links to grassdocs.css/grass_logo.png to a correct manual page of addons
-    dist_file = os.path.join(os.getenv('GISBASE'), 'docs', 'html', fil)
+    dist_file   = os.path.join(os.getenv('GISBASE'), 'docs', 'html', fil)
     addons_file = os.path.join(options['prefix'], 'docs', 'html', fil)
-    # check if file already exists in the grass addons docs html path
+    
     if os.path.isfile(addons_file):
 	return
-    # otherwise copy the file from $GISBASE/docs/html, it doesn't use link 
-    # because os.symlink it work only in Linux
-    else:
-	try:
-	    shutil.copyfile(dist_file,addons_file)
-	except OSError, e:
-	    grass.fatal(_("Unable to create '%s': %s") % (addons_file, e))
-
+    
+    try:
+        shutil.copyfile(dist_file,addons_file)
+    except OSError, e:
+        grass.fatal(_("Unable to create '%s': %s") % (addons_file, e))
+    
 def create_dir(path):
     if os.path.isdir(path):
         return
@@ -564,14 +733,6 @@
     if sys.platform != "win32":
         check_progs()
     
-    # list available modules
-    if flags['l'] or flags['c'] or flags['g']:
-        list_available_modules()
-        return 0
-    else:
-        if not options['extension']:
-            grass.fatal(_('You need to define an extension name or use -l'))
-    
     # define path
     if flags['s']:
         options['prefix'] = os.environ['GISBASE']
@@ -590,6 +751,20 @@
                 grass.warning(_("GRASS_ADDON_PATH has more items, using first defined - '%s'") % path_list[0])
             options['prefix'] = path_list[0]
     
+    # list available modules
+    if flags['l'] or flags['c'] or flags['g']:
+        list_available_extensions()
+        return 0
+    elif flags['a']:
+        grass.message(_("List of installed extensions:"))
+        elist = get_installed_extensions()
+        if elist:
+            print os.linesep.join(elist)
+        return 0
+    else:
+        if not options['extension']:
+            grass.fatal(_('You need to define an extension name or use -l'))
+
     if flags['d']:
         if options['operation'] != 'add':
             grass.warning(_("Flag 'd' is relevant only to 'operation=add'. Ignoring this flag."))

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py	2011-12-14 11:38:11 UTC (rev 49737)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py	2011-12-14 11:57:31 UTC (rev 49738)
@@ -497,6 +497,7 @@
                                 shell = sys.platform == "win32")
         except OSError, e:
             self.error = str(e)
+            print >> sys.stderr, e
             return 1
         
         if self.stdin: # read stdin if requested ...

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py	2011-12-14 11:38:11 UTC (rev 49737)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py	2011-12-14 11:57:31 UTC (rev 49738)
@@ -11,6 +11,8 @@
  - AboutWindow
  - InstallExtensionWindow
  - ExtensionTree
+ - UninstallExtensionWindow
+ - CheckListExtension
  - HelpFrame
  - HelpWindow
  - HelpPanel
@@ -27,6 +29,7 @@
 import sys
 
 import wx
+import wx.lib.mixins.listctrl as listmix
 try:
     import wx.lib.agw.customtreectrl as CT
 #    import wx.lib.agw.hyperlink as hl
@@ -812,7 +815,7 @@
         
         task = gtask.parse_interface('g.extension.py')
         
-        ignoreFlags = ['l', 'c', 'g', 'f', 'quiet', 'verbose']
+        ignoreFlags = ['l', 'c', 'g', 'a', 'f', 'quiet', 'verbose']
         if sys.platform == 'win32':
             ignoreFlags.append('d')
             ignoreFlags.append('i')
@@ -1120,6 +1123,131 @@
         """Check if items are loaded"""
         return self._loaded
 
+class UninstallExtensionWindow(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("Uninstall GRASS Addons extensions"), **kwargs):
+        self.parent = parent
+        
+        wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        self.extBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                   label = " %s " % _("List of installed extensions"))
+        
+        self.extList = CheckListExtension(parent = self.panel)
+
+        # buttons
+        self.btnUninstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("&Uninstall"))
+        self.btnUninstall.SetToolTipString(_("Uninstall selected AddOns extensions"))
+        self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                label = _("Command dialog"))
+        self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension')
+        self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+        
+        self.btnUninstall.Bind(wx.EVT_BUTTON, self.OnUninstall)
+        self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+        self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+        
+        self._layout()
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        extSizer = wx.StaticBoxSizer(self.extBox, wx.HORIZONTAL)
+        extSizer.Add(item = self.extList, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 1)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnCmd, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.AddSpacer(10)
+        btnSizer.Add(item = self.btnClose, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.Add(item = self.btnUninstall, proportion = 0)
+        
+        sizer.Add(item = extSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def OnCloseWindow(self, event):
+        """!Close window"""
+        self.Destroy()
+
+    def OnUninstall(self, event):
+        """!Uninstall selected extensions"""
+        log = self.parent.GetLogWindow()
+        eList = self.extList.GetExtensions()
+        if not eList:
+            gcmd.GError(_("No extension selected for removal. "
+                          "Operation canceled."),
+                        parent = self)
+            return
+        
+        for ext in eList:
+            files = gcmd.RunCommand('g.extension.py', parent = self, read = True, quiet = True,
+                                    extension = ext, operation = 'remove').splitlines()
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _("List of files to be removed:\n%(files)s\n\n"
+                                               "Do you want really to remove <%(ext)s> extension?") % \
+                                       { 'files' : os.linesep.join(files), 'ext' : ext },
+                                   caption = _("Remove extension"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+            
+            if dlg.ShowModal() ==  wx.ID_YES:
+                gcmd.RunCommand('g.extension.py', flags = 'f', parent = self, quiet = True,
+                                extension = ext, operation = 'remove')
+        
+        self.extList.LoadData()
+        
+    def OnCmdDialog(self, event):
+        """!Shows command dialog"""
+        menuform.GUI(parent = self).ParseCommand(cmd = ['g.extension.py'])
+
+class CheckListExtension(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
+    """!List of mapset/owner/group"""
+    def __init__(self, parent):
+        self.parent = parent
+        
+        wx.ListCtrl.__init__(self, parent, id = wx.ID_ANY,
+                             style = wx.LC_REPORT)
+        listmix.CheckListCtrlMixin.__init__(self)
+        
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+        self.InsertColumn(0, _('Extension'))
+        self.LoadData()
+        
+    def LoadData(self):
+        """!Load data into list"""
+        self.DeleteAllItems()
+        for ext in gcmd.RunCommand('g.extension.py',
+                                   quiet = True, parent = self, read = True,
+                                   flags = 'a').splitlines():
+            self.InsertStringItem(sys.maxint, ext)
+
+    def GetExtensions(self):
+        """!Get extensions to be un-installed
+        """
+        extList = list()
+        for i in range(self.GetItemCount()):
+            if self.IsChecked(i):
+                name = self.GetItemText(i)
+                if name:
+                    extList.append(name)
+        
+        return extList
+
 class HelpWindow(wx.html.HtmlWindow):
     """!This panel holds the text from GRASS docs.
     

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py	2011-12-14 11:38:11 UTC (rev 49737)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py	2011-12-14 11:57:31 UTC (rev 49738)
@@ -531,7 +531,10 @@
                 
                 if len(command) == 1:
                     import menuform
-                    task = gtask.parse_interface(command[0])
+                    try:
+                        task = gtask.parse_interface(command[0])
+                    except gcmd.ScriptError, e:
+                        print >> sys.stderr, e
                     # if not task.has_required():
                     # task = None # run command
                 else:

Modified: grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py	2011-12-14 11:38:11 UTC (rev 49737)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py	2011-12-14 11:57:31 UTC (rev 49738)
@@ -82,7 +82,7 @@
 from gui_modules import vclean
 from gui_modules import nviz_tools
 from gui_modules.debug    import Debug
-from gui_modules.ghelp    import MenuTreeWindow, AboutWindow, InstallExtensionWindow
+from gui_modules.ghelp    import MenuTreeWindow, AboutWindow, InstallExtensionWindow, UninstallExtensionWindow
 from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar, LMMiscToolbar, LMVectorToolbar
 from gui_modules.gpyshell import PyShellWindow
 from icons.icon           import Icons
@@ -1054,6 +1054,12 @@
         win.CentreOnScreen()
         win.Show()
 
+    def OnUninstallExtension(self, event):
+        """!Uninstall extension"""
+        win = UninstallExtensionWindow(self, size = (650, 300))
+        win.CentreOnScreen()
+        win.Show()
+
     def OnPreferences(self, event):
         """!General GUI preferences/settings
         """

Modified: grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml	2011-12-14 11:38:11 UTC (rev 49737)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml	2011-12-14 11:57:31 UTC (rev 49738)
@@ -835,11 +835,18 @@
 	<separator />
 	    <menuitem>
 	      <label>Install extension from add-ons</label>
-	      <help>Install new extension from GRASS AddOns SVN repository.</help>
+	      <help>Installs new extension from GRASS AddOns SVN repository.</help>
 	      <keywords>general,extensions</keywords>
 	      <handler>OnInstallExtension</handler>
 	      <command>g.extension</command>
 	    </menuitem>
+	    <menuitem>
+	      <label>Remove extension</label>
+	      <help>Removes installed GRASS AddOns extension.</help>
+	      <keywords>general,extensions</keywords>
+	      <handler>OnUninstallExtension</handler>
+	      <command>g.extension</command>
+	    </menuitem>
 	<separator />
 	<menuitem>
 	  <label>Preferences</label>



More information about the grass-commit mailing list