[GRASS-SVN] r54607 - in grass/trunk/gui/wxpython: . core gcp gui_core lmgr mapdisp ogc_services web_services xml

svn_grass at osgeo.org svn_grass at osgeo.org
Sat Jan 12 08:41:52 PST 2013


Author: martinl
Date: 2013-01-12 08:41:52 -0800 (Sat, 12 Jan 2013)
New Revision: 54607

Added:
   grass/trunk/gui/wxpython/web_services/
   grass/trunk/gui/wxpython/web_services/cap_interface.py
   grass/trunk/gui/wxpython/web_services/dialogs.py
   grass/trunk/gui/wxpython/web_services/widgets.py
Modified:
   grass/trunk/gui/wxpython/Makefile
   grass/trunk/gui/wxpython/core/events.py
   grass/trunk/gui/wxpython/core/gconsole.py
   grass/trunk/gui/wxpython/core/render.py
   grass/trunk/gui/wxpython/core/utils.py
   grass/trunk/gui/wxpython/core/ws.py
   grass/trunk/gui/wxpython/gcp/mapdisplay.py
   grass/trunk/gui/wxpython/gui_core/gselect.py
   grass/trunk/gui/wxpython/gui_core/mapwindow.py
   grass/trunk/gui/wxpython/gui_core/widgets.py
   grass/trunk/gui/wxpython/lmgr/frame.py
   grass/trunk/gui/wxpython/lmgr/layertree.py
   grass/trunk/gui/wxpython/lmgr/toolbars.py
   grass/trunk/gui/wxpython/mapdisp/mapwindow.py
   grass/trunk/gui/wxpython/ogc_services/wms.py
   grass/trunk/gui/wxpython/xml/menudata.xml
Log:
wxGUI: fully-featured support for web services (work in progress)
       patch provided by Stepan Turek


Modified: grass/trunk/gui/wxpython/Makefile
===================================================================
--- grass/trunk/gui/wxpython/Makefile	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/Makefile	2013-01-12 16:41:52 UTC (rev 54607)
@@ -11,12 +11,12 @@
 
 SRCFILES := $(wildcard icons/*.* scripts/* xml/*) \
 	$(wildcard animation/* core/* dbmgr/* gcp/* gmodeler/* gui_core/* iclass/* lmgr/* location_wizard/* \
-	mapdisp/* modules/* nviz/* psmap/* mapswipe/* vdigit/* wxplot/* ogc_services/* rlisetup/* vnet/*) \
+	mapdisp/* modules/* nviz/* psmap/* mapswipe/* vdigit/* wxplot/* web_services/* rlisetup/* vnet/*) \
 	gis_set.py gis_set_error.py wxgui.py README
 DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 
 PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler gui_core iclass lmgr location_wizard \
-	mapdisp modules nviz psmap mapswipe vdigit wxplot ogc_services rlisetup vnet)
+	mapdisp modules nviz psmap mapswipe vdigit wxplot web_services rlisetup vnet)
 DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
 
 default: $(DSTFILES)

Modified: grass/trunk/gui/wxpython/core/events.py
===================================================================
--- grass/trunk/gui/wxpython/core/events.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/core/events.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -44,3 +44,14 @@
 gMapCreated, EVT_MAP_CREATED = NewCommandEvent()
 
 gZoomChanged, EVT_ZOOM_CHANGED = NewEvent()
+
+# Post it to BufferedWindow instance, which you want to update.
+# For relevant attributes for the event see 
+# mapdisp.mapwindow.BufferedWindow UpdateMap method arguments.
+# If event does not contain attribute corresponding to argument with default
+# value, the default value will be used.
+# Arguments with no default value must be among event attributes
+# in order to be the event processed.
+# TODO implement to NVIZ GLWindow
+# TODO change direct calling of UpdateMap method to posting this event 
+gUpdateMap, EVT_UPDATE_MAP = NewEvent()

Modified: grass/trunk/gui/wxpython/core/gconsole.py
===================================================================
--- grass/trunk/gui/wxpython/core/gconsole.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/core/gconsole.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -126,11 +126,15 @@
             requestTime = time.time()
 
             # prepare
+            if not self.receiver:
+                return
+
             event = wxCmdPrepare(cmd=args[0],
                                  time=requestTime,
                                  pid=requestId,
                                  onPrepare=vars()['onPrepare'],
                                  userData=vars()['userData'])
+
             wx.PostEvent(self.receiver, event)
 
             # run command
@@ -188,6 +192,9 @@
                     self.requestCmdColor = vars()['callable'](*argsColor, **kwds)
                     self.resultQ.put((requestId, self.requestCmdColor.run()))
 
+            if not self.receiver:
+                return
+
             event = wxCmdDone(cmd=args[0],
                               aborted=aborted,
                               returncode=returncode,

Modified: grass/trunk/gui/wxpython/core/render.py
===================================================================
--- grass/trunk/gui/wxpython/core/render.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/core/render.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -6,6 +6,8 @@
 @todo Implement RenderManager also for other layers (see WMS
 implementation for details)
 
+ at todo Render classes should not care about updating statusbar (change emiting events).
+
 Classes:
  - render::Layer
  - render::MapLayer
@@ -36,13 +38,14 @@
 from grass.script import core as grass
 
 from core          import utils
-from core.ws       import RenderWMSMgr
 from core.gcmd     import GException, GError, RunCommand
 from core.debug    import Debug
 from core.settings import UserSettings
 
 wxUpdateProgressBar, EVT_UPDATE_PRGBAR = NewEvent()
 
+from core.ws       import RenderWMSMgr
+
 USE_GPNMCOMP = True
 
 class Layer(object):
@@ -51,10 +54,8 @@
     
     - For map layer use MapLayer class.
     - For overlays use Overlay class.
-
-    @todo needs refactoring (avoid parent)
     """
-    def __init__(self, ltype, cmd, parent, name = None,
+    def __init__(self, ltype, cmd, Map, name = None,
                  active = True, hidden = False, opacity = 1.0):
         """!Create new instance
         
@@ -63,12 +64,12 @@
         @param ltype layer type ('raster', 'vector', 'overlay', 'command', etc.)
         @param cmd GRASS command to render layer,
         given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
+        @param Map render.Map instance
         @param name layer name, e.g. 'elevation at PERMANENT' (for layer tree)
         @param active layer is active, will be rendered only if True
         @param hidden layer is hidden, won't be listed in Layer Manager if True
         @param opacity layer opacity <0;1>
         """
-        self.parent = parent
         
         # generated file for each layer
         if USE_GPNMCOMP or ltype == 'overlay':
@@ -84,6 +85,8 @@
         
         # stores class which manages rendering instead of simple command - e. g. wms
         self.renderMgr = None
+
+        self.Map = Map
         self.type      = None
         self.SetType(ltype)
         self.name  = name
@@ -178,8 +181,6 @@
     
     def _runCommand(self, cmd):
         """!Run command to render data
-
-        @todo catch error for wms (?)
         """ 
         if self.type == 'wms':
             ret = 0
@@ -268,7 +269,11 @@
             raise GException(_("Unsupported map layer type '%s'") % ltype)
         
         if ltype == 'wms' and not isinstance(self.renderMgr, RenderWMSMgr):
-            self.renderMgr = RenderWMSMgr(self, self.mapfile, self.maskfile)
+            self.renderMgr = RenderWMSMgr(receiver = self.Map.GetReceiver(),
+                                          layer = self, 
+                                          Map = self.Map, 
+                                          mapfile = self.mapfile, 
+                                          maskfile = self.maskfile)
         elif self.type == 'wms' and ltype != 'wms':
             self.renderMgr = None
         
@@ -315,27 +320,32 @@
         else:
             return self.renderMgr.IsDownloading()
 
-    def AbortDownload(self):
-        """!Abort downloading data"""
+    def AbortThread(self):
+        """!Abort running thread e. g. downloading data"""
         if self.renderMgr is None:
             return
         else:
             self.renderMgr.Abort()
 
+    def GetRenderMgr(self):
+        """!Get render manager """
+        return self.renderMgr
+
 class MapLayer(Layer):
-    def __init__(self, ltype, cmd, parent, name = None,
+    def __init__(self, ltype, cmd, Map, name = None,
                  active = True, hidden = False, opacity = 1.0): 
         """!Represents map layer in the map canvas
         
         @param ltype layer type ('raster', 'vector', 'command', etc.)
         @param cmd GRASS command to render layer,
         given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
+        @param Map render.Map instance
         @param name layer name, e.g. 'elevation at PERMANENT' (for layer tree) or None
         @param active layer is active, will be rendered only if True
         @param hidden layer is hidden, won't be listed in Layer Manager if True
         @param opacity layer opacity <0;1>
         """
-        Layer.__init__(self, ltype, cmd, parent, name,
+        Layer.__init__(self, ltype, cmd, Map, name,
                        active, hidden, opacity)
         
     def GetMapset(self):
@@ -353,7 +363,7 @@
             return self.name
         
 class Overlay(Layer):
-    def __init__(self, id, ltype, cmd, parent,
+    def __init__(self, id, ltype, cmd, Map,
                  active = True, hidden = True, opacity = 1.0):
         """!Represents overlay displayed in map canvas
         
@@ -361,11 +371,12 @@
         @param type overlay type ('barscale', 'legend', etc.)
         @param cmd GRASS command to render overlay,
         given as list, e.g. ['d.legend', 'map=elevation at PERMANENT']
+        @param Map render.Map instance
         @param active layer is active, will be rendered only if True
         @param hidden layer is hidden, won't be listed in Layer Manager if True
         @param opacity layer opacity <0;1>
         """
-        Layer.__init__(self, 'overlay', cmd, parent, ltype,
+        Layer.__init__(self, 'overlay', cmd, Map, ltype,
                        active, hidden, opacity)
         self.id = id
         
@@ -400,8 +411,8 @@
         self._initGisEnv() # g.gisenv
         self.GetWindow()
 
-        # reference to mapWindow, which contains this Map instance
-        self.mapWin = None
+        # receiver of events
+        self.receiver = None
 
         # GRASS environment variable (for rendering)
         self.env = {"GRASS_BACKGROUNDCOLOR" : "FFFFFF",
@@ -607,7 +618,7 @@
         @param update if True update current display region settings
         @param add3d add 3d region settings
         
-        @return region settings as directory, e.g. {
+        @return region settings as dictionary, e.g. {
         'n':'4928010', 's':'4913700', 'w':'589980',...}
         
         @see GetCurrentRegion()
@@ -796,7 +807,7 @@
         
         except:
             return None
-        
+    
     def GetListOfLayers(self, ltype = None, mapset = None, name = None,
                         active = None, hidden = None):
         """!Returns list of layers of selected properties or list of
@@ -865,11 +876,10 @@
         
         return selected
 
-    def _renderLayers(self, force = False, mapWindow = None, overlaysOnly = False):
+    def _renderLayers(self, force = False, overlaysOnly = False):
         """!Render all map layers into files
 
         @param force True to force rendering
-        @param mapWindow GUI window or None (statusbar/progress bar)
         @param overlaysOnly True to render only overlays
 
         @return list of maps, masks and opacities
@@ -884,13 +894,9 @@
             layers = self.layers + self.overlays
         
         self.downloading = False
-        ### event = wxUpdateProgressBar(layer = None, map = self)
-        # When using Event for progress update, the event is handled after 
-        # rendering. Maybe there exists some better solution than calling 
-        # the method directly.
-        if mapWindow:
-            mapWindow.GetProgressBar().UpdateProgress(None, self)
-        ### wx.PostEvent(mapWindow, event)
+        if self.receiver:
+            event = wxUpdateProgressBar(layer = None, map = self)
+            self.receiver.GetEventHandler().ProcessEvent(event)
         for layer in layers:
             # skip non-active map layers
             if not layer or not layer.active:
@@ -903,12 +909,9 @@
 
             if layer.IsDownloading():
                 self.downloading = True     
-            if mapWindow:
-                # update progress bar
-                ### wx.SafeYield(mapWindow)
-                mapWindow.GetProgressBar().UpdateProgress(layer, self)
-                #event = wxUpdateProgressBar(layer = layer, map = self)
-                #wx.PostEvent(mapWindow, event)
+            if self.receiver:
+                event = wxUpdateProgressBar(layer = layer, map = self)
+                self.receiver.GetEventHandler().ProcessEvent(event) 
 
             # skip map layers when rendering fails
             if not os.path.exists(layer.mapfile):
@@ -924,22 +927,21 @@
         
         return maps, masks, opacities
         
-    def GetMapsMasksAndOpacities(self, force, guiFrame, windres):
+    def GetMapsMasksAndOpacities(self, force, windres):
         """!
         Used by Render function.
         
         @return maps, masks, opacities
         """
-        return self._renderLayers(force, guiFrame)
+        return self._renderLayers(force)
     
-    def Render(self, force = False, mapWindow = None, windres = False):
+    def Render(self, force = False, windres = False):
         """!Creates final image composite
         
         This function can conditionaly use high-level tools, which
         should be avaliable in wxPython library
         
         @param force force rendering
-        @param mapWindow reference for MapFrame or similar instance for progress bar
         @param windres use region resolution (True) otherwise display resolution
         
         @return name of file with rendered image or None
@@ -960,7 +962,7 @@
         else:
             os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
         
-        maps, masks, opacities = self.GetMapsMasksAndOpacities(force, mapWindow, windres)
+        maps, masks, opacities = self.GetMapsMasksAndOpacities(force, windres)
         
         # ugly hack for MSYS
         if sys.platform != 'win32':
@@ -1038,7 +1040,7 @@
             opacity = 0
         elif opacity > 1:
             opacity = 1
-        layer = MapLayer(ltype = ltype, name = name, cmd = command, parent = self,
+        layer = MapLayer(ltype = ltype, name = name, cmd = command, Map = self,
                          active = active, hidden = hidden, opacity = opacity)
         
         # add maplayer to the list of layers
@@ -1189,7 +1191,7 @@
         Layer is defined by name at mapset or id.
         
         @param name layer name (must be unique)
-        @param id layer index in layer list
+        @param id layer index in layer list    def __init__(self, targetFile, region, bandsNum, gdalDriver, fillValue = None):
 
         @return removed layer on success
         @return None on failure
@@ -1245,7 +1247,7 @@
         @return None on failure
         """
         Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, render))
-        overlay = Overlay(id = id, ltype = ltype, cmd = command, parent = self,
+        overlay = Overlay(id = id, ltype = ltype, cmd = command, Map = self,
                           active = active, hidden = hidden, opacity = opacity)
         
         # add maplayer to the list of layers
@@ -1356,14 +1358,22 @@
             if force or layer.forceRender:
                 layer.Render()
 
-    def SetParentMapWindow(self, mapWin):
-        """!Set reference to parent MapWindow"""
-        self.mapWin = mapWin
+    def GetReceiver(self):
+        """!Get event receiver"""
+        return self.receiver
 
-    def GetParentMapWindow(self):
-        """!Get reference to parent MapWindow"""
-        return self.mapWin
+    def SetReceiver(self, receiver):
+        """!Set events receiver
 
-    def IsDownloading(self):
-        """!Is any layer downloading data from web server e. g. wms"""
-        return self.downloading
+        @todo  If it will be needed to change receiver, take care of running threads.
+        """
+        self.receiver = receiver
+        for l in self.overlays + self.layers:
+            if l.GetRenderMgr():
+                l.GetRenderMgr().SetReceiver(self.receiver)
+
+    def AbortAllThreads(self, old_receiver = None):
+        """!Abort all layers threads e. g. donwloading data"""
+        for l in self.layers + self.overlays:
+            l.AbortThread(old_receiver)
+ 
\ No newline at end of file

Modified: grass/trunk/gui/wxpython/core/utils.py
===================================================================
--- grass/trunk/gui/wxpython/core/utils.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/core/utils.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -19,6 +19,7 @@
 import glob
 import shlex
 import re
+import inspect
 
 from core.globalvar import ETCDIR
 sys.path.append(os.path.join(ETCDIR, "python"))
@@ -907,3 +908,37 @@
             rgb = (200, 200, 200)
             label = _('Select Color')
     return (rgb, label)
+
+def GetGEventAttribsForHandler(method, event):
+    """!Get attributes from event, which can be used by handler method. 
+
+    Be aware of event class attributes.
+
+    @param method - handler method (including self arg)
+    @param event - event
+
+    @return (valid kwargs for method, 
+             list of method's args without default value 
+             which were not found among event attributes)
+    """
+    args_spec = inspect.getargspec(method)
+
+    args = args_spec[0]
+
+    defaults =[]
+    if args_spec[3]:
+        defaults =  args_spec[3]
+
+    # number of arguments without def value
+    req_args = len(args) - 1 - len(defaults)
+
+    kwargs = {}
+    missing_args = []
+
+    for i, a in enumerate(args):
+        if hasattr(event, a):
+            kwargs[a] = getattr(event, a)
+        elif i < req_args:
+            missing_args.append(a)
+
+    return kwargs, missing_args

Modified: grass/trunk/gui/wxpython/core/ws.py
===================================================================
--- grass/trunk/gui/wxpython/core/ws.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/core/ws.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -7,7 +7,6 @@
 
 Classes:
  - ws::RenderWMSMgr
- - ws::StdErr
  - ws::GDALRasterMerger
 
 (C) 2012 by the GRASS Development Team
@@ -25,9 +24,11 @@
 from grass.script import core as grass
 
 from core          import utils
+from core.events   import gUpdateMap
+from core.render   import wxUpdateProgressBar
 from core.debug    import Debug
 
-from core.gconsole import CmdThread, EVT_CMD_DONE
+from core.gconsole import CmdThread, GStderr, EVT_CMD_DONE, EVT_CMD_OUTPUT
 from core.gcmd     import GException
 
 try:
@@ -39,31 +40,35 @@
 
 class RenderWMSMgr(wx.EvtHandler):
     """!Fetch and prepare WMS data for rendering.
-
-    @todo statusbar: updtade by event or call method ???
     """
-    def __init__(self, parent, mapfile, maskfile):
+    def __init__(self, receiver, layer, Map, mapfile, maskfile):
         if not haveGdal:
             sys.stderr.write(_("Unable to load GDAL Python bindings.\n"\
                                "WMS layers can not be displayed without the bindings.\n"))
-        
-        self.parent = parent
+    
+        self.Map = Map
+        self.receiver = receiver
+        self.layer = layer
 
+        wx.EvtHandler.__init__(self)
+
         # thread for d.wms commands
         self.thread = CmdThread(self)
-        wx.EvtHandler.__init__(self)
         self.Bind(EVT_CMD_DONE, self.OnDataFetched)
 
         self.downloading = False
         self.renderedRegion = None
         self.updateMap = True
+        self.fetched_data_cmd = None
 
-        self.cmdStdErr = StdErr()
+        self.cmdStdErr = GStderr(self)
 
         self.mapfile = mapfile
         self.maskfile = maskfile
         self.tempMap = grass.tempfile()
         self.dstSize = {}
+ 
+        self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
 
     def __del__(self):
         grass.try_remove(self.tempMap)
@@ -87,7 +92,8 @@
         fetchData = False
         zoomChanged = False
 
-        if self.renderedRegion is None:
+        if self.renderedRegion is None or \
+           cmd != self.fetched_data_cmd:
             fetchData = True
         else:
             for c in ['north', 'south', 'east', 'west']:
@@ -103,6 +109,7 @@
                     break
 
         if fetchData:
+            self.fetched_data_cmd = None
             self.renderedRegion = region
 
             grass.try_remove(self.mapfile)
@@ -112,12 +119,15 @@
             self.thread.abort()
             self.downloading = True
 
+            self.fetching_cmd = cmd
             cmdList = utils.CmdTupleToList(cmd)
 
             if Debug.GetLevel() < 3:
                 cmdList.append('--quiet')
-                
-            tempPngfile = os.environ["GRASS_PNGFILE"]
+            
+            tempPngfile = None
+            if "GRASS_PNGFILE" in os.environ:  
+                tempPngfile = os.environ["GRASS_PNGFILE"]
             os.environ["GRASS_PNGFILE"] = self.tempMap
 
             tempRegion = os.environ["GRASS_REGION"]
@@ -125,22 +135,34 @@
 
             self.thread.RunCmd(cmdList, env = os.environ.copy(), stderr = self.cmdStdErr)
 
-            os.environ["GRASS_PNGFILE"] = tempPngfile
+            os.environ.pop("GRASS_PNGFILE")
+            if tempPngfile:
+                os.environ["GRASS_PNGFILE"] = tempPngfile
+
             os.environ["GRASS_REGION"] = tempRegion
 
+    def OnCmdOutput(self, event):
+        """!Print cmd output according to debug level.
+        """
+        if Debug.GetLevel() == 0:
+            if event.type == 'error':
+                sys.stderr.write(event.text)
+                sys.stderr.flush()
+        else:
+            Debug.msg(1, event.text)
+
     def OnDataFetched(self, event):
         """!Fetch data
-
-        @todo ?
-        @todo needs refactoring -  self.parent.parent.mapWin.UpdateMap
         """
         if event.pid != self.currentPid:
             return
         self.downloading = False
         if not self.updateMap:
-            # TODO
-            self.parent.parent.GetParentMapWindow().frame.GetProgressBar().UpdateProgress(self.parent, self.parent.parent)
+            if self.receiver:
+                event = wxUpdateProgressBar(layer = self.layer, map = self.Map)
+                self.receiver.GetEventHandler().ProcessEvent(event) 
             self.renderedRegion = None
+            self.fetched_data_cmd = None
             return
 
         self.mapMerger = GDALRasterMerger(targetFile = self.mapfile, region = self.renderedRegion,
@@ -154,8 +176,12 @@
         self.maskMerger.AddRasterBands(self.tempMap, {4 : 1}) 
         del self.maskMerger
 
-        self.parent.parent.GetParentMapWindow().UpdateMap(render = True)
+        self.fetched_data_cmd = self.fetching_cmd
 
+        if self.receiver:
+            event = gUpdateMap()
+            wx.PostEvent(self.receiver, event)
+
     def _getRegionDict(self):
         """!Parse string from GRASS_REGION env variable into dict.
         """
@@ -219,37 +245,13 @@
         self.updateMap = False
         self.thread.abort(abortall = True)        
 
-class StdErr:
-    """!Redirect error output according to debug mode.
+    def SetReceiver(self, receiver):
+        """!Set events receiver
 
-    @todo: replace or move to the other module (gconsole???)
-    """
-    def flush(self):
-        pass
-    
-    def write(self, s):
-        if "GtkPizza" in s:
-            return
-        if Debug.GetLevel() == 0:
-            message = ''
-            for line in s.splitlines():
-                if len(line) == 0:
-                    continue
-                if 'GRASS_INFO_ERROR' in line:
-                    message += line.split(':', 1)[1].strip() + '\n'
-                elif 'ERROR:' in line:
-                    message = line
-                if message:
-                    sys.stderr.write(message)
-                    message = ''
-            sys.stderr.flush()
+        @todo  If it will be needed to change receiver, take care of running threads.
+        """        
+        self.receiver = receiver
 
-        elif Debug.GetLevel() >= 1:
-            for line in s.splitlines():
-                if len(line) == 0:
-                    continue
-                Debug.msg(3, line)
-
 class GDALRasterMerger:
     """!Merge rasters.
 
@@ -341,7 +343,16 @@
         lrx = geoTrans[0] + size['cols'] * geoTrans[1]
         lry = geoTrans[3] + size['rows'] * geoTrans[5]
 
-        return ulx, uly, lrx, lry
+        return ulx, uly, lrx, lry 
 
+    def SetGeorefAndProj(self):
+        """!Set georeference and projection to target file
+        """
+        projection = grass.read_command('g.proj', 
+                                        flags = 'wf')
+        self.tDataset.SetProjection(projection)
+
+        self.tDataset.SetGeoTransform(self.tGeotransform)
+
     def __del__(self):
         self.tDataset = None

Modified: grass/trunk/gui/wxpython/gcp/mapdisplay.py
===================================================================
--- grass/trunk/gui/wxpython/gcp/mapdisplay.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/gcp/mapdisplay.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -23,7 +23,6 @@
 import wx
 import wx.aui
 
-from core.render       import EVT_UPDATE_PRGBAR
 from mapdisp.toolbars  import MapToolbar
 from gcp.toolbars      import GCPDisplayToolbar, GCPManToolbar
 from mapdisp.gprint    import PrintOptions
@@ -134,7 +133,6 @@
         #
         # Bind various events
         #
-        self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
         self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
         
         #

Modified: grass/trunk/gui/wxpython/gui_core/gselect.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/gselect.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/gui_core/gselect.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -54,6 +54,8 @@
 import grass.temporal as tgis
 from   grass.script import task as gtask
 
+from gui_core.widgets  import ManageSettingsWidget, EVT_SETTINGS_CHANGED, EVT_SETTINGS_SAVING
+
 from core.gcmd     import RunCommand, GError, GMessage
 from core.utils    import GetListOfLocations, GetListOfMapsets, GetFormats
 from core.utils    import GetSettingsPath, GetValidLayerName, ListSortLower
@@ -1162,9 +1164,17 @@
         self.dest   = dest 
         wx.Panel.__init__(self, parent = panel, id = wx.ID_ANY)
 
-        self.settingsBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
-                                        label = " %s " % _("Settings"))
+        if self.ogr:
+            settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
+        else:
+            settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
         
+        self.settsManager = ManageSettingsWidget(parent = self, 
+                                                 id = wx.ID_ANY,
+                                                 settingsFile = settingsFile)
+        self.settsManager.Bind(EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
+        self.settsManager.Bind(EVT_SETTINGS_SAVING, self.OnSettingsSaving)
+
         self.inputBox = wx.StaticBox(parent = self, id = wx.ID_ANY)
         if dest:
             self.inputBox.SetLabel(" %s " % _("Output settings"))
@@ -1208,21 +1218,6 @@
             sources.append(_("PostGIS (PG)"))
             self.sourceMap['db-pg'] = idx
         
-        if self.ogr:
-            self.settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
-        else:
-            self.settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
-
-        self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
-        self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsLoad)
-        self._settings = self._loadSettings() # -> self.settingsChoice.SetItems()
-        self.btnSettingsSave = wx.Button(parent = self, id = wx.ID_SAVE)
-        self.btnSettingsSave.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
-        self.btnSettingsSave.SetToolTipString(_("Save current settings"))
-        self.btnSettingsDel = wx.Button(parent = self, id = wx.ID_REMOVE)
-        self.btnSettingsDel.Bind(wx.EVT_BUTTON, self.OnSettingsDelete)
-        self.btnSettingsSave.SetToolTipString(_("Delete currently selected settings"))
-        
         self.source = wx.RadioBox(parent = self, id = wx.ID_ANY,
                                   style = wx.RA_SPECIFY_COLS,
                                   choices = sources)
@@ -1360,23 +1355,7 @@
     def _layout(self):
         """!Layout"""
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        
-        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
-        settingsSizer.Add(item = wx.StaticText(parent = self,
-                                               id = wx.ID_ANY,
-                                               label = _("Load settings:")),
-                          flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
-                          border  = 5)
-        settingsSizer.Add(item = self.settingsChoice,
-                          proportion = 1,
-                          flag = wx.EXPAND)
-        settingsSizer.Add(item = self.btnSettingsSave,
-                          flag = wx.LEFT | wx.RIGHT,
-                          border = 5)
-        settingsSizer.Add(item = self.btnSettingsDel,
-                          flag = wx.RIGHT,
-                          border = 5)
-        
+                
         inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
         
         self.dsnSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
@@ -1417,7 +1396,7 @@
         inputSizer.Add(item=self.dsnSizer, proportion = 1,
                        flag=wx.EXPAND | wx.BOTTOM, border = 10)
         
-        mainSizer.Add(item=settingsSizer, proportion=0,
+        mainSizer.Add(item=self.settsManager, proportion=0,
                       flag=wx.ALL | wx.EXPAND, border=5)
         mainSizer.Add(item=self.source, proportion=0,
                       flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5)
@@ -1427,6 +1406,33 @@
         self.SetSizer(mainSizer)
         mainSizer.Fit(self)
 
+    def OnSettingsChanged(self, event):
+        """!User changed setting"""
+        data = event.data
+
+        # data list: [type, dsn, format, options]
+        if len(data) == 3:
+            data.append('')
+        elif len < 3:
+            return        
+
+        self.OnSetType(event = None, sel = self.sourceMap[data[0]])
+        self.OnSetFormat(event = None, format = data[2])
+        self.OnSetDsn(event = None, path = data[1])
+        self.creationOpt.SetValue(data[3])
+
+    def OnSettingsSaving(self, event):
+        """!Saving data"""
+        if not self.GetDsn():
+            GMessage(parent = self,
+                     message = _("No data source defined, settings are not saved."))
+            return
+
+        self.settsManager.SetDataToSave((self.dsnType, self.GetDsn(),
+                                         self.format.GetStringSelection(),
+                                         self.creationOpt.GetValue()))
+        event.Skip()
+
     def _getExtPatternGlob(self, ext):
         """!Get pattern for case-insensitive globing"""
         pattern = '*.'
@@ -1438,123 +1444,7 @@
         """!Get pattern for case-insensitive file mask"""
         return '*.%s;*.%s' % (ext.lower(), ext.upper())
 
-    def OnSettingsLoad(self, event):
-        """!Load named settings"""
-        name = event.GetString()
-        if name not in self._settings:
-            GError(parent = self,
-                   message = _("Settings <%s> not found") % name)
-            return
-        data = self._settings[name]
-        self.OnSetType(event = None, sel = self.sourceMap[data[0]])
-        self.OnSetFormat(event = None, format = data[2])
-        self.OnSetDsn(event = None, path = data[1])
-        self.creationOpt.SetValue(data[3])
-        
-    def OnSettingsSave(self, event):
-        """!Save settings"""
-        dlg = wx.TextEntryDialog(parent = self,
-                                 message = _("Name:"),
-                                 caption = _("Save settings"))
-        if dlg.ShowModal() != wx.ID_OK:
-            return
-        
-        # check required params
-        if not dlg.GetValue():
-            GMessage(parent = self,
-                     message = _("Name not given, settings is not saved."))
-            return
-        
-        if not self.GetDsn():
-            GMessage(parent = self,
-                     message = _("No data source defined, settings is not saved."))
-            return
-        
-        name = dlg.GetValue()
-        
-        # check if settings item already exists
-        if name in self._settings:
-            dlgOwt = wx.MessageDialog(self, message = _("Settings <%s> already exists. "
-                                                        "Do you want to overwrite the settings?") % name,
-                                      caption = _("Save settings"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-            if dlgOwt.ShowModal() != wx.ID_YES:
-                dlgOwt.Destroy()
-                return
-        
-        self._settings[name] = (self.dsnType, self.GetDsn(),
-                                self.format.GetStringSelection(),
-                                self.creationOpt.GetValue())
-        
-        if self._saveSettings() == 0:
-            self._settings = self._loadSettings()
-            self.settingsChoice.SetStringSelection(name)
-        
-        dlg.Destroy()
- 
-    def OnSettingsDelete(self, event):
-        """!Save settings"""
-        name = self.settingsChoice.GetStringSelection()
-        if not name:
-            GMessage(parent = self,
-                     message = _("No settings is defined. Operation canceled."))
-            return
-        
-        self._settings.pop(name)
-        if self._saveSettings() == 0:
-            self._settings = self._loadSettings()
-        
-    def _saveSettings(self):
-        """!Save settings into the file
 
-        @return 0 on success
-        @return -1 on failure
-        """
-        try:
-            fd = open(self.settingsFile, 'w')
-            for key, value in self._settings.iteritems():
-                fd.write('%s;%s;%s;%s\n' % (key, value[0], value[1], value[2]))
-        except IOError:
-            GError(parent = self,
-                   message = _("Unable to save settings"))
-            return -1
-        else:
-            fd.close()
-        
-        return 0
-
-    def _loadSettings(self):
-        """!Load settings from the file
-
-        The file is defined by self.SettingsFile.
-        
-        @return parsed dict
-        @return empty dict on error
-        """
-        data = dict()
-        if not os.path.exists(self.settingsFile):
-            return data
-        
-        try:
-            fd = open(self.settingsFile, 'r')
-            for line in fd.readlines():
-                try:
-                    lineData = line.rstrip('\n').split(';')
-                    if len(lineData) > 4:
-                        # type, dsn, format, options
-                        data[lineData[0]] = (lineData[1], lineData[2], lineData[3], lineData[4])
-                    else:
-                        data[lineData[0]] = (lineData[1], lineData[2], lineData[3], '')
-                except ValueError:
-                    pass
-        except IOError:
-            return data
-        else:
-            fd.close()
-        
-        self.settingsChoice.SetItems(sorted(data.keys()))
-        
-        return data
-
     def OnSetType(self, event, sel = None):
         """!Datasource type changed"""
         if event:

Modified: grass/trunk/gui/wxpython/gui_core/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/mapwindow.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/gui_core/mapwindow.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -39,9 +39,9 @@
     def __init__(self, parent, giface, Map, frame, **kwargs):
         self.parent = parent
         self.Map = Map
-        self.Map.SetParentMapWindow(self)
         self.frame = frame
         self._giface = giface
+        self.Map.SetReceiver(self)
         
         # mouse attributes -- position on the screen, begin and end of
         # dragging, and type of drawing

Modified: grass/trunk/gui/wxpython/gui_core/widgets.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/widgets.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/gui_core/widgets.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -16,6 +16,7 @@
  - widgets::ItemTree
  - widgets::GListCtrl
  - widgets::SearchModuleWidget
+ - widgets::ManageSettingsWidget
 
 (C) 2008-2012 by the GRASS Development Team
 
@@ -25,6 +26,7 @@
 @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
 @author Enhancements by Michael Barton <michael.barton asu.edu>
 @author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
+ at author Stepan Turek <stepan.turek seznam.cz> (ManageSettingsWidget - created from GdalSelect)
 """
 
 import os
@@ -48,12 +50,17 @@
     import wx.lib.customtreectrl as CT
 
 from core        import globalvar
+from core.gcmd   import GMessage
 from core.debug  import Debug
 from core.events import gShowNotification
 
 from wx.lib.newevent import NewEvent
 wxSymbolSelectionChanged, EVT_SYMBOL_SELECTION_CHANGED  = NewEvent()
 
+# ManageSettingsWidget
+wxOnSettingsLoaded, EVT_SETTINGS_LOADED = NewEvent()
+wxOnSettingsChanged, EVT_SETTINGS_CHANGED = NewEvent()
+wxOnSettingsSaving, EVT_SETTINGS_SAVING = NewEvent()
 
 class NotebookController:
     """!Provides handling of notebook page names.
@@ -874,3 +881,299 @@
         self.search.SetValue('')
         if self.showTip:
             self.searchTip.SetLabel('')
+
+class ManageSettingsWidget(wx.Panel):
+    """!Widget which allows loading and saving settings into file."""
+    def __init__(self, parent, settingsFile, id = wx.ID_ANY):
+        """
+        Events:
+            EVT_SETTINGS_CHANGED - called when users changes setting
+                                - event object has attribute 'data', with chosen setting data
+            EVT_SETTINGS_SAVING - called when settings are saving
+                                - If you bind instance of ManageSettingsWidget with this event,
+                                  you can use SetDataToSave method to set data for save and then call 
+                                  Skip() to save the data.
+                                  If you do not call Skip(), the data will not be saved. 
+            EVT_SETTINGS_LOADED - called when settings are loaded
+                                - event object has attribute 'settings', which is dict with loaded settings
+                                  {nameofsetting : settingdata, ....}
+
+        @param settingsFile - path to file, where settings will be saved and loaded from
+        """
+        self.settingsFile = settingsFile
+
+        wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+
+        self.settingsBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                        label = " %s " % _("Settings"))
+        
+        self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
+        self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsChanged)
+        self.btnSettingsSave = wx.Button(parent = self, id = wx.ID_SAVE)
+        self.btnSettingsSave.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
+        self.btnSettingsSave.SetToolTipString(_("Save current settings"))
+        self.btnSettingsDel = wx.Button(parent = self, id = wx.ID_REMOVE)
+        self.btnSettingsDel.Bind(wx.EVT_BUTTON, self.OnSettingsDelete)
+        self.btnSettingsSave.SetToolTipString(_("Delete currently selected settings"))
+
+        self.Bind(EVT_SETTINGS_SAVING, self.OnSettingsSaving)
+
+        # escaping with '$' character - index in self.esc_chars
+        self.e_char_i = 0
+        self.esc_chars = ['$', ';']
+
+        self._settings = self._loadSettings() # -> self.settingsChoice.SetItems()
+        event = wxOnSettingsLoaded(settings = self._settings)
+        wx.PostEvent(self, event)
+
+        self.data_to_save = []
+
+        self._layout()
+
+    def _layout(self):
+
+        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
+        settingsSizer.Add(item = wx.StaticText(parent = self,
+                                               id = wx.ID_ANY,
+                                               label = _("Load settings:")),
+                          flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
+                          border  = 5)
+        settingsSizer.Add(item = self.settingsChoice,
+                          proportion = 1,
+                          flag = wx.EXPAND)
+        settingsSizer.Add(item = self.btnSettingsSave,
+                          flag = wx.LEFT | wx.RIGHT,
+                          border = 5)
+        settingsSizer.Add(item = self.btnSettingsDel,
+                          flag = wx.RIGHT,
+                          border = 5)
+
+        self.SetSizer(settingsSizer)
+        settingsSizer.Fit(self)
+
+    def OnSettingsChanged(self, event):
+        """!Load named settings"""
+        name = event.GetString()
+        if name not in self._settings:
+            GError(parent = self,
+                   message = _("Settings <%s> not found") % name)
+            return
+
+        data = self._settings[name]
+        event = wxOnSettingsChanged(data = data)
+        wx.PostEvent(self, event)
+
+    def OnSettingsSave(self, event):
+        """!Save settings"""
+        dlg = wx.TextEntryDialog(parent = self,
+                                 message = _("Name:"),
+                                 caption = _("Save settings"))
+        if dlg.ShowModal() != wx.ID_OK:
+            return
+        
+        # check required params
+        if not dlg.GetValue():
+            GMessage(parent = self,
+                     message = _("Name not given, settings is not saved."))
+            return
+
+        name = dlg.GetValue()
+
+        event = wxOnSettingsSaving(name = name, dlg = dlg)
+        wx.PostEvent(self, event)
+  
+    def OnSettingsSaving(self, event):
+        # check if settings item already exists
+        if event.name in self._settings:
+            dlgOwt = wx.MessageDialog(self, message = _("Settings <%s> already exists. "
+                                                        "Do you want to overwrite the settings?") % event.name,
+                                      caption = _("Save settings"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlgOwt.ShowModal() != wx.ID_YES:
+                dlgOwt.Destroy()
+                return
+
+        if self.data_to_save:
+            self._settings[event.name] = self.data_to_save
+
+        self.SaveSettings()
+        self.settingsChoice.SetStringSelection(event.name)
+
+        self.data_to_save = []
+        event.dlg.Destroy()
+ 
+    def SaveSettings(self):
+        """!Save settings"""
+        if self._saveSettings() == 0:
+            self._settings = self._loadSettings()
+
+    def SetDataToSave(self, data):
+        """!Set data for setting, which will be saved.
+
+        @param data - list of strings, which will be saved
+        """
+        self.data_to_save = data
+
+    def SetSettings(self, settings):
+        """!Set settings
+
+        @param settings - dict with all settigs {nameofsetting : settingdata, ....}
+        """
+        self._settings = settings
+        self.SaveSettings()
+
+    def OnSettingsDelete(self, event):
+        """!Save settings
+        """
+        name = self.settingsChoice.GetStringSelection()
+        if not name:
+            GMessage(parent = self,
+                     message = _("No settings is defined. Operation canceled."))
+            return
+        
+        self._settings.pop(name)
+        if self._saveSettings() == 0:
+            self._settings = self._loadSettings()
+        
+    def _saveSettings(self):
+        """!Save settings into the file
+
+        @return 0 on success
+        @return -1 on failure
+        """
+        try:
+            fd = open(self.settingsFile, 'w')
+            fd.write('format_version=2.0\n')
+            for key, values in self._settings.iteritems():
+                first = True
+                for v in values:
+                    # escaping characters
+                    for e_ch in self.esc_chars:
+                        v = v.replace(e_ch, self.esc_chars[self.e_char_i] + e_ch)
+                    if first:
+                        # escaping characters
+                        for e_ch in self.esc_chars:
+                            key = key.replace(e_ch, self.esc_chars[self.e_char_i] + e_ch)
+                        fd.write('%s;%s;' % (key, v))
+                        first = False
+                    else:
+                        fd.write('%s;' % (v))
+                fd.write('\n')
+
+        except IOError:
+            GError(parent = self,
+                   message = _("Unable to save settings"))
+            return -1
+        fd.close()
+        
+        return 0
+
+    def _loadSettings(self):
+        """!Load settings from the file
+
+        The file is defined by self.SettingsFile.
+        
+        @return parsed dict
+        @return empty dict on error
+        """
+
+        data = dict()
+        if not os.path.exists(self.settingsFile):
+            return data
+
+        try:
+            fd = open(self.settingsFile, 'r')
+        except IOError:
+            return data
+
+        fd_lines = fd.readlines()
+
+        if not fd_lines:
+            fd.close()
+            return data
+
+        if fd_lines[0].strip() == 'format_version=2.0':
+            data = self._loadSettings_v2(fd_lines)
+        else:
+            data = self._loadSettings_v1(fd_lines)
+
+        self.settingsChoice.SetItems(sorted(data.keys()))
+        fd.close()
+
+        event = wxOnSettingsLoaded(settings = data)
+        wx.PostEvent(self, event)
+
+        return data
+
+    def _loadSettings_v2(self, fd_lines):
+        """Load settings from the file in format version 2.0
+
+        The file is defined by self.SettingsFile.
+        
+        @return parsed dict
+        @return empty dict on error
+        """
+        data = dict()
+        
+        for line in fd_lines[1:]:
+            try:
+                lineData = []
+                line = line.rstrip('\n')
+                i_last_found = i_last = 0
+                key = ''
+                while True:
+                    idx = line.find(';', i_last)
+                    if idx < 0:
+                        break
+                    elif idx != 0:
+
+                        # find out whether it is separator
+                        # $$$$; - it is separator
+                        # $$$$$; - it is not separator
+                        i_esc_chars = 0
+                        while True:
+                            if line[idx - (i_esc_chars + 1)] == self.esc_chars[self.e_char_i]:
+                                i_esc_chars += 1
+                            else: 
+                                break
+                        if i_esc_chars%2 != 0:
+                            i_last = idx + 1
+                            continue
+
+                    lineItem = line[i_last_found : idx]
+                    # unescape characters
+                    for e_ch in self.esc_chars:
+                        lineItem = lineItem.replace(self.esc_chars[self.e_char_i] + e_ch, e_ch)
+                    if i_last_found == 0:
+                        key = lineItem
+                    else:
+                        lineData.append(lineItem)
+                    i_last_found = i_last = idx + 1
+                if key and lineData:
+                    data[key] = lineData
+            except ValueError:
+                pass
+
+        return data
+
+    def _loadSettings_v1(self, fd_lines):
+        """!Load settings from the file in format version 1.0 (backward compatibility)
+
+        The file is defined by self.SettingsFile.
+        
+        @return parsed dict
+        @return empty dict on error
+        """
+        data = dict()
+      
+        for line in fd_lines:
+            try:
+                lineData = line.rstrip('\n').split(';')
+                if len(lineData) > 4:
+                    # type, dsn, format, options
+                    data[lineData[0]] = (lineData[1], lineData[2], lineData[3], lineData[4])
+                else:
+                    data[lineData[0]] = (lineData[1], lineData[2], lineData[3], '')
+            except ValueError:
+                pass
+        
+        return data

Modified: grass/trunk/gui/wxpython/lmgr/frame.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/frame.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/lmgr/frame.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -992,9 +992,7 @@
                          (None, None),
                          ('vectImport',    self.OnImportOgrLayers),
                          ('vectLink',      self.OnLinkOgrLayers),
-                         ('vectOut',       self.OnVectorOutputFormat),
-                         (None, None),
-                         ('wmsImport',     self.OnImportWMS)))
+                         ('vectOut',       self.OnVectorOutputFormat)))
         
     def OnWorkspaceNew(self, event = None):
         """!Create new workspace file
@@ -1509,37 +1507,13 @@
         dlg.CentreOnScreen()
         dlg.Show()
         
-    def OnImportWMS(self, event, cmd = None):
-        """!Import data from OGC WMS server"""
-        from ogc_services.wms import WMSDialog
-        dlg = WMSDialog(parent = self)
-        dlg.CenterOnScreen()         
-        if dlg.ShowModal() == wx.ID_OK: # -> import layers
-            layers = dlg.GetLayers()
-            
-            if len(layers.keys()) > 0:
-                for layer in layers.keys():
-                    cmd = ['r.in.wms',
-                           'mapserver=%s' % dlg.GetSettings()['server'],
-                           'layers=%s' % layer,
-                           'output=%s' % layer,
-                           'format=png',
-                           '--overwrite']
-                    styles = ','.join(layers[layer])
-                    if styles:
-                        cmd.append('styles=%s' % styles)
-                    self._gconsole.RunCmd(cmd, switchPage = True)
+    def OnAddWS(self, event, cmd = None):
+        """!Add web services layer"""
+        from web_services.dialogs import AddWSDialog
+        dlg = AddWSDialog(parent = self, gmframe = self)
+        dlg.CentreOnScreen()
+        dlg.Show()
 
-                    self.GetLayerTree().AddLayer(ltype = 'raster',
-                                                    lname = layer,
-                                                    lcmd = ['d.rast', 'map=%s' % layer],
-                                                    multiple = False)
-            else:
-                self._gconsole.WriteWarning(_("Nothing to import. No WMS layer selected."))
-                
-                
-        dlg.Destroy()
-
     def OnShowAttributeTable(self, event, selection = None):
         """!Show attribute table of the given vector map layer
         """

Modified: grass/trunk/gui/wxpython/lmgr/layertree.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/layertree.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/lmgr/layertree.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -29,22 +29,24 @@
 
 from grass.script import core as grass
 
-from core                import globalvar
-from gui_core.dialogs    import SqlQueryFrame, SetOpacityDialog, EVT_APPLY_OPACITY
-from gui_core.forms      import GUI
-from mapdisp.frame       import MapFrame
-from core.render         import Map
-from modules.histogram   import HistogramFrame
-from core.utils          import GetLayerNameFromCmd
-from wxplot.profile      import ProfileFrame
-from core.debug          import Debug
-from core.settings       import UserSettings, GetDisplayVectSettings
-from vdigit.main         import haveVDigit
-from core.gcmd           import GWarning, GError
-from gui_core.toolbars   import BaseIcons
-from icons.icon          import MetaIcon
-from modules.colorrules  import RasterColorTable
+from core                 import globalvar
+from gui_core.dialogs     import SqlQueryFrame, SetOpacityDialog, EVT_APPLY_OPACITY
+from gui_core.forms       import GUI
+from mapdisp.frame        import MapFrame
+from core.render          import Map
+from modules.histogram    import HistogramFrame
+from core.utils           import GetLayerNameFromCmd
+from wxplot.profile       import ProfileFrame
+from core.debug           import Debug
+from core.settings        import UserSettings, GetDisplayVectSettings
+from vdigit.main          import haveVDigit
+from core.gcmd            import GWarning, GError
+from gui_core.toolbars    import BaseIcons
+from icons.icon           import MetaIcon
+from modules.colorrules   import RasterColorTable
+from web_services.dialogs import SaveWMSLayerDialog
 
+
 TREE_ITEM_HEIGHT = 25
 
 LMIcons = {
@@ -391,7 +393,7 @@
         if not hasattr (self, "popupID"):
             self.popupID = dict()
             for key in ('remove', 'rename', 'opacity', 'nviz', 'zoom',
-                        'region', 'export', 'attr', 'edit0', 'edit1',
+                        'region', 'export', 'attr', 'edit0', 'edit1', 'save_ws',
                         'bgmap', 'topo', 'meta', 'null', 'zoom1', 'region1',
                         'color', 'hist', 'univar', 'prof', 'properties', 'sql'):
                 self.popupID[key] = wx.NewId()
@@ -532,9 +534,21 @@
                 self.popupMenu.Append(self.popupID['meta'], _("Metadata"))
                 self.Bind (wx.EVT_MENU, self.OnMetadata, id = self.popupID['meta'])
         
+        # web service layers (specific item)
+        elif mltype and mltype == "wms":
+            self.popupMenu.Append(self.popupID['save_ws'], text = _("Save web service layer"))
+            self.Bind(wx.EVT_MENU, self.OnSaveWs, id = self.popupID['save_ws'])
+
         self.PopupMenu(self.popupMenu)
         self.popupMenu.Destroy()
 
+    def OnSaveWs(self, event):
+        """!Show dialog for saving web service layer into GRASS vector/raster layer"""
+        mapLayer = self.GetLayerInfo(self.layer_selected, key = 'maplayer')
+        dlg = SaveWMSLayerDialog(parent = self, layer = mapLayer, ltree = self)
+        dlg.CentreOnScreen()
+        dlg.Show()
+
     def OnTopology(self, event):
         """!Rebuild topology of selected vector map"""
         mapLayer = self.GetLayerInfo(self.layer_selected, key = 'maplayer')
@@ -1035,12 +1049,14 @@
 
     def PropertiesDialog(self, layer, show = True):
         """!Launch the properties dialog"""
+        ltype  = self.GetLayerInfo(layer, key = 'type')
         if 'propwin' in self.GetLayerInfo(layer) and \
                 self.GetLayerInfo(layer, key = 'propwin') is not None:
             # recycle GUI dialogs
             win = self.GetLayerInfo(layer, key = 'propwin')
-            # update properties (columns, layers)
-            win.notebookpanel.OnUpdateSelection(None)
+            if ltype != 'wms':
+                # update properties (columns, layers)
+                win.notebookpanel.OnUpdateSelection(None)
             if win.IsShown():
                 win.SetFocus()
             else:
@@ -1049,13 +1065,13 @@
             return
         
         params = self.GetLayerParams(layer)
-        ltype  = self.GetLayerInfo(layer, key = 'type')
                 
         Debug.msg (3, "LayerTree.PropertiesDialog(): ltype=%s" % \
                    ltype)
 
         cmd = None
         if self.GetLayerInfo(layer, key = 'cmd'):
+
             module = GUI(parent = self, show = show, centreOnParent = False)
             module.ParseCommand(self.GetLayerInfo(layer, key = 'cmd'),
                                 completed = (self.GetOptData,layer,params))

Modified: grass/trunk/gui/wxpython/lmgr/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/toolbars.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/lmgr/toolbars.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -85,6 +85,8 @@
             'addVect'    : BaseIcons['addVect'].SetLabel(_("Add vector map layer (Ctrl+Shift+V)")),
             'vectMisc'   : MetaIcon(img = 'layer-vector-more',
                                     label = _('Add various vector map layers (thematic, chart...)')),
+            'addWS'       : MetaIcon(img = 'layer-wms-add',
+                                     label = _('Add web service layer (WMS, WMTS, NASA OnEarth)')),
             'addGroup'   : MetaIcon(img = 'layer-group-add',
                                     label = _('Add group')),
             'addOverlay' : MetaIcon(img = 'layer-more',
@@ -107,6 +109,9 @@
                                       self.parent.OnAddGroup),
                                      ('addovl',  icons["addOverlay"],
                                       self.parent.OnAddOverlay),
+                                     ('addWS',  icons["addWS"],
+                                      self.parent.OnAddWS),
+                                     (None, ),
                                      ('delcmd',  icons["delCmd"],
                                       self.parent.OnDeleteLayer),
                                      ))

Modified: grass/trunk/gui/wxpython/mapdisp/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/mapwindow.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/mapdisp/mapwindow.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -34,8 +34,11 @@
 from core.gcmd          import RunCommand, GException, GError, GMessage
 from core.debug         import Debug
 from core.settings      import UserSettings
-from core.events        import gZoomChanged
+from core.events        import gZoomChanged, EVT_UPDATE_MAP
 from gui_core.mapwindow import MapWindow
+from core.render        import EVT_UPDATE_PRGBAR
+from core.utils         import GetGEventAttribsForHandler
+
 try:
     import grass.lib.gis as gislib
     haveCtypes = True
@@ -76,9 +79,13 @@
         self.plineid = None
         
         # event bindings
-        self.Bind(wx.EVT_PAINT,        self.OnPaint)
-        self.Bind(wx.EVT_SIZE,         self.OnSize)
-        self.Bind(wx.EVT_IDLE,         self.OnIdle)
+        self.Bind(wx.EVT_PAINT,           self.OnPaint)
+        self.Bind(wx.EVT_SIZE,            self.OnSize)
+        self.Bind(wx.EVT_IDLE,            self.OnIdle)
+        self.Bind(EVT_UPDATE_MAP,         self.OnUpdateMap)
+        if self.frame and hasattr(self.frame, 'OnUpdateProgress'):
+            self.Bind(EVT_UPDATE_PRGBAR,   self.frame.OnUpdateProgress)
+
         self._bindMouseEvents()
         
         self.processMouse = True
@@ -571,10 +578,26 @@
     def IsAlwaysRenderEnabled(self):
         return self.alwaysRender
 
+    def OnUpdateMap(self, event):
+        """!Called when this class receives core.events.gUpdateMap event. 
+        """
+        kwargs, missing_args = GetGEventAttribsForHandler(self.UpdateMap, event)
+
+        if missing_args:
+            Debug.msg (1, "Invalid call of EVT_UPDATE_MAP event.")
+            return
+
+        self.UpdateMap(self, **kwargs)
+
     def UpdateMap(self, render = True, renderVector = True):
         """!Updates the canvas anytime there is a change to the
         underlaying images or to the geometry of the canvas.
         
+        This method should not be called directly.
+        Post core.events.gUpdateMap event to instance of this class. 
+
+        @todo change direct calling of UpdateMap method to posting core.events.gUpdateMap
+
         @param render re-render map composition
         @param renderVector re-render vector map layer enabled for editing (used for digitizer)
         """
@@ -606,10 +629,10 @@
                 else:
                     windres = False
                 
-                self.mapfile = self.Map.Render(force = True, mapWindow = self.frame,
+                self.mapfile = self.Map.Render(force = True,
                                                windres = windres)
             else:
-                self.mapfile = self.Map.Render(force = False, mapWindow = self.frame)
+                self.mapfile = self.Map.Render(force = False)
             
         except GException, e:
             GError(message = e.value)
@@ -759,9 +782,8 @@
         self.Draw(self.pdcDec, pdctype = 'clear')
         self.Draw(self.pdcTmp, pdctype = 'clear')
 
-        for layer in self.Map.GetListOfLayers(active = True):
-            layer.AbortDownload()
-        
+        self.Map.AbortAllThreads()
+
     def DragMap(self, moveto):
         """!Drag the entire map image for panning.
         

Modified: grass/trunk/gui/wxpython/ogc_services/wms.py
===================================================================
--- grass/trunk/gui/wxpython/ogc_services/wms.py	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/ogc_services/wms.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -1,300 +0,0 @@
-"""!
- at package module.ogc_services
-
- at brief Dialogs for OGC services
-
-Currently only implemeted WMS.
-
-List of classes:
- - ogc_services::WMSDialog
- - ogc_services::LayersList
-
-(C) 2009-2011 by the GRASS Development Team
-
-This program is free software under the GNU General Public License
-(>=v2). Read the file COPYING that comes with GRASS for details.
-
- at author Martin Landa <landa.martin gmail.com>
-"""
-
-import wx
-from wx.gizmos import TreeListCtrl
-import wx.lib.mixins.listctrl as listmix
-
-from core.gcmd     import RunCommand
-from core.settings import UserSettings
-
-class WMSDialog(wx.Dialog):
-    def __init__(self, parent, service = 'wms',
-                 id=wx.ID_ANY,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
-        """!Dialog to import data from WMS server"""
-        self.parent  = parent  # GMFrame 
-        self.service = service # currently only WMS is implemented
-        
-        wx.Dialog.__init__(self, parent, id, style=style)
-        if self.service == 'wms':
-            self.SetTitle(_("Import data from WMS server"))
-            
-        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
-        
-        self.__createWidgets()
-        
-        self.__doLayout()
-
-        self.SetMinSize((550, 400))
-        
-    def __createWidgets(self):
-        """!Create dialog widgets"""
-        #
-        # settings
-        #
-        self.settingsBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
-                                        label = _(" Server settings "))
-
-        self.serverText = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Server:"))
-        self.server  = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
-        
-        #
-        # list of layers
-        #
-        self.layersBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
-                                label=_(" List of layers "))
-
-        self.list = LayersList(self.panel)
-        self.list.LoadData()
-
-        self.add = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
-                               label=_("Add imported layers into layer tree"))
-        self.add.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
-                
-        #
-        # buttons
-        #
-        # cancel
-        self.btn_cancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
-        self.btn_cancel.SetToolTipString(_("Close dialog"))
-        # connect
-        self.btn_connect = wx.Button(parent = self.panel, id = wx.ID_ANY, label = _("&Connect"))
-        self.btn_connect.SetToolTipString(_("Connect to the server"))
-        self.btn_connect.SetDefault()
-        if not self.server.GetValue():
-            self.btn_connect.Enable(False)
-        # import
-        self.btn_import = wx.Button(parent = self.panel, id = wx.ID_OK, label = _("&Import"))
-        self.btn_import.SetToolTipString(_("Import selected layers"))
-        self.btn_import.Enable(False)
-        
-        #
-        # bindings
-        #
-        self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
-        self.btn_connect.Bind(wx.EVT_BUTTON, self.OnConnect)
-        self.server.Bind(wx.EVT_TEXT, self.OnServer)
-        
-    def __doLayout(self):
-        """!Do dialog layout"""
-        dialogSizer = wx.BoxSizer(wx.VERTICAL)
-        
-        #
-        # settings
-        #
-        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
-        
-        gridSizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
-
-        gridSizer.Add(item=self.serverText,
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.AddGrowableCol(1)
-        gridSizer.Add(item=self.server,
-                      flag=wx.EXPAND | wx.ALL)
-        
-        settingsSizer.Add(item=gridSizer, proportion=1,
-                       flag=wx.EXPAND | wx.ALL)
-        
-        dialogSizer.Add(item=settingsSizer, proportion=0,
-                        flag=wx.ALL | wx.EXPAND, border=5)
-        
-        #
-        # list of layers
-        #
-        layersSizer = wx.StaticBoxSizer(self.layersBox, wx.HORIZONTAL)
-
-        layersSizer.Add(item=self.list, proportion=1,
-                        flag=wx.ALL | wx.EXPAND, border=5)
-        
-        dialogSizer.Add(item=layersSizer, proportion=1,
-                        flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
-
-        dialogSizer.Add(item=self.add, proportion=0,
-                        flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
-        
-        #
-        # buttons
-        #
-        btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
-
-        btnsizer.Add(item=self.btn_cancel, proportion=0,
-                     flag=wx.ALL | wx.ALIGN_CENTER,
-                     border=10)
-        
-        btnsizer.Add(item=self.btn_connect, proportion=0,
-                     flag=wx.ALL | wx.ALIGN_CENTER,
-                     border=10)
-        
-        btnsizer.Add(item=self.btn_import, proportion=0,
-                     flag=wx.ALL | wx.ALIGN_CENTER,
-                     border=10)
-        
-        dialogSizer.Add(item=btnsizer, proportion=0,
-                        flag=wx.ALIGN_CENTER)
-        
-        self.panel.SetAutoLayout(True)
-        self.panel.SetSizer(dialogSizer)
-        dialogSizer.Fit(self.panel)
-        self.Layout()
-
-    def OnCancel(self, event):
-        """!Button 'Cancel' pressed -> close the dialog"""
-        self.Close()
-
-    def OnConnect(self, event):
-        """!Button 'Connect' pressed"""
-        server = self.server.GetValue()
-        if not server:
-            self.btn_import.Enable(False)
-            return # not reachable
-
-        layers = {}
-        ret = RunCommand('r.in.wms',
-                         quiet = True,
-                         parent = self,
-                         read = True,
-                         flags = 'l',
-                         mapserver = server)
-        
-        if not ret:
-            self.list.LoadData()
-            self.btn_import.Enable(False)
-            return # no layers found
-        
-        lastLayer = lastStyle = ''
-        for line in ret.splitlines():
-            try:
-                key, value = line.split(':', 1)
-            except ValueError:
-                continue
-            key = key.strip().lower()
-            value = value.strip()
-            
-            if key == 'layer':
-                layers[value] = {}
-                lastLayer = value
-            elif key == 'title':
-                layers[lastLayer][key] = value
-            elif key == 'style':
-                if 'style' not in layers[lastLayer]:
-                    layers[lastLayer]['style'] = {}
-                layers[lastLayer]['style'][value] = ''
-                lastStyle = value
-            elif key == 'style title':
-                layers[lastLayer]['style'][lastStyle] = value
-        
-        # update list of layers
-        self.list.LoadData(layers)
-        
-        if len(layers.keys()) > 0:
-            self.btn_import.Enable(True)
-        else:
-            self.btn_import.Enable(False)
-        
-    def OnServer(self, event):
-        """!Server settings changed"""
-        value = event.GetString()
-        if value:
-            self.btn_connect.Enable(True)
-        else:
-            self.btn_connect.Enable(False)
-        
-    def GetLayers(self):
-        """!Get list of selected layers/styles to be imported"""
-        return self.list.GetSelectedLayers()
-
-    def GetSettings(self):
-        """!Get connection settings"""
-        return { 'server' : self.server.GetValue() }
-    
-class LayersList(TreeListCtrl, listmix.ListCtrlAutoWidthMixin):
-    def __init__(self, parent, pos=wx.DefaultPosition):
-        """!List of layers to be imported (dxf, shp...)"""
-        self.parent = parent
-        
-        TreeListCtrl.__init__(self, parent, wx.ID_ANY,
-                              style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT |
-                              wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_MULTIPLE)
-        
-        # setup mixins
-        listmix.ListCtrlAutoWidthMixin.__init__(self)
-        
-        self.AddColumn(_('Layer / Style'))
-        self.AddColumn(_('Title'))
-        self.SetMainColumn(0) # column with the tree
-        self.SetColumnWidth(0, 175)
-        
-        self.root = None
-        
-    def LoadData(self, data = {}):
-        """!Load data into list"""
-        # detete first all items
-        self.DeleteAllItems()
-        self.root = self.AddRoot(_("Layers"))
-
-        layers = data.keys()
-        if not layers:
-            return
-        
-        layers.sort()
-
-        for layer in layers:
-            title = data[layer]['title']
-            lchild = self.AppendItem(self.root, layer)
-            self.SetItemText(lchild, title, 1)
-            if 'style' in data[layer]:
-                styles = data[layer]['style'].keys()
-                if not styles:
-                    continue
-                styles.sort()
-                for style in styles:
-                    title = data[layer]['style'][style]
-                    schild = self.AppendItem(lchild, style)
-                    self.SetItemText(schild, title, 1)
-        
-        self.Expand(self.root)
-        
-    def GetItemCount(self):
-        """!Required for listmix.ListCtrlAutoWidthMixin"""
-        return 0
-
-    def GetCountPerPage(self):
-        """!Required for listmix.ListCtrlAutoWidthMixin"""
-        return 0
-
-    def GetSelectedLayers(self):
-        """!Get selected layers/styles"""
-        layers = dict()
-
-        for item in self.GetSelections():
-            parent = self.GetItemParent(item)
-            if parent == self.root: # -> layer
-                layer = self.GetItemText(item, 0)
-                layers[layer] = list()
-                sitem, cookie = self.GetFirstChild(item)
-                while sitem:
-                    layers[layer].append(self.GetItemText(sitem, 0))
-                    sitem, cookie = self.GetNextChild(item, cookie)
-            else: # -> style
-                layer = self.GetItemText(parent, 0)
-                layers[layer] = list()
-                layers[layer].append(self.GetItemText(item, 0))
-        
-        return layers

Added: grass/trunk/gui/wxpython/web_services/cap_interface.py
===================================================================
--- grass/trunk/gui/wxpython/web_services/cap_interface.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/web_services/cap_interface.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -0,0 +1,397 @@
+"""!
+ at package web_services.cap_interface
+
+ at brief Provides common interface for GUI web_services.widgets to capabilities data of web services.
+
+List of classes:
+ - cap_interface::CapabilitiesBase
+ - cap_interface::LayerBase
+ - cap_interface::WMSCapabilities
+ - cap_interface::WMSLayer
+ - cap_interface::WMTSCapabilities
+ - cap_interface::WMTSLayer
+ - cap_interface::OnEarthCapabilities
+ - cap_interface::OnEarthLayer
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
+"""
+
+import os
+import sys
+
+WMSLibPath = os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms")
+sys.path.append(WMSLibPath)
+
+from wms_cap_parsers import WMSCapabilitiesTree, \
+                            WMTSCapabilitiesTree, \
+                            OnEarthCapabilitiesTree
+
+class CapabilitiesBase:
+    def GetLayerByName(self, name):
+        """!Find layer by name
+        """
+        for l in self.layers_by_id:
+            if name == l.GetLayerData('name'):
+                return l
+        return None
+
+    def GetRootLayer(self):
+        """!Get children layers
+        """
+        if self.layers_by_id:
+            return self.layers_by_id[0]
+        else:
+            return None
+
+class LayerBase:
+    def GetId(self):
+        """!Get layer id
+        """
+        return self.id
+
+    def GetChildren(self):
+        """!Get children layers
+        """
+        return self.child_layers
+
+    def GetLayerNode(self):
+        """!Get layer node
+        """
+        return self.layer_node
+
+    def AddChildLayer(self, layer):
+        """!Add child layer
+        """
+        self.child_layers.append(layer)
+
+class WMSCapabilities(CapabilitiesBase, WMSCapabilitiesTree):
+    def __init__(self, cap_file, force_version = None):
+        """!Create common interface for web_services.widgets to WMS capabilities data
+        """
+        # checks all elements needed for creation of GetMap requests
+        # by r.in.wms/d.wms modules, invalid elements are removed
+        WMSCapabilitiesTree.__init__(self, cap_file, force_version)
+
+        self.cap_node = self.getroot().find(self.xml_ns.Ns("Capability"))
+        self.root_layer = self.cap_node.find(self.xml_ns.Ns("Layer"))
+
+        self.layers_by_id = {}
+        self._initializeLayerTree(self.root_layer)
+
+    def _initializeLayerTree(self, parent_layer, id = 0):
+        """!Build tree, which represents layers
+        """
+        if id == 0:
+            parent_layer = WMSLayer(parent_layer, id, self)
+            self.layers_by_id[id] = parent_layer
+            id += 1
+        
+        layer_nodes = parent_layer.GetLayerNode().findall((self.xml_ns.Ns("Layer")))
+        
+        for l in layer_nodes:
+            layer = WMSLayer(l, id, self)
+            parent_layer.AddChildLayer(layer)
+            self.layers_by_id[id] = layer
+            id += 1
+            id = self._initializeLayerTree(layer, id)
+        
+        return id
+
+    def GetFormats(self):
+        """!Get supported formats
+        """       
+        request_node = self.cap_node.find(self.xml_ns.Ns("Request"))
+        get_map_node = request_node.find(self.xml_ns.Ns("GetMap"))
+        format_nodes = get_map_node.findall(self.xml_ns.Ns("Format"))
+ 
+        formats = []
+        for node in format_nodes:
+            formats.append(node.text)
+
+        return formats
+
+class WMSLayer(LayerBase):
+    def __init__(self, layer_node, id, cap):
+        """!Common interface for web_services.widgets to WMS capabilities <Layer> element
+        """
+        self.id = id
+        self.cap = cap
+        self.child_layers = []
+        self.layer_node = layer_node
+        self.xml_ns = self.cap.getxmlnshandler()
+
+    def GetLayerData(self, param):
+        """!Get layer data"""
+        title = self.xml_ns.Ns("Title")
+        name = self.xml_ns.Ns("Name")
+
+        if param == 'title':
+            title_node = self.layer_node.find(title)
+            if title_node is not None:
+                return title_node.text 
+            else:
+                return None
+
+        if param == 'name':
+            name_node = self.layer_node.find(name)
+            if name_node is not None:
+                return name_node.text 
+            else:
+                return None
+
+        if param == 'format':
+            return self.cap.GetFormats()
+
+        if param == 'styles':
+            styles = []
+            style = self.xml_ns.Ns("Style")
+            for style_node in self.layer_node.findall(style):
+                style_name = '' 
+                style_title = ''
+
+                if style_node.find(title) is not None:
+                    style_title = style_node.find(title).text
+                if style_node.find(name) is not None:
+                    style_name = style_node.find(name).text
+
+                styles.append({'title' : style_title, 
+                               'name' : style_name,
+                               'isDefault' : False})
+            return styles
+
+        if param == 'srs':
+            projs_nodes = self.layer_node.findall(self.xml_ns.Ns(self.cap.getprojtag()))
+
+            projs = []
+            if projs_nodes is None:
+                return projs
+            for p in projs_nodes:
+                projs.append(p.text.strip())
+            return projs
+
+    def IsRequestable(self):
+        """!Is it possible to use the layer for WMS GetMap request?
+        """
+        name = self.xml_ns.Ns("Name")
+        name_node = self.layer_node.find(name)
+
+        if name_node is not None:
+            return True
+        else:
+            return False
+
+class WMTSCapabilities(CapabilitiesBase, WMTSCapabilitiesTree):
+    def __init__(self, cap_file):
+        """!Create common interface for web_services.widgets to WMTS capabilities data
+        """
+        # checks all elements needed for creation of GetTile requests
+        # by r.in.wms/d.wms modules, invalid elements are removed
+        WMTSCapabilitiesTree.__init__(self, cap_file)
+
+        contents = self._find(self.getroot(), 'Contents', self.xml_ns.NsWmts)
+        layers = self._findall(contents, 'Layer', self.xml_ns.NsWmts)
+
+        self.layers_by_id = {}
+        
+        id = 0
+        root_layer = WMTSLayer(None, id, self)
+        self.layers_by_id[id] = root_layer
+
+        for layer_node in layers:
+            id += 1
+            self.layers_by_id[id] = WMTSLayer(layer_node, id, self)
+            root_layer.child_layers.append(self.layers_by_id[id])
+    
+class WMTSLayer(LayerBase):
+    def __init__(self, layer_node, id, cap):
+        """!Common interface for web_services.widgets to WMTS capabilities <Layer> element
+        """
+        self.id = id
+        self.cap = cap
+        self.child_layers = []
+        self.layer_node = layer_node
+        self.xml_ns = self.cap.getxmlnshandler()
+        self.projs = self._getProjs()
+
+    def GetLayerData(self, param):
+        """!Get layer data
+        """ 
+        title = self.xml_ns.NsOws("Title")
+        name = self.xml_ns.NsOws("Identifier")
+
+        if self.layer_node is None and param in ['title', 'name']:
+            return None
+        elif self.layer_node is None:
+            return []
+
+        if param == 'title':
+            title_node = self.layer_node.find(title)
+            if title_node is not None:
+                return title_node.text 
+            else:
+                return None
+
+        if param == 'name':
+            name_node = self.layer_node.find(name)
+            if name_node is not None:
+                return name_node.text 
+            else:
+                return None
+
+        if param == 'styles':
+            styles = []
+            for style_node in self.layer_node.findall(self.xml_ns.NsWmts("Style")):
+
+                style_name = '' 
+                style_title = ''
+
+                if style_node.find(title) is not None:
+                    style_title = style_node.find(title).text
+                if style_node.find(name) is not None:
+                    style_name = style_node.find(name).text
+
+                is_def = False
+                if 'isDefault' in style_node.attrib and\
+                    style_node.attrib['isDefault'] == 'true':
+                    is_def = True
+
+                styles.append({'title' : style_title, 
+                               'name' : style_name,
+                               'isDefault' : is_def})
+            
+            return styles
+
+        if param == 'format':
+            formats = []
+            for frmt in self.layer_node.findall(self.xml_ns.NsWmts('Format')):
+                formats.append(frmt.text.strip())
+            return formats
+
+        if param == 'srs':
+            return self.projs
+
+    def _getProjs(self):
+        """!Get layer projections
+        """ 
+        layer_projs = []
+        if self.layer_node is None:
+            return layer_projs
+
+        mat_set_links = self.layer_node.findall(self.xml_ns.NsWmts('TileMatrixSetLink'))
+
+        contents = self.cap.getroot().find(self.xml_ns.NsWmts('Contents'))
+        tileMatrixSets = contents.findall(self.xml_ns.NsWmts('TileMatrixSet'))
+
+        for link in  mat_set_links:
+            mat_set_link_id = link.find(self.xml_ns.NsWmts('TileMatrixSet')).text
+            if not mat_set_link_id:
+                continue
+
+            for mat_set in tileMatrixSets:
+                mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier')).text 
+                if mat_set_id and mat_set_id != mat_set_link_id:
+                    continue
+                mat_set_srs = mat_set.find(self.xml_ns.NsOws('SupportedCRS')).text.strip()
+                layer_projs.append(mat_set_srs)
+        return layer_projs
+
+    def IsRequestable(self):
+        """!Is it possible to use the layer for WMTS request?
+        """
+        if self.layer_node is None:
+           return False
+        else:
+            return True
+
+class OnEarthCapabilities(CapabilitiesBase, OnEarthCapabilitiesTree):
+    def __init__(self, cap_file):
+        """!Create Common interface for web_services.widgets to NASA OnEarth 
+            tile service data (equivalent to  WMS, WMTS capabilities data)
+        """
+        # checks all elements needed for creation of GetMap requests
+        # by r.in.wms/d.wms modules, invalid elements are removed
+        OnEarthCapabilitiesTree.__init__(self, cap_file)
+
+        self.layers_by_id = {}
+        self._initializeLayerTree(self.getroot())
+        
+    def _initializeLayerTree(self, parent_layer, id = 0):
+        """!Build tree, which represents layers
+        """
+        if id == 0:
+            tiled_patterns = parent_layer.find('TiledPatterns')
+            layer_nodes = tiled_patterns.findall('TiledGroup')
+            layer_nodes += tiled_patterns.findall('TiledGroups')
+            parent_layer = OnEarthLayer(None, None, id, self)
+            self.layers_by_id[id] = parent_layer
+            id += 1
+        else:
+            layer_nodes = parent_layer.layer_node.findall('TiledGroup')
+            layer_nodes += parent_layer.layer_node.findall('TiledGroups')
+
+        for layer_node in layer_nodes:
+            layer = OnEarthLayer(layer_node, parent_layer, id, self)
+            self.layers_by_id[id] = layer
+            id += 1
+            parent_layer.child_layers.append(layer)
+            if layer_node.tag == 'TiledGroups':
+               id = self._initializeLayerTree(layer, id)
+
+        return id
+
+class OnEarthLayer(LayerBase):
+    def __init__(self, layer_node, parent_layer, id, cap):
+        """!Common interface for web_services.widgets to NASA Earth
+            capabilities <TiledGroup>\<TiledGroups> element 
+            (equivalent to  WMS, WMTS <Layer> element)
+        """
+        self.id = id
+        self.cap = cap
+        self.layer_node = layer_node
+        self.child_layers = []
+        self.parent_layer = parent_layer
+
+    def IsRequestable(self):
+        """!Is it possible to use the layer for NASA OnEarth GetMap request?
+        """
+        if self.layer_node is None or \
+           self.layer_node.tag == 'TiledGroups':
+           return False
+        else:
+            return True
+
+    def GetLayerData(self, param):
+        """!Get layer data
+        """
+        if self.layer_node is None and param in ['title', 'name']:
+            return None
+        elif self.layer_node is None:
+            return []
+
+        if param == 'title':
+            title_node = self.layer_node.find("Title")
+            if title_node is not None:
+                return title_node.text 
+            else:
+                return None
+
+        if param == 'name':
+            name_node = self.layer_node.find("Name")
+            if name_node is not None:
+                return name_node.text 
+            else:
+                return None
+
+        if param == 'styles':
+            return []
+
+        if param == 'format':
+            return []
+
+        if param == 'srs':
+            return []

Copied: grass/trunk/gui/wxpython/web_services/dialogs.py (from rev 54605, grass/trunk/gui/wxpython/ogc_services/wms.py)
===================================================================
--- grass/trunk/gui/wxpython/web_services/dialogs.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/web_services/dialogs.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -0,0 +1,972 @@
+"""!
+ at package web_services.dialogs
+
+ at brief Dialogs for web services.
+
+List of classes:
+ - dialogs::WSDialogBase
+ - dialogs::AddWSDialog
+ - dialogs::WSPropertiesDialog
+ - dialogs::SaveWMSLayerDialog
+
+(C) 2009-2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Stepan Turek <stepan.turek seznam.cz>
+"""
+
+import wx
+
+import os
+import sys
+import shutil
+
+from copy      import deepcopy
+
+import grass.script as grass
+
+from core             import globalvar
+from core.debug       import Debug
+from core.ws          import RenderWMSMgr
+from core.events      import gUpdateMap
+from core.gcmd        import GMessage, RunCommand, GWarning
+from core.utils       import GetSettingsPath, CmdToTuple, CmdTupleToList
+from core.gconsole    import CmdThread, GStderr, EVT_CMD_DONE, EVT_CMD_OUTPUT
+
+from gui_core.gselect import Select
+from gui_core.widgets import ManageSettingsWidget,\
+                             EVT_SETTINGS_CHANGED, EVT_SETTINGS_SAVING, EVT_SETTINGS_LOADED
+
+from web_services.widgets import WSPanel, EVT_CAP_PARSED
+
+class WSDialogBase(wx.Dialog):
+    """!Base class for web service dialogs. 
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+
+        wx.Dialog.__init__(self, parent, id, style = style, **kwargs)
+
+        self.parent = parent 
+
+        # contains panel for every web service on server
+        self.ws_panels =  {'WMS_1.1.1'  : {'panel' : None,
+                                           'label' : 'WMS 1.1.1'},
+                           'WMS_1.3.0' : {'panel' : None,
+                                          'label' : 'WMS 1.3.0'},
+                           'WMTS' : {'panel' : None,
+                                     'label' : 'WMTS'},
+                           'OnEarth' : {'panel' : None,
+                                        'label' : 'OnEarth'},
+                          }
+
+        #TODO: should be in file
+        self.default_servers = { 'OSM-WMS-EUROPE' : ['http://129.206.228.72/cached/osm', '', ''],
+                                 'irs.gis-lab.info (OSM)' : ['http://irs.gis-lab.info', '', ''],
+                                 'NASA OnEarth' : ['http://onearth.jpl.nasa.gov/wms.cgi', '', '']
+                               }
+
+        # holds reference to web service panel which is showed
+        self.active_ws_panel = None
+
+        # buttons which are disabled when the dialog is not connected
+        self.run_btns = []
+
+        self._createWidgets()
+        self._doLayout()
+
+    def _createWidgets(self):
+
+        settingsFile = os.path.join(GetSettingsPath(), 'wxWS')
+
+        self.settsManager = ManageSettingsWidget(parent = self, 
+                                                 id = wx.ID_ANY,
+                                                 settingsFile = settingsFile)
+
+        self.settingsBox = wx.StaticBox(parent = self, 
+                                        id = wx.ID_ANY,
+                                        label = _(" Server settings "))
+        
+        self.serverText = wx.StaticText(parent = self, 
+                                        id = wx.ID_ANY, label = _("Server:"))
+        self.server  = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+
+        self.btn_connect = wx.Button(parent = self, 
+                                     id = wx.ID_ANY, label = _("&Connect"))
+        self.btn_connect.SetToolTipString(_("Connect to the server"))
+        self.btn_connect.SetDefault()
+        if not self.server.GetValue():
+            self.btn_connect.Enable(False)
+
+        self.infoCollapseLabelExp = _('Show advanced connection settings')
+        self.infoCollapseLabelCol = _('Hide advanced connection settings')
+
+        self.adv_conn = wx.CollapsiblePane(parent = self,
+                                           label = self.infoCollapseLabelExp,
+                                           style = wx.CP_DEFAULT_STYLE |
+                                                   wx.CP_NO_TLW_RESIZE | wx.EXPAND)
+
+        self.MakeAdvConnPane(pane = self.adv_conn.GetPane())
+        self.adv_conn.Collapse(True)
+        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnAdvConnPaneChanged, self.adv_conn) 
+
+        self.reqDataPanel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        self.layerNameText = wx.StaticText(parent = self.reqDataPanel, id = wx.ID_ANY, 
+                                           label = _("Output layer name:"))
+        self.layerName = wx.TextCtrl(parent = self.reqDataPanel, id = wx.ID_ANY)
+
+        for ws in self.ws_panels.iterkeys():
+            self.ws_panels[ws]['panel'] =  WSPanel(parent = self.reqDataPanel,
+                                                   web_service = ws,
+                                                   receiver = self)
+
+        # buttons
+        self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
+        self.btn_close.SetToolTipString(_("Close dialog"))
+        
+        # statusbar
+        self.statusbar = wx.StatusBar(parent = self, id = wx.ID_ANY)
+
+        # bindings
+        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        self.btn_connect.Bind(wx.EVT_BUTTON, self.OnConnect)
+
+        self.server.Bind(wx.EVT_TEXT, self.OnServer)
+        self.layerName.Bind(wx.EVT_TEXT, self.OnOutputLayerName)
+
+        self.settsManager.Bind(EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
+        self.settsManager.Bind(EVT_SETTINGS_SAVING, self.OnSettingsSaving)
+        self.settsManager.Bind(EVT_SETTINGS_LOADED, self.OnSettingsLoaded)
+
+        self.Bind(EVT_CAP_PARSED, self.OnPanelCapParsed)
+
+    def _doLayout(self):
+
+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+
+        dialogSizer.Add(item = self.settsManager, proportion = 0,
+                        flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+        
+        # connectin settings
+        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.VERTICAL)
+        
+        serverSizer = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
+
+        serverSizer.Add(item = self.serverText,
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        serverSizer.AddGrowableCol(1)
+        serverSizer.Add(item = self.server,
+                      flag = wx.EXPAND | wx.ALL)
+
+        serverSizer.Add(item = self.btn_connect)
+
+        settingsSizer.Add(item = serverSizer, proportion = 0,
+                          flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
+        
+        settingsSizer.Add(item = self.adv_conn,
+                          flag = wx.ALL | wx.EXPAND, border = 5)
+
+        dialogSizer.Add(item = settingsSizer, proportion = 0,
+                        flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+
+        # layer name, parsed capabilites
+
+        reqDataSizer = wx.BoxSizer(wx.VERTICAL)
+
+        layerNameSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        layerNameSizer.Add(item = self.layerNameText,
+                           flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border = 5)
+
+        layerNameSizer.Add(item = self.layerName, 
+                           flag = wx.EXPAND, proportion = 1)
+ 
+        reqDataSizer.Add(item = layerNameSizer,
+                         flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
+
+        self.ch_ws_sizer = wx.BoxSizer(wx.VERTICAL)
+
+        reqDataSizer.Add(item = self.ch_ws_sizer, proportion = 0,
+                         flag = wx.TOP | wx.EXPAND, border = 5)
+
+        for ws in self.ws_panels.iterkeys():
+            reqDataSizer.Add(item = self.ws_panels[ws]['panel'], proportion = 1,
+                             flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
+            self.ws_panels[ws]['panel'].Hide()
+
+        dialogSizer.Add(item = self.reqDataPanel, proportion = 1,
+                        flag = wx.EXPAND)
+
+        self.reqDataPanel.SetSizer(reqDataSizer)
+        self.reqDataPanel.Hide()
+
+        # buttons
+        self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+
+        self.btnsizer.Add(item = self.btn_close, proportion = 0,
+                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          border = 10)
+        
+        dialogSizer.Add(item = self.btnsizer, proportion = 0,
+                        flag = wx.ALIGN_CENTER)
+
+        dialogSizer.Add(item = self.statusbar, proportion = 0)
+
+        self.SetSizer(dialogSizer)
+        self.Layout()
+
+        self.SetMinSize((550, -1))
+        self.SetMaxSize((-1, self.GetBestSize()[1]))
+        self.Fit()
+
+    def MakeAdvConnPane(self, pane):
+        """!Create advanced connection settings pane
+        """
+        self.usernameText = wx.StaticText(parent = pane,
+                                          id = wx.ID_ANY, label = _("Username:"))
+        self.username  = wx.TextCtrl(parent = pane, id = wx.ID_ANY)
+
+        self.passwText = wx.StaticText(parent = pane, 
+                                        id = wx.ID_ANY, label = _("Password:"))
+        self.password  = wx.TextCtrl(parent = pane, id = wx.ID_ANY,
+                                     style = wx.TE_PASSWORD)
+
+        # pane layout
+        adv_conn_sizer = wx.BoxSizer(wx.VERTICAL)
+
+        usernameSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        usernameSizer.Add(item = self.usernameText,
+                          flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
+
+        usernameSizer.Add(item = self.username, proportion = 1, 
+                          flag = wx.EXPAND, border = 5)
+
+        adv_conn_sizer.Add(item = usernameSizer,
+                           flag = wx.ALL | wx.EXPAND, border = 5)
+
+        passwSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        passwSizer.Add(item = self.passwText,
+                       flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
+
+        passwSizer.Add(item = self.password, proportion = 1, 
+                       flag = wx.EXPAND, border = 5)
+
+        adv_conn_sizer.Add(item = passwSizer,
+                           flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        pane.SetSizer(adv_conn_sizer)
+        adv_conn_sizer.Fit(pane)
+
+        pane.SetSizer(adv_conn_sizer)
+        adv_conn_sizer.Fit(pane)
+
+    def OnSettingsSaving(self, event):
+        """!Check if required data are filled before setting save is performed.
+        """
+        server = self.server.GetValue().strip()
+        if not server:
+            GMessage(parent = self,
+                     message = _("No data source defined, settings are not saved."))
+            return
+
+        self.settsManager.SetDataToSave((server,
+                                         self.username.GetValue(),
+                                         self.password.GetValue()))
+        event.Skip()
+
+    def OnSettingsChanged(self, event):
+        """!Update widgets according to chosen settings"""
+        data = event.data
+
+        # data list: [server, username, password]
+        if len < 3:
+            return
+
+        self.server.SetValue(data[0])
+
+        self.username.SetValue(data[1])
+        self.password.SetValue(data[2])
+
+        if data[1] or data[2]:
+            self.adv_conn.Expand()
+        else:
+            self.adv_conn.Collapse(True)
+
+    def OnSettingsLoaded(self, event):
+        """!If settings are empty set default servers
+        """
+        if not event.settings:
+            self.settsManager.SetSettings(self.default_servers)
+
+    def OnClose(self, event):
+        """!Close the dialog
+        """
+        """!Close dialog"""
+        if not self.IsModal():
+            self.Destroy()
+        event.Skip()
+
+    def _getCapFiles(self):
+        ws_cap_files = {}
+        for v in self.ws_panels.itervalues():
+            ws_cap_files[v['panel'].GetWebService()] = v['panel'].GetCapFile()
+
+        return ws_cap_files
+
+    def OnServer(self, event):
+        """!Server settings edited
+        """
+        value = event.GetString()
+        if value:
+            self.btn_connect.Enable(True)
+        else:
+            self.btn_connect.Enable(False)
+        
+    def OnOutputLayerName(self, event):
+        """!Update layer name to web service panel
+        """
+        lname = event.GetString()
+
+        for v in self.ws_panels.itervalues():
+            v['panel'].SetOutputLayerName(lname.strip())
+
+    def OnConnect(self, event):
+        """!Connect to the server
+        """
+        server = self.server.GetValue().strip()
+
+        self.ch_ws_sizer.Clear(deleteWindows = True)
+
+        if self.active_ws_panel is not None:
+            self.reqDataPanel.Hide()
+            for btn in self.run_btns:
+                btn.Enable(False)
+            self.active_ws_panel = None
+
+            self.Layout()
+            self.Fit()
+
+        self.statusbar.SetStatusText(_("Connectig to <%s>..." % self.server.GetValue().strip()))
+
+        # number of panels already connected
+        self.finished_panels_num = 0
+        for ws in self.ws_panels.iterkeys():
+            self.ws_panels[ws]['panel'].ConnectToServer(url = server,
+                                                        username = self.username.GetValue(),
+                                                        password = self.password.GetValue())
+            self.ws_panels[ws]['panel'].Hide()
+
+    def OnPanelCapParsed(self, event):
+        """!Called when panel has downloaded and parsed capabilities file.
+        """
+        # how many web service panels are finished
+        self.finished_panels_num +=  1
+
+        # if all are finished, show panels, which succeeded in connection
+        if self.finished_panels_num == len(self.ws_panels):
+            self.UpdateDialogAfterConnection()
+            self.Layout()
+            self.Fit()
+
+    def UpdateDialogAfterConnection(self):
+        """!Update dialog after all web service panels downloaded and parsed capabilities data.
+        """
+        avail_ws = {}
+        for ws, data in self.ws_panels.iteritems():
+
+            if data['panel'].IsConnected():
+                avail_ws[ws] = data
+
+        self.web_service_sel = []
+        self.rb_choices = []
+
+        # at least one web service found on server
+        if len(avail_ws) > 0:
+            self.reqDataPanel.Show()
+            self.rb_order = ['WMS_1.1.1', 'WMS_1.3.0', 'WMTS', 'OnEarth']
+
+            for ws in self.rb_order:
+
+                if ws in avail_ws:
+                    self.web_service_sel.append(ws)
+                    self.rb_choices.append(avail_ws[ws]['label'])
+
+            self.choose_ws_rb = wx.RadioBox(parent = self.reqDataPanel, id = wx.ID_ANY, 
+                                            label = _("Available web services"), 
+                                            pos = wx.DefaultPosition, choices = self.rb_choices, 
+                                            majorDimension = 1, style = wx.RA_SPECIFY_ROWS)
+        
+            self.Bind(wx.EVT_RADIOBOX, self.OnChooseWs, self.choose_ws_rb)
+            self.ch_ws_sizer.Add(item = self.choose_ws_rb,
+                                 flag = wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+            self._showWsPanel(self.web_service_sel[self.choose_ws_rb.GetSelection()])
+            self.statusbar.SetStatusText(_("Connected to <%s>" % self.server.GetValue().strip()))
+            for btn in self.run_btns:
+                btn.Enable(True)
+        # no web service found on server
+        else:
+            self.statusbar.SetStatusText(_("Unable to connect to <%s>" % self.server.GetValue().strip()))
+            for btn in self.run_btns:
+                btn.Enable(False)
+            self.reqDataPanel.Hide()
+            self.active_ws_panel = None
+
+    def OnChooseWs(self, event):
+        """!Show panel corresponding to selected web service.
+        """
+        choosen_r = event.GetInt() 
+        self._showWsPanel(self.web_service_sel[choosen_r])
+
+    def _showWsPanel(self, ws):
+        """!Helper function
+        """
+        if self.active_ws_panel is not None:
+            self.active_ws_panel.Hide()
+
+        self.active_ws_panel = self.ws_panels[ws]['panel']
+        self.active_ws_panel.Show()
+        self.SetMaxSize((-1, -1))
+        self.Layout()
+        self.Fit()
+
+    def OnAdvConnPaneChanged(self, event):
+        """!Collapse search module box
+        """
+        if self.adv_conn.IsExpanded():
+            self.adv_conn.SetLabel(self.infoCollapseLabelCol)
+        else:
+            self.adv_conn.SetLabel(self.infoCollapseLabelExp)
+
+        self.Layout()
+        self.SetMaxSize((-1, self.GetBestSize()[1]))
+        self.SendSizeEvent()
+        self.Fit()
+
+class AddWSDialog(WSDialogBase):
+    """!Show web service layer."""
+    def __init__(self, parent, gmframe, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+
+        WSDialogBase.__init__(self, parent, id = wx.ID_ANY,
+                              style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs)
+
+        self.SetTitle(_("Add web service layer"))
+
+        self.gmframe = gmframe
+
+    def _createWidgets(self):
+
+        WSDialogBase._createWidgets(self)
+
+        self.btn_add = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Add layer"))
+        self.btn_add.SetToolTipString(_("Import selected layers"))        
+        self.btn_add.Enable(False)
+
+        self.run_btns.append(self.btn_add)
+
+    def _doLayout(self):
+
+        WSDialogBase._doLayout(self)
+
+        self.btnsizer.Add(item = self.btn_add, proportion = 0,
+                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          border = 10)
+
+        # bindings
+        self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddLayer)
+
+    def OnAddLayer(self, event):
+        """!Add web service layer.
+        """
+        # add layer
+        if self.active_ws_panel is None:
+            return 
+
+        lcmd = self.active_ws_panel.CreateCmd()
+        if not lcmd:
+            return None
+
+        ltree = self.gmframe.GetLayerTree()
+
+        active_ws = self.active_ws_panel.GetWebService()
+        if 'WMS' not in active_ws:
+            cap_file =  self.active_ws_panel.GetCapFile()
+            cmd_cap_file = grass.tempfile()
+            shutil.copyfile(cap_file, cmd_cap_file)
+            lcmd.append('capfile=' + cmd_cap_file)
+
+        layer = ltree.AddLayer(ltype = 'wms', lname = self.layerName.GetValue(), 
+                               lchecked = True, lcmd = lcmd)
+
+
+        ws_cap_files = self._getCapFiles()
+        # create properties dialog
+        cmd_list = ltree.GetLayerInfo(layer,'cmd')
+        cmd = CmdToTuple(cmd_list)
+
+        prop_win = WSPropertiesDialog(parent = self.gmframe,
+                                      id = wx.ID_ANY,
+                                      layer = layer,
+                                      ltree = ltree,
+                                      ws_cap_files = ws_cap_files,
+                                      cmd = cmd)
+
+        prop_win.Hide()
+        ltree.GetOptData(dcmd = None, layer = layer, 
+                         params = None, propwin = prop_win)
+
+class WSPropertiesDialog(WSDialogBase):
+    """!Show web service property."""
+    def __init__(self, parent, layer, ltree, ws_cap_files, cmd, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """
+        @param layer - layer tree item
+        @param ltree - layer tree reference
+        @param ws_cap_files - dict web service('WMS_1.1.1', 'WMS_1.3.0', 'WMTS', 'OnEarth') : cap file path
+                            - cap files, which will be parsed
+        @param cmd - cmd to which dialog widgets will be initialized if it is possible 
+                    (cmp parameters exists in parsed web service cap_file)
+        """
+
+        WSDialogBase.__init__(self, parent, id = wx.ID_ANY,
+                               style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs)
+
+        self.SetTitle(_("Web service layer properties"))
+
+        self.ltree = ltree
+        self.layer = layer
+
+        # after web service panels are connected, set dialog widgets
+        # according to cmd in this variable (if it is not None) 
+        self.cmd_to_set = None
+
+        # store data needed for reverting
+        self.revert_ws_cap_files = {}
+        self.revert_cmd = cmd
+
+        ws_cap = self._getWSfromCmd(cmd)
+        for ws in self.ws_panels.iterkeys():
+            # cap file used in cmd will be deleted, thnaks to the dialogs destructor
+            if ws == ws_cap and cmd[1].has_key('capfile'):
+                self.revert_ws_cap_files[ws] = cmd[1]['capfile']
+                del ws_cap_files[ws]
+            else:
+                self.revert_ws_cap_files[ws] = grass.tempfile()
+
+        self._setRevertCapFiles(ws_cap_files)
+
+        self.LoadCapFiles(ws_cap_files = self.revert_ws_cap_files, cmd = cmd)
+
+    def __del__(self):
+        for f in self.revert_ws_cap_files.itervalues():
+            grass.try_remove(f)
+
+    def _setRevertCapFiles(self, ws_cap_files):
+
+        for ws, f in ws_cap_files.iteritems():
+            if os.path.isfile(ws_cap_files[ws]):
+                shutil.copyfile(f, self.revert_ws_cap_files[ws])
+            else:
+                # delete file content
+                f_o = open(f, 'w')
+                f_o.close()
+
+    def _createWidgets(self):
+
+        WSDialogBase._createWidgets(self)
+
+        self.btn_apply = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Apply"))
+        self.btn_apply.SetToolTipString(_("Apply changes"))        
+        self.btn_apply.Enable(False)
+        self.run_btns.append(self.btn_apply)
+
+        self.btn_save = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Save"))
+        self.btn_save.SetToolTipString(_("Revert changes"))
+        self.btn_save.Enable(False)
+        self.run_btns.append(self.btn_save)
+
+    def _doLayout(self):
+
+        WSDialogBase._doLayout(self)
+
+        self.btnsizer.Add(item = self.btn_apply, proportion = 0,
+                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          border = 10)
+
+        self.btnsizer.Add(item = self.btn_save, proportion = 0,
+                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          border = 10)
+
+        # bindings
+        self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
+        self.btn_save.Bind(wx.EVT_BUTTON, self.OnSave)
+
+    def LoadCapFiles(self, ws_cap_files, cmd):
+        """!Parse cap files and update dialog.
+
+        @params for description see constructor
+        """
+        self.ch_ws_sizer.Clear(deleteWindows = True)
+
+        self.cmd_to_set = cmd
+
+        self.finished_panels_num = 0
+
+        conn = self._getServerConnFromCmd(cmd)
+
+        self.server.SetValue(conn['url'])
+        self.password.SetValue(conn['password'])
+        self.username.SetValue(conn['username'])
+
+        self.layerName.SetValue(cmd[1]['map'])
+
+        for ws, data in self.ws_panels.iteritems():
+            cap_file = None
+
+            if ws_cap_files.has_key(ws):
+                cap_file = ws_cap_files[ws]
+
+            data['panel'].ParseCapFile(url = conn['url'], 
+                                       username = conn['password'], 
+                                       password = conn['username'], 
+                                       cap_file = cap_file)
+
+    def _getServerConnFromCmd(self, cmd):
+        """!Get url/server/passwod from cmd tuple 
+        """
+        conn = { 'url' : '', 'username' : '', 'password' : ''}
+        
+        for k in conn.iterkeys():
+            if cmd[1].has_key(k):
+                conn[k] = cmd[1][k]
+        return conn
+
+    def _apply(self):
+        """!Apply chosen values from widgets to web service layer."""
+        lcmd = self.active_ws_panel.CreateCmd()
+        if not lcmd:
+            return
+
+        active_ws = self.active_ws_panel.GetWebService()
+        if 'WMS' not in active_ws:
+            lcmd.append('capfile=' + self.revert_ws_cap_files[active_ws])
+
+        self.ltree.GetOptData(dcmd = lcmd, 
+                              layer = self.layer, 
+                              params = None,
+                              propwin = self)
+
+        #TODO use just list or tuple
+        cmd = CmdToTuple(lcmd)
+        self.revert_cmd = cmd
+        self._setRevertCapFiles(self._getCapFiles())
+
+        display = self.ltree.GetMapDisplay().GetMapWindow()
+        event = gUpdateMap()
+        wx.PostEvent(display, event)
+
+    def OnApply(self, event):   
+        self._apply()
+
+    def OnSave(self, event):
+        self._apply()
+        self._close()
+
+    def OnClose(self, event):
+        """!Close dialog"""
+        self._close()
+
+    def _close(self):
+        """!Hide dialog"""
+        self.Hide()
+        self.LoadCapFiles(cmd = self.revert_cmd,
+                          ws_cap_files = self.revert_ws_cap_files)
+
+    def OnPanelCapParsed(self, event):
+        """!Called when panel has downloaded and parsed capabilities file.
+        """
+        WSDialogBase.OnPanelCapParsed(self, event)
+
+        if self.finished_panels_num == len(self.ws_panels):
+            if self.cmd_to_set:
+                self._updateWsPanelWidgetsByCmd(self.cmd_to_set)
+                self.cmd_to_set = None
+
+    def _updateWsPanelWidgetsByCmd(self, cmd):
+        """!Set values of  widgets according to parameters in cmd.
+        """
+
+        ws = self._getWSfromCmd(cmd)
+        if self.ws_panels[ws]['panel'].IsConnected():
+            self.ws_panels[ws]['panel'].UpdateWidgetsByCmd(cmd)
+            self.choose_ws_rb.SetStringSelection(self.ws_panels[ws]['label'])
+            self._showWsPanel(ws)
+
+    def _getWSfromCmd(self, cmd):
+        driver = cmd[1]['driver']
+        ws = driver.split('_')[0]
+        
+        if ws == 'WMS':
+            ws += '_' + cmd[1]['wms_version']
+        return ws
+
+class SaveWMSLayerDialog(wx.Dialog):
+    """!Dialog for saving web service layer into GRASS vector/raster layer.
+
+    @todo Implement saving data in region of map display.
+    """
+    def __init__(self, parent, layer, ltree):
+        
+        wx.Dialog.__init__(self, parent = parent, title = ("Save web service layer"), id = wx.ID_ANY)
+
+        self.layer = layer
+        self.ltree = ltree
+
+        self.cmd = self.layer.GetCmd()
+
+        self.thread = CmdThread(self)
+        self.cmdStdErr = GStderr(self)
+
+        self._createWidgets()
+
+    def _createWidgets(self):
+
+        self.labels = {}
+        self.params = {}
+
+        self.labels['output'] = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name for output raster layer:"))
+
+        self.params['output'] = Select(parent = self, type = 'rast', mapsets = [grass.gisenv()['MAPSET']],
+                                       size = globalvar.DIALOG_GSELECT_SIZE)
+
+        self.regionStBoxLabel = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                             label = _("Region"))
+
+        self.region_types_order = ['comp', 'named']
+        self.region_types =  {}
+        #self.region_types['map_display'] = wx.RadioButton(parent = self, id = wx.ID_ANY, label = 'Map display', style = wx.RB_GROUP )
+        self.region_types['comp'] = wx.RadioButton(parent = self, id = wx.ID_ANY, label = 'Computational region')
+        self.region_types['named'] = wx.RadioButton(parent = self, id = wx.ID_ANY, label = 'Named region')
+
+        self.overwrite  = wx.CheckBox(parent = self, id = wx.ID_ANY,
+                                      label = _("Overwrite existing layer"))
+
+        self.named_reg_panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.labels['region'] = wx.StaticText(parent = self.named_reg_panel, id = wx.ID_ANY, 
+                                             label = _("Choose named region:"))
+
+        self.params['region'] = Select(parent = self.named_reg_panel, type = 'region',
+                                       size = globalvar.DIALOG_GSELECT_SIZE)
+
+        # buttons
+        self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
+        self.btn_close.SetToolTipString(_("Close dialog"))
+        
+        self.btn_save = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Save layer"))
+        self.btn_save.SetToolTipString(_("Add web service layer"))     
+
+        # statusbar
+        self.statusbar = wx.StatusBar(parent = self, id = wx.ID_ANY)
+
+        self._layout()
+
+    def _layout(self):
+
+        border = wx.BoxSizer(wx.VERTICAL) 
+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+
+        regionSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        dialogSizer.Add(item = self._addSelectSizer(title = self.labels['output'], 
+                                                    sel = self.params['output']))
+
+        dialogSizer.Add(item = self.overwrite)
+
+        regionSizer = wx.StaticBoxSizer(self.regionStBoxLabel, wx.VERTICAL)
+
+        regionTypeSizer = wx.BoxSizer(wx.HORIZONTAL)
+        for r_type in self.region_types_order:
+            regionTypeSizer.Add(item = self.region_types[r_type])
+
+        regionSizer.Add(item = regionTypeSizer)
+
+        self.named_reg_panel.SetSizer(self._addSelectSizer(title = self.labels['region'],
+                                                            sel = self.params['region']))
+        regionSizer.Add(item = self.named_reg_panel)
+        self.named_reg_panel.Hide()
+
+        dialogSizer.Add(item = regionSizer, flag = wx.EXPAND)
+
+        # buttons
+        self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+
+        self.btnsizer.Add(item = self.btn_close, proportion = 0,
+                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          border = 10)
+        
+        self.btnsizer.Add(item = self.btn_save, proportion = 0,
+                          flag = wx.ALL | wx.ALIGN_CENTER,
+                          border = 10)
+
+        dialogSizer.Add(item = self.btnsizer, proportion = 0,
+                        flag = wx.ALIGN_CENTER)
+
+        border.Add(item = dialogSizer, proportion = 0,
+                   flag = wx.ALL, border = 5)
+
+        border.Add(item = self.statusbar, proportion = 0)
+
+        self.SetSizer(border)
+        self.Layout()
+        self.Fit()
+
+        # bindings
+        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+        self.btn_save.Bind(wx.EVT_BUTTON, self.OnSave)
+
+        self.Bind(EVT_CMD_DONE,   self.OnCmdDone)
+        self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
+
+        for r_type in self.region_types_order:
+            self.Bind(wx.EVT_RADIOBUTTON, self.OnRegionType, self.region_types[r_type])
+
+    def _addSelectSizer(self, title, sel): 
+        """!Helper layout function.
+        """
+        selSizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+        selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
+        selTitleSizer.Add(item = title, proportion = 1,
+                          flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+
+        selSizer.Add(item = selTitleSizer, proportion = 0,
+                     flag = wx.EXPAND)
+
+        selSizer.Add(item = sel, proportion = 1,
+                     flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
+                     border = 5)
+
+        return selSizer
+
+    def OnClose(self, event):
+        """!Close dialog
+        """
+        if not self.IsModal():
+            self.Destroy()
+        event.Skip()
+
+    def OnRegionType(self, event):
+        
+        selected = event.GetEventObject()
+        if selected == self.region_types['named']:
+            self.named_reg_panel.Show()
+        else:
+            self.named_reg_panel.Hide()
+
+        self.Layout()
+        self.Fit()
+
+    def OnSave(self, event):
+        """!Import WMS raster data into GRASS as raster layer.
+        """
+        self.thread.abort(abortall = True)
+        currmapset = grass.gisenv()['MAPSET']
+        
+        self.output = self.params['output'].GetValue().strip()
+        l_spl = self.output.strip().split("@")
+
+        # check output layer
+        msg = None
+        if not self.output:
+            msg = _('Missing output raster.')
+
+        elif len(l_spl) > 1 and \
+             l_spl[1] != currmapset:
+                msg = _('Output map can be added only to current mapset.')
+
+        elif not self.overwrite.IsChecked() and\
+            grass.find_file(self.output, 'cell', '.')['fullname']:
+            msg = _('Output map <%s> already exists' % self.output)
+
+        if msg:
+            GWarning(parent = self,
+                     message = msg)
+            return
+
+        self.output = l_spl[0]
+
+
+        # check region
+        region = self.params['region'].GetValue().strip()
+        reg_spl = region.strip().split("@")
+
+        reg_mapset = '.'
+        if len(reg_spl) > 1:
+            reg_mapset = reg_spl[1]
+
+        if self.region_types['comp'].GetValue() == 1: 
+            pass
+        elif grass.find_file(reg_spl[0], 'region', reg_mapset)['fullname']:
+            msg = _('Region <%s> does not exists.' % self.params['region'].GetValue())
+            GWarning(parent = self,
+                     message = msg)
+            return
+
+        # create r.in.wms command
+        cmd = ('r.in.wms', deepcopy(self.cmd[1]))
+
+        if cmd[1].has_key('map'):
+            del cmd[1]['map']
+
+        cmd[1]['output'] = self.output
+
+        if self.overwrite.IsChecked():
+            cmd[1]['overwrite'] = True
+
+        if self.region_types['named'].GetValue() == 1:
+            cmd[1]['region'] = region
+
+        cmdList = CmdTupleToList(cmd)
+        self.currentPid = self.thread.GetId()
+
+        self.thread.RunCmd(cmdList, stderr = self.cmdStdErr)
+
+        self.statusbar.SetStatusText(_("Downloading data..."))
+
+    def OnCmdDone(self, event):
+        """!When data are fetched.
+        """
+        if event.pid != self.currentPid:
+            return
+
+        self._addLayer()
+        self.statusbar.SetStatusText("")
+
+    def _addLayer(self):
+        """!Add layer into layer tree.
+        """
+        if self.ltree.FindItemByData(key = 'name', value = self.output) is None: 
+            cmd = ['d.rast', 'map=' + self.output]
+            self.ltree.AddLayer(ltype = 'raster',
+                                lname = self.output,
+                                lcmd = cmd,
+                                lchecked = True)
+
+    def OnCmdOutput(self, event):
+        """!Handle cmd output according to debug level.
+        """
+        if Debug.GetLevel() == 0:
+            if event.type == 'error':
+                msg = _('Unable to fetch data.\n')
+                msg += event.text
+                GWarning(parent = self,
+                         message = msg)
+        else:
+            Debug.msg(1, event.text)

Added: grass/trunk/gui/wxpython/web_services/widgets.py
===================================================================
--- grass/trunk/gui/wxpython/web_services/widgets.py	                        (rev 0)
+++ grass/trunk/gui/wxpython/web_services/widgets.py	2013-01-12 16:41:52 UTC (rev 54607)
@@ -0,0 +1,1016 @@
+"""!
+ at package web_services.widgets
+
+ at brief Widgets for web services (WMS, WMTS, NasaOnEarh)
+
+List of classes:
+ - widgets::WSPanel
+ - widgets::LayersList
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Stepan Turek <stepan.turek seznam.cz>
+"""
+
+import os
+import sys
+import shutil
+
+from copy                  import deepcopy
+from xml.etree.ElementTree import ParseError
+
+import wx
+import wx.lib.flatnotebook    as FN
+import wx.lib.colourselect    as csel
+import wx.lib.mixins.listctrl as listmix
+from   wx.lib.newevent        import NewEvent
+from   wx.gizmos              import TreeListCtrl
+
+from core              import globalvar
+from core.debug        import Debug
+from core.gcmd         import GWarning
+from core.gconsole     import CmdThread, EVT_CMD_DONE
+
+from web_services.cap_interface import WMSCapabilities, \
+                                       WMTSCapabilities, \
+                                       OnEarthCapabilities
+
+from gui_core.widgets  import GNotebook
+
+import grass.script as grass
+
+WMSLibPath = os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms")
+sys.path.append(WMSLibPath)
+
+from wms_base import WMSDriversInfo
+
+wxOnCapParsed, EVT_CAP_PARSED = NewEvent()
+
+class WSPanel(wx.Panel):
+    def __init__(self, parent, receiver, web_service, **kwargs):
+        """!Show data from capabilities file.
+
+        Events: EVT_CAP_PARSED - this event is released when capabilities file is downloaded 
+                                 (after ConnectToServer method was called)
+
+        @param parent       - parent widget
+        @param receiver     - receives EVT_CAP_PARSED event
+        @param web_service  - web service to be panel generated for
+        """
+        wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+
+        self.parent = parent
+        self.ws = web_service
+        self.receiver = receiver
+
+        # stores widgets, which represents parameters/flags of d.wms
+        self.params = {}
+        self.flags = {}
+
+        self.o_layer_name = ''
+
+        # stores selected layer from layer list
+        self.sel_layers = []
+
+        # downloaded and parsed data from server successfully?
+        self.is_connected = False
+
+        # common part of command for r.in.wms -c and d.wms
+        self.ws_cmdl = None
+
+        # provides information about driver parameters
+        self.drv_info = WMSDriversInfo()
+        self.drv_props = self.drv_info.GetDrvProperties(self.ws)
+
+        self.ws_drvs = {    
+                        'WMS_1.1.1' : {
+                                        'cmd' : ['wms_version=1.1.1', 
+                                                 'driver=WMS_GRASS'],
+                                        'cap_parser' : lambda temp_file : WMSCapabilities(temp_file, '1.1.1'),
+                                      },
+                        'WMS_1.3.0' : {
+                                        'cmd' : ['wms_version=1.3.0', 
+                                                 'driver=WMS_GRASS'],
+                                        'cap_parser' : lambda temp_file : WMSCapabilities(temp_file, '1.3.0'),
+                                      },
+                        'WMTS' :      {
+                                        'cmd' : ['driver=WMTS_GRASS'],
+                                        'cap_parser' : WMTSCapabilities,
+                                      },
+                        'OnEarth' : {
+                                        'cmd' : ['driver=OnEarth_GRASS'],
+                                        'cap_parser' : OnEarthCapabilities,
+                                      }
+                      }
+
+        self.cmd_thread = CmdThread(self)
+        self.cap_file = grass.tempfile()
+
+        self.notebook = GNotebook(parent = self,
+                                  style = FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)
+
+        self._requestPage()
+        self._advancedSettsPage()
+
+        self._layout()
+
+        self.Bind(EVT_CMD_DONE, self.OnCapDownloadDone)
+
+    def __del__(self):
+        self.cmd_thread.abort(abortall =True)
+        grass.try_remove(self.cap_file)
+
+    def _layout(self):
+        reqDataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                  label = _(" Requested data settings "))
+        sizer = wx.StaticBoxSizer(reqDataBox, wx.VERTICAL)
+        sizer.Add(item = self.notebook, proportion = 1,
+                  flag = wx.EXPAND)
+        self.SetSizer(sizer)
+
+    def _requestPage(self):
+        """!Create request page"""
+        self.req_page_panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.notebook.AddPage(page = self.req_page_panel, 
+                              text=_('Request'), 
+                              name = 'request')
+
+        # list of layers
+        self.layersBox = wx.StaticBox(parent = self.req_page_panel, id = wx.ID_ANY,
+                                      label=_("List of layers "))
+
+        style = wx.TR_DEFAULT_STYLE | wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT 
+        if self.drv_props['req_multiple_layers']: 
+            style = style | wx.TR_MULTIPLE
+        if 'WMS' not in self.ws:
+            style = style | wx.TR_HIDE_ROOT
+
+        self.list = LayersList(parent = self.req_page_panel, 
+                               web_service = self.ws, 
+                               style = style)
+
+        self.params['format'] = None 
+
+        self.params['srs'] = None
+        if 'srs' not in  self.drv_props['ignored_params']:
+            projText = wx.StaticText(parent = self.req_page_panel, id = wx.ID_ANY, label = _("Source projection:"))
+            self.params['srs'] =  wx.Choice(parent = self.req_page_panel, id = wx.ID_ANY, pos = wx.DefaultPosition,
+                                            choices = [], style = wx.RA_SPECIFY_COLS)
+
+        self.list.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnListSelChanged)
+
+        # layout
+        self.req_page_sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        layersSizer = wx.StaticBoxSizer(self.layersBox, wx.HORIZONTAL)
+
+        layersSizer.Add(item = self.list, proportion = 1,
+                        flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        
+        self.req_page_sizer.Add(item = layersSizer, proportion = 1,
+                            flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        if self.params['format'] is not None:
+            self.req_page_sizer.Add(item = self.params['format'],
+                                flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+ 
+        projSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        if self.params['srs'] is not None:
+            projSizer.Add(item = projText, flag = wx.ALIGN_CENTER_VERTICAL)
+            projSizer.Add(item = self.params['srs'])
+
+        self.req_page_sizer.Add(item = projSizer,
+                                flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+         
+        self.req_page_panel.SetSizer(self.req_page_sizer)
+    
+    def enableButtons(self, enable = True):
+        """!Enable/disable up, down, buttons
+        """
+        self.btnUp.Enable(enable)
+        self.btnDown.Enable(enable)
+
+    def _advancedSettsPage(self):
+        """!Create advanced settings page
+        """
+        #TODO parse maxcol, maxrow, settings from d.wms module?
+        #TODO OnEarth driver - add selection of time
+        adv_setts_panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.notebook.AddPage(page = adv_setts_panel, 
+                              text=_('Advanced request settings'), 
+                              name = 'adv_req_setts')
+
+        labels = {}
+        self.l_odrder_list = None
+        if 'WMS' in self.ws:
+            labels['l_order'] = wx.StaticBox(parent = adv_setts_panel, id = wx.ID_ANY,
+                                             label = _("Order of layers in raster"))
+            self.l_odrder_list = wx.ListBox(adv_setts_panel, id = wx.ID_ANY, choices = [], 
+                                                                style = wx.LB_SINGLE|wx.LB_NEEDED_SB)
+            self.btnUp = wx.Button(adv_setts_panel, id = wx.ID_ANY, label = _("Up"))
+            self.btnDown = wx.Button(adv_setts_panel, id = wx.ID_ANY, label = _("Down"))
+
+            self.btnUp.Bind(wx.EVT_BUTTON, self.OnUp)
+            self.btnDown.Bind(wx.EVT_BUTTON, self.OnDown)
+
+        labels['method'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
+                                         label = _("Reprojection method:"))
+
+        self.reproj_methods = ['near', 'bilinear', 'cubic', 'cubicspline']
+        self.params['method'] = wx.Choice(parent = adv_setts_panel, id = wx.ID_ANY,
+                                          choices = [_('near'), _('bilinear'), _('cubic'), _('cubicspline')])
+
+        labels['maxcols'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
+                                          label = _("Maximum columns to request from server at time:"))
+        self.params['maxcols'] = wx.SpinCtrl(parent = adv_setts_panel, id = wx.ID_ANY, size = (100, -1))
+
+        labels['maxrows'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
+                                          label = _("Maximum rows to request from server at time:"))
+        self.params['maxrows'] = wx.SpinCtrl(parent = adv_setts_panel, id = wx.ID_ANY, size = (100, -1))
+
+        min = 100
+        max = 10000
+        self.params['maxcols'].SetRange(min,max)
+        self.params['maxrows'].SetRange(min,max)
+
+        val = 500
+        self.params['maxcols'].SetValue(val)
+        self.params['maxrows'].SetValue(val)
+
+        self.flags['o'] = self.params['bgcolor'] = None
+        if not 'o' in self.drv_props['ignored_flags']:
+            self.flags['o']  = wx.CheckBox(parent = adv_setts_panel, id = wx.ID_ANY,
+                                           label = _("Do not request transparent data"))
+
+            self.flags['o'].Bind(wx.EVT_CHECKBOX, self.OnTransparent)
+            labels['bgcolor'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
+                                              label = _("Background color:"))
+            self.params['bgcolor'] = csel.ColourSelect(parent = adv_setts_panel, id = wx.ID_ANY,
+                                                       colour = (255, 255, 255),
+                                                       size = globalvar.DIALOG_COLOR_SIZE)
+            self.params['bgcolor'].Enable(False)
+
+        self.params['urlparams'] = None
+        if self.params['urlparams'] not in self.drv_props['ignored_params']:
+            labels['urlparams'] = wx.StaticText(parent = adv_setts_panel, id = wx.ID_ANY,
+                                                label = _("Additional query parameters for server:"))
+            self.params['urlparams'] = wx.TextCtrl(parent = adv_setts_panel, id = wx.ID_ANY)
+
+        # layout
+
+        border = wx.BoxSizer(wx.VERTICAL)
+       
+        if 'WMS' in self.ws:
+
+            boxSizer = wx.StaticBoxSizer(labels['l_order'], wx.VERTICAL)
+            gridSizer  =  wx.GridBagSizer (hgap = 3, vgap = 3)
+            gridSizer.AddGrowableCol(0)
+
+            gridSizer.Add(self.l_odrder_list, 
+                          pos = (0,0), 
+                          span = (4, 1), 
+                          flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 
+                          border = 0)
+        
+            gridSizer.Add(self.btnUp,
+                          pos = (0,1), 
+                          flag = wx.ALIGN_CENTER_VERTICAL, 
+                          border = 0)
+
+            gridSizer.Add(self.btnDown, 
+                          pos = (1,1), 
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          border = 0)
+        
+            boxSizer.Add(gridSizer,
+                         flag = wx.EXPAND | wx.ALL,
+                         border = 5)
+
+            border.Add(item = boxSizer,
+                       flag = wx.LEFT | wx.RIGHT | wx.UP | wx.EXPAND, 
+                       border = 5)
+
+        gridSizer  =  wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+
+        row = 0
+        for k in ['method', 'maxcols', 'maxrows', 'o', 'bgcolor']:
+
+            if self.params.has_key(k):
+                param = self.params[k]
+            elif self.flags.has_key(k):
+                param = self.flags[k]
+
+            if param is None:
+                continue
+
+            if labels.has_key(k) or k == 'o':
+                if k != 'o':
+                    label = labels[k]
+                else:
+                    label = param
+
+                gridSizer.Add(label,
+                              flag = wx.ALIGN_LEFT |
+                              wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 0))
+
+            if k != 'o':
+                gridSizer.Add(item = param,
+                              flag = wx.ALIGN_RIGHT |
+                              wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 1))
+            row += 1
+
+        border.Add(item = gridSizer,
+                   flag = wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND, 
+                   border = 5)
+
+        if self.params['urlparams']:
+            gridSizer  =  wx.GridBagSizer (hgap = 3, vgap = 3)
+            gridSizer.AddGrowableCol(1)
+
+            row = 0
+            gridSizer.Add(labels['urlparams'],
+                          flag = wx.ALIGN_LEFT |
+                          wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 0))
+
+            gridSizer.Add(item = self.params['urlparams'],
+                          flag = wx.ALIGN_RIGHT |
+                          wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                          pos = (row, 1))
+
+            border.Add(item = gridSizer,
+                       flag = wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND, 
+                       border = 5)
+
+        adv_setts_panel.SetSizer(border)
+
+    def OnUp(self, event):
+        """!Move selected layer up
+        """
+        if self.l_odrder_list.GetSelections():
+            pos = self.l_odrder_list.GetSelection()
+            if pos:
+                self.sel_layers.insert(pos - 1, self.sel_layers.pop(pos))               
+            if pos > 0:
+                self._updateLayerOrderList(selected = (pos - 1)) 
+            else:
+                self._updateLayerOrderList(selected = 0)
+
+    def OnDown(self, event):
+        """!Move selected to down
+        """
+        if self.l_odrder_list.GetSelections():
+            pos = self.l_odrder_list.GetSelection()
+            if pos != len(self.sel_layers) - 1:
+                self.sel_layers.insert(pos + 1, self.sel_layers.pop(pos))
+            if pos < len(self.sel_layers) -1:
+                self._updateLayerOrderList(selected = (pos + 1)) 
+            else:
+                self._updateLayerOrderList(selected = len(self.sel_layers) -1)
+
+    def _updateLayerOrderList(self, selected = None):
+        """!Update order in list.
+        """
+        def GetLayerCaption(layer):
+            if l['title']:
+                cap = (l['title'])
+            else:
+                cap = (l['name'])
+
+            if l['style']:
+                if l['style']['title']:
+                    cap += ' / ' + l['style']['title']
+                else:
+                    cap += ' / ' + l['style']['name']
+            return cap
+
+        layer_capts = [GetLayerCaption(l) for l in self.sel_layers]
+        self.l_odrder_list.Set(layer_capts)
+        if self.l_odrder_list.IsEmpty():
+            self.enableButtons(False)
+        else:
+            self.enableButtons(True)
+            if selected is not None:
+                self.l_odrder_list.SetSelection(selected)  
+                self.l_odrder_list.EnsureVisible(selected)  
+
+    def OnTransparent(self, event):
+        checked = event.IsChecked()
+        if checked:
+            self.params['bgcolor'].Enable(True)
+        else:
+            self.params['bgcolor'].Enable(False)
+
+    def ConnectToServer(self, url, username, password):
+        """!Download and parse data from capabilities file.
+
+        @param url - server url
+        @param username - username for connection
+        @param password - password for connection
+        """
+        self._prepareForNewConn(url, username, password)
+        cap_cmd = ['r.in.wms', '-c', ('capfile_output=%s' % self.cap_file)] + self.ws_cmdl
+
+        self.currentPid = self.cmd_thread.GetId()
+        self.cmd_thread.RunCmd(cap_cmd)
+
+    def _prepareForNewConn(self, url, username, password):
+        """!Prepare panel for new connection
+        """
+        self.is_connected = False
+
+        self.sel_layers = []
+        self.formats_list = []
+        self.projs_list = []
+
+        self.conn = {
+                        'url' : url,
+                        'password' : password,
+                        'username' : username
+                    }
+
+        conn_cmd = []
+        for k, v in self.conn.iteritems():
+            if v:
+                conn_cmd.append("%s=%s" % (k,v))
+
+        self.ws_cmdl = self.ws_drvs[self.ws]['cmd'] + conn_cmd
+
+    def OnCapDownloadDone(self, event):
+        """!Process donwloaded capabilities file and emits EVT_CAP_PARSED (see class constructor).
+        """
+        if event.pid != self.currentPid:
+            return
+
+        if event.returncode != 0:
+            msg = "Downloading of capabilities file failed."
+            self._postCapParsedEvt(IOError(msg))
+            return
+
+        self._parseCapFile(self.cap_file)
+
+    def _parseCapFile(self, cap_file):
+        """!Parse capabilities data and emits EVT_CAP_PARSED (see class constructor).
+        """ 
+        try:
+            self.cap = self.ws_drvs[self.ws]['cap_parser'](cap_file)
+        except (IOError, ParseError) as error:
+            self._postCapParsedEvt(error)
+            return
+
+        if self.l_odrder_list:
+            self.l_odrder_list.Clear()
+
+        self.is_connected = True
+
+        # WMS standard has formats defined for all layers
+        if 'WMS' in self.ws:
+            self.formats_list = sorted(self._getFormats())
+            self._updateFormatRadioBox(self.formats_list)
+            self._setDefaultFormatVal()
+
+        self.OnListSelChanged(event = None)
+
+        self.list.LoadData(self.cap)
+        self._postCapParsedEvt(None)
+
+    def ParseCapFile(self, url, username, password, cap_file = None,):
+        """!Parse capabilities data and emits EVT_CAP_PARSED (see class constructor).
+        """ 
+        self._prepareForNewConn(url, username, password)
+
+        if cap_file is None or not url:
+            self._postCapParsedEvt(None)
+            return
+
+        shutil.copyfile(cap_file, self.cap_file)
+
+        self._parseCapFile(self.cap_file)
+
+    def UpdateWidgetsByCmd(self, cmd):
+        """!Update panel widgets accordnig to passed cmd tuple
+        @param cmd - cmd in tuple
+        """
+
+        dcmd = cmd[1]
+
+        layers = []
+
+        if self.l_odrder_list:
+            self.l_odrder_list.Clear()
+
+        if dcmd.has_key('layers'):
+            layers = dcmd['layers']
+
+        styles = []
+        if dcmd.has_key('styles'):
+            styles = dcmd['styles']
+
+        if 'WMS' in self.ws:
+            layers = layers.split(',')
+            styles = styles.split(',')
+        else:
+            layers = [layers]
+            styles = [styles]
+
+        if len(layers) != len(styles):
+            styles = [''] * len(layers)
+
+        l_st_list = []
+        for i in range(len(layers)):
+            l_st_list.append({'style' : styles[i],
+                              'layer' : layers[i]})
+        
+        # WMS standard - first layer in params is most bottom...
+        # therefore layers order need to be reversed
+        l_st_list = [l for l in reversed(l_st_list)]
+        self.list.SelectLayers(l_st_list)
+
+        params = {}
+        if  dcmd.has_key('format'):
+            params['format'] = dcmd['format']
+        if  dcmd.has_key('srs'):
+            params['srs'] = 'EPSG:' + dcmd['srs']
+        if  dcmd.has_key('method'):
+            params['method'] = dcmd['method']
+
+        for p, v in params.iteritems():
+            if self.params[p]:
+                self.params[p].SetStringSelection(v)
+   
+        for p, conv_f in [('urlparams', None), ('maxcols', int), ('maxrows', int)]:
+            if dcmd.has_key(p):
+                v = dcmd[p]
+                if conv_f:
+                    v = conv_f(v)
+                self.params[p].SetValue(v)
+
+        if dcmd.has_key('flags') and \
+           'o' in dcmd['flags']:
+           self.flags['o'].SetValue(1)
+           self.params['bgcolor'].Enable(True)
+
+        if dcmd.has_key('bgcolor') and \
+           self.params['bgcolor']:
+            bgcolor = dcmd['bgcolor'].strip().lower()
+            if len(bgcolor) == 8 and \
+               '0x' == bgcolor[:2]:
+
+                colour= '#' + bgcolor[2:]
+                self.params['bgcolor'].SetColour(colour)
+
+    def IsConnected(self):
+        """!Was successful in downloading and parsing capabilities data?
+        """
+        return self.is_connected
+
+    def _postCapParsedEvt(self, error):
+        """!Helper function
+        """
+        if error:
+            msg = "%s web service was not found in fetched capabilities from '%s'.\n%s\n" % \
+                  (self.ws, self.conn['url'], str(error))
+            Debug.msg(3, msg)
+
+        cap_parsed_event = wxOnCapParsed()
+        cap_parsed_event.SetEventObject(self)
+        wx.PostEvent(self.receiver, cap_parsed_event)
+
+    def CreateCmd(self):
+        """!Create d.wms cmd from values of panels widgets 
+
+        @return cmd list
+        @return None if required widgets do not have selected/filled values. 
+        """
+
+        # check required widgets
+        if not self._checkImportValues():
+            return None
+
+        # create d.wms command
+        lcmd = self.ws_cmdl
+        lcmd = ['d.wms'] + lcmd
+
+        layers="layers="
+        styles='styles='
+        first = True
+
+        # WMS standard - first layer in params is most bottom...
+        # therefore layers order need to be reversed
+        for layer in reversed(self.sel_layers):
+            if not first:
+                layers += ','
+                styles += ','
+            first = False
+            layers += layer['name'] 
+            if layer['style'] is not None:
+                styles += layer['style']['name']
+
+        lcmd.append(layers)
+        lcmd.append(styles)
+
+        if 'format' not in self.drv_props['ignored_params']:
+            i_format = self.params['format'].GetSelection()
+            lcmd.append("format=%s" % self.formats_list[i_format])
+
+        if 'srs' not in self.drv_props['ignored_params']:
+            i_srs = self.params['srs'].GetSelection()
+            epsg_num = int(self.projs_list[i_srs].split(':')[1])
+            lcmd.append("srs=%s" % epsg_num)
+
+        for k in ['maxcols', 'maxrows', 'urlparams']:
+            lcmd.append(k + '=' + str(self.params[k].GetValue()))
+
+        i_method = self.params['method'].GetSelection()
+        lcmd.append('method=' + self.reproj_methods[i_method])
+
+        if not 'o' in self.drv_props['ignored_flags'] and \
+            self.flags['o'].IsChecked():
+            lcmd.append('-o')
+
+            c = self.params['bgcolor'].GetColour()
+            hex_color = wx.Color(c[0], c[1], c[2]).GetAsString(wx.C2S_HTML_SYNTAX)
+            lcmd.append("bgcolor=" + '0x' + hex_color[1:]) 
+
+        lcmd.append("map=" + self.o_layer_name)
+
+        return lcmd
+
+    def OnListSelChanged(self, event):
+        """!Update widgets according to selected layer in list.
+        """
+        curr_sel_ls = self.list.GetSelectedLayers()
+
+        # update self.sel_layers (selected layer list)
+
+        if 'WMS' in self.ws:
+            for sel_l in self.sel_layers:
+                if sel_l not in curr_sel_ls:
+                    self.sel_layers.remove(sel_l)
+
+            for l in curr_sel_ls:
+                if l not in self.sel_layers:
+                    self.sel_layers.append(l)
+
+            self._updateLayerOrderList()
+        else:
+            self.sel_layers = curr_sel_ls
+
+        # update projection 
+
+        self.projs_list = []
+        projs_list = []
+
+        intersect_proj = []
+        first = True
+        for l in curr_sel_ls:
+            layer_projs = l['cap_intf_l'].GetLayerData('srs')
+
+            if first:
+                projs_list = layer_projs
+                first = False
+                continue
+
+            projs_list = set(projs_list).intersection(layer_projs)
+
+        if 'srs' not in self.drv_props['ignored_params']:
+
+            for proj in projs_list:
+                proj_spl = proj.strip().split(':')
+
+                if 'EPSG' in proj_spl[0]:
+                    try:
+                        int(proj_spl[1])
+                        self.projs_list.append(proj)
+                    except ValueError, IndexError:
+                        continue
+
+            cur_sel = self.params['srs'].GetStringSelection()
+
+            self.projs_list = sorted(self.projs_list)
+            self.params['srs'].SetItems(self.projs_list)
+
+
+            if cur_sel:
+                self.params['srs'].SetStringSelection(cur_sel)
+            else:
+                try:
+                    i = self.projs_list.index('EPSG:4326')
+                    self.params['srs'].SetSelection(i)
+                except ValueError:
+                    if len(self.projs_list) > 0:
+                        self.params['srs'].SetSelection(0)
+
+        # update format
+
+        if 'WMS' not in self.ws and \
+           'format' not in self.drv_props['ignored_params']:
+            self.formats_list = []
+            cur_sel = None
+
+            if self.params['format'] is not None:
+                cur_sel = self.params['format'].GetStringSelection()
+
+            if len(curr_sel_ls) > 0:
+                self.formats_list  = sorted(self._getFormats(curr_sel_ls[0]['cap_intf_l']))
+                self._updateFormatRadioBox(self.formats_list)
+
+                if cur_sel:
+                    self.params['format'].SetStringSelection(cur_sel)
+                else:
+                    self._setDefaultFormatVal()
+
+        self.Layout()
+
+    def _setDefaultFormatVal(self):
+        """!Set default format value.
+        """
+        try:
+           i = self.formats_list.index('png')
+           self.params['format'].SetSelection(i)
+        except ValueError:
+            pass
+
+    def _updateFormatRadioBox(self, formats_list):
+        """!Helper function
+        """
+        if self.params['format'] is not None:
+            self.req_page_sizer.Detach(self.params['format'])
+            self.params['format'].Destroy()
+        if len(self.formats_list) > 0:
+            self.params['format'] =  wx.RadioBox(parent = self.req_page_panel, id = wx.ID_ANY, 
+                                                 label = _("Source image format"), pos = wx.DefaultPosition, 
+                                                 choices = formats_list,  majorDimension = 4, 
+                                                 style = wx.RA_SPECIFY_COLS)
+            self.req_page_sizer.Insert(item = self.params['format'], before = 2,
+                                       flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+    def _getFormats(self, layer = None):
+        """!Get formats 
+
+        WMS has formats defined generally for whole cap.
+        In WMTS and NASA OnEarh formats are defined for layer.
+        """
+        formats_label = []
+        if layer is None:
+            formats_list = self.cap.GetFormats()
+        else:
+            formats_list = layer.GetLayerData('format')
+
+        for frmt in formats_list:
+            frmt = frmt.strip()
+            label = self.drv_info.GetFormatLabel(frmt)
+
+            if label:
+                formats_label.append(label)
+
+        return formats_label
+
+    def _checkImportValues(self,): 
+        """!Check if required widgets are selected/filled
+        """
+        warning_str = ""
+        show_war = False
+        if not self.list or not self.list.GetSelectedLayers():
+            warning_str += _("Select layer in layer list.\n")
+            show_war = True
+
+        if self.params['format'] is not None and \
+           self.params['format'].GetSelection() == -1:
+            warning_str += _("Select source image format.\n")
+            show_war = True
+
+        if self.params['srs'] is not None and \
+           self.params['srs'].GetSelection() == -1:
+            warning_str += _("Select source projection.\n")
+            show_war = True
+
+        if not self.o_layer_name:
+            warning_str += _("Insert output layer name.\n")  
+            show_war = True
+
+        if show_war:
+            GWarning(parent = self.parent,
+                     message = warning_str)
+            return False
+
+        return True
+
+    def SetOutputLayerName(self, name):
+        """!Set name of layer to be added to layer tree
+        """
+        self.o_layer_name = name
+
+    def GetCapFile(self):
+        """!Get path to file where capabilities are saved
+        """
+        return self.cap_file
+
+    def GetWebService(self):
+        """!Get web service
+        """
+        return self.ws
+
+class LayersList(TreeListCtrl, listmix.ListCtrlAutoWidthMixin):
+    def __init__(self, parent, web_service, style, pos=wx.DefaultPosition):
+        """!List of layers and styles available in capabilities file
+        """
+        self.parent = parent
+        self.ws = web_service
+
+        TreeListCtrl.__init__(self, parent = parent, id = wx.ID_ANY, style = style)
+        
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        if self.ws != 'OnEarth':
+            self.AddColumn(_('Name'))
+            self.AddColumn(_('Type'))
+        else:
+            self.AddColumn(_('Layer name'))
+
+        self.SetMainColumn(0) # column with the tree
+        self.setResizeColumn(0)
+        
+        self.root = None
+        self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnListSelChanging)
+
+    def LoadData(self, cap = None):
+        """!Load data into list
+        """
+        # detete first all items
+        self.DeleteAllItems()
+
+        if not cap:
+            return
+    
+        def AddLayerChildrenToTree(parent_layer, parent_item):
+            """!Recursive function which adds all capabilities layers/styles to the LayersList. 
+            """
+            def gettitle(layer):
+                """!Helper function"""
+                if layer.GetLayerData('title') is not None:
+                    layer_title = layer.GetLayerData('title')
+                elif layer.GetLayerData('name') is not None:
+                    layer_title = layer.GetLayerData('name')
+                else:
+                    layer_title = str(layer.GetId())
+
+                return layer_title
+
+            def addlayer(layer, item):
+
+                if self.ws != 'OnEarth':
+                    self.SetItemText(item, _('layer'), 1)
+
+                styles = layer.GetLayerData('styles')
+
+                def_st = None
+                for st in styles:
+
+                    if st['name']:
+                        style_name = st['name']
+                    else:
+                        continue
+
+                    if st['title']:
+                        style_name = st['title']
+
+                    if st['isDefault']:
+                        def_st = st
+
+                    style_item = self.AppendItem(item, style_name)
+                    if self.ws != 'OnEarth':
+                        self.SetItemText(style_item, _('style'), 1)
+
+                    self.SetPyData(style_item, {'type' : 'style',
+                                                'layer' : layer, # it is parent layer of style 
+                                                'style' : st})
+ 
+                self.SetPyData(item, {'type' : 'layer', # is it layer or style?
+                                      'layer' : layer,  # *Layer instance from web_services.cap_interface
+                                      'style' : def_st}) # layer can have assigned default style
+
+            if parent_layer is None:
+                parent_layer = cap.GetRootLayer()
+                layer_title = gettitle(parent_layer)
+                parent_item = self.AddRoot(layer_title)
+                addlayer(parent_layer, parent_item)
+
+            for layer in parent_layer.GetChildren():
+                item = self.AppendItem(parent_item, gettitle(layer)) 
+                addlayer(layer, item)
+                AddLayerChildrenToTree(layer, item)
+
+        AddLayerChildrenToTree(None, None)
+        self.ExpandAll(self.GetRootItem())
+
+    def GetSelectedLayers(self):
+        """!Get selected layers/styles in LayersList
+
+        @return dict with these items:
+                    'name'  : layer name used for request
+                              if it is style, it is name of parent layer
+                    'title' : layer title
+                    'style' : {'name' : 'style name', title : 'style title'}
+                    'cap_intf_l' : *Layer instance from web_services.cap_interface
+        """
+        sel_layers = self.GetSelections()
+        sel_layers_dict = []
+        for s in sel_layers:
+
+            try:
+                layer = self.GetPyData(s)['layer']
+            except ValueError:
+                continue
+            sel_layers_dict.append({ 
+                                    'name' : layer.GetLayerData('name'),
+                                    'title' : layer.GetLayerData('title'),
+                                    'style' : self.GetPyData(s)['style'],
+                                    'cap_intf_l' : layer
+                                    })
+        return sel_layers_dict
+
+    def OnListSelChanging(self, event):
+        """!Do not allow to select items, which cannot be requested from server.
+        """
+        cur_item = event.GetItem ()
+
+        if not self.GetPyData(cur_item)['layer'].IsRequestable():
+            event.Veto()
+
+    def GetItemCount(self):
+        """!Required for listmix.ListCtrlAutoWidthMixin
+        """
+        return 0
+
+    def GetCountPerPage(self):
+        """!Required for listmix.ListCtrlAutoWidthMixin
+        """
+        return 0
+
+    def SelectLayers(self, l_st_list):
+        """!Select layers/styles in LayersList
+
+        @param l_st_list - [{style : 'style_name', layer : 'layer_name'}, ...]
+        @return items from l_st_list which were not found
+        """
+        def checknext(item, l_st_list, items_to_sel):
+            def compare(item, l_name, st_name):
+                it_l_name = self.GetPyData(item)['layer'].GetLayerData('name')
+                it_st  = self.GetPyData(item)['style']
+                it_type = self.GetPyData(item)['type']
+
+                if it_l_name == l_name and \
+                 (   (not it_st and not st_name) or \
+                     (it_st and it_st['name'] == st_name and it_type == 'style')):
+
+                    return True
+
+                return False
+
+            for i, l_st in enumerate(l_st_list):
+                l_name = l_st['layer']
+                st_name = l_st['style']
+
+                if compare(item, l_name, st_name):
+                    items_to_sel[i] = [item, l_st]
+                    break
+
+            if len(items_to_sel) == len(l_st_list):
+                item = self.GetNext(item)
+                if not item.IsOk():
+                    return
+                checknext(item, l_st_list, items_to_sel)
+
+        self.UnselectAll()
+
+        l_st_list = deepcopy(l_st_list)        
+        root_item = self.GetRootItem()
+
+        items_to_sel = [None] * len(l_st_list)
+        checknext(root_item, l_st_list, items_to_sel)
+
+        # items are selected according to position in l_st_list
+        # to be added to Layers order list in right order 
+        for i in items_to_sel:
+            if not i:
+                continue
+
+            item, l_st = i
+            un_o = True
+            if self.HasFlag(wx.TR_MULTIPLE):
+                un_o = False
+
+            self.SelectItem(item, unselect_others = un_o)
+            l_st_list.remove(l_st)
+
+        return l_st_list

Modified: grass/trunk/gui/wxpython/xml/menudata.xml
===================================================================
--- grass/trunk/gui/wxpython/xml/menudata.xml	2013-01-12 16:13:46 UTC (rev 54606)
+++ grass/trunk/gui/wxpython/xml/menudata.xml	2013-01-12 16:41:52 UTC (rev 54607)
@@ -184,12 +184,6 @@
               <handler>OnMenuCmd</handler>
               <command>r.in.lidar</command>
             </menuitem>
-            <menuitem>
-              <label>WMS import</label>
-              <help>Download and import data from WMS servers.</help>
-              <keywords>raster,import,wms</keywords><handler>OnImportWMS</handler>
-              <command>r.in.wms</command>
-            </menuitem>
             <separator />
             <menuitem>
               <label>Unpack raster map</label>



More information about the grass-commit mailing list