[GRASS-SVN] r43911 - in grass/branches/releasebranch_6_4/gui: icons/grass2 wxpython wxpython/compat wxpython/docs wxpython/gui_modules wxpython/icons wxpython/scripts wxpython/support wxpython/vdigit wxpython/xml

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Oct 14 11:39:14 EDT 2010


Author: martinl
Date: 2010-10-14 08:39:14 -0700 (Thu, 14 Oct 2010)
New Revision: 43911

Added:
   grass/branches/releasebranch_6_4/gui/icons/grass2/3d-fringe.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/3d-light.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/3d-raster.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/3d-vector.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/3d-view.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/3d-volume.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/check.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/data-add.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/execute.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/help.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/image-export.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/layer-add.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/map-add.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/modeler-main.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/modeler-variables.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/module-add.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/move.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/python-export.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/redo.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/relation-create.png
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_toolbar.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_base.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gmodeler.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/layertree.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_command.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_window.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menu.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_preferences.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ogc_services.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/prompt.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/units.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vclean.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/wxnviz.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/d.rast3d.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.cmd.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.db.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.mon.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.rast.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.vect.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/vkrige.py
   grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxm.dtd
   grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml
Modified:
   grass/branches/releasebranch_6_4/gui/icons/grass2/options.png
   grass/branches/releasebranch_6_4/gui/icons/grass2/overlay-add.png
   grass/branches/releasebranch_6_4/gui/wxpython/Makefile
   grass/branches/releasebranch_6_4/gui/wxpython/README
   grass/branches/releasebranch_6_4/gui/wxpython/build_ext.py
   grass/branches/releasebranch_6_4/gui/wxpython/compat/subprocess.py
   grass/branches/releasebranch_6_4/gui/wxpython/docs/Makefile
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Attribute_Table_Manager.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.GCP_Manager.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizing_Tool.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_surface.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_vector.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_view.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_volume.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/__init__.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/colorrules.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/debug.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmanager.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmapdisp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gdialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/georect.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/globalvar.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gselect.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/histogram.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/location_wizard.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mcalc_builder.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menuform.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_mapdisp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_tools.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/preferences.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/profile.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/render.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/rules.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/sqlbuilder.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/utils.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vdigit.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/workspace.py
   grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py
   grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py
   grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py
   grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py
   grass/branches/releasebranch_6_4/gui/wxpython/scripts/Makefile
   grass/branches/releasebranch_6_4/gui/wxpython/support/update_menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/Makefile
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.cpp
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.h
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.cpp
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.h
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/line.cpp
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/pseudodc.i
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/setup.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/vertex.cpp
   grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py
   grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxw.dtd
   grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml
Log:
wxGUI backported from devbr6 (some testing required)


Added: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-fringe.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-fringe.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-light.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-light.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-raster.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-raster.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-vector.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-vector.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-view.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-view.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-volume.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/3d-volume.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/check.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/check.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/data-add.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/data-add.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/execute.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/execute.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/help.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/help.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/image-export.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/image-export.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/layer-add.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/layer-add.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/map-add.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/map-add.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/modeler-main.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/modeler-main.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/modeler-variables.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/modeler-variables.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/module-add.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/module-add.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/move.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/move.png
___________________________________________________________________
Added: svn:mime-type
   + image/png


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/options.png
___________________________________________________________________
Modified: svn:mime-type
   - application/octet-stream
   + image/png


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/overlay-add.png
___________________________________________________________________
Modified: svn:mime-type
   - application/octet-stream
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/python-export.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/python-export.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/redo.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/redo.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: grass/branches/releasebranch_6_4/gui/icons/grass2/relation-create.png
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/icons/grass2/relation-create.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Modified: grass/branches/releasebranch_6_4/gui/wxpython/Makefile
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/Makefile	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/Makefile	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,15 +1,17 @@
 MODULE_TOPDIR = ../..
 
-SUBDIRS = docs scripts 
-EXTRA_CLEAN_FILES = menustrings.py
+SUBDIRS = docs vdigit scripts
+EXTRA_CLEAN_FILES = menustrings.py build_ext.pyc
+CLEAN_SUBDIRS = vdigit scripts
 
 include $(MODULE_TOPDIR)/include/Make/Dir.make
 include $(MODULE_TOPDIR)/include/Make/Doxygen.make
+include $(MODULE_TOPDIR)/include/Make/Python.make
 
 ETCDIR = $(ETC)/wxpython
 
-SRCFILES := $(wildcard scripts/p.* compat/* gui_modules/* icons/*.* icons/silk/* images/* xml/*) gis_set.py wxgui.py README scripts/wxgui
-DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES))
+SRCFILES := $(wildcard compat/* gui_modules/* icons/*.* icons/silk/* images/* xml/*) gis_set.py wxgui.py README
+DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 
 default: install_scripts
 	$(MAKE) parsubdirs
@@ -17,23 +19,20 @@
 clean: cleansubdirs
 
 install_scripts:
-	-for dir in '' compat gui_modules icons icons/silk images scripts xml; do \
+	-for dir in '' compat gui_modules icons icons/silk images vdigit xml; do \
 	if [ ! -d $(ETCDIR)/$$dir ] ; then $(MKDIR) $(ETCDIR)/$$dir ; fi ; \
 	done
 	$(MAKE) $(DSTFILES)
 	$(MAKE) menustrings.py
 
-$(ETCDIR)/scripts/wxgui: wxgui
-	$(INSTALL) $< $@
-
-$(ETCDIR)/scripts/%: scripts/%
-	$(INSTALL) $< $@
-
 $(ETCDIR)/%: %
 	$(INSTALL_DATA) $< $@
 
-menustrings.py: gui_modules/menudata.py xml/menudata.xml
-	python $< $(GISBASE) > $@
+menustrings.py: gui_modules/menudata.py $(ETCDIR)/xml/menudata.xml $(ETCDIR)/xml/menudata_modeler.xml 
+	GISBASE="$(GISBASE)" \
+	$(PYTHON) $< > $@
+	GISBASE="$(GISBASE)" \
+	$(PYTHON) $< "modeler" >> $@
 
 #doxygen:
 DOXNAME=wxpython

Modified: grass/branches/releasebranch_6_4/gui/wxpython/README
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/README	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/README	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,5 +1,5 @@
-wxPython GRASS Graphical User Interface
-=======================================
+wxPython GRASS Graphical User Interface (wxGUI)
+===============================================
 
 $Date$
 
@@ -66,7 +66,7 @@
 
 or easier start new GRASS session with -wxpython switch.
 
-$ grass64 -wxpython
+$ grass65 -wxpython
 
 
 3 - STARTUP FROM GRASS TERMINAL
@@ -98,7 +98,7 @@
 
 Start GRASS:
 
-$~ grass64 ~/grassdata/spearfish60/user1
+$~ grass65 ~/grassdata/spearfish60/user1
 
 Use command p.mon (shell script in gui/scripts directory) to start map
 display:
@@ -138,9 +138,12 @@
 $ export GRASS_WX_DEBUG=3
 
 
-6 - EXTENSIONS - VECTOR DIGITIZER AND 3D VIEWER
+6 - VECTOR DIGITIZER
 
-To enable digitizer you need to compile 'vdigit' or 'nviz'
-component. Note that you must configure GRASS with C++ (--with-cxx),
-Python (--with-python) and wxWidgets (--with-wxwidgets) support.
+To enable digitizer you need to compile 'vdigit' component. Note that
+you must configure GRASS with C++ (--with-cxx), Python (--with-python)
+and wxWidgets (--with-wxwidgets) support.
 
+$ cd gui/wxpython/vdigit
+$ make
+$ make install

Modified: grass/branches/releasebranch_6_4/gui/wxpython/build_ext.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/build_ext.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/build_ext.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -4,7 +4,7 @@
 import sys
 
 def __read_variables(file, dict={}):
-    """Read variables from file (e.g. Platform.make)
+    """!Read variables from file (e.g. Platform.make)
     
     @param file file descriptor
     @param dict dictionary to store (variable, value)
@@ -22,7 +22,7 @@
         dict[var.strip()] = val.strip()
         
 def update_opts(flag, macros, inc_dirs, lib_dirs, libs, extras):
-    """Update Extension options"""
+    """!Update Extension options"""
     global variables
     line = variables[flag]
     fw_next = False

Modified: grass/branches/releasebranch_6_4/gui/wxpython/compat/subprocess.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/compat/subprocess.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/compat/subprocess.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -9,7 +9,7 @@
 # Licensed to PSF under a Contributor Agreement.
 # See http://www.python.org/2.4/license for licensing details.
 
-r"""subprocess - Subprocesses with accessible I/O streams
+r"""!subprocess - Subprocesses with accessible I/O streams
 
 This module allows you to spawn processes, connect to their
 input/output/error pipes, and obtain their return codes.  This module
@@ -402,7 +402,7 @@
 
 
 def call(*args, **kwargs):
-    """Run command with arguments.  Wait for command to complete, then
+    """!Run command with arguments.  Wait for command to complete, then
     return the returncode attribute.
 
     The arguments are the same as for the Popen constructor.  Example:
@@ -486,7 +486,7 @@
                  preexec_fn=None, close_fds=False, shell=False,
                  cwd=None, env=None, universal_newlines=False,
                  startupinfo=None, creationflags=0):
-        """Create new Popen instance."""
+        """!Create new Popen instance."""
         _cleanup()
 
         if not isinstance(bufsize, (int, long)):
@@ -568,7 +568,7 @@
         # Windows methods
         #
         def _get_handles(self, stdin, stdout, stderr):
-            """Construct and return tupel with IO objects:
+            """!Construct and return tupel with IO objects:
             p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
             """
             if stdin == None and stdout == None and stderr == None:
@@ -628,14 +628,14 @@
 
 
         def _make_inheritable(self, handle):
-            """Return a duplicate of handle, which is inheritable"""
+            """!Return a duplicate of handle, which is inheritable"""
             return DuplicateHandle(GetCurrentProcess(), handle,
                                    GetCurrentProcess(), 0, 1,
                                    DUPLICATE_SAME_ACCESS)
 
 
         def _find_w9xpopen(self):
-            """Find and return absolut path to w9xpopen.exe"""
+            """!Find and return absolut path to w9xpopen.exe"""
             w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
                                     "w9xpopen.exe")
             if not os.path.exists(w9xpopen):
@@ -656,7 +656,7 @@
                            p2cread, p2cwrite,
                            c2pread, c2pwrite,
                            errread, errwrite):
-            """Execute program (MS Windows version)"""
+            """!Execute program (MS Windows version)"""
 
             if not isinstance(args, types.StringTypes):
                 args = list2cmdline(args)
@@ -731,7 +731,7 @@
 
 
         def poll(self):
-            """Check if child process has terminated.  Returns returncode
+            """!Check if child process has terminated.  Returns returncode
             attribute."""
             if self.returncode == None:
                 if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
@@ -741,7 +741,7 @@
 
 
         def wait(self):
-            """Wait for child process to terminate.  Returns returncode
+            """!Wait for child process to terminate.  Returns returncode
             attribute."""
             if self.returncode == None:
                 obj = WaitForSingleObject(self._handle, INFINITE)
@@ -755,7 +755,7 @@
 
 
         def communicate(self, input=None):
-            """Interact with process: Send data to stdin.  Read data from
+            """!Interact with process: Send data to stdin.  Read data from
             stdout and stderr, until end-of-file is reached.  Wait for
             process to terminate.  The optional input argument should be a
             string to be sent to the child process, or None, if no data
@@ -812,7 +812,7 @@
         # POSIX methods
         #
         def _get_handles(self, stdin, stdout, stderr):
-            """Construct and return tupel with IO objects:
+            """!Construct and return tupel with IO objects:
             p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
             """
             p2cread, p2cwrite = None, None
@@ -882,7 +882,7 @@
                            p2cread, p2cwrite,
                            c2pread, c2pwrite,
                            errread, errwrite):
-            """Execute program (POSIX version)"""
+            """!Execute program (POSIX version)"""
 
             if isinstance(args, types.StringTypes):
                 args = [args]
@@ -988,7 +988,7 @@
 
 
         def poll(self):
-            """Check if child process has terminated.  Returns returncode
+            """!Check if child process has terminated.  Returns returncode
             attribute."""
             if self.returncode == None:
                 try:
@@ -1001,7 +1001,7 @@
 
 
         def wait(self):
-            """Wait for child process to terminate.  Returns returncode
+            """!Wait for child process to terminate.  Returns returncode
             attribute."""
             if self.returncode == None:
                 pid, sts = os.waitpid(self.pid, 0)
@@ -1010,7 +1010,7 @@
 
 
         def communicate(self, input=None):
-            """Interact with process: Send data to stdin.  Read data from
+            """!Interact with process: Send data to stdin.  Read data from
             stdout and stderr, until end-of-file is reached.  Wait for
             process to terminate.  The optional input argument should be a
             string to be sent to the child process, or None, if no data

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/Makefile
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/Makefile	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/Makefile	2010-10-14 15:39:14 UTC (rev 43911)
@@ -5,6 +5,7 @@
 	wxGUI.Attribute_Table_Manager \
 	wxGUI.Nviz \
 	wxGUI.Icons \
+	wxGUI.Modeler \
 	wxGUI.GCP_Manager
 
 include $(MODULE_TOPDIR)/include/Make/Platform.make

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Attribute_Table_Manager.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Attribute_Table_Manager.html	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Attribute_Table_Manager.html	2010-10-14 15:39:14 UTC (rev 43911)
@@ -36,7 +36,7 @@
 </em>
 
 <p>
-See also <a href="http://grass.osgeo.org/wiki/WxPython-based_GUI_for_GRASS#Attribute_table_manager">Wiki</a> page.
+See also <a href="http://grass.osgeo.org/wiki/WxPython-based_GUI_for_GRASS#Attribute_table_manager">wiki</a> page.
 
 <h2>AUTHORS</h2>
 

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.GCP_Manager.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.GCP_Manager.html	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.GCP_Manager.html	2010-10-14 15:39:14 UTC (rev 43911)
@@ -154,7 +154,7 @@
 the mouse over the map canvas to be used as active canvas.</dd>
 
 <dt><img src="icons/grass2/zoom-last.png">&nbsp;
-  <em>Return to previous zoom</em></dt>
+  <em>Return to pervious zoom</em></dt>
 <dd>Returns to the previous zoom extent. Up to 10 levels of zoom back are
 maintained.</dd>
 

Added: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,39 @@
+<h2>DESCRIPTION</h2>
+
+<b>Note:</b> <em>wxGUI Modeler is currently under development.</em>
+
+<p>
+<b>Graphical modeler</b> is a <em><a href="wxGUI.html">wxGUI</a></em>
+extension which allows the user to create, edit, and manage
+models. It's available from the menu &quot;File | Graphical modeler&quot;.
+
+<p>
+The modeler currently allows to:
+
+<ul>
+  <li>define actions (GRASS modules)
+  <li>define data items (raster, vector, 3D raster)
+  <li>define relations between data and action items
+  <li>validate model
+  <li>run model
+  <li>store model settings to the file (GRASS Model File|*.gxm)
+  <li>export model settings to Python script
+  <li>export model to image file
+</ul>
+
+<h2>SEE ALSO</h2>
+
+<em>
+  <a href="wxGUI.html">wxGUI</a>,
+  <a href="wxGUI.Icons.html">Icon themes</a>
+</em>
+
+<p>
+See also <a href="http://grass.osgeo.org/wiki/WxGUI_Modeler">wiki</a> page.
+
+<h2>AUTHORS</h2>
+
+Martin Landa, CTU in Prague, Czech Republic
+
+<p>
+<i>$Date$</i>


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html
___________________________________________________________________
Added: svn:mime-type
   + text/html
Added: svn:keywords
   + Author Date Id
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,18 +1,21 @@
 <h2>DESCRIPTION</h2>
 
-<b>Note:</b> <em>wxGUI Nviz extension is currently under development. Not
-all functionality is implemented.</em>
+Note: <b>wxNviz is currently under development. Not
+all functionality is implemented.</b>
 
 <p>
-<b>wxGUI Nviz extension</b> allows users to realistically render multiple
+<b>wxNviz</b> is a <em><a href="wxGUI.html">wxGUI</a></em> <b>3D view
+mode</b> which allows users to realistically render multiple
 <em>surfaces</em> (raster data) in a 3D space, optionally using
 thematic coloring, draping 2D <em>vector</em> data over the surfaces,
 displaying 3D vector data in the space, and visualization
 of <em>volume</em> data (3D raster data).
+
 <p>
-To start the wxGUI Nviz extension, choose '3D view' from the map toolbar.
+To start the wxGUI 3D view mode, choose '3D view' from the map
+toolbar.
 <p>
-Nviz is emphasized on the ease and speed of viewer positioning and
+wxNviz is emphasized on the ease and speed of viewer positioning and
 provided flexibility for using a wide range of data. A low resolution
 surface or wire grid (optional) provides real-time viewer positioning
 capabilities. Coarse and fine resolution controls allow the user to
@@ -21,89 +24,76 @@
 vertical dimension.
 
 <p>
-For each session of Nviz, you might want the same set of 2D/3D raster
-and vector data, view parameters, or other attributes. For consistency
-between sessions, you can store this information in
-the <em>workspace</em> file (gxw). Workspace file contains information
-to restore "state" of the system in 2D and if Nviz is enabled also in
+For each session of wxNviz, you might want the same set of 2D/3D
+raster and vector data, view parameters, or other attributes. For
+consistency between sessions, you can store this information in the
+GRASS <em>workspace</em> file (gxw). Workspace contains information to
+restore "state" of the system in 2D and if wxNviz is enabled also in
 the 3D display mode.
 
-<h2>Data visualization</h2>
+<h2>3D View Toolbar</h2>
 
-<h3>Surfaces</h3>
+<center>
+<br><img src="wxGUI_nviz_toolbar.jpg" border="1"><br><br>
+</center>
 
-Each active raster map layer from the current layer tree is displayed
-as surface in the 3D space. Separate raster data or constants can be
-used for various attributes of the surface:
+<dl>
+  <dt><img src="icons/grass2/3d-view.png">&nbsp;
+    <em>Switch to view page</em></dt>
+  <dd>Switch 3D Layer Manager Toolbox's page to the <b>view</b>
+  control page.</dd>
+  <dt><img src="icons/grass2/3d-raster.png">&nbsp;
+    <em>Switch to surface page</em></dt>
+  <dd>Switch 3D Layer Manager Toolbox's page to the <b>surface</b>
+  control page (data properties).</dd>
+  <dt><img src="icons/grass2/3d-vector.png">&nbsp;
+    <em>Switch to vector page</em></dt>
+  <dd>Switch 3D Layer Manager Toolbox's page to the <b>vector</b>
+  control page (data properties).</dd>
+  <dt><img src="icons/grass2/3d-volume.png">&nbsp;
+    <em>Switch to volume page</em></dt>
+  <dd>Switch 3D Layer Manager Toolbox's page to the <b>volume</b>
+  control page (data properties).</dd>
+  <dt><img src="icons/grass2/3d-light.png">&nbsp;
+    <em>Switch to light page</em></dt>
+  <dd>Switch 3D Layer Manager Toolbox's page to the <b>light</b>
+  control page (appearance).</dd>
+  <dt><img src="icons/grass2/3d-fringe.png">&nbsp;
+    <em>Switch to fringe page</em></dt>
+  <dd>Switch 3D Layer Manager Toolbox's page to the <b>fringe</b>
+  control page (appearance).</dd>
+  <dt><img src="icons/grass2/settings.png">&nbsp;
+    <em>Show 3D view mode settings</em></dt>
+  <dd>Show dialog with settings for wxGUI 3D view mode. The user
+  settings can be stored in wxGUI settings file.</dd>
+  <dt><img src="icons/grass2/help.png">&nbsp;
+    <em>Show help</em></dt>
+  <dd>Show this help.</dd>
+  <dt><img src="icons/grass2/quit.png">&nbsp;
+    <em>Quit</em></dt>
+  <dd>Quit 3D view mode and switch map display to the 2D view
+  mode.</dd>
+</dl>
 
-<ul>
-  <li><b>topography</b> - raster map or constant values used as elevation (z
-    values) for the current surface.</li>
-  <li><b>color</b> - raster map or constant color to drape over the current
-    surface. This option is useful for draping imagery such as aerial
-    photography over a DEM.</li>
-  <li><b>mask</b> - raster map that controls the areas displayed from
-    the current surface.</li>
-  <li><b>transparency</b> - raster map or constant value that controls
-    the transparency of the current surface. The default is completely
-    opaque. Range from 0 (opaque) to 255 (transparent).</li>
-  <li><b>shininess</b> - raster map or constant value that controls
-    the shininess (reflectivity) of the current surface. Range from 0 to
-    255.</li>
-  <li><b>emission</b> - raster map or constant value that controls the
-    light emitted from the current surface. Range from 0 to 255.</li>
-</ul>
+<h2>3D View Layer Manager Toolbox</h2>
 
-<h3>Vector data</h3>
+The 3D view toolbox is integrated in the Layer Manager. The toolbox
+has several tabs:
 
-2D vector data can be draped on the selected surfaces with various
-markers to represent point data; you can use attribute of vector
-features to determine size, color, shape of glyph.
-
-3D vector data including volumes (closed group of faces with one
-kernel inside) is also supported.
-
-<h3>Volumes</h3>
-
-Volumes can be displayed either as isosurfaces or slices. Various
-attributes of the isosurface can be defined, similarly to surface
-attributes:
-
 <ul>
-  <li><b>level</b> - reference isosurface level (height in map
-  units).</li>
-  <li><b>color</b> - raster map or constant color to drape over the
-  current volume.</li>
-  <li><b>mask</b> - raster map that controls the areas displayed from
-    the current volume.</li>
-  <li><b>transparency</b> - raster map or constant value that controls
-    the transparency of the current volume. The default is completely
-    opaque. Range from 0 (opaque) to 255 (transparent).</li>
-  <li><b>shininess</b> - raster map or constant value that controls
-    the shininess (reflectivity) of the current volume. Range from 0 to
-    255.</li>
-  <li><b>emission</b> - raster map or constant value that controls the
-    light emitted from the current volume. Range from 0 to 255.</li>
+  <li><b>View</b> for view controling,</li>
+  <li><b>Data</b> for data properties,</li>
+  <li><b>Appearance</b> for appearance settings (lighting, fringes, ...).</li>
 </ul>
 
-<h2>Nviz Toolbox Window</h2>
-
-The toolbox window has currently three tabs:
-
-<ul>
-  <li>View<li>
-  <li>Layer properties<li>
-  <li>Settings</li>
-</ul>
-
 <h3>View</h3>
 
 You can use this panel to set the <em>position, direction, and
   perspective</em> of the view. The position box shows a puck with a
-direction line pointing to the center. The direction line indicates
-the look direction (azimuth). You click and drag the puck to change
-the current eye position. The box annotations are North, South,
-East, and West. You can also set exact position using <em>Look
+  direction line pointing to the center. The direction line indicates
+  the look direction (azimuth). You click and drag the puck to change
+  the current eye position. The box annotations are North, South,
+  East, and West. You can also set exact position using <em>Look
   at</em> choice control.
 
 <center>
@@ -115,20 +105,42 @@
 0 is flat. The scene rotates between -90 and 90 degrees.
 
 <p>
-  You can also adjusts the vertical exaggeration of the surface. As an
-  example, if the easting and northing are in meters and the elevation
-  in feet, a vertical exaggeration of 0.305 would produce a true
-  (unexaggerated) surface.
+You can also adjusts the vertical exaggeration of the surface. As an
+example, if the easting and northing are in meters and the elevation
+in feet, a vertical exaggeration of 0.305 would produce a true
+(unexaggerated) surface.
   
 <p>
-  <em>Reset</em> returns all current settings to their default values.
+<em>Reset</em> returns all current settings to their default values.
 
-<h3>Surface properties</h3>
+<h3>Data properties - Surface</h3>
 
+Each active raster map layer from the current layer tree is displayed
+as surface in the 3D space. Separate raster data or constants can be
+used for various attributes of the surface:
+
+<ul>
+  <li><b>topography</b> - raster map or constant values used as elevation (z
+    values) for the current surface.</li>
+  <li><b>color</b> - raster map or constant color to drape over the current
+    surface. This option is useful for draping imagery such as aerial
+    photography over a DEM.</li>
+  <li><b>mask</b> - raster map that controls the areas displayed from
+    the current surface.</li>
+  <li><b>transparency</b> - raster map or constant value that controls
+    the transparency of the current surface. The default is completely
+    opaque. Range from 0 (opaque) to 255 (transparent).</li>
+  <li><b>shininess</b> - raster map or constant value that controls
+    the shininess (reflectivity) of the current surface. Range from 0 to
+    255.</li>
+  <li><b>emission</b> - raster map or constant value that controls the
+    light emitted from the current surface. Range from 0 to 255.</li>
+</ul>
+
 This panel controls how loaded surfaces are drawn. The top half of the
 panel has options to set, unset or modify attributes of the current
-surface.The bottom half has drawing style options, masking or changing
-surface position in the space.
+surface. The bottom half has drawing style options, masking or
+changing surface position in the space.
 
 <center>
   <br><img src="wxGUI_nviz_tools_surface.jpg" border="1"><br><br>
@@ -139,10 +151,10 @@
 display mode), <em>fine</em> (draws surface as filled polygons with
 fine resolution) or <em>both</em> (which combines coarse and fine
 mode). Additionally set coarse <b>style</b> to <em>wire</em> to draw
-the surface as wire mesh (you can also choose color of the
-wire) or <em>surface</em> to draw the surface using coarse resolution
-filled polygons.  This is a low resolution version of the polygon
-surface style.
+the surface as wire mesh (you can also choose color of the wire)
+or <em>surface</em> to draw the surface using coarse resolution filled
+polygons. This is a low resolution version of the polygon surface
+style.
 
 E.g. surface is drawn as a wire mesh if you set <b>mode</b>
 to <em>coarse</em> and <b>style</b> to <em>wire</em>. Note that it
@@ -158,11 +170,16 @@
 cells. The surface appears faceted.
 
 <p>
-  To set given draw settings for all loaded surfaces press button
-  "All".
+To set given draw settings for all loaded surfaces press button "All".
 
-<h3>Vector properties</h3>
+<h3>Data properties - Vector</h3>
 
+2D vector data can be draped on the selected surfaces with various
+markers to represent point data; you can use attribute of vector
+features to determine size, color, shape of glyph.
+
+3D vector data including volumes (closed group of faces with one
+kernel inside) is also supported.
 This panel controls how loaded 2D or 3D vector data are drawn.
 
 <p>
@@ -190,17 +207,39 @@
 markers:
 
 <ul>
-  <li><b>x</b> sets the current points markers to a 2D "X";</li>
-  <li><b>sphere</b> - solid 3D sphere;</li>
-  <li><b>diamond</b> - solid 3D diamond;</li>
-  <li><b>cube</b> - solid 3D cube;</li>
-  <li><b>box</b> - hollow 3D cube;</li>
-  <li><b>gyroscope</b> - hollow 3D sphere;</li>
-  <li><b>asterisk</b> - 3D line-star;</li>
+  <li><b>x</b> sets the current points markers to a 2D "X",</li>
+  <li><b>sphere</b> - solid 3D sphere,</li>
+  <li><b>diamond</b> - solid 3D diamond,</li>
+  <li><b>cube</b> - solid 3D cube,</li>
+  <li><b>box</b> - hollow 3D cube,</li>
+  <li><b>gyroscope</b> - hollow 3D sphere,</li>
+  <li><b>asterisk</b> - 3D line-star.</li>
 </ul>
  
-<h3>Volume properties</h3>
+<h3>Data properties - Volume</h3>
 
+Volumes can be displayed either as isosurfaces or slices. Various
+attributes of the isosurface can be defined, similarly to surface
+attributes:
+
+<ul>
+  <li><b>level</b> - reference isosurface level (height in map
+  units).</li>
+  <li><b>color</b> - raster map or constant color to drape over the
+  current volume.</li>
+  <li><b>mask</b> - raster map that controls the areas displayed from
+    the current volume.</li>
+  <li><b>transparency</b> - raster map or constant value that controls
+    the transparency of the current volume. The default is completely
+    opaque. Range from 0 (opaque) to 255 (transparent).</li>
+  <li><b>shininess</b> - raster map or constant value that controls
+    the shininess (reflectivity) of the current volume. Range from 0 to
+    255.</li>
+  <li><b>emission</b> - raster map or constant value that controls the
+    light emitted from the current volume. Range from 0 to 255.</li>
+</ul>
+
+<p>
 This panel controls how loaded volumes are drawn. Volume can be drawn
 in two different modes: <b>isosurface</b> or <b>slice</b>. The top
 part of the panel has drawing style options. The middle part has
@@ -221,7 +260,7 @@
 
 <ul>
   <li><b>isosurface</b> - the levels of values for drawing the
-  volume(s) as isosurfaces;</li>
+  volume(s) as isosurfaces,</li>
   <li>and <b>slice</b> - the levels of values for drawing the volume
   as cross-sections.</li>
 </ul>
@@ -238,14 +277,11 @@
 
 <ul>
   <li>Improve intuitive navigation (mouse, fly mode)</li>
-  <li>Interactive lighting controls</li>
   <li>Animation capabilities</li>
-  <li>Data querying and measuring</li>
   <li>Arbitrary cutting planes</li>
   <li>Labels, decoration, etc.</li>
   <li>Scripting capabilities</li>
   <li>Better workspace support (view settings, lighting)
-  <li>Image Dump</li>
   <li>Surface - mask by zero/elevation, more interactive positioning</li>
   <li>Vector points - implement display mode flat/surface for 2D points</li>
   <li>Volume - slice draw mode</li>
@@ -253,12 +289,12 @@
 </ul>
 
 <p>
-<b>Please note that the wxGUI Nviz extension is under active development and
+<b>Please note that wxNviz is under active development and
 distributed as &quot;Experimental Prototype&quot;.</b>
 
 <h2>SEE ALSO</h2>
 
-See also <a href="http://grass.osgeo.org/wiki/WxNVIZ">Wiki</a> page.
+See also <a href="http://grass.osgeo.org/wiki/WxNVIZ">wiki</a> page.
 <br><br>
 
 Command-line module <em><a href="nviz_cmd.html">nviz_cmd</a></em>.
@@ -268,7 +304,11 @@
 
 <h2>AUTHORS</h2>
 
-Martin Landa, Google Summer of Code 2008 (Mentor: Michael Barton)
+Martin
+Landa, <a href="http://grass.osgeo.org/wiki/WxNviz_GSoC_2008">Google
+Summer of Code 2008</a> (mentor: Michael Barton)
+and <a href="http://grass.osgeo.org/wiki/WxNviz_GSoC_2010">Google
+Summer of Code 2010</a> (mentor: Helena Mitasova)
 
 <p>
 <i>$Date$</i>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizing_Tool.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizing_Tool.html	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizing_Tool.html	2010-10-14 15:39:14 UTC (rev 43911)
@@ -229,7 +229,7 @@
 </em>
 
 <p>
-See also <a href="http://grass.osgeo.org/wiki/WxPython-based_GUI_for_GRASS#Vector_digitizer">Wiki</a> page.
+See also <a href="http://grass.osgeo.org/wiki/WxPython-based_GUI_for_GRASS#Vector_digitizer">wiki</a> page.
 
 <p>
 TCL/TK-based <a href="v.digit.html">v.digit</a>.

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html	2010-10-14 15:39:14 UTC (rev 43911)
@@ -8,7 +8,7 @@
 typing at GRASS command line prompt
 
 <div class="code"><pre>
-   g.gui -u wxpython& 
+   g.gui -u wxpython 
 </pre></div>
 
 or define in your <tt>.grassrc6</tt> file 'GRASS_GUI' variable
@@ -35,7 +35,7 @@
 defined by the <tt>-wxpython</tt> switch:
 
 <div class="code"><pre>
-    grass64 -wxpython
+    grass65 -wxpython
 </pre></div>
 
 <p>
@@ -52,6 +52,10 @@
   under different tabs in the Layer Manager.</li>
 </ul>
 
+<p>
+wxGUI comes with more <em>icon themes</em>, for detailed information
+see this <a href="wxGUI.Icons.html">page</a>.
+
 <h3>Layer Manager</h3>
 
 The <em>Layer Manager</em> provides an interactive graphical interface for
@@ -254,6 +258,48 @@
 
 </dl>
 
+<h4>Key shortcuts</h4>
+
+<b>Menu</b>
+<dl>
+  <dt>Ctrl+N</dt>
+  <dd>Create new workspace</dd>
+  <dt>Ctrl+O</dt>
+  <dd>Load workspace from file</dd>
+  <dt>Ctrl+S</dt>
+  <dd>Close workspace</dd>
+  <dt>Ctrl+L</dt>
+  <dd>Load map layers</dd>
+  <dt>Ctrl+W</dt>
+  <dd>Exit GUI</dd>
+  <dt>Ctrl+Q</dt>
+  <dd>Quit GRASS</dd>
+</dl>
+
+<b>Global</b>
+<dl>
+  <dt>Ctrl+Tab</dt>
+  <dd>Switch 'Map layers' and 'Command output' tab</dd>
+  <dt>Ctrl+R</dt>
+  <dd>Add raster map layer</dd>
+  <dt>Ctrl+V</dt>
+  <dd>Add vector map layer</dd>
+</dl>
+
+<b>Command line prompt</b>
+<dl>
+  <dt>Tab</dt>
+  <dd>Show command tooltips</dd>
+  <dt>Esc</dt>
+  <dd>Hide command tooltips</dd>
+  <dt>Ctrl+Space</dt>
+  <dd>Map entries without arguments (as in <tt>r.info [mapname]</tt>)</dd>
+  <dt>Up/Down</dt>
+  <dd>List command history</dd>
+  <dt>Enter</dt>
+  <dd>Run command</dd>
+</dl>
+
 <h3>Map Display Window</h3>
 
 The map display window includes toolbar that can be docked and undocked from 
@@ -466,27 +512,19 @@
   </ul>
 </dl>
 
-<h2>CONFIGURATION</h2>
-
-<em>User GIS settings</em> dialog ('Config->Preferences') enables
-configuration of various options.
-
-<p>
-For information about available <em>icon themes</em> see
-this <a href="wxGUI.Icons.html">page</a>.
-
 <h2>SEE ALSO</h2>
 
 <em>
   <a href="wxGUI.Vector_Digitizing_Tool.html">Vector Digitizer</a><br>
   <a href="wxGUI.Attribute_Table_Manager.html">Attribute Table Manager</a><br>
   <a href="wxGUI.Nviz.html">3D Viewer</a><br>
+  <a href="wxGUI.Modeler.html">Graphical Modeler</a><br>
   <a href="wxGUI.GCP_Manager.html">Manage Ground Control Points</a><br>
   <a href="wxGUI.Icons.html">Icon themes</a>
 </em>
 
 <p>
-See also <a href="http://grass.osgeo.org/wiki/WxGUI">Wiki</a> page.
+See also <a href="http://grass.osgeo.org/wiki/WxGUI">wiki</a> page.
 
 <p>
 <em>

Added: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_toolbar.jpg
===================================================================
(Binary files differ)


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_toolbar.jpg
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_surface.jpg
===================================================================
(Binary files differ)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_vector.jpg
===================================================================
(Binary files differ)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_view.jpg
===================================================================
(Binary files differ)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_volume.jpg
===================================================================
(Binary files differ)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package gis_set.py
 
 GRASS start-up screen.
@@ -8,10 +8,10 @@
 
 Classes:
  - GRASSStartup
- - HelpWindow
+ - GListBox
  - StartUp
 
-(C) 2006-2009 by the GRASS Development Team
+(C) 2006-2010 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.
@@ -29,21 +29,26 @@
 
 ### i18N
 import gettext
-gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
 
 from gui_modules import globalvar
 if not os.getenv("GRASS_WXBUNDLED"):
     globalvar.CheckForWx()
 
+import gui_modules.goutput
+from gui_modules.ghelp import HelpFrame
+from gui_modules.gcmd  import GMessage
+
 import wx
 import wx.html
 import wx.lib.rcsizer as rcs
 import wx.lib.filebrowsebutton as filebrowse
 import wx.lib.mixins.listctrl as listmix
+import wx.lib.scrolledpanel as scrolled
 
 class GRASSStartup(wx.Frame):
-    """GRASS start-up screen"""
-    def __init__(self, parent=None, id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE):
+    """!GRASS start-up screen"""
+    def __init__(self, parent = None, id = wx.ID_ANY, style = wx.DEFAULT_FRAME_STYLE):
 
         #
         # GRASS variables
@@ -59,15 +64,15 @@
         self.listOfMapsets = []
         self.listOfMapsetsSelectable = []
         
-        wx.Frame.__init__(self, parent=parent, id=id, style=style)
+        wx.Frame.__init__(self, parent = parent, id = id, style = style)
         
         self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
-
-        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
-
+        
+        self.panel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        
         # i18N
         import gettext
-        gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+        gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
 
         #
         # graphical elements
@@ -76,88 +81,84 @@
         try:
             name = os.path.join(globalvar.ETCDIR, "gui", "images", "gintro.gif")
             self.hbitmap = wx.StaticBitmap(self.panel, wx.ID_ANY,
-                                           wx.Bitmap(name=name,
-                                                     type=wx.BITMAP_TYPE_GIF))
+                                           wx.Bitmap(name = name,
+                                                     type = wx.BITMAP_TYPE_GIF))
         except:
             self.hbitmap = wx.StaticBitmap(self.panel, wx.ID_ANY, wx.EmptyBitmap(530,150))
 
         # labels
         ### crashes when LOCATION doesn't exist
-        # versionCmd = gcmd.Command(['g.version'], log=None)
-        # grassVersion = versionCmd.ReadStdOutput()[0].replace('GRASS', '').strip()
         versionFile = open(os.path.join(globalvar.ETCDIR, "VERSIONNUMBER"))
         grassVersion = versionFile.readline().replace('%s' % os.linesep, '').strip()
         versionFile.close()
 
-        self.select_box = wx.StaticBox (parent=self.panel, id=wx.ID_ANY,
-                                        label=" %s " % _("Choose project location and mapset"))
+        self.select_box = wx.StaticBox (parent = self.panel, id = wx.ID_ANY,
+                                        label = " %s " % _("Choose project location and mapset"))
 
-        self.manage_box = wx.StaticBox (parent=self.panel, id=wx.ID_ANY,
-                                        label=" %s " % _("Manage"))
-        self.lwelcome = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                      label=_("Welcome to GRASS GIS %s\n"
+        self.manage_box = wx.StaticBox (parent = self.panel, id = wx.ID_ANY,
+                                        label = " %s " % _("Manage"))
+        self.lwelcome = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                      label = _("Welcome to GRASS GIS %s\n"
                                               "The world's leading open source GIS") % grassVersion,
-                                      style=wx.ALIGN_CENTRE)
-        #self.SetFont(wx.Font(pointSize=9, family=wx.FONTFAMILY_DEFAULT,
-        #                     style=wx.NORMAL, weight=wx.NORMAL))
-        self.ltitle = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                    label=_("Select an existing project location and mapset\n"
+                                      style = wx.ALIGN_CENTRE)
+        self.ltitle = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("Select an existing project location and mapset\n"
                                             "or define a new location"),
-                                    style=wx.ALIGN_CENTRE)
-        self.ldbase = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                    label=_("GIS Data Directory:"))
-        self.llocation = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                       label=_("Project location\n(projection/coordinate system)"),
-                                       style=wx.ALIGN_CENTRE)
-        self.lmapset = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                     label=_("Accessible mapsets\n(directories of GIS files)"),
-                                     style=wx.ALIGN_CENTRE)
-        self.lcreate = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                     label=_("Create new mapset\nin selected location"),
-                                     style=wx.ALIGN_CENTRE)
-        self.ldefine = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                     label=_("Define new location"),
-                                     style=wx.ALIGN_CENTRE)
-        self.lmanageloc = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                        label=_("Rename/delete selected\nmapset or location"),
-                                        style=wx.ALIGN_CENTRE)
+                                    style = wx.ALIGN_CENTRE)
+        self.ldbase = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("GIS Data Directory:"))
+        self.llocation = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                       label = _("Project location\n(projection/coordinate system)"),
+                                       style = wx.ALIGN_CENTRE)
+        self.lmapset = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                     label = _("Accessible mapsets\n(directories of GIS files)"),
+                                     style = wx.ALIGN_CENTRE)
+        self.lcreate = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                     label = _("Create new mapset\nin selected location"),
+                                     style = wx.ALIGN_CENTRE)
+        self.ldefine = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                     label = _("Define new location"),
+                                     style = wx.ALIGN_CENTRE)
+        self.lmanageloc = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                        label = _("Rename/delete selected\nmapset or location"),
+                                        style = wx.ALIGN_CENTRE)
 
         # buttons
-        self.bstart = wx.Button(parent=self.panel, id=wx.ID_ANY,
-                                label=_("Start GRASS"))
+        self.bstart = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                label = _("Start &GRASS"))
         self.bstart.SetDefault()
-        self.bexit = wx.Button(parent=self.panel, id=wx.ID_EXIT)
+        self.bexit = wx.Button(parent = self.panel, id = wx.ID_EXIT)
         self.bstart.SetMinSize((180, self.bexit.GetSize()[1]))
-        self.bhelp = wx.Button(parent=self.panel, id=wx.ID_HELP)
-        self.bbrowse = wx.Button(parent=self.panel, id=wx.ID_ANY,
-                                 label=_("Browse"))
-        self.bmapset = wx.Button(parent=self.panel, id=wx.ID_ANY,
-                                 label=_("Create mapset"))
-        self.bwizard = wx.Button(parent=self.panel, id=wx.ID_ANY,
-                                 label=_("Location wizard"))
-        self.manageloc = wx.Choice(parent=self.panel, id=wx.ID_ANY,
-                                   choices=[_('Rename mapset'), _('Rename location'),
+        self.bhelp = wx.Button(parent = self.panel, id = wx.ID_HELP)
+        self.bbrowse = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                 label = _("&Browse"))
+        self.bmapset = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                 label = _("&Create mapset"))
+        self.bwizard = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                 label = _("&Location wizard"))
+        self.manageloc = wx.Choice(parent = self.panel, id = wx.ID_ANY,
+                                   choices = [_('Rename mapset'), _('Rename location'),
                                             _('Delete mapset'), _('Delete location')])
         self.manageloc.SetSelection(0)
 
         # textinputs
-        self.tgisdbase = wx.TextCtrl(parent=self.panel, id=wx.ID_ANY, value="", size=(300, -1),
-                                     style=wx.TE_PROCESS_ENTER)
+        self.tgisdbase = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, value = "", size = (300, -1),
+                                     style = wx.TE_PROCESS_ENTER)
 
         # Locations
-        self.lpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY)
-        self.lblocations = GListBox(parent=self.lpanel,
-                                    id=wx.ID_ANY, size=(180, 200),
-                                    choices=self.listOfLocations)
+        self.lpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
+        self.lblocations = GListBox(parent = self.lpanel,
+                                    id = wx.ID_ANY, size = (180, 200),
+                                    choices = self.listOfLocations)
         
         self.lblocations.SetColumnWidth(0, 180)
 
         # TODO: sort; but keep PERMANENT on top of list
         # Mapsets
-        self.mpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY)
-        self.lbmapsets = GListBox(parent=self.mpanel,
-                                  id=wx.ID_ANY, size=(180, 200),
-                                  choices=self.listOfMapsets)
+        self.mpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
+        self.lbmapsets = GListBox(parent = self.mpanel,
+                                  id = wx.ID_ANY, size = (180, 200),
+                                  choices = self.listOfMapsets)
         
         self.lbmapsets.SetColumnWidth(0, 180)
 
@@ -180,13 +181,13 @@
         self.Bind(wx.EVT_CLOSE,               self.OnCloseWindow)
         
     def _set_properties(self):
-        """Set frame properties"""
+        """!Set frame properties"""
         self.SetTitle(_("Welcome to GRASS GIS"))
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, "grass.ico"),
                              wx.BITMAP_TYPE_ICO))
 
         self.lwelcome.SetForegroundColour(wx.Colour(35, 142, 35))
-        self.lwelcome.SetFont(wx.Font(14, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
+        self.lwelcome.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
 
         self.bstart.SetForegroundColour(wx.Colour(35, 142, 35))
         self.bstart.SetToolTipString(_("Enter GRASS session"))
@@ -207,174 +208,166 @@
             wx.MessageBox(parent = self, caption = _("Error"),
                           message = _("Unable to set GRASS database. "
                                       "Check your locale settings."),
-                          style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                          style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
         
         self.OnSetDatabase(None)
         location = self.GetRCValue("LOCATION_NAME")
         if location == "<UNKNOWN>" or \
                 not os.path.isdir(os.path.join(self.gisdbase, location)):
             location = None
+
         if location:
             # list of locations
             self.UpdateLocations(self.gisdbase)
             try:
                 self.lblocations.SetSelection(self.listOfLocations.index(location),
-                                              force=True)
+                                              force = True)
                 self.lblocations.EnsureVisible(self.listOfLocations.index(location))
             except ValueError:
                 print >> sys.stderr, _("ERROR: Location <%s> not found") % \
-                    (location)
+                    (utils.UnicodeString(location))
                 
             # list of mapsets
-            self.UpdateMapsets(os.path.join(self.gisdbase,location))
+            self.UpdateMapsets(os.path.join(self.gisdbase, location))
             mapset = self.GetRCValue("MAPSET")
             if mapset:
                 try:
                     self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset),
-                                                force=True)
+                                                force = True)
                     self.lbmapsets.EnsureVisible(self.listOfMapsets.index(mapset))
                 except ValueError:
                     self.lbmapsets.Clear()
                     print >> sys.stderr, _("ERROR: Mapset <%s> not found") % \
-                        (mapset)
-
-                # self.bstart.Enable(True)
-
+                        (utils.UnicodeString(mapset))
+            
     def _do_layout(self):
         label_style = wx.ADJUST_MINSIZE | wx.ALIGN_CENTER_HORIZONTAL
 
         sizer           = wx.BoxSizer(wx.VERTICAL)
         dbase_sizer     = wx.BoxSizer(wx.HORIZONTAL)
-        location_sizer  = wx.FlexGridSizer(rows=1, cols=2, vgap=4, hgap=4)
+        location_sizer  = wx.FlexGridSizer(rows = 1, cols = 2, vgap = 4, hgap = 4)
         select_boxsizer = wx.StaticBoxSizer(self.select_box, wx.VERTICAL)
-        select_sizer    = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4)
+        select_sizer    = wx.FlexGridSizer(rows = 2, cols = 2, vgap = 4, hgap = 4)
         manage_boxsizer = wx.StaticBoxSizer(self.manage_box, wx.VERTICAL)
         manage_sizer    = wx.BoxSizer(wx.VERTICAL)
-        btns_sizer    = wx.BoxSizer(wx.HORIZONTAL)
+        btns_sizer      = wx.BoxSizer(wx.HORIZONTAL)
 
         # gis data directory
-        dbase_sizer.Add(item=self.ldbase, proportion=0,
-                        flag=wx.ALIGN_CENTER_VERTICAL |
+        dbase_sizer.Add(item = self.ldbase, proportion = 0,
+                        flag = wx.ALIGN_CENTER_VERTICAL |
                         wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
-                        border=5)
-        dbase_sizer.Add(item=self.tgisdbase, proportion=0,
-                        flag=wx.ALIGN_CENTER_VERTICAL |
+                        border = 3)
+        dbase_sizer.Add(item = self.tgisdbase, proportion = 0,
+                        flag = wx.ALIGN_CENTER_VERTICAL |
                         wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
-                        border=5)
-        dbase_sizer.Add(item=self.bbrowse, proportion=0,
-                        flag=wx.ALIGN_CENTER_VERTICAL |
+                        border = 3)
+        dbase_sizer.Add(item = self.bbrowse, proportion = 0,
+                        flag = wx.ALIGN_CENTER_VERTICAL |
                         wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
-                        border=5)
+                        border = 3)
 
         # select sizer
-        select_sizer.Add(item=self.llocation, proportion=0,
-                         flag=label_style | wx.ALL,
-                         border=5)
-        select_sizer.Add(item=self.lmapset, proportion=0,
-                         flag=label_style | wx.ALL,
-                         border=5)
-        select_sizer.Add(item=self.lpanel, proportion=0,
-                         flag=wx.ADJUST_MINSIZE |
+        select_sizer.Add(item = self.llocation, proportion = 0,
+                         flag = label_style | wx.ALL,
+                         border = 3)
+        select_sizer.Add(item = self.lmapset, proportion = 0,
+                         flag = label_style | wx.ALL,
+                         border = 3)
+        select_sizer.Add(item = self.lpanel, proportion = 0,
+                         flag = wx.ADJUST_MINSIZE |
                          wx.ALIGN_CENTER_VERTICAL |
                          wx.ALIGN_CENTER_HORIZONTAL)
-        select_sizer.Add(item=self.mpanel, proportion=0,
-                         flag=wx.ADJUST_MINSIZE |
+        select_sizer.Add(item = self.mpanel, proportion = 0,
+                         flag = wx.ADJUST_MINSIZE |
                          wx.ALIGN_CENTER_VERTICAL |
                          wx.ALIGN_CENTER_HORIZONTAL)
 
-        select_boxsizer.Add(item=select_sizer, proportion=0)
+        select_boxsizer.Add(item = select_sizer, proportion = 0)
 
         # define new location and mapset
-        manage_sizer.Add(item=self.ldefine, proportion=0,
-                         flag=label_style | wx.ALL,
-                         border=5)
-        manage_sizer.Add(item=self.bwizard, proportion=0,
-                         flag=label_style | wx.BOTTOM,
-                         border=8)
-        manage_sizer.Add(item=self.lcreate, proportion=0,
-                         flag=label_style | wx.ALL,
-                         border=5)
-        manage_sizer.Add(item=self.bmapset, proportion=0,
-                         flag=label_style | wx.BOTTOM,
-                         border=8)
-        manage_sizer.Add(item=self.lmanageloc, proportion=0,
-                         flag=label_style | wx.ALL,
-                         border=5)
-        manage_sizer.Add(item=self.manageloc, proportion=0,
-                         flag=label_style | wx.BOTTOM,
-                         border=8)
+        manage_sizer.Add(item = self.ldefine, proportion = 0,
+                         flag = label_style | wx.ALL,
+                         border = 3)
+        manage_sizer.Add(item = self.bwizard, proportion = 0,
+                         flag = label_style | wx.BOTTOM,
+                         border = 5)
+        manage_sizer.Add(item = self.lcreate, proportion = 0,
+                         flag = label_style | wx.ALL,
+                         border = 3)
+        manage_sizer.Add(item = self.bmapset, proportion = 0,
+                         flag = label_style | wx.BOTTOM,
+                         border = 5)
+        manage_sizer.Add(item = self.lmanageloc, proportion = 0,
+                         flag = label_style | wx.ALL,
+                         border = 3)
+        manage_sizer.Add(item = self.manageloc, proportion = 0,
+                         flag = label_style | wx.BOTTOM,
+                         border = 5)
 
-        manage_boxsizer.Add(item=manage_sizer, proportion=0)
+        manage_boxsizer.Add(item = manage_sizer, proportion = 0)
 
         # location sizer
-        location_sizer.Add(item=select_boxsizer, proportion=0,
-                           flag=wx.ADJUST_MINSIZE |
+        location_sizer.Add(item = select_boxsizer, proportion = 0,
+                           flag = wx.ADJUST_MINSIZE |
                            wx.ALIGN_CENTER_VERTICAL |
                            wx.ALIGN_CENTER_HORIZONTAL |
                            wx.RIGHT | wx.LEFT | wx.EXPAND,
-                           border=5) # GISDBASE setting
-        location_sizer.Add(item=manage_boxsizer, proportion=0,
-                           flag=wx.ADJUST_MINSIZE |
+                           border = 3) # GISDBASE setting
+        location_sizer.Add(item = manage_boxsizer, proportion = 0,
+                           flag = wx.ADJUST_MINSIZE |
                            wx.ALIGN_TOP |
                            wx.ALIGN_CENTER_HORIZONTAL |
                            wx.RIGHT | wx.EXPAND,
-                           border=5)
+                           border = 3)
 
         # buttons
-        btns_sizer.Add(item=self.bstart, proportion=0,
-                       flag=wx.ALIGN_CENTER_HORIZONTAL |
+        btns_sizer.Add(item = self.bstart, proportion = 0,
+                       flag = wx.ALIGN_CENTER_HORIZONTAL |
                        wx.ALIGN_CENTER_VERTICAL |
                        wx.ALL,
-                       border=10)
-        btns_sizer.Add(item=self.bexit, proportion=0,
-                       flag=wx.ALIGN_CENTER_HORIZONTAL |
+                       border = 5)
+        btns_sizer.Add(item = self.bexit, proportion = 0,
+                       flag = wx.ALIGN_CENTER_HORIZONTAL |
                        wx.ALIGN_CENTER_VERTICAL |
                        wx.ALL,
-                       border=10)
-        btns_sizer.Add(item=self.bhelp, proportion=0,
-                       flag=wx.ALIGN_CENTER_HORIZONTAL |
+                       border = 5)
+        btns_sizer.Add(item = self.bhelp, proportion = 0,
+                       flag = wx.ALIGN_CENTER_HORIZONTAL |
                        wx.ALIGN_CENTER_VERTICAL |
                        wx.ALL,
-                       border=10)
+                       border = 5)
 
         # main sizer
-        sizer.Add(item=self.hbitmap, proportion=0,
-                  flag=wx.ADJUST_MINSIZE |
-                  wx.ALIGN_CENTER_VERTICAL |
+        sizer.Add(item = self.hbitmap,
+                  proportion = 0,
+                  flag = wx.ALIGN_CENTER_VERTICAL |
                   wx.ALIGN_CENTER_HORIZONTAL |
                   wx.ALL,
-                  border=5) # image
-        sizer.Add(item=self.lwelcome, # welcome message
-                  proportion=0,
-                  flag= wx.ADJUST_MINSIZE |
-                  wx.ALIGN_CENTER_VERTICAL |
+                  border = 3) # image
+        sizer.Add(item = self.lwelcome, # welcome message
+                  proportion = 0,
+                  flag = wx.ALIGN_CENTER_VERTICAL |
                   wx.ALIGN_CENTER_HORIZONTAL |
                   wx.BOTTOM,
-                  border=10)
-        sizer.Add(item=self.ltitle, # title
-                  proportion=0,
-                  flag=wx.ADJUST_MINSIZE |
-                  wx.ALIGN_CENTER_VERTICAL |
-                  wx.ALIGN_CENTER_HORIZONTAL |
-                  wx.BOTTOM,
-                  border=5)
-        sizer.Add(item=dbase_sizer, proportion=0,
-                  flag=wx.ADJUST_MINSIZE |
-                  wx.ALIGN_CENTER_VERTICAL |
-                  wx.ALIGN_CENTER_HORIZONTAL |
+                  border=1)
+        sizer.Add(item = self.ltitle, # title
+                  proportion = 0,
+                  flag = wx.ALIGN_CENTER_VERTICAL |
+                  wx.ALIGN_CENTER_HORIZONTAL)
+        sizer.Add(item = dbase_sizer, proportion = 0,
+                  flag = wx.ALIGN_CENTER_HORIZONTAL |
                   wx.RIGHT | wx.LEFT,
-                  border=5) # GISDBASE setting
-        sizer.Add(item=location_sizer, proportion=1,
-                  flag=wx.ADJUST_MINSIZE |
-                  wx.ALIGN_CENTER_VERTICAL |
+                  border = 1) # GISDBASE setting
+        sizer.Add(item = location_sizer, proportion = 1,
+                  flag = wx.ALIGN_CENTER_VERTICAL |
                   wx.ALIGN_CENTER_HORIZONTAL |
                   wx.RIGHT | wx.LEFT,
-                  border=5)
-        sizer.Add(item=btns_sizer, proportion=0,
-                  flag=wx.ALIGN_CENTER_VERTICAL |
+                  border = 1)
+        sizer.Add(item = btns_sizer, proportion = 0,
+                  flag = wx.ALIGN_CENTER_VERTICAL |
                   wx.ALIGN_CENTER_HORIZONTAL |
                   wx.RIGHT | wx.LEFT,
-                  border=5)
+                  border = 1)
 
         self.panel.SetAutoLayout(True)
         self.panel.SetSizer(sizer)
@@ -416,7 +409,7 @@
         from gui_modules import location_wizard
         gWizard = location_wizard.LocationWizard(parent = self,
                                                  grassdatabase = self.tgisdbase.GetValue())
-        if gWizard.location != None:
+        if gWizard.location !=  None:
             self.OnSetDatabase(event)
             self.UpdateMapsets(os.path.join(self.gisdbase, gWizard.location))
             self.lblocations.SetSelection(self.listOfLocations.index(gWizard.location))
@@ -426,13 +419,13 @@
         """!Location management choice control handler
         """
         sel = event.GetSelection()
-        if sel == 0:
+        if sel ==  0:
             self.RenameMapset()
-        elif sel == 1:
+        elif sel ==  1:
             self.RenameLocation()
-        elif sel == 2:
+        elif sel ==  2:
             self.DeleteMapset()
-        elif sel == 3:
+        elif sel ==  3:
             self.DeleteLocation()
         
         event.Skip()
@@ -442,29 +435,28 @@
         """
         location = utils.UnicodeString(self.listOfLocations[self.lblocations.GetSelection()])
         mapset   = utils.UnicodeString(self.listOfMapsets[self.lbmapsets.GetSelection()])
-        if mapset == 'PERMANENT':
-            wx.MessageBox(parent = self,
-                          message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
-                                      'This mapset cannot be renamed.'),
-                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+        if mapset ==  'PERMANENT':
+            GMessage(parent = self,
+                     message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
+                                 'This mapset cannot be renamed.'))
             return
         
-        dlg = wx.TextEntryDialog(parent=self,
-                                 message=_('Current name: %s\n\nEnter new name:') % mapset,
-                                 caption=_('Rename selected mapset'))
+        dlg = wx.TextEntryDialog(parent = self,
+                                 message = _('Current name: %s\n\nEnter new name:') % mapset,
+                                 caption = _('Rename selected mapset'))
         
-        if dlg.ShowModal() == wx.ID_OK:
+        if dlg.ShowModal() ==  wx.ID_OK:
             newmapset = dlg.GetValue()
-            if newmapset == mapset:
+            if newmapset ==  mapset:
                 dlg.Destroy()
                 return
             
             if newmapset in self.listOfMapsets:
                 wx.MessageBox(parent = self,
                               caption = _('Message'),
-                              message=_('Unable to rename mapset.\n\n'
+                              message = _('Unable to rename mapset.\n\n'
                                         'Mapset <%s> already exists in location.') % newmapset,
-                              style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+                              style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
             else:
                 try:
                     os.rename(os.path.join(self.gisdbase, location, mapset),
@@ -474,8 +466,8 @@
                 except StandardError, e:
                     wx.MessageBox(parent = self,
                                   caption = _('Error'),
-                                  message=_('Unable to rename mapset.\n\n%s') % e,
-                                  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                                  message = _('Unable to rename mapset.\n\n%s') % e,
+                                  style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
             
         dlg.Destroy()
 
@@ -484,22 +476,22 @@
         """
         location = utils.UnicodeString(self.listOfLocations[self.lblocations.GetSelection()])
 
-        dlg = wx.TextEntryDialog(parent=self,
-                                 message=_('Current name: %s\n\nEnter new name:') % location,
-                                 caption=_('Rename selected location'))
+        dlg = wx.TextEntryDialog(parent = self,
+                                 message = _('Current name: %s\n\nEnter new name:') % location,
+                                 caption = _('Rename selected location'))
 
-        if dlg.ShowModal() == wx.ID_OK:
+        if dlg.ShowModal() ==  wx.ID_OK:
             newlocation = dlg.GetValue()
-            if newlocation == location:
+            if newlocation ==  location:
                 dlg.Destroy()
                 return
 
             if newlocation in self.listOfLocations:
                 wx.MessageBox(parent = self,
                               caption = _('Message'),
-                              message=_('Unable to rename location.\n\n'
+                              message = _('Unable to rename location.\n\n'
                                         'Location <%s> already exists in GRASS database.') % newlocation,
-                              style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+                              style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
             else:
                 try:
                     os.rename(os.path.join(self.gisdbase, location),
@@ -510,8 +502,8 @@
                 except StandardError, e:
                     wx.MessageBox(parent = self,
                                   caption = _('Error'),
-                                  message=_('Unable to rename location.\n\n%s') % e,
-                                  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                                  message = _('Unable to rename location.\n\n%s') % e,
+                                  style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
         
         dlg.Destroy()
 
@@ -520,28 +512,27 @@
         """
         location = self.listOfLocations[self.lblocations.GetSelection()]
         mapset   = self.listOfMapsets[self.lbmapsets.GetSelection()]
-        if mapset == 'PERMANENT':
-            wx.MessageBox(parent = self,
-                          message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
-                                      'This mapset cannot be deleted.'),
-                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+        if mapset ==  'PERMANENT':
+            GMessage(parent = self,
+                     message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
+                                 'This mapset cannot be deleted.'))
             return
         
-        dlg = wx.MessageDialog(parent=self, message=_("Do you want to continue with deleting mapset <%(mapset)s> "
+        dlg = wx.MessageDialog(parent = self, message = _("Do you want to continue with deleting mapset <%(mapset)s> "
                                                       "from location <%(location)s>?\n\n"
                                                       "ALL MAPS included in this mapset will be "
                                                       "PERMANENTLY DELETED!") % {'mapset' : mapset,
                                                                                  'location' : location},
-                               caption=_("Delete selected mapset"),
-                               style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                               caption = _("Delete selected mapset"),
+                               style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
 
-        if dlg.ShowModal() == wx.ID_YES:
+        if dlg.ShowModal() ==  wx.ID_YES:
             try:
                 shutil.rmtree(os.path.join(self.gisdbase, location, mapset))
                 self.OnSelectLocation(None)
                 self.lbmapsets.SetSelection(0)
             except:
-                wx.MessageBox(message=_('Unable to delete mapset'))
+                wx.MessageBox(message = _('Unable to delete mapset'))
 
         dlg.Destroy()
 
@@ -552,14 +543,14 @@
 
         location = self.listOfLocations[self.lblocations.GetSelection()]
 
-        dlg = wx.MessageDialog(parent=self, message=_("Do you want to continue with deleting "
+        dlg = wx.MessageDialog(parent = self, message = _("Do you want to continue with deleting "
                                                       "location <%s>?\n\n"
                                                       "ALL MAPS included in this location will be "
                                                       "PERMANENTLY DELETED!") % (location),
-                               caption=_("Delete selected location"),
-                               style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                               caption = _("Delete selected location"),
+                               style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
 
-        if dlg.ShowModal() == wx.ID_YES:
+        if dlg.ShowModal() ==  wx.ID_YES:
             try:
                 shutil.rmtree(os.path.join(self.gisdbase, location))
                 self.UpdateLocations(self.gisdbase)
@@ -567,29 +558,20 @@
                 self.OnSelectLocation(None)
                 self.lbmapsets.SetSelection(0)
             except:
-                wx.MessageBox(message=_('Unable to delete location'))
+                wx.MessageBox(message = _('Unable to delete location'))
 
         dlg.Destroy()
 
     def UpdateLocations(self, dbase):
-        """Update list of locations"""
-        self.listOfLocations = []
-
+        """!Update list of locations"""
         try:
-            for location in glob.glob(os.path.join(dbase, "*")):
-                try:
-                    if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
-                        self.listOfLocations.append(utils.EncodeString(os.path.basename(location)))
-                except:
-                    pass
-        except UnicodeError:
+            self.listOfLocations = utils.GetListOfLocations(dbase)
+        except UnicodeEncodeError:
             wx.MessageBox(parent = self, caption = _("Error"),
                           message = _("Unable to set GRASS database. "
                                       "Check your locale settings."),
-                          style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                          style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
         
-        utils.ListSortLower(self.listOfLocations)
-
         self.lblocations.Clear()
         self.lblocations.InsertItems(self.listOfLocations, 0)
 
@@ -601,62 +583,60 @@
         return self.listOfLocations
 
     def UpdateMapsets(self, location):
-        """Update list of mapsets"""
+        """!Update list of mapsets"""
         self.FormerMapsetSelection = wx.NOT_FOUND # for non-selectable item
 
-        self.listOfMapsets = []
-        self.listOfMapsetsSelectable = []
-        
-        for mapset in glob.glob(os.path.join(self.gisdbase, location, "*")):
-            if os.path.isdir(mapset) and \
-                    os.path.isfile(os.path.join(self.gisdbase, location, mapset, "WIND")) and \
-                    os.path.basename(mapset) != 'PERMANENT':
-                self.listOfMapsets.append(utils.EncodeString(os.path.basename(mapset)))
-        
-        utils.ListSortLower(self.listOfMapsets)
-        self.listOfMapsets.insert(0, 'PERMANENT')
- 
+        self.listOfMapsetsSelectable = list()
+        self.listOfMapsets = utils.GetListOfMapsets(self.gisdbase, location)
+         
         self.lbmapsets.Clear()
 
         # disable mapset with denied permission
         locationName = os.path.basename(location)
 
         try:
-            mapsets = gcmd.Command(['g.mapset',
-                                    '-l',
-                                    'location=%s' % locationName,
-                                    'gisdbase=%s' % self.gisdbase],
-                                   stderr=None)
+            ret = gcmd.RunCommand('g.mapset',
+                                  read = True,
+                                  flags = 'l',
+                                  location = locationName,
+                                  gisdbase = self.gisdbase)
             
-            for line in mapsets.ReadStdOutput():
-                self.listOfMapsetsSelectable += line.split(' ')
-        except gcmd.CmdError:
-            gcmd.Command(["g.gisenv",
-                          "set=GISDBASE=%s" % self.gisdbase])
-            gcmd.Command(["g.gisenv",
-                          "set=LOCATION_NAME=%s" % locationName])
-            gcmd.Command(["g.gisenv",
-                          "set=MAPSET=PERMANENT"])
+            if not ret:
+                raise gcmd.CmdError("")
+            
+            for line in ret.splitlines():
+                self.listOfMapsetsSelectable +=  line.split(' ')
+        except:
+            gcmd.RunCommand("g.gisenv",
+                            set =  "GISDBASE = %s" % self.gisdbase)
+            gcmd.RunCommand("g.gisenv",
+                            set = "LOCATION_NAME = %s" % locationName)
+            gcmd.RunCommand("g.gisenv",
+                            set = "MAPSET = PERMANENT")
             # first run only
             self.listOfMapsetsSelectable = copy.copy(self.listOfMapsets)
         
         disabled = []
         idx = 0
         for mapset in self.listOfMapsets:
-            if mapset not in self.listOfMapsetsSelectable:
+            mapset = utils.UnicodeString(mapset)
+            if mapset not in self.listOfMapsetsSelectable or \
+                    os.path.isfile(os.path.join(self.gisdbase,
+                                                locationName,
+                                                mapset, ".gislock")):
                 disabled.append(idx)
-            idx += 1
-
-        self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled=disabled)
+            idx +=  1
         
+        self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled = disabled)
+        
         return self.listOfMapsets
 
     def OnSelectLocation(self, event):
-        """Location selected"""
+        """!Location selected"""
         if event:
             self.lblocations.SetSelection(event.GetIndex())
             
-        if self.lblocations.GetSelection() != wx.NOT_FOUND:
+        if self.lblocations.GetSelection() !=  wx.NOT_FOUND:
             self.UpdateMapsets(os.path.join(self.gisdbase,
                                             self.listOfLocations[self.lblocations.GetSelection()]))
         else:
@@ -670,12 +650,15 @@
             locationName = ''
         
         for mapset in self.listOfMapsets:
-            if mapset not in self.listOfMapsetsSelectable:
+            if mapset not in self.listOfMapsetsSelectable or \
+                    os.path.isfile(os.path.join(self.gisdbase,
+                                                locationName,
+                                                mapset, ".gislock")):
                 disabled.append(idx)
-            idx += 1
+            idx +=  1
 
         self.lbmapsets.Clear()
-        self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled=disabled)
+        self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled = disabled)
 
         if len(self.listOfMapsets) > 0:
             self.lbmapsets.SetSelection(0)
@@ -691,7 +674,7 @@
             self.manageloc.Enable(False)
         
     def OnSelectMapset(self, event):
-        """Mapset selected"""
+        """!Mapset selected"""
         self.lbmapsets.SetSelection(event.GetIndex())
 
         if event.GetText() not in self.listOfMapsetsSelectable:
@@ -701,7 +684,7 @@
             event.Skip()
 
     def OnSetDatabase(self, event):
-        """Database set"""
+        """!Database set"""
         self.gisdbase = self.tgisdbase.GetValue()
         
         self.UpdateLocations(self.gisdbase)
@@ -713,8 +696,8 @@
         grassdata = None
 
         dlg = wx.DirDialog(self, _("Choose GIS Data Directory:"),
-                           style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
-        if dlg.ShowModal() == wx.ID_OK:
+                           style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
+        if dlg.ShowModal() ==  wx.ID_OK:
             self.gisdbase = dlg.GetPath()
             self.tgisdbase.SetValue(self.gisdbase)
             self.OnSetDatabase(event)
@@ -722,15 +705,15 @@
         dlg.Destroy()
 
     def OnCreateMapset(self,event):
-        """Create new mapset"""
+        """!Create new mapset"""
         self.gisdbase = self.tgisdbase.GetValue()
         location = self.listOfLocations[self.lblocations.GetSelection()]
 
-        dlg = wx.TextEntryDialog(parent=self,
-                                 message=_('Enter name for new mapset:'),
-                                 caption=_('Create new mapset'))
+        dlg = wx.TextEntryDialog(parent = self,
+                                 message = _('Enter name for new mapset:'),
+                                 caption = _('Create new mapset'))
 
-        if dlg.ShowModal() == wx.ID_OK:
+        if dlg.ShowModal() ==  wx.ID_OK:
             mapset = dlg.GetValue()
             try:
                 os.mkdir(os.path.join(self.gisdbase, location, mapset))
@@ -741,23 +724,28 @@
                 self.OnSelectLocation(None)
                 self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset))
             except StandardError, e:
-                dlg = wx.MessageDialog(parent=self, message=_("Unable to create new mapset: %s") % e,
-                                       caption=_("Error"), style=wx.OK | wx.ICON_ERROR)
+                dlg = wx.MessageDialog(parent = self, message = _("Unable to create new mapset: %s") % e,
+                                       caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
                 dlg.ShowModal()
                 dlg.Destroy()
                 return False
-
+        
+        self.bstart.SetFocus()
+        
         return True
 
     def OnStart(self, event):
         """'Start GRASS' button clicked"""
-        gcmd.Command(["g.gisenv",
-                      "set=GISDBASE=%s" % self.tgisdbase.GetValue()])
-        gcmd.Command(["g.gisenv",
-                      "set=LOCATION_NAME=%s" % self.listOfLocations[self.lblocations.GetSelection()]])
-        gcmd.Command(["g.gisenv",
-                      "set=MAPSET=%s" % self.listOfMapsets[self.lbmapsets.GetSelection()]])
-
+        gcmd.RunCommand("g.gisenv",
+                        set = "GISDBASE = %s" % \
+                            self.tgisdbase.GetValue())
+        gcmd.RunCommand("g.gisenv",
+                        set = "LOCATION_NAME = %s" % \
+                            self.listOfLocations[self.lblocations.GetSelection()])
+        gcmd.RunCommand("g.gisenv",
+                        set = "MAPSET = %s" % \
+                            self.listOfMapsets[self.lbmapsets.GetSelection()])
+        
         self.Destroy()
         sys.exit(0)
 
@@ -769,68 +757,41 @@
     def OnHelp(self, event):
         """'Help' button clicked"""
         # help text in lib/init/helptext.html
-        file=os.path.join(self.gisbase, "docs", "html", "helptext.html")
+        file = os.path.join(self.gisbase, "docs", "html", "helptext.html")
 
-        helpFrame = HelpWindow(parent=self, id=wx.ID_ANY,
-                               title=_("GRASS Quickstart"),
-                               size=(640, 480),
-                               file=file)
+        helpFrame = HelpFrame(parent = self, id = wx.ID_ANY,
+                              title = _("GRASS Quickstart"),
+                              size = (640, 480),
+                              file = file)
         helpFrame.Show(True)
 
         event.Skip()
 
     def OnCloseWindow(self, event):
-        """Close window event"""
+        """!Close window event"""
         event.Skip()
         sys.exit(2)
 
-class HelpWindow(wx.Frame):
-    """GRASS Quickstart help window"""
-    def __init__(self, parent, id, title, size, file):
-
-        wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
-
-        sizer = wx.BoxSizer(wx.VERTICAL)
-
-        # text
-        helpFrame = wx.html.HtmlWindow(parent=self, id=wx.ID_ANY)
-        helpFrame.SetStandardFonts (size = 10)
-        helpFrame.SetBorders(10)
-        wx.InitAllImageHandlers()
-
-        helpFrame.LoadFile(file)
-        self.Ok = True
-
-        sizer.Add(item=helpFrame, proportion=1, flag=wx.EXPAND)
-
-        self.SetAutoLayout(True)
-        self.SetSizer(sizer)
-        #        sizer.Fit(self)
-        #        sizer.SetSizeHints(self)
-        self.Layout()
-
 class GListBox(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
-    """Use wx.ListCtrl instead of wx.ListBox, different style for
+    """!Use wx.ListCtrl instead of wx.ListBox, different style for
     non-selectable items (e.g. mapsets with denied permission)"""
     def __init__(self, parent, id, size,
-                 choices, disabled=[]):
-
-        wx.ListCtrl.__init__(self, parent, id, size=size,
-                             style=wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL |
+                 choices, disabled = []):
+        wx.ListCtrl.__init__(self, parent, id, size = size,
+                             style = wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL |
                              wx.BORDER_SUNKEN)
-
+        
         listmix.ListCtrlAutoWidthMixin.__init__(self)
         
         self.InsertColumn(0, '')
-
+        
         self.selected = wx.NOT_FOUND
         
-        self.__LoadData(choices, disabled)
+        self._LoadData(choices, disabled)
         
-    def __LoadData(self, choices, disabled=[]):
-        """
-        Load data into list
-
+    def _LoadData(self, choices, disabled = []):
+        """!Load data into list
+        
         @param choices list of item
         @param disabled list of indeces of non-selectable items
         """
@@ -838,31 +799,30 @@
         for item in choices:
             index = self.InsertStringItem(sys.maxint, item)
             self.SetStringItem(index, 0, item)
+            
             if idx in disabled:
                 self.SetItemTextColour(idx, wx.Colour(150, 150, 150))
-            idx += 1
-         
-        #self.SetColumnWidth(0, wx.LIST_AUTOSIZE)   
-
+            idx +=  1
+        
     def Clear(self):
         self.DeleteAllItems()
-
-    def InsertItems(self, choices, pos, disabled=[]):
-        self.__LoadData(choices, disabled)
-
+        
+    def InsertItems(self, choices, pos, disabled = []):
+        self._LoadData(choices, disabled)
+        
     def SetSelection(self, item, force = False):
-        if item != wx.NOT_FOUND and \
-                (platform.system() != 'Windows' or force):
+        if item !=  wx.NOT_FOUND and \
+                (platform.system() !=  'Windows' or force):
             ### Windows -> FIXME
             self.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
-	
+        
         self.selected = item
         
     def GetSelection(self):
         return self.selected
         
 class StartUp(wx.App):
-    """Start-up application"""
+    """!Start-up application"""
 
     def OnInit(self):
         wx.InitAllImageHandlers()
@@ -871,26 +831,26 @@
         self.SetTopWindow(StartUp)
         StartUp.Show()
         
-        if StartUp.GetRCValue("LOCATION_NAME") == "<UNKNOWN>":
-            wx.MessageBox(parent=StartUp,
-                          caption=_('Starting GRASS for the first time'),
-                          message=_('GRASS needs a directory in which to store its data. '
+        if StartUp.GetRCValue("LOCATION_NAME") ==  "<UNKNOWN>":
+            wx.MessageBox(parent = StartUp,
+                          caption = _('Starting GRASS for the first time'),
+                          message = _('GRASS needs a directory in which to store its data. '
                                     'Create one now if you have not already done so. '
                                     'A popular choice is "grassdata", located in '
                                     'your home directory.'),
-                          style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                          style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
             
             StartUp.OnBrowse(None)
         
         return 1
 
-if __name__ == "__main__":
+if __name__ ==  "__main__":
 
     if os.getenv("GISBASE") is None:
         print >> sys.stderr, "Failed to start GUI, GRASS GIS is not running."
     else:
         import gettext
-        gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+        gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
 
         import gui_modules.gcmd as gcmd
         import gui_modules.utils as utils

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/__init__.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/__init__.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/__init__.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,30 +1,43 @@
-all = [ "dbm",
-        "debug",
-        "disp_print",
-        "gcmd",
-        "georect",
-        "gcpmanager",
-        "gcpmapdisp",
-        "globalvar",
-        "grassenv",
-        "gselect",
-        "goutput",
-        "histogram",
-        "location_wizard",
-        "mapdisp",
-        "menudata",
-        "menuform",
-        "nviz",
-        "preferences",
-        "profile",
-        "psmap",
-        "render",
-        "rules",
-        "sqlbuilder",
-        "toolbars",
-        "toolbox",
-        "track",
-        "utils",
-        "vdigit",
-        "workspace",
-        "wxgui_utils" ]
+all = [
+    "colorrules.py",
+    "dbm.py",
+    "dbm_base.py",
+    "dbm_dialogs.py",
+    "debug.py",
+    "disp_print.py",
+    "gcmd.py",
+    "gcpmanager.py",
+    "gcpmapdisp.py",
+    "gdialogs.py",
+    "georect.py",
+    "ghelp.py"
+    "globalvar.py",
+    "goutput.py",
+    "gselect.py",
+    "histogram.py",
+    "layertree.py",
+    "location_wizard.py",
+    "mapdisp_command.py",
+    "mapdisp_window.py",
+    "mapdisp.py",
+    "mcalc_builder.py",
+    "menu.py",
+    "menudata.py",
+    "menuform.py",
+    "nviz_mapdisp.py",
+    "nviz_preferences.py",
+    "nviz_tools.py",
+    "ogc_services.py",
+    "preferences.py",
+    "profile.py",
+    "render.py",
+    "rules.py",
+    "sqlbuilder.py",
+    "toolbars.py",
+    "units.py",
+    "utils.py",
+    "vclean.py",
+    "vdigit.py",
+    "workspace.py",
+    "wxnviz.py",
+]

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/colorrules.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/colorrules.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/colorrules.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,14 +1,14 @@
 """
 @package colorrules.py
 
- at brief Dialog for interactive management of raster color tables and vector
-rgb_column
+ at brief Dialog for interactive management of raster color tables and
+vector rgb_column
 
 Classes:
  - ColorTable
  - BuferedWindow
 
-(C) 2008 by the GRASS Development Team
+(C) 2008, 2010 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.
 
@@ -36,24 +36,21 @@
 from preferences import globalSettings as UserSettings
 
 class ColorTable(wx.Frame):
-    def __init__(self, parent, id=wx.ID_ANY, title='',
-                 pos=wx.DefaultPosition, size=(-1, -1),
+    def __init__(self, parent, cmd, id=wx.ID_ANY, title = _("Set color table"),
                  style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
                  **kwargs):
-        wx.Frame.__init__(self, parent, id, title, pos, size, style)
-        """
-        Dialog for interactively entering rules for map management
+        """!Dialog for interactively entering rules for map management
         commands
 
         @param cmd command (given as list)
         """
         self.parent = parent # GMFrame
-  
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        self.cmd    = cmd
         
-        # grass command
-        self.cmd = kwargs['cmd']
+        wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
         
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
         # input map to change
         self.inmap = ''
         
@@ -98,14 +95,7 @@
             self.SetTitle(_('Create new color table for vector map'))
             self.elem = 'vector'
             crlabel = _('Enter vector attribute values or ranges (n or n1 to n2)')
-
-        #
-        # Set the size & cursor
-        #
-        self.SetClientSize(size)
-
-        ### self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
-
+        
         # top controls
         if self.cmd == 'r.colors':
             maplabel = _('Select raster map:')
@@ -195,7 +185,8 @@
         
         # layout
         self.__doLayout()
-
+        self.SetMinSize(self.GetSize())
+        
         self.CentreOnScreen()
         self.Show()
         
@@ -220,7 +211,6 @@
         # body & preview
         #
         bodySizer =  wx.GridBagSizer(hgap=5, vgap=5)
-
         row = 0
         bodySizer.Add(item=self.cr_label, pos=(row, 0), span=(1, 3),
                       flag=wx.ALL, border=5)
@@ -247,6 +237,7 @@
         
         bodySizer.Add(item=self.preview, pos=(row, 2),
                       flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10)
+        bodySizer.AddGrowableRow(row)
         bodySizer.AddGrowableCol(2)
         
         row += 1
@@ -270,7 +261,7 @@
         
         sizer.Add(item=bodySizer, proportion=1,
                   flag=wx.ALL | wx.EXPAND, border=5)
-
+        
         sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY,
                                      style=wx.LI_HORIZONTAL),
                   proportion=0,
@@ -296,12 +287,12 @@
         return cr_panel        
 
     def OnAddRules(self, event):
-        """Add rules button pressed"""
+        """!Add rules button pressed"""
         nrules = self.numRules.GetValue()
         self.AddRules(nrules)
         
     def AddRules(self, nrules):
-        """Add rules"""
+        """!Add rules"""
         snum = len(self.ruleslines.keys())
         for num in range(snum, snum+nrules):
             # enable
@@ -330,29 +321,21 @@
         self.cr_panel.SetupScrolling()
         
     def InitDisplay(self):
+        """!Initialize preview display, set dimensions and region
         """
-        Initialize preview display, set dimensions and region
-        """
-        #self.width, self.height = self.GetClientSize()
         self.width = self.Map.width = 400
         self.height = self.Map.height = 300
         self.Map.geom = self.width, self.height
 
     def OnErase(self, event):
+        """!Erase the histogram display
         """
-        Erase the histogram display
-        """
         self.PreviewWindow.Draw(self.HistWindow.pdc, pdctype='clear')
 
     def OnCloseWindow(self, event):
-        """
-        Window closed
+        """!Window closed
         Also remove associated rendered images
         """
-        #try:
-        #    self.propwin.Close(True)
-        #except:
-        #    pass
         self.Map.Clean()
         self.Destroy()
         
@@ -367,19 +350,19 @@
             return
         
         if self.elem == 'cell':
-            cmdlist = ['r.info',
-                       '-r',
-                       'map=%s' % self.inmap]
+            info = gcmd.RunCommand('r.info',
+                                   parent = self,
+                                   read = True,
+                                   flags = 'r',
+                                   map = self.inmap)
 
-            try:
-                p = gcmd.Command(cmdlist)
-
-                for line in p.ReadStdOutput():
+            if info:
+                for line in info.splitlines():
                     if 'min' in line:
                         self.rast['min'] = float(line.split('=')[1])
                     elif 'max' in line:
                         self.rast['max'] = float(line.split('=')[1])
-            except gcmd.CmdError:
+            else:
                 self.inmap = ''
                 self.rast['min'] = self.rast['max'] = None
                 self.btnPreview.Enable(False)
@@ -422,7 +405,7 @@
         self.vect['rgb'] = event.GetString()
         
     def OnRuleEnable(self, event):
-        """Rule enabled/disabled"""
+        """!Rule enabled/disabled"""
         id = event.GetId()
         
         if event.IsChecked():
@@ -439,7 +422,7 @@
             del self.ruleslines[id]
         
     def OnRuleValue(self, event):
-        """Rule value changed"""
+        """!Rule value changed"""
         num = event.GetId()
         vals = event.GetString().strip()
 
@@ -477,7 +460,7 @@
                     return
         
     def OnRuleColor(self, event):
-        """Rule color changed"""
+        """!Rule color changed"""
         num = event.GetId()
         
         rgba_color = event.GetValue()
@@ -503,7 +486,10 @@
         
     def OnApply(self, event):
         self.CreateColorTable()
-    
+        display = self.parent.GetLayerTree().GetMapDisplay()
+        if display:
+            display.GetWindow().UpdateMap(render = True)
+        
     def OnOK(self, event):
         self.OnApply(event)
         self.Destroy()
@@ -512,7 +498,7 @@
         self.Destroy()
         
     def OnPreview(self, event):
-        """Update preview"""
+        """!Update preview"""
         # raster
         if self.elem == 'cell':
             cmdlist = ['d.rast',
@@ -557,18 +543,20 @@
                 shutil.copyfile(colrtemp, old_colrtable)
                 os.remove(colrtemp)
             else:
-                gcmd.Command(['r.colors',
-                              '-r',
-                              'map=%s' % self.inmap])
+                gcmd.RunCommand('r.colors',
+                                parent = self,
+                                flags = 'r',
+                                map = self.inmap)
         
     def OnHelp(self, event):
-        """Show GRASS manual page"""
-        gcmd.Command(['g.manual',
-                      '--quiet', 
-                      '%s' % self.cmd])
-    
+        """!Show GRASS manual page"""
+        gcmd.RunCommand('g.manual',
+                        quiet = True,
+                        parent = self,
+                        entry = self.cmd)
+        
     def CreateColorTable(self, force=False):
-        """Creates color table"""
+        """!Creates color table"""
         rulestxt = ''
         
         for rule in self.ruleslines.itervalues():
@@ -593,22 +581,25 @@
             output.close()
         
         if self.elem == 'cell': 
-            cmdlist = ['r.colors',
-                       'map=%s' % self.inmap,
-                       'rules=%s' % gtemp]
-            
             if not force and \
                     not self.ovrwrtcheck.IsChecked():
-                cmdlist.append('-w')
+                flags = 'w'
+            else:
+                flags = ''
         
+            gcmd.RunCommand('r.colors',
+                            parent = self,
+                            flags = flags,
+                            map = self.inmap,
+                            rules = gtemp)
+            
         elif self.elem == 'vector':
-            cmdlist = ['db.execute',
-                       'input=%s' % gtemp]
+            gcmd.RunCommand('db.execute',
+                            parent = self,
+                            input = gtemp)
         
-        p = gcmd.Command(cmdlist)
-        
 class BufferedWindow(wx.Window):
-    """A Buffered window class"""
+    """!A Buffered window class"""
     def __init__(self, parent, id,
                  pos = wx.DefaultPosition,
                  size = wx.DefaultSize,
@@ -649,7 +640,7 @@
         self.Map.SetRegion()
 
     def Draw(self, pdc, img=None, pdctype='image'):
-        """Draws preview or clears window"""
+        """!Draws preview or clears window"""
         pdc.BeginDrawing()
 
         Debug.msg (3, "BufferedWindow.Draw(): pdctype=%s" % (pdctype))
@@ -673,7 +664,7 @@
         self.Refresh()
 
     def OnPaint(self, event):
-        """Draw pseudo DC to buffer"""
+        """!Draw pseudo DC to buffer"""
         self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
         dc = wx.BufferedPaintDC(self, self._Buffer)
         
@@ -694,7 +685,7 @@
         self.pdc.DrawToDCClipped(dc, r)
         
     def OnSize(self, event):
-        """Init image size to match window size"""
+        """!Init image size to match window size"""
         # set size of the input image
         self.Map.width, self.Map.height = self.GetClientSize()
 
@@ -716,7 +707,7 @@
         self.resize = True
 
     def OnIdle(self, event):
-        """Only re-render a preview image from GRASS during
+        """!Only re-render a preview image from GRASS during
         idle time instead of multiple times during resizing.
         """
         if self.resize:
@@ -725,7 +716,7 @@
         event.Skip()
 
     def GetImage(self):
-        """Converts files to wx.Image"""
+        """!Converts files to wx.Image"""
         if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
                 os.path.getsize(self.Map.mapfile):
             img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
@@ -735,7 +726,7 @@
         return img
     
     def UpdatePreview(self, img=None):
-        """Update canvas if window changes geometry"""
+        """!Update canvas if window changes geometry"""
         Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render))
         oldfont = ""
         oldencoding = ""
@@ -762,6 +753,6 @@
         self.resize = False
         
     def EraseMap(self):
-        """Erase preview"""
+        """!Erase preview"""
         self.Draw(self.pdc, pdctype='clear')
     


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/colorrules.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -17,9 +17,6 @@
  - Log
  - VirtualAttributeList
  - AttributeManager
- - DisplayAttributesDialog
- - VectorDBInfo
- - ModifyTableRecord
 
 (C) 2007-2009 by the GRASS Development Team
 
@@ -36,6 +33,7 @@
 import locale
 import tempfile
 import copy
+import types
 
 ### i18N
 import gettext
@@ -48,58 +46,18 @@
 import wx
 import wx.lib.mixins.listctrl as listmix
 import wx.lib.flatnotebook as FN
-import wx.lib.scrolledpanel as scrolled
 
 import grass.script as grass
 
 import sqlbuilder
-import grassenv
 import gcmd
 import utils
 import gdialogs
-import gselect
-from debug import Debug as Debug
+import dbm_base
+from debug import Debug
+from dbm_dialogs import ModifyTableRecord
 from preferences import globalSettings as UserSettings
 
-def unicodeValue(value):
-    """Encode value"""
-    enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
-    if enc:
-        value = unicode(value, enc)
-    elif os.environ.has_key('GRASS_DB_ENCODING'):
-        value = unicode(value, os.environ['GRASS_DB_ENCODING'])
-    else:
-        try:
-            value = unicode(value, 'ascii')
-        except UnicodeDecodeError:
-            value = _("Unable to decode value. Set encoding in GUI preferences ('Attributes').")
-    
-    return value
-
-def createDbInfoDesc(panel, mapDBInfo, layer):
-    """!Create database connection information content"""
-    infoFlexSizer = wx.FlexGridSizer (cols=2, hgap=1, vgap=1)
-    infoFlexSizer.AddGrowableCol(1)
-    
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label="Driver:"))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=mapDBInfo.layers[layer]['driver']))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label="Database:"))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=mapDBInfo.layers[layer]['database']))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label="Table:"))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=mapDBInfo.layers[layer]['table']))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label="Key:"))
-    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=mapDBInfo.layers[layer]['key']))
-    
-    return infoFlexSizer
-
 class Log:
     """
     The log output is redirected to the status bar of the containing frame.
@@ -108,7 +66,7 @@
         self.parent = parent
 
     def write(self, text_string):
-        """Update status bar"""
+        """!Update status bar"""
         self.parent.SetStatusText(text_string.strip())
 
 
@@ -135,8 +93,9 @@
         
         try:
             keyColumn = self.LoadData(layer)
-        except gcmd.DBMError, e:
-            e.Show()
+        except gcmd.GException, e:
+            GError(parent = self,
+                   message = e)
             return
         
         #
@@ -166,19 +125,21 @@
         # events
         self.Bind(wx.EVT_LIST_ITEM_SELECTED,   self.OnItemSelected)
         self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
-        self.Bind(wx.EVT_LIST_COL_CLICK,       self.OnColumnClick)     # sorting
+        self.Bind(wx.EVT_LIST_COL_CLICK,       self.OnColumnSort)     
+        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)     
         
     def Update(self, mapDBInfo):
-        """Update list according new mapDBInfo description"""
+        """!Update list according new mapDBInfo description"""
         self.mapDBInfo = mapDBInfo
         self.LoadData(self.layer)
 
-    def LoadData(self, layer, columns=None, where=None):
-        """Load data into list
+    def LoadData(self, layer, columns=None, where=None, sql=None):
+        """!Load data into list
 
         @param layer layer number
-        @param columns list of columns for output
-        @param where where statement
+        @param columns list of columns for output (-> v.db.select)
+        @param where where statement (-> v.db.select)
+        @param sql full sql statement (-> db.select)
         
         @return id of key column 
         @return -1 if key column is not displayed
@@ -190,21 +151,14 @@
         try:
             self.columns = self.mapDBInfo.tables[tableName]
         except KeyError:
-            raise gcmd.DBMError(message=_("Attribute table <%s> not found. "
-                                          "For creating the table switch to "
-                                          "'Manage layers' tab.") % tableName,
-                                parent=self.parent)
+            raise gcmd.GException(_("Attribute table <%s> not found. "
+                                    "For creating the table switch to "
+                                    "'Manage layers' tab.") % tableName)
         
-        cmd = ["v.db.select",
-               "-c", "--q",
-               "map=%s" % self.mapDBInfo.map,
-               "layer=%d" % layer]
-               
         if not columns:
             columns = self.mapDBInfo.GetColumns(tableName)
         else:
             all = self.mapDBInfo.GetColumns(tableName)
-            cmd.append("columns=%s" % ','.join(columns))
             for col in columns:
                 if col not in all:
                     wx.MessageBox(parent=self,
@@ -213,10 +167,6 @@
                                       { 'column' : col, 'table' : tableName },
                                   caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
                     return
-
-
-        if where:
-            cmd.append("where=%s" % where)
         
         try:
             # for maps connected via v.external
@@ -233,9 +183,32 @@
         # TODO: more effective way should be implemented...
         outFile = tempfile.NamedTemporaryFile(mode='w+b')
         
-        selectCommand = gcmd.Command(cmd,
-                                     stdout=outFile)
-
+        if sql:
+            ret = gcmd.RunCommand('db.select',
+                                  quiet = True,
+                                  parent = self,
+                                  flags = 'c',
+                                  sql = sql,
+                                  output = outFile.name)
+        else:
+            if columns:
+                ret = gcmd.RunCommand('v.db.select',
+                                      quiet = True,
+                                      flags = 'c',
+                                      map = self.mapDBInfo.map,
+                                      layer = layer,
+                                      columns = ','.join(columns),
+                                      where = where,
+                                      stdout=outFile)
+            else:
+                ret = gcmd.RunCommand('v.db.select',
+                                      quiet = True,
+                                      flags = 'c',
+                                      map = self.mapDBInfo.map,
+                                      layer = layer,
+                                      where = where,
+                                      stdout=outFile) 
+        
         # These two should probably be passed to init more cleanly
         # setting the numbers of items = number of elements in the dictionary
         self.itemDataMap  = {}
@@ -267,61 +240,17 @@
         while True:
             # os.linesep doesn't work here (MSYS)
             record = outFile.readline().replace('\n', '')
+            
             if not record:
                 break
-            
-            self.itemDataMap[i] = []
-            j = 0
-            
-            for value in record.split('|'):
-                if self.columns[columns[j]]['ctype'] != type(''):
-                    # encode numeric values
-                    try:
-                        ### casting disabled (2009/03)
-                        ### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value))
-                        self.itemDataMap[i].append(value)
-                    except ValueError:
-                        self.itemDataMap[i].append(_('Unknown value'))
-                else:
-                    # encode string values
-                    try:
-                        self.itemDataMap[i].append(unicodeValue(value))
-                    except UnicodeDecodeError:
-                        self.itemDataMap[i].append(_("Unable to decode value. "
-                                                     "Set encoding in GUI preferences ('Attributes')."))
-                
-                if keyId > -1 and keyId == j:
-                    try:
-                        cat = self.columns[columns[j]]['ctype'] (value)
-                    except ValueError, e:
-                        cat = -1
-                        wx.MessageBox(parent=self,
-                                      message=_("Error loading attribute data. "
-                                                "Record number: %(rec)d. Unable to convert value '%(val)s' in "
-                                                "key column (%(key)s) to integer.\n\n"
-                                                "Details: %(detail)s") % \
-                                          { 'rec' : i + 1, 'val' : value,
-                                            'key' : keyColumn, 'detail' : e},
-                                      caption=_("Error"),
-                                      style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                j += 1
+           
+            self.AddDataRow(i, record, columns, keyId)
 
-            # insert to table
-            # index = self.InsertStringItem(index=sys.maxint, label=str(self.itemDataMap[i][0]))
-            # for j in range(len(self.itemDataMap[i][1:])):
-            # self.SetStringItem(index=index, col=j+1, label=str(self.itemDataMap[i][j+1]))
-                
-            # self.SetItemData(item=index, data=i)
-            
-            self.itemIndexMap.append(i)
-            if keyId > -1: # load cats only when LoadData() is called first time
-                self.itemCatsMap[i] = cat
-
             i += 1
             if i >= 100000:
                 self.log.write(_("Limit 100000 records."))
                 break
-
+        
         self.SetItemCount(i)
         
         i = 0
@@ -333,16 +262,62 @@
                 width = 300
             self.SetColumnWidth(col=i, width=width)
             i += 1
-
+        
         self.SendSizeEvent()
-
+        
         self.log.write(_("Number of loaded records: %d") % \
                            self.GetItemCount())
         
         return keyId
     
+    def AddDataRow(self, i, record, columns, keyId):
+        """!Add row to the data list"""
+        self.itemDataMap[i] = []
+        keyColumn = self.mapDBInfo.layers[self.layer]['key']
+        j = 0
+        cat = None
+        
+        if keyColumn == 'OGC_FID':
+            self.itemDataMap[i].append(i+1)
+            j += 1
+            cat = i + 1
+        
+        for value in record.split('|'):
+            if self.columns[columns[j]]['ctype'] != types.StringType:
+                try:
+                    ### casting disabled (2009/03)
+                    ### self.itemDataMap[i].append(self.columns[columns[j]]['ctype'](value))
+                    self.itemDataMap[i].append(value)
+                except ValueError:
+                    self.itemDataMap[i].append(_('Unknown value'))
+            else:
+                # encode string values
+                try:
+                    self.itemDataMap[i].append(dbm_base.unicodeValue(value))
+                except UnicodeDecodeError:
+                    self.itemDataMap[i].append(_("Unable to decode value. "
+                                                 "Set encoding in GUI preferences ('Attributes')."))
+                
+            if not cat and keyId > -1 and keyId == j:
+                try:
+                    cat = self.columns[columns[j]]['ctype'] (value)
+                except ValueError, e:
+                    cat = -1
+                    gcmd.GError(parent = self,
+                                message=_("Error loading attribute data. "
+                                          "Record number: %(rec)d. Unable to convert value '%(val)s' in "
+                                          "key column (%(key)s) to integer.\n\n"
+                                          "Details: %(detail)s") % \
+                                    { 'rec' : i + 1, 'val' : value,
+                                      'key' : keyColumn, 'detail' : e})
+            j += 1
+        
+        self.itemIndexMap.append(i)
+        if keyId > -1: # load cats only when LoadData() is called first time
+            self.itemCatsMap[i] = cat
+        
     def OnItemSelected(self, event):
-        """Item selected. Add item to selected cats..."""
+        """!Item selected. Add item to selected cats..."""
         #         cat = int(self.GetItemText(event.m_itemIndex))
         #         if cat not in self.selectedCats:
         #             self.selectedCats.append(cat)
@@ -351,7 +326,7 @@
         event.Skip()
 
     def OnItemDeselected(self, event):
-        """Item deselected. Remove item from selected cats..."""
+        """!Item deselected. Remove item from selected cats..."""
         #         cat = int(self.GetItemText(event.m_itemIndex))
         #         if cat in self.selectedCats:
         #             self.selectedCats.remove(cat)
@@ -360,7 +335,7 @@
         event.Skip()
 
     def GetSelectedItems(self):
-        """Return list of selected items (category numbers)"""
+        """!Return list of selected items (category numbers)"""
         cats = []
         item = self.GetFirstSelected()
         while item != -1:
@@ -370,32 +345,134 @@
         return cats
 
     def GetColumnText(self, index, col):
-        """Return column text"""
+        """!Return column text"""
         item = self.GetItem(index, col)
         return item.GetText()
 
     def GetListCtrl(self):
-        """Returt list"""
+        """!Returt list"""
         return self
 
     def OnGetItemText(self, item, col):
-        """Get item text"""
+        """!Get item text"""
         index = self.itemIndexMap[item]
         s = self.itemDataMap[index][col]
         return s
 
     def OnGetItemAttr(self, item):
-        """Get item attributes"""
+        """!Get item attributes"""
         index = self.itemIndexMap[item]
         if ( index % 2) == 0:
             return self.attr2
         else:
             return self.attr1
 
-    def OnColumnClick(self, event):
-        """Column heading clicked -> sorting"""
+    def OnColumnMenu(self, event):
+        """!Column heading right mouse button -> pop-up menu"""
         self._col = event.GetColumn()
+        
+        popupMenu = wx.Menu()
 
+        if not hasattr (self, "popupID1"):
+            self.popupID1 = wx.NewId()
+            self.popupID2 = wx.NewId()
+            self.popupID3 = wx.NewId()
+            self.popupID4 = wx.NewId()
+            self.popupID5 = wx.NewId()
+            self.popupID6 = wx.NewId()
+            self.popupID7 = wx.NewId()
+            self.popupID8 = wx.NewId()
+            self.popupID9 = wx.NewId()
+            self.popupID10 = wx.NewId()
+            self.popupID11 = wx.NewId()
+            self.popupID12 = wx.NewId()
+        
+        popupMenu.Append(self.popupID1, text=_("Sort ascending"))
+        popupMenu.Append(self.popupID2, text=_("Sort descending"))
+        popupMenu.AppendSeparator()
+        subMenu = wx.Menu()
+        popupMenu.AppendMenu(self.popupID3, _("Calculate (only numeric columns)"),
+                             subMenu)
+        if not self.log.parent.editable or \
+                self.columns[self.GetColumn(self._col).GetText()]['ctype'] not in (types.IntType, types.FloatType):
+            popupMenu.Enable(self.popupID3, False)
+        
+        subMenu.Append(self.popupID4,  text=_("Area size"))
+        subMenu.Append(self.popupID5,  text=_("Line length"))
+        subMenu.Append(self.popupID6,  text=_("Compactness of an area"))
+        subMenu.Append(self.popupID7,  text=_("Fractal dimension of boundary defining a polygon"))
+        subMenu.Append(self.popupID8,  text=_("Perimeter length of an area"))
+        subMenu.Append(self.popupID9,  text=_("Number of features for each category"))
+        subMenu.Append(self.popupID10, text=_("Slope steepness of 3D line"))
+        subMenu.Append(self.popupID11, text=_("Line sinuousity"))
+        subMenu.Append(self.popupID12, text=_("Line azimuth"))
+        
+        self.Bind (wx.EVT_MENU, self.OnColumnSortAsc,  id=self.popupID1)
+        self.Bind (wx.EVT_MENU, self.OnColumnSortDesc, id=self.popupID2)
+        for id in (self.popupID4, self.popupID5, self.popupID6,
+                   self.popupID7, self.popupID8, self.popupID9,
+                   self.popupID10, self.popupID11, self.popupID12):
+            self.Bind(wx.EVT_MENU, self.OnColumnCompute, id = id)
+        
+        self.PopupMenu(popupMenu)
+        popupMenu.Destroy()
+
+    def OnColumnSort(self, event):
+        """!Column heading left mouse button -> sorting"""
+        self._col = event.GetColumn()
+        
+        self.ColumnSort()
+        
+        event.Skip()
+
+    def OnColumnSortAsc(self, event):
+        """!Sort values of selected column (ascending)"""
+        self.SortListItems(col = self._col, ascending = True)
+        event.Skip()
+
+    def OnColumnSortDesc(self, event):
+        """!Sort values of selected column (descending)"""
+        self.SortListItems(col = self._col, ascending = False)
+        event.Skip()
+        
+    def OnColumnCompute(self, event):
+        """!Compute values of selected column"""
+        id = event.GetId()
+        
+        option = None
+        if id == self.popupID4:
+            option = 'area'
+        elif id == self.popupID5:
+            option = 'length'
+        elif id == self.popupID6:
+            option = 'compact'
+        elif id == self.popupID7:
+            option = 'fd'
+        elif id == self.popupID8:
+            option = 'perimeter'
+        elif id == self.popupID9:
+            option = 'count'
+        elif id == self.popupID10:
+            option = 'slope'
+        elif id == self.popupID11:
+            option = 'sinuous'
+        elif id == self.popupID12:
+            option = 'azimuth'
+        
+        if not option:
+            return
+        
+        gcmd.RunCommand('v.to.db',
+                        parent = self.parent,
+                        map = self.mapDBInfo.map,
+                        layer = self.layer, 
+                        option = option,
+                        columns = self.GetColumn(self._col).GetText())
+        
+        self.LoadData(self.layer)
+        
+    def ColumnSort(self):
+        """!Sort values of selected column (self._col)"""
         # remove duplicated arrow symbol from column header
         # FIXME: should be done automatically
         info = wx.ListItem()
@@ -404,11 +481,9 @@
         for column in range(self.GetColumnCount()):
             info.m_text = self.GetColumn(column).GetText()
             self.SetColumn(column, info)
-
-        event.Skip()
-
+        
     def SortItems(self, sorter=cmp):
-        """Sort items"""
+        """!Sort items"""
         items = list(self.itemDataMap.keys())
         items.sort(self.Sorter)
         self.itemIndexMap = items
@@ -435,18 +510,18 @@
         # If the items are equal then pick something else to make the sort value unique
         if cmpVal == 0:
             cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2))
-
+        
         if ascending:
             return cmpVal
         else:
             return -cmpVal
 
     def GetSortImages(self):
-        """Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
+        """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
         return (self.sm_dn, self.sm_up)
 
     def IsEmpty(self):
-        """Check if list if empty"""
+        """!Check if list if empty"""
         if self.columns:
             return False
         
@@ -456,24 +531,37 @@
     """
     GRASS Attribute manager main window
     """
-    def __init__(self, parent, id, title, vectmap,
+    def __init__(self, parent, id=wx.ID_ANY,
                  size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE,
-                 item=None, log=None):
+                 title=None, vectorName=None, item=None, log=None):
 
-        self.vectmap   = vectmap
-        self.parent    = parent # GMFrame
-        self.treeItem  = item   # item in layer tree
-        self.cmdLog    = log    # self.parent.goutput
-                
+        self.vectorName = vectorName
+        self.parent     = parent # GMFrame
+        self.treeItem   = item   # item in layer tree
+        if self.parent and self.parent.GetName() == "LayerManager" and \
+                self.treeItem and not self.vectorName:
+            maptree = self.parent.curr_page.maptree
+            name = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetName()
+            self.vectorName = name
+        
         # vector attributes can be changed only if vector map is in
         # the current mapset
-        if grass.find_file(name = self.vectmap, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
+        if grass.find_file(name = self.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
             self.editable = True
         else:
             self.editable = False
-                
-        wx.Frame.__init__(self, parent, id, title, style=style)
         
+        self.cmdLog     = log    # self.parent.goutput
+        
+        wx.Frame.__init__(self, parent, id, style=style)
+
+        # title
+        if not title:
+            self.SetTitle("%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
+                                         self.vectorName))
+        else:
+            self.SetTitle(title)
+        
         # icon
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
 
@@ -492,8 +580,8 @@
         self.qlayer = None
 
         # -> layers / tables description
-        self.mapDBInfo = VectorDBInfo(self.vectmap)
-        
+        self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
+
         # sqlbuilder
         self.builder = None
         
@@ -502,7 +590,7 @@
                           message=_("Database connection for vector map <%s> "
                                     "is not defined in DB file. "
                                     "You can define new connection in "
-                                    "'Manage layers' tab.") % self.vectmap,
+                                    "'Manage layers' tab.") % self.vectorName,
                           caption=_("Attribute Table Manager"),
                           style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
 
@@ -591,7 +679,7 @@
         self.SetMinSize(self.GetSize())
 
     def __createBrowsePage(self, onlyLayer=-1):
-        """Create browse tab page"""
+        """!Create browse tab page"""
         for layer in self.mapDBInfo.layers.keys():
             if onlyLayer > 0 and layer != onlyLayer:
                 continue
@@ -733,7 +821,7 @@
             self.layer = None
         
     def __createManageTablePage(self, onlyLayer=-1):
-        """Create manage page (create/link and alter tables)"""
+        """!Create manage page (create/link and alter tables)"""
         for layer in self.mapDBInfo.layers.keys():
             if onlyLayer > 0 and layer != onlyLayer:
                 continue
@@ -756,7 +844,7 @@
             dbBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
                                           label=" %s " % _("Database connection"))
             dbSizer = wx.StaticBoxSizer(dbBox, wx.VERTICAL)
-            dbSizer.Add(item=createDbInfoDesc(panel, self.mapDBInfo, layer),
+            dbSizer.Add(item=dbm_base.createDbInfoDesc(panel, self.mapDBInfo, layer),
                         proportion=1,
                         flag=wx.EXPAND | wx.ALL,
                         border=3)
@@ -903,7 +991,7 @@
             self.layer = None
         
     def __createTableDesc(self, parent, table):
-        """Create list with table description"""
+        """!Create list with table description"""
         list = TableListCtrl(parent=parent, id=wx.ID_ANY,
                              table=self.mapDBInfo.tables[table],
                              columns=self.mapDBInfo.GetColumns(table))
@@ -915,7 +1003,7 @@
         return list
 
     def __createManageLayerPage(self):
-        """Create manage page"""
+        """!Create manage page"""
         splitterWin = wx.SplitterWindow(parent=self.manageLayerPage, id=wx.ID_ANY)
         splitterWin.SetMinimumPaneSize(100)
         
@@ -968,7 +1056,7 @@
         splitterWin.Fit()
 
     def __createLayerDesc(self, parent):
-        """Create list of linked layers"""
+        """!Create list of linked layers"""
         list = LayerListCtrl(parent=parent, id=wx.ID_ANY,
                              layers=self.mapDBInfo.layers)
         
@@ -980,7 +1068,7 @@
         return list
 
     def __layout(self):
-        """Do layout"""
+        """!Do layout"""
         # frame body
         mainSizer = wx.BoxSizer(wx.VERTICAL)
 
@@ -1000,7 +1088,7 @@
         self.Layout()
         
     def OnDataRightUp(self, event):
-        """Table description area, context menu"""
+        """!Table description area, context menu"""
         if not hasattr(self, "popupDataID1"):
             self.popupDataID1 = wx.NewId()
             self.popupDataID2 = wx.NewId()
@@ -1068,7 +1156,7 @@
                            list.GetItemCount())
 
     def OnDataItemDelete(self, event):
-        """Delete selected item(s) from the list (layer/category pair)"""
+        """!Delete selected item(s) from the list (layer/category pair)"""
         list = self.FindWindowById(self.layerPage[self.layer]['data'])
         item = list.GetFirstSelected()
 
@@ -1132,7 +1220,7 @@
         return True
 
     def OnDataItemDeleteAll(self, event):
-        """Delete all items from the list"""
+        """!Delete all items from the list"""
         list = self.FindWindowById(self.layerPage[self.layer]['data'])
         if UserSettings.Get(group='atm', key='askOnDeleteRec', subkey='enabled'):
             deleteDialog = wx.MessageBox(parent=self,
@@ -1157,7 +1245,7 @@
         event.Skip()
 
     def _drawSelected(self, zoom):
-        """Highlight selected features"""
+        """!Highlight selected features"""
         if not self.map or not self.mapdisplay:
             return
         
@@ -1166,7 +1254,7 @@
 
         digitToolbar = self.mapdisplay.toolbars['vdigit']
         if digitToolbar and digitToolbar.GetLayer() and \
-                digitToolbar.GetLayer().GetName() == self.vectmap:
+                digitToolbar.GetLayer().GetName() == self.vectorName:
 
             self.mapdisplay.digit.driver.SetSelected(cats, field=self.layer)
             if zoom:
@@ -1196,16 +1284,19 @@
                         where += '%s = %d or ' % (keyColumn, int(range))
                 where = where.rstrip('or ')
                 
-                cmd = gcmd.Command(["v.db.select",
-                                    "-r", "--q",
-                                    "map=%s" % self.mapDBInfo.map,
-                                    "layer=%d" % self.layer,
-                                    "where=%s" % where])
+                select = gcmd.RunCommand('v.db.select',
+                                         parent = self,
+                                         read = True,
+                                         quiet = True,
+                                         flags = 'r',
+                                         map = self.mapDBInfo.map,
+                                         layer = int(self.layer),
+                                         where = where)
                 
                 region = {}
-                for line in cmd.ReadStdOutput():
+                for line in select.splitlines():
                     key, value = line.split('=')
-                    region[key] = float(value)
+                    region[key.strip()] = float(value.strip())
                 
                 self.mapdisplay.Map.GetRegion(n=region['n'], s=region['s'],
                                               w=region['w'], e=region['e'],
@@ -1219,7 +1310,7 @@
             self.mapdisplay.MapWindow.UpdateMap(render=False, renderVector=True)
         
     def OnDataDrawSelected(self, event):
-        """Reload table description"""
+        """!Reload table description"""
         self._drawSelected(zoom=False)
         event.Skip()
 
@@ -1228,11 +1319,11 @@
         event.Skip()
         
     def OnDataItemAdd(self, event):
-        """Add new record to the attribute table"""
+        """!Add new record to the attribute table"""
         list      = self.FindWindowById(self.layerPage[self.layer]['data'])
         table     = self.mapDBInfo.layers[self.layer]['table']
         keyColumn = self.mapDBInfo.layers[self.layer]['key']
-
+        
         # (column name, value)
         data = []
 
@@ -1301,6 +1392,7 @@
                         else:
                             value = values[i]
                         values[i] = list.columns[columnName[i]]['ctype'] (value)
+
                     except:
                         raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % 
                                          {'value' : str(values[i]),
@@ -1340,7 +1432,7 @@
             self.ApplyCommands()
             
     def OnDataItemEdit(self, event):
-        """Edit selected record of the attribute table"""
+        """!Edit selected record of the attribute table"""
         list      = self.FindWindowById(self.layerPage[self.layer]['data'])
         item      = list.GetFirstSelected()
         if item == -1:
@@ -1435,15 +1527,13 @@
 
             list.Update(self.mapDBInfo)
                         
-            list.Update(self.mapDBInfo)
-                
     def OnDataReload(self, event):
-        """Reload list of records"""
+        """!Reload list of records"""
         self.OnApplySqlStatement(None)
         self.listOfSQLStatements = []
 
     def OnDataSelectAll(self, event):
-        """Select all items"""
+        """!Select all items"""
         list = self.FindWindowById(self.layerPage[self.layer]['data'])
         item = -1
 
@@ -1456,7 +1546,7 @@
         event.Skip()
 
     def OnDataSelectNone(self, event):
-        """Deselect items"""
+        """!Deselect items"""
         list = self.FindWindowById(self.layerPage[self.layer]['data'])
         item = -1
 
@@ -1470,7 +1560,7 @@
 
 
     def OnTableChangeType(self, event):
-        """Data type for new column changed. Enable or disable
+        """!Data type for new column changed. Enable or disable
         data length widget"""
         win = self.FindWindowById(self.layerPage[self.layer]['addColLength'])
         if event.GetString() == "varchar":
@@ -1479,7 +1569,7 @@
             win.Enable(False)
 
     def OnTableRenameColumnName(self, event):
-        """Editing column name to be added to the table"""
+        """!Editing column name to be added to the table"""
         btn  = self.FindWindowById(self.layerPage[self.layer]['renameColButton'])
         col  = self.FindWindowById(self.layerPage[self.layer]['renameCol'])
         colTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo'])
@@ -1491,7 +1581,7 @@
         event.Skip()
 
     def OnTableAddColumnName(self, event):
-        """Editing column name to be added to the table"""
+        """!Editing column name to be added to the table"""
         btn = self.FindWindowById(self.layerPage[self.layer]['addColButton'])
         if len(event.GetString()) > 0:
             btn.Enable(True)
@@ -1501,7 +1591,7 @@
         event.Skip()
 
     def OnTableItemChange(self, event):
-        """Rename column in the table"""
+        """!Rename column in the table"""
         list   = self.FindWindowById(self.layerPage[self.layer]['tableData'])
         name   = self.FindWindowById(self.layerPage[self.layer]['renameCol']).GetValue()
         nameTo = self.FindWindowById(self.layerPage[self.layer]['renameColTo']).GetValue()
@@ -1529,10 +1619,11 @@
                 else:
                     list.SetItemText(item, nameTo)
 
-                    self.listOfCommands.append(['v.db.renamecol',
-                                                'map=%s' % self.vectmap,
-                                                'layer=%d' % self.layer,
-                                                'column=%s,%s' % (name, nameTo)])
+                    self.listOfCommands.append(('v.db.renamecol',
+                                                { 'map'    : self.vectorName,
+                                                  'layer'  : self.layer,
+                                                  'column' : '%s,%s' % (name, nameTo) }
+                                                ))
             else:
                 wx.MessageBox(parent=self,
                               message=_("Unable to rename column. "
@@ -1552,7 +1643,7 @@
         event.Skip()
 
     def OnTableRightUp(self, event):
-        """Table description area, context menu"""
+        """!Table description area, context menu"""
         if not hasattr(self, "popupTableID"):
             self.popupTableID1 = wx.NewId()
             self.popupTableID2 = wx.NewId()
@@ -1574,7 +1665,7 @@
         menu.Destroy()
 
     def OnTableItemDelete(self, event):
-        """Delete selected item(s) from the list"""
+        """!Delete selected item(s) from the list"""
         list = self.FindWindowById(self.layerPage[self.layer]['tableData'])
         
         item = list.GetFirstSelected()
@@ -1590,10 +1681,11 @@
                 return False
         
         while item != -1:
-            self.listOfCommands.append(['v.db.dropcol',
-                                        'map=%s' % self.vectmap,
-                                        'layer=%d' % self.layer,
-                                        'column=%s' % list.GetItemText(item)])
+            self.listOfCommands.append(('v.db.dropcol',
+                                        { 'map' : self.vectorName,
+                                          'layer' : self.layer,
+                                          'column' : list.GetItemText(item) }
+                                        ))
             list.DeleteItem(item)
             item = list.GetFirstSelected()
         
@@ -1608,7 +1700,7 @@
         event.Skip()
 
     def OnTableItemDeleteAll(self, event):
-        """Delete all items from the list"""
+        """!Delete all items from the list"""
         table     = self.mapDBInfo.layers[self.layer]['table']
         cols      = self.mapDBInfo.GetColumns(table)
         keyColumn = self.mapDBInfo.layers[self.layer]['key']
@@ -1626,10 +1718,11 @@
                 return False
         
         for col in cols:
-            self.listOfCommands = [['v.db.dropcol',
-                                    'map=%s' % self.vectmap,
-                                    'layer=%d' % self.layer,
-                                    'column=%s' % col]]
+            self.listOfCommands.append(('v.db.dropcol',
+                                        { 'map' : self.vectorName,
+                                          'layer' : self.layer,
+                                          'column' : col }
+                                        ))
         self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems()
 
         # apply changes
@@ -1643,12 +1736,12 @@
         event.Skip()
 
     def OnTableReload(self, event=None):
-        """Reload table description"""
+        """!Reload table description"""
         self.FindWindowById(self.layerPage[self.layer]['tableData']).Populate(update=True)
         self.listOfCommands = []
 
     def OnTableItemAdd(self, event):
-        """Add new column to the table"""
+        """!Add new column to the table"""
 	table = self.mapDBInfo.layers[self.layer]['table']
         name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
 
@@ -1688,11 +1781,11 @@
         # add v.db.addcol command to the list
         if type == 'varchar':
             type += ' (%d)' % length
-        self.listOfCommands.append(['v.db.addcol',
-                                    'map=%s' % self.vectmap,
-                                    'layer=%d' % self.layer,
-                                    'columns=%s %s' % (name, type)])
-
+        self.listOfCommands.append(('v.db.addcol',
+                                    { 'map' : self.vectorName,
+                                      'layer' : self.layer,
+                                      'columns' : '%s %s' % (name, type) }
+                                    ))
         # apply changes
         self.ApplyCommands()
         
@@ -1704,7 +1797,7 @@
         event.Skip()
         
     def OnLayerPageChanged(self, event):
-        """Layer tab changed"""
+        """!Layer tab changed"""
         pageNum = event.GetSelection()
         self.layer = self.mapDBInfo.layers.keys()[pageNum]
         
@@ -1749,11 +1842,11 @@
         event.Skip()
         
     def OnLayerRightUp(self, event):
-        """Layer description area, context menu"""
+        """!Layer description area, context menu"""
         pass
 
     def OnChangeSql(self, event):
-        """Switch simple/advanced sql statement"""
+        """!Switch simple/advanced sql statement"""
         if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
             self.FindWindowById(self.layerPage[self.layer]['where']).Enable(True)
             self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(False)
@@ -1764,15 +1857,16 @@
             self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True)
 
     def ApplyCommands(self):
-        """Apply changes"""
+        """!Apply changes"""
         # perform GRASS commands (e.g. v.db.addcol)
         if len(self.listOfCommands) > 0:
             for cmd in self.listOfCommands:
-                Debug.msg(3, 'AttributeManager.ApplyCommands() cmd=\'%s\'' %
-                          ' '.join(cmd))
-                gcmd.Command(cmd)
-
-            self.mapDBInfo = VectorDBInfo(self.vectmap)
+                gcmd.RunCommand(prog = cmd[0],
+                                quiet = True,
+                                parent = self,
+                                **cmd[1])
+            
+            self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)
             table = self.mapDBInfo.layers[self.layer]['table']
 
             # update table description
@@ -1806,23 +1900,24 @@
             driver   = self.mapDBInfo.layers[self.layer]["driver"]
             database = self.mapDBInfo.layers[self.layer]["database"]
 
-            cmd = ['db.execute',
-                   'input=%s' % sqlFile.name,
-                   'driver=%s' % driver,
-                   'database=%s' % database]
-
             Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
                       ';'.join(["%s" % s for s in self.listOfSQLStatements]))
 
-            gcmd.Command(cmd)
-
+            gcmd.RunCommand('db.execute',
+                            parent = self,
+                            input = sqlFile.name,
+                            driver = driver,
+                            database = database)
+            
             # reset list of statements
             self.listOfSQLStatements = []
 
     def OnApplySqlStatement(self, event):
-        """Apply simple/advanced sql statement"""
+        """!Apply simple/advanced sql statement"""
         keyColumn = -1 # index of key column
         listWin = self.FindWindowById(self.layerPage[self.layer]['data'])
+        sql = None
+        
         if self.FindWindowById(self.layerPage[self.layer]['simple']).GetValue():
             # simple sql statement
             whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection()
@@ -1832,16 +1927,17 @@
                     keyColumn = listWin.LoadData(self.layer, where=whereCol + whereVal)
                 else:
                     keyColumn = listWin.LoadData(self.layer)
-            except gcmd.CmdError, e:
-                wx.MessageBox(parent=self,
-                              message=_("Loading attribute data failed.\n\n%s") % e.message,
-                              caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            except gcmd.GException, e:
+                gcmd.GError(parent = self,
+                            message = _("Loading attribute data failed.\n\n%s") % e.value)
                 self.FindWindowById(self.layerPage[self.layer]['where']).SetValue('')
         else:
             # advanced sql statement
             win = self.FindWindowById(self.layerPage[self.layer]['statement'])
             try:
                 cols, where = self.ValidateSelectStatement(win.GetValue())
+                if cols is None and where is None:
+                    sql = win.GetValue()
             except TypeError:
                 wx.MessageBox(parent=self,
                               message=_("Loading attribute data failed.\n"
@@ -1851,28 +1947,30 @@
                 cols = None
                 where = None
             
-            if cols or where:
+            if cols or where or sql:
                 try:
                     keyColumn = listWin.LoadData(self.layer, columns=cols,
-                                                 where=where)
-                except gcmd.CmdError, e:
-                    wx.MessageBox(parent=self,
-                                  message=_("Loading attribute data failed.\n\n%s") % e.message,
-                                  caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                                                 where=where, sql=sql)
+                except gcmd.GException, e:
+                    gcmd.GError(parent = self,
+                                message = _("Loading attribute data failed.\n\n%s") % e.value)
                     win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
         
         # sort by key column
-        if keyColumn > -1:
-            listWin.SortListItems(col=keyColumn, ascending=True)
+        if sql and 'order by' in sql.lower():
+            pass # don't order by key column
         else:
-            listWin.SortListItems(col=0, ascending=True) 
-
+            if keyColumn > -1:
+                listWin.SortListItems(col=keyColumn, ascending=True)
+            else:
+                listWin.SortListItems(col=0, ascending=True) 
+        
         # update statusbar
         self.log.write(_("Number of loaded records: %d") % \
                            self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
 
     def ValidateSelectStatement(self, statement):
-        """Validate SQL select statement
+        """!Validate SQL select statement
 
         @return (columns, where)
         @return None on error
@@ -1904,23 +2002,27 @@
             if index > -1:
                 where = statement[index+6:]
             else:
-                return None
+                where = None
         else:
             where = None
         
         return (cols, where)
     
     def OnCloseWindow(self, event):
-        """Cancel button pressed"""
+        """!Cancel button pressed"""
         self.Close()
-        event.Skip()
+        if self.parent and self.parent.GetName() == 'LayerManager':
+            # deregister ATM
+            self.parent.dialogs['atm'].remove(self)
         
+        event.Skip()
+
     def OnBuilder(self,event):
         """!SQL Builder button pressed -> show the SQLBuilder dialog"""
         if not self.builder:
             self.builder = sqlbuilder.SQLFrame(parent = self, id = wx.ID_ANY,
                                                title = _("SQL Builder"),
-                                               vectmap = self.vectmap,
+                                               vectmap = self.vectorName,
                                                evtheader = self.OnBuilderEvt)
             self.builder.Show()
         else:
@@ -1937,12 +2039,9 @@
         
     def OnTextEnter(self, event):
         pass
-
-    def OnSQLBuilder(self, event):
-        pass
-
+    
     def OnDataItemActivated(self, event):
-        """Item activated, highlight selected item"""
+        """!Item activated, highlight selected item"""
         self.OnDataDrawSelected(event)
 
         event.Skip()
@@ -1962,12 +2061,12 @@
             return
         else:
             # dialog to get file name
-            name, add = gdialogs.CreateNewVector(parent=self, title=_('Extract selected features'),
-                                                 log=self.cmdLog,
-                                                 cmdDef=(["v.extract",
-                                                          "input=%s" % self.vectmap,
-                                                          "list=%s" % utils.ListOfCatsToRange(cats)],
-                                                         "output"),
+            name, add = gdialogs.CreateNewVector(parent = self, title = _('Extract selected features'),
+                                                 log = self.cmdLog,
+                                                 cmd = (('v.extract',
+                                                         { 'input' : self.vectorName,
+                                                           'list' : utils.ListOfCatsToRange(cats) },
+                                                         'output')),
                                                  disableTable = True)
             if name and add:
                 # add layer to map layer tree
@@ -1976,7 +2075,7 @@
                                                        lchecked=True,
                                                        lopacity=1.0,
                                                        lcmd=['d.vect', 'map=%s' % name])
-        
+            
     def OnDeleteSelected(self, event):
         """
         Delete vector objects selected in attribute browse window
@@ -1992,20 +2091,21 @@
         if self.OnDataItemDelete(None):
             digitToolbar = self.mapdisplay.toolbars['vdigit']
             if digitToolbar and digitToolbar.GetLayer() and \
-                    digitToolbar.GetLayer().GetName() == self.vectmap:
+                    digitToolbar.GetLayer().GetName() == self.vectorName:
                 self.mapdisplay.digit.driver.SetSelected(map(int, cats), field=self.layer)
                 self.mapdisplay.digit.DeleteSelectedLines()
             else:
-                gcmd.Command(['v.edit',
-                              '--q',
-                              'map=%s' % self.vectmap,
-                              'tool=delete',
-                              'cats=%s' % utils.ListOfCatsToRange(cats)])
-        
+                gcmd.RunCommand('v.edit',
+                                parent = self,
+                                quiet = True,
+                                map = self.vectorName,
+                                tool = 'delete',
+                                cats = utils.ListOfCatsToRange(cats))
+                
             self.mapdisplay.MapWindow.UpdateMap(render=True, renderVector=True)
         
     def AddQueryMapLayer(self):
-        """Redraw a map
+        """!Redraw a map
 
         Return True if map has been redrawn, False if no map is given
         """
@@ -2018,14 +2118,14 @@
             self.qlayer = None
             
         if self.qlayer:
-            self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectmap, cats, addLayer=False))
+            self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer=False))
         else:
-            self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectmap, cats)
+            self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats)
 
         return self.qlayer
     
     def UpdateDialog(self, layer):
-        """Updates dialog layout for given layer"""
+        """!Updates dialog layout for given layer"""
         #
         # delete page
         #
@@ -2041,7 +2141,7 @@
             self.notebook.SetSelection(2)
             
         # fetch fresh db info
-        self.mapDBInfo = VectorDBInfo(self.vectmap)    
+        self.mapDBInfo = dbm_base.VectorDBInfo(self.vectorName)    
 
         #
         # add new page
@@ -2080,11 +2180,29 @@
         ### modify layer
         self.manageLayerBook.modifyLayerWidgets['layer'][1].SetItems(listOfLayers)
         self.manageLayerBook.OnChangeLayer(event=None)
-        
+
+    def GetVectorName(self):
+        """!Get vector name"""
+        return self.vectorName
+    
+    def LoadData(self, layer, columns=None, where=None, sql=None):
+        """!Load data into list
+
+        @param layer layer number
+        @param columns list of columns for output
+        @param where where statement
+        @param sql full sql statement
+
+        @return id of key column 
+        @return -1 if key column is not displayed
+        """
+        listWin = self.FindWindowById(self.layerPage[layer]['data'])
+        return listWin.LoadData(layer, columns, where, sql)
+    
 class TableListCtrl(wx.ListCtrl,
                     listmix.ListCtrlAutoWidthMixin):
                     #                    listmix.TextEditMixin):
-    """Table description list"""
+    """!Table description list"""
 
     def __init__(self, parent, id, table, columns, pos=wx.DefaultPosition,
                  size=wx.DefaultSize):
@@ -2100,12 +2218,12 @@
         # listmix.TextEditMixin.__init__(self)
 
     def Update(self, table, columns):
-        """Update column description"""
+        """!Update column description"""
         self.table   = table
         self.columns = columns
 
     def Populate(self, update=False):
-        """Populate the list"""
+        """!Populate the list"""
         itemData = {} # requested by sorter
 
         if not update:
@@ -2138,7 +2256,7 @@
                     listmix.ListCtrlAutoWidthMixin):
                     # listmix.ColumnSorterMixin):
                     # listmix.TextEditMixin):
-    """Layer description list"""
+    """!Layer description list"""
 
     def __init__(self, parent, id, layers,
                  pos=wx.DefaultPosition,
@@ -2154,11 +2272,11 @@
         # listmix.TextEditMixin.__init__(self)
 
     def Update(self, layers):
-        """Update description"""
+        """!Update description"""
         self.layers = layers
 
     def Populate(self, update=False):
-        """Populate the list"""
+        """!Populate the list"""
         itemData = {} # requested by sorter
 
         if not update:
@@ -2200,7 +2318,7 @@
         return itemData
 
 class LayerBook(wx.Notebook):
-    """Manage layers (add, delete, modify)"""
+    """!Manage layers (add, delete, modify)"""
     def __init__(self, parent, id,
                  parentDialog,
                  style=wx.BK_DEFAULT):
@@ -2213,24 +2331,28 @@
         #
         # drivers
         #
-        cmdDriver = gcmd.Command(['db.drivers',
-                                  '-p',
-                                  '--q'])
+        drivers = gcmd.RunCommand('db.drivers',
+                                  quiet = True,
+                                  read = True,
+                                  flags = 'p')
+        
         self.listOfDrivers = []
-        for drv in cmdDriver.ReadStdOutput():
+        for drv in drivers.splitlines():
             self.listOfDrivers.append(drv.strip())
-
+        
         #
         # get default values
         #
         self.defaultConnect = {}
-        cmdConnect = gcmd.Command(['db.connect',
-                                   '-p',
-                                   '--q'])
-        for line in cmdConnect.ReadStdOutput():
+        connect = gcmd.RunCommand('db.connect',
+                                  flags = 'p',
+                                  read = True,
+                                  quiet = True)
+        
+        for line in connect.splitlines():
             item, value = line.split(':')
             self.defaultConnect[item.strip()] = value.strip()
-
+        
         if len(self.defaultConnect['driver']) == 0 or \
                len(self.defaultConnect['database']) == 0:
             wx.MessageBox(parent=self.parent,
@@ -2253,7 +2375,7 @@
         self.__createModifyPage()
 
     def __createAddPage(self):
-        """Add new layer"""
+        """!Add new layer"""
         self.addPanel = wx.Panel(parent=self, id=wx.ID_ANY)
         self.AddPage(page=self.addPanel, text=_("Add layer"))
         
@@ -2444,7 +2566,7 @@
         pageSizer.Fit(self.addPanel)
         
     def __createDeletePage(self):
-        """Delete layer"""
+        """!Delete layer"""
         self.deletePanel = wx.Panel(parent=self, id=wx.ID_ANY)
         self.AddPage(page=self.deletePanel, text=_("Delete layer"))
 
@@ -2512,7 +2634,7 @@
         self.deletePanel.SetSizer(pageSizer)
 
     def __createModifyPage(self):
-        """Modify layer"""
+        """!Modify layer"""
         self.modifyPanel = wx.Panel(parent=self, id=wx.ID_ANY)
         self.AddPage(page=self.modifyPanel, text=_("Modify layer"))
 
@@ -2622,45 +2744,51 @@
         self.modifyPanel.SetSizer(pageSizer)
 
     def __getTables(self, driver, database):
-        """Get list of tables for given driver and database"""
+        """!Get list of tables for given driver and database"""
         tables = []
 
-        cmdTable = gcmd.Command(['db.tables',
-                                 '-p', '--q',
-                                 'driver=%s' % driver,
-                                 'database=%s' % database], rerr=None)
-
-        if cmdTable.returncode != 0:
+        ret = gcmd.RunCommand('db.tables',
+                              parent = self,
+                              read = True,
+                              flags = 'p',
+                              driver = driver,
+                              database = database)
+        
+        if ret is None:
             wx.MessageBox(parent=self,
                           message=_("Unable to get list of tables.\n"
                                     "Please use db.connect to set database parameters."),
                           caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-
+            
             return tables
         
-        for table in cmdTable.ReadStdOutput():
+        for table in ret.splitlines():
             tables.append(table)
-
+        
         return tables
 
     def __getColumns(self, driver, database, table):
-        """Get list of column of given table"""
+        """!Get list of column of given table"""
         columns = []
 
-        cmdColumn = gcmd.Command(['db.columns',
-                                  '--q',
-                                  'driver=%s' % driver,
-                                  'database=%s' % database,
-                                  'table=%s' % table],
-                                 rerr = None)
-
-        for column in cmdColumn.ReadStdOutput():
+        ret = gcmd.RunCommand('db.columns',
+                              parent = self,
+                              quiet = True,
+                              read = True,
+                              driver = driver,
+                              database = database,
+                              table = table)
+        
+        if ret == None:
+            return columns
+        
+        for column in ret.splitlines():
             columns.append(column)
-
+        
         return columns
 
     def OnDriverChanged(self, event):
-        """Driver selection changed, update list of tables"""
+        """!Driver selection changed, update list of tables"""
         driver = event.GetString()
         database = self.addLayerWidgets['database'][1].GetValue()
 
@@ -2677,11 +2805,11 @@
         event.Skip()
 
     def OnDatabaseChanged(self, event):
-        """Database selection changed, update list of tables"""
+        """!Database selection changed, update list of tables"""
         event.Skip()
 
     def OnTableChanged(self, event):
-        """Table name changed, update list of columns"""
+        """!Table name changed, update list of columns"""
         driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
         database = self.addLayerWidgets['database'][1].GetValue()
         table    = event.GetString()
@@ -2694,7 +2822,7 @@
         event.Skip()
 
     def OnSetDefault(self, event):
-        """Set default values"""
+        """!Set default values"""
         driver   = self.addLayerWidgets['driver'][1]
         database = self.addLayerWidgets['database'][1]
         table    = self.addLayerWidgets['table'][1]
@@ -2718,7 +2846,7 @@
         event.Skip()
 
     def OnCreateTable(self, event):
-        """Create new table (name and key column given)"""
+        """!Create new table (name and key column given)"""
         driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
         database = self.addLayerWidgets['database'][1].GetValue()
         table    = self.tableWidgets['table'][1].GetValue()
@@ -2741,12 +2869,13 @@
         # create table
         sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
 
-        gcmd.Command(['db.execute',
-                      '--q',
-                      'driver=%s' % driver,
-                      'database=%s' % database],
-                     stdin=sql)
-
+        gcmd.RunCommand('db.execute',
+                        quiet = True,
+                        parent = self,
+                        stdin = sql,
+                        driver = driver,
+                        database = database)
+        
         # update list of tables
         tableList = self.addLayerWidgets['table'][1]
         tableList.SetItems(self.__getTables(driver, database))
@@ -2760,7 +2889,7 @@
         event.Skip()
 
     def OnAddLayer(self, event):
-        """Add new layer to vector map"""
+        """!Add new layer to vector map"""
         layer    = int(self.addLayerWidgets['layer'][1].GetValue())
         layerWin = self.addLayerWidgets['layer'][1]
         driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
@@ -2777,26 +2906,28 @@
             return
 
         # add new layer
-        connectCmd = gcmd.Command(['v.db.connect',
-                                   '--q',
-                                   'map=%s' % self.mapDBInfo.map,
-                                   'driver=%s' % driver,
-                                   'database=%s' % database,
-                                   'table=%s' % table,
-                                   'key=%s' % key,
-                                   'layer=%d' % layer])
+        ret = gcmd.RunCommand('v.db.connect',
+                              parent = self,
+                              quiet = True,
+                              map = self.mapDBInfo.map,
+                              driver = driver,
+                              database = database,
+                              table = table,
+                              key = key,
+                              layer = layer)
         
         # insert records into table if required
         if self.addLayerWidgets['addCat'][0].IsChecked():
-            gcmd.Command(['v.to.db',
-                          '--q',
-                          'map=%s' % self.mapDBInfo.map,
-                          'layer=%d' % layer,
-                          'qlayer=%d' % layer,
-                          'option=cat',
-                          'columns=%s' % key])
+            gcmd.RunCommand('v.to.db',
+                            parent = self,
+                            quiet = True,
+                            map = self.mapDBInfo.map,
+                            layer = layer,
+                            qlayer = layer,
+                            option = 'cat',
+                            columns = key)
 
-        if connectCmd.returncode == 0:
+        if ret == 0:
             # update dialog (only for new layer)
             self.parentDialog.UpdateDialog(layer=layer) 
             # update db info
@@ -2812,16 +2943,17 @@
                 self.modifyLayerWidgets[label][1].Enable()
             
     def OnDeleteLayer(self, event):
-        """Delete layer"""
+        """!Delete layer"""
         try:
             layer = int(self.deleteLayer.GetValue())
         except:
             return
 
-        gcmd.Command(['v.db.connect',
-                      '-d', '--q',
-                      'map=%s' % self.mapDBInfo.map,
-                      'layer=%d' % layer])
+        gcmd.RunCommand('v.db.connect',
+                        parent = self,
+                        flags = 'd',
+                        map = self.mapDBInfo.map,
+                        layer = layer)
 
         # drop also table linked to layer which is deleted
         if self.deleteTable.IsChecked():
@@ -2830,12 +2962,13 @@
             table    = self.mapDBInfo.layers[layer]['table']
             sql      = 'DROP TABLE %s' % (table)
 
-            gcmd.Command(['db.execute',
-                          '--q',
-                          'driver=%s' % driver,
-                          'database=%s' % database],
-                         stdin=sql)
-
+            gcmd.RunCommand('db.execute',
+                            parent = self,
+                            stdin = sql,
+                            quiet = True,
+                            driver = driver,
+                            database = database)
+            
             # update list of tables
             tableList = self.addLayerWidgets['table'][1]
             tableList.SetItems(self.__getTables(driver, database))
@@ -2856,7 +2989,7 @@
         event.Skip()
 
     def OnChangeLayer(self, event):
-        """Layer number of layer to be deleted is changed"""
+        """!Layer number of layer to be deleted is changed"""
         try:
             layer = int(event.GetString())
         except:
@@ -2882,7 +3015,7 @@
             event.Skip()
 
     def OnModifyLayer(self, event):
-        """Modify layer connection settings"""
+        """!Modify layer connection settings"""
 
         layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
 
@@ -2899,25 +3032,23 @@
 
         if modify:
             # delete layer
-            gcmd.Command(['v.db.connect',
-                          '-d', '--q',
-                          'map=%s' % self.mapDBInfo.map,
-                          'layer=%d' % layer])
+            gcmd.RunCommand('v.db.connect',
+                            parent = self,
+                            quiet = True,
+                            flag = 'd',
+                            map = self.mapDBInfo.map,
+                            layer = layer)
 
             # add modified layer
-            gcmd.Command(['v.db.connect',
-                          '--q',
-                          'map=%s' % self.mapDBInfo.map,
-                          'driver=%s' % \
-                              self.modifyLayerWidgets['driver'][1].GetStringSelection(),
-                          'database=%s' % \
-                              self.modifyLayerWidgets['database'][1].GetValue(),
-                          'table=%s' % \
-                              self.modifyLayerWidgets['table'][1].GetStringSelection(),
-                          'key=%s' % \
-                              self.modifyLayerWidgets['key'][1].GetStringSelection(),
-                          'layer=%d' % layer])
-
+            gcmd.RunCommand('v.db.connect',
+                            quiet = True,
+                            map = self.mapDBInfo.map,
+                            driver = self.modifyLayerWidgets['driver'][1].GetStringSelection(),
+                            database = self.modifyLayerWidgets['database'][1].GetValue(),
+                            table = self.modifyLayerWidgets['table'][1].GetStringSelection(),
+                            key = self.modifyLayerWidgets['key'][1].GetStringSelection(),
+                            layer = int(layer))
+            
             # update dialog (only for new layer)
             self.parentDialog.UpdateDialog(layer=layer) 
             # update db info
@@ -2925,730 +3056,6 @@
 
         event.Skip()
 
-class DisplayAttributesDialog(wx.Dialog):
-    """
-    Standard dialog used to add/update/display attributes linked
-    to the vector map.
-
-    Attribute data can be selected based on layer and category number
-    or coordinates.
-
-    @param parent
-    @param map vector map
-    @param query query coordinates and distance (used for v.edit)
-    @param cats {layer: cats}
-    @param line feature id (requested for cats)
-    @param style
-    @param pos
-    @param action (add, update, display)
-    """
-    def __init__(self, parent, map,
-                 query=None, cats=None, line=None,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
-                 pos=wx.DefaultPosition,
-                 action="add"):
-        self.parent = parent # mapdisplay.BufferedWindow
-        self.map    = map
-        self.action = action
-
-        # ids/cats of selected features
-        # fid : {layer : cats}
-        self.cats = {}
-        self.fid = -1 # feature id
-        
-        # get layer/table/column information
-        self.mapDBInfo = VectorDBInfo(self.map)
-        
-        layers = self.mapDBInfo.layers.keys() # get available layers
-
-        # check if db connection / layer exists
-        if len(layers) <= 0:
-            label = _("Database connection "
-                      "is not defined in DB file.")
-
-            wx.MessageBox(parent=self.parent,
-                          message=_("No attribute table linked to "
-                                    "vector map <%(vector)s> found. %(msg)s"
-                                    "\nYou can disable this message from digitization settings. Or "
-                                    "you can create and link attribute table to the vector map "
-                                    "using Attribute Table Manager.") % 
-                          {'vector' : self.map, 'msg' : label},
-                          caption=_("Message"), style=wx.OK | wx.ICON_EXCLAMATION | wx.CENTRE)
-            self.mapDBInfo = None
-
-        wx.Dialog.__init__(self, parent=self.parent, id=wx.ID_ANY,
-                           title="", style=style, pos=pos)
-
-        # dialog body
-        mainSizer = wx.BoxSizer(wx.VERTICAL)
-
-        # notebook
-        self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
-
-        self.closeDialog = wx.CheckBox(parent=self, id=wx.ID_ANY,
-                                       label=_("Close dialog on submit"))
-        self.closeDialog.SetValue(True)
-
-        # feature id (text/choice for duplicates)
-        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY,
-                                  size=(150, -1))
-        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
-        self.fidText = wx.StaticText(parent=self, id=wx.ID_ANY)
-
-        self.noFoundMsg = wx.StaticText(parent=self, id=wx.ID_ANY,
-                                        label=_("No attributes found"))
-        
-        self.UpdateDialog(query=query, cats=cats)
-
-        # set title
-        if self.action == "update":
-            self.SetTitle(_("Update attributes"))
-        elif self.action == "add":
-            self.SetTitle(_("Add attributes"))
-        else:
-            self.SetTitle(_("Display attributes"))
-
-        # buttons
-        btnCancel = wx.Button(self, wx.ID_CANCEL)
-        btnReset  = wx.Button(self, wx.ID_UNDO, _("&Reload"))
-        btnSubmit = wx.Button(self, wx.ID_OK, _("&Submit"))
-
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(btnCancel)
-        btnSizer.AddButton(btnReset)
-        btnSizer.SetNegativeButton(btnReset)
-        btnSubmit.SetDefault()
-        btnSizer.AddButton(btnSubmit)
-        btnSizer.Realize()
-
-        mainSizer.Add(item=self.noFoundMsg, proportion=0,
-                      flag=wx.EXPAND | wx.ALL, border=5)
-        mainSizer.Add(item=self.notebook, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
-        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
-        fidSizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY,
-                                        label=_("Feature id:")),
-                     proportion=0, border=5,
-                     flag=wx.ALIGN_CENTER_VERTICAL)
-        fidSizer.Add(item=self.fidMulti, proportion=0,
-                     flag=wx.EXPAND | wx.ALL,  border=5)
-        fidSizer.Add(item=self.fidText, proportion=0,
-                     flag=wx.EXPAND | wx.ALL,  border=5)
-        mainSizer.Add(item=fidSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
-        mainSizer.Add(item=self.closeDialog, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
-                      border=5)
-        mainSizer.Add(item=btnSizer, proportion=0,
-                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
-
-        # bindigs
-        btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
-        btnSubmit.Bind(wx.EVT_BUTTON, self.OnSubmit)
-        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
-
-        self.SetSizer(mainSizer)
-        mainSizer.Fit(self)
-
-        # set min size for dialog
-        w, h = self.GetBestSize()
-        if h < 200:
-            self.SetMinSize((w, 200))
-        else:
-            self.SetMinSize(self.GetBestSize())
-
-        if self.notebook.GetPageCount() == 0:
-            Debug.msg(2, "DisplayAttributesDialog(): Nothing found!")
-            ### self.mapDBInfo = None
-
-    def __SelectAttributes(self, layer):
-        """Select attributes"""
-        pass
-
-    def OnSQLStatement(self, event):
-        """Update SQL statement"""
-        pass
-
-    def GetSQLString(self, updateValues=False):
-        """Create SQL statement string based on self.sqlStatement
-
-        If updateValues is True, update dataFrame according to values
-        in textfields.
-        """
-        sqlCommands = []
-        # find updated values for each layer/category
-        for layer in self.mapDBInfo.layers.keys(): # for each layer
-            table = self.mapDBInfo.layers[layer]["table"]
-            key = self.mapDBInfo.layers[layer]["key"]
-            columns = self.mapDBInfo.tables[table]
-            for idx in range(len(columns[key]['values'])): # for each category
-                updatedColumns = []
-                updatedValues = []
-                for name in columns.keys():
-                    if name == key:
-                        cat = columns[name]['values'][idx]
-                        continue
-                    type  = columns[name]['type']
-                    value = columns[name]['values'][idx]
-                    id    = columns[name]['ids'][idx]
-                    try:
-                        newvalue = self.FindWindowById(id).GetValue()
-                    except:
-                        newvalue = self.FindWindowById(id).GetLabel()
-
-                    if newvalue == '':
-                        newvalue = None
-                    
-                    if newvalue != value:
-                        updatedColumns.append(name)
-                        if newvalue is None:
-                            updatedValues.append('NULL')
-                        else:
-                            if type != 'character':
-                                updatedValues.append(newvalue)
-                            else:
-                                updatedValues.append("'" + newvalue + "'")
-                        columns[name]['values'][idx] = newvalue
-
-                if self.action != "add" and len(updatedValues) == 0:
-                    continue
-
-                if self.action == "add":
-                    sqlString = "INSERT INTO %s (%s," % (table, key)
-                else:
-                    sqlString = "UPDATE %s SET " % table
-
-                for idx in range(len(updatedColumns)):
-                    name = updatedColumns[idx]
-                    if self.action == "add":
-                        sqlString += name + ","
-                    else:
-                        sqlString += name + "=" + updatedValues[idx] + ","
-
-                sqlString = sqlString[:-1] # remove last comma
-
-                if self.action == "add":
-                    sqlString += ") VALUES (%s," % cat
-                    for value in updatedValues:
-                        sqlString += str(value) + ","
-                    sqlString = sqlString[:-1] # remove last comma
-                    sqlString += ")"
-                else:
-                    sqlString += " WHERE cat=%s" % cat
-                sqlCommands.append(sqlString)
-            # for each category
-        # for each layer END
-
-        Debug.msg(3, "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)
-
-        return sqlCommands
-
-    def OnReset(self, event):
-        """Reset form"""
-        for layer in self.mapDBInfo.layers.keys():
-            table = self.mapDBInfo.layers[layer]["table"]
-            key = self.mapDBInfo.layers[layer]["key"]
-            columns = self.mapDBInfo.tables[table]
-            for idx in range(len(columns[key]['values'])):
-                for name in columns.keys():
-                    type  = columns[name]['type']
-                    value = columns[name]['values'][idx]
-                    try:
-                        id = columns[name]['ids'][idx]
-                    except IndexError:
-                        id = wx.NOT_FOUND
-                    
-                    if name != key and id != wx.NOT_FOUND:
-                        self.FindWindowById(id).SetValue(str(value))
-
-    def OnCancel(self, event):
-        """Cancel button pressed"""
-        self.parent.parent.dialogs['attributes'] = None
-        if self.parent.parent.digit:
-            self.parent.parent.digit.driver.SetSelected([])
-            self.parent.UpdateMap(render=False)
-        else:
-            self.parent.parent.OnRender(None)
-
-        self.Close()
-
-    def OnSubmit(self, event):
-        """Submit records"""
-        for sql in self.GetSQLString(updateValues=True):
-            enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
-            if not enc and \
-                    os.environ.has_key('GRASS_DB_ENCODING'):
-                enc = os.environ['GRASS_DB_ENCODING']
-            if enc:
-                sql = sql.encode(enc)
-            
-            gcmd.Command(cmd=["db.execute",
-                              "--q"],
-                         stdin=sql)
-            
-        if self.closeDialog.IsChecked():
-            self.OnCancel(event)
-
-    def OnFeature(self, event):
-        self.fid = int(event.GetString())
-        self.UpdateDialog(cats=self.cats, fid=self.fid)
-        
-    def GetCats(self):
-        """Get id of selected vector object or 'None' if nothing selected
-
-        @param id if true return ids otherwise cats
-        """
-        if self.fid < 0:
-            return None
-        
-        return self.cats[self.fid]
-
-    def GetFid(self):
-        """Get selected feature id"""
-        return self.fid
-    
-    def UpdateDialog(self, map=None, query=None, cats=None, fid=-1):
-        """Update dialog
-
-        Return True if updated otherwise False
-        """
-        if map:
-            self.map = map
-            # get layer/table/column information
-            self.mapDBInfo = VectorDBInfo(self.map)
-        
-        if not self.mapDBInfo:
-            return False
-
-        self.mapDBInfo.Reset()
-
-        layers = self.mapDBInfo.layers.keys() # get available layers
-
-        # id of selected line
-        if query: # select by position
-            data = self.mapDBInfo.SelectByPoint(query[0],
-                                                query[1])
-            self.cats = {}
-            if data and data.has_key('Layer'):
-                idx = 0
-                for layer in data['Layer']:
-                    layer = int(layer)
-                    if data.has_key('Id'):
-                        tfid = int(data['Id'][idx])
-                    else:
-                        tfid = 0 # Area / Volume
-                    if not self.cats.has_key(tfid):
-                        self.cats[tfid] = {}
-                    if not self.cats[tfid].has_key(layer):
-                        self.cats[tfid][layer] = []
-                    cat = int(data['Category'][idx])
-                    self.cats[tfid][layer].append(cat)
-                    idx += 1
-        else:
-            self.cats = cats
-
-        if fid > 0:
-            self.fid = fid
-        elif len(self.cats.keys()) > 0:
-            self.fid = self.cats.keys()[0]
-        else:
-            self.fid = -1
-        
-        if len(self.cats.keys()) == 1:
-            self.fidMulti.Show(False)
-            self.fidText.Show(True)
-            if self.fid > 0:
-                self.fidText.SetLabel("%d" % self.fid)
-            else:
-                self.fidText.SetLabel(_("Unknown"))
-        else:
-            self.fidMulti.Show(True)
-            self.fidText.Show(False)
-            choices = []
-            for tfid in self.cats.keys():
-                choices.append(str(tfid))
-            self.fidMulti.SetItems(choices)
-            self.fidMulti.SetStringSelection(str(self.fid))
-        
-        # reset notebook
-        self.notebook.DeleteAllPages()
-
-        for layer in layers: # for each layer
-            if not query: # select by layer/cat
-                if self.fid > 0 and self.cats[self.fid].has_key(layer): 
-                    for cat in self.cats[self.fid][layer]:
-                        nselected = self.mapDBInfo.SelectFromTable(layer,
-                                                                   where="%s=%d" % \
-                                                                   (self.mapDBInfo.layers[layer]['key'],
-                                                                    cat))
-                else:
-                    nselected = 0
-
-            # if nselected <= 0 and self.action != "add":
-            #    continue # nothing selected ...
-
-            if self.action == "add":
-                if nselected <= 0:
-                    if self.cats[self.fid].has_key(layer):
-                        table = self.mapDBInfo.layers[layer]["table"]
-                        key = self.mapDBInfo.layers[layer]["key"]
-                        columns = self.mapDBInfo.tables[table]
-                        for name in columns.keys():
-                            if name == key:
-                                for cat in self.cats[self.fid][layer]:
-                                    self.mapDBInfo.tables[table][name]['values'].append(cat)
-                            else:
-                                self.mapDBInfo.tables[table][name]['values'].append(None)
-                else: # change status 'add' -> 'update'
-                    self.action = "update"
-            
-            table   = self.mapDBInfo.layers[layer]["table"]
-            key   = self.mapDBInfo.layers[layer]["key"]
-            columns = self.mapDBInfo.tables[table]
-            
-            for idx in range(len(columns[key]['values'])):
-                for name in columns.keys():
-                    if name == key:
-                        cat = int(columns[name]['values'][idx])
-                        break
-
-                # use scrolled panel instead (and fix initial max height of the window to 480px)
-                panel = scrolled.ScrolledPanel(parent=self.notebook, id=wx.ID_ANY,
-                                               size=(-1, 150))
-                panel.SetupScrolling(scroll_x=False)
-                
-                self.notebook.AddPage(page=panel, text=" %s %d / %s %d" % (_("Layer"), layer,
-                                                                           _("Category"), cat))
-           
-                # notebook body
-                border = wx.BoxSizer(wx.VERTICAL)
-
-                flexSizer = wx.FlexGridSizer (cols=4, hgap=3, vgap=3)
-                flexSizer.AddGrowableCol(3)
-                # columns (sorted by index)
-                names = [''] * len(columns.keys())
-                for name in columns.keys():
-                    names[columns[name]['index']] = name
-                
-                for name in names:
-                    if name == key: # skip key column (category)
-                        continue
-                    
-                    vtype  = columns[name]['type']
-                    
-                    if columns[name]['values'][idx] is not None:
-                        if columns[name]['ctype'] != type(''):
-                            value = str(columns[name]['values'][idx])
-                        else:
-                            value = columns[name]['values'][idx]
-                    else:
-                        value = ''
-                        
-                    colName = wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                            label=name)
-                    colType = wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                            label="[" + vtype.lower() + "]")
-                    delimiter = wx.StaticText(parent=panel, id=wx.ID_ANY, label=":")
-                    
-                    colValue = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value=value)
-                    
-                    colValue.SetName(name)
-                    self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
-                    
-                    flexSizer.Add(colName, proportion=0,
-                                  flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
-                    flexSizer.Add(colType, proportion=0,
-                                  flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
-                    flexSizer.Add(delimiter, proportion=0,
-                                  flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
-                    flexSizer.Add(colValue, proportion=1,
-                                  flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
-                    # add widget reference to self.columns
-                    columns[name]['ids'].append(colValue.GetId()) # name, type, values, id
-                # for each attribute (including category) END
-                border.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
-                panel.SetSizer(border)
-            # for each category END
-        # for each layer END
-
-        if self.notebook.GetPageCount() == 0:
-            self.noFoundMsg.Show(True)
-        else:
-            self.noFoundMsg.Show(False)
-        
-        self.Layout()
-        
-        return True
-        
-class VectorDBInfo(gselect.VectorDBInfo):
-    """Class providing information about attribute tables
-    linked to the vector map"""
-    def __init__(self, map):
-        gselect.VectorDBInfo.__init__(self, map)
-        
-    def GetColumns(self, table):
-        """Return list of columns names (based on their index)"""
-        try:
-            names = [''] * len(self.tables[table].keys())
-        except KeyError:
-            return []
-        
-        for name, desc in self.tables[table].iteritems():
-            names[desc['index']] = name
-
-        return names
-
-    def SelectByPoint(self, queryCoords, qdist):
-        """Get attributes by coordinates (all available layers)
-
-        Return line id or None if no line is found"""
-        line = None
-        nselected = 0
-        if os.environ.has_key("LC_ALL"):
-            locale = os.environ["LC_ALL"]
-            os.environ["LC_ALL"] = "C"
-        
-        ### FIXME (implement script-style output)
-        cmdWhat = gcmd.Command(cmd=['v.what',
-                                   '-a', '--q',
-                                    'map=%s' % self.map,
-                                    'east_north=%f,%f' % \
-                                        (float(queryCoords[0]), float(queryCoords[1])),
-                                    'distance=%f' % qdist], stderr=None)
-        
-        if os.environ.has_key("LC_ALL"):
-            os.environ["LC_ALL"] = locale
-        
-        data = {}
-        if cmdWhat.returncode == 0:
-            readAttrb = False
-            for item in cmdWhat.ReadStdOutput():
-                if len(item) < 1:
-                    continue
-                
-                key, value = item.split(':', 1)
-                
-                if key == 'Layer' and readAttrb:
-                    readAttrb = False
-                
-                if readAttrb:
-                    name, value = item.split(':', 1)
-                    name = name.strip()
-                    # append value to the column
-                    if len(value) < 1:
-                        value = None
-                    else:
-                        if self.tables[table][name]['ctype'] != type(''):
-                            value = self.tables[table][name]['ctype'] (value.strip())
-                        else:
-                            value = unicodeValue(value.strip())
-                    self.tables[table][name]['values'].append(value)
-                else:
-                    if not data.has_key(key):
-                        data[key] = []
-                    data[key].append(value.strip())
-                    
-                    if key == 'Table':
-                        table = value.strip()
-                        
-                    if key == 'Key column': # skip attributes
-                        readAttrb = True
-
-        return data
-    
-    def SelectFromTable(self, layer, cols='*', where=None):
-        """Select records from the table
-
-        Return number of selected records, -1 on error
-        """
-        if layer <= 0:
-            return -1
-
-        nselected = 0
-
-        table = self.layers[layer]["table"] # get table desc
-        # select values (only one record)
-        if where is None or where is '':
-            sql="SELECT %s FROM %s" % (cols, table)
-        else:
-            sql="SELECT %s FROM %s WHERE %s" % (cols, table, where)
-
-        selectCommand = gcmd.Command(["db.select", "-v", "--q",
-                                      "sql=%s" % sql,
-                                      "database=%s" % self.layers[layer]["database"],
-                                      "driver=%s"   % self.layers[layer]["driver"]])
-
-        # self.tables[table][key][1] = str(cat)
-        if selectCommand.returncode == 0:
-            for line in selectCommand.ReadStdOutput():
-                name, value = line.split('|')
-                # casting ...
-                if value:
-                    if self.tables[table][name]['ctype'] != type(''):
-                        value = self.tables[table][name]['ctype'] (value)
-                    else:
-                        value = unicodeValue(value)
-                else:
-                    value = None
-                self.tables[table][name]['values'].append(value)
-                nselected = 1
-
-        return nselected
-
-class ModifyTableRecord(wx.Dialog):
-    """Dialog for inserting/updating table record"""
-    def __init__(self, parent, id, title, data, keyEditable=(-1, True),
-                style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
-        """
-        Notes:
-         'Data' is a list: [(column, value)]
-         'KeyEditable' (id, editable?) indicates if textarea for key column
-          is editable(True) or not.
-        """
-        wx.Dialog.__init__(self, parent, id, title, style=style)
-
-        self.CenterOnParent()
-        
-        self.keyId = keyEditable[0]
-        
-        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
-        
-        box = wx.StaticBox(parent=self.panel, id=wx.ID_ANY, label='')
-
-        self.dataPanel = scrolled.ScrolledPanel(parent=self.panel, id=wx.ID_ANY,
-                                            style=wx.TAB_TRAVERSAL)
-        self.dataPanel.SetupScrolling(scroll_x=False)
-        
-        #
-        # buttons
-        #
-        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
-        self.btnSubmit = wx.Button(self.panel, wx.ID_OK, _("Submit"))
-        self.btnSubmit.SetDefault()
-
-        #
-        # data area
-        #
-        self.widgets = []
-        id = 0
-        self.usebox = False
-        self.cat = None
-        for column, value in data:
-            if keyEditable[0] == id:
-                self.cat = int(value)
-                if keyEditable[1] == False:
-                    self.usebox = True
-                    box.SetLabel =" %s %d " % (_("Category"), self.cat)
-                    self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-                    id += 1
-                    continue
-                else:
-                    valueWin = wx.SpinCtrl(parent=self.dataPanel, id=wx.ID_ANY,
-                                           value=value, min=-1e9, max=1e9, size=(250, -1))
-            else:
-                valueWin = wx.TextCtrl(parent=self.dataPanel, id=wx.ID_ANY,
-                                       value=value, size=(250, -1))
-                                
-            label = wx.StaticText(parent=self.dataPanel, id=wx.ID_ANY,
-                                  label=column + ":")
-
-            self.widgets.append((label.GetId(),
-                                 valueWin.GetId()))
-
-            id += 1
-            
-        self.__Layout()
-
-        # winSize = self.GetSize()
-        # fix height of window frame if needed
-        # if winSize[1] > 480:
-        #    winSize[1] = 480
-        #    self.SetSize(winSize)
-        # self.SetMinSize(winSize)
-
-    def __Layout(self):
-        """Do layout"""
-        sizer = wx.BoxSizer(wx.VERTICAL)
-
-        # data area
-        dataSizer = wx.FlexGridSizer (cols=2, hgap=3, vgap=3)
-        dataSizer.AddGrowableCol(1)
-
-        for labelId, valueId in self.widgets:
-            label = self.FindWindowById(labelId)
-            value = self.FindWindowById(valueId)
-
-            dataSizer.Add(label, proportion=0,
-                          flag=wx.ALIGN_CENTER_VERTICAL)
-            dataSizer.Add(value, proportion=0,
-                          flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
-
-        self.dataPanel.SetAutoLayout(True)
-        self.dataPanel.SetSizer(dataSizer)
-        dataSizer.Fit(self.dataPanel)
-
-        if self.usebox:
-            self.boxSizer.Add(item=self.dataPanel, proportion=1,
-                         flag=wx.EXPAND | wx.ALL, border=5)
-            
-        # buttons
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(self.btnCancel)
-        btnSizer.AddButton(self.btnSubmit)
-        btnSizer.Realize()
-
-        if not self.usebox:
-            sizer.Add(item=self.dataPanel, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
-        else:
-            sizer.Add(item=self.boxSizer, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
-            
-
-        sizer.Add(item=btnSizer, proportion=0,
-                 flag=wx.EXPAND | wx.ALL, border=5)
-
-        framewidth = self.GetSize()[0]
-        self.SetMinSize((framewidth,150))
-        self.SetMaxSize((framewidth,300))
-
-        #sizer.SetSizeHints(self.panel)
-        self.panel.SetAutoLayout(True)
-        self.panel.SetSizer(sizer)
-        sizer.Fit(self.panel)
-
-        self.Layout()
-
-#        # set window frame size (min & max)
-#        minFrameHeight = 150
-#        maxFrameHeight = 2 * minFrameHeight
-#        if self.GetSize()[1] > minFrameHeight:
-#            self.SetMinSize((self.GetSize()[0], minFrameHeight))
-#        else:
-#            self.SetMinSize(self.GetSize())
-
-#        if self.GetSize()[1] > maxFrameHeight:
-#            self.SetSize((self.GetSize()[0], maxFrameHeight))
-#        else:
-#            self.SetSize(self.panel.GetSize())
-            
-    def GetValues(self, columns=None):
-        """Return list of values (casted to string).
-
-        If columns is given (list), return only values of given columns.
-        """
-        valueList = []
-        for labelId, valueId in self.widgets:
-            column = self.FindWindowById(labelId).GetLabel().replace(':', '')
-            if columns is None or column in columns:
-                value = str(self.FindWindowById(valueId).GetValue())
-                valueList.append(value)
-
-        # add key value
-        if self.usebox:
-            valueList.insert(self.keyId, str(self.cat))
-                             
-        return valueList
-
 def main(argv=None):
     if argv is None:
         argv = sys.argv
@@ -3669,7 +3076,7 @@
     f = AttributeManager(parent=None, id=wx.ID_ANY,
                          title="%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
                                               argv[1]),
-                         size=(900,600), vectmap=argv[1])
+                         size=(900,600), vectorName=argv[1])
     f.Show()
 
     app.MainLoop()

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_base.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_base.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_base.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,186 @@
+"""
+ at package dbm_base.py
+
+ at brief Support classes for dbm.py
+
+List of classes:
+ - VectorDBInfo
+
+(C) 2007-2009 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 os
+
+import wx
+
+import gselect
+import gcmd
+from preferences import globalSettings as UserSettings
+
+def unicodeValue(value):
+    """!Encode value"""
+    enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
+    if enc:
+        value = unicode(value, enc)
+    elif os.environ.has_key('GRASS_DB_ENCODING'):
+        value = unicode(value, os.environ['GRASS_DB_ENCODING'])
+    else:
+        try:
+            value = unicode(value, 'ascii')
+        except UnicodeDecodeError:
+            value = _("Unable to decode value. Set encoding in GUI preferences ('Attributes').")
+    
+    return value
+
+def createDbInfoDesc(panel, mapDBInfo, layer):
+    """!Create database connection information content"""
+    infoFlexSizer = wx.FlexGridSizer (cols=2, hgap=1, vgap=1)
+    infoFlexSizer.AddGrowableCol(1)
+    
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label="Driver:"))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=mapDBInfo.layers[layer]['driver']))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label="Database:"))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=mapDBInfo.layers[layer]['database']))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label="Table:"))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=mapDBInfo.layers[layer]['table']))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label="Key:"))
+    infoFlexSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=mapDBInfo.layers[layer]['key']))
+    
+    return infoFlexSizer
+        
+class VectorDBInfo(gselect.VectorDBInfo):
+    """!Class providing information about attribute tables
+    linked to the vector map"""
+    def __init__(self, map):
+        gselect.VectorDBInfo.__init__(self, map)
+        
+    def GetColumns(self, table):
+        """!Return list of columns names (based on their index)"""
+        try:
+            names = [''] * len(self.tables[table].keys())
+        except KeyError:
+            return []
+        
+        for name, desc in self.tables[table].iteritems():
+            names[desc['index']] = name
+        
+        return names
+
+    def SelectByPoint(self, queryCoords, qdist):
+        """!Get attributes by coordinates (all available layers)
+
+        Return line id or None if no line is found"""
+        line = None
+        nselected = 0
+        
+        if os.environ.has_key("LC_ALL"):
+            locale = os.environ["LC_ALL"]
+            os.environ["LC_ALL"] = "C"
+        
+        ### FIXME (implement script-style output)        
+        ret = gcmd.RunCommand('v.what',
+                              quiet = True,
+                              read = True,
+                              flags = 'a',
+                              map = self.map,
+                              east_north = '%f,%f' % \
+                                  (float(queryCoords[0]), float(queryCoords[1])),
+                              distance = float(qdist))
+        
+        if os.environ.has_key("LC_ALL"):
+            os.environ["LC_ALL"] = locale
+        
+        data = {}
+        if ret:
+            readAttrb = False
+            for item in ret.splitlines():
+                try:
+                    key, value = item.split(':', 1)
+                except ValueError:
+                    continue
+                
+                if key == 'Layer' and readAttrb:
+                    readAttrb = False
+                
+                if readAttrb:
+                    name, value = item.split(':', 1)
+                    name = name.strip()
+                    value = value.strip()
+                    # append value to the column
+                    if len(value) < 1:
+                        value = None
+                    else:
+                        if self.tables[table][name]['ctype'] != type(''):
+                            value = self.tables[table][name]['ctype'] (value.strip())
+                        else:
+                            value = unicodeValue(value.strip())
+                    self.tables[table][name]['values'].append(value)
+                else:
+                    if not data.has_key(key):
+                        data[key] = []
+                    data[key].append(value.strip())
+                    
+                    if key == 'Table':
+                        table = value.strip()
+                        
+                    if key == 'Key column': # skip attributes
+                        readAttrb = True
+
+        return data
+    
+    def SelectFromTable(self, layer, cols='*', where=None):
+        """!Select records from the table
+
+        Return number of selected records, -1 on error
+        """
+        if layer <= 0:
+            return -1
+
+        nselected = 0
+
+        table = self.layers[layer]["table"] # get table desc
+        # select values (only one record)
+        if where is None or where is '':
+            sql="SELECT %s FROM %s" % (cols, table)
+        else:
+            sql="SELECT %s FROM %s WHERE %s" % (cols, table, where)
+
+        ret = gcmd.RunCommand('db.select',
+                              parent = self,
+                              read = True,
+                              quiet = True,
+                              flags = 'v',
+                              sql= sql,
+                              database = self.layers[layer]["database"],
+                              driver = self.layers[layer]["driver"])
+        
+        # self.tables[table][key][1] = str(cat)
+        if ret:
+            for line in ret.splitlines():
+                name, value = line.split('|')
+                # casting ...
+                if value:
+                    if self.tables[table][name]['ctype'] != type(''):
+                        value = self.tables[table][name]['ctype'] (value)
+                    else:
+                        value = unicodeValue(value)
+                else:
+                    value = None
+                self.tables[table][name]['values'].append(value)
+                nselected = 1
+
+        return nselected


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_base.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_dialogs.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,663 @@
+"""
+ at package dbm_dialogs.py
+
+ at brief DBM-related dialogs
+
+List of classes:
+ - DisplayAttributesDialog
+ - ModifyTableRecord
+
+(C) 2007-2009 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 os
+
+import globalvar
+if not os.getenv("GRASS_WXBUNDLED"):
+    globalvar.CheckForWx()
+
+import wx
+import wx.lib.scrolledpanel as scrolled
+
+import gcmd
+from debug import Debug
+from preferences import globalSettings as UserSettings
+from dbm_base    import VectorDBInfo
+
+class DisplayAttributesDialog(wx.Dialog):
+    """
+    Standard dialog used to add/update/display attributes linked
+    to the vector map.
+
+    Attribute data can be selected based on layer and category number
+    or coordinates.
+
+    @param parent
+    @param map vector map
+    @param query query coordinates and distance (used for v.edit)
+    @param cats {layer: cats}
+    @param line feature id (requested for cats)
+    @param style
+    @param pos
+    @param action (add, update, display)
+    """
+    def __init__(self, parent, map,
+                 query=None, cats=None, line=None,
+                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 pos=wx.DefaultPosition,
+                 action="add"):
+        self.parent = parent # mapdisplay.BufferedWindow
+        self.map    = map
+        self.action = action
+
+        # ids/cats of selected features
+        # fid : {layer : cats}
+        self.cats = {}
+        self.fid = -1 # feature id
+        
+        # get layer/table/column information
+        self.mapDBInfo = VectorDBInfo(self.map)
+        
+        layers = self.mapDBInfo.layers.keys() # get available layers
+
+        # check if db connection / layer exists
+        if len(layers) <= 0:
+            label = _("Database connection "
+                      "is not defined in DB file.")
+
+            wx.MessageBox(parent=self.parent,
+                          message=_("No attribute table linked to "
+                                    "vector map <%(vector)s> found. %(msg)s"
+                                    "\nYou can disable this message from digitization settings. Or "
+                                    "you can create and link attribute table to the vector map "
+                                    "using Attribute Table Manager.") % 
+                          {'vector' : self.map, 'msg' : label},
+                          caption=_("Message"), style=wx.OK | wx.ICON_EXCLAMATION | wx.CENTRE)
+            self.mapDBInfo = None
+
+        wx.Dialog.__init__(self, parent=self.parent, id=wx.ID_ANY,
+                           title="", style=style, pos=pos)
+
+        # dialog body
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+        # notebook
+        self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
+
+        self.closeDialog = wx.CheckBox(parent=self, id=wx.ID_ANY,
+                                       label=_("Close dialog on submit"))
+        self.closeDialog.SetValue(True)
+
+        # feature id (text/choice for duplicates)
+        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY,
+                                  size=(150, -1))
+        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
+        self.fidText = wx.StaticText(parent=self, id=wx.ID_ANY)
+
+        self.noFoundMsg = wx.StaticText(parent=self, id=wx.ID_ANY,
+                                        label=_("No attributes found"))
+        
+        self.UpdateDialog(query=query, cats=cats)
+
+        # set title
+        if self.action == "update":
+            self.SetTitle(_("Update attributes"))
+        elif self.action == "add":
+            self.SetTitle(_("Add attributes"))
+        else:
+            self.SetTitle(_("Display attributes"))
+
+        # buttons
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnReset  = wx.Button(self, wx.ID_UNDO, _("&Reload"))
+        btnSubmit = wx.Button(self, wx.ID_OK, _("&Submit"))
+
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnReset)
+        btnSizer.SetNegativeButton(btnReset)
+        btnSubmit.SetDefault()
+        btnSizer.AddButton(btnSubmit)
+        btnSizer.Realize()
+
+        mainSizer.Add(item=self.noFoundMsg, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(item=self.notebook, proportion=1,
+                      flag=wx.EXPAND | wx.ALL, border=5)
+        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
+        fidSizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY,
+                                        label=_("Feature id:")),
+                     proportion=0, border=5,
+                     flag=wx.ALIGN_CENTER_VERTICAL)
+        fidSizer.Add(item=self.fidMulti, proportion=0,
+                     flag=wx.EXPAND | wx.ALL,  border=5)
+        fidSizer.Add(item=self.fidText, proportion=0,
+                     flag=wx.EXPAND | wx.ALL,  border=5)
+        mainSizer.Add(item=fidSizer, proportion=0,
+                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
+        mainSizer.Add(item=self.closeDialog, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
+                      border=5)
+        mainSizer.Add(item=btnSizer, proportion=0,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+
+        # bindigs
+        btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
+        btnSubmit.Bind(wx.EVT_BUTTON, self.OnSubmit)
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+        # set min size for dialog
+        w, h = self.GetBestSize()
+        if h < 200:
+            self.SetMinSize((w, 200))
+        else:
+            self.SetMinSize(self.GetBestSize())
+
+        if self.notebook.GetPageCount() == 0:
+            Debug.msg(2, "DisplayAttributesDialog(): Nothing found!")
+            ### self.mapDBInfo = None
+
+    def __SelectAttributes(self, layer):
+        """!Select attributes"""
+        pass
+
+    def OnSQLStatement(self, event):
+        """!Update SQL statement"""
+        pass
+
+    def GetSQLString(self, updateValues=False):
+        """!Create SQL statement string based on self.sqlStatement
+
+        If updateValues is True, update dataFrame according to values
+        in textfields.
+        """
+        sqlCommands = []
+        # find updated values for each layer/category
+        for layer in self.mapDBInfo.layers.keys(): # for each layer
+            table = self.mapDBInfo.GetTable(layer)
+            key = self.mapDBInfo.GetKeyColumn(layer)
+            columns = self.mapDBInfo.GetTableDesc(table)
+            for idx in range(len(columns[key]['values'])): # for each category
+                updatedColumns = []
+                updatedValues = []
+                for name in columns.keys():
+                    if name == key:
+                        cat = columns[name]['values'][idx]
+                        continue
+                    type  = columns[name]['type']
+                    value = columns[name]['values'][idx]
+                    id    = columns[name]['ids'][idx]
+                    try:
+                        newvalue = self.FindWindowById(id).GetValue()
+                    except:
+                        newvalue = self.FindWindowById(id).GetLabel()
+
+                    if newvalue == '':
+                        newvalue = None
+                    
+                    if newvalue != value:
+                        updatedColumns.append(name)
+                        if newvalue is None:
+                            updatedValues.append('NULL')
+                        else:
+                            if type != 'character':
+                                updatedValues.append(newvalue)
+                            else:
+                                updatedValues.append("'" + newvalue + "'")
+                        columns[name]['values'][idx] = newvalue
+
+                if self.action != "add" and len(updatedValues) == 0:
+                    continue
+
+                if self.action == "add":
+                    sqlString = "INSERT INTO %s (%s," % (table, key)
+                else:
+                    sqlString = "UPDATE %s SET " % table
+
+                for idx in range(len(updatedColumns)):
+                    name = updatedColumns[idx]
+                    if self.action == "add":
+                        sqlString += name + ","
+                    else:
+                        sqlString += name + "=" + updatedValues[idx] + ","
+
+                sqlString = sqlString[:-1] # remove last comma
+
+                if self.action == "add":
+                    sqlString += ") VALUES (%s," % cat
+                    for value in updatedValues:
+                        sqlString += str(value) + ","
+                    sqlString = sqlString[:-1] # remove last comma
+                    sqlString += ")"
+                else:
+                    sqlString += " WHERE cat=%s" % cat
+                sqlCommands.append(sqlString)
+            # for each category
+        # for each layer END
+
+        Debug.msg(3, "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)
+
+        return sqlCommands
+
+    def OnReset(self, event = None):
+        """!Reset form"""
+        for layer in self.mapDBInfo.layers.keys():
+            table = self.mapDBInfo.layers[layer]["table"]
+            key = self.mapDBInfo.layers[layer]["key"]
+            columns = self.mapDBInfo.tables[table]
+            for idx in range(len(columns[key]['values'])):
+                for name in columns.keys():
+                    type  = columns[name]['type']
+                    value = columns[name]['values'][idx]
+                    if value is None:
+                        value = ''
+                    try:
+                        id = columns[name]['ids'][idx]
+                    except IndexError:
+                        id = wx.NOT_FOUND
+                    
+                    if name != key and id != wx.NOT_FOUND:
+                        self.FindWindowById(id).SetValue(str(value))
+
+    def OnCancel(self, event):
+        """!Cancel button pressed"""
+        self.parent.parent.dialogs['attributes'] = None
+        if self.parent.parent.digit:
+            self.parent.parent.digit.driver.SetSelected([])
+            self.parent.UpdateMap(render=False)
+        else:
+            self.parent.parent.OnRender(None)
+
+        self.Close()
+
+    def OnSubmit(self, event):
+        """!Submit records"""
+        for sql in self.GetSQLString(updateValues=True):
+            enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
+            if not enc and \
+                    os.environ.has_key('GRASS_DB_ENCODING'):
+                enc = os.environ['GRASS_DB_ENCODING']
+            if enc:
+                sql = sql.encode(enc)
+            
+            gcmd.RunCommand('db.execute',
+                            quiet = True,
+                            stdin = sql)
+        
+        if self.closeDialog.IsChecked():
+            self.OnCancel(event)
+
+    def OnFeature(self, event):
+        self.fid = int(event.GetString())
+        self.UpdateDialog(cats=self.cats, fid=self.fid)
+        
+    def GetCats(self):
+        """!Get id of selected vector object or 'None' if nothing selected
+
+        @param id if true return ids otherwise cats
+        """
+        if self.fid < 0:
+            return None
+        
+        return self.cats[self.fid]
+
+    def GetFid(self):
+        """!Get selected feature id"""
+        return self.fid
+    
+    def UpdateDialog(self, map=None, query=None, cats=None, fid=-1):
+        """!Update dialog
+
+        Return True if updated otherwise False
+        """
+        if map:
+            self.map = map
+            # get layer/table/column information
+            self.mapDBInfo = VectorDBInfo(self.map)
+        
+        if not self.mapDBInfo:
+            return False
+
+        self.mapDBInfo.Reset()
+
+        layers = self.mapDBInfo.layers.keys() # get available layers
+
+        # id of selected line
+        if query: # select by position
+            data = self.mapDBInfo.SelectByPoint(query[0],
+                                                query[1])
+            self.cats = {}
+            if data and data.has_key('Layer'):
+                idx = 0
+                for layer in data['Layer']:
+                    layer = int(layer)
+                    if data.has_key('Id'):
+                        tfid = int(data['Id'][idx])
+                    else:
+                        tfid = 0 # Area / Volume
+                    if not self.cats.has_key(tfid):
+                        self.cats[tfid] = {}
+                    if not self.cats[tfid].has_key(layer):
+                        self.cats[tfid][layer] = []
+                    cat = int(data['Category'][idx])
+                    self.cats[tfid][layer].append(cat)
+                    idx += 1
+        else:
+            self.cats = cats
+
+        if fid > 0:
+            self.fid = fid
+        elif len(self.cats.keys()) > 0:
+            self.fid = self.cats.keys()[0]
+        else:
+            self.fid = -1
+        
+        if len(self.cats.keys()) == 1:
+            self.fidMulti.Show(False)
+            self.fidText.Show(True)
+            if self.fid > 0:
+                self.fidText.SetLabel("%d" % self.fid)
+            else:
+                self.fidText.SetLabel(_("Unknown"))
+        else:
+            self.fidMulti.Show(True)
+            self.fidText.Show(False)
+            choices = []
+            for tfid in self.cats.keys():
+                choices.append(str(tfid))
+            self.fidMulti.SetItems(choices)
+            self.fidMulti.SetStringSelection(str(self.fid))
+        
+        # reset notebook
+        self.notebook.DeleteAllPages()
+
+        for layer in layers: # for each layer
+            if not query: # select by layer/cat
+                if self.fid > 0 and self.cats[self.fid].has_key(layer): 
+                    for cat in self.cats[self.fid][layer]:
+                        nselected = self.mapDBInfo.SelectFromTable(layer,
+                                                                   where="%s=%d" % \
+                                                                   (self.mapDBInfo.layers[layer]['key'],
+                                                                    cat))
+                else:
+                    nselected = 0
+
+            # if nselected <= 0 and self.action != "add":
+            #    continue # nothing selected ...
+
+            if self.action == "add":
+                if nselected <= 0:
+                    if self.cats[self.fid].has_key(layer):
+                        table = self.mapDBInfo.layers[layer]["table"]
+                        key = self.mapDBInfo.layers[layer]["key"]
+                        columns = self.mapDBInfo.tables[table]
+                        for name in columns.keys():
+                            if name == key:
+                                for cat in self.cats[self.fid][layer]:
+                                    self.mapDBInfo.tables[table][name]['values'].append(cat)
+                            else:
+                                self.mapDBInfo.tables[table][name]['values'].append(None)
+                else: # change status 'add' -> 'update'
+                    self.action = "update"
+            
+            table   = self.mapDBInfo.layers[layer]["table"]
+            key   = self.mapDBInfo.layers[layer]["key"]
+            columns = self.mapDBInfo.tables[table]
+            
+            for idx in range(len(columns[key]['values'])):
+                for name in columns.keys():
+                    if name == key:
+                        cat = int(columns[name]['values'][idx])
+                        break
+
+                # use scrolled panel instead (and fix initial max height of the window to 480px)
+                panel = scrolled.ScrolledPanel(parent=self.notebook, id=wx.ID_ANY,
+                                               size=(-1, 150))
+                panel.SetupScrolling(scroll_x=False)
+                
+                self.notebook.AddPage(page=panel, text=" %s %d / %s %d" % (_("Layer"), layer,
+                                                                           _("Category"), cat))
+           
+                # notebook body
+                border = wx.BoxSizer(wx.VERTICAL)
+
+                flexSizer = wx.FlexGridSizer (cols=4, hgap=3, vgap=3)
+                flexSizer.AddGrowableCol(3)
+                # columns (sorted by index)
+                names = [''] * len(columns.keys())
+                for name in columns.keys():
+                    names[columns[name]['index']] = name
+                
+                for name in names:
+                    if name == key: # skip key column (category)
+                        continue
+                    
+                    vtype  = columns[name]['type']
+                    
+                    if columns[name]['values'][idx] is not None:
+                        if columns[name]['ctype'] != type(''):
+                            value = str(columns[name]['values'][idx])
+                        else:
+                            value = columns[name]['values'][idx]
+                    else:
+                        value = ''
+                        
+                    colName = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                            label=name)
+                    colType = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                            label="[" + vtype.lower() + "]")
+                    delimiter = wx.StaticText(parent=panel, id=wx.ID_ANY, label=":")
+                    
+                    colValue = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value=value)
+                    
+                    colValue.SetName(name)
+                    self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
+                    
+                    flexSizer.Add(colName, proportion=0,
+                                  flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+                    flexSizer.Add(colType, proportion=0,
+                                  flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+                    flexSizer.Add(delimiter, proportion=0,
+                                  flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+                    flexSizer.Add(colValue, proportion=1,
+                                  flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+                    # add widget reference to self.columns
+                    columns[name]['ids'].append(colValue.GetId()) # name, type, values, id
+                # for each attribute (including category) END
+                border.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+                panel.SetSizer(border)
+            # for each category END
+        # for each layer END
+
+        if self.notebook.GetPageCount() == 0:
+            self.noFoundMsg.Show(True)
+        else:
+            self.noFoundMsg.Show(False)
+        
+        self.Layout()
+        
+        return True
+
+    def SetColumnValue(self, layer, column, value):
+        """!Set attrbute value
+
+        @param column column name
+        @param value value
+        """
+        table = self.mapDBInfo.GetTable(layer)
+        columns = self.mapDBInfo.GetTableDesc(table)
+        
+        for key, col in columns.iteritems():
+            if key == column:
+                col['values'] = [col['ctype'](value),]
+                break
+        
+class ModifyTableRecord(wx.Dialog):
+    """!Dialog for inserting/updating table record"""
+    def __init__(self, parent, id, title, data, keyEditable=(-1, True),
+                style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        """
+        Notes:
+         'Data' is a list: [(column, value)]
+         'KeyEditable' (id, editable?) indicates if textarea for key column
+          is editable(True) or not.
+        """
+        wx.Dialog.__init__(self, parent, id, title, style=style)
+        
+        self.CenterOnParent()
+        
+        self.keyId = keyEditable[0]
+        
+        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
+
+        box = wx.StaticBox(parent=self.panel, id=wx.ID_ANY, label='')
+
+        self.dataPanel = scrolled.ScrolledPanel(parent=self.panel, id=wx.ID_ANY,
+                                            style=wx.TAB_TRAVERSAL)
+        self.dataPanel.SetupScrolling(scroll_x=False)
+        
+        #
+        # buttons
+        #
+        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+        self.btnSubmit = wx.Button(self.panel, wx.ID_OK, _("Submit"))
+        self.btnSubmit.SetDefault()
+
+        #
+        # data area
+        #
+        self.widgets = []
+        id = 0
+        self.usebox = False
+        self.cat = None
+        for column, value in data:
+            if keyEditable[0] == id:
+                self.cat = int(value)
+                if keyEditable[1] == False:
+                    self.usebox = True
+                    box.SetLabel =" %s %d " % (_("Category"), self.cat)
+                    self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+                    id += 1
+                    continue
+                else:
+                    valueWin = wx.SpinCtrl(parent=self.dataPanel, id=wx.ID_ANY,
+                                           value=value, min=-1e9, max=1e9, size=(250, -1))
+            else:
+                valueWin = wx.TextCtrl(parent=self.dataPanel, id=wx.ID_ANY,
+                                       value=value, size=(250, -1))
+                                
+            label = wx.StaticText(parent=self.dataPanel, id=wx.ID_ANY,
+                                  label=column + ":")
+
+            self.widgets.append((label.GetId(),
+                                 valueWin.GetId()))
+
+            id += 1
+            
+        self.__Layout()
+
+        # winSize = self.GetSize()
+        # fix height of window frame if needed
+        # if winSize[1] > 480:
+        #    winSize[1] = 480
+        #    self.SetSize(winSize)
+        # self.SetMinSize(winSize)
+
+    def __Layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        # data area
+        dataSizer = wx.FlexGridSizer (cols=2, hgap=3, vgap=3)
+        dataSizer.AddGrowableCol(1)
+
+        for labelId, valueId in self.widgets:
+            label = self.FindWindowById(labelId)
+            value = self.FindWindowById(valueId)
+
+            dataSizer.Add(label, proportion=0,
+                          flag=wx.ALIGN_CENTER_VERTICAL)
+            dataSizer.Add(value, proportion=0,
+                          flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+
+        self.dataPanel.SetAutoLayout(True)
+        self.dataPanel.SetSizer(dataSizer)
+        dataSizer.Fit(self.dataPanel)
+
+        if self.usebox:
+            self.boxSizer.Add(item=self.dataPanel, proportion=1,
+                         flag=wx.EXPAND | wx.ALL, border=5)
+            
+        # buttons
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnSubmit)
+        btnSizer.Realize()
+
+        if not self.usebox:
+            sizer.Add(item=self.dataPanel, proportion=1,
+                      flag=wx.EXPAND | wx.ALL, border=5)
+        else:
+            sizer.Add(item=self.boxSizer, proportion=1,
+                      flag=wx.EXPAND | wx.ALL, border=5)
+            
+
+        sizer.Add(item=btnSizer, proportion=0,
+                 flag=wx.EXPAND | wx.ALL, border=5)
+
+        framewidth = self.GetSize()[0]
+        self.SetMinSize((framewidth,150))
+        self.SetMaxSize((framewidth,300))
+        
+        #sizer.SetSizeHints(self.panel)
+        self.panel.SetAutoLayout(True)
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+
+        self.Layout()
+        
+#        # set window frame size (min & max)
+#        minFrameHeight = 150
+#        maxFrameHeight = 2 * minFrameHeight
+#        if self.GetSize()[1] > minFrameHeight:
+#            print 'size ='+str(self.GetSize()[1])
+#            print 'if 1'
+#            self.SetMinSize((self.GetSize()[0], minFrameHeight))
+#        else:
+#            print 'else 1'
+#            self.SetMinSize(self.GetSize())
+
+#        if self.GetSize()[1] > maxFrameHeight:
+#            print 'if 2'
+#            self.SetSize((self.GetSize()[0], maxFrameHeight))
+#        else:
+#            print 'else 2'
+#            self.SetSize(self.panel.GetSize())
+            
+
+                
+    def GetValues(self, columns=None):
+        """!Return list of values (casted to string).
+
+        If columns is given (list), return only values of given columns.
+        """
+        valueList = []
+        for labelId, valueId in self.widgets:
+            column = self.FindWindowById(labelId).GetLabel().replace(':', '')
+            if columns is None or column in columns:
+                value = str(self.FindWindowById(valueId).GetValue())
+                valueList.append(value)
+
+        # add key value
+        if self.usebox:
+            valueList.insert(self.keyId, str(self.cat))
+                             
+        return valueList


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/dbm_dialogs.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/debug.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/debug.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/debug.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,21 +1,21 @@
-"""
-MODULE: debug
+"""!
+ at package debug
 
-CLASSES:
- * DebugMsg
+ at brief Debugging
 
-PURPOSE: GRASS debugging
+Classes:
+ - DebugMsg
 
-         from debug import Debug as Debug
-         Debug.msg (3, 'debug message')
+ at code
+from debug import Debug as Debug
+Debug.msg (3, 'debug message')
+ at endcode
          
-AUTHORS: The GRASS Development Team
-         Martin Landa <landa.martin gmail.com>
+COPYRIGHT: (C) 2007-2009 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.
 
-COPYRIGHT: (C) 2007-2008 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 os
@@ -24,10 +24,12 @@
 import globalvar
 
 class DebugMsg:
-    """
-    GRASS Debugging
+    """!
+    wxGUI debugging
 
+    @code
     export GRASS_WX_DEBUG=[0-5]
+    @endcode
     """
     def __init__(self):
         # default level
@@ -46,24 +48,31 @@
                 
             if self.debuglevel != level:
                 self.debuglevel = level
-
-    def msg (self, level, message):
+        
+    def msg (self, level, message, *args):
         self._update_level()
         if self.debuglevel > 0 and level > 0 and level <= self.debuglevel:
-            print >> sys.stderr, "GUI D%d/%d: %s" % (level, self.debuglevel, message)
+            if args:
+                print >> sys.stderr, "GUI D%d/%d: " % (level, self.debuglevel) + \
+                    message % args
+            else:
+                print >> sys.stderr, "GUI D%d/%d: " % (level, self.debuglevel) + \
+                    message
             sys.stderr.flush() # force flush (required for MS Windows)
         
     def get_level(self):
-        """Return current GUI debug level"""
+        """!Return current GUI debug level"""
         return self.debuglevel
-
+    
 # Debug instance
 Debug = DebugMsg()
 
 # testing
 if __name__ == "__main__":
     import gcmd
-    gcmd.Command (cmd=["g.gisenv", "set=DEBUG=3"])
+    gcmd.RunCommand('g.gisenv',
+                    set = 'DEBUG=3')
                 
     for level in range (4):
         Debug.msg (level, "message level=%d" % level)
+    

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcmd.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,21 +1,21 @@
-"""
+"""!
 @package gcmd
 
- at brief GRASS command interface
+ at brief wxGUI command interface
 
 Classes:
+ - GError
+ - GWarning
+ - GMessage
  - GException
- - GStdError
- - CmdError
- - SettingsError
- - DigitError
- - DBMError
- - NvizError
  - Popen (from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554)
  - Command
  - CommandThread
 
-(C) 2007-2009 by the GRASS Development Team
+Functions:
+ - RunCommand
+
+(C) 2007-2008, 2010 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.
@@ -30,6 +30,7 @@
 import errno
 import signal
 import locale
+import traceback
 
 import wx
 
@@ -56,79 +57,58 @@
 import utils
 from debug import Debug as Debug
 
-class GException(Exception):
-    """Generic exception"""
-    def __init__(self, message, title=_("Error"), parent=None):
-        self.msg = message
-        self.parent = parent
-        self.title = title
+class GError:
+    def __init__(self, message, parent = None):
+        caption = _('Error')
+        style = wx.OK | wx.ICON_ERROR | wx.CENTRE
+        exc_type, exc_value, exc_traceback = sys.exc_info()
+        if exc_traceback:
+            exception = traceback.format_exc()
+            reason = exception.splitlines()[-1].split(':', 1)[-1].strip()
         
-    def Show(self):
-        dlg = wx.MessageDialog(parent=self.parent,
-                               caption=self.title,
-                               message=self.msg,
-                               style=wx.ICON_ERROR | wx.CENTRE)
-        dlg.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_error.ico'), wx.BITMAP_TYPE_ICO))
-        if self.parent:
-            dlg.CentreOnParent()
+        if Debug.get_level() > 0 and exc_traceback:
+            sys.stderr.write(exception)
+        
+        if exc_traceback:
+            wx.MessageBox(parent = parent,
+                          message = message + '\n\n%s: %s\n\n%s' % \
+                              (_('Reason'),
+                               reason, exception),
+                          caption = caption,
+                          style = style)
         else:
-            dlg.CentreOnScreen()
+            wx.MessageBox(parent = parent,
+                          message = message,
+                          caption = caption,
+                          style = style)
 
-        dlg.ShowModal()
+class GWarning:
+    def __init__(self, message, parent = None):
+        caption = _('Warning')
+        style = wx.OK | wx.ICON_WARNING | wx.CENTRE
+        wx.MessageBox(parent = parent,
+                      message = message,
+                      caption = caption,
+                      style = style)
         
-    def __str__(self):
-        self.Show()
-        
-        return ''
-    
-class GStdError(GException):
-    """Generic exception"""
+class GMessage:
+    def __init__(self, message, parent = None):
+        caption = _('Message')
+        style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE
+        wx.MessageBox(parent = parent,
+                      message = message,
+                      caption = caption,
+                      style = style)
 
-    def __init__(self, message, title=_("Error"), parent=None):
-        GException.__init__(self, message, title=title, parent=parent)
+class GException(Exception):
+    def __init__(self, value):
+        self.value = value
 
-class CmdError(GException):
-    """Exception used for GRASS commands.
+    def __str__(self):
+        return str(self.value)
 
-    See Command class (command exits with EXIT_FAILURE,
-    G_fatal_error() is called)."""
-    def __init__(self, cmd, message, parent=None):
-        self.cmd = cmd
-        GException.__init__(self, message,
-                            title=_("Error in command execution %s" % self.cmd[0]),
-                            parent=parent)
-
-class SettingsError(GException):
-    """Exception used for GRASS settings, see
-    gui_modules/preferences.py."""
-    def __init__(self, message, parent=None):
-        GException.__init__(self, message,
-                            title=_("Preferences error"),
-                            parent=parent)
-
-class DigitError(GException):
-    """Exception raised during digitization session"""
-    def __init__(self, message, parent=None):
-        GException.__init__(self, message,
-                            title=_("Vector digitizer error"),
-                            parent=parent)
-
-class DBMError(GException):
-    """Attribute Table Manager exception class"""
-    def __init__(self, message, parent=None):
-        GException.__init__(self, message,
-                            title=_("Attribute table manager error"),
-                            parent=parent)
-
-class NvizError(GException):
-    """Nviz exception class"""
-    def __init__(self, message, parent=None):
-        GException.__init__(self, message,
-                            title=_("Nviz error"),
-                            parent=parent)
-
 class Popen(subprocess.Popen):
-    """Subclass subprocess.Popen"""
+    """!Subclass subprocess.Popen"""
     def __init__(self, *args, **kwargs):
         if subprocess.mswindows:
             try:
@@ -162,7 +142,7 @@
         setattr(self, which, None)
 
     def kill(self):
-        """Try to kill running process"""
+        """!Try to kill running process"""
         if subprocess.mswindows:
             import win32api
             handle = win32api.OpenProcess(1, 0, self.pid)
@@ -287,9 +267,9 @@
         data = buffer(data, sent)
 
 class Command:
-    """
-    Run GRASS command in separate thread
-
+    """!Run command in separate thread. Used for commands launched
+    on the background.
+    
     If stdout/err is redirected, write() method is required for the
     given classes.
 
@@ -359,18 +339,17 @@
                            (' '.join(cmd), wait, self.returncode, self.cmdThread.isAlive()))
             if rerr is not None and self.returncode != 0:
                 if rerr is False: # GUI dialog
-                    raise CmdError(cmd=self.cmd,
-                                   message="%s '%s'%s%s%s %s%s" %
-                                   (_("Execution failed:"),
-                                    ' '.join(self.cmd),
-                                    os.linesep, os.linesep,
-                                    _("Details:"),
-                                    os.linesep,
-                                    _("Error: ") + self.GetError()))
+                    raise GException("%s '%s'%s%s%s %s%s" % \
+                                         (_("Execution failed:"),
+                                          ' '.join(self.cmd),
+                                          os.linesep, os.linesep,
+                                          _("Details:"),
+                                          os.linesep,
+                                          _("Error: ") + self.__GetError()))
                 elif rerr == sys.stderr: # redirect message to sys
                     stderr.write("Execution failed: '%s'" % (' '.join(self.cmd)))
                     stderr.write("%sDetails:%s%s" % (os.linesep,
-                                                     _("Error: ") + self.GetError(),
+                                                     _("Error: ") + self.__GetError(),
                                                      os.linesep))
             else:
                 pass # nop
@@ -384,7 +363,7 @@
             del os.environ["GRASS_VERBOSE"]
             
     def __ReadOutput(self, stream):
-        """Read stream and return list of lines
+        """!Read stream and return list of lines
 
         @param stream stream to be read
         """
@@ -402,18 +381,8 @@
 
         return lineList
                     
-    def ReadStdOutput(self):
-        """Read standard output and return list of lines"""
-        if self.cmdThread.stdout:
-            stream = self.cmdThread.stdout # use redirected stream instead
-            stream.seek(0)
-        else:
-            stream = self.cmdThread.module.stdout
-
-        return self.__ReadOutput(stream)
-    
-    def ReadErrOutput(self):
-        """Read standard error output and return list of lines"""
+    def __ReadErrOutput(self):
+        """!Read standard error output and return list of lines"""
         return self.__ReadOutput(self.cmdThread.module.stderr)
 
     def __ProcessStdErr(self):
@@ -423,7 +392,7 @@
         @return list of (type, message)
         """
         if self.stderr is None:
-            lines = self.ReadErrOutput()
+            lines = self.__ReadErrOutput()
         else:
             lines = self.cmdThread.error.strip('%s' % os.linesep). \
                 split('%s' % os.linesep)
@@ -452,8 +421,8 @@
 
         return msg
 
-    def GetError(self):
-        """Get error message or ''"""
+    def __GetError(self):
+        """!Get error message or ''"""
         if not self.cmdThread.module:
             return _("Unable to exectute command: '%s'") % ' '.join(self.cmd)
 
@@ -467,31 +436,9 @@
         
         return ''
     
-    def PrintModuleOutput(self, error=True, warning=False, message=False):
-        """Print module errors, warnings, messages to output
-
-        @param error print errors
-        @param warning print warnings
-        @param message print messages
-
-        @return string
-        """
-
-        msgString = ""
-        for type, msg in self.__ProcessStdErr():
-            if type:
-                if (type == 'ERROR' and error) or \
-                        (type == 'WARNING' and warning) or \
-                        (type == 'MESSAGE' and message):
-                    msgString += " " + type + ": " + msg + "%s" % os.linesep
-            else:
-                msgString += " " + msg + "%s" % os.linesep
-
-        return msgString
-
 class CommandThread(Thread):
-    """!Create separate thread for command
-    """
+    """!Create separate thread for command. Used for commands launched
+    on the background."""
     def __init__ (self, cmd, stdin=None,
                   stdout=sys.stdout, stderr=sys.stderr):
         """
@@ -526,6 +473,7 @@
             del os.environ["GRASS_MESSAGE_FORMAT"]
         
     def run(self):
+        """!Run command"""
         if len(self.cmd) == 0:
             return
 
@@ -549,7 +497,7 @@
             self.__redirect_stream()
 
     def __redirect_stream(self):
-        """Redirect stream"""
+        """!Redirect stream"""
         if self.stdout:
             # make module stdout/stderr non-blocking
             out_fileno = self.module.stdout.fileno()
@@ -590,12 +538,12 @@
                 self.error = line
             
     def abort(self):
-        """Abort running process, used by main thread to signal an abort"""
+        """!Abort running process, used by main thread to signal an abort"""
         self._want_abort = True
     
 def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = False,
-               parent = None, read = False, stdin = None, **kwargs):
-    """Run GRASS command"""
+               parent = None, read = False, stdin = None, getErrorMsg = False, **kwargs):
+    """!Run GRASS command"""
     Debug.msg(1, "gcmd.RunCommand(): %s" % ' '.join(grass.make_command(prog, flags, overwrite,
                                                                        quiet, verbose, **kwargs)))
     
@@ -615,16 +563,23 @@
         ps.stdin = None
     
     stdout, stderr = ps.communicate()
-
+    
     ret = ps.returncode
         
-    if ret != 0 and parent:
-        e = CmdError(cmd = prog,
-                     message = stderr,
-                     parent = parent)
-        e.Show()
-
+    if ret != 0 and parent: 
+        GError(parent = parent,
+               message = stderr)
+    
     if not read:
-        return ret
+        if not getErrorMsg:
+            return ret
+        else:
+            return ret, stderr
 
-    return stdout
+    if not getErrorMsg:
+        return stdout
+    
+    if read and getErrorMsg:
+        return ret, stdout, stderr
+    
+    return stdout, stderr

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmanager.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmanager.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmanager.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -50,7 +50,7 @@
 from location_wizard import TitledPage as TitledPage
 from preferences import globalSettings as UserSettings
 from gcpmapdisp import MapFrame
-from mapdisp import BufferedWindow
+from mapdisp_window import BufferedWindow
 
 try:
     import subprocess # Not needed if GRASS commands could actually be quiet
@@ -288,15 +288,9 @@
 
         if grc == 'target':
             os.environ['GISRC'] = str(self.target_gisrc)
-            gisrcfile = str(self.target_gisrc)
         elif grc == 'source':
             os.environ['GISRC'] = str(self.source_gisrc)
-            gisrcfile = str(self.source_gisrc)
 
-        # do it the hard way
-        gcmd.RunCommand('g.gisenv', 'set=LOCATION_NAME=%s' % self.newlocation)
-        gcmd.RunCommand('g.gisenv', 'set=MAPSET=%s' % self.newmapset)
-
         return True
     
     def OnWizFinished(self):
@@ -331,21 +325,7 @@
         self.xylocation = ''
         self.xymapset = ''
         
-        tmplist = os.listdir(self.grassdatabase)
-        self.locList = []
-        self.mapsetList = []
-        
         #
-        # create a list of valid locations
-        #
-        for item in tmplist:
-            if os.path.isdir(os.path.join(self.grassdatabase, item)) and \
-                    os.path.exists(os.path.join(self.grassdatabase, item, 'PERMANENT')):
-                self.locList.append(item)
-
-        utils.ListSortLower(self.locList)
-        
-        #
         # layout
         #
         self.sizer.AddGrowableCol(2)
@@ -362,9 +342,7 @@
         self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source location:')),
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 1))
-        self.cb_location = wx.ComboBox(parent=self, id=wx.ID_ANY, 
-                                     choices = self.locList, size=(300, -1),
-                                     style=wx.CB_DROPDOWN | wx.CB_READONLY)
+        self.cb_location = gselect.LocationSelect(parent = self, gisdbase = self.grassdatabase)
         self.sizer.Add(item=self.cb_location,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 2))
@@ -373,9 +351,8 @@
         self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source mapset:')),
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(3, 1))
-        self.cb_mapset = wx.ComboBox(parent=self, id=wx.ID_ANY,
-                                     choices = self.mapsetList, size=(300, -1),
-                                     style=wx.CB_DROPDOWN | wx.CB_READONLY)
+        self.cb_mapset = gselect.MapsetSelect(parent = self, gisdbase = self.grassdatabase,
+                                              setItems = False)
         self.sizer.Add(item=self.cb_mapset,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(3,2))
@@ -730,83 +707,13 @@
             return
 
         # filter out all maps not in group
-        # not available in 64
-        #self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
-        # modified from gselect
-        self.srcselection.tcp.seltree.DeleteAllItems()
-        # get current mapset
-        curr_mapset = grass.gisenv()['MAPSET']
-        mapsets = utils.ListOfMapsets()
-        for i in range(len(mapsets)):
-            if i > 0 and mapsets[i] == curr_mapset:
-                mapsets[i] = mapsets[0]
-                mapsets[0] = curr_mapset
-        
-        elementdict = {'cell':'rast',
-                       'raster':'rast',
-                       'rast':'rast',
-                       'raster files':'rast',
-                       'vector':'vect',
-                       'vect':'vect'
-                       }
-        if globalvar.have_mlist:
-            filesdict = grass.mlist_grouped(elementdict[maptype])
-        else:
-            filesdict = grass.list_grouped(elementdict[maptype])
-        
-        first_dir = None
-        for dir in mapsets:
-            dir_node = self.srcselection.tcp.AddItem('Mapset: ' + dir)
-            if not first_dir:
-                first_dir = dir_node
-            
-            self.srcselection.tcp.seltree.SetItemTextColour(dir_node, wx.Colour(50, 50, 200))
-            try:
-                elem_list = filesdict[dir]
-                elem_list.sort(key=str.lower)
-                for elem in elem_list:
-                    if elem != '':
-                        fullqElem = elem + '@' + dir
-                        if fullqElem in self.parent.src_maps:
-                            self.srcselection.tcp.AddItem(fullqElem, parent=dir_node)
-            except:
-                continue
-
-            if self.srcselection.tcp.seltree.ItemHasChildren(dir_node):
-                sel = UserSettings.Get(group='general', key='elementListExpand',
-                                       subkey='selection')
-                collapse = True
-
-                if sel == 0: # collapse all except PERMANENT and current
-                    if dir in ('PERMANENT', curr_mapset):
-                        collapse = False
-                elif sel == 1: # collapse all except PERMANENT
-                    if dir == 'PERMANENT':
-                        collapse = False
-                elif sel == 2: # collapse all except current
-                    if dir == curr_mapset:
-                        collapse = False
-                elif sel == 3: # collapse all
-                    pass
-                elif sel == 4: # expand all
-                    collapse = False
-                
-                if collapse:
-                    self.srcselection.tcp.seltree.Collapse(dir_node)
-                else:
-                    self.srcselection.tcp.seltree.Expand(dir_node)
-        
-        if first_dir:
-            # select first mapset (MSW hack)
-            self.srcselection.tcp.seltree.SelectItem(first_dir)
-
-        # end copy from gselect
+        self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
         src_map = self.parent.src_maps[0]
         self.srcselection.SetValue(src_map)
 
         self.parent.SwitchEnv('target')
         self.tgtselection.SetElementList(maptype)
-        #self.tgtselection.GetElementList()
+        self.tgtselection.GetElementList()
         self.parent.SwitchEnv('source')
 
         if src_map == '':
@@ -954,13 +861,12 @@
         #
         # show new display & draw map
         #
+        self.MapWindow = self.TgtMapWindow
+        self.Map = self.TgtMap
+        self.OnZoomToMap(None)
         self.MapWindow = self.SrcMapWindow
         self.Map = self.SrcMap
         self.OnZoomToMap(None)
-        if self.show_target:
-            self.MapWindow = self.TgtMapWindow
-            self.Map = self.TgtMap
-            self.OnZoomToMap(None)
 
         #
         # bindings
@@ -1028,7 +934,7 @@
                                    0.0,                # forward error
                                    0.0 ] )             # backward error
 
-        if self.toggleStatus.GetSelection() == 7: # go to
+        if self.statusbarWin['toggle'].GetSelection() == 7: # go to
             self.StatusbarUpdate()
 
     def DeleteGCP(self, event):
@@ -1069,11 +975,11 @@
 
         self.UpdateColours()
 
-        if self.toggleStatus.GetSelection() == 7: # go to
+        if self.statusbarWin['toggle'].GetSelection() == 7: # go to
             self.StatusbarUpdate()
             if self.list.selectedkey > 0:
-                self.gotogcp.SetValue(self.list.selectedkey)
-            #self.gotogcp.SetValue(0)
+                self.statusbarWin['goto'].SetValue(self.list.selectedkey)
+            #self.statusbarWin['goto'].SetValue(0)
 
     def ClearGCP(self, event):
         """
@@ -1202,11 +1108,15 @@
             else:
                 currloc = _("target")
             ret = wx.MessageBox(parent=self,
-                          caption=_("Set GCP coordinates"),
-                          message=_('Set %s coordinates for GCP No. %s? \n\n'
-                                    'East: %s \n'
-                                    'North: %s') % (currloc, str(key), str(coord0), str(coord1)),
-                          style=wx.ICON_QUESTION | wx.YES_NO | wx.CENTRE)
+                                caption=_("Set GCP coordinates"),
+                                message=_('Set %(coor)s coordinates for GCP No. %(key)s? \n\n'
+                                          'East: %(coor0)s \n'
+                                          'North: %(coor1)s') % \
+                                    { 'coor' : currloc,
+                                      'key' : str(key),
+                                      'coor0' : str(coord0),
+                                      'coor1' : str(coord1) },
+                                style=wx.ICON_QUESTION | wx.YES_NO | wx.CENTRE)
 
             # for wingrass
             if os.name == 'nt':
@@ -1631,6 +1541,7 @@
 
             self.SrcMap.Clean()
             self.TgtMap.Clean()
+
             self.grwiz.Cleanup()
 
             self.Destroy()
@@ -1852,11 +1763,10 @@
 
     def OnHelp(self, event):
         """!Show GCP Manager manual page"""
-
-        cmdlist = ['g.manual','entry=wxGUI.GCP_Manager']
+        cmdlist = ['g.manual', 'entry=wxGUI.GCP_Manager']
         self.parent.goutput.RunCmd(cmdlist, compReg=False,
-                                   switchPage=False)
-        
+                                       switchPage=False)
+
     def OnUpdateActive(self, event):
 
         if self.activemap.GetSelection() == 0:
@@ -1874,8 +1784,7 @@
     def UpdateActive(self, win):
 
         # optionally disable tool zoomback tool
-        self.toolbars['gcpdisp'].toolbar.EnableTool(self.toolbars['gcpdisp'].zoomback,
-                                         enable = (len(self.MapWindow.zoomhistory) > 1))
+        self.toolbars['gcpdisp'].Enable('zoomback', enable = (len(self.MapWindow.zoomhistory) > 1))
 
         if self.activemap.GetSelection() != (win == self.TgtMapWindow):
             self.activemap.SetSelection(win == self.TgtMapWindow)
@@ -1970,7 +1879,6 @@
         """!GCP Map Display resized, adjust Map Windows
         """
         if self.toolbars['gcpdisp']:
-            time.sleep(0.5)
             srcwidth, srcheight = self.SrcMapWindow.GetSize()
             tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
             tgtwidth = (srcwidth + tgtwidth) / 2
@@ -2638,84 +2546,14 @@
         self.parent.grwiz.SwitchEnv('source')
         self.srcselection.SetElementList(maptype)
         # filter out all maps not in group
-        # not available in 64
-        #self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
-        # modified from gselect
-        self.srcselection.tcp.seltree.DeleteAllItems()
-        # get current mapset
-        curr_mapset = grass.gisenv()['MAPSET']
-        mapsets = utils.ListOfMapsets()
-        for i in range(len(mapsets)):
-            if i > 0 and mapsets[i] == curr_mapset:
-                mapsets[i] = mapsets[0]
-                mapsets[0] = curr_mapset
-        
-        elementdict = {'cell':'rast',
-                       'raster':'rast',
-                       'rast':'rast',
-                       'raster files':'rast',
-                       'vector':'vect',
-                       'vect':'vect'
-                       }
-        if globalvar.have_mlist:
-            filesdict = grass.mlist_grouped(elementdict[maptype])
-        else:
-            filesdict = grass.list_grouped(elementdict[maptype])
-        
-        first_dir = None
-        for dir in mapsets:
-            dir_node = self.srcselection.tcp.AddItem('Mapset: ' + dir)
-            if not first_dir:
-                first_dir = dir_node
-            
-            self.srcselection.tcp.seltree.SetItemTextColour(dir_node, wx.Colour(50, 50, 200))
-            try:
-                elem_list = filesdict[dir]
-                elem_list.sort(key=str.lower)
-                for elem in elem_list:
-                    if elem != '':
-                        fullqElem = elem + '@' + dir
-                        if fullqElem in self.parent.src_maps:
-                            self.srcselection.tcp.AddItem(fullqElem, parent=dir_node)
-            except:
-                continue
+        self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
 
-            if self.srcselection.tcp.seltree.ItemHasChildren(dir_node):
-                sel = UserSettings.Get(group='general', key='elementListExpand',
-                                       subkey='selection')
-                collapse = True
-
-                if sel == 0: # collapse all except PERMANENT and current
-                    if dir in ('PERMANENT', curr_mapset):
-                        collapse = False
-                elif sel == 1: # collapse all except PERMANENT
-                    if dir == 'PERMANENT':
-                        collapse = False
-                elif sel == 2: # collapse all except current
-                    if dir == curr_mapset:
-                        collapse = False
-                elif sel == 3: # collapse all
-                    pass
-                elif sel == 4: # expand all
-                    collapse = False
-                
-                if collapse:
-                    self.srcselection.tcp.seltree.Collapse(dir_node)
-                else:
-                    self.srcselection.tcp.seltree.Expand(dir_node)
-        
-        if first_dir:
-            # select first mapset (MSW hack)
-            self.srcselection.tcp.seltree.SelectItem(first_dir)
-
-        # end copy from gselect
-
         # target map to display
         self.tgtselection = gselect.Select(panel, id=wx.ID_ANY,
                     size=globalvar.DIALOG_GSELECT_SIZE, type='cell', updateOnPopup = False)
         self.parent.grwiz.SwitchEnv('target')
         self.tgtselection.SetElementList(maptype)
-        #self.tgtselection.GetElementList()
+        self.tgtselection.GetElementList()
 
         sizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Select source map to display:')),
                        proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
@@ -2896,9 +2734,9 @@
                 if self.parent.show_target == False:
                     self.parent.show_target = True
                     self.parent._mgr.GetPane("target").Show()
-                    self.parent.toolbars['gcpdisp'].toolbar.EnableTool(self.parent.toolbars['gcpdisp'].zoommenu, enable = True)
+                    self.parent.toolbars['gcpdisp'].Enable('zoommenu', enable = True)
                     self.parent.activemap.Enable()
-                    self.parent.TgtMapWindow.ZoomToMap(layer = self.parent.TgtMap.GetListOfLayers())
+                    self.parent.TgtMapWindow.ZoomToMap(layers = self.parent.TgtMap.GetListOfLayers())
                     self.parent._mgr.Update()
             else: # tgt_map == ''
                 if self.parent.show_target == True:
@@ -2906,7 +2744,7 @@
                     self.parent._mgr.GetPane("target").Hide()
                     self.parent.activemap.SetSelection(0)
                     self.parent.activemap.Enable(False)
-                    self.parent.toolbars['gcpdisp'].toolbar.EnableTool(self.parent.toolbars['gcpdisp'].zoommenu, enable = False)
+                    self.parent.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
                     self.parent._mgr.Update()
 
         self.parent.UpdateColours(srcrender, srcrenderVector, tgtrender, tgtrenderVector)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmapdisp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmapdisp.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gcpmapdisp.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,24 +1,20 @@
 """!
 @package gcpmapdisp.py
 
- at brief GIS map display canvas, with toolbar for various display
-management functions, and additional toolbars (vector digitizer, 3d
-view).
+ at brief display to manage ground control points with two toolbars, one for
+various display management functions, one for manipulating GCPs.
 
 Classes:
 - MapFrame
 
-
 (C) 2006-2010 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.
 
+Derived from mapdisp.py
+
 @author Markus Metz
-based on mapdisp.py by
- at author Michael Barton
- at author Jachym Cepicky
- at author Martin Landa <landa.martin gmail.com>
 """
 
 import os
@@ -54,8 +50,7 @@
 import disp_print
 import gcmd
 import dbm
-import histogram
-import profile
+import dbm_dialogs
 import globalvar
 import utils
 import gdialogs
@@ -64,25 +59,31 @@
 from icon  import Icons
 from preferences import globalSettings as UserSettings
 
-from gcmd import Command
-from mapdisp import BufferedWindow
+from mapdisp_command import Command
+from mapdisp_window import BufferedWindow
 
 import images
 imagepath = images.__path__[0]
 sys.path.append(imagepath)
 
+###
+### global variables
+###
+# for standalone app
+cmdfilename = None
+
 class MapFrame(wx.Frame):
     """!Main frame for map display window. Drawing takes place in
     child double buffered drawing window.
     """
-    def __init__(self, parent=None, id=wx.ID_ANY, title=_("GRASS GIS - Map display"),
+    def __init__(self, parent=None, id=wx.ID_ANY, title=_("Manage Ground Control Points"),
                  style=wx.DEFAULT_FRAME_STYLE, toolbars=["gcpdisp"],
                  tree=None, notebook=None, lmgr=None, page=None,
                  Map=None, auimgr=None, **kwargs):
         """!Main map display window with toolbars, statusbar and
         DrawWindow
 
-        @param toolbars array of activated toolbars, here 'gcpdisp'
+        @param toolbars array of activated toolbars, e.g. ['map', 'digit']
         @param tree reference to layer tree
         @param notebook control book ID in Layer Manager
         @param lmgr Layer Manager
@@ -92,7 +93,6 @@
         @param kwargs wx.Frame attribures
         """
         self._layerManager = lmgr   # Layer Manager object
-        self.gismanager    = lmgr    # GIS Manager object
         self.Map        = Map       # instance of render.Map
         self.tree       = tree      # Layer Manager layer tree object
         self.page       = page      # Notebook page holding the layer tree
@@ -100,7 +100,7 @@
         self.parent     = parent
         
         if not kwargs.has_key('name'):
-            kwargs['name'] = 'GCPDisplay'
+            kwargs['name'] = 'GCPMapWindow'
         wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
         
         # available cursors
@@ -151,7 +151,8 @@
         #
         self.statusbar = self.CreateStatusBar(number=4, style=0)
         self.statusbar.SetStatusWidths([-5, -2, -1, -1])
-        self.toggleStatus = wx.Choice(self.statusbar, wx.ID_ANY,
+        self.statusbarWin = dict()
+        self.statusbarWin['toggle'] = wx.Choice(self.statusbar, wx.ID_ANY,
                                   choices = [_("Coordinates"),
                                              _("Extent"),
                                              _("Comp. region"),
@@ -162,71 +163,83 @@
                                              _("Go to GCP No."),
                                              _("RMS error")])
         # set StatusBar to Go to GCP No.
-        self.toggleStatus.SetSelection(7)
+        self.statusbarWin['toggle'].SetSelection(7)
 
-        self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.toggleStatus)
+        self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.statusbarWin['toggle'])
         # auto-rendering checkbox
-        self.autoRender = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+        self.statusbarWin['render'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
                                                   label=_("Render"))
-        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.autoRender)
-        self.autoRender.SetValue(UserSettings.Get(group='display',
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.statusbarWin['render'])
+        self.statusbarWin['render'].SetValue(UserSettings.Get(group='display',
                                                               key='autoRendering',
                                                               subkey='enabled'))
-        self.autoRender.SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
+        self.statusbarWin['render'].SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
         # show region
-        self.showRegion = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+        self.statusbarWin['region'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
                                                   label=_("Show computational extent"))
-        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.showRegion)
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.statusbarWin['region'])
         
-        self.showRegion.SetValue(False)
-        self.showRegion.Hide()
-        self.showRegion.SetToolTip(wx.ToolTip (_("Show/hide computational "
+        self.statusbarWin['region'].SetValue(False)
+        self.statusbarWin['region'].Hide()
+        self.statusbarWin['region'].SetToolTip(wx.ToolTip (_("Show/hide computational "
                                                              "region extent (set with g.region). "
                                                              "Display region drawn as a blue box inside the "
                                                              "computational region, "
                                                              "computational region inside a display region "
                                                              "as a red box).")))
         # set resolution
-        self.compResolution = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+        self.statusbarWin['resolution'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
                                                       label=_("Constrain display resolution to computational settings"))
-        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.compResolution)
-        self.compResolution.SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
-        self.compResolution.Hide()
-        self.compResolution.SetToolTip(wx.ToolTip (_("Constrain display resolution "
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.statusbarWin['resolution'])
+        self.statusbarWin['resolution'].SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
+        self.statusbarWin['resolution'].Hide()
+        self.statusbarWin['resolution'].SetToolTip(wx.ToolTip (_("Constrain display resolution "
                                                                  "to computational region settings. "
                                                                  "Default value for new map displays can "
                                                                  "be set up in 'User GUI settings' dialog.")))
         # map scale
-        self.mapScale = wx.ComboBox(parent = self.statusbar, id = wx.ID_ANY,
+        self.statusbarWin['mapscale'] = wx.ComboBox(parent = self.statusbar, id = wx.ID_ANY,
                                                     style = wx.TE_PROCESS_ENTER,
                                                     size=(150, -1))
-        self.mapScale.SetItems(['1:1000',
+        self.statusbarWin['mapscale'].SetItems(['1:1000',
                                                 '1:5000',
                                                 '1:10000',
                                                 '1:25000',
                                                 '1:50000',
                                                 '1:100000',
                                                 '1:1000000'])
-        self.mapScale.Hide()
-        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.mapScale)
-        self.statusbar.Bind(wx.EVT_COMBOBOX, self.OnChangeMapScale, self.mapScale)
+        self.statusbarWin['mapscale'].Hide()
+        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.statusbarWin['mapscale'])
+        self.statusbar.Bind(wx.EVT_COMBOBOX, self.OnChangeMapScale, self.statusbarWin['mapscale'])
 
-        # go to GCP
-        self.gotogcp = wx.SpinCtrl(parent=self.statusbar, id=wx.ID_ANY,
+        # go to
+        self.statusbarWin['goto'] = wx.SpinCtrl(parent=self.statusbar, id=wx.ID_ANY,
                              min=0)
-        self.statusbar.Bind(wx.EVT_SPINCTRL, self.OnGoTo, self.gotogcp)
-        self.gotogcp.Hide()
-        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnGoTo, self.gotogcp)
+        self.statusbar.Bind(wx.EVT_SPINCTRL, self.OnGoTo, self.statusbarWin['goto'])
+        self.statusbarWin['goto'].Hide()
+        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnGoTo, self.statusbarWin['goto'])
 
+        # projection, unused but BufferedWindow checks for it
+        self.statusbarWin['projection'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                      label=_("Use defined projection"))
+        self.statusbarWin['projection'].SetValue(False)
+        size = self.statusbarWin['projection'].GetSize()
+        self.statusbarWin['projection'].SetMinSize((size[0] + 150, size[1]))
+        self.statusbarWin['projection'].SetToolTip(wx.ToolTip (_("Reproject coordinates displayed "
+                                                                 "in the statusbar. Projection can be "
+                                                                 "defined in GUI preferences dialog "
+                                                                 "(tab 'Display')")))
+        self.statusbarWin['projection'].Hide()
+
         # mask
-        self.maskInfo = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY,
+        self.statusbarWin['mask'] = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY,
                                                   label = '')
-        self.maskInfo.SetForegroundColour(wx.Colour(255, 0, 0))
+        self.statusbarWin['mask'].SetForegroundColour(wx.Colour(255, 0, 0))
         
         # on-render gauge
-        self.onRenderGauge = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
+        self.statusbarWin['progress'] = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
                                       range=0, style=wx.GA_HORIZONTAL)
-        self.onRenderGauge.Hide()
+        self.statusbarWin['progress'].Hide()
         
         self.StatusbarReposition() # reposition statusbar
 
@@ -235,15 +248,13 @@
         #
         self.grwiz.SwitchEnv('source')
         self.SrcMapWindow = BufferedWindow(self, id=wx.ID_ANY,
-                                          Map=self.SrcMap, tree=self.tree, gismgr=self._layerManager)
+                                          Map=self.SrcMap, tree=self.tree, lmgr=self._layerManager)
 
         self.grwiz.SwitchEnv('target')
         self.TgtMapWindow = BufferedWindow(self, id=wx.ID_ANY,
-                                          Map=self.TgtMap, tree=self.tree, gismgr=self._layerManager)
+                                          Map=self.TgtMap, tree=self.tree, lmgr=self._layerManager)
         self.MapWindow = self.SrcMapWindow
         self.Map = self.SrcMap
-        self.SrcMapWindow.Bind(wx.EVT_MOTION, self.OnMotion)
-        self.TgtMapWindow.Bind(wx.EVT_MOTION, self.OnMotion)
         self.SrcMapWindow.SetCursor(self.cursors["cross"])
         self.TgtMapWindow.SetCursor(self.cursors["cross"])
 
@@ -263,7 +274,7 @@
         #
         # Update fancy gui style
         #
-        # AuiManager wants a CentrePane , workaround to get two equally sized windows
+        # AuiManager wants a CentrePane, workaround to get two equally sized windows
         self.list = self.CreateGCPList()
 
         #self.SrcMapWindow.SetSize((300, 300))
@@ -294,7 +305,6 @@
             self._mgr.GetPane("target").Show()
         else:
             self.activemap.Enable(False)
-
         # called by GCPWizard
         #self._mgr.Update()
 
@@ -303,16 +313,24 @@
         #
         self.printopt = disp_print.PrintOptions(self, self.MapWindow)
         
+        #
+        # Initialization of digitization tool
+        #
+        self.digit = None
+
         # set active map
         self.MapWindow = self.SrcMapWindow
         self.Map = self.SrcMap
         #
-        # Init zoom history
+        # Init zoom history for TgtMapWindow
         #
         self.TgtMapWindow.ZoomHistory(self.Map.region['n'],
                                    self.Map.region['s'],
                                    self.Map.region['e'],
                                    self.Map.region['w'])
+        #
+        # Init zoom history
+        #
         self.MapWindow.ZoomHistory(self.Map.region['n'],
                                    self.Map.region['s'],
                                    self.Map.region['e'],
@@ -340,11 +358,24 @@
          - 'georect' - georectifier
          - 'nviz'    - 3D view mode
         """
-        # default toolbars for GCP display
-        if name == "gcpdisp":
+        # default toolbar
+        if name == "map":
+            self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
+
+            self._mgr.AddPane(self.toolbars['map'],
+                              wx.aui.AuiPaneInfo().
+                              Name("maptoolbar").Caption(_("Map Toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).
+                              BestSize((self.toolbars['map'].GetSize())))
+
+        # GCP display
+        elif name == "gcpdisp":
             self.toolbars['gcpdisp'] = toolbars.GCPDisplayToolbar(self)
 
-            self._mgr.AddPane(self.toolbars['gcpdisp'].toolbar,
+            self._mgr.AddPane(self.toolbars['gcpdisp'],
                               wx.aui.AuiPaneInfo().
                               Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
                               ToolbarPane().Top().
@@ -353,30 +384,18 @@
                               CloseButton(False).Layer(2))
 
             if self.show_target == False:
-                self.toolbars['gcpdisp'].toolbar.EnableTool(self.toolbars['gcpdisp'].zoommenu, enable = False)
+                self.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
 
             self.toolbars['gcpman'] = toolbars.GCPManToolbar(self)
 
-            self._mgr.AddPane(self.toolbars['gcpman'].toolbar,
+            self._mgr.AddPane(self.toolbars['gcpman'],
                               wx.aui.AuiPaneInfo().
                               Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")).
                               ToolbarPane().Top().Row(1).
                               LeftDockable(False).RightDockable(False).
                               BottomDockable(False).TopDockable(True).
                               CloseButton(False).Layer(2))
-
-        # georectifier
-        elif name == "georect":
-            self.toolbars['georect'] = toolbars.GRToolbar(self, self.Map)
-
-            self._mgr.AddPane(self.toolbars['georect'].toolbar,
-                  wx.aui.AuiPaneInfo().
-                  Name("georecttoolbar").Caption(_("Georectification toolbar")).
-                  ToolbarPane().Top().
-                  LeftDockable(False).RightDockable(False).
-                  BottomDockable(False).TopDockable(True).
-                  CloseButton(False).Layer(2))
-
+            
         self._mgr.Update()
 
     def __InitDisplay(self):
@@ -399,7 +418,7 @@
         """
         Update progress bar info
         """
-        self.onRenderGauge.SetValue(event.value)
+        self.statusbarWin['progress'].SetValue(event.value)
         
         event.Skip()
         
@@ -409,49 +428,20 @@
         Or set display for georectifying
         """
         if self._layerManager and \
-                self._layerManager.georectifying:
-            # in georectifying session; display used to get geographic
-            # coordinates for GCPs
-            self.OnPointer(event)
-        elif self._layerManager and \
                 self._layerManager.gcpmanagement:
-            # in georectifying session; display used to get geographic
-            # coordinates for GCPs
+            # in GCP Management, set focus to current MapWindow for mouse actions
             self.OnPointer(event)
             self.MapWindow.SetFocus()
+        else:
+            # change bookcontrol page to page associated with display
+            # GCP Manager: use bookcontrol?
+            if self.page:
+                pgnum = self.layerbook.GetPageIndex(self.page)
+                if pgnum > -1:
+                    self.layerbook.SetSelection(pgnum)
         
         event.Skip()
 
-    def OnMotion(self, event):
-        """
-        Mouse moved
-        Track mouse motion and update status bar
-        """
-        # update statusbar if required
-        if self.toggleStatus.GetSelection() == 0: # Coordinates
-            e, n = self.MapWindow.Pixel2Cell(event.GetPositionTuple())
-            if self.toolbars['vdigit'] and \
-                    self.toolbars['vdigit'].GetAction() == 'addLine' and \
-                    self.toolbars['vdigit'].GetAction('type') in ('line', 'boundary') and \
-                    len(self.MapWindow.polycoords) > 0:
-                # for linear feature show segment and total length
-                distance_seg = self.MapWindow.Distance(self.MapWindow.polycoords[-1],
-                                                       (e, n), screen=False)[0]
-                distance_tot = distance_seg
-                for idx in range(1, len(self.MapWindow.polycoords)):
-                    distance_tot += self.MapWindow.Distance(self.MapWindow.polycoords[idx-1],
-                                                            self.MapWindow.polycoords[idx],
-                                                            screen=False )[0]
-                self.statusbar.SetStatusText("%.2f, %.2f (seg: %.2f; tot: %.2f)" % \
-                                                 (e, n, distance_seg, distance_tot), 0)
-            else:
-                if self.Map.projinfo['proj'] == 'll':
-                    self.statusbar.SetStatusText("%s" % utils.Deg2DMS(e, n), 0)
-                else:
-                    self.statusbar.SetStatusText("%.2f, %.2f" % (e, n), 0)
-
-        event.Skip()
-
     def OnDraw(self, event):
         """!Re-display current map composition
         """
@@ -465,47 +455,23 @@
         for layer in qlayer:
             self.Map.DeleteLayer(layer)
 
-        # delete tmp lines
-        if self.MapWindow.mouse["use"] in ("measure",
-                                           "profile"):
-            self.MapWindow.polycoords = []
-            self.MapWindow.ClearLines()
+        self.SrcMapWindow.UpdateMap(render=True)
+        self.TgtMapWindow.UpdateMap(render=True)
+        self._mgr.Update()
         
-        if self.toolbars['gcpdisp']:
-            self.SrcMapWindow.UpdateMap(render=True)
-            self.TgtMapWindow.UpdateMap(render=True)
-            self._mgr.Update()
-        else:
-            self.MapWindow.UpdateMap(render=True)
-        
         # update statusbar
         self.StatusbarUpdate()
 
     def OnPointer(self, event):
         """!Pointer button clicked
         """
-        if self.toolbars['map']:
-            if event:
-                self.toolbars['map'].OnTool(event)
-            self.toolbars['map'].action['desc'] = ''
-        
-        self.MapWindow.mouse['use'] = "pointer"
-        self.MapWindow.mouse['box'] = "point"
-
         # change the cursor
-        if self._layerManager.gcpmanagement:
-            self.SrcMapWindow.SetCursor(self.cursors["cross"])
-            self.SrcMapWindow.mouse['use'] = "pointer"
-            self.SrcMapWindow.mouse['box'] = "point"
-            self.TgtMapWindow.SetCursor(self.cursors["cross"])
-            self.TgtMapWindow.mouse['use'] = "pointer"
-            self.TgtMapWindow.mouse['box'] = "point"
-        
-        elif self._layerManager and self._layerManager.georectifying:
-            self.MapWindow.SetCursor(self.cursors["cross"])
-        
-        else:
-            self.MapWindow.SetCursor(self.cursors["default"])
+        self.SrcMapWindow.SetCursor(self.cursors["cross"])
+        self.SrcMapWindow.mouse['use'] = "pointer"
+        self.SrcMapWindow.mouse['box'] = "point"
+        self.TgtMapWindow.SetCursor(self.cursors["cross"])
+        self.TgtMapWindow.mouse['use'] = "pointer"
+        self.TgtMapWindow.mouse['box'] = "point"
 
     def OnZoomIn(self, event):
         """
@@ -524,19 +490,18 @@
         # change the cursor
         self.MapWindow.SetCursor(self.cursors["cross"])
 
-        if self._layerManager and self._layerManager.gcpmanagement:
-            if self.MapWindow == self.SrcMapWindow:
-                win = self.TgtMapWindow
-            elif self.MapWindow == self.TgtMapWindow:
-                win = self.SrcMapWindow
+        if self.MapWindow == self.SrcMapWindow:
+            win = self.TgtMapWindow
+        elif self.MapWindow == self.TgtMapWindow:
+            win = self.SrcMapWindow
 
-            win.mouse['use'] = "zoom"
-            win.mouse['box'] = "box"
-            win.zoomtype = 1
-            win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
-            
-            # change the cursor
-            win.SetCursor(self.cursors["cross"])
+        win.mouse['use'] = "zoom"
+        win.mouse['box'] = "box"
+        win.zoomtype = 1
+        win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+        
+        # change the cursor
+        win.SetCursor(self.cursors["cross"])
 
     def OnZoomOut(self, event):
         """
@@ -555,19 +520,18 @@
         # change the cursor
         self.MapWindow.SetCursor(self.cursors["cross"])
 
-        if self._layerManager and self._layerManager.gcpmanagement:
-            if self.MapWindow == self.SrcMapWindow:
-                win = self.TgtMapWindow
-            elif self.MapWindow == self.TgtMapWindow:
-                win = self.SrcMapWindow
+        if self.MapWindow == self.SrcMapWindow:
+            win = self.TgtMapWindow
+        elif self.MapWindow == self.TgtMapWindow:
+            win = self.SrcMapWindow
 
-            win.mouse['use'] = "zoom"
-            win.mouse['box'] = "box"
-            win.zoomtype = -1
-            win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
-            
-            # change the cursor
-            win.SetCursor(self.cursors["cross"])
+        win.mouse['use'] = "zoom"
+        win.mouse['box'] = "box"
+        win.zoomtype = -1
+        win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+        
+        # change the cursor
+        win.SetCursor(self.cursors["cross"])
 
     def OnZoomBack(self, event):
         """
@@ -590,18 +554,17 @@
         # change the cursor
         self.MapWindow.SetCursor(self.cursors["hand"])
 
-        if self._layerManager and self._layerManager.gcpmanagement:
-            if self.MapWindow == self.SrcMapWindow:
-                win = self.TgtMapWindow
-            elif self.MapWindow == self.TgtMapWindow:
-                win = self.SrcMapWindow
+        if self.MapWindow == self.SrcMapWindow:
+            win = self.TgtMapWindow
+        elif self.MapWindow == self.TgtMapWindow:
+            win = self.SrcMapWindow
 
-            win.mouse['use'] = "pan"
-            win.mouse['box'] = "pan"
-            win.zoomtype = 0
-            
-            # change the cursor
-            win.SetCursor(self.cursors["hand"])
+        win.mouse['use'] = "pan"
+        win.mouse['box'] = "pan"
+        win.zoomtype = 0
+        
+        # change the cursor
+        win.SetCursor(self.cursors["hand"])
 
     def OnErase(self, event):
         """
@@ -609,13 +572,12 @@
         """
         self.MapWindow.EraseMap()
 
-        if self._layerManager and self._layerManager.gcpmanagement:
-            if self.MapWindow == self.SrcMapWindow:
-                win = self.TgtMapWindow
-            elif self.MapWindow == self.TgtMapWindow:
-                win = self.SrcMapWindow
+        if self.MapWindow == self.SrcMapWindow:
+            win = self.TgtMapWindow
+        elif self.MapWindow == self.TgtMapWindow:
+            win = self.SrcMapWindow
 
-            win.EraseMap()
+        win.EraseMap()
 
     def OnZoomRegion(self, event):
         """
@@ -640,21 +602,21 @@
         """
         Enable/disable auto-rendering
         """
-        if self.autoRender.GetValue():
+        if self.statusbarWin['render'].GetValue():
             self.OnRender(None)
 
     def OnToggleShowRegion(self, event):
         """
         Show/Hide extent in map canvas
         """
-        if self.showRegion.GetValue():
+        if self.statusbarWin['region'].GetValue():
             # show extent
             self.MapWindow.regionCoords = []
         else:
             del self.MapWindow.regionCoords
 
         # redraw map if auto-rendering is enabled
-        if self.autoRender.GetValue():
+        if self.statusbarWin['render'].GetValue():
             self.OnRender(None)
 
     def OnToggleResolution(self, event):
@@ -663,7 +625,7 @@
         for redering image instead of display resolution
         """
         # redraw map if auto-rendering is enabled
-        if self.autoRender.GetValue():
+        if self.statusbarWin['render'].GetValue():
             self.OnRender(None)
         
     def OnToggleStatus(self, event):
@@ -683,7 +645,7 @@
                 raise ValueError
             value = int(scale[2:])
         except ValueError:
-            self.mapScale.SetValue('1:%ld' % int(self.mapScaleValue))
+            self.statusbarWin['mapscale'].SetValue('1:%ld' % int(self.mapScaleValue))
             return
 
         dEW = value * (self.Map.region['cols'] / self.ppm[0])
@@ -699,14 +661,14 @@
         
         # redraw a map
         self.MapWindow.UpdateMap()
-        self.mapScale.SetFocus()
+        self.statusbarWin['mapscale'].SetFocus()
         
     def OnGoTo(self, event):
         """
-        Go to GCP No
+        Go to position
         """
         #GCPNo = int(event.GetString())
-        GCPNo = self.gotogcp.GetValue()
+        GCPNo = self.statusbarWin['goto'].GetValue()
 
         if GCPNo < 0 or GCPNo > len(self.mapcoordlist):
             wx.MessageBox(parent=self,
@@ -745,58 +707,83 @@
             # redraw map
             self.TgtMapWindow.UpdateMap()
 
-        self.gotogcp.SetFocus()
+        self.statusbarWin['goto'].SetFocus()
         
     def StatusbarUpdate(self):
         """!Update statusbar content"""
 
-        self.showRegion.Hide()
-        self.compResolution.Hide()
-        self.mapScale.Hide()
-        self.gotogcp.Hide()
+        self.statusbarWin['region'].Hide()
+        self.statusbarWin['resolution'].Hide()
+        self.statusbarWin['mapscale'].Hide()
+        self.statusbarWin['goto'].Hide()
         self.mapScaleValue = self.ppm = None
 
-        if self.toggleStatus.GetSelection() == 0: # Coordinates
+        if self.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
             self.statusbar.SetStatusText("", 0)
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.toggleStatus.GetSelection() == 1: # Extent
-            self.statusbar.SetStatusText("%.2f - %.2f, %.2f - %.2f" %
-                                         (self.Map.region["w"], self.Map.region["e"],
-                                          self.Map.region["s"], self.Map.region["n"]), 0)
-            # enable long help
-            self.StatusbarEnableLongHelp()
+        elif self.statusbarWin['toggle'].GetSelection() in (1, 2): # Extent
+            sel = self.statusbarWin['toggle'].GetSelection()
+            if sel == 1:
+                region = self.Map.region
+            else:
+                region = self.Map.GetRegion() # computation region
 
-        elif self.toggleStatus.GetSelection() == 2: # Comp. region
-            compregion = self.Map.GetRegion()
-            self.statusbar.SetStatusText("%.2f - %.2f, %.2f - %.2f (%.2f, %.2f)" %
-                                         (compregion["w"], compregion["e"],
-                                          compregion["s"], compregion["n"],
-                                          compregion["ewres"], compregion["nsres"]), 0)
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            
+            if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                w, s = utils.Deg2DMS(region["w"], region["s"],
+                                     string = False, precision = precision)
+                e, n = utils.Deg2DMS(region["e"], region["n"],
+                                     string = False, precision = precision)
+                if sel == 1:
+                    self.statusbar.SetStatusText("%s - %s, %s - %s" %
+                                                 (w, e, s, n), 0)
+                else:
+                    ewres, nsres = utils.Deg2DMS(region['ewres'], region['nsres'],
+                                                 string = False, precision = precision)
+                    self.statusbar.SetStatusText("%s - %s, %s - %s (%s, %s)" %
+                                                 (w, e, s, n, ewres, nsres), 0)
+            else:
+                w, s = region["w"], region["s"]
+                e, n = region["e"], region["n"]
+                if sel == 1:
+                    self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
+                                                 (precision, w, precision, e,
+                                                  precision, s, precision, n), 0)
+                else:
+                    ewres, nsres = region['ewres'], region['nsres']
+                    self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
+                                                 (precision, w, precision, e,
+                                                  precision, s, precision, n,
+                                                  precision, ewres, precision, nsres), 0)
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.toggleStatus.GetSelection() == 3: # Show comp. extent
+        elif self.statusbarWin['toggle'].GetSelection() == 3: # Show comp. extent
             self.statusbar.SetStatusText("", 0)
-            self.showRegion.Show()
+            self.statusbarWin['region'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.toggleStatus.GetSelection() == 4: # Display mode
+        elif self.statusbarWin['toggle'].GetSelection() == 4: # Display mode
             self.statusbar.SetStatusText("", 0)
-            self.compResolution.Show()
+            self.statusbarWin['resolution'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.toggleStatus.GetSelection() == 5: # Display geometry
+        elif self.statusbarWin['toggle'].GetSelection() == 5: # Display geometry
             self.statusbar.SetStatusText("rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
                                          (self.Map.region["rows"], self.Map.region["cols"],
                                           self.Map.region["nsres"], self.Map.region["ewres"]), 0)
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.toggleStatus.GetSelection() == 6: # Map scale
+        elif self.statusbarWin['toggle'].GetSelection() == 6: # Map scale
             # TODO: need to be fixed...
             ### screen X region problem
             ### user should specify ppm
@@ -836,30 +823,34 @@
 
             self.statusbar.SetStatusText("")
             try:
-                self.mapScale.SetValue("1:%ld" % (scale + 0.5))
+                self.statusbarWin['mapscale'].SetValue("1:%ld" % (scale + 0.5))
             except TypeError:
                 pass
             self.mapScaleValue = scale
-            self.mapScale.Show()
+            self.statusbarWin['mapscale'].Show()
 
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.toggleStatus.GetSelection() == 7: # go to
+        elif self.statusbarWin['toggle'].GetSelection() == 7: # go to
 
             self.statusbar.SetStatusText("")
             max = self.list.GetItemCount()
             if max < 1:
                 max = 1
-            self.gotogcp.SetRange(0, max)
-            self.gotogcp.Show()
+            self.statusbarWin['goto'].SetRange(0, max)
 
+            self.statusbarWin['goto'].Show()
+
             # disable long help
             self.StatusbarEnableLongHelp(False)
         
-        elif self.toggleStatus.GetSelection() == 8: # RMS error
-            self.statusbar.SetStatusText(_("Forward: %s, Backward: %s") %
-                                         (self.fwd_rmserror, self.bkw_rmserror))
+        elif self.statusbarWin['toggle'].GetSelection() == 8: # RMS error
+            self.statusbar.SetStatusText(_("Forward: %(forw)s, Backward: %(back)s") %
+                                         { 'forw' : self.fwd_rmserror,
+                                           'back' : self.bkw_rmserror })
+            # disable long help
+            # self.StatusbarEnableLongHelp(False)
             
         else:
             self.statusbar.SetStatusText("", 1)
@@ -873,23 +864,23 @@
     def StatusbarReposition(self):
         """!Reposition checkbox in statusbar"""
         # reposition checkbox
-        widgets = [(0, self.showRegion),
-                   (0, self.compResolution),
-                   (0, self.mapScale),
-                   (0, self.onRenderGauge),
-                   (0, self.gotogcp),
-                   (1, self.toggleStatus),
-                   (2, self.maskInfo),
-                   (3, self.autoRender)]
+        widgets = [(0, self.statusbarWin['region']),
+                   (0, self.statusbarWin['resolution']),
+                   (0, self.statusbarWin['mapscale']),
+                   (0, self.statusbarWin['progress']),
+                   (0, self.statusbarWin['goto']),
+                   (1, self.statusbarWin['toggle']),
+                   (2, self.statusbarWin['mask']),
+                   (3, self.statusbarWin['render'])]
         for idx, win in widgets:
             rect = self.statusbar.GetFieldRect(idx)
             wWin, hWin = win.GetBestSize()
             if idx == 0: # show region / mapscale / process bar
                 # -> size
-                if win == self.onRenderGauge:
+                if win == self.statusbarWin['progress']:
                     wWin = rect.width - 6
                 # -> position
-                # if win == self.showRegion:
+                # if win == self.statusbarWin['region']:
                 # x, y = rect.x + rect.width - wWin, rect.y - 1
                 # align left
                 # else:
@@ -915,8 +906,7 @@
         img = self.MapWindow.img
         if not img:
             gcmd.GMessage(parent = self,
-                 message = _("Nothing to render (empty map). Operation canceled."),
-                 msgType = 'info')
+                          message = _("Nothing to render (empty map). Operation canceled."))
             return
         filetype, ltype = gdialogs.GetImageHandlers(img)
 
@@ -986,449 +976,6 @@
         """!Get map window"""
         return self.MapWindow
     
-    def OnQueryDisplay(self, event):
-        """!Query currrent raster/vector map layers (display mode)
-        """
-        if self.toolbars['map'].GetAction() == 'displayAttrb': # select previous action
-            self.toolbars['map'].SelectDefault(event)
-            return
-
-        self.toolbars['map'].action['desc'] = 'displayAttrb'
-        
-        # switch GIS Manager to output console to show query results
-        self._layerManager.notebook.SetSelection(1)
-
-        self.MapWindow.mouse['use'] = "query"
-        self.MapWindow.mouse['box'] = "point"
-        self.MapWindow.zoomtype = 0
-
-        # change the cursor
-        self.MapWindow.SetCursor(self.cursors["cross"])
-
-    def OnQueryModify(self, event):
-        """
-        Query vector map layer (edit mode)
-        """
-        if self.toolbars['map'].GetAction() == 'modifyAttrb': # select previous action
-            self.toolbars['map'].SelectDefault(event)
-            return
-        
-        self.toolbars['map'].action['desc'] = 'modifyAttrb'
-        
-        self.MapWindow.mouse['use'] = "queryVector"
-        self.MapWindow.mouse['box'] = "point"
-        self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
-        self.MapWindow.zoomtype = 0
-
-        # change the cursor
-        self.MapWindow.SetCursor(self.cursors["cross"])
-        
-    def QueryMap(self, x, y):
-        """!Query map layer features
-
-        Currently only raster and vector map layers are supported.
-        
-        @param x,y coordinates
-        """
-        #set query snap distance for v.what at mapunit equivalent of 10 pixels
-        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
-        east, north = self.MapWindow.Pixel2Cell((x, y))
-
-        num = 0
-        for layer in self.tree.GetSelections():
-            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
-            if type in ('raster', 'rgb', 'his',
-                        'vector', 'thememap', 'themechart'):
-                num += 1
-        
-        if num < 1:
-            dlg = wx.MessageDialog(parent = self,
-                                   message = _('No raster or vector map layer selected for querying.'),
-                                   caption = _('No map layer selected'),
-                                   style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-            dlg.ShowModal()
-            dlg.Destroy()
-            return
-        
-        mapname = None
-        raststr = ''
-        vectstr = ''
-        rcmd = ['r.what', '--q']
-        vcmd = ['v.what', '--q']
-        for layer in self.tree.GetSelections():
-            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
-            dcmd = self.tree.GetPyData(layer)[0]['cmd']
-            name = utils.GetLayerNameFromCmd(dcmd)
-            if name == '':
-                continue
-            if type in ('raster', 'rgb', 'his'):
-                raststr += "%s," % name
-            elif type in ('vector', 'thememap', 'themechart'):
-                vectstr += "%s," % name
-
-        # use display region settings instead of computation region settings
-        self.tmpreg = os.getenv("GRASS_REGION")
-        os.environ["GRASS_REGION"] = self.Map.SetRegion(windres=False)
-        
-        # build query commands for any selected rasters and vectors
-        if raststr != '':
-            rcmd.append('-f')
-            rcmd.append('input=%s' % raststr.rstrip(','))
-            rcmd.append('east_north=%f,%f' % (float(east), float(north)))
-        
-        if vectstr != '':
-            # check for vector maps open to be edited
-            digitToolbar = self.toolbars['vdigit']
-            if digitToolbar:
-                map = digitToolbar.GetLayer().GetName()
-                vect = []
-                for vector in vectstr.split(','):
-                    if map == vector:
-                        self._layerManager.goutput.WriteWarning("Vector map <%s> "
-                                                                "opened for editing - skipped." % map)
-                        continue
-                    vect.append(vector)
-                vectstr = ','.join(vect)
-            
-            if len(vectstr) <= 1:
-                self._layerManager.goutput.WriteCmdLog("Nothing to query.")
-                return
-            
-            vcmd.append('-a')
-            vcmd.append('map=%s' % vectstr.rstrip(','))
-            vcmd.append('east_north=%f,%f' % (float(east), float(north)))
-            vcmd.append('distance=%f' % float(qdist))
-        
-        # parse query command(s)
-        if self._layerManager:
-            if raststr:
-                self._layerManager.goutput.RunCmd(rcmd,
-                                                  compReg=False,
-                                                  onDone = self._QueryMapDone)
-            if vectstr:
-                self._layerManager.goutput.RunCmd(vcmd,
-                                                  onDone = self._QueryMapDone)
-        else:
-            if raststr:
-                gcmd.RunCommand(rcmd)
-            if vectstr:
-                gcmd.RunCommand(vcmd)
-        
-    def _QueryMapDone(self, returncode):
-        """!Restore settings after querying (restore GRASS_REGION)
-
-        @param returncode command return code
-        """
-        if hasattr(self, "tmpreg"):
-            if self.tmpreg:
-                os.environ["GRASS_REGION"] = self.tmpreg
-            elif os.environ.has_key('GRASS_REGION'):
-                del os.environ["GRASS_REGION"]
-        elif os.environ.has_key('GRASS_REGION'):
-            del os.environ["GRASS_REGION"]
-        
-        if hasattr(self, "tmpreg"):
-            del self.tmpreg
-        
-    def QueryVector(self, x, y):
-        """
-        Query vector map layer features
-
-        Attribute data of selected vector object are displayed in GUI dialog.
-        Data can be modified (On Submit)
-        """
-        if not self.tree.layer_selected or \
-                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] != 'vector':
-            wx.MessageBox(parent=self,
-                          message=_("No vector map selected for querying."),
-                          caption=_("Vector querying"),
-                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-            return
-        
-        if self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetMapset() != \
-                grass.gisenv()['MAPSET']:
-            wx.MessageBox(parent=self,
-                          message=_("Only vector map from the current mapset can be modified."),
-                          caption=_("Vector querying"),
-                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-            return
-        
-        posWindow = self.ClientToScreen((x + self.MapWindow.dialogOffset,
-                                         y + self.MapWindow.dialogOffset))
-
-        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) /
-                        self.Map.width)
-        
-        east, north = self.MapWindow.Pixel2Cell((x, y))
-        
-        mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name
-        
-        if self.dialogs['attributes'] is None:
-            self.dialogs['attributes'] = \
-                dbm_dialogs.DisplayAttributesDialog(parent=self.MapWindow,
-                                                    map=mapName,
-                                                    query=((east, north), qdist),
-                                                    pos=posWindow,
-                                                    action="update")
-        else:
-            # selection changed?
-            if not self.dialogs['attributes'].mapDBInfo or \
-                    self.dialogs['attributes'].mapDBInfo.map != mapName:
-                self.dialogs['attributes'].UpdateDialog(map=mapName, query=((east, north), qdist))
-            else:
-                self.dialogs['attributes'].UpdateDialog(query=((east, north), qdist))
-                
-        cats = self.dialogs['attributes'].GetCats()
-        
-        try:
-            qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)[0]
-        except IndexError:
-            qlayer = None
-        
-        if self.dialogs['attributes'].mapDBInfo and cats:
-            # highlight feature & re-draw map
-            if qlayer:
-                qlayer.SetCmd(self.AddTmpVectorMapLayer(mapName, cats,
-                                                        useId=False,
-                                                        addLayer=False))
-            else:
-                qlayer = self.AddTmpVectorMapLayer(mapName, cats, useId=False)
-            
-            # set opacity based on queried layer
-            opacity = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetOpacity(float=True)
-            qlayer.SetOpacity(opacity)
-            
-            self.MapWindow.UpdateMap(render=False, renderVector=False)
-            if not self.dialogs['attributes'].IsShown():
-                self.dialogs['attributes'].Show()
-        else:
-            if qlayer:
-                self.Map.DeleteLayer(qlayer)
-                self.MapWindow.UpdateMap(render=False, renderVector=False)
-            if self.dialogs['attributes'].IsShown():
-                self.dialogs['attributes'].Hide()
-        
-    def OnQuery(self, event):
-        """!Query tools menu"""
-        if self.toolbars['map']:
-            self.toolbars['map'].OnTool(event)
-            action = self.toolbars['map'].GetAction()
-        
-        point = wx.GetMousePosition()
-        toolsmenu = wx.Menu()
-        # Add items to the menu
-        display = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
-                              text=_("Query raster/vector map(s) (display mode)"),
-                              kind=wx.ITEM_CHECK)
-        toolsmenu.AppendItem(display)
-        self.Bind(wx.EVT_MENU, self.OnQueryDisplay, display)
-        numLayers = 0
-        for layer in self.tree.GetSelections():
-            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
-            if type in ('raster', 'rgb', 'his',
-                        'vector', 'thememap', 'themechart'):
-                numLayers += 1
-        if numLayers < 1:
-            display.Enable(False)
-        
-        if action == "displayAttrb":
-            display.Check(True)
-        
-        modify = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
-                             text=_("Query vector map (edit mode)"),
-                             kind=wx.ITEM_CHECK)
-        toolsmenu.AppendItem(modify)
-        self.Bind(wx.EVT_MENU, self.OnQueryModify, modify)
-        digitToolbar = self.toolbars['vdigit']
-        if self.tree.layer_selected:
-            layer_selected = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer']
-            if layer_selected.GetType() != 'vector' or \
-                    (digitToolbar and \
-                         digitToolbar.GetLayer() == layer_selected):
-                modify.Enable(False)
-            else:
-                if action == "modifyAttrb":
-                    modify.Check(True)
-        
-        self.PopupMenu(toolsmenu)
-        toolsmenu.Destroy()
-
-    def AddTmpVectorMapLayer(self, name, cats, useId=False, addLayer=True):
-        """
-        Add temporal vector map layer to map composition
-
-        @param name name of map layer
-        @param useId use feature id instead of category 
-        """
-        # color settings from ATM
-        color = UserSettings.Get(group='atm', key='highlight', subkey='color')
-        colorStr = str(color[0]) + ":" + \
-            str(color[1]) + ":" + \
-            str(color[2])
-
-        # icon used in vector display and its size
-        icon = ''
-        size = 0
-        vparam = self.tree.GetPyData(self.tree.layer_selected)[0]['cmd']
-        for p in vparam:
-            if '=' in p:
-                parg,pval = p.split('=')
-                if parg == 'icon': icon = pval
-                elif parg == 'size': size = int(pval)
-
-        pattern = ["d.vect",
-                   "map=%s" % name,
-                   "color=%s" % colorStr,
-                   "fcolor=%s" % colorStr,
-                   "width=%d"  % UserSettings.Get(group='atm', key='highlight', subkey='width')]
-        if icon != '':
-            pattern.append('icon=%s' % icon)
-        if size > 0:
-            pattern.append('size=%i' % size)
-        
-        if useId:
-            cmd = pattern
-            cmd.append('-i')
-            cmd.append('cats=%s' % str(cats))
-        else:
-            cmd = []
-            for layer in cats.keys():
-                cmd.append(copy.copy(pattern))
-                lcats = cats[layer]
-                cmd[-1].append("layer=%d" % layer)
-                cmd[-1].append("cats=%s" % utils.ListOfCatsToRange(lcats))
-        
-        #     if self.icon:
-        #         cmd.append("icon=%s" % (self.icon))
-        #     if self.pointsize:
-        #         cmd.append("size=%s" % (self.pointsize))
-
-        if addLayer:
-            if useId:
-                return self.Map.AddLayer(type='vector', name=globalvar.QUERYLAYER, command=cmd,
-                                         l_active=True, l_hidden=True, l_opacity=1.0)
-            else:
-                return self.Map.AddLayer(type='command', name=globalvar.QUERYLAYER, command=cmd,
-                                         l_active=True, l_hidden=True, l_opacity=1.0)
-        else:
-            return cmd
-
-    def OnAnalyze(self, event):
-        """
-        Analysis tools menu
-        """
-        point = wx.GetMousePosition()
-        toolsmenu = wx.Menu()
-        # Add items to the menu
-        measure = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["measure"].GetLabel())
-        measure.SetBitmap(Icons["measure"].GetBitmap(self.iconsize))
-        toolsmenu.AppendItem(measure)
-        self.Bind(wx.EVT_MENU, self.OnMeasure, measure)
-
-        profile = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["profile"].GetLabel())
-        profile.SetBitmap(Icons["profile"].GetBitmap(self.iconsize))
-        toolsmenu.AppendItem(profile)
-        self.Bind(wx.EVT_MENU, self.Profile, profile)
-
-        histogram = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["histogram"].GetLabel())
-        histogram.SetBitmap(Icons["histogram"].GetBitmap(self.iconsize))
-        toolsmenu.AppendItem(histogram)
-        self.Bind(wx.EVT_MENU, self.Histogram, histogram)
-
-        # Popup the menu.  If an item is selected then its handler
-        # will be called before PopupMenu returns.
-        self.PopupMenu(toolsmenu)
-        toolsmenu.Destroy()
-
-    def OnMeasure(self, event):
-        """
-        Init measurement routine that calculates
-        map distance along transect drawn on
-        map display
-        """
-
-        self.totaldist = 0.0 # total measured distance
-
-        # switch GIS Manager to output console to show measure results
-        self._layerManager.notebook.SetSelection(1)
-
-        # change mouse to draw line for measurement
-        self.MapWindow.mouse['use'] = "measure"
-        self.MapWindow.mouse['box'] = "line"
-        self.MapWindow.zoomtype = 0
-        self.MapWindow.pen     = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
-        self.MapWindow.polypen = wx.Pen(colour='green', width=2, style=wx.SHORT_DASH)
-
-        # change the cursor
-        self.MapWindow.SetCursor(self.cursors["pencil"])
-
-        # initiating output
-        style = self._layerManager.goutput.cmd_output.StyleWarning
-        self._layerManager.goutput.WriteLog(_('Click and drag with left mouse button '
-                                              'to measure.%s'
-                                              'Double click with left button to clear.') % \
-                                                (os.linesep), style)
-        if self.Map.projinfo['proj'] != 'xy':
-            units = self.Map.projinfo['units']
-            style = self._layerManager.goutput.cmd_output.StyleCommand
-            self._layerManager.goutput.WriteLog(_('Measuring distance') + ' ('
-                                                + units + '):',
-                                                style)
-        else:
-            self._layerManager.goutput.WriteLog(_('Measuring distance:'),
-                                                style)
-
-    def MeasureDist(self, beginpt, endpt):
-        """!Calculate map distance from screen distance
-        and print to output window
-        """
-        if self._layerManager.notebook.GetSelection() != 1:
-            self._layerManager.notebook.SetSelection(1)
-
-        dist, (north, east) = self.MapWindow.Distance(beginpt, endpt)
-
-        dist = round(dist, 3)
-        d, dunits = self.FormatDist(dist)
-
-        self.totaldist += dist
-        td, tdunits = self.FormatDist(self.totaldist)
-
-        strdist = str(d)
-        strtotdist = str(td)
-
-        if self.Map.projinfo['proj'] == 'xy' or 'degree' not in self.Map.projinfo['unit']:
-            angle = int(math.degrees(math.atan2(north,east)) + 0.5)
-            angle = 180 - angle
-            if angle < 0:
-                angle = 360+angle
-
-            mstring = 'segment = %s %s\ttotal distance = %s %s\tbearing = %d deg' \
-                % (strdist,dunits,strtotdist,tdunits,angle)
-        else:
-            mstring = 'segment = %s %s\ttotal distance = %s %s' \
-                % (strdist,dunits,strtotdist,tdunits)
-
-        self._layerManager.goutput.WriteLog(mstring)
-
-        return dist
-
-    def Profile(self, event):
-        """
-        Init profile canvas and tools
-        """
-        raster = []
-        if self.tree.layer_selected and \
-                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
-            raster.append(self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
-
-        self.profile = profile.ProfileFrame(self,
-                                            id=wx.ID_ANY, pos=wx.DefaultPosition, size=(700,300),
-                                            style=wx.DEFAULT_FRAME_STYLE, rasterList=raster)
-        self.profile.Show()
-        # Open raster select dialog to make sure that a raster (and the desired raster)
-        # is selected to be profiled
-        self.profile.OnSelectRaster(None)
-
     def FormatDist(self, dist):
         """!Format length numbers and units in a nice way,
         as a function of length. From code by Hamish Bowman
@@ -1475,178 +1022,12 @@
 
         return (outdist, outunits)
 
-
-    def Histogram(self, event):
-        """
-        Init histogram display canvas and tools
-        """
-        self.histogram = histogram.HistFrame(self,
-                                             id=wx.ID_ANY, size=globalvar.HIST_WINDOW_SIZE,
-                                             style=wx.DEFAULT_FRAME_STYLE)
-
-        #show new display
-        self.histogram.Show()
-        self.histogram.Refresh()
-        self.histogram.Update()
-
-
-    def OnDecoration(self, event):
-        """
-        Decorations overlay menu
-        """
-        point = wx.GetMousePosition()
-        decmenu = wx.Menu()
-        # Add items to the menu
-        AddScale = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addbarscale"].GetLabel())
-        AddScale.SetBitmap(Icons["addbarscale"].GetBitmap(self.iconsize))
-        decmenu.AppendItem(AddScale)
-        self.Bind(wx.EVT_MENU, self.OnAddBarscale, AddScale)
-
-        AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addlegend"].GetLabel())
-        AddLegend.SetBitmap(Icons["addlegend"].GetBitmap(self.iconsize))
-        decmenu.AppendItem(AddLegend)
-        self.Bind(wx.EVT_MENU, self.OnAddLegend, AddLegend)
-
-        AddText = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addtext"].GetLabel())
-        AddText.SetBitmap(Icons["addtext"].GetBitmap(self.iconsize))
-        decmenu.AppendItem(AddText)
-        self.Bind(wx.EVT_MENU, self.OnAddText, AddText)
-
-        # Popup the menu.  If an item is selected then its handler
-        # will be called before PopupMenu returns.
-        self.PopupMenu(decmenu)
-        decmenu.Destroy()
-
-    def OnAddBarscale(self, event):
-        """
-        Handler for scale/arrow map decoration menu selection.
-        """
-        if self.dialogs['barscale']:
-            return
-
-        id = 0 # unique index for overlay layer
-
-        # If location is latlon, only display north arrow (scale won't work)
-        #        proj = self.Map.projinfo['proj']
-        #        if proj == 'll':
-        #            barcmd = 'd.barscale -n'
-        #        else:
-        #            barcmd = 'd.barscale'
-
-        # decoration overlay control dialog
-        self.dialogs['barscale'] = \
-            gdialogs.DecorationDialog(parent=self, title=_('Scale and North arrow'),
-                                      size=(350, 200),
-                                      style=wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
-                                      cmd=['d.barscale', 'at=0,5'],
-                                      ovlId=id,
-                                      name='barscale',
-                                      checktxt = _("Show/hide scale and North arrow"),
-                                      ctrltxt = _("scale object"))
-
-        self.dialogs['barscale'].CentreOnParent()
-        ### dialog cannot be show as modal - in the result d.barscale is not selectable
-        ### self.dialogs['barscale'].ShowModal()
-        self.dialogs['barscale'].Show()
-        self.MapWindow.mouse['use'] = 'pointer'        
-
-    def OnAddLegend(self, event):
-        """
-        Handler for legend map decoration menu selection.
-        """
-        if self.dialogs['legend']:
-            return
-        
-        id = 1 # index for overlay layer in render
-
-        cmd = ['d.legend', 'at=5,50,2,5']
-        if self.tree.layer_selected and \
-                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
-            cmd.append('map=%s' % self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
-
-        # Decoration overlay control dialog
-        self.dialogs['legend'] = \
-            gdialogs.DecorationDialog(parent=self, title=('Legend'),
-                                      size=(350, 200),
-                                      style=wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
-                                      cmd=cmd,
-                                      ovlId=id,
-                                      name='legend',
-                                      checktxt = _("Show/hide legend"),
-                                      ctrltxt = _("legend object")) 
-
-        self.dialogs['legend'].CentreOnParent() 
-        ### dialog cannot be show as modal - in the result d.legend is not selectable
-        ### self.dialogs['legend'].ShowModal()
-        self.dialogs['legend'].Show()
-        self.MapWindow.mouse['use'] = 'pointer'
-
-    def OnAddText(self, event):
-        """
-        Handler for text decoration menu selection.
-        """
-        if self.MapWindow.dragid > -1:
-            id = self.MapWindow.dragid
-        else:
-            # index for overlay layer in render
-            if len(self.MapWindow.textdict.keys()) > 0:
-                id = self.MapWindow.textdict.keys()[-1] + 1
-            else:
-                id = 101
-
-        self.dialogs['text'] = gdialogs.TextLayerDialog(parent=self, ovlId=id, 
-                                                        title=_('Add text layer'),
-                                                        size=(400, 200))
-        self.dialogs['text'].CenterOnParent()
-
-        # If OK button pressed in decoration control dialog
-        if self.dialogs['text'].ShowModal() == wx.ID_OK:
-            text = self.dialogs['text'].GetValues()['text']
-            active = self.dialogs['text'].GetValues()['active']
-            coords, w, h = self.MapWindow.TextBounds(self.dialogs['text'].GetValues())
-        
-            # delete object if it has no text or is not active
-            if text == '' or active == False:
-                try:
-                    self.MapWindow.pdc.ClearId(id)
-                    self.MapWindow.pdc.RemoveId(id)
-                    del self.MapWindow.textdict[id]
-                except:
-                    pass
-                return
-
-            self.MapWindow.pdc.ClearId(id)
-            self.MapWindow.pdc.SetId(id)
-            self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
-            
-            self.MapWindow.Draw(self.MapWindow.pdcDec, img=self.MapWindow.textdict[id],
-                                drawid=id, pdctype='text', coords=coords)
-            
-            self.MapWindow.UpdateMap(render=False, renderVector=False)
-            
-        self.MapWindow.mouse['use'] = 'pointer'
-
-    def GetOptData(self, dcmd, type, params, propwin):
-        """
-        Callback method for decoration overlay command generated by
-        dialog created in menuform.py
-        """
-        # Reset comand and rendering options in render.Map. Always render decoration.
-        # Showing/hiding handled by PseudoDC
-        self.Map.ChangeOverlay(ovltype=type, type='overlay', name='', command=dcmd,
-                               l_active=True, l_render=False)
-        self.params[type] = params
-        self.propwin[type] = propwin
-
     def OnZoomToMap(self, event):
         """!
         Set display extents to match selected raster (including NULLs)
         or vector map.
         """
-        if self.toolbars['gcpdisp']:
-            self.MapWindow.ZoomToMap(layer = self.Map.GetListOfLayers())
-        else:
-            self.MapWindow.ZoomToMap()
+        self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
 
     def OnZoomToRaster(self, event):
         """!
@@ -1717,12 +1098,11 @@
     def SetProperties(self, render=False, mode=0, showCompExtent=False,
                       constrainRes=False, projection=False):
         """!Set properies of map display window"""
-        self.autoRender.SetValue(render)
-        self.toggleStatus.SetSelection(mode)
+        self.statusbarWin['render'].SetValue(render)
+        self.statusbarWin['toggle'].SetSelection(mode)
         self.StatusbarUpdate()
-        self.showRegion.SetValue(showCompExtent)
-        self.compResolution.SetValue(constrainRes)
-        self.projInfo.SetValue(projection)
+        self.statusbarWin['region'].SetValue(showCompExtent)
+        self.statusbarWin['resolution'].SetValue(constrainRes)
         if showCompExtent:
             self.MapWindow.regionCoords = []
         

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gdialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gdialogs.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gdialogs.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,19 +1,26 @@
-"""
+"""!
 @package gdialogs.py
 
- at brief Common dialog used in wxGUI.
+ at brief Various dialogs used in wxGUI.
 
 List of classes:
+ - ElementDialog
+ - LocationDialog
+ - MapsetDialog
  - NewVectorDialog
  - SavedRegion
  - DecorationDialog
  - TextLayerDialog 
  - LoadMapLayersDialog
- - MultiImportDialog
+ - ImportDialog
+ - GdalImportDialog
+ - DxfImportDialog
  - LayersList (used by MultiImport) 
  - SetOpacityDialog
+ - StaticWrapText
+ - ImageSizeDialog
 
-(C) 2008 by the GRASS Development Team
+(C) 2008-2010 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
@@ -25,7 +32,6 @@
 import os
 import sys
 import re
-import glob
 
 import wx
 import wx.lib.filebrowsebutton as filebrowse
@@ -34,109 +40,211 @@
 from grass.script import core as grass
 
 import gcmd
-import grassenv
 import globalvar
 import gselect
 import menuform
 import utils
 from preferences import globalSettings as UserSettings
 
-class NewVectorDialog(wx.Dialog):
-    """Create new vector map layer"""
-    def __init__(self, parent, id, title, disableAdd=False, disableTable=False,
-                style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+class ElementDialog(wx.Dialog):
+    """!General dialog to choose given element (location, mapset, vector map, etc.)"""
+    def __init__(self, parent, title, label, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 **kwargs):
+        
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+        self.btnOK     = wx.Button(parent = self.panel, id = wx.ID_OK)
+        self.btnOK.SetDefault()
+        self.btnOK.Enable(False)
+        
+        self.label = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                   label = label)
+        
+        self.element = None # must be defined 
+        
+        self.__Layout()
+        
+    def PostInit(self):
+        self.element.SetFocus()
+        self.element.Bind(wx.EVT_TEXT, self.OnElement)
+        
+    def OnElement(self, event):
+        """!Name for vector map layer given"""
+        if len(event.GetString()) > 0:
+            self.btnOK.Enable(True)
+        else:
+            self.btnOK.Enable(False)
+        
+    def __Layout(self):
+        """!Do layout"""
+        self.sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.dataSizer = wx.BoxSizer(wx.VERTICAL)
+        self.dataSizer.Add(self.label, proportion=0,
+                           flag=wx.ALL, border=1)
+        
+        # buttons
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOK)
+        btnSizer.Realize()
+        
+        self.sizer.Add(item=self.dataSizer, proportion=1,
+                       flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.sizer.Add(item=btnSizer, proportion=0,
+                       flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+    def GetElement(self):
+        """!Return (mapName, overwrite)"""
+        return self.element.GetValue()
+    
+class LocationDialog(ElementDialog):
+    """!Dialog used to select location"""
+    def __init__(self, parent, title = _("Select GRASS location and mapset"), id =  wx.ID_ANY):
+        ElementDialog.__init__(self, parent, title, label = _("Name of GRASS location:"))
 
-        wx.Dialog.__init__(self, parent, id, title, style=style)
+        self.element = gselect.LocationSelect(parent = self.panel, id = wx.ID_ANY,
+                                              size = globalvar.DIALOG_GSELECT_SIZE)
 
-        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
+        self.element1 = gselect.MapsetSelect(parent = self.panel, id = wx.ID_ANY,
+                                             size = globalvar.DIALOG_GSELECT_SIZE,
+                                             setItems = False)
+        
+        self.PostInit()
+        
+        self.__Layout()
+        self.SetMinSize(self.GetSize())
 
-        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
-        self.btnOK = wx.Button(self.panel, wx.ID_OK)
-        self.btnOK.SetDefault()
-        self.btnOK.Enable(False)
+    def __Layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=1)
+ 
+        self.dataSizer.Add(wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                         label = _("Name of mapset:")), proportion=0,
+                           flag=wx.EXPAND | wx.ALL, border=1)
 
-        self.label = wx.StaticText(parent=self.panel, id=wx.ID_ANY,
-                                   label=_("Name for new vector map:"))
-        self.mapName = gselect.Select(parent=self.panel, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE,
-                                      type='vector', mapsets=[grassenv.GetGRASSVariable('MAPSET'),])
-        self.mapName.SetFocus()
+        self.dataSizer.Add(self.element1, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=1)
+       
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def OnElement(self, event):
+        """!Select mapset given location name"""
+        location = event.GetString()
         
-        self.table = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
-                                 label=_("Create attribute table"))
+        if location:
+            dbase = grass.gisenv()['GISDBASE']
+            self.element1.SetItems(utils.GetListOfMapsets(dbase, location, selectable = True))
+            self.element1.SetSelection(0)
+            mapset = self.element1.GetStringSelection()
+        
+        if location and mapset:
+            self.btnOK.Enable(True)
+        else:
+            self.btnOK.Enable(False)
+
+    def GetValues(self):
+        """!Get location, mapset"""
+        return (self.GetElement(), self.element1.GetStringSelection())
+    
+class MapsetDialog(ElementDialog):
+    """!Dialog used to select mapset"""
+    def __init__(self, parent, title = _("Select mapset in GRASS location"),
+                 location = None, id =  wx.ID_ANY):
+        ElementDialog.__init__(self, parent, title, label = _("Name of mapset:"))
+        if location:
+            self.SetTitle(self.GetTitle() + ' <%s>' % location)
+        else:
+            self.SetTitle(self.GetTitle() + ' <%s>' % grass.gisenv()['LOCATION_NAME'])
+        
+        self.element = gselect.MapsetSelect(parent = self.panel, id = wx.ID_ANY,
+                                            size = globalvar.DIALOG_GSELECT_SIZE)
+        
+        self.PostInit()
+        
+        self.__Layout()
+        self.SetMinSize(self.GetSize())
+
+    def __Layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=1)
+        
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def GetMapset(self):
+        return self.GetElement()
+    
+class NewVectorDialog(ElementDialog):
+    """!Dialog for creating new vector map"""
+    def __init__(self, parent, id, title, disableAdd=False, disableTable=False,
+                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        
+        ElementDialog.__init__(self, parent, title, label = _("Name for new vector map:"))
+        
+        self.element = gselect.Select(parent=self.panel, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE,
+                                      type='vector', mapsets=[grass.gisenv()['MAPSET'],])
+        
+        self.table = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                 label = _("Create attribute table"))
         self.table.SetValue(True)
         if disableTable:
             self.table.Enable(False)
         
-        self.addbox = wx.CheckBox(parent=self.panel,
-                                  label=_('Add created map into layer tree'), style = wx.NO_BORDER)
+        self.addbox = wx.CheckBox(parent = self.panel,
+                                  label = _('Add created map into layer tree'), style = wx.NO_BORDER)
         if disableAdd:
             self.addbox.SetValue(True)
             self.addbox.Enable(False)
         else:
-            self.addbox.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
+            self.addbox.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
         
-        self.mapName.Bind(wx.EVT_TEXT, self.OnMapName)
+        self.PostInit()
         
         self.__Layout()
-
         self.SetMinSize(self.GetSize())
-
+        
     def OnMapName(self, event):
-        """Name for vector map layer given"""
-        if len(event.GetString()) > 0:
-            self.btnOK.Enable(True)
-        else:
-            self.btnOK.Enable(False)
-
+        """!Name for vector map layer given"""
+        self.OnElement(event)
+        
     def __Layout(self):
-        """Do layout"""
-        sizer = wx.BoxSizer(wx.VERTICAL)
-
-        dataSizer = wx.BoxSizer(wx.VERTICAL)
-        dataSizer.Add(self.label, proportion=0,
-                      flag=wx.ALL, border=1)
-        dataSizer.Add(self.mapName, proportion=0,
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion=0,
                       flag=wx.EXPAND | wx.ALL, border=1)
         
-        dataSizer.Add(self.table, proportion=0,
+        self.dataSizer.Add(self.table, proportion=0,
                       flag=wx.EXPAND | wx.ALL, border=1)
-
-        dataSizer.AddSpacer(5)
         
-        dataSizer.Add(item=self.addbox, proportion=0,
+        self.dataSizer.AddSpacer(5)
+        
+        self.dataSizer.Add(item=self.addbox, proportion=0,
                       flag=wx.EXPAND | wx.ALL, border=1)
-                
-        # buttons
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(self.btnCancel)
-        btnSizer.AddButton(self.btnOK)
-        btnSizer.Realize()
+        
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
 
-        sizer.Add(item=dataSizer, proportion=1,
-                  flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
-
-        sizer.Add(item=btnSizer, proportion=0,
-                  flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
-       
-        self.panel.SetSizer(sizer)
-        sizer.Fit(self)
-
     def GetName(self):
-        """Return (mapName, overwrite)"""
-        mapName = self.mapName.GetValue().split('@', 1)[0]
-
-        return mapName
-    
-def CreateNewVector(parent, cmdDef, title=_('Create new vector map'),
+        """!Return (mapName, overwrite)"""
+        return self.GetElement().split('@', 1)[0]
+            
+def CreateNewVector(parent, cmd, title=_('Create new vector map'),
                     exceptMap=None, log=None, disableAdd=False, disableTable=False):
-    """Create new vector map layer
+    """!Create new vector map layer
 
-    @cmdList tuple/list (cmd list, output paramater)
+    @cmd cmd (prog, **kwargs)
     
     @return tuple (name of create vector map, add to layer tree)
     @return None of failure
     """
-    cmd = cmdDef[0]
     dlg = NewVectorDialog(parent, wx.ID_ANY, title,
                           disableAdd, disableTable)
     if dlg.ShowModal() == wx.ID_OK:
@@ -151,13 +259,14 @@
         if outmap == '': # should not happen
             return (None, None)
         
-        cmd.append("%s=%s" % (cmdDef[1], outmap))
+        cmd[1][cmd[2]] = outmap
         
         try:
             listOfVectors = grass.list_grouped('vect')[grass.gisenv()['MAPSET']]
         except KeyError:
             listOfVectors = []
         
+        overwrite = False
         if not UserSettings.Get(group='cmd', key='overwrite', subkey='enabled') and \
                 outmap in listOfVectors:
             dlgOw = wx.MessageDialog(parent, message=_("Vector map <%s> already exists "
@@ -166,21 +275,23 @@
                                      caption=_("Overwrite?"),
                                      style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
             if dlgOw.ShowModal() == wx.ID_YES:
-                cmd.append('--overwrite')
+                overwrite = True
             else:
                 dlgOw.Destroy()
                 return (None, None)
 
         if UserSettings.Get(group='cmd', key='overwrite', subkey='enabled') is True:
-            cmd.append('--overwrite')
+            overwrite = True
         
         try:
-            gcmd.Command(cmd)
+            gcmd.RunCommand(prog = cmd[0],
+                            overwrite = overwrite,
+                            **cmd[1])
         except gcmd.GException, e:
             gcmd.GError(parent = self,
                         message = e)
             return (None, None)
-
+        
         #
         # create attribute table
         #
@@ -203,79 +314,74 @@
                             table = outmap,
                             key = key,
                             layer = '1')
-        
+            
         # return fully qualified map name
         if '@' not in outmap:
-            outmap += '@' + grassenv.GetGRASSVariable('MAPSET')
+            outmap += '@' + grass.gisenv()['MAPSET']
 
         if log:
             log.WriteLog(_("New vector map <%s> created") % outmap)
 
         return (outmap, dlg.addbox.IsChecked())
-
+    
     return (None, dlg.addbox.IsChecked())
 
 class SavedRegion(wx.Dialog):
-    def __init__(self, parent, id, title="", pos=wx.DefaultPosition, size=wx.DefaultSize,
-                 style=wx.DEFAULT_DIALOG_STYLE,
-                 loadsave='load'):
+    def __init__(self, parent, id = wx.ID_ANY, title="", loadsave='load',
+                 **kwargs):
+        """!Loading and saving of display extents to saved region file
+
+        @param loadsave load or save region?
         """
-        Loading and saving of display extents to saved region file
-        """
-        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+        wx.Dialog.__init__(self, parent, id, title, **kwargs)
 
         self.loadsave = loadsave
         self.wind = ''
-
+        
         sizer = wx.BoxSizer(wx.VERTICAL)
-
+        
         box = wx.BoxSizer(wx.HORIZONTAL)
+        label = wx.StaticText(parent=self, id=wx.ID_ANY)
+        box.Add(item=label, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
         if loadsave == 'load':
-            label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Load region:"))
-            box.Add(item=label, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
-            self.selection = gselect.Select(parent=self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE,
-                                            type='windows')
-            self.selection.SetFocus()
-            box.Add(item=self.selection, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
-            self.selection.Bind(wx.EVT_TEXT, self.OnSelection)
-
+            label.SetLabel(_("Load region:"))
+            selection = gselect.Select(parent=self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE,
+                                       type='windows')
         elif loadsave == 'save':
-            label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Save region:"))
-            box.Add(item=label, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
-            self.textentry = wx.TextCtrl(parent=self, id=wx.ID_ANY, value="",
-                                         size=globalvar.DIALOG_TEXTCTRL_SIZE)
-            self.textentry.SetFocus()
-            box.Add(item=self.textentry, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
-            self.textentry.Bind(wx.EVT_TEXT, self.OnText)
-
+            label.SetLabel(_("Save region:"))
+            selection = gselect.Select(parent=self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE,
+                                       type='windows', mapsets = [grass.gisenv()['MAPSET']])
+        
+        box.Add(item=selection, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5)
+        selection.SetFocus()
+        selection.Bind(wx.EVT_TEXT, self.OnRegion)
+        
         sizer.Add(item=box, proportion=0, flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL,
                   border=5)
-
+        
         line = wx.StaticLine(parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL)
         sizer.Add(item=line, proportion=0,
                   flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=5)
-
+        
         btnsizer = wx.StdDialogButtonSizer()
-
-        btn = wx.Button(self, wx.ID_OK)
+        
+        btn = wx.Button(parent = self, id = wx.ID_OK)
         btn.SetDefault()
         btnsizer.AddButton(btn)
-
-        btn = wx.Button(self, wx.ID_CANCEL)
+        
+        btn = wx.Button(parent = self, id = wx.ID_CANCEL)
         btnsizer.AddButton(btn)
         btnsizer.Realize()
-
+        
         sizer.Add(item=btnsizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
-
+        
         self.SetSizer(sizer)
         sizer.Fit(self)
-
-    def OnSelection(self, event):
+        self.Layout()
+        
+    def OnRegion(self, event):
         self.wind = event.GetString()
-
-    def OnText(self, event):
-        self.wind = event.GetString()
-
+    
 class DecorationDialog(wx.Dialog):
     """
     Controls setting options and displaying/hiding map overlay decorations
@@ -408,13 +514,13 @@
                 self.parent.MapWindow.overlays[self.ovlId]['propwin'].Show()
 
     def OnCancel(self, event):
-        """Cancel dialog"""
+        """!Cancel dialog"""
         self.parent.dialogs['barscale'] = None
 
         self.Destroy()
 
     def OnOK(self, event):
-        """Button 'OK' pressed"""
+        """!Button 'OK' pressed"""
         # enable or disable overlay
         self.parent.Map.GetOverlay(self.ovlId).SetActive(self.chkbox.IsChecked())
 
@@ -425,7 +531,7 @@
         self.OnCancel(None)
 
     def GetOptData(self, dcmd, layer, params, propwin):
-        """Process decoration layer data"""
+        """!Process decoration layer data"""
         # update layer data
         if params:
             self.parent.MapWindow.overlays[self.ovlId]['params'] = params
@@ -566,21 +672,21 @@
         self.Bind(wx.EVT_SPINCTRL,   self.OnRotation,   self.rotation)
 
     def OnRefit(self, event):
-        """Resize text entry to match text"""
+        """!Resize text entry to match text"""
         self.sizer.Fit(self)
 
     def OnText(self, event):
-        """Change text string"""
+        """!Change text string"""
         self.currText = event.GetString()
 
     def OnRotation(self, event):
-        """Change rotation"""
+        """!Change rotation"""
         self.currRot = event.GetInt()
 
         event.Skip()
 
     def OnSelectFont(self, event):
-        """Change font"""
+        """!Change font"""
         data = wx.FontData()
         data.EnableEffects(True)
         data.SetColour(self.currClr)         # set colour
@@ -601,7 +707,7 @@
         dlg.Destroy()
 
     def GetValues(self):
-        """Get text properties"""
+        """!Get text properties"""
         return { 'text' : self.currText,
                  'font' : self.currFont,
                  'color' : self.currClr,
@@ -610,7 +716,7 @@
                  'active' : self.chkbox.IsChecked() }
 
 class LoadMapLayersDialog(wx.Dialog):
-    """Load selected map layers (raster, vector) into layer tree"""
+    """!Load selected map layers (raster, vector) into layer tree"""
     def __init__(self, parent, title, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
         wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title, style=style)
 
@@ -628,16 +734,10 @@
         # buttons
         #
         btnCancel = wx.Button(self, wx.ID_CANCEL)
-        btnOk = wx.Button(self, wx.ID_OK, _("Load") )
+        btnOk = wx.Button(self, wx.ID_OK, _("&Load") )
         btnOk.SetDefault()
-
+        
         #
-        # bindigs
-        #
-        #btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
-        #btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
-
-        #
         # sizers & do layout
         #
         btnSizer = wx.StdDialogButtonSizer()
@@ -672,19 +772,24 @@
         self.layerType.SetSelection(0)
         bodySizer.Add(item=self.layerType,
                       pos=(0,1))
+
+        # select toggle
+        self.toggle = wx.CheckBox(parent=self, id=wx.ID_ANY,
+                                  label=_("Select toggle"))
+        self.toggle.SetValue(True)
+        bodySizer.Add(item=self.toggle,
+                      flag=wx.ALIGN_CENTER_VERTICAL,
+                      pos=(0,2))
         
         # mapset filter
         bodySizer.Add(item=wx.StaticText(parent=self, label=_("Mapset:")),
                       flag=wx.ALIGN_CENTER_VERTICAL,
                       pos=(1,0))
 
-        self.mapset = wx.ComboBox(parent=self, id=wx.ID_ANY,
-                                  style=wx.CB_SIMPLE | wx.CB_READONLY,
-                                  choices=utils.ListOfMapsets(get = 'accessible'),
-                                  size=(250,-1))
-        self.mapset.SetStringSelection(grassenv.GetGRASSVariable("MAPSET"))
+        self.mapset = gselect.MapsetSelect(parent = self)
+        self.mapset.SetStringSelection(grass.gisenv()['MAPSET'])
         bodySizer.Add(item=self.mapset,
-                      pos=(1,1))
+                      pos=(1,1), span=(1, 2))
 
         # map name filter
         bodySizer.Add(item=wx.StaticText(parent=self, label=_("Filter:")),
@@ -696,7 +801,7 @@
                                   size=(250,-1))
         bodySizer.Add(item=self.filter,
                       flag=wx.EXPAND,
-                      pos=(2,1))
+                      pos=(2,1), span=(1, 2))
 
         # layer list 
         bodySizer.Add(item=wx.StaticText(parent=self, label=_("List of maps:")),
@@ -707,29 +812,27 @@
                                       choices=[])
         bodySizer.Add(item=self.layers,
                       flag=wx.EXPAND,
-                      pos=(3,1))
+                      pos=(3,1), span=(1, 2))
 
         # bindings
         self.layerType.Bind(wx.EVT_CHOICE, self.OnChangeParams)
         self.mapset.Bind(wx.EVT_COMBOBOX, self.OnChangeParams)
         self.layers.Bind(wx.EVT_RIGHT_DOWN, self.OnMenu)
         self.filter.Bind(wx.EVT_TEXT, self.OnFilter)
+        self.toggle.Bind(wx.EVT_CHECKBOX, self.OnToggle)
         return bodySizer
 
     def LoadMapLayers(self, type, mapset):
-        """Load list of map layers
+        """!Load list of map layers
 
         @param type layer type ('raster' or 'vector')
         @param mapset mapset name
         """
-        list = gcmd.Command(['g.mlist',
-                             'type=%s' % type,
-                             'mapset=%s' % mapset])
-
-        self.map_layers = []
-        for map in list.ReadStdOutput():
-            self.map_layers.append(map)
-            
+        try:
+            self.map_layers = grass.mlist_grouped(type=type, mapset=mapset)[mapset]
+        except KeyError:
+            self.map_layers = []
+        
         self.layers.Set(self.map_layers)
         
         # check all items by default
@@ -737,7 +840,7 @@
             self.layers.Check(item)
 
     def OnChangeParams(self, event):
-        """Filter parameters changed by user"""
+        """!Filter parameters changed by user"""
         # update list of layer to be loaded
         self.LoadMapLayers(self.layerType.GetStringSelection()[:4],
                            self.mapset.GetStringSelection())
@@ -745,34 +848,45 @@
         event.Skip()
 
     def OnMenu(self, event):
-        """Table description area, context menu"""
+        """!Table description area, context menu"""
         if not hasattr(self, "popupID1"):
             self.popupDataID1 = wx.NewId()
             self.popupDataID2 = wx.NewId()
+            self.popupDataID3 = wx.NewId()
 
-            self.Bind(wx.EVT_MENU, self.OnSelectAll,   id=self.popupDataID1)
-            self.Bind(wx.EVT_MENU, self.OnDeselectAll, id=self.popupDataID2)
+            self.Bind(wx.EVT_MENU, self.OnSelectAll,    id=self.popupDataID1)
+            self.Bind(wx.EVT_MENU, self.OnSelectInvert, id=self.popupDataID2)
+            self.Bind(wx.EVT_MENU, self.OnDeselectAll,  id=self.popupDataID3)
 
         # generate popup-menu
         menu = wx.Menu()
         menu.Append(self.popupDataID1, _("Select all"))
-        menu.Append(self.popupDataID2, _("Deselect all"))
+        menu.Append(self.popupDataID2, _("Invert selection"))
+        menu.Append(self.popupDataID3, _("Deselect all"))
 
         self.PopupMenu(menu)
         menu.Destroy()
 
     def OnSelectAll(self, event):
-        """Select all map layer from list"""
+        """!Select all map layer from list"""
         for item in range(self.layers.GetCount()):
             self.layers.Check(item, True)
 
+    def OnSelectInvert(self, event):
+        """!Invert current selection"""
+        for item in range(self.layers.GetCount()):
+            if self.layers.IsChecked(item):
+                self.layers.Check(item, False)
+            else:
+                self.layers.Check(item, True)
+        
     def OnDeselectAll(self, event):
-        """Select all map layer from list"""
+        """!Select all map layer from list"""
         for item in range(self.layers.GetCount()):
             self.layers.Check(item, False)
 
     def OnFilter(self, event):
-        """Apply filter for map names"""
+        """!Apply filter for map names"""
         if len(event.GetString()) == 0:
            self.layers.Set(self.map_layers) 
            return 
@@ -790,8 +904,16 @@
         
         event.Skip()
 
+    def OnToggle(self, event):
+        """!Select toggle (check or uncheck all layers)"""
+        check = event.Checked()
+        for item in range(self.layers.GetCount()):
+            self.layers.Check(item, check)
+
+        event.Skip()
+        
     def GetMapLayers(self):
-        """Return list of checked map layers"""
+        """!Return list of checked map layers"""
         layerNames = []
         for indx in self.layers.GetSelections():
             # layers.append(self.layers.GetStringSelec(indx))
@@ -807,249 +929,39 @@
         return layerNames
     
     def GetLayerType(self):
-        """Get selected layer type"""
+        """!Get selected layer type"""
         return self.layerType.GetStringSelection()
     
-class MultiImportDialog(wx.Dialog):
-    """!Import dxf layers"""
+class ImportDialog(wx.Dialog):
+    """!Dialog for bulk import of various data (base class)"""
     def __init__(self, parent, type,
-                 id=wx.ID_ANY, title=_("Multiple import"),
-                 link = False,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
-
+                 id = wx.ID_ANY, title = _("Multiple import"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
         self.parent = parent # GMFrame 
         self.importType = type
-        self.link = link     # Link or import data (only for GDAL/OGR) 
         
         self.commandId = -1  # id of running command
-       
-        wx.Dialog.__init__(self, parent, id, title, style=style)
-
-        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
         
-        self.inputTitle = _("Source name")
+        wx.Dialog.__init__(self, parent, id, title, style=style,
+                           name = "MultiImportDialog")
         
-        self.inputBox = wx.StaticBox(parent=self.panel, id=wx.ID_ANY,
-                                     label=" %s " % self.inputTitle)
+        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
+        
         self.layerBox = wx.StaticBox(parent=self.panel, id=wx.ID_ANY,
                                      label=_(" List of %s layers ") % self.importType.upper())
-
-        #
-        # input
-        #
-        if self.importType == 'dxf':
-            inputFile = filebrowse.FileBrowseButton(parent=self.panel, id=wx.ID_ANY, 
-                                                     size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
-                                                     dialogTitle=_('Choose DXF file to import'),
-                                                     buttonText=_('Browse'),
-                                                     startDirectory=os.getcwd(), fileMode=0,
-                                                     changeCallback=self.OnSetInput,
-                                                     fileMask="DXF File (*.dxf)|*.dxf")
-            self.input = { 'file' : [_("DXF file:"),
-                                     inputFile,
-                                     list()] }
-            self.inputType = 'file'
-        else:
-            self.typeRadio = wx.RadioBox(parent = self.panel, id = wx.ID_ANY,
-                                         label = _('Source type'),
-                                         style = wx.RA_SPECIFY_COLS,
-                                         choices = [_("File"),
-                                                    _("Directory"),
-                                                    _("Database"),
-                                                    _("Protocol")])
-            self.typeRadio.SetSelection(0)
-            self.Bind(wx.EVT_RADIOBOX, self.OnChangeType)
-            
-            # input widgets
-            if self.importType == 'gdal':
-                filemask = 'GeoTIFF (*.tif)|*.tif'
-            else:
-                filemask = 'ESRI Shapefile (*.shp)|*.shp'
-            inputFile = filebrowse.FileBrowseButton(parent=self.panel, id=wx.ID_ANY, 
-                                                    size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
-                                                    dialogTitle=_('Choose input file'),
-                                                    buttonText=_('Browse'),
-                                                    startDirectory=os.getcwd(),
-                                                    changeCallback=self.OnSetInput,
-                                                    fileMask=filemask)
-            
-            inputDir = filebrowse.DirBrowseButton(parent=self.panel, id=wx.ID_ANY, 
-                                                  size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
-                                                  dialogTitle=_('Choose input directory'),
-                                                  buttonText=_('Browse'),
-                                                  startDirectory=os.getcwd(),
-                                                  changeCallback=self.OnSetInput)
-            inputDir.Hide()
-
-            inputDbFile = filebrowse.FileBrowseButton(parent=self.panel, id=wx.ID_ANY, 
-                                                      size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
-                                                      dialogTitle=_('Choose file'),
-                                                      buttonText=_('Browse'),
-                                                      startDirectory=os.getcwd(),
-                                                      changeCallback=self.OnSetInput)
-            inputDbFile.Hide()
-            
-            inputDbText = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
-            inputDbText.Hide()
-            inputDbText.Bind(wx.EVT_TEXT, self.OnSetInput)
-
-            inputDbChoice = wx.Choice(parent = self.panel, id = wx.ID_ANY)
-            inputDbChoice.Hide()
-            inputDbChoice.Bind(wx.EVT_CHOICE, self.OnSetInput)
-            
-            inputPro = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY)
-            inputPro.Hide()
-            inputPro.Bind(wx.EVT_TEXT, self.OnSetInput)
-
-            # format widget
-            self.formatText = wx.StaticText(self.panel, id=wx.ID_ANY, label=_("Format:"))
-            self.format = wx.Choice(parent = self.panel, id = wx.ID_ANY, size=(300, -1))
-            self.format.Bind(wx.EVT_CHOICE, self.OnSetFormat)
-
-            if self.importType == 'gdal': 
-                ret = gcmd.RunCommand('r.in.gdal',
-                                      quiet = True, read = True,
-                                      flags = 'f')
-            else: # ogr
-                ret = gcmd.RunCommand('v.in.ogr',
-                                      quiet = True, read = True,
-                                      flags = 'f')
-            
-            self.input = { 'file' : [_("File:"),
-                                     inputFile,
-                                     list()],
-                           'dir'  : [_("Directory:"),
-                                     inputDir,
-                                     list()],
-                           'db'   : [_("Database:"),
-                                     inputDbFile,
-                                     list()],
-                           'pro'  : [_("Protocol:"),
-                                     inputPro,
-                                     list()],
-                           'db-win' : { 'file'   : inputDbFile,
-                                        'text'   : inputDbText,
-                                        'choice' : inputDbChoice },
-                           }
-            
-            self.formatToExt = {
-                # raster
-                'GeoTIFF' : 'tif',
-                'Erdas Imagine Images (.img)' : '.img',
-                'Ground-based SAR Applications Testbed File Format (.gff)' : '.gff',
-                'Arc/Info Binary Grid' : 'adf',
-                'Portable Network Graphics' : 'png',
-                'JPEG JFIF' : 'jpg',
-                'Japanese DEM (.mem)' : 'mem',
-                'Graphics Interchange Format (.gif)' : 'gif',
-                'X11 PixMap Format' : 'xpm',
-                'MS Windows Device Independent Bitmap' : 'bmp',
-                'SPOT DIMAP' : '.dim',
-                'RadarSat 2 XML Product' : 'xml',
-                'EarthWatch .TIL' : '.til',
-                'ERMapper .ers Labelled' : '.ers',
-                'ERMapper Compressed Wavelets' : 'ecw',
-                'GRIdded Binary (.grb)' : 'grb',
-                'EUMETSAT Archive native (.nat)' : '.nat',
-                'Idrisi Raster A.1' : 'rst',
-                'Golden Software ASCII Grid (.grd)' : '.grd',
-                'Golden Software Binary Grid (.grd)' : 'grd',
-                'Golden Software 7 Binary Grid (.grd)' : 'grd',
-                'R Object Data Store' : 'r',
-                'USGS DOQ (Old Style)' : 'doq',
-                'USGS DOQ (New Style)' : 'doq',
-                'ENVI .hdr Labelled' : 'hdr',
-                'ESRI .hdr Labelled' : 'hdr',
-                'Generic Binary (.hdr Labelled)' : 'hdr',
-                'PCI .aux Labelled' : 'aux',
-                'EOSAT FAST Format' : 'fst',
-                'VTP .bt (Binary Terrain) 1.3 Format' : 'bt',
-                'FARSITE v.4 Landscape File (.lcp)' : 'lcp',
-                'Swedish Grid RIK (.rik)' : 'rik',
-                'USGS Optional ASCII DEM (and CDED)' : '.dem',
-                'Northwood Numeric Grid Format .grd/.tab' : '',
-                'Northwood Classified Grid Format .grc/.tab' : '',
-                'ARC Digitized Raster Graphics' : 'arc',
-                'Magellan topo (.blx)' : 'blx',
-                'SAGA GIS Binary Grid (.sdat)' : 'sdat',
-                # vector
-                'ESRI Shapefile' : 'shp',
-                'UK .NTF'        : 'ntf',
-                'SDTS'           : 'ddf',
-                'DGN'            : 'dgn',
-                'VRT'            : 'vrt',
-                'REC'            : 'rec',
-                'BNA'            : 'bna',
-                'CSV'            : 'csv',
-                'GML'            : 'gml',
-                'GPX'            : 'gpx',
-                'KML'            : 'kml',
-                'GMT'            : 'gmt',
-                'PGeo'           : 'mdb',
-                'XPlane'         : 'dat',
-                'AVCBin'         : 'adf',
-                'AVCE00'         : 'e00',
-                'DXF'            : 'dxf',
-                'Geoconcept'     : 'gxt',
-                'GeoRSS'         : 'xml',
-                'GPSTrackMaker'  : 'gtm',
-                'VFK'            : 'vfk' }
-            
-            if ret:
-                for line in ret.splitlines():
-                    format = line.strip().rsplit(':', -1)[1].strip()
-                    if format in ('Memory', 'Virtual Raster', 'In Memory Raster'):
-                        continue
-                    if format in ('PostgreSQL', 'SQLite',
-                                  'ODBC', 'ESRI Personal GeoDatabase',
-                                  'Rasterlite',
-                                  'PostGIS WKT Raster driver'):
-                        self.input['db'][2].append(format)
-                    elif format in ('GeoJSON',
-                                    'OGC Web Coverage Service',
-                                    'OGC Web Map Service',
-                                    'HTTP Fetching Wrapper'):
-                        self.input['pro'][2].append(format)
-                    else:
-                        self.input['file'][2].append(format)
-                        self.input['dir'][2].append(format)
-            
-            self.inputType = 'file'
-            
-            self.format.SetItems(self.input[self.inputType][2])
-            
-            if self.importType == 'gdal':
-                self.format.SetStringSelection('GeoTIFF')
-            elif self.importType == 'ogr':
-                self.format.SetStringSelection('ESRI Shapefile')
         
-        self.inputText = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
-                                       label = self.input[self.inputType][0],
-                                       size = (75, -1))
-        
         #
         # list of layers
         #
         self.list = LayersList(self.panel)
         self.list.LoadData()
-
-        self.add = wx.CheckBox(parent=self.panel, id=wx.ID_ANY)
-        if link:
-            self.add.SetLabel(_("Add linked layers into layer tree"))
-        else:
-            self.add.SetLabel(_("Add imported layers into layer tree"))
-
-        if not link and self.importType in ('gdal', 'ogr'):
-            self.overrideCheck = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
-                                             label=_("Override projection (use location's projection)"))
-            self.overrideCheck.SetValue(True)
-                                             
-        self.add.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
-
+        
         self.overwrite = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
                                      label=_("Allow output files to overwrite existing files"))
         self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
         
+        self.add = wx.CheckBox(parent=self.panel, id=wx.ID_ANY)
+        
         #
         # buttons
         #
@@ -1058,52 +970,19 @@
         self.btn_cancel.SetToolTipString(_("Close dialog"))
         self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
         # run
-        if link:
-            self.btn_run = wx.Button(parent=self.panel, id=wx.ID_OK, label= _("&Link"))
-            self.btn_run.SetToolTipString(_("Link selected layers"))
-        else:
-            self.btn_run = wx.Button(parent=self.panel, id=wx.ID_OK, label= _("&Import"))
-            self.btn_run.SetToolTipString(_("Import selected layers"))
+        self.btn_run = wx.Button(parent=self.panel, id=wx.ID_OK)
         self.btn_run.SetDefault()
         self.btn_run.Enable(False)
         self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
-        
-        self.__doLayout()
-        self.Layout()
 
-    def __doLayout(self):
+    def doLayout(self):
+        """!Do layout"""
         dialogSizer = wx.BoxSizer(wx.VERTICAL)
-        #
-        # input
-        #
-        inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
         
-        gridSizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
-       
-        gridSizer.Add(item=self.inputText,
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.AddGrowableCol(1)
-        self.inputTypeSizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.inputTypeSizer.Add(item=self.input[self.inputType][1], proportion = 1,
-                                flag = wx.ALIGN_CENTER_VERTICAL)
+        # dsn input
+        dialogSizer.Add(item = self.dsnInput, proportion = 0,
+                        flag = wx.EXPAND)
         
-        gridSizer.Add(item=self.inputTypeSizer,
-                      flag=wx.EXPAND | wx.ALL)
-        
-        if self.importType != 'dxf':
-            gridSizer.Add(item=self.formatText,
-                          flag=wx.ALIGN_CENTER_VERTICAL)
-            gridSizer.Add(item=self.format)
-        
-        inputSizer.Add(item=gridSizer, proportion=1,
-                       flag=wx.EXPAND | wx.ALL)
-
-        if self.importType != 'dxf':
-            dialogSizer.Add(item=self.typeRadio, proportion=0,
-                            flag=wx.ALL | wx.EXPAND, border=5)
-        dialogSizer.Add(item=inputSizer, proportion=0,
-                        flag=wx.ALL | wx.EXPAND, border=5)
-
         #
         # list of DXF layers
         #
@@ -1114,17 +993,17 @@
         
         dialogSizer.Add(item=layerSizer, proportion=1,
                         flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
-
+        
         if hasattr(self, "overrideCheck"):
             dialogSizer.Add(item=self.overrideCheck, proportion=0,
                             flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
-
+        
         dialogSizer.Add(item=self.overwrite, proportion=0,
                         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
         #
@@ -1146,64 +1025,100 @@
         self.panel.SetSizer(dialogSizer)
         dialogSizer.Fit(self.panel)
         
-        self.Layout()
         # auto-layout seems not work here - FIXME
         size = wx.Size(globalvar.DIALOG_GSELECT_SIZE[0] + 175, 400)
         self.SetMinSize(size)
         self.SetSize((size.width, size.height + 100))
         width = self.GetSize()[0]
         self.list.SetColumnWidth(col=1, width=width/2 - 50)
+        self.Layout()
         
-    def OnChangeType(self, event):
-        """!Datasource type changed"""
-        sel = event.GetSelection()
-        win = self.input[self.inputType][1]
-        self.inputTypeSizer.Remove(win)
-        win.Hide()
+    def OnCancel(self, event=None):
+        """!Close dialog"""
+        self.Close()
+
+    def OnRun(self, event):
+        """!Import/Link data (each layes as separate vector map)"""
+        pass
+    
+    def AddLayers(self, returncode):
+        """!Add imported/linked layers into layer tree"""
+        self.commandId += 1
         
-        if sel == 0:   # file
-            self.inputType = 'file'
-            format = self.input[self.inputType][2][0]
-            try:
-                ext = self.formatToExt[format]
-                if not ext:
-                    raise KeyError
-                format += ' (*.%s)|*.%s' % (ext, ext)
-            except KeyError:
-                format += ' (*.*)|*.*'
-            
-            win = filebrowse.FileBrowseButton(parent=self.panel, id=wx.ID_ANY, 
-                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
-                                              dialogTitle=_('Choose input file'),
-                                              buttonText=_('Browse'),
-                                              startDirectory=os.getcwd(),
-                                              changeCallback=self.OnSetInput,
-                                              fileMask = format)
-            self.input[self.inputType][1] = win
-        elif sel == 1: # directory
-            self.inputType = 'dir'
-        elif sel == 2: # database
-            self.inputType = 'db'
-        elif sel == 3: # protocol
-            self.inputType = 'pro'
+        if not self.add.IsChecked() or returncode != 0:
+            return
         
-        win = self.input[self.inputType][1]
-        self.inputTypeSizer.Add(item = win, proportion = 1,
-                                flag = wx.ALIGN_CENTER_VERTICAL)
-        win.SetValue('')
-        self.list.DeleteAllItems()
-        win.Show()
+        maptree = self.parent.curr_page.maptree
         
-        self.inputText.SetLabel(self.input[self.inputType][0])
-        self.format.SetItems(self.input[self.inputType][2])
-        self.format.SetSelection(0)
+        layer, output = self.list.GetLayers()[self.commandId]
         
-        self.inputTypeSizer.Layout()
+        if '@' not in output:
+            name = output + '@' + grass.gisenv()['MAPSET']
+        else:
+            name = output
+        # add imported layers into layer tree
+        if self.importType == 'gdal':
+            cmd = ['d.rast',
+                   'map=%s' % name]
+            if UserSettings.Get(group='cmd', key='rasterOverlay', subkey='enabled'):
+                cmd.append('-o')
+                
+            item = maptree.AddLayer(ltype='raster',
+                                    lname=name,
+                                    lcmd=cmd)
+        else:
+            item = maptree.AddLayer(ltype='vector',
+                                    lname=name,
+                                    lcmd=['d.vect',
+                                          'map=%s' % name])
+        maptree.mapdisplay.MapWindow.ZoomToMap()
         
-    def OnCancel(self, event=None):
-        """!Close dialog"""
-        self.Close()
+    def OnAbort(self, event):
+        """!Abort running import
 
+        @todo not yet implemented
+        """
+        pass
+
+class GdalImportDialog(ImportDialog):
+    """!Dialog for bulk import of various raster/vector data"""
+    def __init__(self, parent, ogr = False, link = False):
+        self.link = link
+        if ogr:
+            ImportDialog.__init__(self, parent, type = 'ogr')
+            if link:
+                self.SetTitle(_("Link vector data"))
+            else:
+                self.SetTitle(_("Import vector data"))
+        else:
+            ImportDialog.__init__(self, parent, type = 'gdal') 
+            if link:
+                self.SetTitle(_("Link raster data"))
+            else:
+                self.SetTitle(_("Import raster data"))
+       
+        self.dsnInput = gselect.GdalSelect(parent = self, panel = self.panel, ogr = ogr)
+
+        if not link:
+            self.overrideCheck = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
+                                             label=_("Override projection (use location's projection)"))
+        
+        if link:
+            self.add.SetLabel(_("Add linked layers into layer tree"))
+        else:
+            self.add.SetLabel(_("Add imported layers into layer tree"))
+        
+        self.add.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
+
+        if link:
+            self.btn_run.SetLabel(_("&Link"))
+            self.btn_run.SetToolTipString(_("Link selected layers"))
+        else:
+            self.btn_run.SetLabel(_("&Import"))
+            self.btn_run.SetToolTipString(_("Import selected layers"))
+        
+        self.doLayout()
+
     def OnRun(self, event):
         """!Import/Link data (each layes as separate vector map)"""
         data = self.list.GetLayers()
@@ -1211,30 +1126,13 @@
         # hide dialog
         self.Hide()
         
-        if self.importType == 'dxf':
-            inputDxf = self.input[self.inputType][1].GetValue()
-        else:
-            if self.inputType == 'file':
-                dsn = os.path.dirname(self.input[self.inputType][1].GetValue())
-            else:
-                if self.format.GetStringSelection() == 'PostgreSQL':
-                    dsn = 'PG:dbname=%s' % self.input[self.inputType][1].GetStringSelection()
-                else:
-                    dsn = self.input[self.inputType][1].GetValue()
-            try:
-                ext = '.' + self.formatToExt[self.format.GetStringSelection()]
-            except KeyError:
-                ext = ''
-        
+        dsn = self.dsnInput.GetDsn()
+        ext = self.dsnInput.GetFormatExt()
+            
         for layer, output in data:
-            if self.importType == 'dxf':
-                cmd = ['v.in.dxf',
-                       'input=%s' % inputDxf,
-                       'layers=%s' % layer,
-                       'output=%s' % output]
-            elif self.importType == 'ogr':
-                if layer.rfind(ext) > -1:
-                    layer = layer.replace(ext, '')
+            if self.importType == 'ogr':
+                if ext and layer.rfind(ext) > -1:
+                    layer = layer.replace('.' + ext, '')
                 if self.link:
                     cmd = ['v.external',
                            'dsn=%s' % dsn,
@@ -1246,19 +1144,22 @@
                            'layer=%s' % layer,
                            'output=%s' % output]
             else: # gdal
+                if self.dsnInput.GetType() == 'dir':
+                    idsn = os.path.join(dsn, layer)
+                
                 if self.link:
                     cmd = ['r.external',
-                           'input=%s' % (os.path.join(dsn, layer)),
+                           'input=%s' % idsn,
                            'output=%s' % output]
                 else:
                     cmd = ['r.in.gdal',
-                           'input=%s' % (os.path.join(dsn, layer)),
+                           'input=%s' % idsn,
                            'output=%s' % output]
             
             if self.overwrite.IsChecked():
                 cmd.append('--overwrite')
             
-            if hasattr(self, "overrideCheck") and self.overrideCheck.IsChecked():
+            if not self.link and self.overrideCheck.IsChecked():
                 cmd.append('-o')
             
             if UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'):
@@ -1266,186 +1167,90 @@
             
             # run in Layer Manager
             self.parent.goutput.RunCmd(cmd, switchPage=True,
-                                       onDone = self._addLayers)
+                                       onDone = self.AddLayers)
         
         self.OnCancel()
         
-    def _addLayers(self, returncode):
-        """!Add imported/linked layers to layer tree"""
-        self.commandId += 1
+class DxfImportDialog(ImportDialog):
+    """!Dialog for bulk import of DXF layers""" 
+    def __init__(self, parent):
+        ImportDialog.__init__(self, parent, type = 'dxf',
+                              title = _("Import DXF layers"))
         
-        if not self.add.IsChecked():
-            return
-
-        maptree = self.parent.curr_page.maptree
+        self.dsnInput = filebrowse.FileBrowseButton(parent=self.panel, id=wx.ID_ANY, 
+                                                    size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                                    dialogTitle=_('Choose DXF file to import'),
+                                                    buttonText=_('Browse'),
+                                                    startDirectory=os.getcwd(), fileMode=0,
+                                                    changeCallback=self.OnSetDsn,
+                                                    fileMask="DXF File (*.dxf)|*.dxf")
         
-        layer, output = self.list.GetLayers()[self.commandId]
+        self.add.SetLabel(_("Add imported layers into layer tree"))
         
-        if '@' not in output:
-            name = output + '@' + grass.gisenv()['MAPSET']
-        else:
-            name = output
-        # add imported layers into layer tree
-        if self.importType == 'gdal':
-            cmd = ['d.rast',
-                   'map=%s' % name]
-            if UserSettings.Get(group='cmd', key='rasterOverlay', subkey='enabled'):
-                cmd.append('-o')
-            
-            item = maptree.AddLayer(ltype='raster',
-                                    lname=name,
-                                    lcmd=cmd)
-        else:
-            item = maptree.AddLayer(ltype='vector',
-                                    lname=name,
-                                    lcmd=['d.vect',
-                                          'map=%s' % name])
-        maptree.mapdisplay.MapWindow.ZoomToMap()
+        self.add.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
         
-    def OnAbort(self, event):
-        """!Abort running import
-
-        @todo not yet implemented
-        """
-        pass
+        self.doLayout()
         
-    def OnSetFormat(self, event):
-        """!Format changed"""
-        if self.inputType not in ['file', 'db']:
-            return
+    def OnRun(self, event):
+        """!Import/Link data (each layes as separate vector map)"""
+        data = self.list.GetLayers()
         
-        win = self.input[self.inputType][1]
-        self.inputTypeSizer.Remove(win)
+        # hide dialog
+        self.Hide()
         
-        if self.inputType == 'file':
-            win.Destroy()
-        else: # database
-            win.Hide()
+        inputDxf = self.dsnInput.GetValue()
         
-        format = event.GetString()
-        
-        if self.inputType == 'file':
-            try:
-                ext = self.formatToExt[format]
-                if not ext:
-                    raise KeyError
-                format += ' (*.%s)|*.%s' % (ext, ext)
-            except KeyError:
-                format += ' (*.*)|*.*'
+        for layer, output in data:
+            cmd = ['v.in.dxf',
+                   'input=%s' % inputDxf,
+                   'layers=%s' % layer,
+                   'output=%s' % output]
             
-            win = filebrowse.FileBrowseButton(parent=self.panel, id=wx.ID_ANY, 
-                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
-                                              dialogTitle=_('Choose file'),
-                                              buttonText=_('Browse'),
-                                              startDirectory=os.getcwd(),
-                                              changeCallback=self.OnSetInput,
-                                              fileMask = format)
-        else: # database
-            if format == 'SQLite':
-                win = self.input['db-win']['file']
-            elif format == 'PostgreSQL':
-                if grass.find_program('psql'):
-                    win = self.input['db-win']['choice']
-                    if not win.GetItems():
-                        p = grass.Popen(['psql', '-ltA'], stdout = grass.PIPE)
-                        ret = p.communicate()[0]
-                        if ret:
-                            db = list()
-                            for line in ret.splitlines():
-                                sline = line.split('|')
-                                if len(sline) < 2:
-                                    continue
-                                dbname = sline[0]
-                                if dbname:
-                                    db.append(dbname)
-                            win.SetItems(db)
-                else:
-                    win = self.input['db-win']['text']
-            else:
-                win = self.input['db-win']['text']
+            if self.overwrite.IsChecked() or \
+                    UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'):
+                cmd.append('--overwrite')
+            
+            # run in Layer Manager
+            self.parent.goutput.RunCmd(cmd, switchPage=True,
+                                       onDone = self.AddLayers)
         
-        self.input[self.inputType][1] = win
-        if not win.IsShown():
-            win.Show()
-        self.inputTypeSizer.Add(item = win, proportion = 1,
-                                flag = wx.ALIGN_CENTER_VERTICAL)
-        self.inputTypeSizer.Layout()
-        
-    def OnSetInput(self, event):
-        """!Input DXF file/OGR dsn defined, update list of layer widget"""
+        self.OnCancel()
+
+    def OnSetDsn(self, event):
+        """!Input DXF file defined, update list of layer widget"""
         path = event.GetString()
         if not path:
             return 
-
+        
         data = list()        
-        if self.importType == 'dxf':
-            ret = gcmd.RunCommand('v.in.dxf',
-                                  quiet = True,
-                                  parent = self,
-                                  read = True,
-                                  flags = 'l',
-                                  input = path)
-            if not ret:
-                self.list.LoadData()
-                self.btn_run.Enable(False)
-                return
+        ret = gcmd.RunCommand('v.in.dxf',
+                              quiet = True,
+                              parent = self,
+                              read = True,
+                              flags = 'l',
+                              input = path)
+        if not ret:
+            self.list.LoadData()
+            self.btn_run.Enable(False)
+            return
             
-            for line in ret.splitlines():
-                layerId = line.split(':')[0].split(' ')[1]
-                layerName = line.split(':')[1].strip()
-                grassName = utils.GetValidLayerName(layerName)
-                data.append((layerId, layerName.strip(), grassName.strip()))
+        for line in ret.splitlines():
+            layerId = line.split(':')[0].split(' ')[1]
+            layerName = line.split(':')[1].strip()
+            grassName = utils.GetValidLayerName(layerName)
+            data.append((layerId, layerName.strip(), grassName.strip()))
         
-        else: # gdal/ogr (for ogr maybe to use v.in.ogr -l)
-            layerId = 1
-            if self.format.GetStringSelection() == 'PostgreSQL':
-                dsn = 'PG:dbname=%s' % self.input[self.inputType][1].GetStringSelection()
-            else:
-                dsn = self.input[self.inputType][1].GetValue()
-            if self.inputType == 'file':
-                baseName = os.path.basename(dsn)
-                grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
-                data.append((layerId, baseName, grassName))
-            elif self.inputType == 'dir':
-                try:
-                    ext = self.formatToExt[self.format.GetStringSelection()]
-                except KeyError:
-                    ext = ''
-                for file in glob.glob(os.path.join(dsn, "*.%s") % ext):
-                    baseName = os.path.basename(file)
-                    grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
-                    data.append((layerId, baseName, grassName))
-                    layerId += 1
-            elif self.inputType == 'db':
-                ret = gcmd.RunCommand('v.in.ogr',
-                                      quiet = True,
-                                      parent = self,
-                                      read = True,
-                                      flags = 'l',
-                                      dsn = dsn)
-                if not ret:
-                    self.list.LoadData()
-                    self.btn_run.Enable(False)
-                    return
-                layerId = 1
-                for line in ret.split(','):
-                    layerName = line.strip()
-                    grassName = utils.GetValidLayerName(layerName)
-                    data.append((layerId, layerName.strip(), grassName.strip()))
-                    layerId += 1
-        
         self.list.LoadData(data)
         if len(data) > 0:
             self.btn_run.Enable(True)
         else:
             self.btn_run.Enable(False)
-
+        
 class LayersList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,
-                 listmix.CheckListCtrlMixin):
-#                 listmix.CheckListCtrlMixin, listmix.TextEditMixin):
-    """List of layers to be imported (dxf, shp...)"""
-    def __init__(self, parent, pos=wx.DefaultPosition,
-                 log=None):
+                 listmix.CheckListCtrlMixin, listmix.TextEditMixin):
+    """!List of layers to be imported (dxf, shp...)"""
+    def __init__(self, parent, pos = wx.DefaultPosition,
+                 log = None):
         self.parent = parent
         
         wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
@@ -1455,8 +1260,8 @@
 
         # setup mixins
         listmix.ListCtrlAutoWidthMixin.__init__(self)
-        # listmix.TextEditMixin.__init__(self)
-        
+        listmix.TextEditMixin.__init__(self)
+
         self.InsertColumn(0, _('Layer'))
         self.InsertColumn(1, _('Layer name'))
         self.InsertColumn(2, _('Name for GRASS map'))
@@ -1465,7 +1270,7 @@
         self.Bind(wx.EVT_RIGHT_UP,            self.OnPopupMenu) #wxGTK
 
     def LoadData(self, data=None):
-        """Load data into list"""
+        """!Load data into list"""
         if data is None:
             return
 
@@ -1481,7 +1286,7 @@
         self.SetColumnWidth(col=0, width=wx.LIST_AUTOSIZE_USEHEADER)
 
     def OnPopupMenu(self, event):
-        """Show popup menu"""
+        """!Show popup menu"""
         if self.GetItemCount() < 1:
             return
         
@@ -1501,7 +1306,7 @@
         menu.Destroy()
 
     def OnSelectAll(self, event):
-        """Select all items"""
+        """!Select all items"""
         item = -1
         
         while True:
@@ -1513,7 +1318,7 @@
         event.Skip()
         
     def OnSelectNone(self, event):
-        """Deselect items"""
+        """!Deselect items"""
         item = -1
         
         while True:
@@ -1525,7 +1330,7 @@
         event.Skip()
         
     def GetLayers(self):
-        """Get list of layers (layer name, output name)"""
+        """!Get list of layers (layer name, output name)"""
         data = []
         item = -1
         while True:
@@ -1540,7 +1345,7 @@
         return data
 
 class SetOpacityDialog(wx.Dialog):
-    """Set opacity of map layers"""
+    """!Set opacity of map layers"""
     def __init__(self, parent, id=wx.ID_ANY, title=_("Set Map Layer Opacity"),
                  size=wx.DefaultSize, pos=wx.DefaultPosition,
                  style=wx.DEFAULT_DIALOG_STYLE, opacity=100):
@@ -1602,9 +1407,180 @@
         self.Layout()
 
     def GetOpacity(self):
-        """Button 'OK' pressed"""
+        """!Button 'OK' pressed"""
         # return opacity value
         opacity = float(self.value.GetValue()) / 100
         return opacity
 
+def GetImageHandlers(image):
+    """!Get list of supported image handlers"""
+    lext = list()
+    ltype = list()
+    for h in image.GetHandlers():
+        lext.append(h.GetExtension())
+        
+    filetype = ''
+    if 'png' in lext:
+        filetype += "PNG file (*.png)|*.png|"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_PNG,
+                       'ext'  : 'png' })
+    filetype +=  "BMP file (*.bmp)|*.bmp|"
+    ltype.append({ 'type' : wx.BITMAP_TYPE_BMP,
+                   'ext'  : 'bmp' })
+    if 'gif' in lext:
+        filetype += "GIF file (*.gif)|*.gif|"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_GIF,
+                       'ext'  : 'gif' })
+        
+    if 'jpg' in lext:
+        filetype += "JPG file (*.jpg)|*.jpg|"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_JPEG,
+                       'ext'  : 'jpg' })
 
+    if 'pcx' in lext:
+        filetype += "PCX file (*.pcx)|*.pcx|"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_PCX,
+                       'ext'  : 'pcx' })
+        
+    if 'pnm' in lext:
+        filetype += "PNM file (*.pnm)|*.pnm|"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_PNM,
+                       'ext'  : 'pnm' })
+
+    if 'tif' in lext:
+        filetype += "TIF file (*.tif)|*.tif|"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_TIF,
+                       'ext'  : 'tif' })
+
+    if 'xpm' in lext:
+        filetype += "XPM file (*.xpm)|*.xpm"
+        ltype.append({ 'type' : wx.BITMAP_TYPE_XPM,
+                       'ext'  : 'xpm' })
+    
+    return filetype, ltype
+
+class StaticWrapText(wx.StaticText):
+    """! A Static Text field that wraps its text to fit its width,
+    enlarging its height if necessary.
+    """
+    def __init__(self, parent, id = wx.ID_ANY, label = '', *args, **kwds):
+        self.parent        = parent
+        self.originalLabel = label
+        
+        wx.StaticText.__init__(self, parent, id, label = '', *args, **kwds)
+        
+        self.SetLabel(label)
+        self.Bind(wx.EVT_SIZE, self.OnResize)
+    
+    def SetLabel(self, label):
+        self.originalLabel = label
+        self.wrappedSize = None
+        self.OnResize(None)
+
+    def OnResize(self, event):
+        if not getattr(self, "resizing", False):
+            self.resizing = True
+            newSize = wx.Size(self.parent.GetSize().width,
+                              self.GetSize().height)
+            if self.wrappedSize != newSize:
+                wx.StaticText.SetLabel(self, self.originalLabel)
+                self.Wrap(newSize.width)
+                self.wrappedSize = newSize
+                
+                self.SetSize(self.wrappedSize)
+            del self.resizing
+
+class ImageSizeDialog(wx.Dialog):
+    """!Set size for saved graphic file"""
+    def __init__(self, parent, id = wx.ID_ANY, title=_("Set image size"),
+                 style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
+        self.parent = parent
+        
+        wx.Dialog.__init__(self, parent, id = id, style=style, title=title, **kwargs)
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.box = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                label = ' % s' % _("Image size"))
+        
+        size = self.parent.GetWindow().GetClientSize()
+        self.width = wx.SpinCtrl(parent = self.panel, id = wx.ID_ANY,
+                                 style = wx.SP_ARROW_KEYS)
+        self.width.SetRange(20, 1e6)
+        self.width.SetValue(size.width)
+        wx.CallAfter(self.width.SetFocus)
+        self.height = wx.SpinCtrl(parent = self.panel, id = wx.ID_ANY,
+                                  style = wx.SP_ARROW_KEYS)
+        self.height.SetRange(20, 1e6)
+        self.height.SetValue(size.height)
+        self.template = wx.Choice(parent = self.panel, id = wx.ID_ANY,
+                                  size = (125, -1),
+                                  choices = [ "",
+                                              "640x480",
+                                              "800x600",
+                                              "1024x768",
+                                              "1280x960",
+                                              "1600x1200",
+                                              "1920x1440" ])
+        
+        self.btnOK = wx.Button(parent = self.panel, id = wx.ID_OK)
+        self.btnOK.SetDefault()
+        self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+        
+        self.template.Bind(wx.EVT_CHOICE, self.OnTemplate)
+        
+        self._layout()
+        self.SetSize(self.GetBestSize())
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # body
+        box = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
+        fbox = wx.FlexGridSizer(cols = 2, vgap = 5, hgap = 5)
+        fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                      label = _("Width:")),
+                 flag = wx.ALIGN_CENTER_VERTICAL)
+        fbox.Add(item = self.width)
+        fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                      label = _("Height:")),
+                 flag = wx.ALIGN_CENTER_VERTICAL)
+        fbox.Add(item = self.height)
+        fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                      label = _("Template:")),
+                 flag = wx.ALIGN_CENTER_VERTICAL)
+        fbox.Add(item = self.template)
+        
+        box.Add(item = fbox, proportion = 1,
+                flag = wx.EXPAND | wx.ALL, border = 5)
+        sizer.Add(item = box, proportion = 1,
+                  flag=wx.EXPAND | wx.ALL, border = 3)
+        
+        # buttons
+        btnsizer = wx.StdDialogButtonSizer()
+        btnsizer.AddButton(self.btnOK)
+        btnsizer.AddButton(self.btnCancel)
+        btnsizer.Realize()
+
+        sizer.Add(item = btnsizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.ALL, border=5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        self.Layout()
+    
+    def GetValues(self):
+        """!Get width/height values"""
+        return self.width.GetValue(), self.height.GetValue()
+    
+    def OnTemplate(self, event):
+        """!Template selected"""
+        sel = event.GetString()
+        if not sel:
+            width, height = self.parent.GetWindow().GetClientSize()
+        else:
+            width, height = map(int, sel.split('x'))
+        self.width.SetValue(width)
+        self.height.SetValue(height)
+        

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/georect.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/georect.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/georect.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -16,7 +16,6 @@
  - GrSettingsDialog
 
 (C) 2006-2010 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.
 
@@ -80,9 +79,8 @@
         #
         # get environmental variables
         #
-        p = gcmd.Command(['g.gisenv', 'get=GISDBASE'])
-        self.grassdatabase = p.ReadStdOutput()[0]
-
+        self.grassdatabase = grass.gisenv()['GISDBASE']
+        
         #
         # read original environment settings
         #
@@ -189,15 +187,15 @@
             #
             # start map display
             #
-            self.xy_mapdisp = mapdisp.MapFrame(self.gcpmgr, title=_("Set ground control points (GCPs)"),
+            self.xy_mapdisp = mapdisp.MapFrame(self.gcpmgr, name = "GRMapWindow",
                                                size=globalvar.MAP_WINDOW_SIZE,
                                                toolbars=["georect"],
-                                               Map=self.Map, gismgr=self.parent)
+                                               Map=self.Map, lmgr=self.parent)
             self.xy_mapdisp.SetTitle(_("GRASS GIS Map Display: 1" +
                                        " - Location: " + self.newlocation +
                                        " (source location)"))
-            self.xy_mapdisp.SetName("GRMapWindow")
-
+            self.xy_mapdisp.GetWindow().ResetZoomHistory()
+            
             self.gcpmgr.SetMapDisplay(self.xy_mapdisp)
             
             self.mapwin = self.xy_mapdisp.MapWindow
@@ -221,7 +219,7 @@
             self.Cleanup()
                             
     def SetSrcEnv(self, location, mapset):
-        """Create environment to use for location and mapset
+        """!Create environment to use for location and mapset
         that are the source of the file(s) to georectify
 
         @param location source location
@@ -274,18 +272,18 @@
         return True
         
     def OnGLMFocus(self, event):
-        """Layer Manager focus"""
+        """!Layer Manager focus"""
         # self.SwitchEnv('original')
         
         event.Skip()
 
     def Cleanup(self):
-        """Return to current location and mapset"""
+        """!Return to current location and mapset"""
         self.SwitchEnv('original')
-                
         self.parent.georectifying = None
 
-        if hasattr(self, "xy_mapdisp"):
+        if hasattr(self, "xy_mapdisp") and \
+                self.xy_mapdisp:
             self.xy_mapdisp.Close()
             self.xy_mapdisp = None
 
@@ -307,22 +305,8 @@
         
         self.xylocation = ''
         self.xymapset = ''
-
-        tmplist = os.listdir(self.grassdatabase)
-        self.locList = []
-        self.mapsetList = []
         
         #
-        # create a list of valid locations
-        #
-        for item in tmplist:
-            if os.path.isdir(os.path.join(self.grassdatabase, item)) and \
-                    os.path.exists(os.path.join(self.grassdatabase, item, 'PERMANENT')):
-                self.locList.append(item)
-
-        utils.ListSortLower(self.locList)
-        
-        #
         # layout
         #
         self.sizer.AddGrowableCol(2)
@@ -339,9 +323,7 @@
         self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source location:')),
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 1))
-        self.cb_location = wx.ComboBox(parent=self, id=wx.ID_ANY, 
-                                     choices = self.locList, size=(300, -1),
-                                     style=wx.CB_DROPDOWN | wx.CB_READONLY)
+        self.cb_location = gselect.LocationSelect(parent = self, gisdbase = self.grassdatabase)
         self.sizer.Add(item=self.cb_location,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(2, 2))
@@ -350,9 +332,8 @@
         self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source mapset:')),
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(3, 1))
-        self.cb_mapset = wx.ComboBox(parent=self, id=wx.ID_ANY,
-                                     choices = self.mapsetList, size=(300, -1),
-                                     style=wx.CB_DROPDOWN | wx.CB_READONLY)
+        self.cb_mapset = gselect.MapsetSelect(parent = self, gisdbase = self.grassdatabase,
+                                              setItems = False)
         self.sizer.Add(item=self.cb_mapset,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
                        pos=(3,2))
@@ -368,7 +349,7 @@
         # self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
 
     def OnMaptype(self,event):
-        """Change map type"""
+        """!Change map type"""
         global maptype
 
         if event.GetInt() == 0:
@@ -377,7 +358,7 @@
             maptype = 'vector'
         
     def OnLocation(self, event):
-        """Sets source location for map(s) to georectify"""
+        """!Sets source location for map(s) to georectify"""
         self.xylocation = event.GetString()
         
         #create a list of valid mapsets
@@ -399,7 +380,7 @@
             wx.FindWindowById(wx.ID_FORWARD).Enable(True)
 
     def OnMapset(self, event):
-        """Sets source mapset for map(s) to georectify"""
+        """!Sets source mapset for map(s) to georectify"""
         if self.xylocation == '':
             wx.MessageBox(_('You must select a valid location before selecting a mapset'))
             return
@@ -465,7 +446,6 @@
         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
         self.btn_mkgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Create/edit group..."))
         self.btn_vgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Add vector map to group..."))
-        self.btn_vgroup.Hide()
         btnSizer.Add(item=self.btn_mkgroup,
                      flag=wx.RIGHT, border=5)
 
@@ -495,17 +475,20 @@
         self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
         self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
 
+        # hide vector group button by default
+        self.btn_vgroup.Hide()
+
     def OnGroup(self, event):        
         self.xygroup = event.GetString()
         
     def OnMkGroup(self, event):
-        """Create new group in source location/mapset"""
+        """!Create new group in source location/mapset"""
         menuform.GUI().ParseCommand(['i.group'],
                                     completed=(self.GetOptData, None, ''),
                                     parentframe=self.parent.parent, modal=True)
-                                    
+
     def OnVGroup(self, event):
-        """Add vector maps to group"""
+        """!Add vector maps to group"""
         dlg = VectGroup(parent = self,
                         id = wx.ID_ANY,
                         grassdb = self.grassdatabase,
@@ -520,7 +503,7 @@
         self.OnEnterPage()
         
     def GetOptData(self, dcmd, layer, params, propwin):
-        """Process i.group"""
+        """!Process i.group"""
         # update the page
         if dcmd:
             gcmd.Command(dcmd)
@@ -613,7 +596,7 @@
                        pos=(1, 1))
         
         self.selection = gselect.Select(self, id=wx.ID_ANY,
-                                        size=globalvar.DIALOG_GSELECT_SIZE)
+                                        size=globalvar.DIALOG_GSELECT_SIZE, type='cell')
         
         self.sizer.Add(item=self.selection,
                        flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
@@ -628,7 +611,7 @@
         self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
 
     def OnSelection(self,event):
-        """Map to display selected"""
+        """!Map to display selected"""
         global xy_map
         global maptype
 
@@ -642,9 +625,13 @@
         try:
         # set computational region to match selected map and zoom display to region
             if maptype == 'cell':
-                p = gcmd.RunCommand('g.region', rast = xy_map)
+                p = gcmd.Command(['g.region', 'rast=xy_map'])
             elif maptype == 'vector':
-                p = gcmd.RunCommand('g.region', vect = xy_map)       
+                p = gcmd.Command(['g.region', 'vect=xy_map'])
+            
+            if p.returncode == 0:
+                print 'returncode = ', str(p.returncode)
+                self.parent.Map.region = self.parent.Map.GetRegion()
         except:
             pass
 
@@ -655,12 +642,13 @@
             wx.MessageBox(_('You must select a valid image/map in order to continue'))
             event.Veto()
             return
+
         self.parent.SwitchEnv('original')
         
     def OnEnterPage(self, event=None):
         global maptype
         global xy_map
-        
+
         self.selection.SetElementList(maptype,
                                       mapsets = [self.parent.newmapset, ])
 
@@ -670,17 +658,16 @@
             wx.FindWindowById(wx.ID_FORWARD).Enable(True)
 
 class GCP(wx.Frame):
-    """
+    """!
     Manages ground control points for georectifying. Calculates RMS statics.
     Calls i.rectify or v.transform to georectify map.
     """
+    def __init__(self, parent, grwiz, mapdisp = None, id = wx.ID_ANY,
+                 title = _("Define/manage ground control points"),
+                 size = (625, 300)):
 
-    def __init__(self, parent, grwiz, mapdisp=None, id=wx.ID_ANY,
-                 title=_("Define/manage ground control points"),
-                 size=wx.DefaultSize):
+        wx.Frame.__init__(self, parent, id, title, size = size, name = "GCPFrame")
 
-        wx.Frame.__init__(self, parent, id, title, size=(625, 300))
-
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
         
         #
@@ -704,6 +691,7 @@
         self.xymapset = self.grwiz.gisrc_dict['MAPSET']
         self.xygroup = self.grwiz.grouppage.xygroup
         self.extension = self.grwiz.grouppage.extension
+        self.outname = ''
         self.VectGRList = []
 
         self.file = {
@@ -751,7 +739,7 @@
         #
         # toolbar and display for xy map
         #
-        self.toolbar = toolbars.GCPToolbar(parent=self, tbframe=self).GetToolbar()
+        self.toolbar = toolbars.GCPToolbar(parent=self)
         self.SetToolBar(self.toolbar)
         
         self.SetMapDisplay(self.mapdisp)
@@ -809,7 +797,7 @@
         # sizer.Fit(self)
 
     def __del__(self):
-        """Disable georectification mode"""
+        """!Disable georectification mode"""
         self.parent.georectifying = None
         
     def ClipRegion(self, event):
@@ -826,19 +814,19 @@
         """
         # check to see if we are georectifying map in current working location/mapset
         if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
-            cmdlist = ['i.target',
-                       '-c',
-                       'group=%s' % tgroup]
+            gcmd.RunCommand('i.target',
+                            parent = self,
+                            flags = 'c',
+                            group = tgroup)
         else:
             self.grwiz.SwitchEnv('new')
-            cmdlist = ['i.target',
-                       'group=%s' % tgroup,
-                       'location=%s' % tlocation,
-                       'mapset=%s' % tmapset]
-        gcmd.Command(cmd=cmdlist, stderr=None)
+            gcmd.RunCommand('i.target',
+                            parent = self,
+                            group = tgroup,
+                            location = tlocation,
+                            mapset = tmapset)
+            self.grwiz.SwitchEnv('original')
 
-        self.grwiz.SwitchEnv('original')
-
     def AddGCP(self, event):
         """
         Appends an item to GCP list
@@ -1036,13 +1024,13 @@
         #    self.RMSError(self.xygroup, self.gr_order)
 
     def ReloadGCPs(self, event):
-        """Reload data from file"""
+        """!Reload data from file"""
 
-	# delete all items in mapcoordlist
-	del self.mapcoordlist
-	self.mapcoordlist = []
-
-        self.list.LoadData()
+ 	# delete all items in mapcoordlist
+ 	del self.mapcoordlist
+ 	self.mapcoordlist = []
+ 
+	self.list.LoadData()
     
     def OnFocus(self, event):
         # self.grwiz.SwitchEnv('new')
@@ -1091,30 +1079,11 @@
                        'extension=%s' % self.extension,'order=%s' % self.gr_order]
             if self.clip_to_region:
                 cmdlist.append('-c')
-                
-            p = gcmd.Command(cmdlist)
             
-            output = p.ReadStdOutput()
-            error = p.ReadErrOutput()
+            self.parent.goutput.RunCmd(cmdlist, compReg=False,
+                                       switchPage=True)
             
-            # printing to console
-            for i in error: 
-                try:
-                    msgtype, msg = i.split(':')
-                    if msgtype != 'GRASS_INFO_PERCENT':
-                        self.parent.goutput.WriteLog(text = _(msg), switchPage = True)
-                except:
-                    continue
-            for i in output: 
-                self.parent.goutput.WriteLog(text = _(msg), switchPage = True)
-            
-            if p.returncode == 0:
-                wx.MessageBox('Maps in group %s georectified successfully' % self.xygroup)
-            else:
-                wx.MessageBox('ERROR...',error)
-
             time.sleep(.1)
-            self.grwiz.SwitchEnv('original')
 
         elif maptype == 'vector':
             outmsg = ''
@@ -1150,32 +1119,26 @@
                 self.outname = vect + '_' + self.extension
                 self.parent.goutput.WriteLog(text = _('Transforming <%s>...') % vect,
                                              switchPage = True)
-                                             
-                p = gcmd.Command(['v.transform', 
-                                 '--o', 
-                                 'input=%s' % vect, 
-                                 'output=%s' % self.outname,
-                                 'points=%s' % self.file['points']])
+                msg = err = ''
 
-                output = p.ReadStdOutput()
-                error = p.ReadErrOutput()
-                                
-                # printing to console
-                for i in error: 
-                    try:
-                        msgtype, msg = i.split(':')
-                        if msgtype != 'GRASS_INFO_PERCENT':
-                            self.parent.goutput.WriteLog(text = _(msg), switchPage = True)
-                    except:
-                        continue
-                for i in output: 
-                    self.parent.goutput.WriteLog(text = _(i), switchPage = True)
-                        
-                if p.returncode == 0:
+                ret, out, err = gcmd.RunCommand('v.transform',
+                           flags = '-o',
+                           input=vect,
+                           output=self.outname,
+                           pointsfile=self.file['points'],
+                           getErrorMsg=True, read=True) 
+                
+                    
+                if ret == 0:
                     self.VectGRList.append(self.outname)
+                    print err
+                    # note: WriteLog doesn't handle GRASS_INFO_PERCENT well, so using a print here
+#                    self.parent.goutput.WriteLog(text = _(err), switchPage = True)
+                    self.parent.goutput.WriteLog(text = _(out), switchPage = True)
                 else:
                     self.parent.goutput.WriteError(_('Georectification of vector map <%s> failed') %
                                                            self.outname)
+                    self.parent.goutput.WriteError(_(err))
 
                 # FIXME
                 # Copying database information not working. 
@@ -1221,14 +1184,30 @@
                 else:
                     shutil.move(xyvpath, vpath)
                                                    
-        wx.MessageBox('For all vector maps georectified successfully, ' + '\n' +
-                      'you will need to copy any associated attribute tables' + '\n' +
-                      'and reconnect them to the georectified vectors')
+            wx.MessageBox('For all vector maps georectified successfully, ' + '\n' +
+                          'you will need to copy any attribute tables' + '\n' +
+                          'and reconnect them to the georectified vectors')
             
         self.grwiz.SwitchEnv('original')
+
+    def OnGeorectDone(self, **kargs):
+        """!Print final message"""
+        global maptype
+        if maptype == 'cell':
+            return
         
+        returncode = kargs['returncode']
+        
+        if returncode == 0:
+            self.VectGRList.append(self.outname)
+            print '*****vector list = ' + str(self.VectGRList)
+        else:
+            self.parent.goutput.WriteError(_('Georectification of vector map <%s> failed') %
+                                                   self.outname)
+
+         
     def OnSettings(self, event):
-        """Georectifier settings"""
+        """!Georectifier settings"""
         dlg = GrSettingsDialog(parent=self, id=wx.ID_ANY, title=_('Georectifier settings'))
         
         if dlg.ShowModal() == wx.ID_OK:
@@ -1237,7 +1216,7 @@
         dlg.Destroy()
 
     def OnQuit(self, event):
-        """Quit georectifier"""
+        """!Quit georectifier"""
         self.grwiz.Cleanup()
 
         self.Destroy()
@@ -1287,13 +1266,17 @@
         # get list of forward and reverse rms error values for each point
         self.grwiz.SwitchEnv('new')
         
-        p = gcmd.Command(['g.transform',
-                          'group=%s' % xygroup,
-                          'order=%s' % order])
+        ret = gcmd.RunCommand('g.transform',
+                              parent = self,
+                              read = True,
+                              group = xygroup,
+                              order = order)
         
         self.grwiz.SwitchEnv('original')
 
-        errlist = p.ReadStdOutput()
+        if ret:
+            errlist = ret.splitlines()
+        
         if errlist == []:
             return
         
@@ -1362,7 +1345,7 @@
             idx_col += 1
         
     def LoadData(self):
-        """Load data into list"""
+        """!Load data into list"""
         self.DeleteAllItems()
 
         if os.path.isfile(self.gcp.file['points']):
@@ -1381,7 +1364,7 @@
         self.ResizeColumns()
 
     def OnCheckItem(self, index, flag):
-        """Item is checked/unchecked"""
+        """!Item is checked/unchecked"""
         pass
     
     def AddGCPItem(self):
@@ -1425,7 +1408,7 @@
         return self.selected
         
     def ResizeColumns(self):
-        """Resize columns"""
+        """!Resize columns"""
         minWidth = 90
         for i in range(self.GetColumnCount()):
             self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
@@ -1435,7 +1418,7 @@
         self.SendSizeEvent()
 
     def GetSelected(self):
-        """Get index of selected item"""
+        """!Get index of selected item"""
         return self.selected
 
     def OnItemSelected(self, event):
@@ -1584,7 +1567,7 @@
         self.Layout()
         
     def MakeVGroup(self):
-        """Create VREF file"""
+        """!Create VREF file"""
         vgrouplist = []
         for item in range(self.listMap.GetCount()):
             if not self.listMap.IsChecked(item):
@@ -1602,7 +1585,7 @@
     def __init__(self, parent, data, id=wx.ID_ANY,
                  title=_("Edit GCP"),
                  style=wx.DEFAULT_DIALOG_STYLE):
-        """Dialog for editing GPC and map coordinates in list control"""
+        """!Dialog for editing GPC and map coordinates in list control"""
 
         wx.Dialog.__init__(self, parent, id, title=title, style=style)
 
@@ -1673,7 +1656,7 @@
         sizer.Fit(self)
 
     def GetValues(self, columns=None):
-        """Return list of values (as strings).
+        """!Return list of values (as strings).
         """
         valuelist = []
         try:
@@ -1709,7 +1692,7 @@
         self._do_layout()
         
     def _do_layout(self):
-        """Do layout"""
+        """!Do layout"""
         # dialog layout
         sizer = wx.BoxSizer(wx.VERTICAL)
 
@@ -1794,7 +1777,7 @@
                          value=wx.FindWindowById(self.symbol['width']).GetValue())
 
     def OnSave(self, event):
-        """Button 'Save' pressed"""
+        """!Button 'Save' pressed"""
         self.UpdateSettings()
         fileSettings = {}
         UserSettings.ReadSettingsFile(settings=fileSettings)
@@ -1804,10 +1787,10 @@
         self.Close()
 
     def OnApply(self, event):
-        """Button 'Apply' pressed"""
+        """!Button 'Apply' pressed"""
         self.UpdateSettings()
         self.Close()
 
     def OnCancel(self, event):
-        """Button 'Cancel' pressed"""
+        """!Button 'Cancel' pressed"""
         self.Close()

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,1194 @@
+"""!
+ at package help.py
+
+ at brief Help window
+
+Classes:
+ - SearchModuleWindow
+ - ItemTree
+ - MenuTreeWindow
+ - MenuTree
+ - AboutWindow
+ - InstallExtensionWindow
+ - ExtensionTree
+ - HelpFrame
+ - HelpWindow
+ - HelpPanel
+
+(C) 2008-2010 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 os
+
+import wx
+try:
+    import wx.lib.agw.customtreectrl as CT
+#    import wx.lib.agw.hyperlink as hl
+except ImportError:
+    import wx.lib.customtreectrl as CT
+#    import wx.lib.hyperlink as hl
+import wx.lib.flatnotebook as FN
+import  wx.lib.scrolledpanel as scrolled
+
+import menudata
+import gcmd
+import globalvar
+import gdialogs
+
+class HelpFrame(wx.Frame):
+    """!GRASS Quickstart help window"""
+    def __init__(self, parent, id, title, size, file):
+        wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # text
+        content = HelpPanel(parent = self)
+        content.LoadPage(file)
+        
+        sizer.Add(item = content, proportion=1, flag=wx.EXPAND)
+        
+        self.SetAutoLayout(True)
+        self.SetSizer(sizer)
+        self.Layout()
+
+class SearchModuleWindow(wx.Panel):
+    """!Search module window (used in MenuTreeWindow)"""
+    def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None,
+                 showChoice = True, showTip = False, **kwargs):
+        self.showTip    = showTip
+        self.showChoice = showChoice
+        self.cmdPrompt  = cmdPrompt
+        
+        wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+        
+        self._searchDict = { _('description') : 'description',
+                             _('command')     : 'command',
+                             _('keywords')    : 'keywords' }
+        
+        self.box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                label=" %s " % _("Find module(s)"))
+        
+        self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
+                                  choices = [_('description'),
+                                             _('keywords'),
+                                             _('command')])
+        self.searchBy.SetSelection(0)
+        
+        self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                  value = "", size = (-1, 25),
+                                  style = wx.TE_PROCESS_ENTER)
+        self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
+        
+        if self.showTip:
+            self.searchTip = gdialogs.StaticWrapText(parent = self, id = wx.ID_ANY,
+                                                     size = (-1, 35))
+        
+        if self.showChoice:
+            self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY)
+            if self.cmdPrompt:
+                self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
+            self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)
+        
+        self._layout()
+
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
+        gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(1)
+        
+        gridSizer.Add(item = self.searchBy,
+                      flag=wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        gridSizer.Add(item = self.search,
+                      flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1))
+        row = 1
+        if self.showTip:
+            gridSizer.Add(item = self.searchTip,
+                          flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
+            row += 1
+        
+        if self.showChoice:
+            gridSizer.Add(item = self.searchChoice,
+                          flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
+        
+        sizer.Add(item = gridSizer, proportion = 1)
+        
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def GetSelection(self):
+        """!Get selected element"""
+        selection = self.searchBy.GetStringSelection()
+        
+        return self._searchDict[selection]
+
+    def SetSelection(self, i):
+        """!Set selection element"""
+        self.searchBy.SetSelection(i)
+
+    def OnSearchModule(self, event):
+        """!Search module by keywords or description"""
+        if not self.cmdPrompt:
+            event.Skip()
+            return
+        
+        text = event.GetString()
+        if not text:
+            self.cmdPrompt.SetFilter(None)
+            mList = self.cmdPrompt.GetCommandItems()
+            self.searchChoice.SetItems(mList)
+            if self.showTip:
+                self.searchTip.SetLabel(_("%d modules found") % len(mList))
+            event.Skip()
+            return
+        
+        modules = dict()
+        iFound = 0
+        for module, data in self.cmdPrompt.moduleDesc.iteritems():
+            found = False
+            sel = self.searchBy.GetSelection()
+            if sel == 0: # -> description
+                if text in data['desc']:
+                    found = True
+            elif sel == 1: # keywords
+                if self.cmdPrompt.CheckKey(text, data['keywords']):
+                    found = True
+            else: # command
+                if module[:len(text)] == text:
+                    found = True
+            
+            if found:
+                iFound += 1
+                try:
+                    group, name = module.split('.')
+                except ValueError:
+                    continue # TODO
+                
+                if not modules.has_key(group):
+                    modules[group] = list()
+                modules[group].append(name)
+                
+        self.cmdPrompt.SetFilter(modules)
+        self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
+        if self.showTip:
+            self.searchTip.SetLabel(_("%d modules found") % iFound)
+        
+        event.Skip()
+        
+    def OnSelectModule(self, event):
+        """!Module selected from choice, update command prompt"""
+        cmd  = event.GetString().split(' ', 1)[0]
+        text = cmd + ' '
+        pos = len(text)
+
+        if self.cmdPrompt:
+            self.cmdPrompt.SetText(text)
+            self.cmdPrompt.SetSelectionStart(pos)
+            self.cmdPrompt.SetCurrentPos(pos)
+            self.cmdPrompt.SetFocus()
+        
+        desc = self.cmdPrompt.GetCommandDesc(cmd)
+        if self.showTip:
+            self.searchTip.SetLabel(desc)
+    
+    def Reset(self):
+        """!Reset widget"""
+        self.searchBy.SetSelection(0)
+        self.search.SetValue('')
+        
+class MenuTreeWindow(wx.Panel):
+    """!Show menu tree"""
+    def __init__(self, parent, id = wx.ID_ANY, **kwargs):
+        self.parent = parent # LayerManager
+        
+        wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+        
+        self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                    label=" %s " % _("Menu tree (double-click to run command)"))
+        # tree
+        self.tree = MenuTree(parent = self, data = menudata.ManagerData())
+        self.tree.Load()
+
+        # search widget
+        self.search = SearchModuleWindow(parent = self, showChoice = False)
+        
+        # buttons
+        self.btnRun   = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
+        self.btnRun.SetToolTipString(_("Run selected command"))
+        self.btnRun.Enable(False)
+        
+        # bindings
+        self.btnRun.Bind(wx.EVT_BUTTON,            self.OnRun)
+        self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
+        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED,    self.OnItemSelected)
+        self.search.Bind(wx.EVT_TEXT_ENTER,        self.OnShowItem)
+        self.search.Bind(wx.EVT_TEXT,              self.OnUpdateStatusBar)
+        
+        self._layout()
+        
+        self.search.SetFocus()
+        
+    def _layout(self):
+        """!Do dialog layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # body
+        dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
+        dataSizer.Add(item = self.tree, proportion =1,
+                      flag = wx.EXPAND)
+        
+        # buttons
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnRun, proportion = 0)
+        
+        sizer.Add(item = dataSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.ALL, border = 5)
+
+        sizer.Add(item = self.search, proportion=0,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+        
+        sizer.Add(item = btnSizer, proportion=0,
+                  flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
+        
+        sizer.Fit(self)
+        sizer.SetSizeHints(self)
+        
+        self.SetSizer(sizer)
+        
+        self.Fit()
+        self.SetAutoLayout(True)        
+        self.Layout()
+        
+    def OnCloseWindow(self, event):
+        """!Close window"""
+        self.Destroy()
+        
+    def OnRun(self, event):
+        """!Run selected command"""
+        if not self.tree.GetSelected():
+            return # should not happen
+        
+        data = self.tree.GetPyData(self.tree.GetSelected())
+        if not data:
+            return
+
+        handler = 'self.parent.' + data['handler'].lstrip('self.')
+        if data['handler'] == 'self.OnXTerm':
+            wx.MessageBox(parent = self,
+                          message = _('You must run this command from the menu or command line',
+                                      'This command require an XTerm'),
+                          caption = _('Message'), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+        elif data['command']:
+            eval(handler)(event = None, cmd = data['command'].split())
+        else:
+            eval(handler)(None)
+
+    def OnShowItem(self, event):
+        """!Show selected item"""
+        self.tree.OnShowItem(event)
+        if self.tree.GetSelected():
+            self.btnRun.Enable()
+        else:
+            self.btnRun.Enable(False)
+        
+    def OnItemActivated(self, event):
+        """!Item activated (double-click)"""
+        item = event.GetItem()
+        if not item or not item.IsOk():
+            return
+        
+        data = self.tree.GetPyData(item)
+        if not data or not data.has_key('command'):
+            return
+        
+        self.tree.itemSelected = item
+        
+        self.OnRun(None)
+        
+    def OnItemSelected(self, event):
+        """!Item selected"""
+        item = event.GetItem()
+        if not item or not item.IsOk():
+            return
+        
+        data = self.tree.GetPyData(item)
+        if not data or not data.has_key('command'):
+            return
+        
+        if data['command']:
+            label = data['command'] + ' -- ' + data['description']
+        else:
+            label = data['description']
+        
+        self.parent.SetStatusText(label, 0)
+        
+    def OnUpdateStatusBar(self, event):
+        """!Update statusbar text"""
+        element = self.search.GetSelection()
+        self.tree.SearchItems(element = element,
+                              value = event.GetString())
+        
+        nItems = len(self.tree.itemsMarked)
+        if event.GetString():
+            self.parent.SetStatusText(_("%d modules match") % nItems, 0)
+        else:
+            self.parent.SetStatusText("", 0)
+        
+        event.Skip()
+        
+class ItemTree(CT.CustomTreeCtrl):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
+                 CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
+        if globalvar.hasAgw:
+            super(ItemTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs)
+        else:
+            super(ItemTree, self).__init__(parent, id, style = ctstyle, **kwargs)
+        
+        self.root = self.AddRoot(_("Menu tree"))
+        self.itemsMarked = [] # list of marked items
+        self.itemSelected = None
+
+    def SearchItems(self, element, value):
+        """!Search item 
+
+        @param element element index (see self.searchBy)
+        @param value
+
+        @return list of found tree items
+        """
+        items = list()
+        if not value:
+            return items
+        
+        item = self.GetFirstChild(self.root)[0]
+        self._processItem(item, element, value, items)
+        
+        self.itemsMarked  = items
+        self.itemSelected = None
+        
+        return items
+    
+    def _processItem(self, item, element, value, listOfItems):
+        """!Search items (used by SearchItems)
+        
+        @param item reference item
+        @param listOfItems list of found items
+        """
+        while item and item.IsOk():
+            subItem = self.GetFirstChild(item)[0]
+            if subItem:
+                self._processItem(subItem, element, value, listOfItems)
+            data = self.GetPyData(item)
+            
+            if data and data.has_key(element) and \
+                    value.lower() in data[element].lower():
+                listOfItems.append(item)
+            
+            item = self.GetNextSibling(item)
+            
+    def GetSelected(self):
+        """!Get selected item"""
+        return self.itemSelected
+
+    def OnShowItem(self, event):
+        """!Highlight first found item in menu tree"""
+        if len(self.itemsMarked) > 0:
+            if self.GetSelected():
+                self.ToggleItemSelection(self.GetSelected())
+                idx = self.itemsMarked.index(self.GetSelected()) + 1
+            else:
+                idx = 0
+            try:
+                self.ToggleItemSelection(self.itemsMarked[idx])
+                self.itemSelected = self.itemsMarked[idx]
+                self.EnsureVisible(self.itemsMarked[idx])
+            except IndexError:
+                self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
+                self.EnsureVisible(self.itemsMarked[0])
+                self.itemSelected = self.itemsMarked[0]
+        else:
+            for item in self.root.GetChildren():
+                self.Collapse(item)
+            itemSelected = self.GetSelection()
+            if itemSelected:
+                self.ToggleItemSelection(itemSelected)
+            self.itemSelected = None
+    
+class MenuTree(ItemTree):
+    """!Menu tree class"""
+    def __init__(self, parent, data, **kwargs):
+        self.parent   = parent
+        self.menudata = data
+
+        super(MenuTree, self).__init__(parent, **kwargs)
+        
+    def Load(self, data = None):
+        """!Load menu data tree
+
+        @param data menu data (None to use self.menudata)
+        """
+        if not data:
+            data = self.menudata
+        
+        self.itemsMarked = [] # list of marked items
+        for eachMenuData in data.GetMenu():
+            for label, items in eachMenuData:
+                item = self.AppendItem(parentId = self.root,
+                                       text = label.replace('&', ''))
+                self.__AppendItems(item, items)
+        
+    def __AppendItems(self, item, data):
+        """!Append items into tree (used by Load()
+        
+        @param item tree item (parent)
+        @parent data menu data"""
+        for eachItem in data:
+            if len(eachItem) == 2:
+                if eachItem[0]:
+                    itemSub = self.AppendItem(parentId = item,
+                                    text = eachItem[0])
+                self.__AppendItems(itemSub, eachItem[1])
+            else:
+                if eachItem[0]:
+                    itemNew = self.AppendItem(parentId = item,
+                                              text = eachItem[0])
+                    
+                    data = { 'item'        : eachItem[0],
+                             'description' : eachItem[1],
+                             'handler'  : eachItem[2],
+                             'command'  : eachItem[3],
+                             'keywords' : eachItem[4] }
+                    
+                    self.SetPyData(itemNew, data)
+        
+class AboutWindow(wx.Frame):
+    def __init__(self, parent):
+        """!Create custom About Window
+
+        @todo improve styling
+        """
+        wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400), 
+                          title=_('About GRASS GIS'))
+        
+        panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        # icon
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+        # get version and web site
+        version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
+                                                                 flags = 'r',
+                                                                 read = True).splitlines()
+        
+        infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
+        infoSizer = wx.BoxSizer(wx.VERTICAL)
+        infoGridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+        infoGridSizer.AddGrowableCol(0)
+        infoGridSizer.AddGrowableCol(1)
+        logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
+        logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
+                                     bitmap = wx.Bitmap(name = logo,
+                                                        type = wx.BITMAP_TYPE_ICO))
+        infoSizer.Add(item = logoBitmap, proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
+        
+        info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                             label = version.replace('GRASS', 'GRASS GIS').strip() + '\n\n')
+        info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
+        infoSizer.Add(item = info, proportion = 0,
+                          flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
+
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('Official GRASS site:')),
+                          pos = (0, 0),
+                          flag = wx.ALIGN_RIGHT)
+
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = 'http://grass.osgeo.org'),
+                          pos = (0, 1),
+                          flag = wx.ALIGN_LEFT)
+
+        # infoGridSizer.Add(item = hl.HyperLinkCtrl(parent = self, id = wx.ID_ANY,
+        #                                           label = 'http://grass.osgeo.org',
+        #                                           URL = 'http://grass.osgeo.org'),
+        #                   pos = (0, 1),
+        #                   flag = wx.LEFT)
+        
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('GIS Library Revision:')),
+                          pos = (2, 0),
+                          flag = wx.ALIGN_RIGHT)
+        
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = svn_gis_h_rev.split(' ')[1] + ' (' +
+                                               svn_gis_h_date.split(' ')[1] + ')'),
+                          pos = (2, 1),
+                          flag = wx.ALIGN_LEFT)
+
+        infoSizer.Add(item = infoGridSizer,
+                      proportion = 1,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
+                      border = 25)
+        
+        #
+        # create pages
+        #
+        copyrightwin = self.PageCopyright()
+        licensewin   = self.PageLicense()
+        authorwin    = self.PageCredit()
+        contribwin   = self.PageContributors()
+        transwin     = self.PageTranslators()
+
+        # create a flat notebook for displaying information about GRASS
+        nbstyle = FN.FNB_VC8 | \
+                FN.FNB_BACKGROUND_GRADIENT | \
+                FN.FNB_TABS_BORDER_SIMPLE | \
+                FN.FNB_NO_X_BUTTON
+        
+        if globalvar.hasAgw:
+            aboutNotebook = FN.FlatNotebook(panel, id = wx.ID_ANY, agwStyle = nbstyle)
+        else:
+            aboutNotebook = FN.FlatNotebook(panel, id = wx.ID_ANY, style = nbstyle)
+        aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
+        
+        # make pages for About GRASS notebook
+        pg1 = aboutNotebook.AddPage(infoTxt,      text=_("Info"))
+        pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright"))
+        pg3 = aboutNotebook.AddPage(licensewin,   text=_("License"))
+        pg4 = aboutNotebook.AddPage(authorwin,    text=_("Authors"))
+        pg5 = aboutNotebook.AddPage(contribwin,   text=_("Contributors"))
+        pg5 = aboutNotebook.AddPage(transwin,     text=_("Translators"))
+        
+        wx.CallAfter(aboutNotebook.SetSelection, 0)
+        
+        # buttons
+        btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = btnClose, proportion = 0,
+                     flag = wx.ALL | wx.ALIGN_RIGHT,
+                     border = 5)
+        # bindings
+        # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
+        btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+
+        infoTxt.SetSizer(infoSizer)
+        infoSizer.Fit(infoTxt)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(item=aboutNotebook, proportion=1,
+                  flag=wx.EXPAND | wx.ALL, border=1)
+        sizer.Add(item=btnSizer, proportion=0,
+                  flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
+        panel.SetSizer(sizer)
+        self.Layout()
+    
+    def PageCopyright(self):
+        """Copyright information"""
+        copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
+        if os.path.exists(copyfile):
+            copyrightFile = open(copyfile, 'r')
+            copytext = copyrightFile.read()
+            copyrightFile.close()
+        else:
+            copytext = _('%s file missing') % 'COPYING'
+        
+        # put text into a scrolling panel
+        copyrightwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, 
+                                              size=wx.DefaultSize,
+                                              style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+        copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext)
+        copyrightwin.SetAutoLayout(True)
+        copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
+        copyrightwin.sizer.Add(item=copyrighttxt, proportion=1,
+                               flag=wx.EXPAND | wx.ALL, border=3)
+        copyrightwin.SetSizer(copyrightwin.sizer)
+        copyrightwin.Layout()
+        copyrightwin.SetupScrolling()
+        
+        return copyrightwin
+    
+    def PageLicense(self):
+        """Licence about"""
+        licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
+        if os.path.exists(licfile):
+            licenceFile = open(licfile, 'r')
+            license = ''.join(licenceFile.readlines())
+            licenceFile.close()
+        else:
+            license = _('%s file missing') % 'GPL.TXT'
+        # put text into a scrolling panel
+        licensewin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, 
+                                            style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+        licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license)
+        licensewin.SetAutoLayout(True)
+        licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
+        licensewin.sizer.Add(item=licensetxt, proportion=1,
+                flag=wx.EXPAND | wx.ALL, border=3)
+        licensewin.SetSizer(licensewin.sizer)
+        licensewin.Layout()
+        licensewin.SetupScrolling()
+        
+        return licensewin
+    
+    def PageCredit(self):
+        """Credit about"""
+                # credits
+        authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
+        if os.path.exists(authfile):
+            authorsFile = open(authfile, 'r')
+            authors = unicode(''.join(authorsFile.readlines()), "utf-8")
+            authorsFile.close()
+        else:
+            authors = _('%s file missing') % 'AUTHORS'
+        authorwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, 
+                                           style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
+        authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=authors)
+        authorwin.SetAutoLayout(1)
+        authorwin.SetupScrolling()
+        authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
+        authorwin.sizer.Add(item=authortxt, proportion=1,
+                flag=wx.EXPAND | wx.ALL, border=3)
+        authorwin.SetSizer(authorwin.sizer)
+        authorwin.Layout()      
+        
+        return authorwin
+
+    def PageContributors(self):
+        """Contributors info"""
+        contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
+        if os.path.exists(contribfile):
+            contribFile = open(contribfile, 'r')
+            contribs = list()
+            for line in contribFile.readlines():
+                cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
+                contribs.append((name, email, country, osgeo_id))
+            contribs[0] = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
+            contribFile.close()
+        else:
+            contribs = None
+        
+        contribwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, 
+                                           style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
+        contribwin.SetAutoLayout(1)
+        contribwin.SetupScrolling()
+        contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        if not contribs:
+            contribtxt = wx.StaticText(contribwin, id=wx.ID_ANY,
+                                       label=_('%s file missing') % 'contibutors.csv')
+            contribwin.sizer.Add(item=contribtxt, proportion=1,
+                                 flag=wx.EXPAND | wx.ALL, border=3)
+        else:
+            contribBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
+            for developer in contribs:
+                for item in developer:
+                    contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
+                                                        label = item))
+            contribwin.sizer.Add(item=contribBox, proportion=1,
+                                 flag=wx.EXPAND | wx.ALL, border=3)
+        
+        contribwin.SetSizer(contribwin.sizer)
+        contribwin.Layout()      
+        
+        return contribwin
+
+    def PageTranslators(self):
+        """Translators info"""
+        translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
+        if os.path.exists(translatorsfile):
+            translatorsFile = open(translatorsfile, 'r')
+            translators = dict()
+            for line in translatorsFile.readlines()[1:]:
+                name, email, languages = line.rstrip('\n').split(',')
+                for language in languages.split(' '):
+                    if not translators.has_key(language):
+                        translators[language] = list()
+                    translators[language].append((name, email))
+            translatorsFile.close()
+        else:
+            translators = None
+        
+        translatorswin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, 
+                                           style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
+        translatorswin.SetAutoLayout(1)
+        translatorswin.SetupScrolling()
+        translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        if not translators:
+            translatorstxt = wx.StaticText(translatorswin, id=wx.ID_ANY,
+                                           label=_('%s file missing') % 'translators.csv')
+            translatorswin.sizer.Add(item=translatorstxt, proportion=1,
+                                 flag=wx.EXPAND | wx.ALL, border=3)
+        else:
+            translatorsBox = wx.FlexGridSizer(cols=3, vgap=5, hgap=5)
+            languages = translators.keys()
+            languages.sort()
+            translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+                                                    label = _('Name')))
+            translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+                                                    label = _('E-mail')))
+            translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+                                                    label = _('Language')))
+            for lang in languages:
+                for translator in translators[lang]:
+                    name, email = translator
+                    translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+                                                            label =  unicode(name, "utf-8")))
+                    translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+                                                            label = email))
+                    translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
+                                                            label = lang))
+            
+            translatorswin.sizer.Add(item=translatorsBox, proportion=1,
+                                 flag=wx.EXPAND | wx.ALL, border=3)
+        
+        translatorswin.SetSizer(translatorswin.sizer)
+        translatorswin.Layout()      
+        
+        return translatorswin
+    
+    def OnCloseWindow(self, event):
+        """!Close window"""
+        self.Close()
+
+class InstallExtensionWindow(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("Fetch & install new extension from GRASS Addons"), **kwargs):
+        self.parent = parent
+        
+        wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label=" %s " % _("Repository"))
+        self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label=" %s " % _("List of extensions"))
+        
+        self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+                                value = 'https://svn.osgeo.org/grass/grass-addons')
+        self.fullDesc = wx.CheckBox(parent = self.panel, id=wx.ID_ANY,
+                                    label = _("Fetch full info including description and keywords (takes time)"))
+        self.fullDesc.SetValue(False)
+        
+        self.search = SearchModuleWindow(parent = self.panel)
+        self.search.SetSelection(2) 
+        
+        self.tree   = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
+        
+        self.statusbar = self.CreateStatusBar(0)
+        
+        self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                  label = _("&Fetch"))
+        self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
+        self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+        self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("&Install"))
+        self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
+        self.btnInstall.Enable(False)
+        
+        self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+        self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
+        self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
+        self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
+        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED,    self.OnItemSelected)
+        self.search.Bind(wx.EVT_TEXT_ENTER,        self.OnShowItem)
+        self.search.Bind(wx.EVT_TEXT,              self.OnUpdateStatusBar)
+
+        self._layout()
+
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
+        repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
+        repo1Sizer.Add(item = self.repo, proportion = 1,
+                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
+        repo1Sizer.Add(item = self.btnFetch, proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
+        repoSizer.Add(item = repo1Sizer,
+                      flag = wx.EXPAND)
+        repoSizer.Add(item = self.fullDesc)
+        
+        findSizer = wx.BoxSizer(wx.HORIZONTAL)
+        findSizer.Add(item = self.search, proportion = 1)
+        
+        treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
+        treeSizer.Add(item = self.tree, proportion = 1,
+                      flag = wx.ALL | wx.EXPAND, border = 1)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnClose, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.Add(item = self.btnInstall, proportion = 0)
+        
+        sizer.Add(item = repoSizer, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        sizer.Add(item = findSizer, proportion = 0,
+                  flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        sizer.Add(item = treeSizer, proportion = 1,
+                  flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        sizer.Add(item = btnSizer, proportion=0,
+                  flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def _install(self, name):
+        if not name:
+            return
+        log = self.parent.GetLogWindow()
+        log.RunCmd(['g.extension', 'extension=' + name,
+                    'svnurl=' + self.repo.GetValue().strip()])
+        self.OnCloseWindow(None)
+        
+    def OnUpdateStatusBar(self, event):
+        """!Update statusbar text"""
+        element = self.search.GetSelection()
+        if not self.tree.IsLoaded():
+            self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
+            return
+        
+        self.tree.SearchItems(element = element,
+                              value = event.GetString())
+        
+        nItems = len(self.tree.itemsMarked)
+        if event.GetString():
+            self.SetStatusText(_("%d items match") % nItems, 0)
+        else:
+            self.SetStatusText("", 0)
+        
+        event.Skip()
+    
+    def OnCloseWindow(self, event):
+        """!Close window"""
+        self.Destroy()
+
+    def OnFetch(self, event):
+        """!Fetch list of available extensions"""
+        self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
+        self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
+        self.SetStatusText("", 0)
+
+    def OnItemActivated(self, event):
+        item = event.GetItem()
+        data = self.tree.GetPyData(item)
+        if data and data.has_key('command'):
+            self._install(data['command'])
+            
+    def OnInstall(self, event):
+        """!Install selected extension"""
+        item = self.tree.GetSelected()
+        if not item.IsOk():
+            return
+        self._install(self.tree.GetItemText(item))
+        
+    def OnItemSelected(self, event):
+        """!Item selected"""
+        item = event.GetItem()
+        self.tree.itemSelected = item
+        data = self.tree.GetPyData(item)
+        if not data:
+            self.SetStatusText('', 0)
+            self.btnInstall.Enable(False)
+        else:
+            self.SetStatusText(data.get('description', ''), 0)
+            self.btnInstall.Enable(True)
+
+    def OnShowItem(self, event):
+        """!Show selected item"""
+        self.tree.OnShowItem(event)
+        if self.tree.GetSelected():
+            self.btnInstall.Enable()
+        else:
+            self.btnInstall.Enable(False)
+        
+class ExtensionTree(ItemTree):
+    """!List of available extensions"""
+    def __init__(self, parent, log, id = wx.ID_ANY,
+                 ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
+                 CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
+                 **kwargs):
+        self.parent = parent # GMFrame
+        self.log    = log
+        
+        super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
+        
+        self._initTree()
+        
+    def _initTree(self):
+        for prefix in ('display', 'database',
+                       'general', 'imagery',
+                       'misc', 'postscript', 'paint',
+                       'raster', 'raster3D', 'sites', 'vector'):
+            self.AppendItem(parentId = self.root,
+                            text = prefix)
+        self._loaded = False
+        
+    def _expandPrefix(self, c):
+        name = { 'd'  : 'display',
+                 'db' : 'database',
+                 'g'  : 'general',
+                 'i'  : 'imagery',
+                 'm'  : 'misc',
+                 'ps' : 'postscript',
+                 'p'  : 'paint',
+                 'r'  : 'raster',
+                 'r3' : 'raster3D',
+                 's'  : 'sites',
+                 'v'  : 'vector' }
+        
+        if name.has_key(c):
+            return name[c]
+        
+        return c
+    
+    def _findItem(self, text):
+        """!Find item"""
+        item = self.GetFirstChild(self.root)[0]
+        while item and item.IsOk():
+            if text == self.GetItemText(item):
+                return item
+            
+            item = self.GetNextSibling(item)
+        
+        return None
+    
+    def Load(self, url, full = False):
+        """!Load list of extensions"""
+        self.DeleteAllItems()
+        self.root = self.AddRoot(_("Menu tree"))
+        self._initTree()
+        
+        if full:
+            flags = 'g'
+        else:
+            flags = 'l'
+        ret = gcmd.RunCommand('g.extension', read = True,
+                              svnurl = url,
+                              flags = flags, quiet = True)
+        if not ret:
+            return
+        
+        mdict = dict()
+        for line in ret.splitlines():
+            if full:
+                key, value = line.split('=', 1)
+                if key == 'name':
+                    prefix, name = value.split('.', 1)
+                    if not mdict.has_key(prefix):
+                        mdict[prefix] = dict()
+                    mdict[prefix][name] = dict()
+                else:
+                    mdict[prefix][name][key] = value
+            else:
+                prefix, name = line.strip().split('.', 1)
+                if not mdict.has_key(prefix):
+                    mdict[prefix] = dict()
+                
+                mdict[prefix][name] = { 'command' : prefix + '.' + name }
+        
+        for prefix in mdict.keys():
+            prefixName = self._expandPrefix(prefix)
+            item = self._findItem(prefixName)
+            names = mdict[prefix].keys()
+            names.sort()
+            for name in names:
+                new = self.AppendItem(parentId = item,
+                                      text = prefix + '.' + name)
+                data = dict()
+                for key in mdict[prefix][name].keys():
+                    data[key] = mdict[prefix][name][key]
+                
+                self.SetPyData(new, data)
+        
+        self._loaded = True
+
+    def IsLoaded(self):
+        """Check if items are loaded"""
+        return self._loaded
+
+class HelpWindow(wx.html.HtmlWindow):
+    """!This panel holds the text from GRASS docs.
+    
+    GISBASE must be set in the environment to find the html docs dir.
+    The SYNOPSIS section is skipped, since this Panel is supposed to
+    be integrated into the cmdPanel and options are obvious there.
+    """
+    def __init__(self, parent, grass_command, text, skip_description,
+                 **kwargs):
+        """!If grass_command is given, the corresponding HTML help
+        file will be presented, with all links pointing to absolute
+        paths of local files.
+
+        If 'skip_description' is True, the HTML corresponding to
+        SYNOPSIS will be skipped, thus only presenting the help file
+        from the DESCRIPTION section onwards.
+
+        If 'text' is given, it must be the HTML text to be presented
+        in the Panel.
+        """
+        self.parent = parent
+        wx.InitAllImageHandlers()
+        wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs)
+        
+        gisbase = os.getenv("GISBASE")
+        self.loaded = False
+        self.history = list()
+        self.historyIdx = 0
+        self.fspath = os.path.join(gisbase, "docs", "html")
+        
+        self.SetStandardFonts (size = 10)
+        self.SetBorders(10)
+        
+        if text is None:
+            if skip_description:
+                url = os.path.join(self.fspath, grass_command + ".html")
+                self.fillContentsFromFile(url,
+                                          skip_description = skip_description)
+                self.history.append(url)
+                self.loaded = True
+            else:
+                ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
+                # self.LoadPage(self.fspath + grass_command + ".html")
+                self.loaded = False
+        else:
+            self.SetPage(text)
+            self.loaded = True
+        
+    def OnLinkClicked(self, linkinfo):
+        url = linkinfo.GetHref()
+        if url[:4] != 'http':
+            url = os.path.join(self.fspath, url)
+        self.history.append(url)
+        self.historyIdx += 1
+        self.parent.OnHistory()
+        
+        super(HelpWindow, self).OnLinkClicked(linkinfo)
+        
+    def fillContentsFromFile(self, htmlFile, skip_description=True):
+        """!Load content from file"""
+        aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
+        imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
+        try:
+            contents = []
+            skip = False
+            for l in file(htmlFile, "rb").readlines():
+                if "DESCRIPTION" in l:
+                    skip = False
+                if not skip:
+                    # do skip the options description if requested
+                    if "SYNOPSIS" in l:
+                        skip = skip_description
+                    else:
+                        # FIXME: find only first item
+                        findALink = aLink.search(l)
+                        if findALink is not None: 
+                            contents.append(aLink.sub(findALink.group(1)+
+                                                      self.fspath+findALink.group(2),l))
+                        findImgLink = imgLink.search(l)
+                        if findImgLink is not None: 
+                            contents.append(imgLink.sub(findImgLink.group(1)+
+                                                        self.fspath+findImgLink.group(2),l))
+                        
+                        if findALink is None and findImgLink is None:
+                            contents.append(l)
+            self.SetPage("".join(contents))
+            self.loaded = True
+        except: # The Manual file was not found
+            self.loaded = False
+        
+class HelpPanel(wx.Panel):
+    def __init__(self, parent, grass_command = "index", text = None,
+                 skip_description = False, **kwargs):
+        self.grass_command = grass_command
+        wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+        
+        self.content = HelpWindow(self, grass_command, text,
+                                  skip_description)
+        
+        self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
+                                 label = _("&Next"))
+        self.btnNext.Enable(False)
+        self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
+                                 label = _("&Previous"))
+        self.btnPrev.Enable(False)
+        
+        self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
+        self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
+        
+        self._layout()
+
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        btnSizer.Add(item = self.btnPrev, proportion = 0,
+                     flag = wx.ALL, border = 5)
+        btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
+        btnSizer.Add(item = self.btnNext, proportion = 0,
+                     flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        sizer.Add(item = self.content, proportion = 1,
+                  flag = wx.EXPAND)
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.EXPAND)
+        
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def LoadPage(self, path = None):
+        """!Load page"""
+        if not path:
+            path = os.path.join(self.content.fspath, self.grass_command + ".html")
+        self.content.history.append(path)
+        self.content.LoadPage(path)
+        
+    def IsFile(self):
+        """!Check if file exists"""
+        return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html"))
+
+    def IsLoaded(self):
+        return self.content.loaded
+
+    def OnHistory(self):
+        """!Update buttons"""
+        nH = len(self.content.history)
+        iH = self.content.historyIdx
+        if iH == nH - 1:
+            self.btnNext.Enable(False)
+        elif iH > -1:
+            self.btnNext.Enable(True)
+        if iH < 1:
+            self.btnPrev.Enable(False)
+        else:
+            self.btnPrev.Enable(True)
+
+    def OnNext(self, event):
+        """Load next page"""
+        self.content.historyIdx += 1
+        idx = self.content.historyIdx
+        path = self.content.history[idx]
+        self.content.LoadPage(path)
+        self.OnHistory()
+        
+        event.Skip()
+        
+    def OnPrev(self, event):
+        """Load previous page"""
+        self.content.historyIdx -= 1
+        idx = self.content.historyIdx
+        path = self.content.history[idx]
+        self.content.LoadPage(path)
+        self.OnHistory()
+        
+        event.Skip()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ghelp.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/globalvar.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/globalvar.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/globalvar.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -8,9 +8,8 @@
 
 (C) 2007-2010 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.
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
 
 @author Martin Landa <landa.martin gmail.com>
 """
@@ -19,10 +18,20 @@
 import sys
 import locale
 
+if not os.getenv("GISBASE"):
+    sys.exit("GRASS is not running. Exiting...")
 ### i18N
 import gettext
 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
 
+# path to python scripts
+ETCDIR = os.path.join(os.getenv("GISBASE"), "etc")
+ETCICONDIR = os.path.join(os.getenv("GISBASE"), "etc", "gui", "icons")
+ETCWXDIR = os.path.join(ETCDIR, "wxpython")
+
+sys.path.append(os.path.join(ETCDIR, "python"))
+import grass.script as grass
+
 # wxversion.select() called once at the beginning
 check = True
 
@@ -35,7 +44,7 @@
     return True
 
 def CheckForWx():
-    """Try to import wx module and check its version"""
+    """!Try to import wx module and check its version"""
     global check
     if not check:
         return
@@ -84,24 +93,26 @@
 # temporal query layer (removed on re-render action)
 QUERYLAYER = 'qlayer'
 
-# path to python scripts
-ETCDIR = os.path.join(os.getenv("GISBASE"), "etc")
-ETCICONDIR = os.path.join(os.getenv("GISBASE"), "etc", "gui", "icons")
-ETCWXDIR = os.path.join(ETCDIR, "wxpython")
-
-"""Style definition for FlatNotebook pages"""
+"""!Style definition for FlatNotebook pages"""
 FNPageStyle = FN.FNB_VC8 | \
     FN.FNB_BACKGROUND_GRADIENT | \
     FN.FNB_NODRAG | \
     FN.FNB_TABS_BORDER_SIMPLE 
+
+FNPageDStyle = FN.FNB_FANCY_TABS | \
+    FN.FNB_BOTTOM | \
+    FN.FNB_NO_NAV_BUTTONS | \
+    FN.FNB_NO_X_BUTTON
+
 FNPageColor = wx.Colour(125,200,175)
 
-"""Dialog widget dimension"""
+"""!Dialog widget dimension"""
 DIALOG_SPIN_SIZE = (150, -1)
 DIALOG_COMBOBOX_SIZE = (300, -1)
 DIALOG_GSELECT_SIZE = (400, -1)
 DIALOG_TEXTCTRL_SIZE = (400, -1)
 DIALOG_LAYER_SIZE = (100, -1)
+DIALOG_COLOR_SIZE = (30, 30)
 
 MAP_WINDOW_SIZE = (770, 570)
 HIST_WINDOW_SIZE = (500, 350)
@@ -112,9 +123,11 @@
                               _("Show comp. extent"),
                               _("Display mode"),
                               _("Display geometry"),
-                              _("Map scale")]
+                              _("Map scale"),
+                              _("Go to"),
+                              _("Projection"),]
 
-"""File name extension binaries/scripts"""
+"""!File name extension binaries/scripts"""
 if subprocess.mswindows:
     EXT_BIN = '.exe'
     EXT_SCT = '.bat'
@@ -122,7 +135,7 @@
     EXT_BIN = ''
     EXT_SCT = ''
 
-def GetGRASSCmds(bin=True, scripts=True, gui_scripts=True):
+def GetGRASSCmds(bin = True, scripts = True, gui_scripts = True):
     """!Create list of all available GRASS commands to use when
     parsing string from the command line
     """
@@ -137,10 +150,11 @@
         
         # add special call for setting vector colors
         cmd.append('vcolors')
-    if scripts is True:
+    if scripts:
         cmd = cmd + os.listdir(os.path.join(gisbase, 'scripts')) 
-    if gui_scripts is True:
+    if gui_scripts:
         os.environ["PATH"] = os.getenv("PATH") + os.pathsep + os.path.join(gisbase, 'etc', 'gui', 'scripts')
+        os.environ["PATH"] = os.getenv("PATH") + os.pathsep + os.path.join(gisbase, 'etc', 'wxpython', 'scripts')
         cmd = cmd + os.listdir(os.path.join(gisbase, 'etc', 'gui', 'scripts'))
 
     if subprocess.mswindows:
@@ -153,7 +167,7 @@
 """@brief Collected GRASS-relared binaries/scripts"""
 grassCmd = {}
 grassCmd['all'] = GetGRASSCmds()
-grassCmd['script'] = GetGRASSCmds(bin=False)
+grassCmd['script'] = GetGRASSCmds(bin = False)
 
 """@Toolbar icon size"""
 toolbarSize = (24, 24)
@@ -166,3 +180,6 @@
 
 """@Check version of wxPython, use agwStyle for 2.8.11+"""
 hasAgw = CheckWxVersion()
+
+"""@List of commands for auto-rendering"""
+cmdAutoRender = [ 'r.colors', 'i.landsat.rgb' ]

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gmodeler.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gmodeler.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gmodeler.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,4534 @@
+"""!
+ at package gmodeler.py
+
+ at brief wxGUI Graphical Modeler for creating, editing, and managing models
+
+Classes:
+ - Model
+ - ModelFrame
+ - ModelCanvas
+ - ModelObject
+ - ModelAction
+ - ModelSearchDialog
+ - ModelData
+ - ModelDataDialog
+ - ModelRelation
+ - ProcessModelFile
+ - WriteModelFile
+ - PreferencesDialog
+ - PropertiesDialog
+ - ModelParamDialog
+ - ModelListCtrl
+ - VariablePanel
+ - ValiableListCtrl
+ - ModelItem
+ - ModelItemDialog
+ - ModelLoop
+ - ModelLoopDialog
+ - ItemPanel
+ - ItemListCtrl
+ - ItemCheckListCtrl
+ - ModelCondition
+ - ModelConditionDialog
+ - WritePythonFile
+
+(C) 2010 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 os
+import sys
+import shlex
+import time
+import traceback
+import getpass
+import stat
+import textwrap
+import tempfile
+import copy
+import re
+
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+import globalvar
+if not os.getenv("GRASS_WXBUNDLED"):
+    globalvar.CheckForWx()
+import wx
+import wx.lib.ogl             as ogl
+import wx.lib.flatnotebook    as FN
+import wx.lib.colourselect    as csel
+import wx.lib.mixins.listctrl as listmix
+
+import menu
+import menudata
+import toolbars
+import menuform
+import prompt
+import utils
+import goutput
+import gselect
+from debug        import Debug
+from gcmd         import GMessage, GException, GWarning, GError
+from gdialogs     import ElementDialog, GetImageHandlers
+from preferences  import PreferencesBaseDialog, globalSettings as UserSettings
+from ghelp        import SearchModuleWindow
+
+from grass.script import core as grass
+
+class Model(object):
+    """!Class representing the model"""
+    def __init__(self, canvas = None):
+        self.items      = list() # list of actions/loops/...
+        
+        # model properties
+        self.properties = { 'name'        : _("model"),
+                            'description' : _("Script generated by wxGUI Graphical Modeler."),
+                            'author'      : getpass.getuser() }
+        # model variables
+        self.variables = dict()
+        self.variablesParams = dict()
+        
+        self.canvas  = canvas
+        
+    def GetCanvas(self):
+        """!Get canvas or None"""
+        return self.canvas
+    
+    def GetItems(self, objType = None):
+        """!Get list of model items
+
+        @param objType Object type to filter model objects
+        """
+        if not objType:
+            return self.items
+        
+        result = list()
+        for item in self.items:
+            if isinstance(item, objType):
+                result.append(item)
+        
+        return result
+
+    def GetItem(self, aId):
+        """!Get item of given id
+
+        @param aId item id
+        
+        @return Model* instance
+        @return None if no item found
+        """
+        ilist = self.GetItems()
+        for item in ilist:
+            if item.GetId() == aId:
+                return item
+        
+        return None
+
+    def GetNumItems(self, actionOnly = False):
+        """!Get number of items"""
+        if actionOnly:
+            return len(self.GetItems(objType = ModelAction))
+        
+        return len(self.GetItems())
+    
+    def GetProperties(self):
+        """!Get model properties"""
+        return self.properties
+
+    def GetVariables(self, params = False):
+        """!Get model variables"""
+        if params:
+            return self.variablesParams
+        
+        return self.variables
+    
+    def SetVariables(self, data):
+        """!Set model variables"""
+        self.variables = data
+    
+    def Reset(self):
+        """!Reset model"""
+        self.items = list()
+        
+    def RemoveItem(self, item):
+        """!Remove item from model
+        
+        @return list of related items to remove/update
+        """
+        relList = list()
+        upList = list()
+        
+        if not isinstance(item, ModelData):
+            self.items.remove(item)
+        
+        if isinstance(item, ModelAction):
+            for rel in item.GetRelations():
+                relList.append(rel)
+                data = rel.GetData()
+                if len(data.GetRelations()) < 2:
+                    relList.append(data)
+                else:
+                    upList.append(data)
+            
+        elif isinstance(item, ModelData):
+            for rel in item.GetRelations():
+                relList.append(rel)
+                if rel.GetFrom() == self:
+                    relList.append(rel.GetTo())
+                else:
+                    relList.append(rel.GetFrom())
+        
+        return relList, upList
+    
+    def FindAction(self, aId):
+        """!Find action by id"""
+        alist = self.GetItems(objType = ModelAction)
+        for action in alist:
+            if action.GetId() == aId:
+                return action
+        
+        return None
+
+    def GetData(self):
+        """!Get list of data items"""
+        result = list()
+        for action in self.GetItems(objType = ModelAction):
+            for rel in action.GetRelations():
+                dataItem = rel.GetData()
+                if dataItem not in result:
+                    result.append(dataItem)
+        
+        return result
+
+    def FindData(self, value, prompt):
+        """!Find data item in the model
+
+        @param value value
+        @param prompt prompt
+
+        @return ModelData instance
+        @return None if not found
+        """
+        for data in self.GetData():
+            if data.GetValue() == value and \
+                    data.GetPrompt() == prompt:
+                return data
+        
+        return None
+                
+    def LoadModel(self, filename):
+        """!Load model definition stored in GRASS Model XML file (gxm)
+        
+        @todo Validate against DTD
+        
+        Raise exception on error.
+        """
+        dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxm.dtd")
+        
+        # parse workspace file
+        try:
+            gxmXml = ProcessModelFile(etree.parse(filename))
+        except StandardError, e:
+            raise GException(e)
+        
+        if self.canvas:
+            win = self.canvas.parent
+            if gxmXml.pos:
+                win.SetPosition(gxmXml.pos)
+            if gxmXml.size:
+                win.SetSize(gxmXml.size)
+        
+        # load properties
+        self.properties = gxmXml.properties
+        self.variables  = gxmXml.variables
+        
+        # load model.GetActions()
+        for action in gxmXml.actions:
+            actionItem = ModelAction(parent = self, 
+                                     x = action['pos'][0],
+                                     y = action['pos'][1],
+                                     width = action['size'][0],
+                                     height = action['size'][1],
+                                     task = action['task'],
+                                     id = action['id'])
+            
+            if action['disabled']:
+                actionItem.Enable(False)
+            
+            self.AddItem(actionItem)
+            
+            task = actionItem.GetTask()
+            parameterized = False
+            valid = True
+            for f in task.get_options()['flags']:
+                if f.get('parameterized', False):
+                    parameterized = True
+                    break
+            for p in task.get_options()['params']:
+                if p.get('required', 'no') != 'no' and \
+                        p.get('value', '') == '' and \
+                        p.get('default', '') == '':
+                    valid = False
+                if p.get('parameterized', False):
+                    parameterized = True
+            
+            actionItem.SetValid(valid)
+            actionItem.SetParameterized(parameterized)
+            actionItem.GetLog() # substitute variables (-> valid/invalid)
+        
+        # load data & relations
+        for data in gxmXml.data:
+            dataItem = ModelData(parent = self, 
+                                 x = data['pos'][0],
+                                 y = data['pos'][1],
+                                 width = data['size'][0],
+                                 height = data['size'][1],
+                                 prompt = data['prompt'],
+                                 value = data['value'])
+            dataItem.SetIntermediate(data['intermediate'])
+            
+            for rel in data['rels']:
+                actionItem = self.FindAction(rel['id'])
+                if rel['dir'] == 'from':
+                    relation = ModelRelation(dataItem, actionItem, rel['name'])
+                else:
+                    relation = ModelRelation(actionItem, dataItem, rel['name'])
+                relation.SetControlPoints(rel['points'])
+                actionItem.AddRelation(relation)
+                dataItem.AddRelation(relation)
+            
+            dataItem.Update()
+                   
+        # load loops
+        for loop in gxmXml.loops:
+            loopItem = ModelLoop(parent = self, 
+                                 x = loop['pos'][0],
+                                 y = loop['pos'][1],
+                                 width = loop['size'][0],
+                                 height = loop['size'][1],
+                                 text = loop['text'],
+                                 id = loop['id'])
+            self.AddItem(loopItem)
+
+        # load conditions
+        for condition in gxmXml.conditions:
+            conditionItem = ModelCondition(parent = self, 
+                                           x = condition['pos'][0],
+                                           y = condition['pos'][1],
+                                           width = condition['size'][0],
+                                           height = condition['size'][1],
+                                           text = condition['text'],
+                                           id = condition['id'])
+            self.AddItem(conditionItem)
+
+        # define loops & if/else items
+        for loop in gxmXml.loops:
+            alist = list()
+            for aId in loop['items']:
+                action = self.GetItem(aId)
+                alist.append(action)
+            
+            loopItem = self.GetItem(loop['id'])
+            loopItem.SetItems(alist)
+            
+            for action in loopItem.GetItems():
+                action.SetBlock(loopItem)
+        
+        for condition in gxmXml.conditions:
+            conditionItem = self.GetItem(condition['id'])
+            for b in condition['items'].keys():
+                alist = list()
+                for aId in condition['items'][b]:
+                    action = self.GetItem(aId)
+                    alist.append(action)
+                conditionItem.SetItems(alist, branch = b)
+            
+            items = conditionItem.GetItems()
+            for b in items.keys():
+                for action in items[b]:
+                    action.SetBlock(conditionItem)
+        
+    def AddItem(self, newItem):
+        """!Add item to the list"""
+        iId = newItem.GetId()
+        
+        i   = 0
+        for item in self.items:
+            if item.GetId() > iId:
+                self.items.insert(i, newItem)
+                return
+            i += 1
+        
+        self.items.append(newItem)
+        
+    def IsValid(self):
+        """Return True if model is valid"""
+        if self.Validate():
+            return False
+        
+        return True
+    
+    def Validate(self):
+        """!Validate model, return None if model is valid otherwise
+        error string"""
+        errList = list()
+        for action in self.GetItems(objType = ModelAction):
+            task = menuform.GUI().ParseCommand(cmd = action.GetLog(string = False),
+                                               show = None)
+            errList += task.getCmdError()
+
+        return errList
+
+    def Run(self, log, onDone):
+        """!Run model"""
+        for action in self.actions:
+            if not action.IsEnabled():
+                continue
+            log.RunCmd(command = action.GetLog(string = False),
+                       onDone = onDone)
+        
+    def DeleteIntermediateData(self, log):
+        """!Detele intermediate data"""
+        rast, vect, rast3d, msg = self.GetIntermediateData()
+        
+        if rast:
+            log.RunCmd(['g.remove', 'rast=%s' %','.join(rast)])
+        if rast3d:
+            log.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)])
+        if vect:
+            log.RunCmd(['g.remove', 'vect=%s' %','.join(vect)])
+        
+    def GetIntermediateData(self):
+        """!Get info about intermediate data"""
+        rast = list()
+        rast3d = list()
+        vect = list()
+        for data in self.GetData():
+            if not data.IsIntermediate():
+                continue
+            name = data.GetValue()
+            prompt = data.GetPrompt()
+            if prompt == 'raster':
+                rast.append(name)
+            elif prompt == 'vector':
+                vect.append(name)
+            elif prompt == 'rast3d':
+                rast3d.append(name)
+        
+        msg = ''
+        if rast:
+            msg += '\n\n%s: ' % _('Raster maps')
+            msg += ', '.join(rast)
+        if rast3d:
+            msg += '\n\n%s: ' % _('3D raster maps')
+            msg += ', '.join(rast3d)
+        if vect:
+            msg += '\n\n%s: ' % _('Vector maps')
+            msg += ', '.join(vect)
+        
+        return rast, vect, rast3d, msg
+
+    def Update(self):
+        """!Update model"""
+        for item in self.items:
+            item.Update()
+        
+    def IsParameterized(self):
+        """!Return True if model is parameterized"""
+        if self.Parameterize():
+            return True
+        
+        return False
+    
+    def Parameterize(self):
+        """!Return parameterized options"""
+        result = dict()
+        idx = 0
+        if self.variables:
+            params = list()
+            result[_("Variables")] = { 'flags'  : list(),
+                                       'params' : params,
+                                       'idx' : idx }
+            for name, values in self.variables.iteritems():
+                gtype = values.get('type', 'string')
+                if gtype in ('raster', 'vector'):
+                    gisprompt = True
+                    prompt = gtype
+                    if gtype == 'raster':
+                        element = 'cell'
+                    else:
+                        element = 'vector'
+                    ptype = 'string'
+                else:
+                    gisprompt = False
+                    prompt = None
+                    element = None
+                    ptype = gtype
+                params.append({ 'gisprompt' : gisprompt,
+                                'multiple'  : 'no',
+                                'description' : values.get('description', ''),
+                                'guidependency' : '',
+                                'default' : '',
+                                'age' : None,
+                                'required' : 'yes',
+                                'value' : values.get('value', ''),
+                                'label' : '',
+                                'guisection' : '',
+                                'key_desc' : '',
+                                'values' : list(),
+                                'parameterized' : False,
+                                'values_desc' : list(),
+                                'prompt' : prompt,
+                                'element' : element,
+                                'type' : ptype,
+                                'name' : name })
+            
+            idx += 1
+        
+        for action in self.GetItems(objType = ModelAction):
+            if not action.IsEnabled():
+                continue
+            name   = action.GetName()
+            params = action.GetParams()
+            for f in params['flags']:
+                if f.get('parameterized', False):
+                    if not result.has_key(name):
+                        result[name] = { 'flags' : list(),
+                                         'params': list(),
+                                         'idx'   : idx }
+                    result[name]['flags'].append(f)
+            for p in params['params']:
+                if p.get('parameterized', False):
+                    if not result.has_key(name):
+                        result[name] = { 'flags' : list(),
+                                         'params': list(),
+                                         'idx'   : idx }
+                    result[name]['params'].append(p)
+            idx += 1
+
+        self.variablesParams = result # record parameters
+        
+        return result
+    
+class ModelFrame(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("GRASS GIS Graphical Modeler"), **kwargs):
+        """!Graphical modeler main window
+        
+        @param parent parent window
+        @param id window id
+        @param title window title
+
+        @param kwargs wx.Frames' arguments
+        """
+        self.parent = parent
+        self.searchDialog = None # module search dialog
+        self.baseTitle = title
+        self.modelFile = None    # loaded model
+        self.modelChanged = False
+        
+        self.cursors = {
+            "default" : wx.StockCursor(wx.CURSOR_ARROW),
+            "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
+            }
+        
+        wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        self.SetName("Modeler")
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.menubar = menu.Menu(parent = self, data = menudata.ModelerData())
+        
+        self.SetMenuBar(self.menubar)
+        
+        self.toolbar = toolbars.ModelToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        
+        self.statusbar = self.CreateStatusBar(number = 1)
+        
+        if globalvar.hasAgw:
+            self.notebook = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                            agwStyle = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
+                                            FN.FNB_NO_NAV_BUTTONS | FN.FNB_NO_X_BUTTON)
+        else:
+            self.notebook = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                            style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
+                                            FN.FNB_NO_NAV_BUTTONS | FN.FNB_NO_X_BUTTON)
+        
+        self.canvas = ModelCanvas(self)
+        self.canvas.SetBackgroundColour(wx.WHITE)
+        self.canvas.SetCursor(self.cursors["default"])
+        
+        self.model = Model(self.canvas)
+        
+        self.variablePanel = VariablePanel(parent = self)
+        
+        self.itemPanel = ItemPanel(parent = self)
+        
+        self.goutput = goutput.GMConsole(parent = self, pageid = 3,
+                                         notebook = self.notebook)
+        
+        self.notebook.AddPage(self.canvas, text=_('Model'))
+        self.notebook.AddPage(self.itemPanel, text=_('Items'))
+        self.notebook.AddPage(self.variablePanel, text=_('Variables'))
+        self.notebook.AddPage(self.goutput, text=_('Command output'))
+        wx.CallAfter(self.notebook.SetSelection, 0)
+        wx.CallAfter(self.ModelChanged, False)
+
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        
+        self._layout()
+        self.SetMinSize((475, 300))
+        self.SetSize((640, 480))
+        
+        # fix goutput's pane size
+        if self.goutput:
+            self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        sizer.Add(item = self.notebook, proportion = 1,
+                  flag = wx.EXPAND)
+        
+        self.SetAutoLayout(True)
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+        
+        self.Layout()
+
+    def _addEvent(self, item):
+        """!Add event to item"""
+        evthandler = ModelEvtHandler(self.statusbar,
+                                     self)
+        evthandler.SetShape(item)
+        evthandler.SetPreviousHandler(item.GetEventHandler())
+        item.SetEventHandler(evthandler)
+
+    def GetCanvas(self):
+        """!Get canvas"""
+        return self.canvas
+    
+    def GetModel(self):
+        """!Get model"""
+        return self.model
+    
+    def ModelChanged(self, changed = True):
+        """!Update window title"""
+        self.modelChanged = changed
+        
+        if self.modelFile:
+            if self.modelChanged:
+                self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile) + '*')
+            else:
+                self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile))
+        else:
+            self.SetTitle(self.baseTitle)
+        
+    def OnVariables(self, event):
+        """!Switch to variables page"""
+        self.notebook.SetSelection(2)
+        
+    def OnRemoveItem(self, event):
+        """!Remove shape
+        """
+        self.GetCanvas().RemoveSelected()
+        
+    def OnCanvasRefresh(self, event):
+        """!Refresh canvas"""
+        self.SetStatusText(_("Redrawing model..."), 0)
+        self.GetCanvas().Refresh()
+        self.SetStatusText("", 0)
+
+    def OnCmdRun(self, event):
+        """!Run command"""
+        try:
+            action = self.GetModel().GetItems()[event.pid]
+            if hasattr(action, "task"):
+                action.Update(running = True)
+        except IndexError:
+            pass
+        
+    def OnCmdDone(self, event):
+        """!Command done (or aborted)"""
+        try:
+            action = self.GetModel().GetItems()[event.pid]
+            if hasattr(action, "task"):
+                action.Update(running = True)
+        except IndexError:
+            pass
+        
+    def OnCloseWindow(self, event):
+        """!Close window"""
+        if self.modelChanged and \
+                UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
+            if self.modelFile:
+                message = _("Do you want to save changes in the model?")
+            else:
+                message = _("Do you want to store current model settings "
+                            "to model file?")
+            
+            # ask user to save current settings
+            dlg = wx.MessageDialog(self,
+                                   message = message,
+                                   caption=_("Quit Graphical Modeler"),
+                                   style = wx.YES_NO | wx.YES_DEFAULT |
+                                   wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
+            ret = dlg.ShowModal()
+            if ret == wx.ID_YES:
+                if not self.modelFile:
+                        self.OnWorkspaceSaveAs()
+                else:
+                    self.WriteModelFile(self.modelFile)
+            elif ret == wx.ID_CANCEL:
+                dlg.Destroy()
+                return
+            dlg.Destroy()
+        
+        self.Destroy()
+
+    def OnSize(self, event):
+        """Window resized, save to the model"""
+        self.ModelChanged()
+        event.Skip()
+        
+    def OnPreferences(self, event):
+        """!Open preferences dialog"""
+        dlg = PreferencesDialog(parent = self)
+        dlg.CenterOnParent()
+        
+        dlg.ShowModal()
+        self.canvas.Refresh()
+        
+    def OnHelp(self, event):
+        """!Show help"""
+        if self.parent and self.parent.GetName() == 'LayerManager':
+            log = self.parent.GetLogWindow()
+            log.RunCmd(['g.manual',
+                        'entry=wxGUI.Modeler'])
+        else:
+            gcmd.RunCommand('g.manual',
+                            quiet = True,
+                            entry = 'wxGUI.Modeler')
+        
+    def OnModelProperties(self, event):
+        """!Model properties dialog"""
+        dlg = PropertiesDialog(parent = self)
+        dlg.CentreOnParent()
+        properties = self.model.GetProperties()
+        dlg.Init(properties)
+        if dlg.ShowModal() == wx.ID_OK:
+            self.ModelChanged()
+            for key, value in dlg.GetValues().iteritems():
+                properties[key] = value
+            for action in self.model.GetItems(objType = ModelAction):
+                action.GetTask().set_flag('overwrite', properties['overwrite'])
+        
+        dlg.Destroy()
+        
+    def OnDeleteData(self, event):
+        """!Delete intermediate data"""
+        rast, vect, rast3d, msg = self.model.GetIntermediateData()
+        
+        if not rast and not vect and not rast3d:
+            GMessage(parent = self,
+                     message = _('Nothing to delete.'))
+            return
+        
+        dlg = wx.MessageDialog(parent = self,
+                               message= _("Do you want to permanently delete data?%s" % msg),
+                               caption=_("Delete intermediate data?"),
+                               style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+        
+        ret = dlg.ShowModal()
+        if ret == wx.ID_YES:
+            dlg.Destroy()
+            
+            if rast:
+                self.goutput.RunCmd(['g.remove', 'rast=%s' %','.join(rast)])
+            if rast3d:
+                self.goutput.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)])
+            if vect:
+                self.goutput.RunCmd(['g.remove', 'vect=%s' %','.join(vect)])
+            
+            self.SetStatusText(_("%d maps deleted from current mapset") % \
+                                 int(len(rast) + len(rast3d) + len(vect)))
+            return
+        
+        dlg.Destroy()
+                
+    def OnModelNew(self, event):
+        """!Create new model"""
+        Debug.msg(4, "ModelFrame.OnModelNew():")
+        
+        # ask user to save current model
+        if self.modelFile and self.modelChanged:
+            self.OnModelSave()
+        elif self.modelFile is None and \
+                (self.model.GetNumItems() > 0 or len(self.model.GetData()) > 0):
+            dlg = wx.MessageDialog(self, message=_("Current model is not empty. "
+                                                   "Do you want to store current settings "
+                                                   "to model file?"),
+                                   caption=_("Create new model?"),
+                                   style=wx.YES_NO | wx.YES_DEFAULT |
+                                   wx.CANCEL | wx.ICON_QUESTION)
+            ret = dlg.ShowModal()
+            if ret == wx.ID_YES:
+                self.OnModelSaveAs()
+            elif ret == wx.ID_CANCEL:
+                dlg.Destroy()
+                return
+            
+            dlg.Destroy()
+        
+        # delete all items
+        self.canvas.GetDiagram().DeleteAllShapes()
+        self.model.Reset()
+        self.canvas.Refresh()
+        
+        # no model file loaded
+        self.modelFile = None
+        self.modelChanged = False
+        self.SetTitle(self.baseTitle)
+        
+    def OnModelOpen(self, event):
+        """!Load model from file"""
+        filename = ''
+        dlg = wx.FileDialog(parent = self, message=_("Choose model file"),
+                            defaultDir = os.getcwd(),
+                            wildcard=_("GRASS Model File (*.gxm)|*.gxm"))
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+                    
+        if not filename:
+            return
+        
+        Debug.msg(4, "ModelFrame.OnModelOpen(): filename=%s" % filename)
+        
+        # close current model
+        self.OnModelClose()
+        
+        self.LoadModelFile(filename)
+        
+        self.modelFile = filename
+        self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile))
+        self.SetStatusText(_('%(items)d items (%(actions)d actions) loaded into model') % \
+                               { 'items' : self.model.GetNumItems(),
+                                 'actions' : self.model.GetNumItems(actionOnly = True) }, 0)
+        
+    def OnModelSave(self, event = None):
+        """!Save model to file"""
+        if self.modelFile and self.modelChanged:
+            dlg = wx.MessageDialog(self, message=_("Model file <%s> already exists. "
+                                                   "Do you want to overwrite this file?") % \
+                                       self.modelFile,
+                                   caption=_("Save model"),
+                                   style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() == wx.ID_NO:
+                dlg.Destroy()
+            else:
+                Debug.msg(4, "ModelFrame.OnModelSave(): filename=%s" % self.modelFile)
+                self.WriteModelFile(self.modelFile)
+                self.SetStatusText(_('File <%s> saved') % self.modelFile, 0)
+                self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile))
+        elif not self.modelFile:
+            self.OnModelSaveAs(None)
+        
+    def OnModelSaveAs(self, event):
+        """!Create model to file as"""
+        filename = ''
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose file to save current model"),
+                            defaultDir = os.getcwd(),
+                            wildcard=_("GRASS Model File (*.gxm)|*.gxm"),
+                            style=wx.FD_SAVE)
+        
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            return
+        
+        # check for extension
+        if filename[-4:] != ".gxm":
+            filename += ".gxm"
+        
+        if os.path.exists(filename):
+            dlg = wx.MessageDialog(parent = self,
+                                   message=_("Model file <%s> already exists. "
+                                             "Do you want to overwrite this file?") % filename,
+                                   caption=_("File already exists"),
+                                   style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() != wx.ID_YES:
+                dlg.Destroy()
+                return
+        
+        Debug.msg(4, "GMFrame.OnModelSaveAs(): filename=%s" % filename)
+        
+        self.WriteModelFile(filename)
+        self.modelFile = filename
+        self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile))
+        self.SetStatusText(_('File <%s> saved') % self.modelFile, 0)
+
+    def OnModelClose(self, event = None):
+        """!Close model file"""
+        Debug.msg(4, "ModelFrame.OnModelClose(): file=%s" % self.modelFile)
+        # ask user to save current model
+        if self.modelFile and self.modelChanged:
+            self.OnModelSave()
+        elif self.modelFile is None and \
+                (self.model.GetNumItems() > 0 or len(self.model.GetData()) > 0):
+            dlg = wx.MessageDialog(self, message=_("Current model is not empty. "
+                                                   "Do you want to store current settings "
+                                                   "to model file?"),
+                                   caption=_("Create new model?"),
+                                   style=wx.YES_NO | wx.YES_DEFAULT |
+                                   wx.CANCEL | wx.ICON_QUESTION)
+            ret = dlg.ShowModal()
+            if ret == wx.ID_YES:
+                self.OnModelSaveAs()
+            elif ret == wx.ID_CANCEL:
+                dlg.Destroy()
+                return
+            
+            dlg.Destroy()
+        
+        self.modelFile = None
+        self.SetTitle(self.baseTitle)
+        
+        self.canvas.GetDiagram().DeleteAllShapes()
+        self.model.Reset()
+        
+        self.canvas.Refresh()
+        
+    def OnRunModel(self, event):
+        """!Run entire model"""
+        if self.model.GetNumItems() < 1:
+            GMessage(parent = self, 
+                     message = _('Model is empty. Nothing to run.'))
+            return
+        
+        # validation
+        errList = self._validateModel()
+        if errList:
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _('Model is not valid. Do you want to '
+                                               'run the model anyway?\n\n%s') % '\n'.join(errList),
+                                   caption=_("Run model?"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT |
+                                   wx.ICON_QUESTION | wx.CENTRE)
+            ret = dlg.ShowModal()
+            if ret != wx.ID_YES:
+                return
+        
+        # parametrization
+        params = self.model.Parameterize()
+        if params:
+            dlg = ModelParamDialog(parent = self,
+                                   params = params)
+            dlg.CenterOnParent()
+            
+            ret = dlg.ShowModal()
+            if ret != wx.ID_OK:
+                dlg.Destroy()
+                return
+        
+            err = dlg.GetErrors()
+            if err:
+                GError(parent = self,
+                       message = unicode('\n'.join(err)))
+                return
+        
+        self.goutput.cmdThread.SetId(-1)
+        for item in self.model.GetItems():
+            if not item.IsEnabled():
+                continue
+            if isinstance(item, ModelAction):
+                if item.GetLoopId():
+                    continue
+                self._runAction(item, params)
+            elif isinstance(item, ModelLoop):
+                cond = item.GetText()
+                # substitute variables in condition
+                variables = self.model.GetVariables()
+                for variable in variables:
+                    pattern = re.compile('%' + variable)
+                    if pattern.search(cond):
+                        value = variables[variable].get('value', '')
+                        vtype = variables[variable].get('type', 'string')
+                        if vtype == 'string':
+                            value = '"' + value + '"'
+                        cond = pattern.sub(value, cond)
+                # split condition
+                condVar, condText = re.split('\s*in\s*', cond)
+                
+                for action in item.GetItems():
+                    for vars()[condVar] in eval(condText):
+                        if isinstance(action, ModelAction):
+                            self._runAction(action, params)
+        
+        if params:
+            dlg.Destroy()
+        
+    def _runAction(self, item, params):
+        """!Run given action"""
+        name = item.GetName()
+        if params.has_key(name):
+            paramsOrig = item.GetParams(dcopy = True)
+            item.MergeParams(params[name])
+            
+        self.SetStatusText(_('Running model...'), 0)
+        self.goutput.RunCmd(command = item.GetLog(string = False),
+                            onDone = self.OnDone)
+            
+        if params.has_key(name):
+            item.SetParams(paramsOrig)
+        
+    def OnDone(self, cmd, returncode):
+        """!Computation finished"""
+        self.SetStatusText('', 0)
+        
+    def OnValidateModel(self, event, showMsg = True):
+        """!Validate entire model"""
+        if self.model.GetNumItems() < 1:
+            GMessage(parent = self, 
+                     message = _('Model is empty. Nothing to validate.'))
+            return
+        
+        errList = self._validateModel()
+        
+        if errList:
+            GWarning(parent = self,
+                     message = _('Model is not valid.\n\n%s') % '\n'.join(errList))
+        else:
+            GMessage(parent = self,
+                     message = _('Model is valid.'))
+    
+    def OnExportImage(self, event):
+        """!Export model to image (default image)
+        """
+        xminImg = 0
+        xmaxImg = 0
+        yminImg = 0
+        ymaxImg = 0
+        # get current size of canvas
+        for shape in self.canvas.GetDiagram().GetShapeList():
+            w, h = shape.GetBoundingBoxMax()
+            x    = shape.GetX()
+            y    = shape.GetY()
+            xmin = x - w / 2
+            xmax = x + w / 2
+            ymin = y - h / 2
+            ymax = y + h / 2
+            if xmin < xminImg:
+                xminImg = xmin
+            if xmax > xmaxImg:
+                xmaxImg = xmax
+            if ymin < yminImg:
+                xminImg = xmin
+            if ymax < ymaxImg:
+                xminImg = xmin
+        
+        size = wx.Size(int(xmaxImg - xminImg),
+                       int(ymaxImg - ymaxImg))
+        bitmap = wx.EmptyBitmap(width = size.width, height = size.height)
+        
+        filetype, ltype = GetImageHandlers(wx.ImageFromBitmap(bitmap))
+        
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose a file name to save the image (no need to add extension)"),
+                            defaultDir = "",
+                            defaultFile = "",
+                            wildcard = filetype,
+                            style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            base, ext = os.path.splitext(path)
+            fileType = ltype[dlg.GetFilterIndex()]['type']
+            extType  = ltype[dlg.GetFilterIndex()]['ext']
+            if ext != extType:
+                path = base + '.' + extType
+            
+            dc = wx.MemoryDC(bitmap)
+            dc.SetBackground(wx.WHITE_BRUSH)
+            dc.SetBackgroundMode(wx.SOLID)
+            
+            dc.BeginDrawing()
+            self.canvas.GetDiagram().Clear(dc)
+            self.canvas.GetDiagram().Redraw(dc)
+            dc.EndDrawing()
+            
+            bitmap.SaveFile(path, fileType)
+            self.SetStatusText(_("Model exported to <%s>") % path)
+        
+        dlg.Destroy()
+        
+    def OnExportPython(self, event):
+        """!Export model to Python script"""
+        filename = ''
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose file to save"),
+                            defaultDir = os.getcwd(),
+                            wildcard=_("Python script (*.py)|*.py"),
+                            style=wx.FD_SAVE)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            return
+        
+        # check for extension
+        if filename[-3:] != ".py":
+            filename += ".py"
+        
+        if os.path.exists(filename):
+            dlg = wx.MessageDialog(self, message=_("File <%s> already exists. "
+                                                   "Do you want to overwrite this file?") % filename,
+                                   caption=_("Save file"),
+                                   style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() == wx.ID_NO:
+                dlg.Destroy()
+                return
+            
+            dlg.Destroy()
+        
+        fd = open(filename, "w")
+        try:
+            WritePythonFile(fd, self.model)
+        finally:
+            fd.close()
+        
+        # executable file
+        os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
+        
+        self.SetStatusText(_("Model exported to <%s>") % filename)
+
+    def _validateModel(self):
+        """!Validate model"""
+        self.SetStatusText(_('Validating model...'), 0)
+        
+        errList = self.model.Validate()
+        
+        self.SetStatusText('', 0)
+        
+        return errList
+    
+    def OnDefineRelation(self, event):
+        """!Define relation between data and action items"""
+        self.canvas.SetCursor(self.cursors["cross"])
+        self.defineRelation = { 'from' : None,
+                                'to'   : None }
+        
+    def OnDefineLoop(self, event):
+        """!Define new loop in the model"""
+        self.ModelChanged()
+        
+        width, height = self.canvas.GetSize()
+        loop = ModelLoop(self, x = width/2, y = height/2,
+                         id = self.model.GetNumItems() + 1)
+        self.canvas.diagram.AddShape(loop)
+        loop.Show(True)
+        
+        self._addEvent(loop)
+        self.model.AddItem(loop)
+        
+        self.canvas.Refresh()
+        
+    def OnDefineCondition(self, event):
+        """!Define new condition in the model"""
+        self.ModelChanged()
+        
+        width, height = self.canvas.GetSize()
+        cond = ModelCondition(self, x = width/2, y = height/2,
+                              id = self.model.GetNumItems() + 1)
+        self.canvas.diagram.AddShape(cond)
+        cond.Show(True)
+        
+        self._addEvent(cond)
+        self.model.AddItem(cond)
+        
+        self.canvas.Refresh()
+    
+    def OnAddAction(self, event):
+        """!Add action to model"""
+        if self.searchDialog is None:
+            self.searchDialog = ModelSearchDialog(self)
+            self.searchDialog.CentreOnParent()
+        else:
+            self.searchDialog.Reset()
+            
+        if self.searchDialog.ShowModal() == wx.ID_CANCEL:
+            self.searchDialog.Hide()
+            return
+            
+        cmd = self.searchDialog.GetCmd()
+        self.searchDialog.Hide()
+        
+        self.ModelChanged()
+        
+        # add action to canvas
+        width, height = self.canvas.GetSize()
+        if cmd[0] == 'r.mapcalc':
+            GMessage(parent = self,
+                     message = _("Module r.mapcalc cannot be used in the model. "
+                                 "Use r.mapcalculator instead."))
+            return
+        
+        action = ModelAction(self.model, cmd = cmd, x = width/2, y = height/2,
+                             id = self.model.GetNumItems() + 1)
+        overwrite = self.model.GetProperties().get('overwrite', None)
+        if overwrite is not None:
+            action.GetTask().set_flag('overwrite', overwrite)
+        
+        self.canvas.diagram.AddShape(action)
+        action.Show(True)
+
+        self._addEvent(action)
+        self.model.AddItem(action)
+        
+        self.itemPanel.Update()
+        self.canvas.Refresh()
+        time.sleep(.1)
+        
+        # show properties dialog
+        win = action.GetPropDialog()
+        if not win and action.GetLog(string = False):
+            module = menuform.GUI().ParseCommand(action.GetLog(string = False),
+                                                 completed = (self.GetOptData, action, action.GetParams()),
+                                                 parentframe = self, show = True)
+        elif win and not win.IsShown():
+            win.Show()
+        
+        if win:
+            win.Raise()
+        
+    def OnAddData(self, event):
+        """!Add data item to model
+
+        @todo
+        """
+        # add action to canvas
+        width, height = self.canvas.GetSize()
+        data = ModelData(self, x = width/2, y = height/2)
+        self.canvas.diagram.AddShape(data)
+        data.Show(True)
+        
+        self._addEvent(data)
+        # self.model.AddData(data)
+        
+        self.canvas.Refresh()
+        
+    def OnHelp(self, event):
+        """!Display manual page"""
+        grass.run_command('g.manual',
+                          entry = 'wxGUI.Modeler')
+
+    def OnAbout(self, event):
+        """!Display About window"""
+        info = wx.AboutDialogInfo()
+
+        info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        info.SetName(_('wxGUI Graphical Modeler'))
+        info.SetWebSite('http://grass.osgeo.org')
+        info.SetDescription(_('(C) 2010 by the GRASS Development Team\n\n') + 
+                            '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
+                                                      '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
+        
+        wx.AboutBox(info)
+        
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """!Process action data"""
+        if params: # add data items
+            for p in params['params']:
+                if p.get('prompt', '') in ('raster', 'vector', 'raster3d'):
+                    try:
+                        name, mapset = p.get('value', '').split('@', 1)
+                    except (ValueError, IndexError):
+                        continue
+                    
+                    if mapset != grass.gisenv()['MAPSET']:
+                        continue
+                    
+                    # don't use fully qualified names
+                    p['value'] = p.get('value', '').split('@')[0]
+                    for idx in range(1, len(dcmd)):
+                        if p.get('name', '') in dcmd[idx]:
+                            dcmd[idx] = p.get('name', '') + '=' + p.get('value', '')
+                            break
+            
+            width, height = self.canvas.GetSize()
+            x = [width/2 + 200, width/2 - 200]
+            for p in params['params']:
+                if p.get('prompt', '') in ('raster', 'vector', 'raster3d') and \
+                        (p.get('value', None) or \
+                             (p.get('age', 'old') != 'old' and p.get('required', 'no') == 'yes')):
+                    data = layer.FindData(p.get('name', ''))
+                    if data:
+                        data.SetValue(p.get('value', ''))
+                        data.Update()
+                        continue
+                    
+                    data = self.model.FindData(p.get('value', ''),
+                                               p.get('prompt', ''))
+                    if data:
+                        if p.get('age', 'old') == 'old':
+                            rel = ModelRelation(data, layer, p.get('name', ''))
+                        else:
+                            rel = ModelRelation(layer, data, p.get('name', ''))
+                        layer.AddRelation(rel)
+                        data.AddRelation(rel)
+                        self.AddLine(rel)
+                        data.Update()
+                        continue
+                    
+                    data = ModelData(self, value = p.get('value', ''),
+                                     prompt = p.get('prompt', ''),
+                                     x = x.pop(), y = height/2)
+                    self._addEvent(data)
+                    self.canvas.diagram.AddShape(data)
+                    data.Show(True)
+                                                            
+                    if p.get('age', 'old') == 'old':
+                        rel = ModelRelation(data, layer, p.get('name', ''))
+                    else:
+                        rel = ModelRelation(layer, data, p.get('name', ''))
+                    layer.AddRelation(rel)
+                    data.AddRelation(rel)
+                    self.AddLine(rel)
+                    data.Update()
+            
+            # valid ?
+            valid = True
+            for p in params['params']:
+                if p.get('required', 'no') != 'no' and \
+                        p.get('value', '') == '' and \
+                        p.get('default', '') == '':
+                    valid = False
+                    break
+            layer.SetValid(valid)
+
+            # parameterized ?
+            parameterized = False
+            for f in params['flags']:
+                if f.get('parameterized', False):
+                    parameterized = True
+                    break
+            if not parameterized:
+                for p in params['params']:
+                    if p.get('parameterized', False):
+                        parameterized = True
+                        break
+            layer.SetParameterized(parameterized)
+            
+            self.canvas.Refresh()
+        
+        if dcmd:
+            layer.SetProperties(params, propwin)
+            
+        self.SetStatusText(layer.GetLog(), 0)
+        
+    def AddLine(self, rel):
+        """!Add connection between model objects
+        
+        @param rel relation
+        """
+        fromShape = rel.GetFrom()
+        toShape   = rel.GetTo()
+        
+        rel.SetCanvas(self)
+        rel.SetPen(wx.BLACK_PEN)
+        rel.SetBrush(wx.BLACK_BRUSH)
+        rel.AddArrow(ogl.ARROW_ARROW)
+        points = rel.GetControlPoints()
+        rel.MakeLineControlPoints(2)
+        if points:
+            for x, y in points:
+                rel.InsertLineControlPoint(point = wx.RealPoint(x, y))
+        
+        self._addEvent(rel)
+        try:
+            fromShape.AddLine(rel, toShape)
+        except TypeError:
+            pass # bug when connecting ModelCondition and ModelLoop - to be fixed
+        
+        self.canvas.diagram.AddShape(rel)
+        rel.Show(True)
+        
+    def LoadModelFile(self, filename):
+        """!Load model definition stored in GRASS Model XML file (gxm)
+        """
+        try:
+            self.model.LoadModel(filename)
+        except GException, e:
+            GError(parent = self,
+                   message = _("Reading model file <%s> failed.\n"
+                               "Invalid file, unable to parse XML document.") % filename)
+        
+        self.modelFile = filename
+        self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile))
+        
+        self.SetStatusText(_("Please wait, loading model..."), 0)
+        
+        # load actions
+        for item in self.model.GetItems(objType = ModelAction):
+            self._addEvent(item)
+            self.canvas.diagram.AddShape(item)
+            item.Show(True)
+            # relations/data
+            for rel in item.GetRelations():
+                if rel.GetFrom() == item:
+                    dataItem = rel.GetTo()
+                else:
+                    dataItem = rel.GetFrom()
+                self._addEvent(dataItem)
+                self.canvas.diagram.AddShape(dataItem)
+                self.AddLine(rel)
+                dataItem.Show(True)
+        
+        # load loops
+        for item in self.model.GetItems(objType = ModelLoop):
+            self._addEvent(item)
+            self.canvas.diagram.AddShape(item)
+            item.Show(True)
+            
+            # connect items in the loop
+            self.DefineLoop(item)
+
+        # load conditions
+        for item in self.model.GetItems(objType = ModelCondition):
+            self._addEvent(item)
+            self.canvas.diagram.AddShape(item)
+            item.Show(True)
+            
+            # connect items in the condition
+            self.DefineCondition(item)
+        
+        # load variables
+        self.variablePanel.Update()
+        self.itemPanel.Update()
+        self.SetStatusText('', 0)
+        
+        self.canvas.Refresh(True)
+        
+    def WriteModelFile(self, filename):
+        """!Save model to model file, recover original file on error.
+        
+        @return True on success
+        @return False on failure
+        """
+        self.ModelChanged(False)
+        tmpfile = tempfile.TemporaryFile(mode='w+b')
+        try:
+            WriteModelFile(fd = tmpfile, model = self.model)
+        except StandardError:
+            GError(parent = self,
+                   message = _("Writing current settings to model file failed."))
+            return False
+        
+        try:
+            mfile = open(filename, "w")
+            tmpfile.seek(0)
+            for line in tmpfile.readlines():
+                mfile.write(line)
+        except IOError:
+            wx.MessageBox(parent = self,
+                          message = _("Unable to open file <%s> for writing.") % filename,
+                          caption = _("Error"),
+                          style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            return False
+        
+        mfile.close()
+        
+        return True
+    
+    def DefineLoop(self, loop):
+        """!Define loop with given list of items"""
+        parent = loop
+        items = loop.GetItems()
+        if not items:
+            return
+        
+        # remove defined relations first
+        for rel in loop.GetRelations():
+            self.canvas.GetDiagram().RemoveShape(rel)
+        loop.Clear()
+        
+        for item in items:
+            rel = ModelRelation(parent, item)
+            dx = item.GetX() - parent.GetX()
+            dy = item.GetY() - parent.GetY()
+            loop.AddRelation(rel)
+            if dx != 0:
+                rel.SetControlPoints(((parent.GetX(), parent.GetY() + dy / 2),
+                                      (parent.GetX() + dx, parent.GetY() + dy / 2)))
+            self.AddLine(rel)
+            parent = item
+        
+        # close loop
+        item = loop.GetItems()[-1]
+        rel = ModelRelation(item, loop)
+        loop.AddRelation(rel)
+        self.AddLine(rel)
+        dx = (item.GetX() - loop.GetX()) + loop.GetWidth() / 2 + 50
+        dy = item.GetHeight() / 2 + 50
+        rel.MakeLineControlPoints(0)
+        rel.InsertLineControlPoint(point = wx.RealPoint(loop.GetX() - loop.GetWidth() / 2 ,
+                                                        loop.GetY()))
+        rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
+                                                        item.GetY() + item.GetHeight() / 2))
+        rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
+                                                        item.GetY() + dy))
+        rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
+                                                        item.GetY() + dy))
+        rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
+                                                        loop.GetY()))
+        
+        self.canvas.Refresh()
+
+    def DefineCondition(self, condition):
+        """!Define if-else statement with given list of items"""
+        parent = condition
+        items = condition.GetItems()
+        if not items['if'] and not items['else']:
+            return
+        
+        # remove defined relations first
+        for rel in condition.GetRelations():
+            self.canvas.GetDiagram().RemoveShape(rel)
+        condition.Clear()
+        dxIf   = condition.GetX() + condition.GetWidth() / 2
+        dxElse = condition.GetX() - condition.GetWidth() / 2
+        dy     = condition.GetY()
+        for branch in items.keys():
+            for item in items[branch]:
+                rel = ModelRelation(parent, item)
+                condition.AddRelation(rel)
+                self.AddLine(rel)
+                rel.MakeLineControlPoints(0)
+                if branch == 'if':
+                    rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
+                    rel.InsertLineControlPoint(point = wx.RealPoint(dxIf, dy))
+                else:
+                    rel.InsertLineControlPoint(point = wx.RealPoint(dxElse, dy))
+                    rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
+                parent = item
+        
+        self.canvas.Refresh()
+        
+class ModelCanvas(ogl.ShapeCanvas):
+    """!Canvas where model is drawn"""
+    def __init__(self, parent):
+        self.parent = parent
+        ogl.OGLInitialize()
+        ogl.ShapeCanvas.__init__(self, parent)
+        
+        self.diagram = ogl.Diagram()
+        self.SetDiagram(self.diagram)
+        self.diagram.SetCanvas(self)
+        
+        self.SetScrollbars(20, 20, 1000/20, 1000/20)
+        
+        self.Bind(wx.EVT_CHAR,  self.OnChar)
+        
+    def OnChar(self, event):
+        """!Key pressed"""
+        kc = event.GetKeyCode()
+        diagram = self.GetDiagram()
+        if kc == wx.WXK_DELETE:
+            self.RemoveSelected()
+        
+    def RemoveSelected(self):
+        """!Remove selected shapes"""
+        self.parent.ModelChanged()
+        
+        diagram = self.GetDiagram()
+        for shape in diagram.GetShapeList():
+            if not shape.Selected():
+                continue
+            remList, upList = self.parent.GetModel().RemoveItem(shape)
+            shape.Select(False)
+            diagram.RemoveShape(shape)
+            del shape
+            for item in remList:
+                diagram.RemoveShape(item)
+                item.__del__()
+            
+            for item in upList:
+                item.Update()
+        
+        self.Refresh()
+        
+class ModelObject:
+    def __init__(self, id = -1):
+        self.id   = id
+        self.rels = list() # list of ModelRelations
+        
+        self.isEnabled = True
+        self.inBlock   = list() # list of related loops/conditions
+        
+    def __del__(self):
+        pass
+    
+    def GetId(self):
+        """!Get id"""
+        return self.id
+    
+    def AddRelation(self, rel):
+        """!Record new relation
+        """
+        self.rels.append(rel)
+
+    def GetRelations(self, fdir = None):
+        """!Get list of relations
+        
+        @param fdir True for 'from'
+        """
+        if fdir is None:
+            return self.rels
+        
+        result = list()
+        for rel in self.rels:
+            if fdir == 'from':
+                if rel.GetFrom() == self:
+                    result.append(rel)
+            else:
+                if rel.GetTo() == self:
+                    result.append(rel)
+        
+        return result
+    
+    def IsEnabled(self):
+        """!Get True if action is enabled, otherwise False"""
+        return self.isEnabled
+    
+    def Enable(self, enabled = True):
+        """!Enable/disable action"""
+        self.isEnabled = enabled
+        self.Update()
+
+    def Update(self):
+        pass
+
+    def SetBlock(self, item):
+        """!Add object to the block (loop/condition)
+
+        @param item reference to ModelLoop or ModelCondition which
+        defines loops/condition
+        """
+        if item not in self.inBlock:
+            self.inBlock.append(item)
+        
+    def UnSetBlock(self, item):
+        """!Remove object from the block (loop/consition)
+
+        @param item reference to ModelLoop or ModelCondition which
+        defines loops/codition
+        """
+        if item in self.inBlock:
+            self.inBlock.remove(item)
+        
+    def GetBlock(self):
+        """!Get list of related ModelObject(s) which defines block
+        (loop/condition)
+
+        @return list of ModelObjects
+        """
+        return self.inBlock
+    
+    def GetBlockId(self):
+        """!Get list of related ids which defines block
+
+        @return list of ids
+        """
+        ret = list()
+        for mo in self.inBlock:
+            ret.append(mo.GetId())
+        
+        return ret
+    
+class ModelAction(ModelObject, ogl.RectangleShape):
+    """!Action class (GRASS module)"""
+    def __init__(self, parent, x, y, id = -1, cmd = None, task = None, width = None, height = None):
+        ModelObject.__init__(self, id)
+        
+        self.parent  = parent
+        self.task    = task
+        
+        if not width:
+            width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width'))
+        if not height:
+            height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height'))
+        
+        if cmd:
+            self.task = menuform.GUI().ParseCommand(cmd = cmd,
+                                                    show = None)
+        else:
+            if task:
+                self.task = task
+            else:
+                self.task = None
+        
+        self.propWin = None
+        
+        self.data = list()   # list of connected data items
+        
+        self.isValid = False
+        self.isParameterized = False
+        
+        if self.parent.GetCanvas():
+            ogl.RectangleShape.__init__(self, width, height)
+            
+            self.SetCanvas(self.parent)
+            self.SetX(x)
+            self.SetY(y)
+            self.SetPen(wx.BLACK_PEN)
+            self._setPen()
+            self._setBrush()
+            self.SetId(id)
+            
+    def _setBrush(self, running = False):
+        """!Set brush"""
+        if running:
+            color = UserSettings.Get(group='modeler', key='action',
+                                     subkey=('color', 'running'))
+        elif not self.isEnabled:
+            color = UserSettings.Get(group='modeler', key='action',
+                                     subkey=('color', 'disabled'))
+        elif self.isValid:
+            color = UserSettings.Get(group='modeler', key='action',
+                                     subkey=('color', 'valid'))
+        else:
+            color = UserSettings.Get(group='modeler', key='action',
+                                     subkey=('color', 'invalid'))
+        
+        wxColor = wx.Color(color[0], color[1], color[2])
+        self.SetBrush(wx.Brush(wxColor))
+        
+    def _setPen(self):
+        """!Set pen"""
+        if self.isParameterized:
+            width = int(UserSettings.Get(group='modeler', key='action',
+                                         subkey=('width', 'parameterized')))
+        else:
+            width = int(UserSettings.Get(group='modeler', key='action',
+                                         subkey=('width', 'default')))
+        pen = self.GetPen()
+        pen.SetWidth(width)
+        self.SetPen(pen)
+
+    def SetId(self, id):
+        """!Set id"""
+        self.id = id
+        cmd = self.task.getCmd(ignoreErrors = True)
+        if cmd and len(cmd) > 0:
+            self.ClearText()
+            self.AddText('(%d) %s' % (self.id, cmd[0]))
+        else:
+            self.AddText('(%d) <<%s>>' % (self.id, _("unknown")))
+        
+    def SetProperties(self, params, propwin):
+        """!Record properties dialog"""
+        self.task.params = params['params']
+        self.task.flags  = params['flags']
+        self.propWin = propwin
+
+    def GetPropDialog(self):
+        """!Get properties dialog"""
+        return self.propWin
+
+    def GetLog(self, string = True):
+        """!Get logging info"""
+        cmd = self.task.getCmd(ignoreErrors = True)
+        # substitute variables
+        variables = self.parent.GetVariables()
+        fparams = self.parent.GetVariables(params = True)
+        params = None
+        for values in fparams.itervalues():
+            params = values['params']
+            break
+        
+        for variable in variables:
+            pattern= re.compile('%' + variable)
+            value = None
+            if params:
+                for p in params:
+                    if variable == p.get('name', ''):
+                        value = p.get('value', '')
+                        break
+            if not value:
+                value = variables[variable].get('value', '')
+            
+            for idx in range(len(cmd)):
+                if pattern.search(cmd[idx]):
+                    if value:
+                        cmd[idx] = pattern.sub(value, cmd[idx])
+                    else:
+                        self.isValid = False
+                    break
+                idx += 1
+        
+        if string:
+            if cmd is None:
+                return ''
+            else:
+                return ' '.join(cmd)
+        
+        return cmd
+    
+    def GetName(self):
+        """!Get name"""
+        cmd = self.task.getCmd(ignoreErrors = True)
+        if cmd and len(cmd) > 0:
+            return cmd[0]
+        
+        return _('unknown')
+
+    def GetParams(self, dcopy = False):
+        """!Get dictionary of parameters"""
+        if dcopy:
+            return copy.deepcopy(self.task.get_options())
+        
+        return self.task.get_options()
+
+    def GetTask(self):
+        """!Get grassTask instance"""
+        return self.task
+    
+    def SetParams(self, params):
+        """!Set dictionary of parameters"""
+        self.task.params = params['params']
+        self.task.flags  = params['flags']
+        
+    def MergeParams(self, params):
+        """!Merge dictionary of parameters"""
+        if params.has_key('flags'):
+            for f in params['flags']:
+                self.task.set_flag(f['name'],
+                                   f.get('value', False))
+        if params.has_key('params'):
+            for p in params['params']:
+                self.task.set_param(p['name'],
+                                    p.get('value', ''))
+        
+    def SetValid(self, isvalid):
+        """!Set instance to be valid/invalid"""
+        self.isValid = isvalid
+        self._setBrush()
+        
+    def SetParameterized(self, isparameterized):
+        """!Set action parameterized"""
+        self.isParameterized = isparameterized
+        self._setPen()
+
+    def IsParameterized(self):
+        """!Check if action is parameterized"""
+        return self.isParameterized
+    
+    def FindData(self, name):
+        """!Find data item by name"""
+        for rel in self.GetRelations():
+            data = rel.GetData()
+            if name == rel.GetName() and name in data.GetName():
+                return data
+        
+        return None
+
+    def Update(self, running = False):
+        """!Update action"""
+        if running:
+            self._setBrush(running = True)
+        else:
+            self._setBrush()
+        self._setPen()
+
+    def OnDraw(self, dc):
+        """!Draw action in canvas"""
+        self._setBrush()
+        self._setPen()
+        ogl.RectangleShape.OnDraw(self, dc)
+    
+class ModelData(ModelObject, ogl.EllipseShape):
+    def __init__(self, parent, x, y, value = '', prompt = '', width = None, height = None):
+        """Data item class
+        
+        @param parent window parent
+        @param x, y   position of the shape
+        @param fname, tname list of parameter names from / to
+        @param value  value
+        @param prompt type of GIS element
+        @param width, height dimension of the shape
+        """
+        ModelObject.__init__(self)
+        
+        self.parent  = parent
+        self.value   = value
+        self.prompt  = prompt
+        self.intermediate = False
+        self.propWin = None
+        if not width:
+            width = UserSettings.Get(group='modeler', key='data', subkey=('size', 'width'))
+        if not height:
+            height = UserSettings.Get(group='modeler', key='data', subkey=('size', 'height'))
+        
+        if self.parent.GetCanvas():
+            ogl.EllipseShape.__init__(self, width, height)
+            
+            self.SetCanvas(self.parent)
+            self.SetX(x)
+            self.SetY(y)
+            self.SetPen(wx.BLACK_PEN)
+            self._setBrush()
+            
+            self._setText()
+            
+    def IsIntermediate(self):
+        """!Checks if data item is intermediate"""
+        return self.intermediate
+    
+    def SetIntermediate(self, im):
+        """!Set intermediate flag"""
+        self.intermediate = im
+  
+    def OnDraw(self, dc):
+        pen = self.GetPen()
+        pen.SetWidth(1)
+        if self.intermediate:
+            pen.SetStyle(wx.SHORT_DASH)
+        else:
+            pen.SetStyle(wx.SOLID)
+        self.SetPen(pen)
+        
+        ogl.EllipseShape.OnDraw(self, dc)
+        
+    def GetLog(self, string = True):
+        """!Get logging info"""
+        name = list()
+        for rel in self.GetRelations():
+            name.append(rel.GetName())
+        if name:
+            return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')'
+        else:
+            return _('unknown')
+
+    def GetName(self):
+        """!Get list of names"""
+        name = list()
+        for rel in self.GetRelations():
+            name.append(rel.GetName())
+        
+        return name
+    
+    def GetPrompt(self):
+        """!Get prompt"""
+        return self.prompt
+
+    def GetValue(self):
+        """!Get value"""
+        return self.value
+
+    def SetValue(self, value):
+        """!Set value"""
+        self.value = value
+        self._setText()
+        for direction in ('from', 'to'):
+            for rel in self.GetRelations(direction):
+                if direction == 'from':
+                    action = rel.GetTo()
+                else:
+                    action = rel.GetFrom()
+                
+                task = menuform.GUI().ParseCommand(cmd = action.GetLog(string = False),
+                                                   show = None)
+                task.set_param(rel.GetName(), self.value)
+                action.SetParams(params = task.get_options())
+        
+    def AddName(self, name, direction):
+        """!Record new name (parameter)
+
+        @param direction direction - 'from' or 'to'
+        """
+        self.name[direction].append(name)
+        
+    def GetPropDialog(self):
+        """!Get properties dialog"""
+        return self.propWin
+
+    def SetPropDialog(self, win):
+        """!Get properties dialog"""
+        self.propWin = win
+
+    def _setBrush(self):
+        """!Set brush"""
+        if self.prompt == 'raster':
+            color = UserSettings.Get(group='modeler', key='data',
+                                     subkey=('color', 'raster'))
+        elif self.prompt == 'raster3d':
+            color = UserSettings.Get(group='modeler', key='data',
+                                     subkey=('color', 'raster3d'))
+        elif self.prompt == 'vector':
+            color = UserSettings.Get(group='modeler', key='data',
+                                     subkey=('color', 'vector'))
+        else:
+            color = UserSettings.Get(group='modeler', key='action',
+                                     subkey=('color', 'invalid'))
+        wxColor = wx.Color(color[0], color[1], color[2])
+        self.SetBrush(wx.Brush(wxColor))
+        
+    def _setPen(self):
+        """!Set pen"""
+        isParameterized = False
+        for rel in self.GetRelations('from'):
+            if rel.GetTo().IsParameterized():
+                isParameterized = True
+                break
+        if not isParameterized:
+            for rel in self.GetRelations('to'):
+                if rel.GetFrom().IsParameterized():
+                    isParameterized = True
+                    break
+
+        if isParameterized:
+            width = int(UserSettings.Get(group='modeler', key='action',
+                                         subkey=('width', 'parameterized')))
+        else:
+            width = int(UserSettings.Get(group='modeler', key='action',
+                                         subkey=('width', 'default')))
+        pen = self.GetPen()
+        pen.SetWidth(width)
+        self.SetPen(pen)
+        
+    def _setText(self):
+        """!Update text"""
+        self.ClearText()
+        name = []
+        for rel in self.GetRelations():
+            name.append(rel.GetName())
+        self.AddText('/'.join(name))
+        if self.value:
+            self.AddText(self.value)
+        else:
+            self.AddText(_('<not defined>'))
+        
+    def Update(self):
+        """!Update action"""
+        self._setBrush()
+        self._setPen()
+        self._setText()
+        
+class ModelDataDialog(ElementDialog):
+    """!Data item properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Data properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        self.parent = parent
+        self.shape = shape
+        prompt = shape.GetPrompt()
+        
+        if prompt == 'raster':
+            label = _('Name of raster map:')
+        elif prompt == 'vector':
+            label = _('Name of vector map:')
+        else:
+            label = _('Name of element:')
+        
+        ElementDialog.__init__(self, parent, title, label = label)
+        
+        self.element = gselect.Select(parent = self.panel, id = wx.ID_ANY,
+                                      size = globalvar.DIALOG_GSELECT_SIZE,
+                                      type = prompt)
+        self.element.SetValue(shape.GetValue())
+        
+        self.Bind(wx.EVT_BUTTON, self.OnOK,     self.btnOK)
+        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
+        
+        self.PostInit()
+        
+        if shape.GetValue():
+            self.btnOK.Enable()
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        
+    def _layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion=0,
+                      flag=wx.EXPAND | wx.ALL, border=1)
+        
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def OnOK(self, event):
+        """!Ok pressed"""
+        self.shape.SetValue(self.GetElement())
+        self.parent.canvas.Refresh()
+        self.parent.SetStatusText('', 0)
+        self.OnCancel(event)
+        
+    def OnCancel(self, event):
+        """!Cancel pressed"""
+        self.shape.SetPropDialog(None)
+        self.Destroy()
+        
+class ModelEvtHandler(ogl.ShapeEvtHandler):
+    """!Model event handler class"""
+    def __init__(self, log, frame):
+        ogl.ShapeEvtHandler.__init__(self)
+        self.log = log
+        self.frame = frame
+        self.x = self.y = None
+        
+    def OnLeftClick(self, x, y, keys = 0, attachment = 0):
+        """!Left mouse button pressed -> select item & update statusbar"""
+        shape = self.GetShape()
+        canvas = shape.GetCanvas()
+        dc = wx.ClientDC(canvas)
+        canvas.PrepareDC(dc)
+        
+        if hasattr(self.frame, 'defineRelation'):
+            drel = self.frame.defineRelation
+            if drel['from'] is None:
+                drel['from'] = shape
+            elif drel['to'] is None:
+                drel['to'] = shape
+                rel = ModelRelation(drel['from'], drel['to'])
+                drel['from'].AddRelation(rel)
+                self.frame.AddLine(rel)
+                del self.frame.defineRelation
+        
+        if shape.Selected():
+            shape.Select(False, dc)
+        else:
+            redraw = False
+            shapeList = canvas.GetDiagram().GetShapeList()
+            toUnselect = list()
+            
+            for s in shapeList:
+                if s.Selected():
+                    toUnselect.append(s)
+            
+            shape.Select(True, dc)
+            
+            for s in toUnselect:
+                s.Select(False, dc)
+                
+        canvas.Refresh(False)
+
+        if hasattr(shape, "GetLog"):
+            self.log.SetStatusText(shape.GetLog(), 0)
+        else:
+            self.log.SetStatusText('', 0)
+        
+    def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
+        """!Left mouse button pressed (double-click) -> show properties"""
+        self.OnProperties()
+        
+    def OnProperties(self, event = None):
+        """!Show properties dialog"""
+        self.frame.ModelChanged()
+        shape = self.GetShape()
+        if isinstance(shape, ModelAction):
+            module = menuform.GUI().ParseCommand(shape.GetLog(string = False),
+                                                 completed = (self.frame.GetOptData, shape, shape.GetParams()),
+                                                 parentframe = self.frame, show = True)
+        
+        elif isinstance(shape, ModelData):
+            dlg = ModelDataDialog(parent = self.frame, shape = shape)
+            shape.SetPropDialog(dlg)
+            dlg.CentreOnParent()
+            dlg.Show()
+        
+        elif isinstance(shape, ModelLoop):
+            dlg = ModelLoopDialog(parent = self.frame, shape = shape)
+            dlg.CentreOnParent()
+            if dlg.ShowModal() == wx.ID_OK:
+                shape.SetText(dlg.GetCondition())
+                alist = list()
+                ids = dlg.GetItems()
+                for aId in ids['unchecked']:
+                    action = self.frame.GetModel().GetItem(aId)
+                    action.UnSetBlock(shape)
+                for aId in ids['checked']:
+                    action = self.frame.GetModel().GetItem(aId)
+                    action.SetBlock(shape)
+                    if action:
+                        alist.append(action)
+                shape.SetItems(alist)
+                self.frame.DefineLoop(shape)
+            self.frame.GetCanvas().Refresh()
+            
+            dlg.Destroy()
+        
+        elif isinstance(shape, ModelCondition):
+            dlg = ModelConditionDialog(parent = self.frame, shape = shape)
+            dlg.CentreOnParent()
+            if dlg.ShowModal() == wx.ID_OK:
+                shape.SetText(dlg.GetCondition())
+                ids = dlg.GetItems()
+                for b in ids.keys():
+                    alist = list()
+                    for aId in ids[b]['unchecked']:
+                        action = self.frame.GetModel().GetItem(aId)
+                        action.UnSetBlock(shape)
+                    for aId in ids[b]['checked']:
+                        action = self.frame.GetModel().GetItem(aId)
+                        action.SetBlock(shape)
+                        if action:
+                            alist.append(action)
+                    shape.SetItems(alist, branch = b)
+                self.frame.DefineCondition(shape)
+            self.frame.GetCanvas().Refresh()
+            
+            dlg.Destroy()
+                   
+    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
+        """!Drag shape (begining)"""
+        self.frame.ModelChanged()
+        if self._previousHandler:
+            self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
+        
+    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
+        """!Drag shape (end)"""
+        if self._previousHandler:
+            self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
+        
+        shape = self.GetShape()
+        if isinstance(shape, ModelLoop):
+            self.frame.DefineLoop(shape)
+        elif isinstance(shape, ModelCondition):
+            self.frame.DefineCondition(shape)
+        
+        for mo in shape.GetBlock():
+            if isinstance(mo, ModelLoop):
+                self.frame.DefineLoop(mo)
+            elif isinstance(mo, ModelCondition):
+                self.frame.DefineCondition(mo)
+        
+    def OnEndSize(self, x, y):
+        """!Resize shape"""
+        self.frame.ModelChanged()
+        if self._previousHandler:
+            self._previousHandler.OnEndSize(x, y)
+        
+    def OnRightClick(self, x, y, keys = 0, attachment = 0):
+        """!Right click -> pop-up menu"""
+        if not hasattr (self, "popupID1"):
+            self.popupID1 = wx.NewId()
+            self.popupID2 = wx.NewId()
+            self.popupID3 = wx.NewId()
+            self.popupID4 = wx.NewId()
+
+        # record coordinates
+        self.x = x
+        self.y = y
+        
+        shape = self.GetShape()
+        popupMenu = wx.Menu()
+        popupMenu.Append(self.popupID1, text=_('Remove'))
+        self.frame.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID1)
+        if isinstance(shape, ModelAction):
+            if shape.IsEnabled():
+                popupMenu.Append(self.popupID3, text=_('Disable'))
+                self.frame.Bind(wx.EVT_MENU, self.OnDisable, id = self.popupID3)
+            else:
+                popupMenu.Append(self.popupID3, text=_('Enable'))
+                self.frame.Bind(wx.EVT_MENU, self.OnEnable, id = self.popupID3)
+        
+        if isinstance(shape, ModelRelation):
+            popupMenu.AppendSeparator()
+            popupMenu.Append(self.popupID2, text=_('Add control point'))
+            self.frame.Bind(wx.EVT_MENU, self.OnAddPoint, id = self.popupID2)
+            popupMenu.Append(self.popupID3, text=_('Remove control point'))
+            self.frame.Bind(wx.EVT_MENU, self.OnRemovePoint, id = self.popupID3)
+            if len(shape.GetLineControlPoints()) == 2:
+                popupMenu.Enable(self.popupID3, False)
+        
+        if isinstance(shape, ModelData) and '@' not in shape.GetValue():
+            popupMenu.AppendSeparator()
+            popupMenu.Append(self.popupID3, text=_('Intermediate'),
+                             kind = wx.ITEM_CHECK)
+            if self.GetShape().IsIntermediate():
+                popupMenu.Check(self.popupID3, True)
+            
+            self.frame.Bind(wx.EVT_MENU, self.OnIntermediate, id = self.popupID3)
+            
+        if isinstance(shape, ModelData) or \
+                isinstance(shape, ModelAction) or \
+                isinstance(shape, ModelLoop):
+            popupMenu.AppendSeparator()
+            popupMenu.Append(self.popupID2, text=_('Properties'))
+            self.frame.Bind(wx.EVT_MENU, self.OnProperties, id = self.popupID2)
+        
+        if isinstance(shape, ModelAction):
+            popupMenu.Append(self.popupID4, text=_('Change ID'))
+            self.frame.Bind(wx.EVT_MENU, self.OnChangeId, id = self.popupID3)
+        
+        self.frame.PopupMenu(popupMenu)
+        popupMenu.Destroy()
+
+    def OnChangeId(self, event):
+        """!Change action id"""
+        pass
+    
+    def OnDisable(self, event):
+        """!Disable action"""
+        action = self.GetShape()
+        action.Enable(False)
+        self.frame.ModelChanged()
+        self.frame.canvas.Refresh()
+
+    def OnEnable(self, event):
+        """!Disable action"""
+        action = self.GetShape()
+        action.Enable(True)
+        self.frame.ModelChanged()
+        self.frame.canvas.Refresh()
+        
+    def OnAddPoint(self, event):
+        """!Add control point"""
+        shape = self.GetShape()
+        shape.InsertLineControlPoint(point = wx.RealPoint(self.x, self.y))
+        shape.ResetShapes()
+        shape.Select(True)
+        self.frame.ModelChanged()
+        self.frame.canvas.Refresh()
+        
+    def OnRemovePoint(self, event):
+        """!Remove control point"""
+        shape = self.GetShape()
+        shape.DeleteLineControlPoint()
+        shape.Select(False)
+        shape.Select(True)
+        self.frame.ModelChanged()
+        self.frame.canvas.Refresh()
+        
+    def OnIntermediate(self, event):
+        """!Mark data as intermediate"""
+        self.frame.ModelChanged()
+        shape = self.GetShape()
+        shape.SetIntermediate(event.IsChecked())
+        self.frame.canvas.Refresh()
+
+    def OnRemove(self, event):
+        """!Remove shape
+        """
+        self.frame.GetCanvas().RemoveSelected()
+        self.frame.itemPanel.Update()
+        
+class ModelSearchDialog(wx.Dialog):
+    def __init__(self, parent, id = wx.ID_ANY, title = _("Add new GRASS module to the model"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Graphical modeler module search window
+        
+        @param parent parent window
+        @param id window id
+        @param title window title
+        @param kwargs wx.Dialogs' arguments
+        """
+        self.parent = parent
+        
+        wx.Dialog.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        self.SetName("ModelerDialog")
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                   label=" %s " % _("Command"))
+        
+        self.cmd_prompt = prompt.GPromptSTC(parent = self)
+        self.search = SearchModuleWindow(parent = self.panel, cmdPrompt = self.cmd_prompt, showTip = True)
+        wx.CallAfter(self.cmd_prompt.SetFocus)
+        
+        # get commands
+        items = self.cmd_prompt.GetCommandItems()
+        
+        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+        self.btnOk     = wx.Button(self.panel, wx.ID_OK)
+        self.btnOk.SetDefault()
+        self.btnOk.Enable(False)
+
+        self.cmd_prompt.Bind(wx.EVT_CHAR, self.OnText)
+        self.search.searchChoice.Bind(wx.EVT_CHOICE, self.OnText)
+        self.Bind(wx.EVT_BUTTON, self.OnOk, self.btnOk)
+        
+        self._layout()
+        
+        self.SetSize((500, 275))
+        
+    def _layout(self):
+        cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL)
+        cmdSizer.Add(item = self.cmd_prompt, proportion = 1,
+                     flag = wx.EXPAND)
+        
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.search, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 3)
+        mainSizer.Add(item = cmdSizer, proportion = 1,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.panel.SetSizer(mainSizer)
+        mainSizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def GetPanel(self):
+        """!Get dialog panel"""
+        return self.panel
+
+    def GetCmd(self):
+        """!Get command"""
+        line = self.cmd_prompt.GetCurLine()[0].strip()
+        if len(line) == 0:
+            list()
+        
+        try:
+            cmd = shlex.split(str(line))
+        except UnicodeError:
+            cmd = shlex.split(utils.EncodeString((line)))
+            
+        return cmd
+    
+    def OnOk(self, event):
+        self.btnOk.SetFocus()
+        cmd = self.GetCmd()
+        
+        if len(cmd) < 1:
+            GError(parent = self,
+                   message = _("Command not defined.\n\n"
+                               "Unable to add new action to the model."))
+            return
+        
+        if cmd[0] not in globalvar.grassCmd['all']:
+            GError(parent = self,
+                   message = _("'%s' is not a GRASS module.\n\n"
+                               "Unable to add new action to the model.") % cmd[0])
+            return
+        
+        self.EndModal(wx.ID_OK)
+        
+    def OnText(self, event):
+        """!Text entered"""
+        if self.cmd_prompt.AutoCompActive():
+            event.Skip()
+            return
+        
+        if isinstance(event, wx.KeyEvent):
+            entry = self.cmd_prompt.GetTextLeft() # FIXME
+        else:
+            entry = event.GetString()
+        
+        if entry:
+            self.btnOk.Enable()
+        else:
+            self.btnOk.Enable(False)
+            
+        event.Skip()
+        
+    def Reset(self):
+        """!Reset dialog"""
+        self.search.Reset()
+        self.cmd_prompt.OnCmdErase(None)
+
+class ModelRelation(ogl.LineShape):
+    """!Data - action relation"""
+    def __init__(self, fromShape, toShape, param = ''):
+        self.fromShape = fromShape
+        self.toShape   = toShape
+        self.param     = param
+        
+        self._points    = None
+        
+        ogl.LineShape.__init__(self)
+    
+    def __del__(self):
+        self.fromShape.rels.remove(self)
+        self.toShape.rels.remove(self)
+
+    def GetFrom(self):
+        """!Get id of 'from' shape"""
+        return self.fromShape
+    
+    def GetTo(self):
+        """!Get id of 'to' shape"""
+        return self.toShape
+    
+    def GetData(self):
+        """!Get related ModelData instance
+
+        @return ModelData instance
+        @return None if not found
+        """
+        if isinstance(self.fromShape, ModelData):
+            return self.fromShape
+        elif isinstance(self.toShape, ModelData):
+            return self.toShape
+        
+        return None
+    
+    def GetName(self):
+        """!Get parameter name"""
+        return self.param
+    
+    def ResetShapes(self):
+        """!Reset related objects"""
+        self.fromShape.ResetControlPoints()
+        self.toShape.ResetControlPoints()
+        self.ResetControlPoints()
+        
+    def SetControlPoints(self, points):
+        """!Set control points"""
+        self._points = points
+        
+    def GetControlPoints(self):
+        """!Get list of control points"""
+        return self._points
+    
+    def _setPen(self):
+        """!Set pen"""
+        pen = self.GetPen()
+        pen.SetWidth(1)
+        pen.SetStyle(wx.SOLID)
+        self.SetPen(pen)
+        
+    def OnDraw(self, dc):
+        """!Draw relation"""
+        self._setPen()
+        ogl.LineShape.OnDraw(self, dc)
+        
+class ProcessModelFile:
+    """!Process GRASS model file (gxm)"""
+    def __init__(self, tree):
+        """!A ElementTree handler for the GXM XML file, as defined in
+        grass-gxm.dtd.
+        """
+        self.tree = tree
+        self.root = self.tree.getroot()
+        
+        # list of actions, data
+        self.properties = dict()
+        self.variables  = dict() 
+        self.actions = list()
+        self.data    = list()
+        self.loops   = list()
+        self.conditions = list()
+        
+        self._processWindow()
+        self._processProperties()
+        self._processVariables()
+        self._processItems()
+        self._processData()
+        
+    def _filterValue(self, value):
+        """!Filter value
+        
+        @param value
+        """
+        value = value.replace('&lt;', '<')
+        value = value.replace('&gt;', '>')
+        
+        return value
+        
+    def _getNodeText(self, node, tag, default = ''):
+        """!Get node text"""
+        p = node.find(tag)
+        if p is not None:
+            if p.text:
+                return utils.normalize_whitespace(p.text)
+            else:
+                return ''
+        
+        return default
+    
+    def _processWindow(self):
+        """!Process window properties"""
+        node = self.root.find('window')
+        if node is None:
+            self.pos = self.size = None
+            return
+        
+        self.pos, self.size = self._getDim(node)
+        
+    def _processProperties(self):
+        """!Process model properties"""
+        node = self.root.find('properties')
+        if node is None:
+            return
+        for key in ('name', 'description', 'author'):
+            self._processProperty(node, key)
+        
+        for f in node.findall('flag'):
+            name = f.get('name', '')
+            if name == 'overwrite':
+                self.properties['overwrite'] = True
+        
+    def _processProperty(self, pnode, name):
+        """!Process given property"""
+        node = pnode.find(name)
+        if node is not None:
+            self.properties[name] = node.text
+        else:
+            self.properties[name] = ''
+
+    def _processVariables(self):
+        """!Process model variables"""
+        vnode = self.root.find('variables')
+        if vnode is None:
+            return
+        for node in vnode.findall('variable'):
+            name = node.get('name', '')
+            if not name:
+                continue # should not happen
+            self.variables[name] = { 'type' : node.get('type', 'string') }
+            for key in ('description', 'value'):
+                self._processVariable(node, name, key)
+        
+    def _processVariable(self, pnode, name, key):
+        """!Process given variable"""
+        node = pnode.find(key)
+        if node is not None:
+            if node.text:
+                self.variables[name][key] = node.text
+
+    def _processItems(self):
+        """!Process model items (actions, loops, conditions)"""
+        self._processActions()
+        self._processLoops()
+        self._processConditions()
+        
+    def _processActions(self):
+        """!Process model file"""
+        for action in self.root.findall('action'):
+            pos, size = self._getDim(action)
+            disabled  = False
+            
+            task = action.find('task')
+            if task is not None:
+                if task.find('disabled') is not None:
+                    disabled = True
+                task = self._processTask(task)
+            else:
+                task = None
+            
+            aId = int(action.get('id', -1))
+            
+            self.actions.append({ 'pos'      : pos,
+                                  'size'     : size,
+                                  'task'     : task,
+                                  'id'       : aId,
+                                  'disabled' : disabled })
+            
+    def _getDim(self, node):
+        """!Get position and size of shape"""
+        pos = size = None
+        posAttr = node.get('pos', None)
+        if posAttr:
+            posVal = map(int, posAttr.split(','))
+            try:
+                pos = (posVal[0], posVal[1])
+            except:
+                pos = None
+        
+        sizeAttr = node.get('size', None)
+        if sizeAttr:
+            sizeVal = map(int, sizeAttr.split(','))
+            try:
+                size = (sizeVal[0], sizeVal[1])
+            except:
+                size = None
+        
+        return pos, size        
+    
+    def _processData(self):
+        """!Process model file"""
+        for data in self.root.findall('data'):
+            pos, size = self._getDim(data)
+            param = data.find('data-parameter')
+            prompt = value = None
+            if param is not None:
+                prompt = param.get('prompt', None)
+                value = self._filterValue(self._getNodeText(param, 'value'))
+            
+            if data.find('intermediate') is None:
+                intermediate = False
+            else:
+                intermediate = True
+            
+            rels = list()
+            for rel in data.findall('relation'):
+                defrel = { 'id'  : int(rel.get('id', -1)),
+                           'dir' : rel.get('dir', 'to'),
+                           'name' : rel.get('name', '') }
+                points = list()
+                for point in rel.findall('point'):
+                    x = self._filterValue(self._getNodeText(point, 'x'))
+                    y = self._filterValue(self._getNodeText(point, 'y'))
+                    points.append((float(x), float(y)))
+                defrel['points'] = points
+                rels.append(defrel)
+            
+            self.data.append({ 'pos' : pos,
+                               'size': size,
+                               'prompt' : prompt,
+                               'value' : value,
+                               'intermediate' : intermediate,
+                               'rels' : rels })
+        
+    def _processTask(self, node):
+        """!Process task
+
+        @return grassTask instance
+        @return None on error
+        """
+        cmd = list()
+        parameterized = list()
+        
+        name = node.get('name', None)
+        if not name:
+            return None
+        
+        cmd.append(name)
+        
+        # flags
+        for f in node.findall('flag'):
+            flag = f.get('name', '')
+            if f.get('parameterized', '0') == '1':
+                parameterized.append(('flag', flag))
+                if f.get('value', '1') == '0':
+                    continue
+            if len(flag) > 1:
+                cmd.append('--' + flag)
+            else:
+                cmd.append('-' + flag)
+        # parameters
+        for p in node.findall('parameter'):
+            name = p.get('name', '')
+            if p.find('parameterized') is not None:
+                parameterized.append(('param', name))
+            cmd.append('%s=%s' % (name,
+                                  self._filterValue(self._getNodeText(p, 'value'))))
+        
+        task, err = menuform.GUI().ParseCommand(cmd = cmd,
+                                                show = None, checkError = True)
+        if err:
+            GWarning(os.linesep.join(err))
+        
+        for opt, name in parameterized:
+            if opt == 'flag':
+                task.set_flag(name, True, element = 'parameterized')
+            else:
+                task.set_param(name, True, element = 'parameterized')
+        
+        return task
+
+    def _processLoops(self):
+        """!Process model loops"""
+        for node in self.root.findall('loop'):
+            pos, size = self._getDim(node)
+            text = self._filterValue(self._getNodeText(node, 'condition')).strip()
+            aid = list()
+            for anode in node.findall('item'):
+                try:
+                    aid.append(int(anode.text))
+                except ValueError:
+                    pass
+            
+            self.loops.append({ 'pos'     : pos,
+                                'size'    : size,
+                                'text'    : text,
+                                'id'      : int(node.get('id', -1)),
+                                'items'   : aid })
+        
+    def _processConditions(self):
+        """!Process model conditions"""
+        for node in self.root.findall('if-else'):
+            pos, size = self._getDim(node)
+            text = self._filterValue(self._getNodeText(node, 'condition')).strip()
+            aid = { 'if'   : list(),
+                    'else' : list() }
+            for b in aid.keys():
+                bnode = node.find(b)
+                if bnode is None:
+                    continue
+                for anode in bnode.findall('item'):
+                    try:
+                        aid[b].append(int(anode.text))
+                    except ValueError:
+                        pass
+            
+            self.conditions.append({ 'pos'     : pos,
+                                     'size'    : size,
+                                     'text'    : text,
+                                     'id'      : int(node.get('id', -1)),
+                                     'items'   : aid })
+        
+class WriteModelFile:
+    """!Generic class for writing model file"""
+    def __init__(self, fd, model):
+        self.fd         = fd
+        self.model      = model
+        self.properties = model.GetProperties()
+        self.variables  = model.GetVariables()
+        self.items      = model.GetItems()
+        
+        self.indent = 0
+        
+        self._header()
+        
+        self._window()
+        self._properties()
+        self._variables()
+        self._items()
+        
+        dataList = list()
+        for action in model.GetItems(objType = ModelAction):
+            for rel in action.GetRelations():
+                dataItem = rel.GetData()
+                if dataItem not in dataList:
+                    dataList.append(dataItem)
+        self._data(dataList)
+        
+        self._footer()
+
+    def _filterValue(self, value):
+        """!Make value XML-valid"""
+        value = value.replace('<', '&lt;')
+        value = value.replace('>', '&gt;')
+        
+        return value
+        
+    def _header(self):
+        """!Write header"""
+        self.fd.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+        self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n')
+        self.fd.write('%s<gxm>\n' % (' ' * self.indent))
+        self.indent += 4
+                
+    def _footer(self):
+        """!Write footer"""
+        self.indent -= 4
+        self.fd.write('%s</gxm>\n' % (' ' * self.indent))
+
+    def _window(self):
+        """!Write window properties"""
+        canvas = self.model.GetCanvas()
+        if canvas is None:
+            return
+        win  = canvas.parent
+        pos  = win.GetPosition()
+        size = win.GetSize()
+        self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' % \
+                          (' ' * self.indent, pos[0], pos[1], size[0], size[1]))
+        
+    def _properties(self):
+        """!Write model properties"""
+        self.fd.write('%s<properties>\n' % (' ' * self.indent))
+        self.indent += 4
+        if self.properties['name']:
+            self.fd.write('%s<name>%s</name>\n' % (' ' * self.indent, self.properties['name']))
+        if self.properties['description']:
+            self.fd.write('%s<description>%s</description>\n' % (' ' * self.indent, self.properties['description']))
+        if self.properties['author']:
+            self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent, self.properties['author']))
+        
+        if self.properties.has_key('overwrite') and \
+                self.properties['overwrite']:
+            self.fd.write('%s<flag name="overwrite" />\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</properties>\n' % (' ' * self.indent))
+
+    def _variables(self):
+        """!Write model variables"""
+        if not self.variables:
+            return
+        self.fd.write('%s<variables>\n' % (' ' * self.indent))
+        self.indent += 4
+        for name, values in self.variables.iteritems():
+            self.fd.write('%s<variable name="%s" type="%s">\n' % \
+                              (' ' * self.indent, name, values['type']))
+            self.indent += 4
+            if values.has_key('value'):
+                self.fd.write('%s<value>%s</value>\n' % \
+                                  (' ' * self.indent, values['value']))
+            if values.has_key('description'):
+                self.fd.write('%s<description>%s</description>\n' % \
+                                  (' ' * self.indent, values['description']))
+            self.indent -= 4
+            self.fd.write('%s</variable>\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</variables>\n' % (' ' * self.indent))
+        
+    def _items(self):
+        """!Write actions/loops/conditions"""
+        for item in self.items:
+            if isinstance(item, ModelAction):
+                self._action(item)
+            elif isinstance(item, ModelLoop):
+                self._loop(item)
+            elif isinstance(item, ModelCondition):
+                self._condition(item)
+        
+    def _action(self, action):
+        """!Write actions"""
+        self.fd.write('%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' % \
+                          (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(),
+                           action.GetWidth(), action.GetHeight()))
+        self.indent += 4
+        self.fd.write('%s<task name="%s">\n' % (' ' * self.indent, action.GetLog(string = False)[0]))
+        self.indent += 4
+        if not action.IsEnabled():
+            self.fd.write('%s<disabled />\n' % (' ' * self.indent))
+        for key, val in action.GetParams().iteritems():
+            if key == 'flags':
+                for f in val:
+                    if f.get('value', False) or f.get('parameterized', False):
+                        if f.get('parameterized', False):
+                            if f.get('value', False) == False:
+                                self.fd.write('%s<flag name="%s" value="0" parameterized="1" />\n' %
+                                              (' ' * self.indent, f.get('name', '')))
+                            else:
+                                self.fd.write('%s<flag name="%s" parameterized="1" />\n' %
+                                              (' ' * self.indent, f.get('name', '')))
+                        else:
+                            self.fd.write('%s<flag name="%s" />\n' %
+                                          (' ' * self.indent, f.get('name', '')))
+            else: # parameter
+                for p in val:
+                    if not p.get('value', ''):
+                        continue
+                    self.fd.write('%s<parameter name="%s">\n' %
+                                  (' ' * self.indent, p.get('name', '')))
+                    self.indent += 4
+                    if p.get('parameterized', False):
+                        self.fd.write('%s<parameterized />\n' % (' ' * self.indent))
+                    self.fd.write('%s<value>%s</value>\n' %
+                                  (' ' * self.indent, self._filterValue(p.get('value', ''))))
+                    self.indent -= 4
+                    self.fd.write('%s</parameter>\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</task>\n' % (' ' * self.indent))
+        self.indent -= 4
+        self.fd.write('%s</action>\n' % (' ' * self.indent))
+                
+    def _data(self, dataList):
+        """!Write data"""
+        for data in dataList:
+            self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' % \
+                              (' ' * self.indent, data.GetX(), data.GetY(),
+                               data.GetWidth(), data.GetHeight()))
+            self.indent += 4
+            self.fd.write('%s<data-parameter prompt="%s">\n' % \
+                              (' ' * self.indent, data.GetPrompt()))
+            self.indent += 4
+            self.fd.write('%s<value>%s</value>\n' %
+                          (' ' * self.indent, self._filterValue(data.GetValue())))
+            self.indent -= 4
+            self.fd.write('%s</data-parameter>\n' % (' ' * self.indent))
+            
+            if data.IsIntermediate():
+                self.fd.write('%s<intermediate />\n' % (' ' * self.indent))
+
+            # relations
+            for ft in ('from', 'to'):
+                for rel in data.GetRelations(ft):
+                    if ft == 'from':
+                        aid = rel.GetTo().GetId()
+                    else:
+                        aid  = rel.GetFrom().GetId()
+                    self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' % \
+                                      (' ' * self.indent, ft, aid, rel.GetName()))
+                    self.indent += 4
+                    for point in rel.GetLineControlPoints()[1:-1]:
+                        self.fd.write('%s<point>\n' % (' ' * self.indent))
+                        self.indent += 4
+                        x, y = point.Get()
+                        self.fd.write('%s<x>%d</x>\n' % (' ' * self.indent, int(x)))
+                        self.fd.write('%s<y>%d</y>\n' % (' ' * self.indent, int(y)))
+                        self.indent -= 4
+                        self.fd.write('%s</point>\n' % (' ' * self.indent))
+                    self.indent -= 4
+                    self.fd.write('%s</relation>\n' % (' ' * self.indent))
+                
+            self.indent -= 4
+            self.fd.write('%s</data>\n' % (' ' * self.indent))
+
+    def _loop(self, loop):
+        """!Write loops"""
+        self.fd.write('%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' % \
+                          (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(),
+                           loop.GetWidth(), loop.GetHeight()))
+        text = loop.GetText()
+        self.indent += 4
+        if text:
+            self.fd.write('%s<condition>%s</condition>\n' %
+                          (' ' * self.indent, self._filterValue(text)))
+        for item in loop.GetItems():
+            self.fd.write('%s<item>%d</item>\n' %
+                          (' ' * self.indent, item.GetId()))
+        self.indent -= 4
+        self.fd.write('%s</loop>\n' % (' ' * self.indent))
+
+    def _condition(self, condition):
+        """!Write conditions"""
+        bbox = condition.GetBoundingBoxMin()
+        self.fd.write('%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' % \
+                          (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(),
+                           bbox[0], bbox[1]))
+        text = condition.GetText()
+        self.indent += 4
+        if text:
+            self.fd.write('%s<condition>%s</condition>\n' %
+                          (' ' * self.indent, self._filterValue(text)))
+        items = condition.GetItems()
+        for b in items.keys():
+            if len(items[b]) < 1:
+                continue
+            self.fd.write('%s<%s>\n' % (' ' * self.indent, b))
+            self.indent += 4
+            for item in items[b]:
+                self.fd.write('%s<item>%d</item>\n' %
+                              (' ' * self.indent, item.GetId()))
+            self.indent -= 4
+            self.fd.write('%s</%s>\n' % (' ' * self.indent, b))
+        
+        self.indent -= 4
+        self.fd.write('%s</if-else>\n' % (' ' * self.indent))
+        
+class PreferencesDialog(PreferencesBaseDialog):
+    """!User preferences dialog"""
+    def __init__(self, parent, settings = UserSettings,
+                 title = _("Modeler settings")):
+        
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        
+        # create notebook pages
+        self._createActionPage(self.notebook)
+        self._createDataPage(self.notebook)
+                
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+
+    def _createActionPage(self, notebook):
+        """!Create notebook page for action settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Action"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Validity"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Valid:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'valid')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        vColor.SetName('GetColour')
+        self.winId['modeler:action:color:valid'] = vColor.GetId()
+        
+        gridSizer.Add(item = vColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Invalid:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        iColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'invalid')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        iColor.SetName('GetColour')
+        self.winId['modeler:action:color:invalid'] = iColor.GetId()
+        
+        gridSizer.Add(item = iColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Running:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'running')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:action:color:running'] = rColor.GetId()
+        
+        gridSizer.Add(item = rColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Disabled:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'disabled')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:action:color:disabled'] = rColor.GetId()
+        
+        gridSizer.Add(item = rColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        
+        # size
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Shape size"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(0)
+
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                            min = 0, max = 500,
+                            initial = self.settings.Get(group='modeler', key='action', subkey=('size', 'width')))
+        width.SetName('GetValue')
+        self.winId['modeler:action:size:width'] = width.GetId()
+        
+        gridSizer.Add(item = width,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=_("Height:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        
+        height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                             min = 0, max = 500,
+                             initial = self.settings.Get(group='modeler', key='action', subkey=('size', 'height')))
+        height.SetName('GetValue')
+        self.winId['modeler:action:size:height'] = height.GetId()
+        
+        gridSizer.Add(item = height,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
+                
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createDataPage(self, notebook):
+        """!Create notebook page for data settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Data"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Type"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Raster:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'raster')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:data:color:raster'] = rColor.GetId()
+        
+        gridSizer.Add(item = rColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("3D raster:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        r3Color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                    colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'raster3d')),
+                                    size = globalvar.DIALOG_COLOR_SIZE)
+        r3Color.SetName('GetColour')
+        self.winId['modeler:data:color:raster3d'] = r3Color.GetId()
+        
+        gridSizer.Add(item = r3Color,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Vector:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                   colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'vector')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        vColor.SetName('GetColour')
+        self.winId['modeler:data:color:vector'] = vColor.GetId()
+        
+        gridSizer.Add(item = vColor,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+
+        # size
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Shape size"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                            min = 0, max = 500,
+                            initial = self.settings.Get(group='modeler', key='data', subkey=('size', 'width')))
+        width.SetName('GetValue')
+        self.winId['modeler:data:size:width'] = width.GetId()
+        
+        gridSizer.Add(item = width,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=_("Height:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        
+        height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                             min = 0, max = 500,
+                             initial = self.settings.Get(group='modeler', key='data', subkey=('size', 'height')))
+        height.SetName('GetValue')
+        self.winId['modeler:data:size:height'] = height.GetId()
+        
+        gridSizer.Add(item = height,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
+        
+        panel.SetSizer(border)
+        
+        return panel
+
+    def OnApply(self, event):
+        """!Button 'Apply' pressed"""
+        PreferencesBaseDialog.OnApply(self, event)
+        
+        self.parent.GetModel().Update()
+        self.parent.GetCanvas().Refresh()
+
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        PreferencesBaseDialog.OnSave(self, event)
+        
+        self.parent.GetModel().Update()
+        self.parent.GetCanvas().Refresh()
+
+class PropertiesDialog(wx.Dialog):
+    """!Model properties dialog
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _('Model properties'),
+                 size = (350, 400),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        wx.Dialog.__init__(self, parent, id, title, size = size,
+                           style = style)
+        
+        self.metaBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                    label=" %s " % _("Metadata"))
+        self.cmdBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                   label=" %s " % _("Commands"))
+        
+        self.name = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                size = (300, 25))
+        self.desc = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                style = wx.TE_MULTILINE,
+                                size = (300, 50))
+        self.author = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                size = (300, 25))
+        
+        # commands
+        self.overwrite = wx.CheckBox(parent = self, id=wx.ID_ANY,
+                                     label=_("Allow output files to overwrite existing files"))
+        self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
+        
+        # buttons
+        self.btnOk     = wx.Button(self, wx.ID_OK)
+        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+        self.btnOk.SetDefault()
+        
+        self.btnOk.SetToolTipString(_("Apply properties"))
+        self.btnOk.SetDefault()
+        self.btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+        
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        
+        self._layout()
+
+    def _layout(self):
+        metaSizer = wx.StaticBoxSizer(self.metaBox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        gridSizer.AddGrowableRow(1)
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label = _("Name:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 0))
+        gridSizer.Add(item = self.name,
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (0, 1))
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label = _("Description:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 0))
+        gridSizer.Add(item = self.desc,
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (1, 1))
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label = _("Author(s):")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2, 0))
+        gridSizer.Add(item = self.author,
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (2, 1))
+        metaSizer.Add(item = gridSizer)
+        
+        cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL)
+        cmdSizer.Add(item = self.overwrite,
+                     flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        btnStdSizer = wx.StdDialogButtonSizer()
+        btnStdSizer.AddButton(self.btnCancel)
+        btnStdSizer.AddButton(self.btnOk)
+        btnStdSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item=metaSizer, proportion=1,
+                      flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(item=cmdSizer, proportion=0,
+                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
+        mainSizer.Add(item=btnStdSizer, proportion=0,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+    def OnCloseWindow(self, event):
+        self.Hide()
+        
+    def GetValues(self):
+        """!Get values"""
+        return { 'name'        : self.name.GetValue(),
+                 'description' : self.desc.GetValue(),
+                 'author'      : self.author.GetValue(),
+                 'overwrite'   : self.overwrite.IsChecked() }
+    
+    def Init(self, prop):
+        """!Initialize dialog"""
+        self.name.SetValue(prop['name'])
+        self.desc.SetValue(prop['description'])
+        self.author.SetValue(prop['author'])
+        if prop.has_key('overwrite'):
+            self.overwrite.SetValue(prop['overwrite'])
+
+class ModelParamDialog(wx.Dialog):
+    def __init__(self, parent, params, id = wx.ID_ANY, title = _("Model parameters"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Model parameters dialog
+        """
+        self.parent = parent
+        self.params = params
+        self.tasks  = list() # list of tasks/pages
+        
+        wx.Dialog.__init__(self, parent = parent, id = id, title = title, style = style, **kwargs)
+        
+        if globalvar.hasAgw:
+            self.notebook = FN.FlatNotebook(self, id = wx.ID_ANY,
+                                            agwStyle = FN.FNB_FANCY_TABS |
+                                            FN.FNB_BOTTOM |
+                                            FN.FNB_NO_NAV_BUTTONS |
+                                            FN.FNB_NO_X_BUTTON)
+        else:
+            self.notebook = FN.FlatNotebook(self, id = wx.ID_ANY,
+                                            style = FN.FNB_FANCY_TABS |
+                                            FN.FNB_BOTTOM |
+                                            FN.FNB_NO_NAV_BUTTONS |
+                                            FN.FNB_NO_X_BUTTON)
+        
+        panel = self._createPages()
+        wx.CallAfter(self.notebook.SetSelection, 0)
+        
+        self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+        self.btnRun    = wx.Button(parent = self, id = wx.ID_OK,
+                                   label = _("&Run"))
+        self.btnRun.SetDefault()
+        
+        self._layout()
+        
+        size = self.GetBestSize()
+        self.SetMinSize(size)
+        self.SetSize((size.width, size.height +
+                      panel.constrained_size[1] -
+                      panel.panelMinHeight))
+                
+    def _layout(self):
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnRun)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.notebook, proportion = 1,
+                      flag = wx.EXPAND)
+        mainSizer.Add(item=btnSizer, proportion=0,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def _createPages(self):
+        """!Create for each parameterized module its own page"""
+        nameOrdered = [''] * len(self.params.keys())
+        for name, params in self.params.iteritems():
+            nameOrdered[params['idx']] =  name
+        for name in nameOrdered:
+            params = self.params[name]
+            panel = self._createPage(name, params)
+            self.notebook.AddPage(panel, text = name)
+        
+        return panel
+    
+    def _createPage(self, name, params):
+        """!Define notebook page"""
+        if name in globalvar.grassCmd['all']:
+            task = menuform.grassTask(name)
+        else:
+            task = menuform.grassTask()
+        task.flags  = params['flags']
+        task.params = params['params']
+        
+        panel = menuform.cmdPanel(parent = self, id = wx.ID_ANY, task = task)
+        self.tasks.append(task)
+        
+        return panel
+
+    def GetErrors(self):
+        """!Check for errors, get list of messages"""
+        errList = list()
+        for task in self.tasks:
+            errList += task.getCmdError()
+        
+        return errList
+    
+class ModelListCtrl(wx.ListCtrl,
+                    listmix.ListCtrlAutoWidthMixin,
+                    listmix.TextEditMixin,
+                    listmix.ColumnSorterMixin):
+    def __init__(self, parent, columns, id = wx.ID_ANY,
+                 style = wx.LC_REPORT | wx.BORDER_NONE |
+                 wx.LC_SORT_ASCENDING |wx.LC_HRULES |
+                 wx.LC_VRULES, **kwargs):
+        """!List of model variables"""
+        self.parent = parent
+        self.columns = columns
+        self.shape = None
+        try:
+            self.frame  = parent.parent
+        except AttributeError:
+            self.frame = None
+        
+        wx.ListCtrl.__init__(self, parent, id = id, style = style, **kwargs)
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.TextEditMixin.__init__(self)
+        listmix.ColumnSorterMixin.__init__(self, 4)
+        
+        i = 0
+        for col in columns:
+            self.InsertColumn(i, col)
+            self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
+            i += 1
+        
+        self.itemDataMap = {} # requested by sorter
+        self.itemCount   = 0
+        
+        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit)
+        self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit)
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
+        self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
+        self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)            #wxGTK
+                
+    def OnBeginEdit(self, event):
+        """!Editing of item started"""
+        event.Allow()
+
+    def OnEndEdit(self, event):
+        """!Finish editing of item"""
+        pass
+    
+    def OnColClick(self, event):
+        """!Click on column header (order by)"""
+        event.Skip()
+
+class VariablePanel(wx.Panel):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 **kwargs):
+        """!Manage model variables panel
+        """
+        self.parent = parent
+        
+        wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+        
+        self.listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                    label=" %s " % _("List of variables - right-click to delete"))
+        
+        self.list = VariableListCtrl(parent = self,
+                                     columns = [_("Name"), _("Data type"),
+                                                _("Default value"), _("Description")])
+        
+        # add new category
+        self.addBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                   label = " %s " % _("Add new variable"))
+        self.name = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+        wx.CallAfter(self.name.SetFocus)
+        self.type = wx.Choice(parent = self, id = wx.ID_ANY,
+                              choices = [_("integer"),
+                                         _("float"),
+                                         _("string"),
+                                         _("raster"),
+                                         _("vector")])
+        self.value = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+        self.desc = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+        
+        # buttons
+        self.btnAdd = wx.Button(parent = self, id = wx.ID_ADD)
+        self.btnAdd.SetToolTipString(_("Add new variable to the model"))
+        self.btnAdd.Enable(False)
+        
+        # bindings
+        self.name.Bind(wx.EVT_TEXT, self.OnText)
+        self.value.Bind(wx.EVT_TEXT, self.OnText)
+        self.desc.Bind(wx.EVT_TEXT, self.OnText)
+        self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAdd)
+        
+        self._layout()
+
+    def _layout(self):
+        """!Layout dialog"""
+        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+        listSizer.Add(item = self.list, proportion = 1,
+                      flag = wx.EXPAND)
+        
+        addSizer = wx.StaticBoxSizer(self.addBox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridSizer.AddGrowableCol(1)
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                           label = "%s:" % _("Name")),
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 0))
+        gridSizer.Add(item = self.name,
+                      pos = (0, 1),
+                      flag = wx.EXPAND)
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                           label = "%s:" % _("Data type")),
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 2))
+        gridSizer.Add(item = self.type,
+                      pos = (0, 3))
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                           label = "%s:" % _("Default value")),
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 0))
+        gridSizer.Add(item = self.value,
+                      pos = (1, 1), span = (1, 3),
+                      flag = wx.EXPAND)
+        gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                           label = "%s:" % _("Description")),
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2, 0))
+        gridSizer.Add(item = self.desc,
+                      pos = (2, 1), span = (1, 3),
+                      flag = wx.EXPAND)
+        addSizer.Add(item = gridSizer,
+                     flag = wx.EXPAND)
+        addSizer.Add(item = self.btnAdd, proportion = 0,
+                     flag = wx.TOP | wx.ALIGN_RIGHT, border = 5)
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = listSizer, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        mainSizer.Add(item = addSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALIGN_CENTER |
+                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def OnText(self, event):
+        """!Text entered"""
+        if self.name.GetValue():
+            self.btnAdd.Enable()
+        else:
+            self.btnAdd.Enable(False)
+    
+    def OnAdd(self, event):
+        """!Add new variable to the list"""
+        msg = self.list.Append(self.name.GetValue(),
+                               self.type.GetStringSelection(),
+                               self.value.GetValue(),
+                               self.desc.GetValue())
+        self.name.SetValue('')
+        self.name.SetFocus()
+        
+        if msg:
+            GError(parent = self,
+                   message = msg)
+        else:
+            self.type.SetSelection(0)
+            self.value.SetValue('')
+            self.desc.SetValue('')
+            self.UpdateModelVariables()
+        
+    def UpdateModelVariables(self):
+        """!Update model variables"""
+        variables = dict()
+        for values in self.list.GetData().itervalues():
+            name = values[0]
+            variables[name] = { 'type' : str(values[1]) }
+            if values[2]:
+                variables[name]['value'] = values[2]
+            if values[3]:
+                variables[name]['description'] = values[3]
+        
+        self.parent.GetModel().SetVariables(variables)
+        self.parent.ModelChanged()
+
+    def Update(self):
+        """!Reload list of variables"""
+        self.list.OnReload(None)
+        
+class VariableListCtrl(ModelListCtrl):
+    def __init__(self, parent, columns, **kwargs):
+        """!List of model variables"""
+        ModelListCtrl.__init__(self, parent, columns, **kwargs)
+        
+    def GetListCtrl(self):
+        """!Used by ColumnSorterMixin"""
+        return self
+    
+    def GetData(self):
+        """!Get list data"""
+        return self.itemDataMap
+    
+    def Populate(self, data):
+        """!Populate the list"""
+        self.itemDataMap = dict()
+        i = 0
+        for name, values in data.iteritems():
+            self.itemDataMap[i] = [name, values['type'],
+                                   values.get('value', ''),
+                                   values.get('description', '')]
+            i += 1
+        
+        self.itemCount = len(self.itemDataMap.keys())
+        self.DeleteAllItems()
+        i = 0
+        for name, vtype, value, desc in self.itemDataMap.itervalues():
+            index = self.InsertStringItem(sys.maxint, name)
+            self.SetStringItem(index, 0, name)
+            self.SetStringItem(index, 1, vtype)
+            self.SetStringItem(index, 2, value)
+            self.SetStringItem(index, 3, desc)
+            self.SetItemData(index, i)
+            i += 1
+        
+    def Append(self, name, vtype, value, desc):
+        """!Append new item to the list
+
+        @return None on success
+        @return error string
+        """
+        for iname, ivtype, ivalue, idesc in self.itemDataMap.itervalues():
+            if iname == name:
+                return _("Variable <%s> already exists in the model. "
+                         "Adding variable failed.") % name
+        
+        index = self.InsertStringItem(sys.maxint, name)
+        self.SetStringItem(index, 0, name)
+        self.SetStringItem(index, 1, vtype)
+        self.SetStringItem(index, 2, value)
+        self.SetStringItem(index, 3, desc)
+        self.SetItemData(index, self.itemCount)
+        
+        self.itemDataMap[self.itemCount] = [name, vtype, value, desc]
+        self.itemCount += 1
+        
+        return None
+
+    def OnRemove(self, event):
+        """!Remove selected variable(s) from the model"""
+        item = self.GetFirstSelected()
+        while item != -1:
+            self.DeleteItem(item)
+            del self.itemDataMap[item]
+            item = self.GetFirstSelected()
+        self.parent.UpdateModelVariables()
+        
+        event.Skip()
+        
+    def OnRemoveAll(self, event):
+        """!Remove all variable(s) from the model"""
+        dlg = wx.MessageBox(parent=self,
+                            message=_("Do you want to delete all variables from "
+                                      "the model?"),
+                            caption=_("Delete variables"),
+                            style=wx.YES_NO | wx.CENTRE)
+        if dlg != wx.YES:
+            return
+        
+        self.DeleteAllItems()
+        self.itemDataMap = dict()
+        
+        self.parent.UpdateModelVariables()
+        
+    def OnEndEdit(self, event):
+        """!Finish editing of item"""
+        itemIndex = event.GetIndex()
+        columnIndex = event.GetColumn()
+        nameOld = self.GetItem(itemIndex, 0).GetText()
+
+        if columnIndex == 0: # TODO
+            event.Veto()
+        
+        self.itemDataMap[itemIndex][columnIndex] = event.GetText()
+        
+        self.parent.UpdateModelVariables()
+
+    def OnReload(self, event):
+        """!Reload list of variables"""
+        self.Populate(self.parent.parent.GetModel().GetVariables())
+
+    def OnRightUp(self, event):
+        """!Mouse right button up"""
+        if not hasattr(self, "popupID1"):
+            self.popupID1 = wx.NewId()
+            self.popupID2 = wx.NewId()
+            self.popupID3 = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnRemove,    id = self.popupID1)
+            self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2)
+            self.Bind(wx.EVT_MENU, self.OnReload,    id = self.popupID3)
+        
+        # generate popup-menu
+        menu = wx.Menu()
+        menu.Append(self.popupID1, _("Delete selected"))
+        menu.Append(self.popupID2, _("Delete all"))
+        if self.GetFirstSelected() == -1:
+            menu.Enable(self.popupID1, False)
+            menu.Enable(self.popupID2, False)
+        
+        menu.AppendSeparator()
+        menu.Append(self.popupID3, _("Reload"))
+        
+        self.PopupMenu(menu)
+        menu.Destroy()
+        
+class ModelItem(ModelObject):
+    def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []):
+        """!Abstract class for loops and conditions"""
+        ModelObject.__init__(self, id)
+        self.parent  = parent
+        self.text    = text
+        self.items   = items  # list of items in the loop
+        
+    def GetText(self):
+        """!Get loop text"""
+        return self.text
+
+    def GetItems(self):
+        """!Get items (id)"""
+        return self.items
+
+    def SetId(self, id):
+        """!Set loop id"""
+        self.id = id
+
+    def SetText(self, cond):
+        """!Set loop text (condition)"""
+        self.text = cond
+        self.ClearText()
+        self.AddText('(' + str(self.id) + ') ' + self.text)
+
+    def GetLog(self):
+        """!Get log info"""
+        if self.text:
+            return _("Condition: ") + self.text
+        else:
+            return _("Condition: not defined")
+
+    def AddRelation(self, rel):
+        """!Record relation"""
+        self.rels.append(rel)
+        
+    def Clear(self):
+        """!Clear object, remove rels"""
+        self.rels = list()
+        
+class ModelItemDialog(wx.Dialog):
+    """!Abstract item properties dialog"""
+    def __init__(self, parent, shape, title, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        self.parent = parent
+        self.shape = shape
+        
+        wx.Dialog.__init__(self, parent, id, title = title, style = style, **kwargs)
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.condBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label=" %s " % _("Condition"))
+        self.condText = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+                                    value = shape.GetText())
+        
+        self.itemList = ItemCheckListCtrl(parent = self.panel,
+                                          window = self,
+                                          columns = [_("ID"), _("Name"),
+                                                     _("Command")],
+                                          shape = shape)
+        self.itemList.Populate(self.parent.GetModel().GetItems())
+        
+        self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+        self.btnOk     = wx.Button(parent = self.panel, id = wx.ID_OK)
+        self.btnOk.SetDefault()
+        
+    def _layout(self):
+        """!Do layout (virtual method)"""
+        pass
+    
+    def GetCondition(self):
+        """!Get loop condition"""
+        return self.condText.GetValue()
+    
+class ModelLoop(ModelItem, ogl.RectangleShape):
+    def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []):
+        """!Defines a loop"""
+        ModelItem.__init__(self, parent, x, y, id, width, height, text, items)
+        
+        if not width:
+            width = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'width'))
+        if not height:
+            height = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'height'))
+        
+        if self.parent.GetCanvas():
+            ogl.RectangleShape.__init__(self, width, height)
+            
+            self.SetCanvas(self.parent)
+            self.SetX(x)
+            self.SetY(y)
+            self.SetPen(wx.BLACK_PEN)
+            self.SetCornerRadius(100)
+            if text:
+                self.AddText('(' + str(self.id) + ') ' + text)
+            else:
+                self.AddText('(' + str(self.id) + ')')
+        
+    def GetName(self):
+        """!Get name"""
+        return _("loop")
+    
+    def SetItems(self, items):
+        """!Set items (id)"""
+        self.items = items
+
+class ModelLoopDialog(ModelItemDialog):
+    """!Loop properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Loop properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        ModelItemDialog.__init__(self, parent, shape, title,
+                                 style = style, **kwargs)
+        
+        self.listBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label=" %s " % _("List of items in loop"))
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        self.SetSize((500, 400))
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        condSizer = wx.StaticBoxSizer(self.condBox, wx.VERTICAL)
+        condSizer.Add(item = self.condText, proportion = 1,
+                      flag = wx.EXPAND)
+        
+        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+        listSizer.Add(item = self.itemList, proportion = 1,
+                      flag = wx.EXPAND)
+        
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+
+        sizer.Add(item = condSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL, border = 3)
+        sizer.Add(item = listSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        sizer.Add(item = btnSizer, proportion=0,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+        
+    def GetItems(self):
+        """!Get list of selected actions"""
+        return self.itemList.GetItems()
+
+class ItemPanel(wx.Panel):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 **kwargs):
+        """!Manage model items
+        """
+        self.parent = parent
+        
+        wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+        
+        self.listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                    label=" %s " % _("List of items - right-click to delete"))
+        
+        self.list = ItemListCtrl(parent = self,
+                                 columns = [_("ID"), _("Name"), _("In block"),
+                                            _("Command / Condition")])
+        
+        self._layout()
+
+    def _layout(self):
+        """!Layout dialog"""
+        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+        listSizer.Add(item = self.list, proportion = 1,
+                      flag = wx.EXPAND)
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = listSizer, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def Update(self):
+        """!Reload list of variables"""
+        self.list.OnReload(None)
+        
+class ItemListCtrl(ModelListCtrl):
+    def __init__(self, parent, columns, disablePopup = False, **kwargs):
+        """!List of model actions"""
+        self.disablePopup = disablePopup
+                
+        ModelListCtrl.__init__(self, parent, columns, **kwargs)
+        self.SetColumnWidth(1, 100)
+        self.SetColumnWidth(2, 65)
+        
+    def GetListCtrl(self):
+        """!Used by ColumnSorterMixin"""
+        return self
+    
+    def GetData(self):
+        """!Get list data"""
+        return self.itemDataMap
+    
+    def Populate(self, data):
+        """!Populate the list"""
+        self.itemDataMap = dict()
+        if self.shape:
+            if isinstance(self.shape, ModelCondition):
+                if self.GetName() == 'ElseBlockList':
+                    shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['else'])
+                else:
+                    shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['if'])
+            else:
+                shapeItems = map(lambda x: x.GetId(), self.shape.GetItems())
+        else:
+            shapeItems = list()
+        i = 0
+        if len(self.columns) == 3: # ItemCheckList
+            checked = list()
+        for action in data:
+            if len(self.columns) == 3:
+                self.itemDataMap[i] = [str(action.GetId()),
+                                       action.GetName(),
+                                       action.GetLog()]
+                aId = action.GetBlockId()
+                if action.GetId() in shapeItems:
+                    checked.append(aId)
+                else:
+                    checked.append(None)
+            else:
+                bId = action.GetBlockId()
+                if not bId:
+                    bId = ''
+                self.itemDataMap[i] = [str(action.GetId()),
+                                       action.GetName(),
+                                       ','.join(map(str, bId)),
+                                       action.GetLog()]
+            
+            i += 1
+        
+        self.itemCount = len(self.itemDataMap.keys())
+        self.DeleteAllItems()
+        i = 0
+        if len(self.columns) == 3:
+            for aid, name, desc in self.itemDataMap.itervalues():
+                index = self.InsertStringItem(sys.maxint, aid)
+                self.SetStringItem(index, 0, aid)
+                self.SetStringItem(index, 1, name)
+                self.SetStringItem(index, 2, desc)
+                self.SetItemData(index, i)
+                if checked[i]:
+                    self.CheckItem(index, True)
+                i += 1
+        else:
+            for aid, name, inloop, desc in self.itemDataMap.itervalues():
+                index = self.InsertStringItem(sys.maxint, aid)
+                self.SetStringItem(index, 0, aid)
+                self.SetStringItem(index, 1, name)
+                self.SetStringItem(index, 2, inloop)
+                self.SetStringItem(index, 3, desc)
+                self.SetItemData(index, i)
+                i += 1
+                
+    def OnRemove(self, event):
+        """!Remove selected action(s) from the model"""
+        model = self.frame.GetModel()
+        canvas = self.frame.GetCanvas()
+        
+        item = self.GetFirstSelected()
+        while item != -1:
+            self.DeleteItem(item)
+            del self.itemDataMap[item]
+            
+            aId = self.GetItem(item, 0).GetText()
+            action = model.GetItem(int(aId))
+            if not action:
+                item = self.GetFirstSelected()
+                continue
+            
+            model.RemoveItem(action)
+            canvas.GetDiagram().RemoveShape(action)
+            self.frame.ModelChanged()
+            
+            item = self.GetFirstSelected()
+        
+        canvas.Refresh()
+        
+        event.Skip()
+    
+    def OnRemoveAll(self, event):
+        """!Remove all variable(s) from the model"""
+        deleteDialog = wx.MessageBox(parent=self,
+                                     message=_("Selected data records (%d) will permanently deleted "
+                                               "from table. Do you want to delete them?") % \
+                                         (len(self.listOfSQLStatements)),
+                                     caption=_("Delete records"),
+                                     style=wx.YES_NO | wx.CENTRE)
+        if deleteDialog != wx.YES:
+            return False
+        
+        self.DeleteAllItems()
+        self.itemDataMap = dict()
+
+        self.parent.UpdateModelVariables()
+
+    def OnEndEdit(self, event):
+        """!Finish editing of item"""
+        itemIndex = event.GetIndex()
+        columnIndex = event.GetColumn()
+        
+        self.itemDataMap[itemIndex][columnIndex] = event.GetText()
+        
+        aId = int(self.GetItem(itemIndex, 0).GetText())
+        action = self.frame.GetModel().GetItem(aId)
+        if not action:
+            event.Veto()
+        if columnIndex == 0:
+            action.SetId(int(event.GetText()))
+        
+        self.frame.ModelChanged()
+
+    def OnReload(self, event = None):
+        """!Reload list of actions"""
+        self.Populate(self.frame.GetModel().GetItems())
+
+    def OnRightUp(self, event):
+        """!Mouse right button up"""
+        if self.disablePopup:
+            return
+        
+        if not hasattr(self, "popupID1"):
+            self.popupID1 = wx.NewId()
+            self.popupID2 = wx.NewId()
+            self.popupID3 = wx.NewId()
+            self.popupID4 = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnRemove,    id = self.popupID1)
+            self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2)
+            self.Bind(wx.EVT_MENU, self.OnReload,    id = self.popupID3)
+            self.Bind(wx.EVT_MENU, self.OnNormalize, id = self.popupID4)
+
+        # generate popup-menu
+        menu = wx.Menu()
+        menu.Append(self.popupID1, _("Delete selected"))
+        menu.Append(self.popupID2, _("Delete all"))
+        if self.GetFirstSelected() == -1:
+            menu.Enable(self.popupID1, False)
+            menu.Enable(self.popupID2, False)
+        
+        menu.AppendSeparator()
+        menu.Append(self.popupID4, _("Normalize"))
+        menu.Append(self.popupID3, _("Reload"))
+        
+        self.PopupMenu(menu)
+        menu.Destroy()
+    
+    def OnNormalize(self, event):
+        """!Update id of actions"""
+        model = self.frame.GetModel()
+        
+        aId = 1
+        for item in model.GetItems():
+            item.SetId(aId)
+            aId += 1
+        
+        self.OnReload(None)
+        self.frame.GetCanvas().Refresh()
+        self.frame.ModelChanged()
+
+class ItemCheckListCtrl(ItemListCtrl, listmix.CheckListCtrlMixin):
+    def __init__(self, parent, shape, columns, window = None, **kwargs):
+        self.parent = parent
+        self.window = window
+        
+        ItemListCtrl.__init__(self, parent, columns, disablePopup = True, **kwargs)
+        listmix.CheckListCtrlMixin.__init__(self)
+        self.SetColumnWidth(0, 50)
+        
+        self.shape  = shape
+        
+    def OnBeginEdit(self, event):
+        """!Disable editing"""
+        event.Veto()
+        
+    def OnCheckItem(self, index, flag):
+        """!Item checked/unchecked"""
+        name = self.GetName()
+        if name == 'IfBlockList' and self.window:
+            self.window.OnCheckItemIf(index, flag)
+        elif name == 'ElseBlockList' and self.window:
+            self.window.OnCheckItemElse(index, flag)
+        
+    def GetItems(self):
+        """!Get list of selected actions"""
+        ids = { 'checked'   : list(),
+                'unchecked' : list() }
+        for i in range(self.GetItemCount()):
+            iId = int(self.GetItem(i, 0).GetText())
+            if self.IsChecked(i):
+                ids['checked'].append(iId)
+            else:
+                ids['unchecked'].append(iId)
+            
+        return ids
+
+    def CheckItemById(self, aId, flag):
+        """!Check/uncheck given item by id"""
+        for i in range(self.GetItemCount()):
+            iId = int(self.GetItem(i, 0).GetText())
+            if iId == aId:
+                self.CheckItem(i, flag)
+                break
+        
+class ModelCondition(ModelItem, ogl.PolygonShape):
+    def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '',
+                 items = { 'if' : [], 'else' : [] }):
+        """!Defines a if-else condition"""
+        ModelItem.__init__(self, parent, x, y, id, width, height, text, items)
+        
+        if not width:
+            self.width = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'width'))
+        else:
+            self.width = width
+        if not height:
+            self.height = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'height'))
+        else:
+            self.height = height
+        
+        if self.parent.GetCanvas():
+            ogl.PolygonShape.__init__(self)
+            
+            points = [(0, - self.height / 2),
+                      (self.width / 2, 0),
+                      (0, self.height / 2),
+                      (- self.width / 2, 0)]
+            self.Create(points)
+            
+            self.SetCanvas(self.parent)
+            self.SetX(x)
+            self.SetY(y)
+            self.SetPen(wx.BLACK_PEN)
+            if text:
+                self.AddText('(' + str(self.id) + ') ' + text)
+            else:
+                self.AddText('(' + str(self.id) + ')')
+
+    def GetName(self):
+        """!Get name"""
+        return _("if-else")
+
+    def GetWidth(self):
+        """!Get object width"""
+        return self.width
+
+    def GetHeight(self):
+        """!Get object height"""
+        return self.height
+
+    def SetItems(self, items, branch = 'if'):
+        """!Set items (id)
+
+        @param items list of items
+        @param branch 'if' / 'else'
+        """
+        if branch in ['if', 'else']:
+            self.items[branch] = items
+        
+class ModelConditionDialog(ModelItemDialog):
+    """!Condition properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("If-else properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        ModelItemDialog.__init__(self, parent, shape, title,
+                                 style = style, **kwargs)
+        
+        self.listBoxIf = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                      label=" %s " % _("List of items in 'if' block"))
+        self.itemListIf = self.itemList
+        self.itemListIf.SetName('IfBlockList')
+        
+        self.listBoxElse = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                        label=" %s " % _("List of items in 'else' block"))
+        self.itemListElse = ItemCheckListCtrl(parent = self.panel,
+                                              window = self,
+                                              columns = [_("ID"), _("Name"),
+                                                         _("Command")],
+                                              shape = shape)
+        self.itemListElse.SetName('ElseBlockList')
+        self.itemListElse.Populate(self.parent.GetModel().GetItems())
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        self.SetSize((500, 400))
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        condSizer = wx.StaticBoxSizer(self.condBox, wx.VERTICAL)
+        condSizer.Add(item = self.condText, proportion = 1,
+                      flag = wx.EXPAND)
+        
+        listIfSizer = wx.StaticBoxSizer(self.listBoxIf, wx.VERTICAL)
+        listIfSizer.Add(item = self.itemListIf, proportion = 1,
+                        flag = wx.EXPAND)
+        listElseSizer = wx.StaticBoxSizer(self.listBoxElse, wx.VERTICAL)
+        listElseSizer.Add(item = self.itemListElse, proportion = 1,
+                          flag = wx.EXPAND)
+        
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+
+        sizer.Add(item = condSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL, border = 3)
+        sizer.Add(item = listIfSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        sizer.Add(item = listElseSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        sizer.Add(item = btnSizer, proportion=0,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def OnCheckItemIf(self, index, flag):
+        """!Item in if-block checked/unchecked"""
+        if flag is False:
+            return
+        
+        aId = int(self.itemListIf.GetItem(index, 0).GetText())
+        if aId in self.itemListElse.GetItems()['checked']:
+            self.itemListElse.CheckItemById(aId, False)
+            
+    def OnCheckItemElse(self, index, flag):
+        """!Item in else-block checked/unchecked"""
+        if flag is False:
+            return
+        
+        aId = int(self.itemListElse.GetItem(index, 0).GetText())
+        if aId in self.itemListIf.GetItems()['checked']:
+            self.itemListIf.CheckItemById(aId, False)
+        
+    def GetItems(self):
+        """!Get items"""
+        return { 'if'   : self.itemListIf.GetItems(),
+                 'else' : self.itemListElse.GetItems() }
+
+class WritePythonFile:
+    def __init__(self, fd, model):
+        """!Class for exporting model to Python script
+
+        @param fd file desciptor
+        """
+        self.fd     = fd
+        self.model  = model
+        self.indent = 4
+
+        self._writePython()
+        
+    def _writePython(self):
+        """!Write model to file"""
+        properties = self.model.GetProperties()
+        
+        self.fd.write(
+r"""#!/usr/bin/env python
+#
+############################################################################
+#
+# MODULE:       %s
+#
+# AUTHOR(S):	%s
+#               
+# PURPOSE:      %s
+#
+# DATE:         %s
+#
+#############################################################################
+""" % (properties['name'],
+       properties['author'],
+       properties['description'],
+       time.asctime()))
+        
+        self.fd.write(
+r"""
+import sys
+import os
+import atexit
+
+import grass.script as grass
+""")
+        
+        # cleanup()
+        rast, vect, rast3d, msg = self.model.GetIntermediateData()
+        self.fd.write(
+r"""
+def cleanup():
+""")
+        if rast:
+            self.fd.write(
+r"""    grass.run_command('g.remove',
+                      rast=%s)
+""" % ','.join(map(lambda x: "'" + x + "'", rast)))
+        if vect:
+            self.fd.write(
+r"""    grass.run_command('g.remove',
+                      vect = %s)
+""" % ','.join(map(lambda x: "'" + x + "'", vect)))
+        if rast3d:
+            self.fd.write(
+r"""    grass.run_command('g.remove',
+                      rast3d = %s)
+""" % ','.join(map(lambda x: "'" + x + "'", rast3d)))
+        if not rast and not vect and not rast3d:
+            self.fd.write('    pass\n')
+        
+        self.fd.write("\ndef main():\n")
+        for item in self.model.GetItems():
+            self._writePythonItem(item)
+        
+        self.fd.write("\n    return 0\n")
+        
+        self.fd.write(
+r"""
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    atexit.register(cleanup)
+    sys.exit(main())
+""")
+        
+    def _writePythonItem(self, item, ignoreBlock = True):
+        """!Write model object to Python file"""
+        if isinstance(item, ModelAction):
+            if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions
+                return
+            self._writePythonAction(item)
+        elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition):
+            # substitute condition
+            variables = self.model.GetVariables()
+            cond = item.GetText()
+            for variable in variables:
+                pattern= re.compile('%' + variable)
+                if pattern.search(cond):
+                    value = variables[variable].get('value', '')
+                    if variables[variable].get('type', 'string') == 'string':
+                        value = '"' + value + '"'
+                    cond = pattern.sub(value, cond)
+            if isinstance(item, ModelLoop):
+                self.fd.write('%sfor %s:\n' % (' ' * self.indent, cond))
+                self.indent += 4
+                for action in item.GetItems():
+                    self._writePythonItem(action, ignoreBlock = False)
+                self.indent -= 4
+            else: # ModelCondition
+                self.fd.write('%sif %s:\n' % (' ' * self.indent, cond))
+                self.indent += 4
+                condItems = item.GetItems()
+                for action in condItems['if']:
+                    self._writePythonItem(action, ignoreBlock = False)
+                if condItems['else']:
+                    self.indent -= 4
+                    self.fd.write('%selse:\n' % (' ' * self.indent))
+                    self.indent += 4
+                    for action in condItems['else']:
+                        self._writePythonItem(action, ignoreBlock = False)
+                self.indent += 4
+        
+    def _writePythonAction(self, item):
+        """!Write model action to Python file"""
+        task = menuform.GUI().ParseCommand(cmd = item.GetLog(string = False),
+                                           show = None)
+        opts = task.get_options()
+        flags = ''
+        params = list()
+        strcmd = "%sgrass.run_command(" % (' ' * self.indent)
+        cmdIndent = len(strcmd)
+        for f in opts['flags']:
+            if f.get('value', False) == True:
+                name = f.get('name', '')
+                if len(name) > 1:
+                    params.append('%s = True' % name)
+                else:
+                    flags += name
+            
+        for p in opts['params']:
+            name = p.get('name', None)
+            value = p.get('value', None)
+            if name and value:
+                ptype = p.get('type', 'string')
+                if ptype == 'string':
+                    params.append('%s = "%s"' % (name, value))
+                else:
+                    params.append("%s = %s" % (name, value))
+        
+        self.fd.write(strcmd + '"%s"' % task.get_name())
+        if flags:
+            self.fd.write(",\n%sflags = '%s'" % (' ' * cmdIndent, flags))
+        if len(params) > 0:
+            self.fd.write(",\n")
+            for opt in params[:-1]:
+                self.fd.write("%s%s,\n" % (' ' * cmdIndent, opt))
+            self.fd.write("%s%s)\n" % (' ' * cmdIndent, params[-1]))
+        else:
+            self.fd.write(")\n")
+
+        
+def main():
+    app = wx.PySimpleApp()
+    wx.InitAllImageHandlers()
+    frame = ModelFrame(parent = None)
+    if len(sys.argv) > 1:
+        frame.LoadModelFile(sys.argv[1])
+    frame.Show()
+    
+    app.MainLoop()
+    
+if __name__ == "__main__":
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gmodeler.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/goutput.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package goutput
 
 @brief Command output log widget
@@ -9,7 +9,7 @@
  - GMStdout
  - GMStderr
 
-(C) 2007-2008 by the GRASS Development Team
+(C) 2007-2010 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.
@@ -29,11 +29,18 @@
 import wx.stc
 from wx.lib.newevent import NewEvent
 
+import grass.script as grass
+
 import globalvar
 import gcmd
 import utils
-from debug import Debug as Debug
+import preferences
+import menuform
+import prompt
+
+from debug       import Debug
 from preferences import globalSettings as UserSettings
+from ghelp       import SearchModuleWindow
 
 wxCmdOutput,   EVT_CMD_OUTPUT   = NewEvent()
 wxCmdProgress, EVT_CMD_PROGRESS = NewEvent()
@@ -42,12 +49,12 @@
 wxCmdAbort,    EVT_CMD_ABORT    = NewEvent()
 
 def GrassCmd(cmd, stdout, stderr):
-    """Return GRASS command thread"""
+    """!Return GRASS command thread"""
     return gcmd.CommandThread(cmd,
                               stdout=stdout, stderr=stderr)
 
 class CmdThread(threading.Thread):
-    """Thread for GRASS commands"""
+    """!Thread for GRASS commands"""
     requestId = 0
     def __init__(self, parent, requestQ, resultQ, **kwds):
         threading.Thread.__init__(self, **kwds)
@@ -55,6 +62,7 @@
         self.setDaemon(True)
 
         self.parent = parent # GMConsole
+        self._want_abort_all = False
         
         self.requestQ = requestQ
         self.resultQ = resultQ
@@ -69,7 +77,12 @@
         
         return CmdThread.requestId
 
+    def SetId(self, id):
+        """!Set starting id"""
+        CmdThread.requestId = id
+        
     def run(self):
+        os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
         while True:
             requestId, callable, onDone, args, kwds = self.requestQ.get()
             
@@ -77,11 +90,15 @@
             event = wxCmdRun(cmd=args[0],
                              pid=requestId)
             wx.PostEvent(self.parent, event)
-
+            
             time.sleep(.1)
             
             self.requestCmd = callable(*args, **kwds)
-
+            if self._want_abort_all:
+                self.requestCmd.abort()
+                if self.requestQ.empty():
+                    self._want_abort_all = False
+            
             self.resultQ.put((requestId, self.requestCmd.run()))
 
             try:
@@ -96,36 +113,60 @@
             
             time.sleep(.1)
 
-            event = wxCmdDone(aborted = aborted,
+            # set default color table for raster data
+            if UserSettings.Get(group='cmd', key='rasterColorTable', subkey='enabled') and \
+                    args[0][0][:2] == 'r.':
+                moduleInterface = menuform.GUI().ParseCommand(args[0], show = None)
+                outputParam = moduleInterface.get_param(value = 'output', raiseError = False)
+                colorTable = UserSettings.Get(group='cmd', key='rasterColorTable', subkey='selection')
+                if outputParam and outputParam['prompt'] == 'raster':
+                    argsColor = list(args)
+                    argsColor[0] = [ 'r.colors',
+                                     'map=%s' % outputParam['value'],
+                                     'color=%s' % colorTable ]
+                    self.requestCmdColor = callable(*argsColor, **kwds)
+                    self.resultQ.put((requestId, self.requestCmdColor.run()))
+            
+            event = wxCmdDone(cmd = args[0],
+                              aborted = aborted,
                               returncode = returncode,
                               time = requestTime,
                               pid = requestId,
                               onDone = onDone)
-
+            
+            # send event
             wx.PostEvent(self.parent, event)
-
-    def abort(self):
+                
+    def abort(self, abortall = True):
+        """!Abort command(s)"""
+        if abortall:
+            self._want_abort_all = True
         self.requestCmd.abort()
-    
-class GMConsole(wx.Panel):
+        if self.requestQ.empty():
+            self._want_abort_all = False
+        
+class GMConsole(wx.SplitterWindow):
+    """!Create and manage output console for commands run by GUI.
     """
-    Create and manage output console for commands entered on the
-    GIS Manager command line.
-    """
     def __init__(self, parent, id=wx.ID_ANY, margin=False, pageid=0,
                  notebook = None,
-                 pos=wx.DefaultPosition, size=wx.DefaultSize,
-                 style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE):
-        wx.Panel.__init__(self, parent, id, pos, size, style)
+                 style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
+                 **kwargs):
+        wx.SplitterWindow.__init__(self, parent, id, style = style, *kwargs)
         self.SetName("GMConsole")
         
+        self.panelOutput = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.panelPrompt = wx.Panel(parent = self, id = wx.ID_ANY)
+        
         # initialize variables
-        self.Map             = None
-        self.parent          = parent # GMFrame | CmdPanel
+        self.parent          = parent # GMFrame | CmdPanel | ?
         if notebook:
-            self.parent.notebook = notebook
+            self._notebook = notebook
+        else:
+            self._notebook = self.parent.notebook
         self.lineWidth       = 80
         self.pageid          = pageid
+                        
         # remember position of line begining (used for '\r')
         self.linePos         = -1
         
@@ -134,11 +175,11 @@
         #
         self.requestQ = Queue.Queue()
         self.resultQ = Queue.Queue()
-
+        
         #
         # progress bar
         #
-        self.console_progressbar = wx.Gauge(parent=self, id=wx.ID_ANY,
+        self.console_progressbar = wx.Gauge(parent=self.panelOutput, id=wx.ID_ANY,
                                             range=100, pos=(110, 50), size=(-1, 25),
                                             style=wx.GA_HORIZONTAL)
         self.console_progressbar.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
@@ -146,7 +187,7 @@
         #
         # text control for command output
         #
-        self.cmd_output = GMStc(parent=self, id=wx.ID_ANY, margin=margin,
+        self.cmd_output = GMStc(parent=self.panelOutput, id=wx.ID_ANY, margin=margin,
                                 wrap=None) 
         self.cmd_output_timer = wx.Timer(self.cmd_output, id=wx.ID_ANY)
         self.cmd_output.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
@@ -154,61 +195,152 @@
         self.Bind(EVT_CMD_RUN, self.OnCmdRun)
         self.Bind(EVT_CMD_DONE, self.OnCmdDone)
         
+        # search & command prompt
+        self.cmd_prompt = prompt.GPromptSTC(parent = self)
+        
+        if self.parent.GetName() != 'LayerManager':
+            self.search = None
+            self.cmd_prompt.Hide()
+        else:
+            self.infoCollapseLabelExp = _("Click here to show search module engine")
+            self.infoCollapseLabelCol = _("Click here to hide search module engine")
+            self.searchPane = wx.CollapsiblePane(parent = self.panelOutput,
+                                                 label = self.infoCollapseLabelExp,
+                                                 style = wx.CP_DEFAULT_STYLE |
+                                                 wx.CP_NO_TLW_RESIZE | wx.EXPAND)
+            self.MakeSearchPaneContent(self.searchPane.GetPane())
+            self.searchPane.Collapse(True)
+            self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSearchPaneChanged, self.searchPane) 
+            self.search.Bind(wx.EVT_TEXT,             self.OnUpdateStatusBar)
+        
         #
         # stream redirection
         #
         self.cmd_stdout = GMStdout(self)
         self.cmd_stderr = GMStderr(self)
-
+        
         #
         # thread
         #
         self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
-
+        
         #
         # buttons
         #
-        self.console_clear = wx.Button(parent=self, id=wx.ID_CLEAR)
-        self.console_save  = wx.Button(parent=self, id=wx.ID_SAVE)
-        self.Bind(wx.EVT_BUTTON, self.ClearHistory, self.console_clear)
-        self.Bind(wx.EVT_BUTTON, self.SaveHistory,  self.console_save)
-
-        self.Bind(EVT_CMD_ABORT, self.OnCmdAbort)
+        self.btn_console_clear = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY,
+                                           label = _("C&lear output"), size=(125,-1))
+        self.btn_cmd_clear = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY,
+                                       label = _("&Clear command"), size=(125,-1))
+        if self.parent.GetName() != 'LayerManager':
+            self.btn_cmd_clear.Hide()
+        self.btn_console_save  = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY,
+                                           label = _("&Save output"), size=(125,-1))
+        # abort
+        self.btn_abort = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY, label = _("&Abort command"),
+                                   size=(125,-1))
+        self.btn_abort.SetToolTipString(_("Abort the running command"))
+        self.btn_abort.Enable(False)
         
+        self.btn_cmd_clear.Bind(wx.EVT_BUTTON,     self.cmd_prompt.OnCmdErase)
+        self.btn_console_clear.Bind(wx.EVT_BUTTON, self.ClearHistory)
+        self.btn_console_save.Bind(wx.EVT_BUTTON,  self.SaveHistory)
+        self.btn_abort.Bind(wx.EVT_BUTTON,         self.OnCmdAbort)
+        self.btn_abort.Bind(EVT_CMD_ABORT,         self.OnCmdAbort)
+        
         self.__layout()
-
+        
     def __layout(self):
-        """Do layout"""
-        boxsizer1 = wx.BoxSizer(wx.VERTICAL)
-        gridsizer1 = wx.GridSizer(rows=1, cols=2, vgap=0, hgap=0)
-        boxsizer1.Add(item=self.cmd_output, proportion=1,
-                      flag=wx.EXPAND | wx.ADJUST_MINSIZE, border=0)
-        gridsizer1.Add(item=self.console_clear, proportion=0,
-                       flag=wx.ALIGN_CENTER_HORIZONTAL | wx.ADJUST_MINSIZE, border=0)
-        gridsizer1.Add(item=self.console_save, proportion=0,
-                       flag=wx.ALIGN_CENTER_HORIZONTAL | wx.ADJUST_MINSIZE, border=0)
+        """!Do layout"""
+        OutputSizer = wx.BoxSizer(wx.VERTICAL)
+        PromptSizer = wx.BoxSizer(wx.VERTICAL)
+        ButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
 
+        if self.search and self.search.IsShown():
+            OutputSizer.Add(item=self.searchPane, proportion=0,
+                            flag=wx.EXPAND | wx.ALL, border=3)
+        OutputSizer.Add(item=self.cmd_output, proportion=1,
+                        flag=wx.EXPAND | wx.ALL, border=3)
+        OutputSizer.Add(item=self.console_progressbar, proportion=0,
+                        flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=3)
+        
+        PromptSizer.Add(item=self.cmd_prompt, proportion=1,
+                        flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3)
+        
+        ButtonSizer.Add(item=self.btn_console_clear, proportion=0,
+                        flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
+        ButtonSizer.Add(item=self.btn_console_save, proportion=0,
+                        flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
+        ButtonSizer.Add(item=self.btn_cmd_clear, proportion=0,
+                        flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
+        ButtonSizer.Add(item=self.btn_abort, proportion=0,
+                        flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
+        PromptSizer.Add(item=ButtonSizer, proportion=0,
+                        flag=wx.ALIGN_CENTER)
+        
+        OutputSizer.Fit(self)
+        OutputSizer.SetSizeHints(self)
+        
+        PromptSizer.Fit(self)
+        PromptSizer.SetSizeHints(self)
+        
+        self.panelOutput.SetSizer(OutputSizer)
+        self.panelPrompt.SetSizer(PromptSizer)
+        
+        # split window
+        if self.parent.GetName() == 'LayerManager':
+            self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50)
+            self.SetMinimumPaneSize(self.btn_cmd_clear.GetSize()[1] + 50)
+        else:
+            self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45)
+            self.SetMinimumPaneSize(self.btn_cmd_clear.GetSize()[1] + 10)
+        
+        self.SetSashGravity(1.0)
+        
+        # layout
+        self.SetAutoLayout(True)
+        self.Layout()
 
-        boxsizer1.Add(item=gridsizer1, proportion=0,
-                      flag=wx.EXPAND | wx.ALIGN_CENTRE_VERTICAL | wx.TOP | wx.BOTTOM,
-                      border=5)
-        boxsizer1.Add(item=self.console_progressbar, proportion=0,
-                      flag=wx.EXPAND | wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
+    def MakeSearchPaneContent(self, pane):
+        """!Create search pane"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        self.search = SearchModuleWindow(parent = pane, cmdPrompt = self.cmd_prompt)
+        
+        border.Add(item = self.search, proportion = 0,
+                   flag = wx.EXPAND | wx.ALL, border = 1)
+        
+        pane.SetSizer(border)
+        border.Fit(pane)
+        
+    def OnSearchPaneChanged(self, event):
+        """!Collapse search module box"""
+        if self.searchPane.IsExpanded():
+            self.searchPane.SetLabel(self.infoCollapseLabelCol)
+        else:
+            self.searchPane.SetLabel(self.infoCollapseLabelExp)
+        
+        self.panelOutput.Layout()
+        self.panelOutput.SendSizeEvent()
+        
+    def GetPanel(self, prompt = True):
+        """!Get panel
 
-        boxsizer1.Fit(self)
-        boxsizer1.SetSizeHints(self)
+        @param prompt get prompt / output panel
 
-        # layout
-        self.SetAutoLayout(True)
-        self.SetSizer(boxsizer1)
+        @return wx.Panel reference
+        """
+        if prompt:
+            return self.panelPrompt
 
+        return self.panelOutput
+    
     def Redirect(self):
-        """Redirect stderr
+        """!Redirect stderr
 
         @return True redirected
         @return False failed
         """
-        if Debug.get_level() == 0:
+        if Debug.get_level() == 0 and int(grass.gisenv().get('DEBUG', 0)) == 0:
             # don't redirect when debugging is enabled
             sys.stdout = self.cmd_stdout
             sys.stderr = self.cmd_stderr
@@ -219,23 +351,27 @@
 
     def WriteLog(self, text, style = None, wrap = None,
                  switchPage = False):
-        """Generic method for writing log message in 
+        """!Generic method for writing log message in 
         given style
 
         @param line text line
         @param style text style (see GMStc)
         @param stdout write to stdout or stderr
         """
+
+        self.cmd_output.SetStyle()
+
         if switchPage and \
-                self.parent.notebook.GetSelection() != self.parent.goutput.pageid:
-            self.parent.notebook.SetSelection(self.parent.goutput.pageid)
+                self._notebook.GetSelection() != self.parent.goutput.pageid:
+            self._notebook.SetSelection(self.parent.goutput.pageid)
         
         if not style:
             style = self.cmd_output.StyleDefault
         
         # p1 = self.cmd_output.GetCurrentPos()
         p1 = self.cmd_output.GetEndStyled()
-        self.cmd_output.GotoPos(p1)
+#        self.cmd_output.GotoPos(p1)
+        self.cmd_output.DocumentEnd()
         
         for line in text.splitlines():
             # fill space
@@ -253,52 +389,66 @@
         self.cmd_output.EnsureCaretVisible()
         
     def WriteCmdLog(self, line, pid=None):
-        """Write message in selected style"""
+        """!Write message in selected style"""
         if pid:
             line = '(' + str(pid) + ') ' + line
-        self.WriteLog(line, style=self.cmd_output.StyleCommand)
+        self.WriteLog(line, style=self.cmd_output.StyleCommand, switchPage = True)
 
     def WriteWarning(self, line):
-        """Write message in warning style"""
-        self.WriteLog(line, style=self.cmd_output.StyleWarning)
+        """!Write message in warning style"""
+        self.WriteLog(line, style=self.cmd_output.StyleWarning, switchPage = True)
 
     def WriteError(self, line):
-        """Write message in error style"""
-        self.WriteLog(line, style=self.cmd_output.StyleError)
+        """!Write message in error style"""
+        self.WriteLog(line, style=self.cmd_output.StyleError, switchPage = True)
 
-    def RunCmd(self, command, compReg=True, switchPage=False,
+    def RunCmd(self, command, compReg = True, switchPage = False,
                onDone = None):
-        """
-        Run in GUI GRASS (or other) commands typed into
-        console command text widget, and send stdout output to output
-        text widget.
+        """!Run in GUI GRASS (or other) commands typed into console
+        command text widget, and send stdout output to output text
+        widget.
 
         Command is transformed into a list for processing.
 
-        TODO: Display commands (*.d) are captured and
-        processed separately by mapdisp.py. Display commands are
-        rendered in map display widget that currently has
-        the focus (as indicted by mdidx).
+        @todo Display commands (*.d) are captured and processed
+        separately by mapdisp.py. Display commands are rendered in map
+        display widget that currently has the focus (as indicted by
+        mdidx).
 
         @param command command (list)
         @param compReg if true use computation region
         @param switchPage switch to output page
         @param onDone function to be called when command is finished
         """
-        
-        # map display window available ?
-        try:
-            curr_disp = self.parent.curr_page.maptree.mapdisplay
-            self.Map = curr_disp.GetRender()
-        except:
-            curr_disp = None
-        
         # command given as a string ?
         try:
             cmdlist = command.strip().split(' ')
         except:
             cmdlist = command
         
+        # update history file
+        env = grass.gisenv()
+        try:
+            fileHistory = open(os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'],
+                                            '.bash_history'), 'a')
+        except IOError, e:
+            self.WriteError(str(e))
+            fileHistory = None
+        
+        cmdString = ' '.join(cmdlist)
+        if fileHistory:
+            try:
+                fileHistory.write(cmdString + '\n')
+            finally:
+                fileHistory.close()
+        
+        # update history items
+        if self.parent.GetName() == 'LayerManager':
+            try:
+                self.parent.cmdinput.SetHistoryItems()
+            except AttributeError:
+                pass
+        
         if cmdlist[0] in globalvar.grassCmd['all']:
             # send GRASS command without arguments to GUI command interface
             # except display commands (they are handled differently)
@@ -308,6 +458,7 @@
                 #
                 try:
                     layertype = {'d.rast'         : 'raster',
+                                 'd.rast3d'       : '3d-raster',
                                  'd.rgb'          : 'rgb',
                                  'd.his'          : 'his',
                                  'd.shaded'       : 'shaded',
@@ -322,9 +473,9 @@
                                  'd.rhumbline'    : 'rhumb',
                                  'd.labels'       : 'labels'}[cmdlist[0]]
                 except KeyError:
-                    wx.MessageBox(caption = _("Message"),
-                                  message=_("Command '%s' not yet implemented in the GUI. "
-                                            "Try adding it as a command layer instead.") % cmdlist[0])
+                    gcmd.GMessage(parent = self.parent,
+                                  message = _("Command '%s' not yet implemented in the WxGUI. "
+                                              "Try adding it as a command layer instead.") % cmdlist[0])
                     return None
                 
                 # add layer into layer tree
@@ -336,9 +487,8 @@
                                                       layerType = 'vector')
                 else:
                     lname = None
-                                
-                # add layer into layer tree
-                if self.parent.GetName() == "LayerManager":
+                
+                if self.parent.GetName() == "LayerManager":                
                     self.parent.curr_page.maptree.AddLayer(ltype=layertype,
                                                            lname=lname,
                                                            lcmd=cmdlist)
@@ -349,8 +499,8 @@
                 
                 # switch to 'Command output'
                 if switchPage:
-                    if self.parent.notebook.GetSelection() != self.parent.goutput.pageid:
-                        self.parent.notebook.SetSelection(self.parent.goutput.pageid)
+                    if self._notebook.GetSelection() != self.parent.goutput.pageid:
+                        self._notebook.SetSelection(self.parent.goutput.pageid)
                     
                     self.parent.SetFocus() # -> set focus
                     self.parent.Raise()
@@ -361,20 +511,26 @@
                     tmpreg = os.getenv("GRASS_REGION")
                     if os.environ.has_key("GRASS_REGION"):
                         del os.environ["GRASS_REGION"]
-                    
+
                 if len(cmdlist) == 1:
                     import menuform
+                    task = menuform.GUI().ParseInterface(cmdlist)
+                    if not task.has_required():
+                        task = None # run command
+                else:
+                    task = None
+                
+                if task and cmdlist[0] not in ('v.krige.py'):
                     # process GRASS command without argument
-                    menuform.GUI().ParseCommand(cmdlist, parentframe=self)
+                    menuform.GUI().ParseCommand(cmdlist, parentframe = self)
                 else:
                     # process GRASS command with argument
                     self.cmdThread.RunCmd(GrassCmd,
                                           onDone,
                                           cmdlist,
                                           self.cmd_stdout, self.cmd_stderr)                                          
+                    self.cmd_output_timer.Start(50)
                     
-                    self.cmd_output_timer.Start(50)
-
                     return None
                 
                 # deactivate computational region and return to display settings
@@ -383,24 +539,23 @@
         else:
             # Send any other command to the shell. Send output to
             # console output window
-
-            # if command is not a GRASS command, treat it like a shell command
-            try:
-                generalCmd = gcmd.Command(cmdlist,
-                                          stdout=self.cmd_stdout,
-                                          stderr=self.cmd_stderr)
-            except gcmd.CmdError, e:
-                print >> sys.stderr, e
-
+            self.cmdThread.RunCmd(GrassCmd,
+                                  onDone,
+                                  cmdlist,
+                                  self.cmd_stdout, self.cmd_stderr)                                         
+            self.cmd_output_timer.Start(50)
+        
         return None
 
     def ClearHistory(self, event):
-        """Clear history of commands"""
+        """!Clear history of commands"""
+        self.cmd_output.SetReadOnly(False)
         self.cmd_output.ClearAll()
+        self.cmd_output.SetReadOnly(True)
         self.console_progressbar.SetValue(0)
 
     def SaveHistory(self, event):
-        """Save history of commands"""
+        """!Save history of commands"""
         self.history = self.cmd_output.GetSelectedText()
         if self.history == '':
             self.history = self.cmd_output.GetText()
@@ -427,19 +582,29 @@
         dlg.Destroy()
 
     def GetCmd(self):
-        """Get running command or None"""
+        """!Get running command or None"""
         return self.requestQ.get()
+    
+    def OnUpdateStatusBar(self, event):
+        """!Update statusbar text"""
+        if event.GetString():
+            nItems = len(self.cmd_prompt.GetCommandItems())
+            self.parent.SetStatusText(_('%d modules match') % nItems, 0)
+        else:
+            self.parent.SetStatusText('', 0)
         
+        event.Skip()
+
     def OnCmdOutput(self, event):
-        """Print command output"""
+        """!Print command output"""
         message = event.text
         type  = event.type
-        if self.parent.notebook.GetSelection() != self.parent.goutput.pageid:
-            textP = self.parent.notebook.GetPageText(self.parent.goutput.pageid)
+        if self._notebook.GetSelection() != self.parent.goutput.pageid:
+            textP = self._notebook.GetPageText(self.parent.goutput.pageid)
             if textP[-1] != ')':
                 textP += ' (...)'
-            self.parent.notebook.SetPageText(self.parent.goutput.pageid,
-                                             textP)
+            self._notebook.SetPageText(self.parent.goutput.pageid,
+                                       textP)
         
         # message prefix
         if type == 'warning':
@@ -476,7 +641,7 @@
                 self.cmd_output.AddTextWrapped(message, wrap=60)
             else:
                 self.cmd_output.AddTextWrapped(message, wrap=None)
-	    
+
         p2 = self.cmd_output.GetCurrentPos()
         
         if p2 >= p1:
@@ -494,19 +659,26 @@
         self.cmd_output.EnsureCaretVisible()
         
     def OnCmdProgress(self, event):
-        """Update progress message info"""
+        """!Update progress message info"""
         self.console_progressbar.SetValue(event.value)
 
     def OnCmdAbort(self, event):
-        """Abort running command"""
+        """!Abort running command"""
         self.cmdThread.abort()
-
+        
     def OnCmdRun(self, event):
-        """Run command"""
+        """!Run command"""
+        if self.parent.GetName() == 'Modeler':
+            self.parent.OnCmdRun(event)
+        
         self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)))
+        self.btn_abort.Enable()
 
     def OnCmdDone(self, event):
-        """Command done (or aborted)"""
+        """!Command done (or aborted)"""
+        if self.parent.GetName() == 'Modeler':
+            self.parent.OnCmdDone(event)
+        
         if event.aborted:
             # Thread aborted (using our convention of None return)
             self.WriteLog(_('Please note that the data are left in inconsistent state '
@@ -515,78 +687,98 @@
                                                    _('Command aborted'),
                                                    (time.time() - event.time)))
             # pid=self.cmdThread.requestId)
+            self.btn_abort.Enable(False)
         else:
             try:
                 # Process results here
                 self.WriteCmdLog('(%s) %s (%d sec)' % (str(time.ctime()),
                                                        _('Command finished'),
                                                        (time.time() - event.time)))
-                # pid=event.pid)
             except KeyError:
                 # stopped deamon
                 pass
+            
+            self.btn_abort.Enable(False)
         
         if event.onDone:
-            event.onDone(returncode = event.returncode)
+            event.onDone(cmd = event.cmd, returncode = event.returncode)
         
         self.console_progressbar.SetValue(0) # reset progress bar on '0%'
 
         self.cmd_output_timer.Stop()
 
-        # updated command dialog
-        if hasattr(self.parent.parent, "btn_run"):
+        # set focus on prompt
+        if self.parent.GetName() == "LayerManager":
+            self.btn_abort.Enable(False)
+            if event.cmd[0] in globalvar.cmdAutoRender:
+                display = self.parent.GetLayerTree().GetMapDisplay()
+                if display and display.IsAutoRendered():
+                    display.GetWindow().UpdateMap(render = True)
+        
+        else:
+            # updated command dialog
             dialog = self.parent.parent
 
             if hasattr(self.parent.parent, "btn_abort"):
                 dialog.btn_abort.Enable(False)
-
+            
             if hasattr(self.parent.parent, "btn_cancel"):
                 dialog.btn_cancel.Enable(True)
-
+            
             if hasattr(self.parent.parent, "btn_clipboard"):
                 dialog.btn_clipboard.Enable(True)
-
+            
             if hasattr(self.parent.parent, "btn_help"):
                 dialog.btn_help.Enable(True)
-
-            dialog.btn_run.Enable(True)
-
-            if event.returncode == 0 and \
-                    not event.aborted and hasattr(dialog, "addbox") and \
-                    dialog.addbox.IsChecked():
-                # add new map into layer tree
-                if dialog.outputType in ('raster', 'vector'):
-                    # add layer into layer tree
-                    cmd = dialog.notebookpanel.createCmd(ignoreErrors = True)
-                    name = utils.GetLayerNameFromCmd(cmd, fullyQualified=True, param='output')
-                    winName = self.parent.parent.parent.GetName()
-                    if winName == 'LayerManager':
-                        mapTree = self.parent.parent.parent.curr_page.maptree
-                    else: # GMConsole
-                        mapTree = self.parent.parent.parent.parent.curr_page.maptree
-                    
-                    if dialog.outputType == 'raster':
-                        lcmd = ['d.rast',
-                                'map=%s' % name]
-                    else:
-                        lcmd = ['d.vect',
-                                'map=%s' % name]
-                    mapTree.AddLayer(ltype=dialog.outputType,
-                                     lcmd=lcmd,
-                                     lname=name)
             
-            if dialog.get_dcmd is None and \
-                   dialog.closebox.IsChecked():
+            if hasattr(self.parent.parent, "btn_run"):
+                dialog.btn_run.Enable(True)
+            
+            if event.returncode == 0 and not event.aborted:
+                winName = self.parent.parent.parent.GetName()
+                if winName == 'LayerManager':
+                    mapTree = self.parent.parent.parent.GetLayerTree()
+                elif winName == 'LayerTree':
+                    mapTree = self.parent.parent.parent
+                else: # GMConsole
+                    mapTree = self.parent.parent.parent.parent.GetLayerTree()
+                
+                cmd = dialog.notebookpanel.createCmd(ignoreErrors = True)
+                if hasattr(dialog, "addbox") and dialog.addbox.IsChecked():
+                    # add created maps into layer tree
+                    for p in dialog.task.get_options()['params']:
+                        prompt = p.get('prompt', '')
+                        if prompt in ('raster', 'vector', '3d-raster') and \
+                                p.get('age', 'old') == 'new' and \
+                                p.get('value', None):
+                            name = utils.GetLayerNameFromCmd(cmd, fullyQualified=True, param = p.get('name', ''))
+                            
+                            if mapTree.GetMap().GetListOfLayers(l_name = name):
+                                continue
+                            
+                            if prompt == 'raster':
+                                lcmd = ['d.rast',
+                                        'map=%s' % name]
+                            else:
+                                lcmd = ['d.vect',
+                                        'map=%s' % name]
+                            mapTree.AddLayer(ltype = prompt,
+                                             lcmd = lcmd,
+                                             lname = name)
+            
+            if hasattr(dialog, "get_dcmd") and \
+                    dialog.get_dcmd is None and \
+                    dialog.closebox.IsChecked():
                 time.sleep(1)
                 dialog.Close()
-
+        
         event.Skip()
         
     def OnProcessPendingOutputWindowEvents(self, event):
         self.ProcessPendingEvents()
 
 class GMStdout:
-    """GMConsole standard output
+    """!GMConsole standard output
 
     Based on FrameOutErr.py
 
@@ -612,7 +804,7 @@
             wx.PostEvent(self.parent.cmd_output, evt)
         
 class GMStderr:
-    """GMConsole standard error output
+    """!GMConsole standard error output
 
     Based on FrameOutErr.py
 
@@ -629,6 +821,9 @@
         self.message = ''
         self.printMessage = False
         
+    def flush(self):
+        pass
+    
     def write(self, s):
         if "GtkPizza" in s:
             return
@@ -639,7 +834,7 @@
         for line in s.splitlines():
             if len(line) == 0:
                 continue
-
+            
             if 'GRASS_INFO_PERCENT' in line:
                 value = int(line.rsplit(':', 1)[1].strip())
                 if value >= 0 and value < 100:
@@ -682,7 +877,7 @@
             wx.PostEvent(self.parent.console_progressbar, evt)
             
 class GMStc(wx.stc.StyledTextCtrl):
-    """Styled GMConsole
+    """!Styled GMConsole
 
     Based on FrameOutErr.py
 
@@ -695,39 +890,14 @@
     def __init__(self, parent, id, margin=False, wrap=None):
         wx.stc.StyledTextCtrl.__init__(self, parent, id)
         self.parent = parent
+        self.SetUndoCollection(True)
+        self.SetReadOnly(True)
 
         #
         # styles
-        #
-        self.StyleDefault     = 0
-        self.StyleDefaultSpec = "face:Courier New,size:10,fore:#000000,back:#FFFFFF"
-        self.StyleCommand     = 1
-        self.StyleCommandSpec = "face:Courier New,size:10,fore:#000000,back:#bcbcbc"
-        self.StyleOutput      = 2
-        self.StyleOutputSpec  = "face:Courier New,size:10,fore:#000000,back:#FFFFFF"
-        # fatal error
-        self.StyleError       = 3
-        self.StyleErrorSpec   = "face:Courier New,size:10,fore:#7F0000,back:#FFFFFF"
-        # warning
-        self.StyleWarning     = 4
-        self.StyleWarningSpec = "face:Courier New,size:10,fore:#0000FF,back:#FFFFFF"
-        # message
-        self.StyleMessage     = 5
-        self.StyleMessageSpec = "face:Courier New,size:10,fore:#000000,back:#FFFFFF"
-        # unknown
-        self.StyleUnknown     = 6
-        self.StyleUnknownSpec = "face:Courier New,size:10,fore:#000000,back:#FFFFFF"
+        #                
+        self.SetStyle()
         
-        # default and clear => init
-        self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
-        self.StyleClearAll()
-        self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec)
-        self.StyleSetSpec(self.StyleOutput,  self.StyleOutputSpec)
-        self.StyleSetSpec(self.StyleError,   self.StyleErrorSpec)
-        self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec)
-        self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec)
-        self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)
-
         #
         # line margins
         #
@@ -751,22 +921,67 @@
         self.SetUseHorizontalScrollBar(True)
 
         #
-        # bindins
+        # bindings
         #
         self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+        
+    def SetStyle(self):
+        """!Set styles for styled text output windows with type face 
+        and point size selected by user (Courier New 10 is default)"""
 
+        settings = preferences.Settings()
+        
+        typeface = settings.Get(group='display', key='outputfont', subkey='type')   
+        if typeface == "": typeface = "Courier New"
+                           
+        typesize = settings.Get(group='display', key='outputfont', subkey='size')
+        if typesize == None or typesize <= 0: typesize = 10
+        typesize = float(typesize)
+
+        self.StyleDefault     = 0
+        self.StyleDefaultSpec = "face:%s,size:%d,fore:#000000,back:#FFFFFF" % (typeface, typesize)
+        self.StyleCommand     = 1
+        self.StyleCommandSpec = "face:%s,size:%d,,fore:#000000,back:#bcbcbc" % (typeface, typesize)
+        self.StyleOutput      = 2
+        self.StyleOutputSpec  = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
+        # fatal error
+        self.StyleError       = 3
+        self.StyleErrorSpec   = "face:%s,size:%d,,fore:#7F0000,back:#FFFFFF" % (typeface, typesize)
+        # warning
+        self.StyleWarning     = 4
+        self.StyleWarningSpec = "face:%s,size:%d,,fore:#0000FF,back:#FFFFFF" % (typeface, typesize)
+        # message
+        self.StyleMessage     = 5
+        self.StyleMessageSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
+        # unknown
+        self.StyleUnknown     = 6
+        self.StyleUnknownSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
+        
+        # default and clear => init
+        self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
+        self.StyleClearAll()
+        self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec)
+        self.StyleSetSpec(self.StyleOutput,  self.StyleOutputSpec)
+        self.StyleSetSpec(self.StyleError,   self.StyleErrorSpec)
+        self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec)
+        self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec)
+        self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)        
+
     def OnDestroy(self, evt):
-        """The clipboard contents can be preserved after
+        """!The clipboard contents can be preserved after
         the app has exited"""
         
         wx.TheClipboard.Flush()
         evt.Skip()
 
     def AddTextWrapped(self, txt, wrap=None):
-        """Add string to text area.
+        """!Add string to text area.
 
         String is wrapped and linesep is also added to the end
         of the string"""
+        # allow writing to output window
+        self.SetReadOnly(False)
+        
         if wrap:
             txt = textwrap.fill(txt, wrap) + '\n'
         else:
@@ -795,5 +1010,8 @@
                 else:
                     txt = _('Unable to encode text. Please set encoding in GUI preferences.') + '\n'
                     
-                self.AddText(txt) 
+                self.AddText(txt)
+        
+        # reset output window to read only
+        self.SetReadOnly(True)
     

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gselect.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gselect.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/gselect.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package gselect
 
 @brief Custom control that selects elements
@@ -12,59 +12,81 @@
  - DriverSelect
  - DatabaseSelect
  - ColumnSelect
+ - LocationSelect
+ - MapsetSelect
  - SubGroupSelect
+ - FormatSelect
+ - GdalSelect
+ 
+(C) 2007-2010 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.
 
-(C) 2007-2010 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.
-
 @author Michael Barton
 @author Martin Landa <landa.martin gmail.com>
 """
 
 import os
 import sys
+import glob
 
 import wx
 import wx.combo
+import wx.lib.filebrowsebutton as filebrowse
+from wx.lib.newevent import NewEvent
 
+import globalvar
+
+sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
 import grass.script as grass
 
-import globalvar
 import gcmd
 import utils
 from preferences import globalSettings as UserSettings
+from debug import Debug
 
+wxGdalSelect, EVT_GDALSELECT = NewEvent()
+
 class Select(wx.combo.ComboCtrl):
     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
-                 type = None, multiple = False, mapsets = None, exceptOf = [],
-                 updateOnPopup = True):
+                 type = None, multiple = False, mapsets = None,
+                 updateOnPopup = True, onPopup = None):
+        """!Custom control to create a ComboBox with a tree control to
+        display and select GIS elements within acessible mapsets.
+        Elements can be selected with mouse. Can allow multiple
+        selections, when argument multiple=True. Multiple selections
+        are separated by commas.
+
+        @param type type of GIS elements ('raster, 'vector', ...)
+        @param multiple multiple input allowed?
+        @param mapsets force list of mapsets (otherwise search path)
+        @param updateOnPopup True for updating list of elements on popup
+        @param onPopup function to be called on Popup
         """
-        Custom control to create a ComboBox with a tree control
-        to display and select GIS elements within acessible mapsets.
-        Elements can be selected with mouse. Can allow multiple selections, when
-        argument multiple=True. Multiple selections are separated by commas.
-        """
         wx.combo.ComboCtrl.__init__(self, parent=parent, id=id, size=size)
         self.GetChildren()[0].SetName("Select")
         self.GetChildren()[0].type = type
         
         self.tcp = TreeCtrlComboPopup()
-
         self.SetPopupControl(self.tcp)
         self.SetPopupExtents(0,100)
         if type:
-            self.tcp.GetElementList(type, mapsets, exceptOf)
             self.tcp.SetData(type = type, mapsets = mapsets,
-                             exceptOf = exceptOf, multiple = multiple,
-                             updateOnPopup = updateOnPopup)
+                             multiple = multiple,
+                             updateOnPopup = updateOnPopup, onPopup = onPopup)
+        
+    def SetElementList(self, type, mapsets = None):
+        """!Set element list
 
-    def SetElementList(self, type, mapsets = None, exceptOf = []):
-        self.tcp.seltree.DeleteAllItems()
-        self.tcp.GetElementList(type)
-        self.tcp.SetData(type = type, mapsets = mapsets,
-                         exceptOf = exceptOf)
+        @param type GIS element type
+        @param mapsets list of acceptable mapsets (None for all in search path)
+        """
+        self.tcp.SetData(type = type, mapsets = mapsets)
         
+    def GetElementList(self):
+        """!Load elements"""
+        self.tcp.GetElementList()
+    
 class VectorSelect(Select):
     def __init__(self, parent, ftype, **kwargs):
         """!Custom to create a ComboBox with a tree control to display and
@@ -92,21 +114,19 @@
         return True
 
 class TreeCtrlComboPopup(wx.combo.ComboPopup):
-    """
-    Create a tree ComboBox for selecting maps and other GIS elements
+    """!Create a tree ComboBox for selecting maps and other GIS elements
     in accessible mapsets within the current location
     """
-
     # overridden ComboPopup methods
     def Init(self):
         self.value = [] # for multiple is False -> len(self.value) in [0,1]
         self.curitem = None
         self.multiple = False
         self.type = None
+        self.mapsets = None
         self.updateOnPopup = True
-        self.mapsets = []
-        self.exceptOf = []
-
+        self.onPopup = None
+        
         self.SetFilter(None)
         
     def Create(self, parent):
@@ -154,14 +174,27 @@
         """!Set filter for GIS elements, see e.g. VectorSelect"""
         self.filterElements = filter
         
-    def OnPopup(self):
-        """Limited only for first selected"""
+    def OnPopup(self, force = False):
+        """!Limited only for first selected"""
+        if not force and not self.updateOnPopup:
+            return
+        if self.onPopup:
+            selected, exclude = self.onPopup(self.type)
+        else:
+            selected = None
+            exclude  = False
+            
+        self.GetElementList(selected, exclude)
+    
+    def GetElementList(self, elements = None, exclude = False):
+        """!Get filtered list of GIS elements in accessible mapsets
+        and display as tree with all relevant elements displayed
+        beneath each mapset branch
+        """
         # update list
-        if not self.updateOnPopup:
-            return
         self.seltree.DeleteAllItems()
-        self.GetElementList(self.type, self.mapsets, self.exceptOf)
-
+        self._getElementList(self.type, self.mapsets, elements, exclude)
+        
         if len(self.value) > 0:
             root = self.seltree.GetRootItem()
             if not root:
@@ -186,10 +219,14 @@
     def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
         return wx.Size(minWidth, min(200, maxHeight))
 
-    def GetElementList(self, element, mapsets=None, exceptOf=[]):
-        """
-        Get list of GIS elements in accessible mapsets and display as tree
+    def _getElementList(self, element, mapsets = None, elements = None, exclude = False):
+        """!Get list of GIS elements in accessible mapsets and display as tree
         with all relevant elements displayed beneath each mapset branch
+
+        @param element GIS element
+        @param mapsets list of acceptable mapsets (None for all mapsets in search path)
+        @param elements list of forced GIS elements
+        @param exclude True to exclude, False for forcing the list (elements)
         """
         # get current mapset
         curr_mapset = grass.gisenv()['MAPSET']
@@ -197,7 +234,7 @@
         # list of mapsets in current location
         if mapsets is None:
             mapsets = utils.ListOfMapsets(get = 'accessible')
-
+        
         # map element types to g.mlist types
         elementdict = {'cell':'rast',
                        'raster':'rast',
@@ -243,11 +280,11 @@
                        '3dview':'3dview',
                        '3D viewing parameters':'3dview',
                        '3D view parameters':'3dview'}
-
+        
         if element not in elementdict:
             self.AddItem(_('Not selectable element'))
             return
-
+        
         # get directory tree nodes
         # reorder mapsets based on search path (TODO)
         for i in range(len(mapsets)):
@@ -273,9 +310,16 @@
                 for elem in elem_list:
                     if elem != '':
                         fullqElem = elem + '@' + dir
-                        if len(exceptOf) > 0 and fullqElem in exceptOf:
-                            continue
-                        self.AddItem(fullqElem, parent=dir_node)
+                        if elements:
+                            if (exclude and fullqElem in elements) or \
+                                    (not exclude and fullqElem not in elements):
+                                continue
+                        
+                        if self.filterElements:
+                            if self.filterElements(fullqElem):
+                                self.AddItem(fullqElem, parent=dir_node)
+                        else:
+                            self.AddItem(fullqElem, parent=dir_node)
             except:
                 continue
 
@@ -357,20 +401,20 @@
         evt.Skip()
 
     def SetData(self, **kargs):
-        """Set object properties"""
+        """!Set object properties"""
         if kargs.has_key('type'):
             self.type = kargs['type']
         if kargs.has_key('mapsets'):
             self.mapsets = kargs['mapsets']
-        if kargs.has_key('exceptOf'):
-            self.exceptOf = kargs['exceptOf']
         if kargs.has_key('multiple'):
             self.multiple = kargs['multiple']
         if kargs.has_key('updateOnPopup'):
             self.updateOnPopup = kargs['updateOnPopup']
+        if kargs.has_key('onPopup'):
+            self.onPopup = kargs['onPopup']
         
 class VectorDBInfo:
-    """Class providing information about attribute tables
+    """!Class providing information about attribute tables
     linked to a vector map"""
     def __init__(self, map):
         self.map = map
@@ -380,13 +424,13 @@
          # dictionary of table and associated columns (type, length, values, ids)
         self.tables = {}
         
-        if not self.__CheckDBConnection(): # -> self.layers
+        if not self._CheckDBConnection(): # -> self.layers
             return
 
-        self.__DescribeTables() # -> self.tables
+        self._DescribeTables() # -> self.tables
 
-    def __CheckDBConnection(self):
-        """Check DB connection"""
+    def _CheckDBConnection(self):
+        """!Check DB connection"""
         nuldev = file(os.devnull, 'w+')
         self.layers = grass.vector_db(map=self.map, stderr=nuldev)
         nuldev.close()
@@ -395,45 +439,37 @@
             return False
 
         return True
-    
-    def __DescribeTables(self):
-        """Describe linked tables"""
+
+    def _DescribeTables(self):
+        """!Describe linked tables"""
         for layer in self.layers.keys():
             # determine column names and types
             table = self.layers[layer]["table"]
-            columnsCommand = gcmd.Command (cmd=["db.describe",
-                                                "-c", "--q",
-                                                "table=%s" % self.layers[layer]["table"],
-                                                "driver=%s" % self.layers[layer]["driver"],
-                                                "database=%s" % self.layers[layer]["database"]],
-                                           rerr = None)
-
-
             columns = {} # {name: {type, length, [values], [ids]}}
+            i = 0
+            Debug.msg(1, "gselect.VectorDBInfo._DescribeTables(): table=%s driver=%s database=%s" % \
+                          (self.layers[layer]["table"], self.layers[layer]["driver"],
+                           self.layers[layer]["database"]))
+            for item in grass.db_describe(table = self.layers[layer]["table"],
+                                          driver = self.layers[layer]["driver"],
+                                          database = self.layers[layer]["database"])['cols']:
+                name, type, length = item
+                # FIXME: support more datatypes
+                if type.lower() == "integer":
+                    ctype = int
+                elif type.lower() == "double precision":
+                    ctype = float
+                else:
+                    ctype = str
 
-            if columnsCommand.returncode == 0:
-                # skip nrows and ncols
-                i = 0
-                for line in columnsCommand.ReadStdOutput()[2:]:
-                    num, name, type, length = line.strip().split(':')
-                    # FIXME: support more datatypes
-                    if type.lower() == "integer":
-                        ctype = int
-                    elif type.lower() == "double precision":
-                        ctype = float
-                    else:
-                        ctype = str
-
-                    columns[name.strip()] = { 'index'  : i,
-                                              'type'   : type.lower(),
-                                              'ctype'  : ctype,
-                                              'length' : int(length),
-                                              'values' : [],
-                                              'ids'    : []}
-                    i += 1
-            else:
-                return False
-
+                columns[name.strip()] = { 'index'  : i,
+                                          'type'   : type.lower(),
+                                          'ctype'  : ctype,
+                                          'length' : int(length),
+                                          'values' : [],
+                                          'ids'    : []}
+                i += 1
+            
             # check for key column
             # v.db.connect -g/p returns always key column name lowercase
             if self.layers[layer]["key"] not in columns.keys():
@@ -447,14 +483,14 @@
         return True
     
     def Reset(self):
-        """Reset"""
+        """!Reset"""
         for layer in self.layers:
             table = self.layers[layer]["table"] # get table desc
             columns = self.tables[table]
             for name in self.tables[table].keys():
                 self.tables[table][name]['values'] = []
                 self.tables[table][name]['ids']    = []
-        
+    
     def GetName(self):
         """!Get vector name"""
         return self.map
@@ -464,7 +500,7 @@
         
         @param layer vector layer number
         """
-        return self.layers[layer]['key']
+        return str(self.layers[layer]['key'])
     
     def GetTable(self, layer):
         """!Get table name of given layer
@@ -490,8 +526,7 @@
         return self.tables[table]
 
 class LayerSelect(wx.Choice):
-    """
-    Creates combo box for selecting data layers defined for vector.
+    """!Creates combo box for selecting data layers defined for vector.
     The 'layer' terminology is likely to change for GRASS 7
     """
     def __init__(self, parent,
@@ -522,13 +557,13 @@
             self.SetStringSelection('1')
         
     def InsertLayers(self, vector):
-        """Insert layers for a vector into the layer combobox"""
+        """!Insert layers for a vector into the layer combobox"""
         layerchoices = utils.GetVectorNumberOfLayers(vector)
         for layer in self.initial:
             if layer in layerchoices:
                 continue
             layerchoices.append(layer)
-
+        
         # sort list of available layers
         utils.ListSortLower(layerchoices)
         
@@ -543,9 +578,8 @@
             self.SetStringSelection(str(self.default))
         
 class DriverSelect(wx.ComboBox):
+    """!Creates combo box for selecting database driver.
     """
-    Creates combo box for selecting database driver.
-    """
     def __init__(self, parent, choices, value,
                  id=wx.ID_ANY, pos=wx.DefaultPosition,
                  size=globalvar.DIALOG_LAYER_SIZE, **kargs):
@@ -558,9 +592,8 @@
         self.SetStringSelection(value)
 
 class DatabaseSelect(wx.TextCtrl):
+    """!Creates combo box for selecting database driver.
     """
-    Creates combo box for selecting database driver.
-    """
     def __init__(self, parent, value='',
                  id=wx.ID_ANY, pos=wx.DefaultPosition,
                  size=globalvar.DIALOG_TEXTCTRL_SIZE, **kargs):
@@ -570,9 +603,8 @@
         self.SetName("DatabaseSelect")
 
 class TableSelect(wx.ComboBox):
+    """!Creates combo box for selecting attribute tables from the database
     """
-    Creates combo box for selecting attribute tables from the database
-    """
     def __init__(self, parent,
                  id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
                  size=globalvar.DIALOG_COMBOBOX_SIZE,
@@ -587,24 +619,23 @@
             self.InsertTables()
                 
     def InsertTables(self, driver=None, database=None):
-        """Insert attribute tables into combobox"""
+        """!Insert attribute tables into combobox"""
         items = []
-        cmd = ['db.tables',
-               '-p']
-        if driver:
-            cmd.append('driver=%s' % driver)
-        if database:
-            cmd.append('database=%s' % database)
-        
-        try:
-            tableCmd = gcmd.Command(cmd)
-        except gcmd.CmdError:
-            tableCmd = None
 
+        if not driver or not database:
+            connect = grass.db_connection()
+            
+            driver = connect['driver']
+            database = connect['database']
         
-        if tableCmd and \
-                tableCmd.returncode == 0:
-            for table in tableCmd.ReadStdOutput():
+        ret = gcmd.RunCommand('db.tables',
+                              flags = 'p',
+                              read = True,
+                              driver = driver,
+                              database = database)
+        
+        if ret:
+            for table in ret.splitlines():
                 items.append(table)
             
         self.SetItems(items)
@@ -620,46 +651,41 @@
     @param size window size
     @param vector vector map name
     @param layer layer number
+    @param param parameters list (see menuform.py)
     @param **kwags wx.ComboBox parameters
     """
-    def __init__(self, parent,
-                 id = wx.ID_ANY, value = '',
-                 size = globalvar.DIALOG_COMBOBOX_SIZE,
-                 vector = None, layer = 1, choices = [], readonly = False, param = None,
-                 **kwargs):
-        if readonly:
-            if kwargs.has_key('style'):
-                style |= wx.CB_READONLY
-            else:
-                style = wx.CB_READONLY
-        
+    def __init__(self, parent, id = wx.ID_ANY, value = '', 
+                 size=globalvar.DIALOG_COMBOBOX_SIZE,
+                 vector = None, layer = 1, param = None, **kwargs):
         self.defaultValue = value
         self.param = param
         
-        super(ColumnSelect, self).__init__(parent, id, value = value, choices = choices, size = size,
-                                           **kwargs)
+        super(ColumnSelect, self).__init__(parent, id, value, size = size, **kwargs)
         self.SetName("ColumnSelect")
         
         if vector:
             self.InsertColumns(vector, layer)
     
-    def InsertColumns(self, vector, layer, type = None, dbInfo = None):
+    def InsertColumns(self, vector, layer, excludeKey = False, type = None, dbInfo = None):
         """!Insert columns for a vector attribute table into the columns combobox
 
         @param vector vector name
         @param layer vector layer number
+        @param excludeKey exclude key column from the list?
         @param type only columns of given type (given as list)
-        @param dbInfo reference to VectorDbInfo instance or None
         """
         if not dbInfo:
             dbInfo = VectorDBInfo(vector)
         
         try:
-            table = dbInfo.layers[int(layer)]['table']
-            columnchoices = dbInfo.tables[table]
+            table = dbInfo.GetTable(int(layer))
+            columnchoices = dbInfo.GetTableDesc(table)
+            keyColumn = dbInfo.GetKeyColumn(int(layer))
             columns = len(columnchoices.keys()) * ['']
             for key, val in columnchoices.iteritems():
                 columns[val['index']] = key
+            if excludeKey: # exclude key column
+                columns.remove(keyColumn)
             if type: # only selected column types
                 for key, value in columnchoices.iteritems():
                     if value['type'] not in type:
@@ -697,45 +723,44 @@
         if self.param:
             self.param['value'] = ''
         
-class DbColumnSelect(wx.ComboBox):
-    """
-    Creates combo box for selecting columns from any table.
-    """
-    def __init__(self, parent,
-                 id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
-                 size=wx.DefaultSize, choices=[''], **kargs):
+class LocationSelect(wx.ComboBox):
+    """!Widget for selecting GRASS location"""
+    def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
+                 gisdbase = None, **kwargs):
+        super(LocationSelect, self).__init__(parent, id, size = size, 
+                                             style = wx.CB_READONLY, **kwargs)
+
+        self.SetName("LocationSelect")
         
-        super(ColumnSelect, self).__init__(parent, id, value, pos, size, choices)
+        if not gisdbase:
+            self.gisdbase = grass.gisenv()['GISDBASE']
+        else:
+            self.gisdbase = gisdbase
 
-        dbtable = kargs['table'] # table to check for columns
-        dbdriver = kargs['driver'] # driver for table
-        dbdatabase = kargs['database'] # database for table
+        self.SetItems(utils.GetListOfLocations(self.gisdbase))
 
-        if dbtable == '':
-            return
+class MapsetSelect(wx.ComboBox):
+    """!Widget for selecting GRASS mapset"""
+    def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
+                 gisdbase = None, location = None, setItems = True, **kwargs):
+        super(MapsetSelect, self).__init__(parent, id, size = size, 
+                                           style = wx.CB_READONLY, **kwargs)
+        
+        self.SetName("MapsetSelect")
+        
+        if not gisdbase:
+            self.gisdbase = grass.gisenv()['GISDBASE']
         else:
-            self.InsertColumns(dbtable)
-                
-    def InsertColumns(self, table):
-        """insert columns for a table into the columns combobox"""
-        if table == '' : return
+            self.gisdbase = gisdbase
         
-        cmd = ['db.columns',
-               'table=%s' % table]
+        if not location:
+            self.location = grass.gisenv()['LOCATION_NAME']
+        else:
+            self.location = location
         
-        if dbdriver:
-            cmd.append('driver=%s' % dbdriver)
-        if dbdatabase:
-            cmd.append('database=%s' % dbdatabase)
-        
-        try:
-            columnchoices = gcmd.Command(cmd).ReadStdOutput()
-        except gcmd.CmdError:
-            columnchoices = []
+        if setItems:
+            self.SetItems(utils.GetListOfMapsets(self.gisdbase, self.location, selectable = True)) # selectable
 
-        # columnchoices.sort()
-        self.SetItems(columnchoices)
-    
 class SubGroupSelect(wx.ComboBox):
     """!Widget for selecting subgroups"""
     def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE, 
@@ -762,4 +787,477 @@
         except OSError:
             self.SetItems([])
         self.SetValue('')
+
+class FormatSelect(wx.Choice):
+    def __init__(self, parent, ogr = False,
+                 sourceType = None, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
+                 **kwargs):
+        """!Widget for selecting external (GDAL/OGR) format
+
+        @param parent parent window
+        @param sourceType source type ('file', 'directory', 'database', 'protocol') or None
+        @param ogr True for OGR otherwise GDAL
+        """
+        super(FormatSelect, self).__init__(parent, id, size = size, 
+                                           style = wx.CB_READONLY, **kwargs)
+        self.SetName("FormatSelect")
         
+        if ogr:
+            ftype = 'ogr'
+        else:
+            ftype = 'gdal'
+        
+        formats = list()
+        for f in utils.GetFormats()[ftype].values():
+            formats += f
+        self.SetItems(formats)
+        
+    def GetExtension(self, name):
+        """!Get file extension by format name"""
+        formatToExt = {
+            # raster
+            'GeoTIFF' : 'tif',
+            'Erdas Imagine Images (.img)' : 'img',
+            'Ground-based SAR Applications Testbed File Format (.gff)' : 'gff',
+            'Arc/Info Binary Grid' : 'adf',
+            'Portable Network Graphics' : 'png',
+            'JPEG JFIF' : 'jpg',
+            'Japanese DEM (.mem)' : 'mem',
+            'Graphics Interchange Format (.gif)' : 'gif',
+            'X11 PixMap Format' : 'xpm',
+            'MS Windows Device Independent Bitmap' : 'bmp',
+            'SPOT DIMAP' : 'dim',
+            'RadarSat 2 XML Product' : 'xml',
+            'EarthWatch .TIL' : 'til',
+            'ERMapper .ers Labelled' : 'ers',
+            'ERMapper Compressed Wavelets' : 'ecw',
+            'GRIdded Binary (.grb)' : 'grb',
+            'EUMETSAT Archive native (.nat)' : 'nat',
+            'Idrisi Raster A.1' : 'rst',
+            'Golden Software ASCII Grid (.grd)' : 'grd',
+            'Golden Software Binary Grid (.grd)' : 'grd',
+            'Golden Software 7 Binary Grid (.grd)' : 'grd',
+            'R Object Data Store' : 'r',
+            'USGS DOQ (Old Style)' : 'doq',
+            'USGS DOQ (New Style)' : 'doq',
+            'ENVI .hdr Labelled' : 'hdr',
+            'ESRI .hdr Labelled' : 'hdr',
+            'Generic Binary (.hdr Labelled)' : 'hdr',
+            'PCI .aux Labelled' : 'aux',
+            'EOSAT FAST Format' : 'fst',
+            'VTP .bt (Binary Terrain) 1.3 Format' : 'bt',
+            'FARSITE v.4 Landscape File (.lcp)' : 'lcp',
+            'Swedish Grid RIK (.rik)' : 'rik',
+            'USGS Optional ASCII DEM (and CDED)' : 'dem',
+            'Northwood Numeric Grid Format .grd/.tab' : '',
+            'Northwood Classified Grid Format .grc/.tab' : '',
+            'ARC Digitized Raster Graphics' : 'arc',
+            'Magellan topo (.blx)' : 'blx',
+            'SAGA GIS Binary Grid (.sdat)' : 'sdat',
+            # vector
+            'ESRI Shapefile' : 'shp',
+            'UK .NTF'        : 'ntf',
+            'SDTS'           : 'ddf',
+            'DGN'            : 'dgn',
+            'VRT'            : 'vrt',
+            'REC'            : 'rec',
+            'BNA'            : 'bna',
+            'CSV'            : 'csv',
+            'GML'            : 'gml',
+            'GPX'            : 'gpx',
+            'KML'            : 'kml',
+            'GMT'            : 'gmt',
+            'PGeo'           : 'mdb',
+            'XPlane'         : 'dat',
+            'AVCBin'         : 'adf',
+            'AVCE00'         : 'e00',
+            'DXF'            : 'dxf',
+            'Geoconcept'     : 'gxt',
+            'GeoRSS'         : 'xml',
+            'GPSTrackMaker'  : 'gtm',
+            'VFK'            : 'vfk'
+            }
+        
+        try:
+            return formatToExt[name]
+        except KeyError:
+            return ''
+        
+class GdalSelect(wx.Panel):
+    def __init__(self, parent, panel, ogr = False,
+                 default = 'file',
+                 exclude = [],
+                 envHandler = None):
+        """!Widget for selecting GDAL/OGR datasource, format
+        
+        @param parent parent window
+        @param ogr    use OGR selector instead of GDAL
+        """
+        self.parent = parent
+        self.ogr    = ogr
+        wx.Panel.__init__(self, parent = panel, id = wx.ID_ANY)
+        
+        self.inputBox = wx.StaticBox(parent = self, id=wx.ID_ANY,
+                                     label=" %s " % _("Source name"))
+        
+        # source type
+        sources = list()
+        self.sourceMap = { 'file' : -1,
+                           'dir'  : -1,
+                           'db'   : -1,
+                           'pro'  : -1 }
+        idx = 0
+        if 'file' not in exclude:
+            sources.append(_("File"))
+            self.sourceMap['file'] = idx
+            idx += 1
+        if 'directory' not in exclude:
+            sources.append(_("Directory"))
+            self.sourceMap['dir'] = idx
+            idx += 1
+        if 'database' not in exclude:
+            sources.append(_("Database"))
+            self.sourceMap['db'] = idx
+            idx += 1
+        if 'protocol' not in exclude:
+            sources.append(_("Protocol"))
+            self.sourceMap['pro'] = idx
+        
+        self.source = wx.RadioBox(parent = self, id = wx.ID_ANY,
+                                  label = _('Source type'),
+                                  style = wx.RA_SPECIFY_COLS,
+                                  choices = sources)
+        self.source.SetSelection(0)
+        self.source.Bind(wx.EVT_RADIOBOX, self.OnSetType)
+        
+        # dsn widgets
+        if not ogr:
+            filemask = 'GeoTIFF (*.tif)|*.tif'
+        else:
+            filemask = 'ESRI Shapefile (*.shp)|*.shp'
+        
+        dsnFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
+                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                              dialogTitle=_('Choose input file'),
+                                              buttonText=_('Browse'),
+                                              startDirectory=os.getcwd(),
+                                              changeCallback=self.OnSetDsn,
+                                              fileMask=filemask)
+        dsnFile.Hide()
+        
+        dsnDir = filebrowse.DirBrowseButton(parent=self, id=wx.ID_ANY, 
+                                            size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                            dialogTitle=_('Choose input directory'),
+                                            buttonText=_('Browse'),
+                                            startDirectory=os.getcwd(),
+                                            changeCallback=self.OnSetDsn)
+        dsnDir.SetName('GdalSelect')
+        dsnDir.Hide()
+        
+        dsnDbFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
+                                                size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                                  dialogTitle=_('Choose file'),
+                                                buttonText=_('Browse'),
+                                                startDirectory=os.getcwd(),
+                                                changeCallback=self.OnSetDsn)
+        dsnDbFile.Hide()
+        dsnDbFile.SetName('GdalSelect')
+
+        dsnDbText = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+        dsnDbText.Hide()
+        dsnDbText.Bind(wx.EVT_TEXT, self.OnSetDsn)
+        dsnDbText.SetName('GdalSelect')
+
+        dsnDbChoice = wx.Choice(parent = self, id = wx.ID_ANY)
+        dsnDbChoice.Hide()
+        dsnDbChoice.Bind(wx.EVT_CHOICE, self.OnSetDsn)
+        dsnDbChoice.SetName('GdalSelect')
+
+        dsnPro = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+        dsnPro.Hide()
+        dsnPro.Bind(wx.EVT_TEXT, self.OnSetDsn)
+        dsnPro.SetName('GdalSelect')
+
+        # format
+        self.format = FormatSelect(parent = self,
+                                   ogr = ogr)
+        self.format.Bind(wx.EVT_CHOICE, self.OnSetFormat)
+        
+        if ogr:
+            fType = 'ogr'
+        else:
+            fType = 'gdal'
+        self.input = { 'file' : [_("File:"),
+                                 dsnFile,
+                                 utils.GetFormats()[fType]['file']],
+                       'dir'  : [_("Directory:"),
+                                 dsnDir,
+                                 utils.GetFormats()[fType]['file']],
+                       'db'   : [_("Database:"),
+                                 dsnDbFile,
+                                 utils.GetFormats()[fType]['database']],
+                       'pro'  : [_("Protocol:"),
+                                 dsnPro,
+                                 utils.GetFormats()[fType]['protocol']],
+                       'db-win' : { 'file'   : dsnDbFile,
+                                    'text'   : dsnDbText,
+                                    'choice' : dsnDbChoice },
+                       }
+        
+        self.dsnType = default
+        self.input[self.dsnType][1].Show()
+        self.format.SetItems(self.input[self.dsnType][2])
+        
+        if not ogr:
+            self.format.SetStringSelection('GeoTIFF')
+        else:
+            self.format.SetStringSelection('ESRI Shapefile')
+        
+        self.dsnText = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                     label = self.input[self.dsnType][0],
+                                     size = (75, -1))
+        self.formatText = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                        label = _("Format:"))
+        self._layout()
+        
+    def _layout(self):
+        """!Layout"""
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
+        
+        self.dsnSizer = wx.GridBagSizer(vgap=3, hgap=3)
+        self.dsnSizer.AddGrowableRow(1)
+        self.dsnSizer.AddGrowableCol(1)
+        
+        self.dsnSizer.Add(item=self.dsnText,
+                          flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos = (0, 0))
+        self.dsnSizer.Add(item=self.input[self.dsnType][1],
+                          flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                          pos = (0, 1))
+        
+        self.dsnSizer.Add(item=self.formatText,
+                          flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 0))
+        self.dsnSizer.Add(item=self.format,
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 1))
+        
+        inputSizer.Add(item=self.dsnSizer, proportion=1,
+                       flag=wx.EXPAND | wx.ALL)
+        
+        mainSizer.Add(item=self.source, proportion=0,
+                      flag=wx.ALL | wx.EXPAND, border=5)
+        mainSizer.Add(item=inputSizer, proportion=0,
+                      flag=wx.ALL | wx.EXPAND, border=5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def OnSetType(self, event):
+        """!Datasource type changed"""
+        sel = event.GetSelection()
+        win = self.input[self.dsnType][1]
+        self.dsnSizer.Remove(win)
+        win.Hide()
+        
+        if sel == self.sourceMap['file']:   # file
+            self.dsnType = 'file'
+            format = self.input[self.dsnType][2][0]
+            try:
+                ext = self.format.GetExtension(format)
+                if not ext:
+                    raise KeyError
+                format += ' (*.%s)|*.%s' % (ext, ext)
+            except KeyError:
+                format += ' (*.*)|*.*'
+            
+            win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
+                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                              dialogTitle=_('Choose input file'),
+                                              buttonText=_('Browse'),
+                                              startDirectory=os.getcwd(),
+                                              changeCallback=self.OnSetDsn,
+                                              fileMask = format)
+            self.input[self.dsnType][1] = win
+        elif sel == self.sourceMap['dir']: # directory
+            self.dsnType = 'dir'
+        elif sel == self.sourceMap['db']: # database
+            self.dsnType = 'db'
+        elif sel == self.sourceMap['pro']: # protocol
+            self.dsnType = 'pro'
+        
+        self.dsnText.SetLabel(self.input[self.dsnType][0])
+        if self.parent.GetName() == 'MultiImportDialog':
+            self.parent.list.DeleteAllItems()
+        self.format.SetItems(self.input[self.dsnType][2])        
+
+        if sel in (self.sourceMap['file'],
+                   self.sourceMap['dir']):
+            win = self.input[self.dsnType][1]
+            self.dsnSizer.Add(item=self.input[self.dsnType][1],
+                              flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                              pos = (0, 1))
+            win.SetValue('')
+            win.Show()
+            
+            if not self.ogr:
+                self.format.SetStringSelection('GeoTIFF')
+            else:
+                self.format.SetStringSelection('ESRI Shapefile')
+        elif sel == self.sourceMap['pro']:
+            win = self.input[self.dsnType][1]
+            self.dsnSizer.Add(item=self.input[self.dsnType][1],
+                              flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                              pos = (0, 1))
+            win.SetValue('')
+            win.Show()
+        
+        self.dsnSizer.Layout()
+        
+    def OnSetDsn(self, event):
+        """!Input DXF file/OGR dsn defined, update list of layer widget"""
+        path = event.GetString()
+        if not path:
+            return 
+        
+        data = list()        
+        
+        layerId = 1
+        if self.format.GetStringSelection() == 'PostgreSQL':
+            dsn = 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
+        else:
+            dsn = self.input[self.dsnType][1].GetValue()
+        if self.dsnType == 'file':
+            baseName = os.path.basename(dsn)
+            grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
+            data.append((layerId, baseName, grassName))
+        elif self.dsnType == 'dir':
+            try:
+                ext = self.format.GetExtension(self.format.GetStringSelection())
+            except KeyError:
+                ext = ''
+            for file in glob.glob(os.path.join(dsn, "*.%s") % ext):
+                baseName = os.path.basename(file)
+                grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
+                data.append((layerId, baseName, grassName))
+                layerId += 1
+        elif self.dsnType == 'db':
+            ret = gcmd.RunCommand('v.in.ogr',
+                                  quiet = True,
+                                  parent = self,
+                                  read = True,
+                                  flags = 'l',
+                                  dsn = dsn)
+            if not ret:
+                self.list.LoadData()
+                self.btn_run.Enable(False)
+                return
+            layerId = 1
+            for line in ret.splitlines():
+                layerName = line.strip()
+                grassName = utils.GetValidLayerName(layerName)
+                data.append((layerId, layerName.strip(), grassName.strip()))
+                layerId += 1
+        
+        evt = wxGdalSelect(dsn = dsn + '@OGR')
+        evt.SetId(self.input[self.dsnType][1].GetId())
+        wx.PostEvent(self.parent, evt)
+        
+        if self.parent.GetName() == 'MultiImportDialog':
+            self.parent.list.LoadData(data)
+            if len(data) > 0:
+                self.parent.btn_run.Enable(True)
+            else:
+                self.parent.btn_run.Enable(False)
+        
+        event.Skip()
+        
+    def OnSetFormat(self, event):
+        """!Format changed"""
+        if self.dsnType not in ['file', 'db']:
+            return
+        
+        win = self.input[self.dsnType][1]
+        self.dsnSizer.Remove(win)
+        
+        if self.dsnType == 'file':
+            win.Destroy()
+        else: # database
+            win.Hide()
+        
+        format = event.GetString()
+        
+        if self.dsnType == 'file':
+            try:
+                ext = self.format.GetExtension(format)
+                if not ext:
+                    raise KeyError
+                format += ' (*.%s)|*.%s' % (ext, ext)
+            except KeyError:
+                format += ' (*.*)|*.*'
+            
+            win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
+                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                              dialogTitle=_('Choose file'),
+                                              buttonText=_('Browse'),
+                                              startDirectory=os.getcwd(),
+                                              changeCallback=self.OnSetDsn,
+                                              fileMask = format)
+        else: # database
+            if format == 'SQLite' or format == 'Rasterlite':
+                win = self.input['db-win']['file']
+            elif format == 'PostgreSQL' or format == 'PostGIS WKT Raster driver':
+                if grass.find_program('psql'):
+                    win = self.input['db-win']['choice']
+                    if not win.GetItems():
+                        p = grass.Popen(['psql', '-ltA'], stdout = grass.PIPE)
+                        ret = p.communicate()[0]
+                        if ret:
+                            db = list()
+                            for line in ret.splitlines():
+                                sline = line.split('|')
+                                if len(sline) < 2:
+                                    continue
+                                dbname = sline[0]
+                                if dbname:
+                                    db.append(dbname)
+                            win.SetItems(db)
+                else:
+                    win = self.input['db-win']['text']
+            else:
+                win = self.input['db-win']['text']
+        
+        self.input[self.dsnType][1] = win
+        if not win.IsShown():
+            win.Show()
+        self.dsnSizer.Add(item=self.input[self.dsnType][1],
+                          flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                          pos = (0, 1))
+        self.dsnSizer.Layout()
+
+    def GetType(self):
+        """!Get source type"""
+        return self.dsnType
+
+    def GetDsn(self):
+        """!Get DSN"""
+        if self.format.GetStringSelection() == 'PostgreSQL':
+            return 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
+        
+        return self.input[self.dsnType][1].GetValue()
+
+    def GetDsnWin(self):
+        """!Get list of DSN windows"""
+        win = list()
+        for stype in ('file', 'dir', 'pro'):
+            win.append(self.input[stype][1])
+        for stype in ('file', 'text', 'choice'):
+            win.append(self.input['db-win'][stype])
+        
+        return win
+    
+    def GetFormatExt(self):
+        """!Get format extension"""
+        return self.format.GetExtension(self.format.GetStringSelection())
+    

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/histogram.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/histogram.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/histogram.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,73 +1,54 @@
-"""
-MODULE: histogram
+"""!
+ at package histogram.py
 
-CLASSES:
-    * BufferedWindow
-    * HistFrame
+Plotting histogram
 
-PURPOSE: Plotting histogram
+Classes:
+ - BufferedWindow
+ - HistFrame
 
-AUTHORS: The GRASS Development Team
-         Michael Barton
+COPYRIGHT: (C) 2007, 2010 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.
 
-COPYRIGHT: (C) 2007 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 Michael Barton
+ at author Various updates by Martin Landa
 """
 
+import os
+import sys
+
 import wx
-import wx.aui
-import os, sys, time, glob, math
-from threading import Thread
 
-import globalvar
-try:
-    import subprocess
-except:
-    CompatPath = os.path.join(globalvar.ETCWXDIR)
-    sys.path.append(CompatPath)
-    from compat import subprocess
-
-gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
-sys.path.append(gmpath)
-
 import render
 import menuform
 import disp_print
 import utils
-from gui_modules.preferences import DefaultFontDialog as DefaultFontDialog
-from debug import Debug as Debug
-from icon import Icons as Icons
+import gdialogs
+import globalvar
+from toolbars import HistogramToolbar
+from preferences import DefaultFontDialog
+from debug import Debug
+from icon import Icons
 
-import images
-imagepath = images.__path__[0]
-sys.path.append(imagepath)
-
-os.environ["GRASS_BACKGROUNDCOLOR"] = "blue"
-
 class BufferedWindow(wx.Window):
-    """
-    A Buffered window class.
+    """!A Buffered window class.
 
     When the drawing needs to change, you app needs to call the
     UpdateHist() method. Since the drawing is stored in a bitmap, you
     can also save the drawing to file by calling the
     SaveToFile(self,file_name,file_type) method.
     """
-
-    def __init__(self, parent, id,
-                 pos = wx.DefaultPosition,
-                 size = wx.DefaultSize,
-                 style=wx.NO_FULL_REPAINT_ON_RESIZE,
-                 Map=None):
-
-        wx.Window.__init__(self, parent, id, pos, size, style)
-
+    def __init__(self, parent, id =  wx.ID_ANY,
+                 style = wx.NO_FULL_REPAINT_ON_RESIZE,
+                 Map = None, **kwargs):
+        
+        wx.Window.__init__(self, parent, id = id, style = style, **kwargs)
+        
         self.parent = parent
         self.Map = Map
         self.mapname = self.parent.mapname
-
+        
         #
         # Flags
         #
@@ -75,36 +56,34 @@
         self.resize = False # indicates whether or not a resize event has taken place
         self.dragimg = None # initialize variable for map panning
         self.pen = None     # pen for drawing zoom boxes, etc.
-
+        
         #
         # Event bindings
         #
         self.Bind(wx.EVT_PAINT,        self.OnPaint)
         self.Bind(wx.EVT_SIZE,         self.OnSize)
         self.Bind(wx.EVT_IDLE,         self.OnIdle)
-
+        
         #
         # Render output objects
         #
         self.mapfile = None # image file to be rendered
         self.img = ""       # wx.Image object (self.mapfile)
-
+        
         self.imagedict = {} # images and their PseudoDC ID's for painting and dragging
-
+        
         self.pdc = wx.PseudoDC()
-        self._Buffer = '' # will store an off screen empty bitmap for saving to file
-
+        self._buffer = '' # will store an off screen empty bitmap for saving to file
+        
         # make sure that extents are updated at init
         self.Map.region = self.Map.GetRegion()
         self.Map.SetRegion() 
-
+        
         self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
-
-    def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0,0,0,0]):
+        
+    def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0,0,0,0]):
+        """!Draws histogram or clears window
         """
-        Draws histogram or clears window
-        """
-
         if drawid == None:
             if pdctype == 'image' :
                 drawid = imagedict[img]
@@ -114,11 +93,11 @@
                 drawid = wx.NewId()
         else:
             pdc.SetId(drawid)
-
+        
         pdc.BeginDrawing()
-
+        
         Debug.msg (3, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % (drawid, pdctype, coords))
-
+        
         if pdctype == 'clear': # erase the display
             bg = wx.WHITE_BRUSH
             pdc.SetBackground(bg)
@@ -126,7 +105,7 @@
             self.Refresh()
             pdc.EndDrawing()
             return
-
+        
         if pdctype == 'image':
             bg = wx.TRANSPARENT_BRUSH
             pdc.SetBackground(bg)
@@ -134,17 +113,15 @@
             w,h = bitmap.GetSize()
             pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
             pdc.SetIdBounds(drawid, (coords[0],coords[1],w,h))
-
+        
         pdc.EndDrawing()
         self.Refresh()
-
+        
     def OnPaint(self, event):
+        """!Draw psuedo DC to buffer
         """
-        Draw psuedo DC to buffer
-        """
-
-        dc = wx.BufferedPaintDC(self, self._Buffer)
-
+        dc = wx.BufferedPaintDC(self, self._buffer)
+        
         # use PrepareDC to set position correctly
         self.PrepareDC(dc)
         # we need to clear the dc BEFORE calling PrepareDC
@@ -157,80 +134,83 @@
         r = rgn.GetBox()
         # draw to the dc using the calculated clipping rect
         self.pdc.DrawToDCClipped(dc,r)
-
-
+        
     def OnSize(self, event):
+        """!Init image size to match window size
         """
-         Init image size to match window size
-        """
-
-            # set size of the input image
+        # set size of the input image
         self.Map.width, self.Map.height = self.GetClientSize()
-
+        
         # Make new off screen bitmap: this bitmap will always have the
         # current drawing in it, so it can be used to save the image to
         # a file, or whatever.
-        self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
-
+        self._buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
+        
         # get the image to be rendered
         self.img = self.GetImage()
-
+        
         # update map display
         if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
             self.img = self.img.Scale(self.Map.width, self.Map.height)
             self.render = False
             self.UpdateHist()
-
+        
         # re-render image on idle
         self.resize = True
-
+        
     def OnIdle(self, event):
+        """!Only re-render a histogram image from GRASS during idle
+        time instead of multiple times during resizing.
         """
-        Only re-render a histogram image from GRASS during
-        idle time instead of multiple times during resizing.
-            """
-
         if self.resize:
             self.render = True
             self.UpdateHist()
         event.Skip()
-
-    def SaveToFile(self, FileName, FileType):
+        
+    def SaveToFile(self, FileName, FileType, width, height):
+        """!This will save the contents of the buffer to the specified
+        file. See the wx.Windows docs for wx.Bitmap::SaveFile for the
+        details
         """
-        This will save the contents of the buffer
-        to the specified file. See the wx.Windows docs for
-        wx.Bitmap::SaveFile for the details
-        """
-        dc = wx.BufferedPaintDC(self, self._Buffer)
+        busy = wx.BusyInfo(message=_("Please wait, exporting image..."),
+                           parent=self)
+        wx.Yield()
+        
+        self.Map.ChangeMapSize((width, height))
+        ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
+        self.Map.Render(force=True, windres = True)
+        img = self.GetImage()
+        self.Draw(self.pdc, img, drawid = 99)
+        dc = wx.BufferedPaintDC(self, ibuffer)
+        dc.Clear()
+        self.PrepareDC(dc)
         self.pdc.DrawToDC(dc)
-        self._Buffer.SaveFile(FileName, FileType)
-
+        ibuffer.SaveFile(FileName, FileType)
+        
+        busy.Destroy()
+        
     def GetImage(self):
+        """!Converts files to wx.Image
         """
-        Converts files to wx.Image
-        """
         if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
                 os.path.getsize(self.Map.mapfile):
             img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
         else:
             img = None
-
+        
         self.imagedict[img] = 99 # set image PeudoDC ID
         return img
-
-
-    def UpdateHist(self, img=None):
+    
+    def UpdateHist(self, img = None):
+        """!Update canvas if histogram options changes or window
+        changes geometry
         """
-        Update canvas if histogram options changes or window changes geometry
-        """
-
         Debug.msg (2, "BufferedWindow.UpdateHist(%s): render=%s" % (img, self.render))
         oldfont = ""
         oldencoding = ""
-
+        
         if self.render:
             # render new map images
-
             # set default font and encoding environmental variables
             if "GRASS_FONT" in os.environ:
                 oldfont = os.environ["GRASS_FONT"]
@@ -241,297 +221,230 @@
                 os.environ[GRASS_ENCODING] = self.parent.encoding
             
             # using active comp region
-            self.Map.GetRegion(update=True)
+            self.Map.GetRegion(update = True)
             
             self.Map.width, self.Map.height = self.GetClientSize()
-            self.mapfile = self.Map.Render(force=self.render)
+            self.mapfile = self.Map.Render(force = self.render)
             self.img = self.GetImage()
             self.resize = False
-
+        
         if not self.img: return
         try:
             id = self.imagedict[self.img]
         except:
             return
-
+        
         # paint images to PseudoDC
         self.pdc.Clear()
         self.pdc.RemoveAll()
-        self.Draw(self.pdc, self.img, drawid=id) # draw map image background
-
+        self.Draw(self.pdc, self.img, drawid = id) # draw map image background
+        
         self.resize = False
-
+        
         # update statusbar
         # Debug.msg (3, "BufferedWindow.UpdateHist(%s): region=%s" % self.Map.region)
         self.Map.SetRegion()
         self.parent.statusbar.SetStatusText("Raster/Image map layer <%s>" % self.parent.mapname)
-
+        
         # set default font and encoding environmental variables
         if oldfont != "":
             os.environ["GRASS_FONT"] = oldfont
         if oldencoding != "":
             os.environ["GRASS_ENCODING"] = oldencoding
-
+        
     def EraseMap(self):
+        """!Erase the map display
         """
-        Erase the map display
-        """
-        self.Draw(self.pdc, pdctype='clear')
-
+        self.Draw(self.pdc, pdctype = 'clear')
+        
 class HistFrame(wx.Frame):
+    """!Main frame for hisgram display window.  Uses d.histogram
+    rendered onto canvas
     """
-    Main frame for hisgram display window.
-    Uses d.histogram rendered onto canvas
-    """
-
-    def __init__(self, parent=None, id = wx.ID_ANY,
-                 title= _("GRASS GIS Histogram of image or raster map"),
-                 pos=wx.DefaultPosition, size=wx.DefaultSize,
-                 style=wx.DEFAULT_FRAME_STYLE):
-
-        wx.Frame.__init__(self, parent, id, title, pos, size, style)
+    def __init__(self, parent = None, id = wx.ID_ANY,
+                 title = _("GRASS GIS Histogram of image or raster map"),
+                 style = wx.DEFAULT_FRAME_STYLE, **kwargs):
+        wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
-        toolbar = self.__createToolBar()
-        
         self.Map   = render.Map()  # instance of render.Map to be associated with display
         self.layer = None          # reference to layer with histogram
-        #
-        # Set the size & cursor
-        #
-        self.SetClientSize(size)
         
         # Init variables
         self.params = {}  # previously set histogram parameters
         self.propwin = '' # ID of properties dialog
-
+        
         self.font = ""
         self.encoding = 'ISO-8859-1' # default encoding for display fonts
+        
+        self.toolbar = HistogramToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
 
-        #
         # Add statusbar
-        #
         self.mapname = ''
-        self.statusbar = self.CreateStatusBar(number=1, style=0)
+        self.statusbar = self.CreateStatusBar(number = 1, style = 0)
         # self.statusbar.SetStatusWidths([-2, -1])
         hist_frame_statusbar_fields = ["Histogramming %s" % self.mapname]
         for i in range(len(hist_frame_statusbar_fields)):
             self.statusbar.SetStatusText(hist_frame_statusbar_fields[i], i)
-
-        #
+        
         # Init map display
-        #
         self.InitDisplay() # initialize region values
-
+        
         # initialize buffered DC
-        self.HistWindow = BufferedWindow(self, id = wx.ID_ANY, Map=self.Map) # initialize buffered DC
-
-        #
+        self.HistWindow = BufferedWindow(self, id = wx.ID_ANY, Map = self.Map) # initialize buffered DC
+        
         # Bind various events
-        #
         self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
-
-        #
+        
         # Init print module and classes
-        #
         self.printopt = disp_print.PrintOptions(self, self.HistWindow)
-
-        #
+        
         # Add layer to the map
-        #
-        self.layer = self.Map.AddLayer(type="command", name='histogram', command=['d.histogram'],
-                                       l_active=False, l_hidden=False, l_opacity=1, l_render=False)
-
-
-    def __createToolBar(self):
-        """Creates toolbar"""
-        toolbar = self.CreateToolBar()
-        toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-        for each in self.toolbarData():
-            self.AddToolbarButton(toolbar, *each)
-        toolbar.Realize()
+        self.layer = self.Map.AddLayer(type = "command", name = 'histogram', command = ['d.histogram'],
+                                       l_active = False, l_hidden = False, l_opacity = 1, l_render = False)
         
-        return toolbar
-    
-    def AddToolbarButton(self, toolbar, label, icon, help, handler):
-        """Adds buttons to the toolbar"""
-
-        if not label:
-            toolbar.AddSeparator()
-            return
-        tool = toolbar.AddLabelTool(id=wx.ID_ANY, label=label, bitmap=icon, shortHelp=help)
-        self.Bind(wx.EVT_TOOL, handler, tool)
-
-    def toolbarData(self):
-
-        return   (
-                 ('histogram',
-                  Icons["histogram"].GetBitmap(),
-                  Icons["histogram"].GetLabel(),
-                  self.OnOptions),
-                 ('rendermap',
-                  Icons["displaymap"].GetBitmap(),
-                  Icons["displaymap"].GetLabel(),
-                  self.OnRender),
-                 ('erase',
-                  Icons["erase"].GetBitmap(),
-                  Icons["erase"].GetLabel(),
-                  self.OnErase),
-                 ('font',
-                  Icons["font"].GetBitmap(),
-                  Icons["font"].GetLabel(),
-                  self.SetHistFont),
-                 ('', '', '', ''),
-                 ('save', 
-                  Icons["savefile"].GetBitmap(),
-                  Icons["savefile"].GetLabel(),
-                  self.SaveToFile),
-                 ('print',
-                  Icons["printmap"].GetBitmap(),
-                  Icons["printmap"].GetLabel(),
-                  self.PrintMenu),
-                 ('quit', 
-                  Icons["quit"].GetBitmap(),
-                  Icons["quit"].GetLabel(),
-                  self.OnQuit))
-
     def InitDisplay(self):
+        """!Initialize histogram display, set dimensions and region
         """
-        Initialize histogram display, set dimensions and region
-        """
         self.width, self.height = self.GetClientSize()
         self.Map.geom = self.width, self.height
-
+        
     def OnOptions(self, event):
-        """Change histogram settings"""
-
+        """!Change histogram settings"""
         cmd = ['d.histogram']
         if self.mapname != '':
             cmd.append('map=%s' % self.mapname)
-
+        
         menuform.GUI().ParseCommand(cmd,
-                                    completed=(self.GetOptData, None, self.params),
-                                    parentframe=self)
+                                    completed = (self.GetOptData, None, self.params),
+                                    parentframe = self)
         
     def GetOptData(self, dcmd, layer, params, propwin):
+        """!Callback method for histogram command generated by dialog
+        created in menuform.py
         """
-        Callback method for histogram command generated by
-        dialog created in menuform.py
-        """
         if dcmd:
-            name = utils.GetLayerNameFromCmd(dcmd, fullyQualified=True)
+            name = utils.GetLayerNameFromCmd(dcmd, fullyQualified = True)
             self.SetHistLayer(name)
         self.params = params
         self.propwin = propwin
-
+        
         self.HistWindow.UpdateHist()
-
+        
     def SetHistLayer(self, name):
+        """!Set histogram layer
         """
-        Set histogram layer
-        """
         self.mapname = name
-
-        self.layer = self.Map.ChangeLayer(layer=self.layer,
-                                          command=[['d.histogram', 'map=%s' % self.mapname],],
-                                          active=True)
-
+        
+        self.layer = self.Map.ChangeLayer(layer = self.layer,
+                                          command = [['d.histogram', 'map=%s' % self.mapname],],
+                                          active = True)
+        
         return self.layer
 
     def SetHistFont(self, event):
+        """!Set font for histogram. If not set, font will be default
+        display font.
         """
-        Set font for histogram. If not
-        set, font will be default display font.
-        """
-
-        dlg = DefaultFontDialog(parent=self, id=wx.ID_ANY,
-                                title=_('Select font for histogram text'))        
+        dlg = DefaultFontDialog(parent = self, id = wx.ID_ANY,
+                                title = _('Select font for histogram text'))        
         dlg.fontlb.SetStringSelection(self.font, True)
         
         if dlg.ShowModal() == wx.ID_CANCEL:
             dlg.Destroy()
             return
-
+        
         # set default font type, font, and encoding to whatever selected in dialog
         if dlg.font != None:
             self.font = dlg.font
         if dlg.encoding != None:
             self.encoding = dlg.encoding
-
+        
         dlg.Destroy()
         self.HistWindow.UpdateHist()
 
     def OnErase(self, event):
+        """!Erase the histogram display
         """
-        Erase the histogram display
-        """
-        self.HistWindow.Draw(self.HistWindow.pdc, pdctype='clear')
-
+        self.HistWindow.Draw(self.HistWindow.pdc, pdctype = 'clear')
+        
     def OnRender(self, event):
+        """!Re-render histogram
         """
-        Re-render histogram
-        """
         self.HistWindow.UpdateHist()
-
+        
+    def GetWindow(self):
+        """!Get buffered window"""
+        return self.HistWindow
+    
     def SaveToFile(self, event):
+        """!Save to file
         """
-        Save to file
-        """
-        filetype =  "PNG file (*.png)|*.png|"\
-                    "TIF file (*.tif)|*.tif|"\
-                    "GIF file (*.gif)|*.gif"
-
-        dlg = wx.FileDialog(self, "Choose a file name to save the image as a PNG to",
-            defaultDir = "",
-            defaultFile = "",
-            wildcard = filetype,
-            style=wx.SAVE|wx.FD_OVERWRITE_PROMPT)
+        filetype, ltype = gdialogs.GetImageHandlers(self.HistWindow.img)
+        
+        # get size
+        dlg = gdialogs.ImageSizeDialog(self)
+        dlg.CentreOnParent()
+        if dlg.ShowModal() != wx.ID_OK:
+            dlg.Destroy()
+            return
+        width, height = dlg.GetValues()
+        dlg.Destroy()
+        
+        # get filename
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose a file name to save the image "
+                                        "(no need to add extension)"),
+                            wildcard = filetype,
+                            style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+        
         if dlg.ShowModal() == wx.ID_OK:
-            base = os.path.splitext(dlg.GetPath())[0]
-            ext = os.path.splitext(dlg.GetPath())[1]
-            if dlg.GetFilterIndex() == 0:
-                type = wx.BITMAP_TYPE_PNG
-                path = dlg.GetPath()
-                if ext != '.png': path = base+'.png'
-            elif dlg.GetFilterIndex() == 1:
-                type = wx.BITMAP_TYPE_TIF
-                if ext != '.tif': path = base+'.tif'
-            elif dlg.GetFilterIndex() == 2:
-                type = wx.BITMAP_TYPE_TIF
-                if ext != '.gif': path = base+'.gif'
-            self.HistWindow.SaveToFile(path, type)
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            base, ext = os.path.splitext(path)
+            fileType = ltype[dlg.GetFilterIndex()]['type']
+            extType  = ltype[dlg.GetFilterIndex()]['ext']
+            if ext != extType:
+                path = base + '.' + extType
+            
+            self.HistWindow.SaveToFile(path, fileType,
+                                       width, height)
+        
+        self.HistWindow.UpdateHist()
         dlg.Destroy()
-
+        
     def PrintMenu(self, event):
+        """!Print options and output menu
         """
-        Print options and output menu
-        """
         point = wx.GetMousePosition()
         printmenu = wx.Menu()
         # Add items to the menu
-        setup = wx.MenuItem(printmenu, -1,'Page setup')
+        setup = wx.MenuItem(printmenu, id = wx.ID_ANY, text = _('Page setup'))
         printmenu.AppendItem(setup)
         self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
-
-        preview = wx.MenuItem(printmenu, -1,'Print preview')
+        
+        preview = wx.MenuItem(printmenu, id = wx.ID_ANY, text = _('Print preview'))
         printmenu.AppendItem(preview)
         self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
-
-        doprint = wx.MenuItem(printmenu, -1,'Print display')
+        
+        doprint = wx.MenuItem(printmenu, id = wx.ID_ANY, text = _('Print display'))
         printmenu.AppendItem(doprint)
         self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
-
+        
         # Popup the menu.  If an item is selected then its handler
         # will be called before PopupMenu returns.
         self.PopupMenu(printmenu)
         printmenu.Destroy()
-
+        
     def OnQuit(self, event):
         self.Close(True)
-
+        
     def OnCloseWindow(self, event):
-        """
-        Window closed
+        """!Window closed
         Also remove associated rendered images
         """
         try:
@@ -540,5 +453,4 @@
             pass
         self.Map.Clean()
         self.Destroy()
-
-
+        

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/layertree.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/layertree.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/layertree.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,1505 @@
+"""!
+ at package layertree.py
+
+ at brief Utility classes for map layer management.
+
+Classes:
+ - AbstractLayer
+ - Layer
+ - LayerTree
+
+(C) 2007-2010 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 Michael Barton (Arizona State University)
+ at author Jachym Cepicky (Mendel University of Agriculture)
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import string
+
+import wx
+try:
+    import wx.lib.agw.customtreectrl as CT
+except ImportError:
+    import wx.lib.customtreectrl as CT
+import wx.combo
+import wx.lib.newevent
+import wx.lib.buttons  as  buttons
+
+import globalvar
+
+from grass.script import core as grass
+
+import gdialogs
+import menuform
+import toolbars
+import mapdisp
+import render
+import histogram
+import utils
+import profile
+from debug import Debug as Debug
+from icon import Icons as Icons
+from preferences import globalSettings as UserSettings
+from vdigit import haveVDigit
+from gcmd import GError
+try:
+    import subprocess
+except:
+    from compat import subprocess
+    
+try:
+    import treemixin 
+except ImportError:
+    from wx.lib.mixins import treemixin
+
+TREE_ITEM_HEIGHT = 25
+
+class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
+    """
+    Creates layer tree structure
+    """
+    def __init__(self, parent,
+                 id = wx.ID_ANY, style=wx.SUNKEN_BORDER,
+                 ctstyle=CT.TR_HAS_BUTTONS | CT.TR_HAS_VARIABLE_ROW_HEIGHT |
+                 CT.TR_HIDE_ROOT | CT.TR_ROW_LINES | CT.TR_FULL_ROW_HIGHLIGHT |
+                 CT.TR_MULTIPLE, **kwargs):
+        
+        if 'style' in kwargs:
+            ctstyle |= kwargs['style']
+            del kwargs['style']
+        self.disp_idx = kwargs['idx']
+        del kwargs['idx']
+        self.lmgr = kwargs['lmgr']
+        del kwargs['lmgr']
+        self.notebook = kwargs['notebook']   # GIS Manager notebook for layer tree
+        del kwargs['notebook']
+        self.auimgr = kwargs['auimgr']       # aui manager
+        del kwargs['auimgr']
+        showMapDisplay = kwargs['showMapDisplay']
+        del kwargs['showMapDisplay']
+        self.treepg = parent                 # notebook page holding layer tree
+        self.Map = render.Map()              # instance of render.Map to be associated with display
+        self.root = None                     # ID of layer tree root node
+        self.groupnode = 0                   # index value for layers
+        self.optpage = {}                    # dictionary of notebook option pages for each map layer
+        self.layer_selected = None           # ID of currently selected layer
+        self.saveitem = {}                   # dictionary to preserve layer attributes for drag and drop
+        self.first = True                    # indicates if a layer is just added or not
+        self.flag = ''                       # flag for drag and drop hittest
+        self.rerender = False                # layer change requires a rerendering if auto render
+        self.reorder = False                 # layer change requires a reordering
+        
+        if globalvar.hasAgw:
+            super(LayerTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs)
+        else:
+            super(LayerTree, self).__init__(parent, id, style = ctstyle, **kwargs)
+        self.SetName("LayerTree")
+        
+        ### SetAutoLayout() causes that no vertical scrollbar is displayed
+        ### when some layers are not visible in layer tree
+        # self.SetAutoLayout(True)
+        self.SetGradientStyle(1)
+        self.EnableSelectionGradient(True)
+        self.SetFirstGradientColour(wx.Colour(100, 100, 100))
+        self.SetSecondGradientColour(wx.Colour(150, 150, 150))
+        
+        # init associated map display
+        pos = wx.Point((self.disp_idx + 1) * 25, (self.disp_idx + 1) * 25)
+        self.mapdisplay = mapdisp.MapFrame(self,
+                                           id=wx.ID_ANY, pos=pos,
+                                           size=globalvar.MAP_WINDOW_SIZE,
+                                           style=wx.DEFAULT_FRAME_STYLE,
+                                           tree=self, notebook=self.notebook,
+                                           lmgr=self.lmgr, page=self.treepg,
+                                           Map=self.Map, auimgr=self.auimgr)
+        
+        # title
+        self.mapdisplay.SetTitle(_("GRASS GIS Map Display: %(id)d  - Location: %(loc)s") % \
+                                     { 'id' : self.disp_idx + 1,
+                                       'loc' : grass.gisenv()["LOCATION_NAME"] })
+        
+        # show new display
+        if showMapDisplay is True:
+            self.mapdisplay.Show()
+            self.mapdisplay.Refresh()
+            self.mapdisplay.Update()
+        
+        self.root = self.AddRoot(_("Map Layers"))
+        self.SetPyData(self.root, (None, None))
+        
+        #create image list to use with layer tree
+        il = wx.ImageList(16, 16, mask=False)
+        
+        trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_OTHER, (16, 16))
+        self.folder_open = il.Add(trart)
+        trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16, 16))
+        self.folder = il.Add(trart)
+        
+        bmpsize = (16, 16)
+        trgif = Icons["addrast"].GetBitmap(bmpsize)
+        self.rast_icon = il.Add(trgif)
+        
+        trgif = Icons["addrast3d"].GetBitmap(bmpsize)
+        self.rast3d_icon = il.Add(trgif)
+        
+        trgif = Icons["addrgb"].GetBitmap(bmpsize)
+        self.rgb_icon = il.Add(trgif)
+        
+        trgif = Icons["addhis"].GetBitmap(bmpsize)
+        self.his_icon = il.Add(trgif)
+        
+        trgif = Icons["addshaded"].GetBitmap(bmpsize)
+        self.shaded_icon = il.Add(trgif)
+        
+        trgif = Icons["addrarrow"].GetBitmap(bmpsize)
+        self.rarrow_icon = il.Add(trgif)
+        
+        trgif = Icons["addrnum"].GetBitmap(bmpsize)
+        self.rnum_icon = il.Add(trgif)
+        
+        trgif = Icons["addvect"].GetBitmap(bmpsize)
+        self.vect_icon = il.Add(trgif)
+        
+        trgif = Icons["addthematic"].GetBitmap(bmpsize)
+        self.theme_icon = il.Add(trgif)
+        
+        trgif = Icons["addchart"].GetBitmap(bmpsize)
+        self.chart_icon = il.Add(trgif)
+        
+        trgif = Icons["addgrid"].GetBitmap(bmpsize)
+        self.grid_icon = il.Add(trgif)
+        
+        trgif = Icons["addgeodesic"].GetBitmap(bmpsize)
+        self.geodesic_icon = il.Add(trgif)
+        
+        trgif = Icons["addrhumb"].GetBitmap(bmpsize)
+        self.rhumb_icon = il.Add(trgif)
+        
+        trgif = Icons["addlabels"].GetBitmap(bmpsize)
+        self.labels_icon = il.Add(trgif)
+        
+        trgif = Icons["addcmd"].GetBitmap(bmpsize)
+        self.cmd_icon = il.Add(trgif)
+        
+        self.AssignImageList(il)
+        
+        self.Bind(wx.EVT_TREE_ITEM_EXPANDING,   self.OnExpandNode)
+        self.Bind(wx.EVT_TREE_ITEM_COLLAPSED,   self.OnCollapseNode)
+        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED,   self.OnActivateLayer)
+        self.Bind(wx.EVT_TREE_SEL_CHANGED,      self.OnChangeSel)
+        self.Bind(CT.EVT_TREE_ITEM_CHECKED,     self.OnLayerChecked)
+        self.Bind(wx.EVT_TREE_DELETE_ITEM,      self.OnDeleteLayer)
+        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnLayerContextMenu)
+        self.Bind(wx.EVT_TREE_END_DRAG,         self.OnEndDrag)
+        self.Bind(wx.EVT_KEY_UP,                self.OnKeyUp)
+        self.Bind(wx.EVT_IDLE,                  self.OnIdle)
+        
+    def GetMap(self):
+        """!Get map instace"""
+        return self.Map
+    
+    def GetMapDisplay(self):
+        """!Get associated MapFrame"""
+        return self.mapdisplay
+    
+    def OnIdle(self, event):
+        """
+        Only re-order and re-render a composite map image from GRASS during
+        idle time instead of multiple times during layer changing.
+        """
+        if self.rerender:
+            if self.mapdisplay.statusbarWin['render'].GetValue():
+                self.mapdisplay.MapWindow.UpdateMap(render=True)
+
+        event.Skip()
+        
+    def OnKeyUp(self, event):
+        """!Key pressed"""
+        key = event.GetKeyCode()
+        
+        if key == wx.WXK_DELETE and self.lmgr:
+            self.lmgr.OnDeleteLayer(None)
+
+        event.Skip()
+        
+    def OnLayerContextMenu (self, event):
+        """!Contextual menu for item/layer"""
+        if not self.layer_selected:
+            event.Skip()
+            return
+
+        ltype = self.GetPyData(self.layer_selected)[0]['type']
+
+        Debug.msg (4, "LayerTree.OnContextMenu: layertype=%s" % \
+                       ltype)
+
+        if not hasattr (self, "popupID1"):
+            self.popupID1 = wx.NewId()
+            self.popupID2 = wx.NewId()
+            self.popupID3 = wx.NewId()
+            self.popupID4 = wx.NewId()
+            self.popupID5 = wx.NewId()
+            self.popupID6 = wx.NewId()
+            self.popupID7 = wx.NewId()
+            self.popupID8 = wx.NewId()
+            self.popupID9 = wx.NewId()
+            self.popupID10 = wx.NewId()
+            self.popupID11 = wx.NewId() # nviz
+            self.popupID12 = wx.NewId()
+            self.popupID13 = wx.NewId()
+            self.popupID14 = wx.NewId()
+            self.popupID15 = wx.NewId()
+            self.popupID16 = wx.NewId()
+
+        self.popupMenu = wx.Menu()
+
+        numSelected = len(self.GetSelections())
+        
+        # general item
+        self.popupMenu.Append(self.popupID1, text=_("Remove"))
+        self.Bind(wx.EVT_MENU, self.lmgr.OnDeleteLayer, id=self.popupID1)
+
+        if ltype != "command": # rename
+            self.popupMenu.Append(self.popupID2, text=_("Rename"))
+            self.Bind(wx.EVT_MENU, self.RenameLayer, id=self.popupID2)
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID2, False)
+            
+        # map layer items
+        if ltype != "group" and \
+                ltype != "command":
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID8, text=_("Change opacity level"))
+            self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=self.popupID8)
+            self.popupMenu.Append(self.popupID3, text=_("Properties"))
+            self.Bind(wx.EVT_MENU, self.OnPopupProperties, id=self.popupID3)
+            
+            if ltype in ('raster', 'vector', '3d-raster') and self.mapdisplay.toolbars['nviz']:
+                self.popupMenu.Append(self.popupID11, _("3D view properties"))
+                self.Bind (wx.EVT_MENU, self.OnNvizProperties, id=self.popupID11)
+            
+            if ltype in ('raster', 'vector', 'rgb'):
+                self.popupMenu.Append(self.popupID9, text=_("Zoom to selected map(s)"))
+                self.Bind(wx.EVT_MENU, self.mapdisplay.OnZoomToMap, id=self.popupID9)
+                self.popupMenu.Append(self.popupID10, text=_("Set computational region from selected map(s)"))
+                self.Bind(wx.EVT_MENU, self.OnSetCompRegFromMap, id=self.popupID10)
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID8, False)
+                self.popupMenu.Enable(self.popupID3, False)
+            
+        # specific items
+        try:
+            mltype = self.GetPyData(self.layer_selected)[0]['type']
+        except:
+            mltype = None
+        #
+        # vector layers (specific items)
+        #
+        if mltype and mltype == "vector":
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID4, text=_("Show attribute data"))
+            self.Bind (wx.EVT_MENU, self.lmgr.OnShowAttributeTable, id=self.popupID4)
+
+            self.popupMenu.Append(self.popupID5, text=_("Start editing"))
+            self.popupMenu.Append(self.popupID6, text=_("Stop editing"))
+            self.popupMenu.Enable(self.popupID6, False)
+            self.Bind (wx.EVT_MENU, self.OnStartEditing, id=self.popupID5)
+            self.Bind (wx.EVT_MENU, self.OnStopEditing,  id=self.popupID6)
+
+            layer = self.GetPyData(self.layer_selected)[0]['maplayer']
+            # enable editing only for vector map layers available in the current mapset
+            digitToolbar = self.mapdisplay.toolbars['vdigit']
+            if digitToolbar:
+                # background vector map
+                self.popupMenu.Append(self.popupID14,
+                                      text=_("Use as background vector map"),
+                                      kind=wx.ITEM_CHECK)
+                self.Bind(wx.EVT_MENU, self.OnSetBgMap, id=self.popupID14)
+                if UserSettings.Get(group='vdigit', key='bgmap', subkey='value',
+                                    internal=True) == layer.GetName():
+                    self.popupMenu.Check(self.popupID14, True)
+            
+            self.popupMenu.Append(self.popupID16, text=_("Rebuild topology"))
+            self.Bind(wx.EVT_MENU, self.OnTopology, id=self.popupID16)
+
+            if layer.GetMapset() != grass.gisenv()['MAPSET']:
+                # only vector map in current mapset can be edited
+                self.popupMenu.Enable (self.popupID5, False)
+                self.popupMenu.Enable (self.popupID6, False)
+                self.popupMenu.Enable (self.popupID16, False)
+            elif digitToolbar and digitToolbar.GetLayer():
+                # vector map already edited
+                vdigitLayer = digitToolbar.GetLayer()
+                if vdigitLayer is layer:
+                    # disable 'start editing'
+                    self.popupMenu.Enable (self.popupID5, False)
+                    # enable 'stop editing'
+                    self.popupMenu.Enable(self.popupID6, True)
+                    # disable 'remove'
+                    self.popupMenu.Enable(self.popupID1, False)
+                    # disable 'bgmap'
+                    self.popupMenu.Enable(self.popupID14, False)
+                    # disable 'topology'
+                    self.popupMenu.Enable (self.popupID16, False)
+                else:
+                    # disable 'start editing'
+                    self.popupMenu.Enable(self.popupID5, False)
+                    # disable 'stop editing'
+                    self.popupMenu.Enable(self.popupID6, False)
+                    # enable 'bgmap'
+                    self.popupMenu.Enable(self.popupID14, True)
+            
+            self.popupMenu.Append(self.popupID7, _("Metadata"))
+            self.Bind (wx.EVT_MENU, self.OnMetadata, id=self.popupID7)
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID4, False)
+                self.popupMenu.Enable(self.popupID5, False)
+                self.popupMenu.Enable(self.popupID6, False)
+                self.popupMenu.Enable(self.popupID7, False)
+                self.popupMenu.Enable(self.popupID14, False)
+        
+        #
+        # raster layers (specific items)
+        #
+        elif mltype and mltype == "raster":
+            self.popupMenu.Append(self.popupID12, text=_("Zoom to selected map(s) (ignore NULLs)"))
+            self.Bind(wx.EVT_MENU, self.mapdisplay.OnZoomToRaster, id=self.popupID12)
+            self.popupMenu.Append(self.popupID13, text=_("Set computational region from selected map(s) (ignore NULLs)"))
+            self.Bind(wx.EVT_MENU, self.OnSetCompRegFromRaster, id=self.popupID13)
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID15, _("Set color table"))
+            self.Bind (wx.EVT_MENU, self.OnColorTable, id=self.popupID15)
+            self.popupMenu.Append(self.popupID4, _("Histogram"))
+            self.Bind (wx.EVT_MENU, self.OnHistogram, id=self.popupID4)
+            self.popupMenu.Append(self.popupID5, _("Profile"))
+            self.Bind (wx.EVT_MENU, self.OnProfile, id=self.popupID5)
+            self.popupMenu.Append(self.popupID6, _("Metadata"))
+            self.Bind (wx.EVT_MENU, self.OnMetadata, id=self.popupID6)
+            
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID12, False)
+                self.popupMenu.Enable(self.popupID13, False)
+                self.popupMenu.Enable(self.popupID15, False)
+                self.popupMenu.Enable(self.popupID4, False)
+                self.popupMenu.Enable(self.popupID5, False)
+                self.popupMenu.Enable(self.popupID6, False)
+                self.popupMenu.Enable(self.popupID11, False)
+        
+        ## self.PopupMenu(self.popupMenu, pos)
+        self.PopupMenu(self.popupMenu)
+        self.popupMenu.Destroy()
+
+    def OnTopology(self, event):
+        """!Rebuild topology of selected vector map"""
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        cmd = ['v.build',
+               'map=%s' % mapLayer.GetName()]
+        self.lmgr.goutput.RunCmd(cmd, switchPage = True)
+        
+    def OnMetadata(self, event):
+        """!Print metadata of raster/vector map layer
+        TODO: Dialog to modify metadata
+        """
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        mltype = self.GetPyData(self.layer_selected)[0]['type']
+
+        if mltype == 'raster':
+            cmd = ['r.info']
+        elif mltype == 'vector':
+            cmd = ['v.info']
+        cmd.append('map=%s' % mapLayer.GetName())
+
+        # print output to command log area
+        self.lmgr.goutput.RunCmd(cmd, switchPage=True)
+
+    def OnSetCompRegFromRaster(self, event):
+        """!Set computational region from selected raster map (ignore NULLs)"""
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        
+        cmd = ['g.region',
+               '-p',
+               'zoom=%s' % mapLayer.GetName()]
+        
+        # print output to command log area
+        self.lmgr.goutput.RunCmd(cmd)
+         
+    def OnSetCompRegFromMap(self, event):
+        """!Set computational region from selected raster/vector map"""
+        rast = []
+        vect = []
+        rast3d = []
+        for layer in self.GetSelections():
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            mltype = self.GetPyData(layer)[0]['type']
+                
+            if mltype == 'raster':
+                rast.append(mapLayer.GetName())
+            elif mltype == 'vector':
+                vect.append(mapLayer.GetName())
+            elif mltype == '3d-raster':
+                rast3d.append(mapLayer.GetName())
+
+        cmd = ['g.region']
+        if rast:
+            cmd.append('rast=%s' % ','.join(rast))
+        if vect:
+            cmd.append('vect=%s' % ','.join(vect))
+        if rast3d:
+            cmd.append('rast3d=%s' % ','.join(rast3d))
+        
+        # print output to command log area
+        if len(cmd) > 1:
+            cmd.append('-p')
+            self.lmgr.goutput.RunCmd(cmd)
+        
+    def OnProfile(self, event):
+        """!Plot profile of given raster map layer"""
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        if not mapLayer.GetName():
+            wx.MessageBox(parent=self,
+                          message=_("Unable to create profile of "
+                                    "raster map."),
+                          caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            return False
+
+        if not hasattr (self, "profileFrame"):
+            self.profileFrame = None
+
+        if hasattr (self.mapdisplay, "profile") and self.mapdisplay.profile:
+            self.profileFrame = self.mapdisplay.profile
+
+        if not self.profileFrame:
+            self.profileFrame = profile.ProfileFrame(self.mapdisplay,
+                                                     id=wx.ID_ANY, pos=wx.DefaultPosition, size=(700,300),
+                                                     style=wx.DEFAULT_FRAME_STYLE, rasterList=[mapLayer.GetName()])
+            # show new display
+            self.profileFrame.Show()
+        
+    def OnColorTable(self, event):
+        """!Set color table for raster map"""
+        name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
+        menuform.GUI().ParseCommand(['r.colors',
+                                     'map=%s' % name],
+                                    parentframe=self)
+        
+    def OnHistogram(self, event):
+        """
+        Plot histogram for given raster map layer
+        """
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        if not mapLayer.GetName():
+            wx.MessageBox(parent=self,
+                          message=_("Unable to display histogram of "
+                                    "raster map."),
+                          caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            return False
+
+        if not hasattr (self, "histogramFrame"):
+            self.histogramFrame = None
+
+        if hasattr (self.mapdisplay, "histogram") and self.mapdisplay.histogram:
+            self.histogramFrame = self.mapdisplay.histogram
+
+        if not self.histogramFrame:
+            self.histogramFrame = histogram.HistFrame(self,
+                                                      id=wx.ID_ANY,
+                                                      pos=wx.DefaultPosition, size=globalvar.HIST_WINDOW_SIZE,
+                                                      style=wx.DEFAULT_FRAME_STYLE)
+            # show new display
+            self.histogramFrame.Show()
+
+        self.histogramFrame.SetHistLayer(mapLayer.GetName())
+        self.histogramFrame.HistWindow.UpdateHist()
+        self.histogramFrame.Refresh()
+        self.histogramFrame.Update()
+
+        return True
+
+    def OnStartEditing(self, event):
+        """!Start editing vector map layer requested by the user
+        """
+        try:
+            maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        except:
+            event.Skip()
+            return
+        
+        if not self.mapdisplay.toolbars['vdigit']: # enable tool
+            self.mapdisplay.AddToolbar('vdigit')
+        else: # tool already enabled
+            pass
+        
+        # mark layer as 'edited'
+                
+        if not self.mapdisplay.toolbars['vdigit'].StartEditing(maplayer) or \
+                not haveVDigit:
+            if not haveVDigit:
+                from vdigit import errorMsg
+            else:
+                errorMsg = _("Unable to initialize display driver of vector "
+                             "digitizer")
+            msg = _("Unable to start wxGUI vector digitizer.\nDo you want to start "
+                    "TCL/TK digitizer (v.digit) instead?\n\n"
+                    "Details: %s" % errorMsg)
+            
+            self.mapdisplay.toolbars['map'].combo.SetValue (_("2D view"))
+            dlg = wx.MessageDialog(parent = self.mapdisplay,
+                                   message = msg,
+                                   caption=_("Vector digitizer failed"),
+                                   style = wx.YES_NO | wx.CENTRE)
+            if dlg.ShowModal() == wx.ID_YES:
+                self.lmgr.goutput.RunCmd(['v.digit', 'map=%s' % maplayer.GetName()],
+                                         switchPage=False)
+            
+            dlg.Destroy()
+        
+    def OnStopEditing(self, event):
+        """
+        Stop editing the current vector map layer
+        """
+        maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        
+        self.mapdisplay.toolbars['vdigit'].OnExit()
+        self.mapdisplay.imgVectorMap = None
+        
+    def OnSetBgMap(self, event):
+        """!Set background vector map for editing sesstion"""
+        if event.IsChecked():
+            mapName = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
+            UserSettings.Set(group='vdigit', key='bgmap', subkey='value',
+                             value=str(mapName), internal=True)
+        else:
+            UserSettings.Set(group='vdigit', key='bgmap', subkey='value',
+                             value='', internal=True)
+        
+    def OnPopupProperties (self, event):
+        """!Popup properties dialog"""
+        self.PropertiesDialog(self.layer_selected)
+
+    def OnPopupOpacityLevel(self, event):
+        """!Popup opacity level indicator"""
+        if not self.GetPyData(self.layer_selected)[0]['ctrl']:
+            return
+
+        #win = self.FindWindowById(self.GetPyData(self.layer_selected)[0]['ctrl'])
+        #type = win.GetName()
+        #
+        #self.layer_selected.DeleteWindow()
+
+        maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        current_opacity = maplayer.GetOpacity()
+        
+        dlg = gdialogs.SetOpacityDialog(self, opacity=current_opacity,
+                                        title=_("Set opacity <%s>") % maplayer.GetName())
+        dlg.CentreOnParent()
+
+        if dlg.ShowModal() == wx.ID_OK:
+            new_opacity = dlg.GetOpacity() # string
+            self.Map.ChangeOpacity(maplayer, new_opacity)
+            maplayer.SetOpacity(new_opacity)
+            opacity_pct = int(new_opacity * 100)
+            layername = self.GetItemText(self.layer_selected)
+            layerbase = layername.split('(')[0].strip()
+            self.SetItemText(self.layer_selected,
+                             layerbase + ' (opacity: ' + str(opacity_pct) + '%)')
+            
+            # vector layer currently edited
+            if self.mapdisplay.toolbars['vdigit'] and \
+                    self.mapdisplay.toolbars['vdigit'].GetLayer() == maplayer:   
+                alpha = int(new_opacity * 255)
+                self.mapdisplay.digit.driver.UpdateSettings(alpha)
+                
+            # redraw map if auto-rendering is enabled
+            self.rerender = True
+            self.reorder = True
+            #if self.mapdisplay.statusbarWin['render'].GetValue():
+            #    print "*** Opacity OnRender *****"
+            #    self.mapdisplay.OnRender(None)
+
+    def OnNvizProperties(self, event):
+        """!Nviz-related properties (raster/vector/volume)
+
+        @todo vector/volume
+        """
+        self.lmgr.notebook.SetSelection(3)
+        ltype = self.GetPyData(self.layer_selected)[0]['type']
+        if ltype == 'raster':
+            self.lmgr.nviz.SetPage('surface')
+        elif ltype == 'vector':
+            self.lmgr.nviz.SetPage('vector')
+        elif ltype == '3d-raster':
+            self.lmgr.nviz.SetPage('volume')
+        
+    def RenameLayer (self, event):
+        """!Rename layer"""
+        self.EditLabel(self.layer_selected)
+
+    def AddLayer(self, ltype, lname=None, lchecked=None,
+                 lopacity=1.0, lcmd=None, lgroup=None, lvdigit=None, lnviz=None):
+        """!Add new item to the layer tree, create corresponding MapLayer instance.
+        Launch property dialog if needed (raster, vector, etc.)
+
+        @param ltype layer type (raster, vector, 3d-raster, ...)
+        @param lname layer name
+        @param lchecked if True layer is checked
+        @param lopacity layer opacity level
+        @param lcmd command (given as a list)
+        @param lgroup index of group item (-1 for root) or None
+        @param lvdigit vector digitizer settings (eg. geometry attributes)
+        @param lnviz layer Nviz properties
+        """
+        self.first = True
+        params = {} # no initial options parameters
+
+        # deselect active item
+        if self.layer_selected:
+            self.SelectItem(self.layer_selected, select=False)
+
+        Debug.msg (3, "LayerTree().AddLayer(): ltype=%s" % (ltype))
+        
+        if ltype == 'command':
+            # generic command item
+            ctrl = wx.TextCtrl(self, id=wx.ID_ANY, value='',
+                               pos=wx.DefaultPosition, size=(self.GetSize()[0]-100,25),
+                               # style=wx.TE_MULTILINE|wx.TE_WORDWRAP)
+                               style=wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
+            ctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged)
+            # ctrl.Bind(wx.EVT_TEXT,       self.OnCmdChanged)
+        elif ltype == 'group':
+            # group item
+            ctrl = None
+            grouptext = _('Layer group:') + str(self.groupnode)
+            self.groupnode += 1
+        else:
+            btnbmp = Icons["layeropts"].GetBitmap((16,16))
+            ctrl = buttons.GenBitmapButton(self, id=wx.ID_ANY, bitmap=btnbmp, size=(24,24))
+            ctrl.SetToolTipString(_("Click to edit layer settings"))
+            self.Bind(wx.EVT_BUTTON, self.OnLayerContextMenu, ctrl)
+        # add layer to the layer tree
+        if self.layer_selected and self.layer_selected != self.GetRootItem():
+            if self.GetPyData(self.layer_selected)[0]['type'] == 'group' \
+                and self.IsExpanded(self.layer_selected):
+                # add to group (first child of self.layer_selected) if group expanded
+                layer = self.PrependItem(parent=self.layer_selected,
+                                         text='', ct_type=1, wnd=ctrl)
+            else:
+                # prepend to individual layer or non-expanded group
+                if lgroup == -1:
+                    # -> last child of root (loading from workspace)
+                    layer = self.AppendItem(parentId=self.root,
+                                            text='', ct_type=1, wnd=ctrl)
+                elif lgroup > -1:
+                    # -> last child of group (loading from workspace)
+                    parent = self.FindItemByIndex(index = lgroup)
+                    if not parent:
+                        parent = self.root
+                    layer = self.AppendItem(parentId=parent,
+                                            text='', ct_type=1, wnd=ctrl)
+                elif lgroup is None:
+                    # -> previous sibling of selected layer
+                    parent = self.GetItemParent(self.layer_selected)
+                    layer = self.InsertItem(parentId=parent,
+                                            input=self.GetPrevSibling(self.layer_selected),
+                                            text='', ct_type=1, wnd=ctrl)
+        else: # add first layer to the layer tree (first child of root)
+            layer = self.PrependItem(parent=self.root, text='', ct_type=1, wnd=ctrl)
+
+        # layer is initially unchecked as inactive (beside 'command')
+        # use predefined value if given
+        if lchecked is not None:
+            checked = lchecked
+        else:
+            checked = True
+
+        self.CheckItem(layer, checked=checked)
+
+        # select new item
+        self.SelectItem(layer, select=True)
+        self.layer_selected = layer
+        
+        # add text and icons for each layer ltype
+        if ltype == 'raster':
+            self.SetItemImage(layer, self.rast_icon)
+            self.SetItemText(layer, '%s %s' % (_('raster'), _('(double click to set properties)')))
+        elif ltype == '3d-raster':
+            self.SetItemImage(layer, self.rast3d_icon)
+            self.SetItemText(layer, '%s %s' % (_('3d raster'), _('(double click to set properties)')))
+        elif ltype == 'rgb':
+            self.SetItemImage(layer, self.rgb_icon)
+            self.SetItemText(layer, '%s %s' % (_('RGB'), _('(double click to set properties)')))
+        elif ltype == 'his':
+            self.SetItemImage(layer, self.his_icon)
+            self.SetItemText(layer, '%s %s' % (_('HIS'), _('(double click to set properties)')))
+        elif ltype == 'shaded':
+            self.SetItemImage(layer, self.shaded_icon)
+            self.SetItemText(layer, '%s %s' % (_('Shaded relief'), _('(double click to set properties)')))
+        elif ltype == 'rastnum':
+            self.SetItemImage(layer, self.rnum_icon)
+            self.SetItemText(layer, '%s %s' % (_('raster cell numbers'), _('(double click to set properties)')))
+        elif ltype == 'rastarrow':
+            self.SetItemImage(layer, self.rarrow_icon)
+            self.SetItemText(layer, '%s %s' % (_('raster flow arrows'), _('(double click to set properties)')))
+        elif ltype == 'vector':
+            self.SetItemImage(layer, self.vect_icon)
+            self.SetItemText(layer, '%s %s' % (_('vector'), _('(double click to set properties)')))
+        elif ltype == 'thememap':
+            self.SetItemImage(layer, self.theme_icon)
+            self.SetItemText(layer, '%s %s' % (_('thematic map'), _('(double click to set properties)')))
+        elif ltype == 'themechart':
+            self.SetItemImage(layer, self.chart_icon)
+            self.SetItemText(layer, '%s %s' % (_('thematic charts'), _('(double click to set properties)')))
+        elif ltype == 'grid':
+            self.SetItemImage(layer, self.grid_icon)
+            self.SetItemText(layer, '%s %s' % (_('grid'), _('(double click to set properties)')))
+        elif ltype == 'geodesic':
+            self.SetItemImage(layer, self.geodesic_icon)
+            self.SetItemText(layer, '%s %s' % (_('geodesic line'), _('(double click to set properties)')))
+        elif ltype == 'rhumb':
+            self.SetItemImage(layer, self.rhumb_icon)
+            self.SetItemText(layer, '%s %s' % (_('rhumbline'), _('(double click to set properties)')))
+        elif ltype == 'labels':
+            self.SetItemImage(layer, self.labels_icon)
+            self.SetItemText(layer, '%s %s' % (_('vector labels'), _('(double click to set properties)')))
+        elif ltype == 'command':
+            self.SetItemImage(layer, self.cmd_icon)
+        elif ltype == 'group':
+            self.SetItemImage(layer, self.folder)
+            self.SetItemText(layer, grouptext)
+
+        self.first = False
+
+        if ltype != 'group':
+            if lcmd and len(lcmd) > 1:
+                cmd = lcmd
+                render = False
+                name = utils.GetLayerNameFromCmd(lcmd)
+            else:
+                cmd = []
+                if ltype == 'command' and lname:
+                    for c in lname.split(';'):
+                        cmd.append(c.split(' '))
+                
+                render = False
+                name = None
+
+            if ctrl:
+                ctrlId = ctrl.GetId()
+            else:
+                ctrlId = None
+                
+            # add a data object to hold the layer's command (does not apply to generic command layers)
+            self.SetPyData(layer, ({'cmd'      : cmd,
+                                    'type'     : ltype,
+                                    'ctrl'     : ctrlId,
+                                    'maplayer' : None,
+                                    'vdigit'   : lvdigit,
+                                    'nviz'     : lnviz,
+                                    'propwin'  : None}, 
+                                   None))
+            
+            # find previous map layer instance 
+            prevItem = self.GetFirstChild(self.root)[0]
+            prevMapLayer = None 
+            pos = -1
+            while prevItem and prevItem.IsOk() and prevItem != layer: 
+                if self.GetPyData(prevItem)[0]['maplayer']: 
+                    prevMapLayer = self.GetPyData(prevItem)[0]['maplayer'] 
+                
+                prevItem = self.GetNextSibling(prevItem) 
+                
+                if prevMapLayer: 
+                    pos = self.Map.GetLayerIndex(prevMapLayer)
+                else: 
+                    pos = -1
+            
+            maplayer = self.Map.AddLayer(pos=pos,
+                                         type=ltype, command=self.GetPyData(layer)[0]['cmd'], name=name,
+                                         l_active=checked, l_hidden=False,
+                                         l_opacity=lopacity, l_render=render)
+            self.GetPyData(layer)[0]['maplayer'] = maplayer
+
+            # run properties dialog if no properties given
+            if len(cmd) == 0:
+                self.PropertiesDialog(layer, show=True)
+        
+        else: # group
+            self.SetPyData(layer, ({'cmd': None,
+                                    'type' : ltype,
+                                    'ctrl' : None,
+                                    'maplayer' : None,
+                                    'propwin' : None}, 
+                                   None))
+
+        # use predefined layer name if given
+        if lname:
+            if ltype == 'group':
+                self.SetItemText(layer, lname)
+            elif ltype == 'command':
+                ctrl.SetValue(lname)
+            else:
+                name = lname + ' (opacity: ' + \
+                       str(self.GetPyData(layer)[0]['maplayer'].GetOpacity()) + '%)'
+                self.SetItemText(layer, name)
+                
+        # updated progress bar range (mapwindow statusbar)
+        if checked is True:
+            self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active=True)))
+
+        # layer.SetHeight(TREE_ITEM_HEIGHT)
+
+        return layer
+
+    def PropertiesDialog (self, layer, show=True):
+        """!Launch the properties dialog"""
+        if self.GetPyData(layer)[0].has_key('propwin') and \
+                self.GetPyData(layer)[0]['propwin'] is not None:
+            # recycle GUI dialogs
+            win = self.GetPyData(layer)[0]['propwin']
+            # update properties (columns, layers)
+            win.notebookpanel.OnUpdateSelection(None)
+            if win.IsShown():
+                win.SetFocus()
+            else:
+                win.Show()
+            
+            return
+        
+        completed = ''
+        params = self.GetPyData(layer)[1]
+        ltype  = self.GetPyData(layer)[0]['type']
+                
+        Debug.msg (3, "LayerTree.PropertiesDialog(): ltype=%s" % \
+                   ltype)
+
+        if self.GetPyData(layer)[0]['cmd']:
+            module = menuform.GUI()
+            module.ParseCommand(self.GetPyData(layer)[0]['cmd'],
+                                completed=(self.GetOptData,layer,params),
+                                parentframe=self, show=show)
+            
+            self.GetPyData(layer)[0]['cmd'] = module.GetCmd()
+        elif ltype == 'raster':
+            cmd = ['d.rast']
+            
+            if UserSettings.Get(group='cmd', key='rasterOverlay', subkey='enabled'):
+                cmd.append('-o')
+            menuform.GUI().ParseCommand(cmd, completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == '3d-raster':
+            cmd = ['d.rast3d']
+            menuform.GUI().ParseCommand(cmd, completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'rgb':
+            menuform.GUI().ParseCommand(['d.rgb'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'his':
+            menuform.GUI().ParseCommand(['d.his'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'shaded':
+            menuform.GUI().ParseCommand(['d.shadedmap'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'rastarrow':
+            menuform.GUI().ParseCommand(['d.rast.arrow'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'rastnum':
+            menuform.GUI().ParseCommand(['d.rast.num'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'vector':
+            types = ''
+            for type in UserSettings.Get(group='cmd', key='showType').keys():
+                if UserSettings.Get(group='cmd', key='showType', subkey=[type, 'enabled']):
+                    types += type + ','
+            types = types.rstrip(',')
+            
+            menuform.GUI().ParseCommand(['d.vect', 'type=%s' % types],
+                                         completed=(self.GetOptData,layer,params),
+                                         parentframe=self)
+        elif ltype == 'thememap':
+            # -s flag requested, otherwise only first thematic category is displayed
+            # should be fixed by C-based d.thematic.* modules
+            menuform.GUI().ParseCommand(['d.vect.thematic', '-s'], 
+                                        completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'themechart':
+            menuform.GUI().ParseCommand(['d.vect.chart'],
+                                        completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'grid':
+            menuform.GUI().ParseCommand(['d.grid'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'geodesic':
+            menuform.GUI().ParseCommand(['d.geodesic'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'rhumb':
+            menuform.GUI().ParseCommand(['d.rhumbline'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'labels':
+            menuform.GUI().ParseCommand(['d.labels'], completed=(self.GetOptData,layer,params),
+                                        parentframe=self)
+        elif ltype == 'cmdlayer':
+            pass
+        elif ltype == 'group':
+            pass
+        
+    def OnActivateLayer(self, event):
+        """!Double click on the layer item.
+        Launch property dialog, or expand/collapse group of items, etc.
+        """
+        self.lmgr.WorkspaceChanged()
+        layer = event.GetItem()
+        self.layer_selected = layer
+        
+        self.PropertiesDialog (layer)
+        
+        if self.GetPyData(layer)[0]['type'] == 'group':
+            if self.IsExpanded(layer):
+                self.Collapse(layer)
+            else:
+                self.Expand(layer)
+        
+    def OnDeleteLayer(self, event):
+        """!Remove selected layer item from the layer tree"""
+        self.lmgr.WorkspaceChanged()
+        item = event.GetItem()
+        
+        try:
+            item.properties.Close(True)
+        except:
+            pass
+
+        if item != self.root:
+            Debug.msg (3, "LayerTree.OnDeleteLayer(): name=%s" % \
+                           (self.GetItemText(item)))
+        else:
+            self.root = None
+
+        # unselect item
+        self.Unselect()
+        self.layer_selected = None
+
+        try:
+            if self.GetPyData(item)[0]['type'] != 'group':
+                self.Map.DeleteLayer( self.GetPyData(item)[0]['maplayer'])
+        except:
+            pass
+
+        # redraw map if auto-rendering is enabled
+        self.rerender = True
+        self.reorder = True
+        #if self.mapdisplay.statusbarWin['render'].GetValue():
+        #    print "*** Delete OnRender *****"
+        #    self.mapdisplay.OnRender(None)
+
+        if self.mapdisplay.toolbars['vdigit']:
+            self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers (updateTool=True)
+
+        # update progress bar range (mapwindow statusbar)
+        self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active=True)))
+
+        event.Skip()
+
+    def OnLayerChecked(self, event):
+        """!Enable/disable data layer"""
+        self.lmgr.WorkspaceChanged()
+        
+        item    = event.GetItem()
+        checked = item.IsChecked()
+
+        digitToolbar = self.mapdisplay.toolbars['vdigit']
+        if self.first == False:
+            # change active parameter for item in layers list in render.Map
+            if self.GetPyData(item)[0]['type'] == 'group':
+                child, cookie = self.GetFirstChild(item)
+                while child:
+                    self.CheckItem(child, checked)
+                    mapLayer = self.GetPyData(child)[0]['maplayer']
+                    if not digitToolbar or \
+                           (digitToolbar and digitToolbar.GetLayer() != mapLayer):
+                        # ignore when map layer is edited
+                        self.Map.ChangeLayerActive(mapLayer, checked)
+                    child = self.GetNextSibling(child)
+            else:
+                mapLayer = self.GetPyData(item)[0]['maplayer']
+                if not digitToolbar or \
+                       (digitToolbar and digitToolbar.GetLayer() != mapLayer):
+                    # ignore when map layer is edited
+                    self.Map.ChangeLayerActive(mapLayer, checked)
+
+        #
+        # update progress bar range (mapwindow statusbar)
+        #
+        self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active=True)))
+
+        #
+        # nviz
+        #
+        if self.mapdisplay.toolbars['nviz'] and \
+                self.GetPyData(item) is not None:
+            # nviz - load/unload data layer
+            mapLayer = self.GetPyData(item)[0]['maplayer']
+
+            self.mapdisplay.SetStatusText(_("Please wait, updating data..."), 0)
+
+            if checked: # enable
+                if mapLayer.type == 'raster':
+                    self.mapdisplay.MapWindow.LoadRaster(item)
+                elif mapLayer.type == '3d-raster':
+                    self.mapdisplay.MapWindow.LoadRaster3d(item)
+                elif mapLayer.type == 'vector':
+                    self.mapdisplay.MapWindow.LoadVector(item)
+
+            else: # disable
+                data = self.GetPyData(item)[0]['nviz']
+
+                if mapLayer.type == 'raster':
+                    self.mapdisplay.MapWindow.UnloadRaster(item)
+                elif mapLayer.type == '3d-raster':
+                    self.mapdisplay.MapWindow.UnloadRaster3d(item)
+                elif mapLayer.type == 'vector':
+                    self.mapdisplay.MapWindow.UnloadVector(item)
+            
+            self.mapdisplay.SetStatusText("", 0)
+
+        # redraw map if auto-rendering is enabled
+        self.rerender = True
+        self.reorder = True
+        
+    def OnCmdChanged(self, event):
+        """!Change command string"""
+        ctrl = event.GetEventObject().GetId()
+        cmd = event.GetString()
+        
+        layer = self.GetFirstVisibleItem()
+
+        while layer and layer.IsOk():
+            if self.GetPyData(layer)[0]['ctrl'] == ctrl:
+                break
+            
+            layer = self.GetNextVisible(layer)
+
+        # change parameters for item in layers list in render.Map
+        self.ChangeLayer(layer)
+        
+        event.Skip()
+
+    def OnChangeSel(self, event):
+        """!Selection changed"""
+        oldlayer = event.GetOldItem()
+        layer = event.GetItem()
+        if layer == oldlayer:
+            event.Veto()
+            return
+        
+        self.layer_selected = layer
+        
+        try:
+            if self.IsSelected(oldlayer):
+                self.SetItemWindowEnabled(oldlayer, True)
+            else:
+                self.SetItemWindowEnabled(oldlayer, False)
+
+            if self.IsSelected(layer):
+                self.SetItemWindowEnabled(layer, True)
+            else:
+                self.SetItemWindowEnabled(layer, False)
+        except:
+            pass
+
+        try:
+            self.RefreshLine(oldlayer)
+            self.RefreshLine(layer)
+        except:
+            pass
+
+        #
+        # update statusbar -> show command string
+        #
+        if self.GetPyData(layer) and self.GetPyData(layer)[0]['maplayer']:
+            cmd = self.GetPyData(layer)[0]['maplayer'].GetCmd(string=True)
+            if len(cmd) > 0:
+                self.lmgr.SetStatusText(cmd)
+
+        # set region if auto-zooming is enabled
+        if self.GetPyData(layer) and self.GetPyData(layer)[0]['cmd'] and \
+               UserSettings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled'):
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            if mapLayer.GetType() in ('raster', 'vector'):
+                render = self.mapdisplay.statusbarWin['render'].IsChecked()
+                self.mapdisplay.MapWindow.ZoomToMap(layers = [mapLayer,],
+                                                    render = render)
+        
+        #
+        # update nviz tools
+        #
+        if self.mapdisplay.toolbars['nviz'] and \
+                self.GetPyData(self.layer_selected) is not None:
+            if self.layer_selected.IsChecked():
+                # update Nviz tool window
+                type = self.GetPyData(self.layer_selected)[0]['maplayer'].type
+                
+                if type == 'raster':
+                    self.lmgr.nviz.UpdatePage('surface')
+                    self.lmgr.nviz.SetPage('surface')
+                elif type == 'vector':
+                    self.lmgr.nviz.UpdatePage('vector')
+                    self.lmgr.nviz.SetPage('vector')
+                elif type == '3d-raster':
+                    self.lmgr.nviz.UpdatePage('volume')
+                    self.lmgr.nviz.SetPage('volume')
+        
+    def OnCollapseNode(self, event):
+        """!Collapse node
+        """
+        if self.GetPyData(self.layer_selected)[0]['type'] == 'group':
+            self.SetItemImage(self.layer_selected, self.folder)
+
+    def OnExpandNode(self, event):
+        """!Expand node
+        """
+        self.layer_selected = event.GetItem()
+        if self.GetPyData(self.layer_selected)[0]['type'] == 'group':
+            self.SetItemImage(self.layer_selected, self.folder_open)
+    
+    def OnEndDrag(self, event):
+        self.StopDragging()
+        dropTarget = event.GetItem()
+        self.flag = self.HitTest(event.GetPoint())[1]
+        if self.IsValidDropTarget(dropTarget):
+            self.UnselectAll()
+            if dropTarget != None:
+                self.SelectItem(dropTarget)
+            self.OnDrop(dropTarget, self._dragItem)
+        elif dropTarget == None:
+            self.OnDrop(dropTarget, self._dragItem)
+
+    def OnDrop(self, dropTarget, dragItem):
+        # save everthing associated with item to drag
+        try:
+            old = dragItem  # make sure this member exists
+        except:
+            return
+
+        Debug.msg (4, "LayerTree.OnDrop(): layer=%s" % \
+                   (self.GetItemText(dragItem)))
+        
+        # recreate data layer, insert copy of layer in new position, and delete original at old position
+        newItem  = self.RecreateItem (dragItem, dropTarget)
+
+        # if recreated layer is a group, also recreate its children
+        if  self.GetPyData(newItem)[0]['type'] == 'group':
+            (child, cookie) = self.GetFirstChild(dragItem)
+            if child:
+                while child:
+                    self.RecreateItem(child, dropTarget, parent=newItem)
+                    self.Delete(child)
+                    child = self.GetNextChild(old, cookie)[0]
+            #self.Expand(newItem)
+
+        # delete layer at original position
+        try:
+            self.Delete(old) # entry in render.Map layers list automatically deleted by OnDeleteLayer handler
+        except AttributeError:
+            # FIXME being ugly (item.SetWindow(None))
+            pass
+
+        # reorder layers in render.Map to match new order after drag and drop
+        #self.ReorderLayers()
+
+        # redraw map if auto-rendering is enabled
+        self.rerender = True
+        self.reorder = True
+        #if self.mapdisplay.statusbarWin['render'].GetValue():
+        #    print "*** Drop OnRender *****"
+        #    self.mapdisplay.OnRender(None)
+
+        # select new item
+        self.SelectItem(newItem)
+        
+    def RecreateItem (self, dragItem, dropTarget, parent=None):
+        """
+        Recreate item (needed for OnEndDrag())
+        """
+        Debug.msg (4, "LayerTree.RecreateItem(): layer=%s" % \
+                   self.GetItemText(dragItem))
+
+        # fetch data (dragItem)
+        checked = self.IsItemChecked(dragItem)
+        image   = self.GetItemImage(dragItem, 0)
+        text    = self.GetItemText(dragItem)
+        if self.GetPyData(dragItem)[0]['ctrl']:
+            # recreate data layer
+            btnbmp = Icons["layeropts"].GetBitmap((16,16))
+            newctrl = buttons.GenBitmapButton(self, id=wx.ID_ANY, bitmap=btnbmp, size=(24, 24))
+            newctrl.SetToolTipString(_("Click to edit layer settings"))
+            self.Bind(wx.EVT_BUTTON, self.OnLayerContextMenu, newctrl)
+            data    = self.GetPyData(dragItem)
+        
+        elif self.GetPyData(dragItem)[0]['type'] == 'command':
+            # recreate command layer
+            oldctrl = None
+            newctrl = wx.TextCtrl(self, id=wx.ID_ANY, value='',
+                                  pos=wx.DefaultPosition, size=(250,25),
+                                  style=wx.TE_MULTILINE|wx.TE_WORDWRAP)
+            try:
+                newctrl.SetValue(self.GetPyData(dragItem)[0]['maplayer'].GetCmd(string=True))
+            except:
+                pass
+            newctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged)
+            newctrl.Bind(wx.EVT_TEXT,       self.OnCmdChanged)
+            data    = self.GetPyData(dragItem)
+
+        elif self.GetPyData(dragItem)[0]['type'] == 'group':
+            # recreate group
+            newctrl = None
+            data    = None
+            
+        # decide where to put recreated item
+        if dropTarget != None and dropTarget != self.GetRootItem():
+            if parent:
+                # new item is a group
+                afteritem = parent
+            else:
+                # new item is a single layer
+                afteritem = dropTarget
+
+            # dragItem dropped on group
+            if  self.GetPyData(afteritem)[0]['type'] == 'group':
+                newItem = self.PrependItem(afteritem, text=text, \
+                                      ct_type=1, wnd=newctrl, image=image, \
+                                      data=data)
+                self.Expand(afteritem)
+            else:
+                #dragItem dropped on single layer
+                newparent = self.GetItemParent(afteritem)
+                newItem = self.InsertItem(newparent, self.GetPrevSibling(afteritem), \
+                                       text=text, ct_type=1, wnd=newctrl, \
+                                       image=image, data=data)
+        else:
+            # if dragItem not dropped on a layer or group, append or prepend it to the layer tree
+            if self.flag & wx.TREE_HITTEST_ABOVE:
+                newItem = self.PrependItem(self.root, text=text, \
+                                      ct_type=1, wnd=newctrl, image=image, \
+                                      data=data)
+            elif (self.flag &  wx.TREE_HITTEST_BELOW) or (self.flag & wx.TREE_HITTEST_NOWHERE) \
+                     or (self.flag & wx.TREE_HITTEST_TOLEFT) or (self.flag & wx.TREE_HITTEST_TORIGHT):
+                newItem = self.AppendItem(self.root, text=text, \
+                                      ct_type=1, wnd=newctrl, image=image, \
+                                      data=data)
+
+        #update new layer 
+        self.SetPyData(newItem, self.GetPyData(dragItem))
+        if newctrl:
+            self.GetPyData(newItem)[0]['ctrl'] = newctrl.GetId()
+        else:
+            self.GetPyData(newItem)[0]['ctrl'] = None
+            
+        self.CheckItem(newItem, checked=checked) # causes a new render
+
+        # newItem.SetHeight(TREE_ITEM_HEIGHT)
+
+        return newItem
+
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """!Process layer data"""
+        # set layer text to map name
+        if dcmd:
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            opacity = int(mapLayer.GetOpacity(float=True) * 100)
+            mapname = utils.GetLayerNameFromCmd(dcmd, layerType=mapLayer.type,
+                                                fullyQualified=True)
+            if not mapname:
+                GError(parent=self,
+                       message=_("Map <%s> not found.") % utils.GetLayerNameFromCmd(dcmd))
+                return
+            
+            self.SetItemText(layer, mapname + ' (opacity: ' + str(opacity) + '%)')
+        
+        # update layer data
+        if params:
+            self.SetPyData(layer, (self.GetPyData(layer)[0], params))
+        if dcmd:
+            self.GetPyData(layer)[0]['cmd'] = dcmd
+        self.GetPyData(layer)[0]['propwin'] = propwin
+        
+        # change parameters for item in layers list in render.Map
+        self.ChangeLayer(layer)
+
+        # set region if auto-zooming is enabled
+        if dcmd and UserSettings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled'):
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            if mapLayer.GetType() in ('raster', 'vector'):
+                render = UserSettings.Get(group = 'display', key = 'autoRendering', subkey = 'enabled')
+                self.mapdisplay.MapWindow.ZoomToMap(layers = [mapLayer,],
+                                                    render = render)
+        
+        if self.mapdisplay.toolbars['nviz'] and dcmd:
+            # update nviz session
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            mapWin = self.mapdisplay.MapWindow
+            if len(mapLayer.GetCmd()) > 0:
+                id = -1
+                if mapLayer.type == 'raster':
+                    if mapWin.IsLoaded(layer):
+                        mapWin.UnloadRaster(layer)
+                    
+                    mapWin.LoadRaster(layer)
+                    
+                elif mapLayer.type == '3d-raster':
+                    if mapWin.IsLoaded(layer):
+                        mapWin.UnloadRaster3d(layer)
+                    
+                    mapWin.LoadRaster3d(layer)
+                    
+                elif mapLayer.type == 'vector':
+                    if mapWin.IsLoaded(layer):
+                        mapWin.UnloadVector(layer)
+                    
+                    mapWin.LoadVector(layer)
+
+                # reset view when first layer loaded
+                nlayers = len(mapWin.Map.GetListOfLayers(l_type=('raster', 'vector'),
+                                                         l_active=True))
+                if nlayers < 2:
+                    mapWin.ResetView()
+        
+    def ReorderLayers(self):
+        """!Add commands from data associated with
+        any valid layers (checked or not) to layer list in order to
+        match layers in layer tree."""
+
+        # make a list of visible layers
+        treelayers = []
+
+        vislayer = self.GetFirstVisibleItem()
+
+        if not vislayer or self.GetPyData(vislayer) is None:
+            return
+
+        itemList = ""
+
+        for item in range(self.GetCount()):
+            itemList += self.GetItemText(vislayer) + ','
+            if self.GetPyData(vislayer)[0]['type'] != 'group':
+                treelayers.append(self.GetPyData(vislayer)[0]['maplayer'])
+
+            if not self.GetNextVisible(vislayer):
+                break
+            else:
+                vislayer = self.GetNextVisible(vislayer)
+
+        Debug.msg (4, "LayerTree.ReorderLayers(): items=%s" % \
+                   (itemList))
+
+        # reorder map layers
+        treelayers.reverse()
+        self.Map.ReorderLayers(treelayers)
+        self.reorder = False
+
+    def ChangeLayer(self, item):
+        """!Change layer"""
+        type = self.GetPyData(item)[0]['type']
+        layerName = None
+        
+        if type == 'command':
+            win = self.FindWindowById(self.GetPyData(item)[0]['ctrl'])
+            if win.GetValue() != None:
+                cmd = win.GetValue().split(';')
+                cmdlist = []
+                for c in cmd:
+                    cmdlist.append(c.split(' '))
+                opac = 1.0
+                chk = self.IsItemChecked(item)
+                hidden = not self.IsVisible(item)
+        elif type != 'group':
+            if self.GetPyData(item)[0] is not None:
+                cmdlist = self.GetPyData(item)[0]['cmd']
+                opac = self.GetPyData(item)[0]['maplayer'].GetOpacity(float=True)
+                chk = self.IsItemChecked(item)
+                hidden = not self.IsVisible(item)
+                # determine layer name
+                layerName = utils.GetLayerNameFromCmd(cmdlist, fullyQualified=True)
+                if not layerName:
+                    layerName = self.GetItemText(item)
+        
+        maplayer = self.Map.ChangeLayer(layer=self.GetPyData(item)[0]['maplayer'], type=type,
+                                        command=cmdlist, name=layerName,
+                                        l_active=chk, l_hidden=hidden, l_opacity=opac, l_render=False)
+        
+        self.GetPyData(item)[0]['maplayer'] = maplayer
+        
+        # if digitization tool enabled -> update list of available vector map layers
+        if self.mapdisplay.toolbars['vdigit']:
+            self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers(updateTool=True)
+        
+        # redraw map if auto-rendering is enabled
+        self.rerender = True
+        self.reorder = True
+        #if self.mapdisplay.statusbarWin['render'].GetValue():
+        #    print "*** Change OnRender *****"
+        #    self.mapdisplay.OnRender(None)
+        
+    def OnCloseWindow(self, event):
+        pass
+        # self.Map.Clean()
+
+    def FindItemByData(self, key, value):
+        """!Find item based on key and value (see PyData[0])
+        
+        @return item instance
+        @return None not found
+        """
+        item = self.GetFirstChild(self.root)[0]
+        return self.__FindSubItemByData(item, key, value)
+
+    def FindItemByIndex(self, index):
+        """!Find item by index (starting at 0)
+
+        @return item instance
+        @return None not found
+        """
+        item = self.GetFirstChild(self.root)[0]
+        i = 0
+        while item and item.IsOk():
+            if i == index:
+                return item
+            
+            item = self.GetNextVisible(item)
+            i += 1
+        
+        return None
+    
+    def EnableItemType(self, type, enable=True):
+        """!Enable/disable items in layer tree"""
+        item = self.GetFirstChild(self.root)[0]
+        while item and item.IsOk():
+            mapLayer = self.GetPyData(item)[0]['maplayer']
+            if mapLayer and type == mapLayer.type:
+                self.EnableItem(item, enable)
+            
+            item = self.GetNextSibling(item)
+        
+    def __FindSubItemByData(self, item, key, value):
+        """!Support method for FindItemByValue"""
+        while item and item.IsOk():
+            try:
+                itemValue = self.GetPyData(item)[0][key]
+            except KeyError:
+                return None
+            
+            if value == itemValue:
+                return item
+            if self.GetPyData(item)[0]['type'] == 'group':
+                subItem = self.GetFirstChild(item)[0]
+                found = self.__FindSubItemByData(subItem, key, value)
+                if found:
+                    return found
+            item = self.GetNextSibling(item)
+
+        return None
+


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/layertree.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/location_wizard.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/location_wizard.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/location_wizard.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -22,7 +22,7 @@
  - LocationWizard
  - SelectTransformDialog
 
-COPYRIGHT: (C) 2007-2010 by the GRASS Development Team
+(C) 2007-2010 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.
@@ -33,7 +33,6 @@
 """
 import os
 import shutil
-import re
 import string
 import sys
 import locale
@@ -126,9 +125,8 @@
         self.Layout()
 
 class DatabasePage(TitledPage):
+    """!Wizard page for setting GIS data directory and location name
     """
-    Wizard page for setting GIS data directory and location name
-    """
     def __init__(self, wizard, parent, grassdatabase):
         TitledPage.__init__(self, wizard, _("Define GRASS Database and Location Name"))
 
@@ -234,7 +232,7 @@
         event.Skip()
         
 class CoordinateSystemPage(TitledPage):
-    """!
+    """
     Wizard page for choosing method for location creation
     """
     def __init__(self, wizard, parent):
@@ -345,7 +343,7 @@
             self.parent.sumpage.SetPrev(self.parent.csystemspage)
 
 class ProjectionsPage(TitledPage):
-    """!
+    """
     Wizard page for selecting projection (select coordinate system option)
     """
     def __init__(self, wizard, parent):
@@ -484,9 +482,10 @@
         for column in columns:
             self.InsertColumn(i, column)
             i += 1
+
         if self.sourceData:
             self.Populate()
-
+        
         for i in range(self.GetColumnCount()):
             self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
             if self.GetColumnWidth(i) < 80:
@@ -663,192 +662,184 @@
     def __init__(self, wizard, parent):
         TitledPage.__init__(self, wizard, _("Choose projection parameters"))
         global coordsys
-
-        self.utmzoneNum = 0
-        self.hemischoices = ["north","south"]
+        
         self.parent = parent
         self.panel = None
-        self.prjparamsizer = ''
-        self.pentry = {}
-        self.pdesc = {}
-        self.ptype = {}
-        self.pval = {}
-        self.proj4param = {}
-        self.pcount = 0
-        self.paramlist = []
+        self.prjParamSizer = None
+        
+        self.pparam = dict()
+        
         self.p4projparams = ''
         self.projdesc = ''
-        self.paramSBox = None
         
-        radioSBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
-                               label=" %s " % _("Select datum or ellipsoid (next page)"))
+        radioSBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                 label = " %s " % _("Select datum or ellipsoid (next page)"))
         radioSBSizer = wx.StaticBoxSizer(radioSBox)
-        self.sizer.AddGrowableCol(1)
-        self.sizer.Add(item = radioSBSizer, pos = (0, 1), 
-                       flag=wx.EXPAND | wx.ALIGN_TOP | wx.TOP, border=10)
+        self.sizer.Add(item = radioSBSizer, pos = (0, 1),
+                       flag = wx.EXPAND | wx.ALIGN_TOP | wx.TOP, border = 10)
         
         self.radio1 = wx.RadioButton(parent=self, id=wx.ID_ANY, 
-                                label=_("Datum with associated ellipsoid"),
-                                style = wx.RB_GROUP)
+                                     label=_("Datum with associated ellipsoid"),
+                                     style = wx.RB_GROUP)
         self.radio2 = wx.RadioButton(parent=self, id=wx.ID_ANY,
-                                label=_("Ellipsoid only"))   
+                                     label=_("Ellipsoid only"))   
         
         # default button setting
         if self.radio1.GetValue() == False and self.radio2.GetValue() == False:
             self.radio1.SetValue(True)
             self.SetNext(self.parent.datumpage)
-        #            self.parent.sumpage.SetPrev(self.parent.datumpage)  
-
+            #            self.parent.sumpage.SetPrev(self.parent.datumpage)  
+        
         radioSBSizer.Add(item=self.radio1,
                          flag=wx.ALIGN_LEFT | wx.RIGHT, border=20)
         radioSBSizer.Add(item=self.radio2,
                          flag=wx.ALIGN_LEFT)
-
+        
         # bindings
-        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id=self.radio1.GetId())
-        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id=self.radio2.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio1.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio2.GetId())
         self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChange)
         self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
         
     def OnParamEntry(self, event):
         """!Parameter value changed"""
-        num = 0
-        if event.GetId() >= 2000:
-            num = event.GetId() - 2000
-        else:
-            pass
-        
+        id  = event.GetId()
         val = event.GetString()
         
-        if self.ptype[num] == 'zone':
-            try:
-                intval = int(val)
-                if intval < 1: 
-                    self.pentry[num].SetValue('1')
-                    val = 1
-                if intval > 60: 
-                    self.pentry[num].SetValue('60')
-                    val = 60
-            except:
-                pass
+        if not self.pparam.has_key(id):
+            event.Skip()
+            return
 
-        self.pval[num] = val
+        param = self.pparam[id]
+        win = self.FindWindowById(id)
+        if param['type'] == 'zone':
+            val = event.GetInt()
+            if val < 1:
+                win.SetValue(1)
+            elif val > 60:
+                    win.SetValue(60)
         
+        if param['type'] == 'bool':
+            param['value'] = event.GetSelection()
+        else:
+            param['value'] = val
+        
         event.Skip()
-
-    def OnPageChange(self, event = None):
+        
+    def OnPageChange(self,event=None):
         """!Go to next page"""
         if event.GetDirection():
             self.p4projparams = ''
-            for num in range(self.pcount + 1):
-                if self.ptype[num] == 'bool':
-                    if self.pval[num] == 'No':
+            for id, param in self.pparam.iteritems():
+                if param['type'] == 'bool':
+                    if param['value'] == False:
                         continue
                     else:
-                        self.p4projparams += (' +' + self.proj4param[num])
+                        self.p4projparams += (' +' + param['proj4'])
                 else:
-                    if self.pval[num] == '':
+                    if param['value'] is None:
                         wx.MessageBox(parent = self,
-                                      message = _('You must enter a value for %s') % self.pdesc[num],
-                                      caption = _('Error'), style= wx.ICON_ERROR | wx.CENTRE)
+                                      message = _('You must enter a value for %s') % param['desc'],
+                                      caption = _('Error'), style = wx.ICON_ERROR | wx.CENTRE)
                         event.Veto()
                     else:
-                        self.pval[num] = str(self.pval[num])
-                        self.p4projparams += (' +' + self.proj4param[num] + '=' + self.pval[num])
+                        self.p4projparams += (' +' + param['proj4'] + '=' + str(param['value']))
 
-    def OnEnterPage(self, event):
+    def OnEnterPage(self,event):
         """!Page entered"""
         self.projdesc = self.parent.projections[self.parent.projpage.proj][0]
-        try:
-            # page already formatted
-            if self.pagesizer.GetItem(self.panel):
-                self.paramSBox.SetLabel(_(" Enter parameters for %s projection ") % self.projdesc)
-        except:
+        if self.prjParamSizer is None:
             # entering page for the first time
-            self.paramSBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
-                                   label=_(" Enter parameters for %s projection ") % self.projdesc)
-            paramSBSizer = wx.StaticBoxSizer(self.paramSBox)
-
-            self.panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
-            self.prjparamsizer = wx.GridBagSizer(vgap=0, hgap=0) 
-
+            paramSBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                     label=_(" Enter parameters for %s projection ") % self.projdesc)
+            paramSBSizer = wx.StaticBoxSizer(paramSBox)
+            
+            self.panel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
             self.panel.SetupScrolling()
             
-            self.pagesizer.Add(paramSBSizer, proportion=1, 
-                               flag=wx.EXPAND | wx.ALIGN_TOP | wx.ALL, border=10)
-            paramSBSizer.Add(self.panel, proportion=1, 
-                             flag=wx.ALIGN_CENTER|wx.EXPAND)
-
+            self.prjParamSizer = wx.GridBagSizer(vgap=0, hgap=0) 
+            
+            self.pagesizer.Add(item = paramSBSizer, proportion = 1, 
+                               flag = wx.EXPAND | wx.ALIGN_TOP | wx.ALL, border = 10)
+            paramSBSizer.Add(item = self.panel, proportion = 1, 
+                             flag = wx.ALIGN_CENTER | wx.EXPAND)
+            
             paramSBSizer.Fit(self.panel)
-            self.panel.SetSizer(self.prjparamsizer)
+            self.panel.SetSizer(self.prjParamSizer)
                     
         if event.GetDirection(): 
-            self.pcount = 0
-            self.prjparamsizer.Clear(True)
-            num = 0
-            
+            self.prjParamSizer.Clear(True)
+
+            self.pparam = dict()
+            row = 0
             for paramgrp in self.parent.projections[self.parent.projpage.proj][1]:
                 # get parameters
-                self.pcount = num
-                self.ptype[num] = self.parent.paramdesc[paramgrp[0]][0]
-                self.proj4param[num] = self.parent.paramdesc[paramgrp[0]][1]
-                self.pdesc[num] = self.parent.paramdesc[paramgrp[0]][2]
-
+                id = wx.NewId()
+                param = self.pparam[id] = { 'type' : self.parent.paramdesc[paramgrp[0]][0],
+                                            'proj4': self.parent.paramdesc[paramgrp[0]][1],
+                                            'desc' : self.parent.paramdesc[paramgrp[0]][2] }
+                
                 # default values
-                if self.ptype[num] == 'bool':
-                    self.pval[num] = 'No'
-                elif self.ptype[num] == 'zone': 
-                    self.pdesc[num] += ' (1-60)'
-                    self.pval[num] = '' 
+                if param['type'] == 'bool':
+                    param['value'] = 0
+                elif param['type'] == 'zone': 
+                    param['value'] = 30 
+                    param['desc'] += ' (1-60)'
                 else:
-                    self.pval[num] = paramgrp[2]
+                    param['value'] = paramgrp[2]
                 
-                label = wx.StaticText(self.panel, id=1000+num, label=self.pdesc[num], 
-                                      style=wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)
-                if self.ptype[num] == 'bool':
-                    self.pentry[num] = wx.Choice(self.panel, id=2000+num, size=(100,-1), 
-                                                 choices = ['No','Yes'])  
-                    self.pentry[num].SetStringSelection(self.pval[num])
-                    self.Bind(wx.EVT_CHOICE, self.OnParamEntry)
+                label = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = param['desc'], 
+                                      style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)
+                if param['type'] == 'bool':
+                    win = wx.Choice(parent = self.panel, id = id, size = (100,-1), 
+                                    choices = [_('No'), _('Yes')])  
+                    win.SetSelection(param['value'])
+                    win.Bind(wx.EVT_CHOICE, self.OnParamEntry)
+                elif param['type'] == 'zone':
+                    win = wx.SpinCtrl(parent = self.panel, id = id,
+                                      size = (100, -1), 
+                                      style = wx.SP_ARROW_KEYS | wx.SP_WRAP,
+                                      min = 1, max = 60)
+                    win.SetValue(param['value'])
+                    win.Bind(wx.EVT_SPINCTRL, self.OnParamEntry)
+                    win.Bind(wx.EVT_TEXT, self.OnParamEntry)
                 else:
-                    self.pentry[num] = wx.TextCtrl(self.panel, id=2000+num, 
-                                                   value=self.pval[num],
-                                                   size=(100, -1))
-                    self.Bind(wx.EVT_TEXT, self.OnParamEntry)
+                    win = wx.TextCtrl(parent = self.panel, id = id,
+                                      value = param['value'],
+                                      size=(100, -1))
+                    win.Bind(wx.EVT_TEXT, self.OnParamEntry)
                     if paramgrp[1] == 'noask':
-                        self.pentry[num].SetEditable(False)
-                        self.pentry[num].SetBackgroundColour(wx.LIGHT_GREY)
-                        self.pval[num] = paramgrp[2]
-                
-                self.prjparamsizer.Add(item=label, pos=(num, 1),
-                                       flag=wx.ALIGN_RIGHT | 
+                        win.Enable(False)
+                    
+                self.prjParamSizer.Add(item = label, pos = (row, 1),
+                                       flag = wx.ALIGN_RIGHT | 
                                        wx.ALIGN_CENTER_VERTICAL |
-                                       wx.RIGHT, border=5)
-                self.prjparamsizer.Add(item=self.pentry[num], pos=(num, 2),
-                                       flag=wx.ALIGN_LEFT | 
+                                       wx.RIGHT, border = 5)
+                self.prjParamSizer.Add(item = win, pos = (row, 2),
+                                       flag = wx.ALIGN_LEFT | 
                                        wx.ALIGN_CENTER_VERTICAL |
-                                       wx.LEFT, border=5)                
-                num += 1    
-                
+                                       wx.LEFT, border = 5)           
+                row += 1
+        
         self.panel.SetSize(self.panel.GetBestSize())
         self.panel.Layout()
         self.Layout()
         self.Update()
-
-
+        
         if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
             wx.FindWindowById(wx.ID_FORWARD).Enable()
-
+        
         event.Skip()
 
     def SetVal(self, event):
+        """!Set value"""
         if event.GetId() == self.radio1.GetId():
             self.SetNext(self.parent.datumpage)
             self.parent.sumpage.SetPrev(self.parent.datumpage)
         elif event.GetId() == self.radio2.GetId():
             self.SetNext(self.parent.ellipsepage)
             self.parent.sumpage.SetPrev(self.parent.ellipsepage)
-
+    
 class DatumPage(TitledPage):
     """
     Wizard page for selecting datum (with associated ellipsoid)
@@ -926,7 +917,8 @@
             if self.datum not in self.parent.datums:
                 event.Veto()
             else:
-                # check for datum tranforms
+                # check for datum tranforms            
+#                proj4string = self.parent.CreateProj4String() + ' +datum=%s' % self.datum
                 ret = gcmd.RunCommand('g.proj',
                                       read = True,
                                       proj4 = '%s +datum=%s' % (proj, self.datum), 
@@ -1380,8 +1372,9 @@
                 # check for datum transforms
                 ret = gcmd.RunCommand('g.proj',
                                       read = True,
-                                      epsg = '%s' % self.epsgcode, 
+                                      epsg = self.epsgcode,
                                       datumtrans = '-1')
+                
                 if ret != '':
                     dtrans = ''
                     # open a dialog to select datum transform number
@@ -1473,53 +1466,20 @@
         
     def OnBrowseCodes(self, event, search=None):
         """!Browse EPSG codes"""
-        if True:
-            data = []
-            self.epsgCodeDict = {}
-            try:
-                f = open(self.tfile.GetValue(), "r")
-            except IOError:
-                wx.MessageBox(parent=self,
-                              message=_("Unable to read EPGS file: '%s'") % self.tfile.GetValue(),
-                              caption=_("Error"),  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                self.epsglist.Populate([], update=True)
-                return
-            
-            i = 0
-            code = None
-            for line in f.readlines():
-                line = line.strip()
-                if len(line) < 1:
-                    continue
-                
-                if line[0] == '#':
-                    descr = line[1:].strip()
-                elif line[0] == '<':
-                    code, params = line.split(" ", 1)
-                    try:
-                        code = int(code.replace('<', '').replace('>', ''))
-                    except ValueError:
-                        code = None
-                
-                if code is not None:
-                    data.append((code, descr, params))
-                    self.epsgCodeDict[code] = (descr, params)
-                    code = None
-                i += 1
-            f.close()
-        
-        if type(self.epsgCodeDict) == type(''):
+        self.epsgCodeDict = utils.ReadEpsgCodes(self.tfile.GetValue())
+
+        if type(self.epsgCodeDict) != dict:
             wx.MessageBox(parent=self,
                           message=_("Unable to read EPGS codes: %s") % self.epsgCodeDict,
                           caption=_("Error"),  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-            self.epsglist.Populate([], update=True)
+            self.epsglist.Populate(list(), update=True)
             return
-
-        data = []
+        
+        data = list()
         for code, val in self.epsgCodeDict.iteritems():
             if code is not None:
                 data.append((code, val[0], val[1]))
-                
+        
         self.epsglist.Populate(data, update=True)
         
 class CustomPage(TitledPage):
@@ -1566,30 +1526,39 @@
     def OnPageChanging(self, event):
         if event.GetDirection() and not self.customstring:
             event.Veto()
-        else:
-            # check for datum tranforms    
-            ret = gcmd.RunCommand('g.proj',
-                                  read = True,
-                                  proj4 = '%s' % self.customstring, 
-                                  datumtrans = '-1')
-            if ret != '':
+        elif not event.GetDirection() and not self.customstring:
+            return
+        else: # check for datum tranforms            
+            ret, out, err = gcmd.RunCommand('g.proj',
+                                            read = True, getErrorMsg = True,
+                                            proj4 = self.customstring, 
+                                            datumtrans = '-1')
+            if ret != 0:
+                wx.MessageBox(parent = self,
+                              message = err,
+                              caption = _("Error"),
+                              style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                event.Veto()
+                return
+            
+            if out:
                 dtrans = ''
                 # open a dialog to select datum transform number
                 dlg = SelectTransformDialog(self.parent.parent, transforms=out)
                 
                 if dlg.ShowModal() == wx.ID_OK:
                     dtrans = dlg.GetTransform()
-                    if dtrans == '':
+                    if len(dtrans) == 0:
                         dlg.Destroy()
                         event.Veto()
-                        return 'Datum transform is required.'
+                        return _('Datum transform is required.')
                 else:
                     dlg.Destroy()
                     event.Veto()
-                    return 'Datum transform is required.'
+                    return _('Datum transform is required.')
                 
                 self.parent.datumtrans = dtrans
-                
+        
         self.GetNext().SetPrev(self)
             
     def GetProjstring(self, event):
@@ -1605,15 +1574,13 @@
                 nextButton.Enable()
 
 class SummaryPage(TitledPage):
-    """
-    Shows summary result of choosing coordinate system parameters
+    """!Shows summary result of choosing coordinate system parameters
     prior to creating location
     """
     def __init__(self, wizard, parent):
         TitledPage.__init__(self, wizard, _("Summary"))
-
         self.parent = parent
-
+        
         # labels
         self.ldatabase  = self.MakeLabel("")
         self.llocation  = self.MakeLabel("")
@@ -1621,11 +1588,8 @@
         self.lproj4string = self.MakeLabel("")
         self.lproj4stringLabel = self.MakeLabel("")
         
-        self.lprojection.Wrap(400)
-        
         self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
-        # self.Bind(wx.EVT_BUTTON, self.OnFinish, wx.ID_FINISH)
-
+        
         # do sub-page layout
         self.__DoLayout()
         
@@ -1661,10 +1625,9 @@
                        border=5, pos=(5, 0), span=(1, 2))
 
     def OnEnterPage(self,event):
+        """!Insert values into text controls for summary of location
+        creation options
         """
-        Insert values into text controls for summary of location creation options
-        """
-
         database = self.parent.startpage.grassdatabase
         location = self.parent.startpage.location
         proj4string = self.parent.CreateProj4String()
@@ -1681,61 +1644,54 @@
             self.lproj4string.Show()
             self.lproj4stringLabel.SetLabel(_("PROJ.4 definition:"))
             if coordsys == 'proj':
-                p = gcmd.Command(['g.proj', '-j',
-                                  'proj4=%s' % proj4string,
-                                  'datumtrans=%s' % dtrans,
-                                  'location=%s' % location])
-                                     
+                ret, projlabel, err = gcmd.RunCommand('g.proj',
+                                                      flags = 'jf',
+                                                      proj4 = proj4string,
+                                                      datumtrans = dtrans,
+                                                      location = location,
+                                                      getErrorMsg = True,
+                                                      read = True)
             elif coordsys == 'epsg':
-                p = gcmd.Command(['g.proj', '-j',
-                                  'epsg=%s' % epsgcode,
-                                  'datumtrans=%s' % dtrans,
-                                  'location=%s' % location])
-                                     
-            if p.returncode == 0:
-                projlabel = ''
-                msg = p.ReadStdOutput()
-                for line in msg:
-                    projlabel = projlabel + '%s ' % line
-                self.lproj4string.SetLabel(projlabel)
-            else:
-                err = p.ReadErrOutput()
-                wx.MessageBox(err, 'Error', wx.ICON_ERROR)
-
-            self.lproj4string.Wrap(400)
+                ret, projlabel, err = gcmd.RunCommand('g.proj',
+                                                      flags = 'jf',
+                                                      epsg = epsgcode,
+                                                      datumtrans = dtrans,
+                                                      location = location,
+                                                      getErrorMsg = True,
+                                                      read = True)
             
+            if ret == 0:
+                self.lproj4string.SetLabel(projlabel.replace(' ', os.linesep))
+            else:
+                gcmd.GError(err, parent = self)
+        
         projdesc = self.parent.projpage.projdesc
         ellipsedesc = self.parent.ellipsepage.ellipsedesc
         datumdesc = self.parent.datumpage.datumdesc
         self.ldatabase.SetLabel(database)
         self.llocation.SetLabel(location)
-        label = ''
         
+        label = ''
         if coordsys == 'epsg':
             label = 'EPSG code %s (%s)' % (self.parent.epsgpage.epsgcode, self.parent.epsgpage.epsgdesc)
-            self.lprojection.SetLabel(label)
         elif coordsys == 'file':
             label = 'matches file %s' % self.parent.filepage.georeffile
-            self.lprojection.SetLabel(label)
         elif coordsys == 'wkt':
             label = 'matches file %s' % self.parent.wktpage.wktfile
-            self.lprojection.SetLabel(label)
         elif coordsys == 'proj':
             label = ('%s, %s %s' % (projdesc, datumdesc, ellipsedesc))
-            self.lprojection.SetLabel(label)
         elif coordsys == 'xy':
             label = ('XY coordinate system (not projected).')
-            self.lprojection.SetLabel(label)
         elif coordsys == 'custom':
-            label = ('%s' % self.parent.custompage.customstring)
-            self.lprojection.SetLabel(label)
-
+            label = ('%s' % self.parent.custompage.customstring.replace(' ', os.linesep))
+        self.lprojection.SetLabel(label)
+        
     def OnFinish(self, event):
         dlg = wx.MessageDialog(parent=self.wizard,
                                message=_("Do you want to create GRASS location <%s>?") % location,
                                caption=_("Create new location?"),
                                style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-
+        
         if dlg.ShowModal() == wx.ID_NO:
             dlg.Destroy()
             event.Veto()
@@ -1744,15 +1700,14 @@
             event.Skip()
 
 class LocationWizard(wx.Object):
-    """!
-    Start wizard here and finish wizard here
+    """!Start wizard here and finish wizard here
     """
     def __init__(self, parent, grassdatabase):
         self.__cleanUp()
         
         global coordsys
         self.parent = parent
-
+        
         #
         # define wizard image
         #
@@ -1770,7 +1725,7 @@
         #
         self.datumtrans = 0
         self.proj4string = ''
-
+        
         #
         # define wizard pages
         #
@@ -1822,7 +1777,7 @@
         self.custompage.SetNext(self.sumpage)
 
         self.sumpage.SetPrev(self.csystemspage)
-
+        
         #
         # do pages layout
         #
@@ -1838,7 +1793,9 @@
         self.custompage.DoLayout()
         self.sumpage.DoLayout()
         self.wizard.FitToPage(self.datumpage)
-
+        size = self.wizard.GetPageSize()
+        self.wizard.SetPageSize((size[0], size[1] + 75))
+        
         # new location created?
         self.location = None 
         success = False
@@ -1899,13 +1856,13 @@
         global wizerror
         global translist
         
-        coordsys = ''
-        north = ''
-        south = ''
-        east = ''
-        west = ''
-        resolution = ''
-        transformlist = []
+        coordsys = None
+        north = None
+        south = None
+        east = None
+        west = None
+        resolution = None
+        transformlist = list()
 
     def __readData(self):
         """!Get georeferencing information from tables in $GISBASE/etc"""
@@ -2120,7 +2077,8 @@
             proj4string = '%s %s' % (proj4string, item)
             
         # set datum and transform parameters if relevant
-        if datum != '': proj4string = '%s +datum=%s' % (proj4string, datum)
+        if datum != '':
+            proj4string = '%s +datum=%s' % (proj4string, datum)
         if datumparams:
             for item in datumparams:
                 proj4string = '%s +%s' % (proj4string,item)
@@ -2134,16 +2092,17 @@
         
         @return error message (empty string on success)
         """
-        p = gcmd.Command(['g.proj', '-c',
-                          'proj4=%s' % proj4string,
-                          'location=%s' % self.startpage.location,
-                          'datumtrans=%s' % self.datumtrans])
-                                         
-        if p.returncode == 0:
+        ret, msg = gcmd.RunCommand('g.proj',
+                                   flags = 'c',
+                                   proj4 = proj4string,
+                                   location = self.startpage.location,
+                                   datumtrans = self.datumtrans,
+                                   getErrorMsg = True)
+        
+        if ret == 0:
             return ''
 
-        err = p.ReadErrOutput()
-        return err
+        return msg
         
 
     def CustomCreate(self):
@@ -2153,18 +2112,18 @@
         """
         proj4string = self.custompage.customstring
         location = self.startpage.location
-                
-        p = gcmd.Command(['g.proj', '-c',
-                          'proj4=%s' % proj4string,
-                          'location=%s' % self.startpage.location,
-                          'datumtrans=%s' % self.datumtrans])
-                                         
-        if p.returncode == 0:
+        
+        ret, msg = gcmd.RunCommand('g.proj',
+                                   flags = 'c',
+                                   proj4 = proj4string,
+                                   location = location,
+                                   getErrorMsg = True)
+        
+        if ret == 0:
             return ''
+        
+        return msg
 
-        err = p.ReadErrOutput()
-        return err
-
     def EPSGCreate(self):
         """!Create a new location from an EPSG code.
 
@@ -2178,17 +2137,18 @@
         if epsgcode == '':
             return _('EPSG code missing.')
         
-        p = gcmd.Command(['g.proj', '-c',
-                          'epsg=%s' % epsgcode,
-                          'location=%s' % self.startpage.location,
-                          'datumtrans=%s' % self.datumtrans])
+        ret, msg = gcmd.RunCommand('g.proj',
+                                   flags = 'c',
+                                   epsg = epsgcode,
+                                   location = location,
+                                   datumtrans = self.datumtrans,
+                                   getErrorMsg = True)
         
-        if p.returncode == 0:
+        if ret == 0:
             return ''
-        
-        err = p.ReadErrOutput()
-        return err
-        
+
+        return msg
+
     def FileCreate(self):
         """!Create a new location from a georeferenced file
 
@@ -2202,16 +2162,17 @@
             return _("File not found.")
         
         # creating location
-        p = gcmd.Command(['g.proj', '-c',
-                          'georef=%s' % georeffile,
-                          'location=%s' % self.startpage.location])
-                                         
-        if p.returncode == 0:
+        ret, msg = gcmd.RunCommand('g.proj',
+                                   flags = 'c',
+                                   georef = georeffile,
+                                   location = location,
+                                   getErrorMsg = True)
+        
+        if ret == 0:
             return ''
+        
+        return msg
 
-        err = p.ReadErrOutput()
-        return err
-
     def WKTCreate(self):
         """!Create a new location from a WKT file
         
@@ -2224,17 +2185,16 @@
         if not wktfile or not os.path.isfile(wktfile):
             return _("File not found.")
         
-        # creating location        
-        p = gcmd.Command(['g.proj', '-c',
-                          'wkt=%s' % wktfile,
-                          'location=%s' % self.startpage.location])
-                                 
-        out = p.ReadStdOutput()
-        msg = p.ReadErrOutput()
+        # creating location
+        ret, msg = gcmd.RunCommand('g.proj',
+                                   flags = 'c',
+                                   wkt = wktfile,
+                                   location = location,
+                                   getErrorMsg = True)
         
-        if p.returncode == 0:
+        if ret == 0:
             return ''
-
+        
         return msg
 
 class RegionDef(BaseClass, wx.Frame):
@@ -2663,23 +2623,11 @@
 
     def __UpdateInfo(self):
         """!Update number of rows/cols/cells"""
-        try:
-            self.rows = abs(int((self.north - self.south) / self.nsres))
-        except:
-            self.rows = 0
-            
-        try:
-            self.cols = abs(int((self.east - self.west) / self.ewres))
-        except:
-            self.rows = 0
-            
+        self.rows = int((self.north - self.south) / self.nsres)
+        self.cols = int((self.east - self.west) / self.ewres)
         self.cells = self.rows * self.cols
 
-        try:
-            self.depth = abs(int((self.top - self.bottom) / self.tbres))
-        except:
-            self.depth = 0
-            
+        self.depth = int((self.top - self.bottom) / self.tbres)
         self.cells3 = self.rows * self.cols * self.depth
 
         # 2D
@@ -2692,26 +2640,20 @@
 
     def OnSetButton(self, event=None):
         """!Set default region"""
+        ret = gcmd.RunCommand('g.region',
+                              flags = 'sgpa',
+                              n = self.north,
+                              s = self.south,
+                              e = self.east,
+                              w = self.west,
+                              nsres = self.nsres,
+                              ewres = self.ewres,
+                              t = self.top,
+                              b = self.bottom,
+                              tbres = self.tbres)
+        if ret == 0:
+            self.Destroy()
 
-        if self.cells <= 0 or self.cells3 <= 0:
-            dlg = wx.MessageBox(message = _("Resolution cannot be 0"),
-                                caption = _("Extents set incorrectly"),
-                                style = wx.OK)
-        else:
-            ret = gcmd.RunCommand('g.region',
-                                  flags = 'sgpa',
-                                  n = self.north,
-                                  s = self.south,
-                                  e = self.east,
-                                  w = self.west,
-                                  nsres = self.nsres,
-                                  ewres = self.ewres,
-                                  t = self.top,
-                                  b = self.bottom,
-                                  tbres = self.tbres)
-            if ret == 0:
-                self.Destroy()
-
     def OnCancel(self, event):
         self.Destroy()
         

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,23 +1,20 @@
-"""
+"""!
 @package mapdisp.py
 
 @brief GIS map display canvas, with toolbar for various display
-management functions, and second toolbar for vector
-digitizing. 
+management functions, and additional toolbars (vector digitizer, 3d
+view).
 
-Can be used either from GIS Manager or as p.mon backend.
+Can be used either from Layer Manager or as p.mon backend.
 
 Classes:
- - Command
- - MapWindow
- - BufferedWindow
- - MapFrame
- - MapApp
+- MapFrame
+- MapApp
 
 Usage:
- python mapdisp.py monitor-identifier /path/to/command/file
+python mapdisp.py monitor-identifier /path/to/command/file
 
-(C) 2006-2008 by the GRASS Development Team
+(C) 2006-2010 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.
@@ -29,7 +26,6 @@
 
 import os
 import sys
-import time
 import glob
 import math
 import tempfile
@@ -41,7 +37,6 @@
 import wx
 import wx.aui
 
-from threading import Thread
 try:
     import subprocess
 except:
@@ -52,28 +47,30 @@
 gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
 sys.path.append(gmpath)
 
+grassPath = os.path.join(globalvar.ETCDIR, "python")
+sys.path.append(grassPath)
+
 import render
 import toolbars
-import track
 import menuform
 import gselect
 import disp_print
 import gcmd
 import dbm
+import dbm_dialogs
 import histogram
 import profile
 import globalvar
 import utils
 import gdialogs
 from grass.script import core as grass
-from vdigit import VDigitCategoryDialog as VDigitCategoryDialog
-from vdigit import VDigitZBulkDialog    as VDigitZBulkDialog
-from vdigit import VDigitDuplicatesDialog as VDigitDuplicatesDialog
-from vdigit import GV_LINES            as VDigit_Lines_Type
-from debug import Debug               as Debug
-from icon  import Icons               as Icons
+from debug import Debug
+from icon  import Icons
 from preferences import globalSettings as UserSettings
 
+from mapdisp_command import Command
+from mapdisp_window import BufferedWindow
+
 import images
 imagepath = images.__path__[0]
 sys.path.append(imagepath)
@@ -84,2545 +81,38 @@
 # for standalone app
 cmdfilename = None
 
-class Command(Thread):
-    """
-    Creates thread which will observe the command file and see, if
-    there is new command to be executed
-    """
-    def __init__ (self, parent, Map):
-        Thread.__init__(self)
-
-        global cmdfilename
-
-        self.parent = parent
-        self.map = Map
-        self.cmdfile = open(cmdfilename, "r")
-
-    def run(self):
-        """
-        Run this in thread
-        """
-        dispcmd = []
-        while 1:
-            self.parent.redraw = False
-            line = self.cmdfile.readline().strip()
-            if line == "quit":
-                break
-
-            if line:
-                try:
-                    Debug.msg (3, "Command.run(): cmd=%s" % (line))
-
-                    self.map.AddLayer(item=None, type="raster",
-                                      name='',
-                                      command=line,
-                                      l_opacity=1)
-
-                    self.parent.redraw =True
-
-                except Exception, e:
-                    print "Command Thread: ",e
-
-            time.sleep(0.1)
-
-        sys.exit()
-
-class MapWindow(object):
-    """
-    Abstract map window class
-
-    Parent for BufferedWindow class (2D display mode) and
-    GLWindow (3D display mode)
-    """
-    def __init__(self, parent, id=wx.ID_ANY,
-                 pos=wx.DefaultPosition,
-                 size=wx.DefaultSize,
-                 style=wx.NO_FULL_REPAINT_ON_RESIZE,
-                 Map=None, tree=None, gismgr=None):
-        self.parent = parent # MapFrame
-
-        #
-        # mouse attributes like currently pressed buttons, position on
-        # the screen, begin and end of dragging, and type of drawing
-        #
-        self.mouse = {
-            'l'    : False,
-            'r'    : False,
-            'm'    : False,
-            'begin': [0, 0], # screen coordinates
-            'end'  : [0, 0],
-            'use'  : "pointer",
-            'box'  : "point"
-            }
-        
-    def EraseMap(self):
-        """
-        Erase the canvas (virtual method)
-        """
-        pass
-
-    def UpdateMap(self):
-        """
-        Updates the canvas anytime there is a change to the
-        underlaying images or to the geometry of the canvas.
-        """
-        pass
-
-    def OnLeftDown(self, event):
-        pass
-
-    def OnLeftUp(self, event):
-        pass
-
-    def OnMouseMotion(self, event):
-        pass
-
-    def OnZoomToMap(self, event):
-        pass
-
-    def OnZoomToRaster(self, event):
-        pass
-
-    def GetSelectedLayer(self, type = 'layer', multi = False):
-        """
-        Get selected layer from layer tree
-
-        @param type 'item' / 'layer' / 'nviz'
-        @param multi return first selected layer or all
-        
-        @return layer / map layer properties / nviz properties
-        @return None / [] on failure
-        """
-        ret = []
-        if not self.tree or \
-                not self.tree.GetSelection():
-            if multi:
-                return []
-            else:
-                return None
-        
-        if multi and \
-                type == 'item':
-            return self.tree.GetSelections()
-        
-        for item in self.tree.GetSelections():
-            if not item.IsChecked():
-                if multi:
-                    continue
-                else:
-                    return None
-
-            if type == 'item': # -> multi = False
-                return item
-        
-            try:
-                if type == 'nviz':
-                    layer = self.tree.GetPyData(item)[0]['nviz']
-                else:
-                    layer = self.tree.GetPyData(item)[0]['maplayer']
-            except:
-                layer = None
-
-            if multi:
-                ret.append(layer)
-            else:
-                return layer
-            
-        return ret
-
-class BufferedWindow(MapWindow, wx.Window):
-    """
-    A Buffered window class.
-
-    When the drawing needs to change, you app needs to call the
-    UpdateMap() method. Since the drawing is stored in a bitmap, you
-    can also save the drawing to file by calling the
-    SaveToFile(self,file_name,file_type) method.
-    """
-
-    def __init__(self, parent, id,
-                 pos = wx.DefaultPosition,
-                 size = wx.DefaultSize,
-                 style=wx.NO_FULL_REPAINT_ON_RESIZE,
-                 Map=None, tree=None, gismgr=None):
-
-        MapWindow.__init__(self, parent, id, pos, size, style,
-                           Map, tree, gismgr)
-        wx.Window.__init__(self, parent, id, pos, size, style)
-
-        self.Map = Map
-        self.tree = tree
-        self.gismanager = gismgr
-
-        #
-        # Flags
-        #
-        self.resize = False # indicates whether or not a resize event has taken place
-        self.dragimg = None # initialize variable for map panning
-
-        #
-        # Variable for drawing on DC
-        #
-        self.pen = None      # pen for drawing zoom boxes, etc.
-        self.polypen = None  # pen for drawing polylines (measurements, profiles, etc)
-        # List of wx.Point tuples defining a polyline (geographical coordinates)
-        self.polycoords = []
-        # ID of rubber band line
-        self.lineid = None
-        # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
-        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_MOTION,       self.MouseActions)
-        self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
-        self.processMouse = True
-        
-        #
-        # Render output objects
-        #
-        self.mapfile = None   # image file to be rendered
-        self.img = ""         # wx.Image object (self.mapfile)
-        # used in digitization tool (do not redraw vector map)
-        self.imgVectorMap = None
-        # decoration overlays
-        self.overlays = {}
-        # images and their PseudoDC ID's for painting and dragging
-        self.imagedict = {}   
-        self.select = {}      # selecting/unselecting decorations for dragging
-        self.textdict = {}    # text, font, and color indexed by id
-        self.currtxtid = None # PseudoDC id for currently selected text
-
-        #
-        # Zoom objects
-        #
-        self.zoomhistory = [] # list of past zoom extents
-        self.currzoom = 0 # current set of extents in zoom history being used
-        
-        self.zoomtype = 1   # 1 zoom in, 0 no zoom, -1 zoom out
-        self.hitradius = 10 # distance for selecting map decorations
-        self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
-
-        # OnSize called to make sure the buffer is initialized.
-        # This might result in OnSize getting called twice on some
-        # platforms at initialization, but little harm done.
-        ### self.OnSize(None)
-
-        # create PseudoDC used for background map, map decorations like scales and legends
-        self.pdc = wx.PseudoDC()
-        # used for digitization tool
-        self.pdcVector = None
-        # decorations (region box, etc.)
-        self.pdcDec = wx.PseudoDC()
-        # pseudoDC for temporal objects (select box, measurement tool, etc.)
-        self.pdcTmp = wx.PseudoDC()
-        # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
-        self.redrawAll = True
-
-        # will store an off screen empty bitmap for saving to file
-        self._buffer = ''
-
-        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
-
-        # vars for handling mouse clicks
-        self.dragid   = -1
-        self.lastpos  = (0, 0)
-
-    def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0, 0, 0, 0]):
-        """
-        Draws map and overlay decorations
-        """
-        if drawid == None:
-            if pdctype == 'image' and img:
-                drawid = self.imagedict[img]
-            elif pdctype == 'clear':
-                drawid == None
-            else:
-                drawid = wx.NewId()
-        
-        if img and pdctype == 'image':
-            # self.imagedict[img]['coords'] = coords
-            self.select[self.imagedict[img]['id']] = False # ?
-
-        pdc.BeginDrawing()
-
-        if drawid != 99:
-            bg = wx.TRANSPARENT_BRUSH
-        else:
-            bg = wx.Brush(self.GetBackgroundColour())
-
-        pdc.SetBackground(bg)
-        ### pdc.Clear()
-
-        Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % \
-                       (drawid, pdctype, coords))
-
-        # set PseudoDC id
-        if drawid is not None:
-            pdc.SetId(drawid)
-        
-        if pdctype == 'clear': # erase the display
-            bg = wx.WHITE_BRUSH
-            # bg = wx.Brush(self.GetBackgroundColour())
-            pdc.SetBackground(bg)
-            pdc.RemoveAll()
-            pdc.Clear()
-            pdc.EndDrawing()
-            
-            self.Refresh()
-            return
-
-        if pdctype == 'image': # draw selected image
-            bitmap = wx.BitmapFromImage(img)
-            w,h = bitmap.GetSize()
-            pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
-            pdc.SetIdBounds(drawid, (coords[0],coords[1], w, h))
-
-        elif pdctype == 'box': # draw a box on top of the map
-            if self.pen:
-                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
-                pdc.SetPen(self.pen)
-                x2 = max(coords[0],coords[2])
-                x1 = min(coords[0],coords[2])
-                y2 = max(coords[1],coords[3])
-                y1 = min(coords[1],coords[3])
-                rwidth = x2-x1
-                rheight = y2-y1
-                rect = wx.Rect(x1,y1,rwidth,rheight)
-                pdc.DrawRectangleRect(rect)
-                pdc.SetIdBounds(drawid,rect)
-                # self.ovlcoords[drawid] = coords
-
-        elif pdctype == 'line': # draw a line on top of the map
-            if self.pen:
-                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
-                pdc.SetPen(self.pen)
-                pdc.DrawLine(coords[0], coords[1], coords[2], coords[3])
-                pdc.SetIdBounds(drawid,(coords[0], coords[1], coords[2], coords[3]))
-                # self.ovlcoords[drawid] = coords
-
-        elif pdctype == 'polyline': # draw a polyline on top of the map
-            if self.polypen:
-                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
-                pdc.SetPen(self.polypen)
-                pdc.DrawLines(coords)
-
-                # get bounding rectangle for polyline
-                xlist = []
-                ylist = []
-                if len(coords) > 0:
-                    for point in coords:
-                        x,y = point
-                        xlist.append(x)
-                        ylist.append(y)
-                    x1=min(xlist)
-                    x2=max(xlist)
-                    y1=min(ylist)
-                    y2=max(ylist)
-                    pdc.SetIdBounds(drawid,(x1,y1,x2,y2))
-                    # self.ovlcoords[drawid] = [x1,y1,x2,y2]
-
-        elif pdctype == 'point': # draw point
-            if self.pen:
-                pdc.SetPen(self.pen)
-                pdc.DrawPoint(coords[0], coords[1])
-                coordsBound = (coords[0] - 5,
-                               coords[1] - 5,
-                               coords[0] + 5,
-                               coords[1] + 5)
-                pdc.SetIdBounds(drawid, coordsBound)
-                # self.ovlcoords[drawid] = coords
-
-        elif pdctype == 'text': # draw text on top of map
-            if not img['active']:
-                return #only draw active text
-            if img.has_key('rotation'):
-                rotation = float(img['rotation'])
-            else:
-                rotation = 0.0
-            w, h = self.GetFullTextExtent(img['text'])[0:2]
-            pdc.SetFont(img['font'])
-            pdc.SetTextForeground(img['color'])
-            coords, w, h = self.TextBounds(img)
-            if rotation == 0:
-                pdc.DrawText(img['text'], coords[0], coords[1])
-            else:
-                pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
-            pdc.SetIdBounds(drawid, (coords[0], coords[1], w, h))
-            
-        pdc.EndDrawing()
-        
-        self.Refresh()
-        
-        return drawid
-
-    def TextBounds(self, textinfo):
-        """
-        Return text boundary data
-
-        @param textinfo text metadata (text, font, color, rotation)
-        @param coords reference point
-        """
-        if textinfo.has_key('rotation'):
-            rotation = float(textinfo['rotation'])
-        else:
-            rotation = 0.0
-        
-        coords = textinfo['coords']
-        
-        Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
-                   (textinfo['text'], rotation))
-
-        self.Update()
-        ### self.Refresh()
-
-        self.SetFont(textinfo['font'])
-
-        w, h = self.GetTextExtent(textinfo['text'])
-
-        if rotation == 0:
-            coords[2], coords[3] = coords[0] + w, coords[1] + h
-            return coords, w, h
-
-        boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
-        boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
-        coords[2] = coords[0] + boxw
-        coords[3] = coords[1] + boxh
-        
-        return coords, boxw, boxh
-
-    def OnPaint(self, event):
-        """
-        Draw PseudoDC's to buffered paint DC
-
-        self.pdc for background and decorations
-        self.pdcVector for vector map which is edited
-        self.pdcTmp for temporaly drawn objects (self.polycoords)
-
-        If self.redrawAll is False on self.pdcTmp content is re-drawn
-        """
-        Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
-        
-        dc = wx.BufferedPaintDC(self, self.buffer)
-        
-        ### dc.SetBackground(wx.Brush("White"))
-        dc.Clear()
-
-        # use PrepareDC to set position correctly
-        self.PrepareDC(dc)
-
-        # create a clipping rect from our position and size
-        # and update region
-        rgn = self.GetUpdateRegion().GetBox()
-        dc.SetClippingRect(rgn)
-
-        switchDraw = False
-        if self.redrawAll is None:
-            self.redrawAll = True
-            switchDraw = True
-        
-        if self.redrawAll: # redraw pdc and pdcVector
-            # draw to the dc using the calculated clipping rect
-            self.pdc.DrawToDCClipped(dc, rgn)
-
-            # draw vector map layer
-            if self.pdcVector:
-                # decorate with GDDC (transparency)
-                try:
-                    gcdc = wx.GCDC(dc)
-                    self.pdcVector.DrawToDCClipped(gcdc, rgn)
-                except NotImplementedError, e:
-                    print >> sys.stderr, e
-                    self.pdcVector.DrawToDCClipped(dc, rgn)
-
-            self.bufferLast = None
-        else: # do not redraw pdc and pdcVector
-            if self.bufferLast is None:
-                # draw to the dc
-                self.pdc.DrawToDC(dc)
-
-                if self.pdcVector:
-                    # decorate with GDDC (transparency)
-                    try:
-                        gcdc = wx.GCDC(dc)
-                        self.pdcVector.DrawToDC(gcdc)
-                    except NotImplementedError, e:
-                        print >> sys.stderr, e
-                        self.pdcVector.DrawToDC(dc)
-                        
-                # store buffered image
-                # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
-                self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
-
-            pdcLast = wx.PseudoDC()
-            pdcLast.DrawBitmap(bmp=self.bufferLast, x=0, y=0)
-            pdcLast.DrawToDC(dc)
-
-        # draw decorations (e.g. region box)
-        try:
-            gcdc = wx.GCDC(dc)
-            self.pdcDec.DrawToDC(gcdc)
-        except NotImplementedError, e:
-            print >> sys.stderr, e
-            self.pdcDec.DrawToDC(dc)
-
-        # draw temporary object on the foreground
-        ### self.pdcTmp.DrawToDCClipped(dc, rgn)
-        self.pdcTmp.DrawToDC(dc)
-
-        if switchDraw:
-            self.redrawAll = False
-                
-    def OnSize(self, event):
-        """
-        Scale map image so that it is
-        the same size as the Window
-        """
-        Debug.msg(3, "BufferedWindow.OnSize():")
-
-        # set size of the input image
-        self.Map.ChangeMapSize(self.GetClientSize())
-        # align extent based on center point and display resolution
-        # this causes that image is not resized when display windows is resized
-        # self.Map.AlignExtentFromDisplay()
-
-        # Make new off screen bitmap: this bitmap will always have the
-        # current drawing in it, so it can be used to save the image to
-        # a file, or whatever.
-        self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
-
-        # get the image to be rendered
-        self.img = self.GetImage()
-
-        # update map display
-        if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
-            self.img = self.img.Scale(self.Map.width, self.Map.height)
-            if len(self.Map.GetListOfLayers()) > 0:
-                self.UpdateMap()
-
-        # re-render image on idle
-        self.resize = True
-
-        # reposition checkbox in statusbar
-        self.parent.StatusbarReposition()
-
-        # update statusbar
-        self.parent.StatusbarUpdate()
-
-    def OnIdle(self, event):
-        """
-        Only re-render a composite map image from GRASS during
-        idle time instead of multiple times during resizing.
-        """
-        if self.resize:
-            self.UpdateMap(render=True)
-
-        event.Skip()
-
-    def SaveToFile(self, FileName, FileType):
-        """
-        This draws the psuedo DC to a buffer that
-        can be saved to a file.
-        """
-        dc = wx.BufferedPaintDC(self, self.buffer)
-        self.pdc.DrawToDC(dc)
-        if self.pdcVector:
-            self.pdcVector.DrawToDC(dc)
-        self.buffer.SaveFile(FileName, FileType)
-
-    def GetOverlay(self):
-        """
-        Converts rendered overlay files to wx.Image
-
-        Updates self.imagedict
-
-        @return list of images
-        """
-        imgs = []
-        for overlay in self.Map.GetListOfLayers(l_type="overlay", l_active=True):
-            if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
-                img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
-                self.imagedict[img] = { 'id' : overlay.id,
-                                        'layer' : overlay }
-                imgs.append(img)
-
-        return imgs
-
-    def GetImage(self):
-        """
-        Converts redered map files to wx.Image
-
-        Updates self.imagedict (id=99)
-
-        @return wx.Image instance (map composition)
-        """
-        imgId = 99
-        if self.mapfile and self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
-                os.path.getsize(self.Map.mapfile):
-            img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
-        else:
-            img = None
-
-        self.imagedict[img] = { 'id': imgId }
-
-        return img
-
-    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.
-
-        @param render re-render map composition
-        @param renderVector re-render vector map layer enabled for editing (used for digitizer)
-        """
-        start = time.clock()
-
-        self.resize = False
-
-        # if len(self.Map.GetListOfLayers()) == 0:
-        #    return False
-        
-        if self.img is None:
-            render = True
-
-        #
-        # initialize process bar (only on 'render')
-        #
-        if render is True or renderVector is True:
-            self.parent.onRenderGauge.Show()
-            if self.parent.onRenderGauge.GetRange() > 0:
-                self.parent.onRenderGauge.SetValue(1)
-        
-        #
-        # render background image if needed
-        #
-        
-        # update layer dictionary if there has been a change in layers
-        if self.tree and self.tree.reorder == True:
-            self.tree.ReorderLayers()
-            
-        # reset flag for auto-rendering
-        if self.tree:
-            self.tree.rerender = False
-        
-        if render:
-            # update display size
-            self.Map.ChangeMapSize(self.GetClientSize())
-            if self.parent.compResolution.IsChecked():
-                # use computation region resolution for rendering
-                windres = True
-            else:
-                windres = False
-            self.mapfile = self.Map.Render(force=True, mapWindow=self.parent,
-                                           windres=windres)
-        else:
-            self.mapfile = self.Map.Render(force=False, mapWindow=self.parent)
-        
-        self.img = self.GetImage() # id=99
-            
-        #
-        # clear pseudoDcs
-        #
-        for pdc in (self.pdc,
-                    self.pdcDec,
-                    self.pdcTmp):
-            pdc.Clear()
-            pdc.RemoveAll()
-        
-        #
-        # draw background map image to PseudoDC
-        #
-        if not self.img:
-            self.Draw(self.pdc, pdctype='clear')
-        else:
-            try:
-                id = self.imagedict[self.img]['id']
-            except:
-                return False
-
-            self.Draw(self.pdc, self.img, drawid=id)
-
-        #
-        # render vector map layer
-        #
-        digitToolbar = self.parent.toolbars['vdigit']
-        if renderVector and digitToolbar and \
-                digitToolbar.GetLayer():
-            # set region
-            self.parent.digit.driver.UpdateRegion()
-            # re-calculate threshold for digitization tool
-            self.parent.digit.driver.GetThreshold()
-            # draw map
-            self.pdcVector.Clear()
-            self.pdcVector.RemoveAll()
-            try:
-                item = self.tree.FindItemByData('maplayer', digitToolbar.GetLayer())
-            except TypeError:
-                item = None
-            
-            if item and self.tree.IsItemChecked(item):
-                self.parent.digit.driver.DrawMap()
-
-            # translate tmp objects (pointer position)
-            if digitToolbar.GetAction() == 'moveLine':
-                if  hasattr(self, "vdigitMove") and \
-                        self.vdigitMove.has_key('beginDiff'):
-                    # move line
-                    for id in self.vdigitMove['id']:
-                        # print self.pdcTmp.GetIdBounds(id)
-                        self.pdcTmp.TranslateId(id,
-                                                self.vdigitMove['beginDiff'][0],
-                                                self.vdigitMove['beginDiff'][1])
-                    del self.vdigitMove['beginDiff']
-        
-        #
-        # render overlays
-        #
-        for img in self.GetOverlay():
-            # draw any active and defined overlays
-            if self.imagedict[img]['layer'].IsActive():
-                id = self.imagedict[img]['id']
-                self.Draw(self.pdc, img=img, drawid=id,
-                          pdctype=self.overlays[id]['pdcType'], coords=self.overlays[id]['coords'])
-
-        for id in self.textdict.keys():
-            self.Draw(self.pdc, img=self.textdict[id], drawid=id,
-                      pdctype='text', coords=[10, 10, 10, 10])
-
-        # optionally draw computational extent box
-        self.DrawCompRegionExtent()
-
-        #
-        # redraw pdcTmp if needed
-        #
-        if len(self.polycoords) > 0:
-            self.DrawLines(self.pdcTmp)
-        
-        if self.parent.gismanager.gcpmanagement:
-            # -> GCP Manager (redraw GCPs)
-            if self.parent.toolbars['gcpdisp']:
-                if self == self.parent.TgtMapWindow:
-                    coordtype = 'target'
-                else:
-                    coordtype = 'source'
-                self.parent.gismanager.gcpmanagement.DrawGCP(coordtype)
-            
-        if self.parent.gismanager.georectifying:
-            # -> georectifier (redraw GCPs)
-            if self.parent.toolbars['georect']:
-                coordtype = 'gcpcoord'
-            else:
-                coordtype = 'mapcoord'
-            self.parent.gismanager.georectifying.DrawGCP(coordtype)
-
-        # 
-        # clear measurement
-        #
-        
-        if self.mouse["use"] == "measure":
-            self.ClearLines(pdc=self.pdcTmp)
-            self.polycoords = []
-            self.mouse['use'] = 'pointer'
-            self.mouse['box'] = 'point'
-            self.mouse['end'] = [0, 0]
-            self.SetCursor(self.parent.cursors["default"])
-            
-        stop = time.clock()
-
-        #
-        # hide process bar
-        #
-        self.parent.onRenderGauge.Hide()
-
-        #
-        # update statusbar
-        #
-        ### self.Map.SetRegion()
-        self.parent.StatusbarUpdate()
-        if grass.find_file(name = 'MASK', element = 'cell')['name']:
-            # mask found
-            self.parent.maskInfo.SetLabel(_('MASK'))
-        else:
-            self.parent.maskInfo.SetLabel('')
-        
-        Debug.msg (2, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
-                   (render, renderVector, (stop-start)))
-        
-        return True
-
-    def DrawCompRegionExtent(self):
-        """
-        Draw computational region extent in the display
-        
-        Display region is drawn as a blue box inside the computational region,
-        computational region inside a display region as a red box).
-        """
-        if hasattr(self, "regionCoords"):
-            compReg = self.Map.GetRegion()
-            dispReg = self.Map.GetCurrentRegion()
-            reg = None
-            if self.IsInRegion(dispReg, compReg):
-                self.polypen = wx.Pen(colour=wx.Colour(0, 0, 255, 128), width=3, style=wx.SOLID)
-                reg = dispReg
-            else:
-                self.polypen = wx.Pen(colour=wx.Colour(255, 0, 0, 128),
-                                      width=3, style=wx.SOLID)
-                reg = compReg
-            
-            self.regionCoords = []
-            self.regionCoords.append((reg['w'], reg['n']))
-            self.regionCoords.append((reg['e'], reg['n']))
-            self.regionCoords.append((reg['e'], reg['s']))
-            self.regionCoords.append((reg['w'], reg['s']))
-            self.regionCoords.append((reg['w'], reg['n']))
-            # draw region extent
-            self.DrawLines(pdc=self.pdcDec, polycoords=self.regionCoords)
-
-    def IsInRegion(self, region, refRegion):
-        """
-        Test if 'region' is inside of 'refRegion'
-
-        @param region input region
-        @param refRegion reference region (e.g. computational region)
-
-        @return True if region is inside of refRegion
-        @return False 
-        """
-        if region['s'] >= refRegion['s'] and \
-                region['n'] <= refRegion['n'] and \
-                region['w'] >= refRegion['w'] and \
-                region['e'] <= refRegion['e']:
-            return True
-
-        return False
-
-    def EraseMap(self):
-        """
-        Erase the canvas
-        """
-        self.Draw(self.pdc, pdctype='clear')
-                  
-        if self.pdcVector:
-            self.Draw(self.pdcVector, pdctype='clear')
-        
-        self.Draw(self.pdcDec, pdctype='clear')
-        self.Draw(self.pdcTmp, pdctype='clear')
-        
-    def DragMap(self, moveto):
-        """
-        Drag the entire map image for panning.
-        """
-
-        dc = wx.BufferedDC(wx.ClientDC(self))
-        dc.SetBackground(wx.Brush("White"))
-        dc.Clear()
-
-        self.dragimg = wx.DragImage(self.buffer)
-        self.dragimg.BeginDrag((0, 0), self)
-        self.dragimg.GetImageRect(moveto)
-        self.dragimg.Move(moveto)
-
-        self.dragimg.DoDrawImage(dc, moveto)
-        self.dragimg.EndDrag()
-
-        return True
-
-    def DragItem(self, id, event):
-        """
-        Drag an overlay decoration item
-        """
-        if id == 99 or id == '' or id == None: return
-        Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
-        x, y = self.lastpos
-        dx = event.GetX() - x
-        dy = event.GetY() - y
-        self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
-        r = self.pdc.GetIdBounds(id)
-        if id > 100: # text dragging
-            rtop = (r[0],r[1]-r[3],r[2],r[3])
-            r = r.Union(rtop)
-            rleft = (r[0]-r[2],r[1],r[2],r[3])
-            r = r.Union(rleft)
-        self.pdc.TranslateId(id, dx, dy)
-
-        r2 = self.pdc.GetIdBounds(id)
-        if id > 100: # text
-            self.textdict[id]['coords'] = r2
-        r = r.Union(r2)
-        r.Inflate(4,4)
-        self.RefreshRect(r, False)
-        self.lastpos = (event.GetX(), event.GetY())
-                
-    def MouseDraw(self, pdc=None, begin=None, end=None):
-        """
-        Mouse box or line from 'begin' to 'end'
-
-        If not given from self.mouse['begin'] to self.mouse['end'].
-
-        """
-#        self.redrawAll = False
-        
-        if not pdc:
-            return
-
-        if begin is None:
-            begin = self.mouse['begin']
-        if end is None:
-            end   = self.mouse['end']
-
-        Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
-                       (self.mouse['use'], self.mouse['box'],
-                        begin[0], begin[1], end[0], end[1]))
-
-        if self.mouse['box'] == "box":
-            boxid = wx.ID_NEW
-            mousecoords = [begin[0], begin[1],
-                           end[0], end[1]]
-            r = pdc.GetIdBounds(boxid)
-            r.Inflate(4,4)
-            pdc.ClearId(boxid)
-            self.RefreshRect(r, False)
-            pdc.SetId(boxid)
-            self.Draw(pdc, drawid=boxid, pdctype='box', coords=mousecoords)
-        elif self.mouse['box'] == "line" or self.mouse['box'] == 'point':
-            self.lineid = wx.ID_NEW
-            mousecoords = [begin[0], begin[1], \
-                           end[0], end[1]]
-            x1=min(begin[0],end[0])
-            x2=max(begin[0],end[0])
-            y1=min(begin[1],end[1])
-            y2=max(begin[1],end[1])
-            r = wx.Rect(x1,y1,x2-x1,y2-y1)
-            r.Inflate(4,4)
-            try:
-                pdc.ClearId(self.lineid)
-            except:
-                pass
-            self.RefreshRect(r, False)
-            pdc.SetId(self.lineid)
-
-            self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=mousecoords)
-
-    def DrawLines(self, pdc=None, polycoords=None):
-        """
-        Draw polyline in PseudoDC
-
-        Set self.pline to wx.NEW_ID + 1
-
-        polycoords - list of polyline vertices, geographical coordinates
-        (if not given, self.polycoords is used)
-
-        """
-        if not pdc:
-            pdc = self.pdcTmp
-
-        if not polycoords:
-            polycoords = self.polycoords
-        
-        if len(polycoords) > 0:
-            self.plineid = wx.ID_NEW + 1
-            # convert from EN to XY
-            coords = []
-            for p in polycoords:
-                coords.append(self.Cell2Pixel(p))
-
-            self.Draw(pdc, drawid=self.plineid, pdctype='polyline', coords=coords)
-            
-            Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
-                           (coords, self.plineid))
-
-            return self.plineid
-
-        return -1
-
-    def DrawCross(self, pdc, coords, size, rotation=0,
-                  text=None, textAlign='lr', textOffset=(5, 5)):
-        """Draw cross in PseudoDC
-
-        @todo implement rotation
-
-        @param pdc PseudoDC
-        @param coord center coordinates
-        @param rotation rotate symbol
-        @param text draw also text (text, font, color, rotation)
-        @param textAlign alignment (default 'lower-right')
-        @textOffset offset for text (from center point)
-        """
-        Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
-                  (pdc, coords, size))
-        coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
-                       (coords[0], coords[1] - size, coords[0], coords[1] + size))
-
-        self.lineid = wx.NewId()
-        for lineCoords in coordsCross:
-            self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=lineCoords)
-
-        if not text:
-            return self.lineid
-
-        if textAlign == 'ul':
-            coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
-        elif textAlign == 'ur':
-            coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
-        elif textAlign == 'lr':
-            coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
-        else:
-            coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
-        
-        self.Draw(pdc, img=text,
-                  pdctype='text', coords=coord)
-
-        return self.lineid
-
-    def MouseActions(self, event):
-        """
-        Mouse motion and button click notifier
-        """
-        if not self.processMouse:
-            return
-        
-        ### if self.redrawAll is False:
-        ###    self.redrawAll = True
-        
-        # zoom with mouse wheel
-        if event.GetWheelRotation() != 0:
-            self.OnMouseWheel(event)
-            
-        # left mouse button pressed
-        elif event.LeftDown():
-            self.OnLeftDown(event)
-
-        # left mouse button released
-        elif event.LeftUp():
-            self.OnLeftUp(event)
-
-        # dragging
-        elif event.Dragging():
-            self.OnDragging(event)
-
-        # double click
-        elif event.ButtonDClick():
-            self.OnButtonDClick(event)
-
-        # middle mouse button pressed
-        elif event.MiddleDown():
-            self.OnMiddleDown(event)
-
-        # right mouse button pressed
-        elif event.RightDown():
-            self.OnRightDown(event)
-
-        # right mouse button released
-        elif event.RightUp():
-            self.OnRightUp(event)
-
-        elif event.Entering():
-            self.OnMouseEnter(event)
-
-        elif event.Moving():
-            self.OnMouseMoving(event)
-
-#        event.Skip()
-        
-    def OnMouseWheel(self, event):
-        """
-        Mouse wheel moved
-        """
-        self.processMouse = False
-        current  = event.GetPositionTuple()[:]
-        wheel = event.GetWheelRotation()
-        Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
-        # zoom 1/2 of the screen, centered to current mouse position (TODO: settings)
-        begin = (current[0] - self.Map.width / 4,
-                 current[1] - self.Map.height / 4)
-        end   = (current[0] + self.Map.width / 4,
-                 current[1] + self.Map.height / 4)
-
-        if wheel > 0:
-            zoomtype = 1
-        else:
-            zoomtype = -1
-
-        # zoom
-        self.Zoom(begin, end, zoomtype)
-
-        # redraw map
-        self.UpdateMap()
-
-        ### self.OnPaint(None)
-
-        # update statusbar
-        self.parent.StatusbarUpdate()
-
-        self.Refresh()
-        self.processMouse = True
-#        event.Skip()
-
-    def OnDragging(self, event):
-        """
-        Mouse dragging with left button down
-        """
-        Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
-        current  = event.GetPositionTuple()[:]
-        previous = self.mouse['begin']
-        move = (current[0] - previous[0],
-                current[1] - previous[1])
-
-        digitToolbar = self.parent.toolbars['vdigit']
-
-        # dragging or drawing box with left button
-        if self.mouse['use'] == 'pan':
-            self.DragMap(move)
-
-        # dragging decoration overlay item
-        elif (self.mouse['use'] == 'pointer' and 
-                not digitToolbar and 
-                self.dragid != None):
-            self.DragItem(self.dragid, event)
-
-        # dragging anything else - rubber band box or line
-        else:
-            if (self.mouse['use'] == 'pointer' and 
-                not digitToolbar): return
-            self.mouse['end'] = event.GetPositionTuple()[:]
-            digitClass = self.parent.digit
-            if (event.LeftIsDown() and 
-                not (digitToolbar and 
-                    digitToolbar.GetAction() in ("moveLine",) and 
-                    digitClass.driver.GetSelected() > 0)):
-                # draw box only when left mouse button is pressed
-                self.MouseDraw(pdc=self.pdcTmp)
-      
-#        event.Skip()
-
-    def OnLeftDown(self, event):
-        """
-        Left mouse button pressed
-        """
-        Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
-                   self.mouse["use"])
-
-        self.mouse['begin'] = event.GetPositionTuple()[:]
-
-        if self.mouse["use"] in ["measure", "profile"]:
-            # measure or profile
-            if len(self.polycoords) == 0:
-                self.mouse['end'] = self.mouse['begin']
-                self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
-                self.ClearLines(pdc=self.pdcTmp)
-            else:
-                self.mouse['begin'] = self.mouse['end']
-        elif self.mouse['use'] == 'zoom':
-            pass
-        elif self.mouse["use"] == "pointer" and self.parent.toolbars['vdigit']:
-            # digitization
-            digitToolbar = self.parent.toolbars['vdigit']
-            digitClass   = self.parent.digit
-            east, north = self.Pixel2Cell(self.mouse['begin'])
-
-            try:
-                map = digitToolbar.GetLayer().GetName()
-            except:
-                map = None
-                wx.MessageBox(parent=self,
-                              message=_("No vector map selected for editing."),
-                              caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                event.Skip()
-                return
-
-            # calculate position of 'update record' dialog
-            position = self.mouse['begin']
-            posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
-                                             position[1] + self.dialogOffset))
-
-            if digitToolbar.GetAction() not in ("moveVertex", "addVertex",
-                                                "removeVertex", "editLine"):
-                # set pen
-                self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
-                self.polypen = wx.Pen(colour='dark green', width=2, style=wx.SOLID)
-
-            if digitToolbar.GetAction() in ("addVertex", "removeVertex"):
-                # unselect
-                digitClass.driver.SetSelected([])
-
-            if digitToolbar.GetAction() == "addLine":
-                if digitToolbar.GetAction('type') in ["point", "centroid"]:
-                    # add new point
-                    if digitToolbar.GetAction('type') == 'point':
-                        point = True
-                    else:
-                        point = False
-
-                    fid = digitClass.AddPoint(map, point, east, north)
-                    if fid < 0:
-                        return
-
-                    self.UpdateMap(render=False) # redraw map
-
-                    # add new record into atribute table
-                    if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled')  is True:
-                        # select attributes based on layer and category
-                        cats = { fid : {
-                                UserSettings.Get(group='vdigit', key="layer", subkey='value') :
-                                    (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
-                                }}
-                        addRecordDlg = dbm.DisplayAttributesDialog(parent=self, map=map,
-                                                                   cats=cats,
-                                                                   pos=posWindow,
-                                                                   action="add")
-                        if addRecordDlg.mapDBInfo and \
-                               addRecordDlg.ShowModal() == wx.ID_OK:
-                            sqlfile = tempfile.NamedTemporaryFile(mode="w")
-                            for sql in addRecordDlg.GetSQLString():
-                                sqlfile.file.write(sql + ";\n")
-                            sqlfile.file.flush()
-                            executeCommand = gcmd.Command(cmd=["db.execute",
-                                                               "--q",
-                                                               "input=%s" % sqlfile.name])
-
-                elif digitToolbar.GetAction('type') in ["line", "boundary"]:
-                    # add new point to the line
-                    self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
-                    self.DrawLines(pdc=self.pdcTmp)
-            
-            elif digitToolbar.GetAction() == "editLine" and hasattr(self, "vdigitMove"):
-                self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
-                self.vdigitMove['id'].append(wx.NewId())
-                self.DrawLines(pdc=self.pdcTmp)
-
-            elif digitToolbar.GetAction() == "deleteLine":
-                pass
-
-            elif digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] and \
-                    not hasattr(self, "vdigitMove"):
-                self.vdigitMove = {}
-                # geographic coordinates of initial position (left-down)
-                self.vdigitMove['begin'] = None
-                # list of ids to modify    
-                self.vdigitMove['id'] = []
-                # ids geographic coordinates
-                self.vdigitMove['coord'] = {}
-                
-                if digitToolbar.GetAction() in ["moveVertex", "editLine"]:
-                    # set pen
-                    pcolor = UserSettings.Get(group='vdigit', key="symbol",
-                                              subkey=["highlight", "color"])
-                    self.pen = self.polypen = wx.Pen(colour=pcolor,
-                                                     width=2, style=wx.SHORT_DASH)
-                    self.pdcTmp.SetPen(self.polypen)
-
-            elif digitToolbar.GetAction() == "splitLine":
-                # unselect
-                digitClass.driver.SetSelected([])
-
-            elif digitToolbar.GetAction() in ["displayAttrs", "displayCats"]:
-                qdist = digitClass.driver.GetThreshold(type='selectThresh')
-                coords = (east, north)
-                if digitClass.type == 'vdigit':
-                    # unselect
-                    digitClass.driver.SetSelected([])
-
-                    # select feature by point
-                    cats = {}
-                    if digitClass.driver.SelectLineByPoint(coords,
-                                                        digitClass.GetSelectType()) is not None:
-                        if UserSettings.Get(group='vdigit', key='checkForDupl',
-                                            subkey='enabled'):
-                            lines = digitClass.driver.GetSelected()
-                        else:
-                            lines = (digitClass.driver.GetSelected()[0],) # only first found
-                        
-                        for line in lines:
-                            cats[line] = digitClass.GetLineCats(line)
-                    
-                if digitToolbar.GetAction() == "displayAttrs":
-                    # select attributes based on coordinates (all layers)
-                    if self.parent.dialogs['attributes'] is None:
-                        if digitClass.type == 'vedit':
-                            self.parent.dialogs['attributes'] = dbm.DisplayAttributesDialog(parent=self, map=map,
-                                                                                            query=(coords, qdist),
-                                                                                            pos=posWindow,
-                                                                                            action="update")
-                        else:
-                            self.parent.dialogs['attributes'] = dbm.DisplayAttributesDialog(parent=self, map=map,
-                                                                                            cats=cats,
-                                                                                            action="update")
-                    else:
-                        # update currently open dialog
-                        if digitClass.type == 'vedit':
-                            self.parent.dialogs['attributes'].UpdateDialog(query=(coords, qdist))
-                        else:
-                            # upgrade dialog
-                            self.parent.dialogs['attributes'].UpdateDialog(cats=cats)
-
-                    if self.parent.dialogs['attributes']:
-                        if len(cats.keys()) > 0:
-                            # highlight feature & re-draw map
-                            if not self.parent.dialogs['attributes'].IsShown():
-                                self.parent.dialogs['attributes'].Show()
-                        else:
-                            if self.parent.dialogs['attributes'] and \
-                                   self.parent.dialogs['attributes'].IsShown():
-                                self.parent.dialogs['attributes'].Hide()
-
-                else: # displayCats
-                    if self.parent.dialogs['category'] is None:
-                        # open new dialog
-                        if digitClass.type == 'vedit':
-                            dlg = VDigitCategoryDialog(parent=self,
-                                                        map=map,
-                                                        query=(coords, qdist),
-                                                        pos=posWindow,
-                                                        title=_("Update categories"))
-                            self.parent.dialogs['category'] = dlg
-                        else:
-                            dlg = VDigitCategoryDialog(parent=self,
-                                                       map=map,
-                                                       cats=cats,
-                                                       pos=posWindow,
-                                                       title=_("Update categories"))
-                            self.parent.dialogs['category'] = dlg
-                    else:
-                        # update currently open dialog
-                        if digitClass.type == 'vedit':
-                            self.parent.dialogs['category'].UpdateDialog(query=(coords, qdist))
-                        else:
-                            # upgrade dialog
-                            self.parent.dialogs['category'].UpdateDialog(cats=cats)
-                            
-                    if self.parent.dialogs['category']:
-                        if len(cats.keys()) > 0:
-                            # highlight feature & re-draw map
-                            ### digitClass.driver.SetSelected(line)
-                            if not self.parent.dialogs['category'].IsShown():
-                                self.parent.dialogs['category'].Show()
-                        else:
-                            if self.parent.dialogs['category'].IsShown():
-                                self.parent.dialogs['category'].Hide()
-                
-                self.UpdateMap(render=False)
-
-            elif digitToolbar.GetAction() in ("copyCats", "copyAttrs"):
-                if not hasattr(self, "copyCatsList"):
-                    self.copyCatsList = []
-                else:
-                    self.copyCatsIds = []
-                    self.mouse['box'] = 'box'
-
-            elif digitToolbar.GetAction() == "copyLine":
-                self.copyIds = []
-                self.layerTmp = None
-
-            elif digitToolbar.GetAction() == "zbulkLine":
-                if len(self.polycoords) > 1: # start new line
-                    self.polycoords = []
-                    self.ClearLines(pdc=self.pdcTmp)
-                self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
-                if len(self.polycoords) == 1:
-                    begin = self.Pixel2Cell(self.polycoords[-1])
-                    end   = self.Pixel2Cell(self.mouse['end'])
-                else:
-                    end   = self.Pixel2Cell(self.polycoords[-1])
-                    begin = self.Pixel2Cell(self.mouse['begin'])
-
-                    self.DrawLines(self.pdcTmp, polycoords = [begin, end])
-        elif self.mouse['use'] == 'pointer':
-            # get decoration or text id
-            self.idlist = []
-            self.dragid = ''
-            self.lastpos = self.mouse['begin']
-            idlist = self.pdc.FindObjects(x=self.lastpos[0], y=self.lastpos[1],
-                                          radius=self.hitradius)
-                                          
-            if 99 in idlist: idlist.remove(99)                             
-            if idlist != [] :
-                self.dragid = idlist[0] #drag whatever is on top
-        else:
-            pass
-
-        event.Skip()
-
-    def OnLeftUp(self, event):
-        """
-        Left mouse button released
-        """
-        Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
-                   self.mouse["use"])
-
-        self.mouse['end'] = event.GetPositionTuple()[:]
-
-        if self.mouse['use'] in ["zoom", "pan"]:
-            # set region in zoom or pan
-            begin = self.mouse['begin']
-            end = self.mouse['end']
-            if self.mouse['use'] == 'zoom':
-                # set region for click (zero-width box)
-                if begin[0] - end[0] == 0 or \
-                        begin[1] - end[1] == 0:
-                    # zoom 1/2 of the screen (TODO: settings)
-                    begin = (end[0] - self.Map.width / 4,
-                             end[1] - self.Map.height / 4)
-                    end   = (end[0] + self.Map.width / 4,
-                             end[1] + self.Map.height / 4)
-
-            self.Zoom(begin, end, self.zoomtype)
-
-            # redraw map
-            self.UpdateMap(render=True)
-
-            # update statusbar
-            self.parent.StatusbarUpdate()
-
-        elif self.mouse["use"] == "query":
-            # querying
-            self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
-
-        elif self.mouse["use"] == "queryVector":
-            # editable mode for vector map layers
-            self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
-
-            # clear temp canvas
-            self.UpdateMap(render=False, renderVector=False)
-            
-        elif self.mouse["use"] in ["measure", "profile"]:
-            # measure or profile
-            if self.mouse["use"] == "measure":
-                self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
-
-            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
-            self.ClearLines(pdc=self.pdcTmp)
-            self.DrawLines(pdc=self.pdcTmp)
-        
-        elif self.mouse["use"] == "pointer" and self.parent.gismanager.gcpmanagement:
-            # -> GCP Manager
-            coord = self.Pixel2Cell(self.mouse['end'])
-            if self.parent.toolbars['gcpdisp']:
-                if self.parent.MapWindow == self.parent.SrcMapWindow:
-                    coordtype = 'source'
-                else:
-                    coordtype = 'target'
-
-                self.parent.gismanager.gcpmanagement.SetGCPData(coordtype, coord, self, confirm=True)
-                self.UpdateMap(render = False, renderVector = False)
-
-        elif self.mouse["use"] == "pointer" and self.parent.gismanager.georectifying:
-            # -> georectifying
-            coord = self.Pixel2Cell(self.mouse['end'])
-            if self.parent.toolbars['georect']:
-                coordtype = 'gcpcoord'
-            else:
-                coordtype = 'mapcoord'
-
-            self.parent.gismanager.georectifying.SetGCPData(coordtype, coord, self)
-            self.UpdateMap(render=False, renderVector=False)
-
-        elif self.mouse["use"] == "pointer" and self.parent.toolbars['vdigit']:
-            # digitization tool active
-            digitToolbar = self.parent.toolbars['vdigit']
-            digitClass   = self.parent.digit
-
-            pos1 = self.Pixel2Cell(self.mouse['begin'])
-            pos2 = self.Pixel2Cell(self.mouse['end'])
-
-            if hasattr(self, "vdigitMove"):
-                if len(digitClass.driver.GetSelected()) == 0:
-                    self.vdigitMove['begin'] = pos1 # left down
-                ### else:
-                ###    dx = pos2[0] - pos1[0] ### ???
-                ###    dy = pos2[1] - pos1[1]
-                ###    self.vdigitMove = (self.vdigitMove['begin'][0] + dx,
-                ###                       self.vdigitMove['begin'][1] + dy)
-                
-                # eliminate initial mouse moving efect
-                self.mouse['begin'] = self.mouse['end'] 
-
-            if digitToolbar.GetAction() in ["deleteLine", "moveLine", "moveVertex",
-                                            "copyCats", "copyAttrs", "editLine", "flipLine",
-                                            "mergeLine", "snapLine",
-                                            "queryLine", "breakLine", "typeConv", "connectLine"]:
-                nselected = 0
-                # -> delete line || move line || move vertex
-                if digitToolbar.GetAction() in ["moveVertex", "editLine"]:
-                    if len(digitClass.driver.GetSelected()) == 0:
-                        nselected = digitClass.driver.SelectLineByPoint(pos1, type=VDigit_Lines_Type)
-                        if digitToolbar.GetAction() == "editLine":
-                            try:
-                                selVertex = digitClass.driver.GetSelectedVertex(pos1)[0]
-                            except IndexError:
-                                selVertex = None
-
-                            if selVertex:
-                                # self.UpdateMap(render=False)
-                                ids = digitClass.driver.GetSelected(grassId=False)
-                                # move this line to tmp layer
-                                self.polycoords = []
-                                for id in ids:
-                                    if id % 2: # register only vertices
-                                        e, n = self.Pixel2Cell(self.pdcVector.GetIdBounds(id)[0:2])
-                                        self.polycoords.append((e, n))
-                                    # self.pdcVector.RemoveId(id)
-                                digitClass.driver.DrawSelected(False) 
-                                
-                                if selVertex < ids[-1] / 2:
-                                    # choose first or last node of line
-                                    self.vdigitMove['id'].reverse()
-                                    self.polycoords.reverse()
-                            else:
-                                # unselect
-                                digitClass.driver.SetSelected([])
-                                del self.vdigitMove
-                                
-                            self.UpdateMap(render=False)
-
-                        
-                elif digitToolbar.GetAction() in ("copyCats", "copyAttrs"):
-                    if not hasattr(self, "copyCatsIds"):
-                        # 'from' -> select by point
-                        nselected = digitClass.driver.SelectLineByPoint(pos1, digitClass.GetSelectType())
-                        if nselected:
-                            self.copyCatsList = digitClass.driver.GetSelected()
-                    else:
-                        # -> 'to' -> select by bbox
-                        digitClass.driver.SetSelected([])
-                        # return number of selected features (by box/point)
-                        nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
-                                                                       digitClass.GetSelectType())
-                        if nselected == 0:
-                            if digitClass.driver.SelectLineByPoint(pos1,
-                                                                   digitClass.GetSelectType()) is not None:
-                                nselected = 1
-
-                        if nselected > 0:
-                            self.copyCatsIds = digitClass.driver.GetSelected()
-
-                elif digitToolbar.GetAction() == "queryLine":
-                    selected = digitClass.SelectLinesByQuery(pos1, pos2)
-                    nselected = len(selected)
-                    if nselected > 0:
-                        digitClass.driver.SetSelected(selected)
-
-                else:
-                    # -> moveLine || deleteLine, etc. (select by point/box)
-                    if digitToolbar.GetAction() == 'moveLine' and \
-                           len(digitClass.driver.GetSelected()) > 0:
-                        nselected = 0
-                    else:
-                        if digitToolbar.GetAction() == 'moveLine':
-                            drawSeg = True
-                        else:
-                            drawSeg = False
-                        nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
-                                                                       digitClass.GetSelectType(),
-                                                                       drawSeg)
-                        
-                        if nselected == 0:
-                            if digitClass.driver.SelectLineByPoint(pos1,
-                                                                   digitClass.GetSelectType()) is not None:
-                                nselected = 1
-                
-                if nselected > 0:
-                    if digitToolbar.GetAction() in ["moveLine", "moveVertex"]:
-                        # get pseudoDC id of objects which should be redrawn
-                        if digitToolbar.GetAction() == "moveLine":
-                            # -> move line
-                            self.vdigitMove['id'] = digitClass.driver.GetSelected(grassId=False)
-                            self.vdigitMove['coord'] = digitClass.driver.GetSelectedCoord()
-                        elif digitToolbar.GetAction() == "moveVertex":
-                            # -> move vertex
-                            self.vdigitMove['id'] = digitClass.driver.GetSelectedVertex(pos1)
-                            if len(self.vdigitMove['id']) == 0: # no vertex found
-                                digitClass.driver.SetSelected([])
-
-                                   
-                    #
-                    # check for duplicates
-                    #
-                    if UserSettings.Get(group='vdigit', key='checkForDupl', subkey='enabled') is True:
-                        dupl = digitClass.driver.GetDuplicates()
-                        self.UpdateMap(render=False)
-
-                        if dupl:
-                            posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
-                                                             self.mouse['end'][1] + self.dialogOffset))
-                            
-                            dlg = VDigitDuplicatesDialog(parent=self, data=dupl, pos=posWindow)
-
-                            if dlg.ShowModal() == wx.ID_OK:
-                                digitClass.driver.UnSelect(dlg.GetUnSelected())
-                                # update selected
-                                self.UpdateMap(render=False)
-
-                    if digitToolbar.GetAction() != "editLine":
-                        # -> move line || move vertex
-                        self.UpdateMap(render=False)
-
-                else: # no vector object found
-                    if not (digitToolbar.GetAction() in ["moveLine", "moveVertex"] and \
-                                len(self.vdigitMove['id']) > 0):
-                        # avoid left-click when features are already selected
-                        self.UpdateMap(render=False, renderVector=False)
-
-            elif digitToolbar.GetAction() in ["splitLine", "addVertex", "removeVertex"]:
-                pointOnLine = digitClass.driver.SelectLineByPoint(pos1,
-                                                                  type=VDigit_Lines_Type)
-                if pointOnLine:
-                    if digitToolbar.GetAction() in ["splitLine", "addVertex"]:
-                        self.UpdateMap(render=False) # highlight object
-                        self.DrawCross(pdc=self.pdcTmp, coords=self.Cell2Pixel(pointOnLine),
-                                       size=5)
-                    elif digitToolbar.GetAction() == "removeVertex":
-                        # get only id of vertex
-                        try:
-                            id = digitClass.driver.GetSelectedVertex(pos1)[0]
-                        except IndexError:
-                            id = None
-
-                        if id:
-                            x, y = self.pdcVector.GetIdBounds(id)[0:2]
-                            self.pdcVector.RemoveId(id)
-                            self.UpdateMap(render=False) # highlight object
-                            self.DrawCross(pdc=self.pdcTmp, coords=(x, y),
-                                           size=5)
-                        else:
-                            # unselect
-                            digitClass.driver.SetSelected([])
-                            self.UpdateMap(render=False)
-                            
-            elif digitToolbar.GetAction() == "copyLine":
-                if UserSettings.Get(group='vdigit', key='bgmap',
-                                    subkey='value', internal=True) == '':
-                    # no background map -> copy from current vector map layer
-                    nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
-                                                                   digitClass.GetSelectType())
-
-                    if nselected > 0:
-                        # highlight selected features
-                        self.UpdateMap(render=False)
-                    else:
-                        self.UpdateMap(render=False, renderVector=False)
-                else:
-                    # copy features from background map
-                    self.copyIds = digitClass.SelectLinesFromBackgroundMap(pos1, pos2)
-                    if len(self.copyIds) > 0:
-                        color = UserSettings.Get(group='vdigit', key='symbol',
-                                                 subkey=['highlight', 'color'])
-                        colorStr = str(color[0]) + ":" + \
-                            str(color[1]) + ":" + \
-                            str(color[2])
-                        dVectTmp = ['d.vect',
-                                    'map=%s' % UserSettings.Get(group='vdigit', key='bgmap',
-                                                                subkey='value', internal=True),
-                                    'cats=%s' % utils.ListOfCatsToRange(self.copyIds),
-                                    '-i',
-                                    'color=%s' % colorStr,
-                                    'fcolor=%s' % colorStr,
-                                    'type=point,line,boundary,centroid',
-                                    'width=2']
-                        
-                        self.layerTmp = self.Map.AddLayer(type='vector',
-                                                          name=globalvar.QUERYLAYER,
-                                                          command=dVectTmp)
-                        self.UpdateMap(render=True, renderVector=False)
-                    else:
-                        self.UpdateMap(render=False, renderVector=False)
-                    self.redrawAll = None
-
-            elif digitToolbar.GetAction() == "zbulkLine" and len(self.polycoords) == 2:
-                # select lines to be labeled
-                pos1 = self.polycoords[0]
-                pos2 = self.polycoords[1]
-                nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
-                                                               digitClass.GetSelectType())
-
-                if nselected > 0:
-                    # highlight selected features
-                    self.UpdateMap(render=False)
-                    self.DrawLines(pdc=self.pdcTmp) # redraw temp line
-                else:
-                    self.UpdateMap(render=False, renderVector=False)
-
-            elif digitToolbar.GetAction() == "connectLine":
-                if len(digitClass.driver.GetSelected()) > 0:
-                    self.UpdateMap(render=False)
-                    
-            if len(digitClass.driver.GetSelected()) > 0:
-                self.redrawAll = None
-                ### self.OnPaint(None)
-                
-        elif (self.mouse['use'] == 'pointer' and 
-                self.dragid >= 0):
-            # end drag of overlay decoration
-            
-            if self.dragid < 99 and self.overlays.has_key(self.dragid):
-                self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
-            elif self.dragid > 100 and self.textdict.has_key(self.dragid):
-                self.textdict[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
-            else:
-                pass
-            self.dragid = None
-            self.currtxtid = None
-#            self.UpdateMap(render=True)
-            
-        else:
-            pass
-                                              
-#        event.Skip()
-
-    def OnButtonDClick(self, event):
-        """
-        Mouse button double click
-        """
-        Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
-                   self.mouse["use"])
-
-        if self.mouse["use"] == "measure":
-            # measure
-            self.ClearLines(pdc=self.pdcTmp)
-            self.polycoords = []
-            self.mouse['use'] = 'pointer'
-            self.mouse['box'] = 'point'
-            self.mouse['end'] = [0, 0]
-            self.Refresh()
-            self.SetCursor(self.parent.cursors["default"])
-        elif self.mouse["use"] == "profile":
-            # profile
-            pass
-        #                self.pdc.ClearId(self.lineid)
-        #                self.pdc.ClearId(self.plineid)
-        #                print 'coordinates: ',self.polycoords
-        #                self.polycoords = []
-        #                self.mouse['begin'] = self.mouse['end'] = [0, 0]
-        #                self.Refresh()
-        elif self.mouse['use'] == 'pointer' and self.parent.toolbars['vdigit']:
-            # digitization tool
-            pass
-        else:
-            # select overlay decoration options dialog
-            clickposition = event.GetPositionTuple()[:]
-            idlist  = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
-            if idlist == []:
-                return
-            self.dragid = idlist[0]
-
-            # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
-            if self.dragid > 100:
-                self.currtxtid = self.dragid
-                self.parent.OnAddText(None)
-            elif self.dragid == 0:
-                self.parent.OnAddBarscale(None)
-            elif self.dragid == 1:
-                self.parent.OnAddLegend(None)
-                
-#        event.Skip()
-
-    def OnRightDown(self, event):
-        """
-        Right mouse button pressed
-        """
-        Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
-                   self.mouse["use"])
-                   
-        digitToolbar = self.parent.toolbars['vdigit']
-        if digitToolbar:
-            digitClass = self.parent.digit
-            # digitization tool (confirm action)
-            if digitToolbar.GetAction() in ["moveLine", "moveVertex"] and \
-                    hasattr(self, "vdigitMove"):
-
-                pFrom = self.vdigitMove['begin']
-                pTo = self.Pixel2Cell(event.GetPositionTuple())
-                
-                move = (pTo[0] - pFrom[0],
-                        pTo[1] - pFrom[1])
-                
-                if digitToolbar.GetAction() == "moveLine":
-                    # move line
-                    if digitClass.MoveSelectedLines(move) < 0:
-                        return
-                elif digitToolbar.GetAction() == "moveVertex":
-                    # move vertex
-                    if digitClass.MoveSelectedVertex(pFrom, move) < 0:
-                        return
-                
-                del self.vdigitMove
-                
-        event.Skip()
-
-    def OnRightUp(self, event):
-        """
-        Right mouse button released
-        """
-        Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
-                   self.mouse["use"])
-
-        digitToolbar = self.parent.toolbars['vdigit']
-        if digitToolbar:
-            digitClass = self.parent.digit
-            # digitization tool (confirm action)
-            if digitToolbar.GetAction() == "addLine" and \
-                    digitToolbar.GetAction('type') in ["line", "boundary"]:
-                # -> add new line / boundary
-                try:
-                    map = digitToolbar.GetLayer().GetName()
-                except:
-                    map = None
-                    wx.MessageBox(parent=self,
-                                  message=_("No vector map selected for editing."),
-                                  caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                    
-                if map:
-                    # mapcoords = []
-                    # xy -> EN
-                    # for coord in self.polycoords:
-                    #    mapcoords.append(self.Pixel2Cell(coord))
-                    if digitToolbar.GetAction('type') == 'line':
-                        line = True
-                    else:
-                        line = False
-
-                    if len(self.polycoords) < 2: # ignore 'one-point' lines
-                        return
-                    
-                    fid = digitClass.AddLine(map, line, self.polycoords)
-                    if fid < 0:
-                        return
-                    
-                    position = self.Cell2Pixel(self.polycoords[-1])
-                    self.polycoords = []
-                    self.UpdateMap(render=False)
-                    self.redrawAll = True
-                    self.Refresh()
-                    
-                    # add new record into atribute table
-                    if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') is True:
-                        posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
-                                                         position[1] + self.dialogOffset))
-
-                        # select attributes based on layer and category
-                        cats = { fid : {
-                                UserSettings.Get(group='vdigit', key="layer", subkey='value') :
-                                    (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
-                                }}
-
-                        addRecordDlg = dbm.DisplayAttributesDialog(parent=self, map=map,
-                                                                   cats=cats,
-                                                                   pos=posWindow,
-                                                                   action="add")
-                        if addRecordDlg.mapDBInfo and \
-                               addRecordDlg.ShowModal() == wx.ID_OK:
-                            sqlfile = tempfile.NamedTemporaryFile(mode="w")
-                            for sql in addRecordDlg.GetSQLString():
-                                sqlfile.file.write(sql + ";\n")
-                            sqlfile.file.flush()
-                            executeCommand = gcmd.Command(cmd=["db.execute",
-                                                              "--q",
-                                                              "input=%s" % sqlfile.name])
-            elif digitToolbar.GetAction() == "deleteLine":
-                # -> delete selected vector features
-                if digitClass.DeleteSelectedLines() < 0:
-                    return
-            elif digitToolbar.GetAction() == "splitLine":
-                # split line
-                if digitClass.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0:
-                    return
-            elif digitToolbar.GetAction() == "addVertex":
-                # add vertex
-                if digitClass.AddVertex(self.Pixel2Cell(self.mouse['begin'])) < 0:
-                    return
-            elif digitToolbar.GetAction() == "removeVertex":
-                # remove vertex
-                if digitClass.RemoveVertex(self.Pixel2Cell(self.mouse['begin'])) < 0:
-                    return
-            elif digitToolbar.GetAction() in ("copyCats", "copyAttrs"):
-                try:
-                    if digitToolbar.GetAction() == 'copyCats':
-                        if digitClass.CopyCats(self.copyCatsList,
-                                               self.copyCatsIds, copyAttrb=False) < 0:
-                            return
-                    else:
-                        if digitClass.CopyCats(self.copyCatsList,
-                                               self.copyCatsIds, copyAttrb=True) < 0:
-                            return
-                    
-                    del self.copyCatsList
-                    del self.copyCatsIds
-                except AttributeError:
-                    pass
-            elif digitToolbar.GetAction() == "editLine" and \
-                    hasattr(self, "vdigitMove"):
-                line = digitClass.driver.GetSelected()
-                if digitClass.EditLine(line, self.polycoords) < 0:
-                    return
-                
-                del self.vdigitMove
-                
-            elif digitToolbar.GetAction() == "flipLine":
-                if digitClass.FlipLine() < 0:
-                    return
-            elif digitToolbar.GetAction() == "mergeLine":
-                if digitClass.MergeLine() < 0:
-                    return
-            elif digitToolbar.GetAction() == "breakLine":
-                if digitClass.BreakLine() < 0:
-                    return
-            elif digitToolbar.GetAction() == "snapLine":
-                if digitClass.SnapLine() < 0:
-                    return
-            elif digitToolbar.GetAction() == "connectLine":
-                if len(digitClass.driver.GetSelected()) > 1:
-                    if digitClass.ConnectLine() < 0:
-                        return
-            elif digitToolbar.GetAction() == "copyLine":
-                if digitClass.CopyLine(self.copyIds) < 0:
-                    return
-                del self.copyIds
-                if self.layerTmp:
-                    self.Map.DeleteLayer(self.layerTmp)
-                    self.UpdateMap(render=True, renderVector=False)
-                del self.layerTmp
-
-            elif digitToolbar.GetAction() == "zbulkLine" and len(self.polycoords) == 2:
-                pos1 = self.polycoords[0]
-                pos2 = self.polycoords[1]
-
-                selected = digitClass.driver.GetSelected()
-                dlg = VDigitZBulkDialog(parent=self, title=_("Z bulk-labeling dialog"),
-                                        nselected=len(selected))
-                if dlg.ShowModal() == wx.ID_OK:
-                    if digitClass.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
-                                            dlg.step.GetValue()) < 0:
-                        return
-                self.UpdateMap(render=False, renderVector=True)
-            elif digitToolbar.GetAction() == "typeConv":
-                # -> feature type conversion
-                # - point <-> centroid
-                # - line <-> boundary
-                if digitClass.TypeConvForSelectedLines() < 0:
-                    return
-
-            if digitToolbar.GetAction() != "addLine":
-                # unselect and re-render
-                digitClass.driver.SetSelected([])
-                self.polycoords = []
-                self.UpdateMap(render=False)
-
-            self.redrawAll = True
-            self.Refresh()
-        
-        event.Skip()
-
-    def OnMiddleDown(self, event):
-        """
-        Middle mouse button pressed
-        """
-        digitToolbar = self.parent.toolbars['vdigit']
-        # digitization tool
-        if self.mouse["use"] == "pointer" and digitToolbar:
-            digitClass = self.parent.digit
-            if (digitToolbar.GetAction() == "addLine" and \
-                    digitToolbar.GetAction('type') in ["line", "boundary"]) or \
-                    digitToolbar.GetAction() == "editLine":
-                # add line or boundary -> remove last point from the line
-                try:
-                    removed = self.polycoords.pop()
-                    Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
-                                  [removed,])
-
-                    self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
-                except:
-                    pass
-
-                if digitToolbar.GetAction() == "editLine":
-                    # remove last vertex & line
-                    if len(self.vdigitMove['id']) > 1:
-                        self.vdigitMove['id'].pop()
-
-                self.UpdateMap(render=False, renderVector=False)
-
-            elif digitToolbar.GetAction() in ["deleteLine", "moveLine", "splitLine",
-                                              "addVertex", "removeVertex", "moveVertex",
-                                              "copyCats", "flipLine", "mergeLine",
-                                              "snapLine", "connectLine", "copyLine",
-                                              "queryLine", "breakLine", "typeConv"]:
-                # varios tools -> unselected selected features
-                digitClass.driver.SetSelected([])
-                if digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] and \
-                        hasattr(self, "vdigitMove"):
-
-                    del self.vdigitMove
-                    
-                elif digitToolbar.GetAction() == "copyCats":
-                    try:
-                        del self.copyCatsList
-                        del self.copyCatsIds
-                    except AttributeError:
-                        pass
-                
-                elif digitToolbar.GetAction() == "copyLine":
-                    del self.copyIds
-                    if self.layerTmp:
-                        self.Map.DeleteLayer(self.layerTmp)
-                        self.UpdateMap(render=True, renderVector=False)
-                    del self.layerTmp
-
-                self.polycoords = []
-                self.UpdateMap(render=False) # render vector
-
-            elif digitToolbar.GetAction() == "zbulkLine":
-                # reset polyline
-                self.polycoords = []
-                digitClass.driver.SetSelected([])
-                self.UpdateMap(render=False)
-            
-            self.redrawAll = True
-            
-    def OnMouseEnter(self, event):
-        """!
-        Mouse entered window and no mouse buttons were pressed
-        """
-        if self.parent.gismanager.gcpmanagement:
-            if self.parent.toolbars['gcpdisp']:
-                if not self.parent.MapWindow == self:
-                    self.parent.MapWindow = self
-                    self.parent.Map = self.Map
-                    self.parent.UpdateActive(self)
-                self.SetFocus()
-        else:
-            event.Skip()
-
-    def OnMouseMoving(self, event):
-        """
-        Motion event and no mouse buttons were pressed
-        """
-        digitToolbar = self.parent.toolbars['vdigit']
-        if self.mouse["use"] == "pointer" and digitToolbar:
-            digitClass = self.parent.digit
-            self.mouse['end'] = event.GetPositionTuple()[:]
-            Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
-                           (self.mouse['end'][0], self.mouse['end'][1]))
-            if digitToolbar.GetAction() == "addLine" and digitToolbar.GetAction('type') in ["line", "boundary"]:
-                if len(self.polycoords) > 0:
-                    self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1]))
-            elif digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] \
-                    and hasattr(self, "vdigitMove"):
-                dx = self.mouse['end'][0] - self.mouse['begin'][0]
-                dy = self.mouse['end'][1] - self.mouse['begin'][1]
-                
-                if len(self.vdigitMove['id']) > 0:
-                    # draw lines on new position
-                    if digitToolbar.GetAction() == "moveLine":
-                        # move line
-                        for id in self.vdigitMove['id']:
-                            self.pdcTmp.TranslateId(id, dx, dy)
-                    elif digitToolbar.GetAction() in ["moveVertex", "editLine"]:
-                        # move vertex ->
-                        # (vertex, left vertex, left line,
-                        # right vertex, right line)
-
-                        # do not draw static lines
-                        if digitToolbar.GetAction() == "moveVertex":
-                            self.polycoords = []
-                            ### self.pdcTmp.TranslateId(self.vdigitMove['id'][0], dx, dy)
-                            self.pdcTmp.RemoveId(self.vdigitMove['id'][0])
-                            if self.vdigitMove['id'][1] > 0: # previous vertex
-                                x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][1])[0:2])
-                                self.pdcTmp.RemoveId(self.vdigitMove['id'][1]+1)
-                                self.polycoords.append((x, y))
-                            ### x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][0])[0:2])
-                            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
-                            if self.vdigitMove['id'][2] > 0: # next vertex
-                                x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][2])[0:2])
-                                self.pdcTmp.RemoveId(self.vdigitMove['id'][2]-1)
-                                self.polycoords.append((x, y))
-                            
-                            self.ClearLines(pdc=self.pdcTmp)
-                            self.DrawLines(pdc=self.pdcTmp)
-
-                        else: # edit line
-                            try:
-                                if self.vdigitMove['id'][-1] > 0: # previous vertex
-                                    self.MouseDraw(pdc=self.pdcTmp,
-                                                   begin=self.Cell2Pixel(self.polycoords[-1]))
-                            except: # no line
-                                self.vdigitMove['id'] = []
-                                self.polycoords = []
-
-                self.Refresh() # TODO: use RefreshRect()
-                self.mouse['begin'] = self.mouse['end']
-
-            elif digitToolbar.GetAction() == "zbulkLine":
-                if len(self.polycoords) == 1:
-                    # draw mouse moving
-                    self.MouseDraw(self.pdcTmp)
-
-        event.Skip()
-
-    def ClearLines(self, pdc=None):
-        """
-        Clears temporary drawn lines from PseudoDC
-        """
-        if not pdc:
-            pdc=self.pdcTmp
-        try:
-            pdc.ClearId(self.lineid)
-            pdc.RemoveId(self.lineid)
-        except:
-            pass
-
-        try:
-            pdc.ClearId(self.plineid)
-            pdc.RemoveId(self.plineid)
-        except:
-            pass
-
-        Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
-                  (self.lineid, self.plineid))
-
-        ### self.Refresh()
-
-        return True
-
-    def Pixel2Cell(self, (x, y)):
-        """
-        Convert image coordinates to real word coordinates
-
-        Input : int x, int y
-        Output: float x, float y
-        """
-
-        try:
-            x = int(x)
-            y = int(y)
-        except:
-            return None
-
-        if self.Map.region["ewres"] > self.Map.region["nsres"]:
-            res = self.Map.region["ewres"]
-        else:
-            res = self.Map.region["nsres"]
-
-        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
-        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
-
-        east  = w + x * res
-        north = n - y * res
-
-        # extent does not correspond with whole map canvas area...
-        # east  = self.Map.region['w'] + x * self.Map.region["ewres"]
-        # north = self.Map.region['n'] - y * self.Map.region["nsres"]
-
-        return (east, north)
-
-    def Cell2Pixel(self, (east, north)):
-        """
-        Convert real word coordinates to image coordinates
-        """
-
-        try:
-            east  = float(east)
-            north = float(north)
-        except:
-            return None
-
-        if self.Map.region["ewres"] > self.Map.region["nsres"]:
-            res = self.Map.region["ewres"]
-        else:
-            res = self.Map.region["nsres"]
-
-        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
-        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
-
-        # x = int((east  - w) / res)
-        # y = int((n - north) / res)
-
-        x = (east  - w) / res
-        y = (n - north) / res
-
-        return (x, y)
-
-    def Zoom(self, begin, end, zoomtype):
-        """
-        Calculates new region while (un)zoom/pan-ing
-        """
-        x1, y1 = begin
-        x2, y2 = end
-        newreg = {}
-
-        # threshold - too small squares do not make sense
-        # can only zoom to windows of > 5x5 screen pixels
-        if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
-
-            if x1 > x2:
-                x1, x2 = x2, x1
-            if y1 > y2:
-                y1, y2 = y2, y1
-
-            # zoom in
-            if zoomtype > 0:
-                newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
-                newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
-
-            # zoom out
-            elif zoomtype < 0:
-                newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
-                newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + 2 * \
-                                                                (self.Map.width  - x2),
-                                                            self.Map.height + 2 * \
-                                                                (self.Map.height - y2)))
-        # pan
-        elif zoomtype == 0:
-            dx = x1 - x2
-            dy = y1 - y2
-            if dx == 0 and dy == 0:
-                dx = x1 - self.Map.width / 2
-                dy = y1 - self.Map.height / 2
-            newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
-            newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + dx,
-                                                        self.Map.height + dy))
-
-        # if new region has been calculated, set the values
-        if newreg != {}:
-            # LL locations
-            if self.parent.Map.projinfo['proj'] == 'll':
-                if newreg['n'] > 90.0:
-                    newreg['n'] = 90.0
-                if newreg['s'] < -90.0:
-                    newreg['s'] = -90.0
-            
-            ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
-            cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
-            
-            if hasattr(self, "vdigitMove"):
-                # xo = self.Cell2Pixel((self.Map.region['center_easting'], self.Map.region['center_northing']))
-                # xn = self.Cell2Pixel(ce, cn))
-                tmp = self.Pixel2Cell(self.mouse['end'])
-            
-            # calculate new center point and display resolution
-            self.Map.region['center_easting'] = ce
-            self.Map.region['center_northing'] = cn
-            self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
-            self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
-            self.Map.AlignExtentFromDisplay()
-
-            if hasattr(self, "vdigitMove"):
-                tmp1 = self.mouse['end']
-                tmp2 = self.Cell2Pixel(self.vdigitMove['begin'])
-                dx = tmp1[0] - tmp2[0]
-                dy = tmp1[1] - tmp2[1]
-                self.vdigitMove['beginDiff'] = (dx, dy)
-                for id in self.vdigitMove['id']:
-                    self.pdcTmp.RemoveId(id)
-            
-            self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
-                             self.Map.region['e'], self.Map.region['w'])
-
-        if self.redrawAll is False:
-            self.redrawAll = True
-
-    def ZoomBack(self):
-        """
-        Zoom to previous extents in zoomhistory list
-        """
-
-        zoom = []
-        if len(self.zoomhistory) > 1:
-            self.zoomhistory.pop()
-            zoom = self.zoomhistory[len(self.zoomhistory)-1]
-            # (n, s, e, w)
-        if zoom:
-            # zoom to selected region
-            self.Map.region['center_easting'] = zoom[3] + \
-                (zoom[2] - zoom[3]) / 2
-            self.Map.region['center_northing'] = zoom[1] + \
-                (zoom[0] - zoom[1]) / 2
-            self.Map.region["ewres"] = (zoom[2] - zoom[3]) / self.Map.width
-            self.Map.region["nsres"] = (zoom[0] - zoom[1]) / self.Map.height
-            self.Map.AlignExtentFromDisplay()
-
-            # update map
-            self.UpdateMap()
-
-            # update statusbar
-            self.parent.StatusbarUpdate()
-
-    def ZoomHistory(self, n, s, e, w):
-        """
-        Manages a list of last 10 zoom extents
-
-        Return removed history item if exists
-        """
-        removed = None
-        self.zoomhistory.append((n,s,e,w))
-
-        if len(self.zoomhistory) > 10:
-            removed = self.zoomhistory.pop(0)
-
-        if removed:
-            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
-                      (self.zoomhistory, removed))
-        else:
-            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
-                      (self.zoomhistory))
-
-        return removed
-
-    def OnZoomToMap(self, event):
-        """
-        Set display extents to match selected raster (including NULLs)
-        or vector map.
-        """
-        self.ZoomToMap()
-
-    def OnZoomToRaster(self, event):
-        """
-        Set display extents to match selected raster map (ignore NULLs)
-        """
-        self.ZoomToMap(zoom=True)
-        
-    def ZoomToMap(self, layer = None, zoom = False):
-        """
-        Set display extents to match selected raster
-        or vector map.
-        """
-        zoomreg = {}
-
-        if not layer:
-            layer = self.GetSelectedLayer(multi = True)
-        
-        if not layer:
-            return
-
-        rast = []
-        vect = []
-        updated = False
-        for l in layer:
-            # only raster/vector layers are currently supported
-            if l.type == 'raster':
-                rast.append(l.name)
-            elif l.type == 'vector':
-                if self.parent.digit and l.name == self.parent.digit.map and \
-                        self.parent.digit.type == 'vdigit':
-                    w, s, b, e, n, t = self.parent.digit.driver.GetMapBoundingBox()
-                    self.Map.GetRegion(n=n, s=s, w=w, e=e,
-                                       update=True)
-                    updated = True
-                else:
-                    vect.append(l.name)
-
-        if not updated:
-            self.Map.GetRegion(rast = rast,
-                               vect = vect,
-                               update = True)
-        
-        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
-                         self.Map.region['e'], self.Map.region['w'])
-
-        self.UpdateMap()
-
-        self.parent.StatusbarUpdate()
-
-    def ZoomToWind(self, event):
-        """
-        Set display geometry to match computational
-        region settings (set with g.region)
-        """
-        self.Map.region = self.Map.GetRegion()
-        ### self.Map.SetRegion(windres=True)
-
-        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
-                         self.Map.region['e'], self.Map.region['w'])
-
-        self.UpdateMap()
-
-        self.parent.StatusbarUpdate()
-
-    def ZoomToDefault(self, event):
-        """
-        Set display geometry to match default region settings
-        """
-        self.Map.region = self.Map.GetRegion(default=True)
-        self.Map.AdjustRegion() # aling region extent to the display
-
-        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
-                         self.Map.region['e'], self.Map.region['w'])
-
-        self.UpdateMap()
-
-        self.parent.StatusbarUpdate()
-        
-    def DisplayToWind(self, event):
-        """
-        Set computational region (WIND file) to
-        match display extents
-        """
-        tmpreg = os.getenv("GRASS_REGION")
-        if tmpreg:
-            del os.environ["GRASS_REGION"]
-
-        # We ONLY want to set extents here. Don't mess with resolution. Leave that
-        # for user to set explicitly with g.region
-        new = self.Map.AlignResolution()
-        
-        cmdRegion = ["g.region", "--o",
-                     "n=%f"    % new['n'],
-                     "s=%f"    % new['s'],
-                     "e=%f"    % new['e'],
-                     "w=%f"    % new['w'],
-                     "rows=%d" % int(new['rows']),
-                     "cols=%d" % int(new['cols'])]
-        
-        p = gcmd.Command(cmdRegion)
-
-        if tmpreg:
-            os.environ["GRASS_REGION"] = tmpreg
-
-    def ZoomToSaved(self, event):
-        """!Set display geometry to match extents in
-        saved region file
-        """
-        dlg = gdialogs.SavedRegion(parent = self, id = wx.ID_ANY,
-                                   title = _("Zoom to saved region extents"),
-                                   pos=wx.DefaultPosition, size=wx.DefaultSize,
-                                   style=wx.DEFAULT_DIALOG_STYLE,
-                                   loadsave='load')
-        
-        if dlg.ShowModal() == wx.ID_CANCEL:
-            dlg.Destroy()
-            return
-        
-        wind = dlg.wind
-        
-        self.Map.GetRegion(regionName = wind,
-                           update = True)
-        
-        dlg.Destroy()
-        
-        self.ZoomHistory(self.Map.region['n'],
-                         self.Map.region['s'],
-                         self.Map.region['e'],
-                         self.Map.region['w'])
-        
-        self.UpdateMap()
-    
-    def SaveDisplayRegion(self, event):
-        """
-        Save display extents to named region file.
-        """
-
-        dlg = gdialogs.SavedRegion(parent = self, id = wx.ID_ANY,
-                                   title = _("Save display extents to region file"),
-                                   pos=wx.DefaultPosition, size=wx.DefaultSize,
-                                   style=wx.DEFAULT_DIALOG_STYLE,
-                                   loadsave='save')
-        if dlg.ShowModal() == wx.ID_CANCEL:
-            dlg.Destroy()
-            return
-        
-        wind = dlg.wind
-        
-        # test to see if it already exists and ask permission to overwrite
-        windpath = os.path.join(self.Map.env["GISDBASE"], self.Map.env["LOCATION_NAME"],
-                                self.Map.env["MAPSET"], "windows", wind)
-        
-        if windpath and not os.path.exists(windpath):
-            self.SaveRegion(wind)
-        elif windpath and os.path.exists(windpath):
-            overwrite = wx.MessageBox(_("Region file <%s> already exists. "
-                                        "Do you want to overwrite it?") % (wind),
-                                      _("Warning"), wx.YES_NO | wx.CENTRE)
-            if (overwrite == wx.YES):
-                self.SaveRegion(wind)
-        else:
-            pass
-
-        dlg.Destroy()
-
-    def SaveRegion(self, wind):
-        """
-        Save region settings
-        """
-        ### new = self.Map.AlignResolution()
-        new = self.Map.GetCurrentRegion()
-        
-        cmdRegion = ["g.region",
-                     "-u",
-                     "n=%f" % new['n'],
-                     "s=%f" % new['s'],
-                     "e=%f" % new['e'],
-                     "w=%f" % new['w'],
-                     "rows=%d" % new['rows'],
-                     "cols=%d" % new['cols'],
-                     "save=%s" % wind,
-                     "--o"]
-
-        tmpreg = os.getenv("GRASS_REGION")
-        if tmpreg:
-            del os.environ["GRASS_REGION"]
-        
-        p = gcmd.Command(cmdRegion)
-
-        if tmpreg:
-            os.environ["GRASS_REGION"] = tmpreg
-
-    def Distance(self, beginpt, endpt, screen=True):
-        """Calculete distance
-
-        LL-locations not supported
-
-        @todo Use m.distance
-
-        @param beginpt first point
-        @param endpt second point
-        @param screen True for screen coordinates otherwise EN
-        """
-        x1, y1 = beginpt
-        x2, y2 = endpt
-        if screen:
-            dEast  = (x2 - x1) * self.Map.region["ewres"]
-            dNorth = (y2 - y1) * self.Map.region["nsres"]
-        else:
-            dEast  = (x2 - x1)
-            dNorth = (y2 - y1)
-            
-
-        return (math.sqrt(math.pow((dEast),2) + math.pow((dNorth),2)), (dEast, dNorth))
-
 class MapFrame(wx.Frame):
+    """!Main frame for map display window. Drawing takes place in
+    child double buffered drawing window.
     """
-    Main frame for map display window. Drawing takes place in child double buffered
-    drawing window.
-    """
-
     def __init__(self, parent=None, id=wx.ID_ANY, title=_("GRASS GIS - Map display"),
-                 pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_FRAME_STYLE, toolbars=["map"],
-                 tree=None, notebook=None, gismgr=None, page=None,
-                 Map=None, auimgr=None):
-        """
-        Main map display window with toolbars, statusbar and
+                 tree=None, notebook=None, lmgr=None, page=None,
+                 Map=None, auimgr=None, **kwargs):
+        """!Main map display window with toolbars, statusbar and
         DrawWindow
 
         @param toolbars array of activated toolbars, e.g. ['map', 'digit']
         @param tree reference to layer tree
         @param notebook control book ID in Layer Manager
-        @param gismgr Layer Manager panel
+        @param lmgr Layer Manager
         @param page notebook page with layer tree
         @param Map instance of render.Map
+        @param auimgs AUI manager
+        @param kwargs wx.Frame attribures
         """
-        self.gismanager = gismgr    # GIS Manager object
+        self._layerManager = lmgr   # Layer Manager object
         self.Map        = Map       # instance of render.Map
-        self.tree       = tree      # GIS Manager layer tree object
+        self.tree       = tree      # Layer Manager layer tree object
         self.page       = page      # Notebook page holding the layer tree
-        self.layerbook  = notebook  # GIS Manager layer tree notebook
+        self.layerbook  = notebook  # Layer Manager layer tree notebook
         self.parent     = parent
-
-        #
+        
+        if not kwargs.has_key('name'):
+            kwargs['name'] = 'MapWindow'
+        wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
+        
         # available cursors
-        #
         self.cursors = {
             # default: cross
             # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
@@ -2632,14 +122,11 @@
             "pencil"  : wx.StockCursor(wx.CURSOR_PENCIL),
             "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
             }
-
-        wx.Frame.__init__(self, parent, id, title, pos, size, style)
-        self.SetName("MapWindow")
-
+        
         #
         # set the size & system icon
         #
-        self.SetClientSize(size)
+        self.SetClientSize(self.GetSize())
         self.iconsize = (16, 16)
 
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
@@ -2655,8 +142,8 @@
         #
         self.toolbars = { 'map' : None,
                           'vdigit' : None,
-                          'gcpdisp' : None, 
                           'georect' : None, 
+                          'gcpdisp' : None, 
                           'nviz' : None }
         for toolb in toolbars:
             self.AddToolbar(toolb)
@@ -2666,56 +153,87 @@
         #
         self.statusbar = self.CreateStatusBar(number=4, style=0)
         self.statusbar.SetStatusWidths([-5, -2, -1, -1])
-        self.toggleStatus = wx.Choice(self.statusbar, wx.ID_ANY,
-                                      choices = globalvar.MAP_DISPLAY_STATUSBAR_MODE)
-        self.toggleStatus.SetSelection(UserSettings.Get(group='display', key='statusbarMode', subkey='selection'))
-        self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.toggleStatus)
+        self.statusbarWin = dict()
+        self.statusbarWin['toggle'] = wx.Choice(self.statusbar, wx.ID_ANY,
+                                                choices = globalvar.MAP_DISPLAY_STATUSBAR_MODE)
+        self.statusbarWin['toggle'].SetSelection(UserSettings.Get(group='display',
+                                                                  key='statusbarMode',
+                                                                  subkey='selection'))
+        self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.statusbarWin['toggle'])
         # auto-rendering checkbox
-        self.autoRender = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
-                                      label=_("Render"))
-        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.autoRender)
-        self.autoRender.SetValue(UserSettings.Get(group='display', key='autoRendering', subkey='enabled'))
-        self.autoRender.SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
+        self.statusbarWin['render'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                  label=_("Render"))
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.statusbarWin['render'])
+        self.statusbarWin['render'].SetValue(UserSettings.Get(group='display',
+                                                              key='autoRendering',
+                                                              subkey='enabled'))
+        self.statusbarWin['render'].SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
         # show region
-        self.showRegion = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
-                                      label=_("Show computational extent"))
-        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.showRegion)
+        self.statusbarWin['region'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                  label=_("Show computational extent"))
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.statusbarWin['region'])
         
-        self.showRegion.SetValue(False)
-        self.showRegion.Hide()
-        self.showRegion.SetToolTip(wx.ToolTip (_("Show/hide computational "
-                                                 "region extent (set with g.region). "
-                                                 "Display region drawn as a blue box inside the "
-                                                 "computational region, "
-                                                 "computational region inside a display region "
-                                                 "as a red box).")))
+        self.statusbarWin['region'].SetValue(False)
+        self.statusbarWin['region'].Hide()
+        self.statusbarWin['region'].SetToolTip(wx.ToolTip (_("Show/hide computational "
+                                                             "region extent (set with g.region). "
+                                                             "Display region drawn as a blue box inside the "
+                                                             "computational region, "
+                                                             "computational region inside a display region "
+                                                             "as a red box).")))
         # set resolution
-        self.compResolution = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
-                                         label=_("Constrain display resolution to computational settings"))
-        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.compResolution)
-        self.compResolution.SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
-        self.compResolution.Hide()
-        self.compResolution.SetToolTip(wx.ToolTip (_("Constrain display resolution "
-                                                     "to computational region settings. "
-                                                     "Default value for new map displays can "
-                                                     "be set up in 'User GUI settings' dialog.")))
+        self.statusbarWin['resolution'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                      label=_("Constrain display resolution to computational settings"))
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.statusbarWin['resolution'])
+        self.statusbarWin['resolution'].SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
+        self.statusbarWin['resolution'].Hide()
+        self.statusbarWin['resolution'].SetToolTip(wx.ToolTip (_("Constrain display resolution "
+                                                                 "to computational region settings. "
+                                                                 "Default value for new map displays can "
+                                                                 "be set up in 'User GUI settings' dialog.")))
         # map scale
-        self.mapScale = wx.TextCtrl(parent=self.statusbar, id=wx.ID_ANY,
-                                    value="", style=wx.TE_PROCESS_ENTER,
-                                    size=(150, -1))
-        self.mapScale.Hide()
-        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.mapScale)
+        self.statusbarWin['mapscale'] = wx.ComboBox(parent = self.statusbar, id = wx.ID_ANY,
+                                                    style = wx.TE_PROCESS_ENTER,
+                                                    size=(150, -1))
+        self.statusbarWin['mapscale'].SetItems(['1:1000',
+                                                '1:5000',
+                                                '1:10000',
+                                                '1:25000',
+                                                '1:50000',
+                                                '1:100000',
+                                                '1:1000000'])
+        self.statusbarWin['mapscale'].Hide()
+        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.statusbarWin['mapscale'])
+        self.statusbar.Bind(wx.EVT_COMBOBOX, self.OnChangeMapScale, self.statusbarWin['mapscale'])
 
+        # go to
+        self.statusbarWin['goto'] = wx.TextCtrl(parent=self.statusbar, id=wx.ID_ANY,
+                                                value="", style=wx.TE_PROCESS_ENTER,
+                                                size=(300, -1))
+        self.statusbarWin['goto'].Hide()
+        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnGoTo, self.statusbarWin['goto'])
+
+        # projection
+        self.statusbarWin['projection'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                      label=_("Use defined projection"))
+        self.statusbarWin['projection'].SetValue(False)
+        size = self.statusbarWin['projection'].GetSize()
+        self.statusbarWin['projection'].SetMinSize((size[0] + 150, size[1]))
+        self.statusbarWin['projection'].SetToolTip(wx.ToolTip (_("Reproject coordinates displayed "
+                                                                 "in the statusbar. Projection can be "
+                                                                 "defined in GUI preferences dialog "
+                                                                 "(tab 'Display')")))
+        self.statusbarWin['projection'].Hide()
+        
         # mask
-        self.maskInfo = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY,
+        self.statusbarWin['mask'] = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY,
                                                   label = '')
-        self.maskInfo.SetForegroundColour(wx.Colour(255, 0, 0))
+        self.statusbarWin['mask'].SetForegroundColour(wx.Colour(255, 0, 0))
         
-
         # on-render gauge
-        self.onRenderGauge = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
+        self.statusbarWin['progress'] = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
                                       range=0, style=wx.GA_HORIZONTAL)
-        self.onRenderGauge.Hide()
+        self.statusbarWin['progress'].Hide()
         
         self.StatusbarReposition() # reposition statusbar
 
@@ -2723,10 +241,9 @@
         # Init map display (buffered DC & set default cursor)
         #
         self.MapWindow2D = BufferedWindow(self, id=wx.ID_ANY,
-                                          Map=self.Map, tree=self.tree, gismgr=self.gismanager)
+                                          Map=self.Map, tree=self.tree, lmgr=self._layerManager)
         # default is 2D display mode
         self.MapWindow = self.MapWindow2D
-        self.MapWindow.Bind(wx.EVT_MOTION, self.OnMotion)
         self.MapWindow.SetCursor(self.cursors["default"])
         # used by Nviz (3D display mode)
         self.MapWindow3D = None 
@@ -2747,9 +264,9 @@
         # Update fancy gui style
         #
         self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
-                   Dockable(False).BestSize((-1,-1)).
-                   CloseButton(False).DestroyOnClose(True).
-                   Layer(0))
+                          Dockable(False).BestSize((-1,-1)).
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
         self._mgr.Update()
 
         #
@@ -2782,26 +299,27 @@
         self.decorationDialog = None # decoration/overlays
 
     def AddToolbar(self, name):
-        """
-        Add defined toolbar to the window
-
+        """!Add defined toolbar to the window
+        
         Currently known toolbars are:
-         - map basic map toolbar
-         - digit vector digitizer
-         - georect georectifier
+         - 'map'     - basic map toolbar
+         - 'vdigit'  - vector digitizer
+         - 'gcpdisp' - GCP Manager Display
+         - 'georect' - georectifier
+         - 'nviz'    - 3D view mode
         """
         # default toolbar
         if name == "map":
             self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
 
-            self._mgr.AddPane(self.toolbars['map'].toolbar,
+            self._mgr.AddPane(self.toolbars['map'],
                               wx.aui.AuiPaneInfo().
-                              Name("maptoolbar").Caption(_("Map Toolbar")).
+                              Name("maptoolbar").Caption(_("Map toolbar")).
                               ToolbarPane().Top().
                               LeftDockable(False).RightDockable(False).
                               BottomDockable(False).TopDockable(True).
                               CloseButton(False).Layer(2).
-                              BestSize((self.toolbars['map'].GetToolbar().GetSize())))
+                              BestSize((self.toolbars['map'].GetSize())))
 	
         # vector digitizer
         elif name == "vdigit":
@@ -2812,43 +330,46 @@
                         "TCL/TK digitizer (v.digit) instead?\n\n"
                         "Details: %s" % errorMsg)
                 
-                self.toolbars['map'].combo.SetValue (_("2D view"))
-                dlg = wx.MessageDialog(parent = self,
+                self.mapdisplay.toolbars['map'].combo.SetValue (_("2D view"))
+                dlg = wx.MessageDialog(parent = self.mapdisplay,
                                        message = msg,
                                        caption=_("Vector digitizer failed"),
                                        style = wx.YES_NO | wx.CENTRE)
                 if dlg.ShowModal() == wx.ID_YES:
-                    mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name
-                    self.gismanager.goutput.RunCmd(['v.digit', 'map=%s' % mapName],
-                                                   switchPage=False)
+                    self.lmgr.goutput.RunCmd(['v.digit', 'map=%s' % maplayer.GetName()],
+                                             switchPage=False)
                 dlg.Destroy()
+                
+                self.toolbars['map'].combo.SetValue (_("2D view"))
                 return
             
-            self.toolbars['vdigit'] = toolbars.VDigitToolbar(parent=self, map=self.Map,
+            if self._layerManager:
+                log = self._layerManager.goutput
+            else:
+                log = None
+            self.toolbars['vdigit'] = toolbars.VDigitToolbar(parent=self, mapcontent=self.Map,
                                                              layerTree=self.tree,
-                                                             log=self.gismanager.goutput)
+                                                             log=log)
             
-            for toolRow in range(0, self.toolbars['vdigit'].numOfRows):
-                self._mgr.AddPane(self.toolbars['vdigit'].toolbar[toolRow],
-                                  wx.aui.AuiPaneInfo().
-                                  Name("vdigittoolbar" + str(toolRow)).Caption(_("Vector digitizer toolbar")).
-                                  ToolbarPane().Top().Row(toolRow + 1).
-                                  LeftDockable(False).RightDockable(False).
-                                  BottomDockable(False).TopDockable(True).
-                                  CloseButton(False).Layer(2).
-                                  BestSize((self.toolbars['vdigit'].GetToolbar().GetSize())))
+            self._mgr.AddPane(self.toolbars['vdigit'],
+                              wx.aui.AuiPaneInfo().
+                              Name("vdigittoolbar").Caption(_("Vector digitizer toolbar")).
+                              ToolbarPane().Top().Row(1).
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).
+                              BestSize((self.toolbars['vdigit'].GetSize())))
             
             # change mouse to draw digitized line
             self.MapWindow.mouse['box'] = "point"
             self.MapWindow.zoomtype = 0
             self.MapWindow.pen     = wx.Pen(colour='red',   width=2, style=wx.SOLID)
             self.MapWindow.polypen = wx.Pen(colour='green', width=2, style=wx.SOLID)
-        
         # georectifier
         elif name == "georect":
             self.toolbars['georect'] = toolbars.GRToolbar(self, self.Map)
 
-            self._mgr.AddPane(self.toolbars['georect'].toolbar,
+            self._mgr.AddPane(self.toolbars['georect'],
                               wx.aui.AuiPaneInfo().
                               Name("georecttoolbar").Caption(_("Georectification toolbar")).
                               ToolbarPane().Top().
@@ -2861,105 +382,87 @@
             
             # check for GLCanvas and OpenGL
             if not nviz.haveNviz:
-                wx.MessageBox(parent=self,
+                self.toolbars['map'].combo.SetValue (_("2D view"))
+                wx.MessageBox(parent = self,
                               message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
                                           "was not found or loaded properly.\n"
                                           "Switching back to 2D display mode.\n\nDetails: %s" % nviz.errorMsg),
-                              caption=_("Error"),
-                              style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                self.toolbars['map'].combo.SetValue (_("2D view"))
+                              caption = _("Error"),
+                              style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
                 return
             
-            #
             # add Nviz toolbar and disable 2D display mode tools
-            #
             self.toolbars['nviz'] = toolbars.NvizToolbar(self, self.Map)
             self.toolbars['map'].Enable2D(False)
-
-            #
-            # update layer tree (-> enable 3d-rasters)
-            #
-            self.tree.EnableItemType(type='3d-raster', enable=True)
             
-            #
             # update status bar
-            #
-            self.toggleStatus.Enable(False)
+            self.statusbarWin['toggle'].Enable(False)
 
-            #
             # erase map window
-            #
             self.MapWindow.EraseMap()
-
-            busy = wx.BusyInfo(message=_("Please wait, loading data..."),
-                               parent=self)
-            wx.Yield()
-        
-            #
+            
+            self._layerManager.goutput.WriteCmdLog(_("Starting 3D view mode..."))
+            self.statusbar.SetStatusText(_("Please wait, loading data..."), 0)
+            
             # create GL window & NVIZ toolbar
-            #
             if not self.MapWindow3D:
                 self.MapWindow3D = nviz.GLWindow(self, id=wx.ID_ANY,
-                                                 Map=self.Map, tree=self.tree, gismgr=self.gismanager)
-                self.nvizToolWin = nviz.NvizToolWindow(self, id=wx.ID_ANY,
-                                                       mapWindow=self.MapWindow3D)
+                                                 Map=self.Map, tree=self.tree, lmgr=self._layerManager)
+                self.MapWindow = self.MapWindow3D
+                self.MapWindow.SetCursor(self.cursors["default"])
+                
+                # add Nviz notebookpage
+                self._layerManager.AddNviz()
+                
                 self.MapWindow3D.OnPaint(None) # -> LoadData
                 self.MapWindow3D.Show()
                 self.MapWindow3D.UpdateView(None)
-
-            busy.Destroy()
-
-            self.nvizToolWin.Show()
-
-            #
+            else:
+                self.MapWindow = self.MapWindow3D
+                # add Nviz notebookpage
+                self._layerManager.AddNviz()
+            
             # switch from MapWindow to MapWindowGL
             # add nviz toolbar
-            #
             self._mgr.DetachPane(self.MapWindow2D)
             self.MapWindow2D.Hide()
             self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
                               Dockable(False).BestSize((-1,-1)).
                               CloseButton(False).DestroyOnClose(True).
                               Layer(0))
-            self._mgr.AddPane(self.toolbars['nviz'].toolbar,
+            self._mgr.AddPane(self.toolbars['nviz'],
                               wx.aui.AuiPaneInfo().
-                              Name("nviztoolbar").Caption(_("Nviz toolbar")).
+                              Name("nviztoolbar").Caption(_("3D view toolbar")).
                               ToolbarPane().Top().Row(1).
                               LeftDockable(False).RightDockable(False).
                               BottomDockable(False).TopDockable(True).
                               CloseButton(False).Layer(2))
             
-            self.MapWindow = self.MapWindow3D
             self.SetStatusText("", 0)
             
         self._mgr.Update()
 
     def RemoveToolbar (self, name):
-        """
-        Removes toolbar from the window
+        """!Removes toolbar from the window
 
-        TODO: Only hide, activate by calling AddToolbar()
+        @todo Only hide, activate by calling AddToolbar()
         """
-
         # cannot hide main toolbar
         if name == "map":
             return
         elif name == "vdigit":
             # TODO: not destroy only hide
-            for toolRow in range(0, self.toolbars['vdigit'].numOfRows):
-                self._mgr.DetachPane (self.toolbars['vdigit'].toolbar[toolRow])
-                self.toolbars['vdigit'].toolbar[toolRow].Destroy()
+            self._mgr.DetachPane(self.toolbars['vdigit'])
+            self.toolbars['vdigit'].Destroy()
         else:
-            self._mgr.DetachPane (self.toolbars[name].toolbar)
-            self.toolbars[name].toolbar.Destroy()
+            self._mgr.DetachPane (self.toolbars[name])
+            self.toolbars[name].Destroy()
 
         self.toolbars[name] = None
 
         if name == 'nviz':
-            # hide nviz tools
-            self.nvizToolWin.Hide()
             # unload data
-#            self.MapWindow3D.Reset()
+            #            self.MapWindow3D.Reset()
             # switch from MapWindowGL to MapWindow
             self._mgr.DetachPane(self.MapWindow3D)
             self.MapWindow3D.Hide()
@@ -2969,16 +472,14 @@
                               CloseButton(False).DestroyOnClose(True).
                               Layer(0))
             self.MapWindow = self.MapWindow2D
-  
-            #
-            # update layer tree (-> disable 3d-rasters)
-            #
-            self.tree.EnableItemType(type='3d-raster', enable=False)
+            # remove nviz notebook page
+            self._layerManager.RemoveNviz()
+            
             self.MapWindow.UpdateMap()
             
         self.toolbars['map'].combo.SetValue (_("2D view"))
         self.toolbars['map'].Enable2D(True)
-        self.toggleStatus.Enable(True)
+        self.statusbarWin['toggle'].Enable(True)
 
         self._mgr.Update()
 
@@ -2997,7 +498,7 @@
         """
         Update progress bar info
         """
-        self.onRenderGauge.SetValue(event.value)
+        self.statusbarWin['progress'].SetValue(event.value)
         
         event.Skip()
         
@@ -3006,8 +507,9 @@
         Change choicebook page to match display.
         Or set display for georectifying
         """
-        if self.gismanager.georectifying:
-            # in georectifying session; display used to get get geographic
+        if self._layerManager and \
+                self._layerManager.georectifying:
+            # in georectifying session; display used to get geographic
             # coordinates for GCPs
             self.OnPointer(event)
         else:
@@ -3018,54 +520,23 @@
                     self.layerbook.SetSelection(pgnum)
         
         event.Skip()
-
-    def OnMotion(self, event):
-        """
-        Mouse moved
-        Track mouse motion and update status bar
-        """
-        # update statusbar if required
-        if self.toggleStatus.GetSelection() == 0: # Coordinates
-            e, n = self.MapWindow.Pixel2Cell(event.GetPositionTuple())
-            if self.toolbars['vdigit'] and \
-                    self.toolbars['vdigit'].GetAction() == 'addLine' and \
-                    self.toolbars['vdigit'].GetAction('type') in ('line', 'boundary') and \
-                    len(self.MapWindow.polycoords) > 0:
-                # for linear feature show segment and total length
-                distance_seg = self.MapWindow.Distance(self.MapWindow.polycoords[-1],
-                                                       (e, n), screen=False)[0]
-                distance_tot = distance_seg
-                for idx in range(1, len(self.MapWindow.polycoords)):
-                    distance_tot += self.MapWindow.Distance(self.MapWindow.polycoords[idx-1],
-                                                            self.MapWindow.polycoords[idx],
-                                                            screen=False )[0]
-                self.statusbar.SetStatusText("%.2f, %.2f (seg: %.2f; tot: %.2f)" % \
-                                                 (e, n, distance_seg, distance_tot), 0)
-            else:
-                if self.Map.projinfo['proj'] == 'll':
-                    self.statusbar.SetStatusText("%s" % utils.Deg2DMS(e, n), 0)
-                else:
-                    self.statusbar.SetStatusText("%.2f, %.2f" % (e, n), 0)
         
-        event.Skip()
-
     def OnDraw(self, event):
+        """!Re-display current map composition
         """
-        Re-display current map composition
-        """
-        self.MapWindow.UpdateMap(render=False)
+        self.MapWindow.UpdateMap(render = False)
         
     def OnRender(self, event):
+        """!Re-render map composition (each map layer)
         """
-        Re-render map composition (each map layer)
-        """
         # delete tmp map layers (queries)
         qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
         for layer in qlayer:
             self.Map.DeleteLayer(layer)
-
+        
         # delete tmp lines
-        if self.MapWindow.mouse["use"] in ["measure", "profile"]:
+        if self.MapWindow.mouse["use"] in ("measure",
+                                           "profile"):
             self.MapWindow.polycoords = []
             self.MapWindow.ClearLines()
         
@@ -3080,9 +551,8 @@
         self.StatusbarUpdate()
 
     def OnPointer(self, event):
+        """!Pointer button clicked
         """
-        Pointer button clicked
-        """
         if self.toolbars['map']:
             if event:
                 self.toolbars['map'].OnTool(event)
@@ -3109,7 +579,7 @@
             else: # moveLine, deleteLine
                 self.MapWindow.mouse['box'] = 'box'
         
-        elif self.gismanager.georectifying:
+        elif self._layerManager and self._layerManager.georectifying:
             self.MapWindow.SetCursor(self.cursors["cross"])
         
         else:
@@ -3166,7 +636,7 @@
         self.MapWindow.mouse['use'] = "pan"
         self.MapWindow.mouse['box'] = "pan"
         self.MapWindow.zoomtype = 0
-                
+        
         # change the cursor
         self.MapWindow.SetCursor(self.cursors["hand"])
 
@@ -3196,24 +666,26 @@
         # event.Skip()
 
     def OnToggleRender(self, event):
+        """!Enable/disable auto-rendering
         """
-        Enable/disable auto-rendering
-        """
-        if self.autoRender.GetValue():
+        if self.statusbarWin['render'].GetValue():
             self.OnRender(None)
 
+    def IsAutoRendered(self):
+        """!Check if auto-rendering is enabled"""
+        return self.statusbarWin['render'].IsChecked()
+    
     def OnToggleShowRegion(self, event):
+        """!Show/Hide extent in map canvas
         """
-        Show/Hide extent in map canvas
-        """
-        if self.showRegion.GetValue():
+        if self.statusbarWin['region'].GetValue():
             # show extent
             self.MapWindow.regionCoords = []
         else:
             del self.MapWindow.regionCoords
 
         # redraw map if auto-rendering is enabled
-        if self.autoRender.GetValue():
+        if self.statusbarWin['render'].GetValue():
             self.OnRender(None)
 
     def OnToggleResolution(self, event):
@@ -3222,7 +694,7 @@
         for redering image instead of display resolution
         """
         # redraw map if auto-rendering is enabled
-        if self.autoRender.GetValue():
+        if self.statusbarWin['render'].GetValue():
             self.OnRender(None)
         
     def OnToggleStatus(self, event):
@@ -3242,7 +714,7 @@
                 raise ValueError
             value = int(scale[2:])
         except ValueError:
-            self.mapScale.SetValue('1:%ld' % int(self.mapScaleValue))
+            self.statusbarWin['mapscale'].SetValue('1:%ld' % int(self.mapScaleValue))
             return
 
         dEW = value * (self.Map.region['cols'] / self.ppm[0])
@@ -3258,57 +730,213 @@
         
         # redraw a map
         self.MapWindow.UpdateMap()
-        self.mapScale.SetFocus()
+        self.statusbarWin['mapscale'].SetFocus()
         
+    def OnGoTo(self, event):
+        """
+        Go to position
+        """
+        try:
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    # reproject values
+                    projIn = UserSettings.Get(group='projection',
+                                              key='statusbar',
+                                              subkey='proj4')
+                    projOut = gcmd.RunCommand('g.proj',
+                                              flags = 'jf',
+                                              read = True)
+                    proj = projIn.split(' ')[0].split('=')[1]
+                    if proj in ('ll', 'latlong', 'longlat'):
+                        e, n = self.statusbarWin['goto'].GetValue().split(';')
+                        e, n = utils.DMS2Deg(e, n)
+                        proj, coord1 = utils.ReprojectCoordinates(coord = (e, n),
+                                                                  projIn = projIn,
+                                                                  projOut = projOut, flags = 'd')
+                        e, n = coord1
+                    else:
+                        e, n = map(float, self.statusbarWin['goto'].GetValue().split(';'))
+                        proj, coord1 = utils.ReprojectCoordinates(coord = (e, n),
+                                                                  projIn = projIn,
+                                                                  projOut = projOut, flags = 'd')
+                        e, n = coord1
+            else:
+                if self.Map.projinfo['proj'] == 'll':
+                    e, n = self.statusbarWin['goto'].GetValue().split(';')
+                else:
+                    e, n = map(float, self.statusbarWin['goto'].GetValue().split(';'))
+                    
+            region = self.Map.GetCurrentRegion()
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    region['center_easting'], region['center_northing'] = e, n
+            else:
+                if self.Map.projinfo['proj'] == 'll':
+                    region['center_easting'], region['center_northing'] = utils.DMS2Deg(e, n)
+                else:
+                    region['center_easting'], region['center_northing'] = e, n
+        except ValueError:
+            region = self.Map.GetCurrentRegion()
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                    self.statusbarWin['goto'].SetValue("%s" % utils.Deg2DMS(region['center_easting'], 
+                                                                            region['center_northing'],
+                                                                            precision = precision))
+            else:
+                self.statusbarWin['goto'].SetValue("%.*f; %.*f" % \
+                                                       (precision, region['center_easting'],
+                                                        precision, region['center_northing']))
+            return
+        
+        
+        dn = (region['nsres'] * region['rows']) / 2.
+        region['n'] = region['center_northing'] + dn
+        region['s'] = region['center_northing'] - dn
+        de = (region['ewres'] * region['cols']) / 2.
+        region['e'] = region['center_easting'] + de
+        region['w'] = region['center_easting'] - de
+        
+        self.Map.AdjustRegion()
+
+        # add to zoom history
+        self.MapWindow.ZoomHistory(region['n'], region['s'],
+                                   region['e'], region['w'])
+        
+        # redraw a map
+        self.MapWindow.UpdateMap()
+        self.statusbarWin['goto'].SetFocus()
+        
     def StatusbarUpdate(self):
-        """Update statusbar content"""
+        """!Update statusbar content"""
 
-        self.showRegion.Hide()
-        self.compResolution.Hide()
-        self.mapScale.Hide()
+        self.statusbarWin['region'].Hide()
+        self.statusbarWin['resolution'].Hide()
+        self.statusbarWin['mapscale'].Hide()
+        self.statusbarWin['goto'].Hide()
+        self.statusbarWin['projection'].Hide()
         self.mapScaleValue = self.ppm = None
 
-        if self.toggleStatus.GetSelection() == 0: # Coordinates
+        if self.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
             self.statusbar.SetStatusText("", 0)
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.toggleStatus.GetSelection() == 1: # Extent
-            self.statusbar.SetStatusText("%.2f - %.2f, %.2f - %.2f" %
-                                         (self.Map.region["w"], self.Map.region["e"],
-                                          self.Map.region["s"], self.Map.region["n"]), 0)
-            # enable long help
-            self.StatusbarEnableLongHelp()
+        elif self.statusbarWin['toggle'].GetSelection() in (1, 2): # Extent
+            sel = self.statusbarWin['toggle'].GetSelection()
+            if sel == 1:
+                region = self.Map.region
+            else:
+                region = self.Map.GetRegion() # computation region
 
-        elif self.toggleStatus.GetSelection() == 2: # Comp. region
-            compregion = self.Map.GetRegion()
-            self.statusbar.SetStatusText("%.2f - %.2f, %.2f - %.2f (%.2f, %.2f)" %
-                                         (compregion["w"], compregion["e"],
-                                          compregion["s"], compregion["n"],
-                                          compregion["ewres"], compregion["nsres"]), 0)
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    projOut = UserSettings.Get(group='projection',
+                                               key='statusbar',
+                                               subkey='proj4')
+                    proj, coord1 = utils.ReprojectCoordinates(coord = (region["w"], region["s"]),
+                                                              projOut = projOut, flags = 'd')
+                    proj, coord2 = utils.ReprojectCoordinates(coord = (region["e"], region["n"]),
+                                                          projOut = projOut, flags = 'd')
+                    if sel == 2:
+                        proj, coord3 = utils.ReprojectCoordinates(coord = (0.0, 0.0),
+                                                                  projOut = projOut, flags = 'd')
+                        proj, coord4 = utils.ReprojectCoordinates(coord = (region["ewres"], region["nsres"]),
+                                                                  projOut = projOut, flags = 'd')
+                    if coord1 and coord2:
+                        if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                            w, s = utils.Deg2DMS(coord1[0], coord1[1], string = False,
+                                                 precision = precision)
+                            e, n = utils.Deg2DMS(coord2[0], coord2[1], string = False,
+                                                 precision = precision)
+                            if sel == 1:
+                                self.statusbar.SetStatusText("%s - %s, %s - %s" %
+                                                             (w, e, s, n), 0)
+                            else:
+                                ewres, nsres = utils.Deg2DMS(abs(coord3[0]) - abs(coord4[0]),
+                                                             abs(coord3[1]) - abs(coord4[1]),
+                                                             string = False, hemisphere = False,
+                                                             precision = precision)
+                                self.statusbar.SetStatusText("%s - %s, %s - %s (%s, %s)" %
+                                                             (w, e, s, n, ewres, nsres), 0)
+                        else:
+                            w, s = coord1
+                            e, n = coord2
+                            if sel == 1:
+                                self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
+                                                         (precision, w, precision, e,
+                                                          precision, s, precision, n), 0)
+                            else:
+                                ewres, nsres = coord3
+                                self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
+                                                             (precision, w, precision, e,
+                                                              precision, s, precision, n,
+                                                              precision, ewres, precision, nsres), 0)
+                    else:
+                        self.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
+            else:
+                if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                    w, s = utils.Deg2DMS(region["w"], region["s"],
+                                         string = False, precision = precision)
+                    e, n = utils.Deg2DMS(region["e"], region["n"],
+                                         string = False, precision = precision)
+                    if sel == 1:
+                        self.statusbar.SetStatusText("%s - %s, %s - %s" %
+                                                     (w, e, s, n), 0)
+                    else:
+                        ewres, nsres = utils.Deg2DMS(region['ewres'], region['nsres'],
+                                                     string = False, precision = precision)
+                        self.statusbar.SetStatusText("%s - %s, %s - %s (%s, %s)" %
+                                                     (w, e, s, n, ewres, nsres), 0)
+                else:
+                    w, s = region["w"], region["s"]
+                    e, n = region["e"], region["n"]
+                    if sel == 1:
+                        self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
+                                                     (precision, w, precision, e,
+                                                      precision, s, precision, n), 0)
+                    else:
+                        ewres, nsres = region['ewres'], region['nsres']
+                        self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
+                                                     (precision, w, precision, e,
+                                                      precision, s, precision, n,
+                                                      precision, ewres, precision, nsres), 0)
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.toggleStatus.GetSelection() == 3: # Show comp. extent
+        elif self.statusbarWin['toggle'].GetSelection() == 3: # Show comp. extent
             self.statusbar.SetStatusText("", 0)
-            self.showRegion.Show()
+            self.statusbarWin['region'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.toggleStatus.GetSelection() == 4: # Display mode
+        elif self.statusbarWin['toggle'].GetSelection() == 4: # Display mode
             self.statusbar.SetStatusText("", 0)
-            self.compResolution.Show()
+            self.statusbarWin['resolution'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.toggleStatus.GetSelection() == 5: # Display geometry
+        elif self.statusbarWin['toggle'].GetSelection() == 5: # Display geometry
             self.statusbar.SetStatusText("rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
                                          (self.Map.region["rows"], self.Map.region["cols"],
                                           self.Map.region["nsres"], self.Map.region["ewres"]), 0)
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.toggleStatus.GetSelection() == 6: # Map scale
+        elif self.statusbarWin['toggle'].GetSelection() == 6: # Map scale
             # TODO: need to be fixed...
             ### screen X region problem
             ### user should specify ppm
@@ -3348,45 +976,100 @@
 
             self.statusbar.SetStatusText("")
             try:
-                self.mapScale.SetValue("1:%ld" % (scale + 0.5))
+                self.statusbarWin['mapscale'].SetValue("1:%ld" % (scale + 0.5))
             except TypeError:
                 pass
             self.mapScaleValue = scale
-            self.mapScale.Show()
+            self.statusbarWin['mapscale'].Show()
 
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
+        elif self.statusbarWin['toggle'].GetSelection() == 7: # go to
+            self.statusbar.SetStatusText("")
+            region = self.Map.GetCurrentRegion()
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    proj, coord  = utils.ReprojectCoordinates(coord = (region['center_easting'],
+                                                                       region['center_northing']),
+                                                              projOut = UserSettings.Get(group='projection',
+                                                                                         key='statusbar',
+                                                                                         subkey='proj4'),
+                                                              flags = 'd')
+                    if coord:
+                        if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                            self.statusbarWin['goto'].SetValue("%s" % utils.Deg2DMS(coord[0],
+                                                                                    coord[1],
+                                                                                    precision = precision))
+                        else:
+                            self.statusbarWin['goto'].SetValue("%.*f; %.*f" % (precision, coord[0],
+                                                                               precision, coord[1]))
+                    else:
+                        self.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
+            else:
+                if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                    self.statusbarWin['goto'].SetValue("%s" % utils.Deg2DMS(region['center_easting'], 
+                                                                            region['center_northing'],
+                                                                            precision = precision))
+                else:
+                    self.statusbarWin['goto'].SetValue("%.*f; %.*f" % (precision, region['center_easting'],
+                                                                       precision, region['center_northing']))
+            self.statusbarWin['goto'].Show()
+
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+        
+        elif self.statusbarWin['toggle'].GetSelection() == 8: # projection
+            self.statusbar.SetStatusText("")
+            epsg = UserSettings.Get(group='projection', key='statusbar', subkey='epsg')
+            if epsg:
+                label = '%s (EPSG: %s)' % (_("Use defined projection"), epsg)
+                self.statusbarWin['projection'].SetLabel(label)
+            else:
+                self.statusbarWin['projection'].SetLabel(_("Use defined projection"))
+            self.statusbarWin['projection'].Show()
+            
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+            
         else:
             self.statusbar.SetStatusText("", 1)
 
     def StatusbarEnableLongHelp(self, enable=True):
-        """Enable/disable toolbars long help"""
+        """!Enable/disable toolbars long help"""
         for toolbar in self.toolbars.itervalues():
             if toolbar:
                 toolbar.EnableLongHelp(enable)
                 
     def StatusbarReposition(self):
-        """Reposition checkbox in statusbar"""
+        """!Reposition checkbox in statusbar"""
         # reposition checkbox
-        widgets = [(0, self.showRegion),
-                   (0, self.compResolution),
-                   (0, self.mapScale),
-                   (0, self.onRenderGauge),
-                   (1, self.toggleStatus),
-                   (2, self.maskInfo),
-                   (3, self.autoRender)]
+        widgets = [(0, self.statusbarWin['region']),
+                   (0, self.statusbarWin['resolution']),
+                   (0, self.statusbarWin['mapscale']),
+                   (0, self.statusbarWin['progress']),
+                   (0, self.statusbarWin['projection']),
+                   (1, self.statusbarWin['toggle']),
+                   (2, self.statusbarWin['mask']),
+                   (3, self.statusbarWin['render'])]
         for idx, win in widgets:
             rect = self.statusbar.GetFieldRect(idx)
             if idx == 0: # show region / mapscale / process bar
                 # -> size
                 wWin, hWin = win.GetBestSize()
-                if win == self.onRenderGauge:
+                if win == self.statusbarWin['progress']:
                     wWin = rect.width - 6
                 # -> position
-                # if win == self.showRegion:
-                    # x, y = rect.x + rect.width - wWin, rect.y - 1
-                    # align left
+                # if win == self.statusbarWin['region']:
+                # x, y = rect.x + rect.width - wWin, rect.y - 1
+                # align left
                 # else:
                 x, y = rect.x + 3, rect.y - 1
                 w, h = wWin, rect.height + 2
@@ -3398,38 +1081,55 @@
                     y += 4
                 elif idx == 3: # render
                     x += 5
-            
             win.SetPosition((x, y))
             win.SetSize((w, h))
 
     def SaveToFile(self, event):
+        """!Save map to image
         """
-        Save image to file
-        """
-        filetype =  "PNG file (*.png)|*.png|"\
-                    "TIF file (*.tif)|*.tif|"\
-                    "GIF file (*.gif)|*.gif"
-
-        dlg = wx.FileDialog(self, "Choose a file name to save the image as a PNG to",
-            defaultDir = "",
-            defaultFile = "",
-            wildcard = filetype,
-            style=wx.SAVE|wx.FD_OVERWRITE_PROMPT)
+        if self.toolbars['nviz']:
+            filetype = "PPM file (*.ppm)|*.ppm|TIF file (*.tif)|*.tif"
+            ltype = [{ 'ext' : 'ppm', 'type' : -1 },
+                     { 'ext' : 'tif', 'type' : wx.BITMAP_TYPE_TIF }]
+        else:
+            img = self.MapWindow.img
+            if not img:
+                gcmd.GMessage(parent = self,
+                              message = _("Nothing to render (empty map). Operation canceled."))
+                return
+            filetype, ltype = gdialogs.GetImageHandlers(img)
+        
+        # get size
+        dlg = gdialogs.ImageSizeDialog(self)
+        dlg.CentreOnParent()
+        if dlg.ShowModal() != wx.ID_OK:
+            dlg.Destroy()
+            return
+        width, height = dlg.GetValues()
+        dlg.Destroy()
+        
+        # get filename
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose a file name to save the image "
+                                        "(no need to add extension)"),
+                            wildcard = filetype,
+                            style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+        
         if dlg.ShowModal() == wx.ID_OK:
-            base = os.path.splitext(dlg.GetPath())[0]
-            ext = os.path.splitext(dlg.GetPath())[1]
-            if dlg.GetFilterIndex() == 0:
-                type = wx.BITMAP_TYPE_PNG
-                path = dlg.GetPath()
-                if ext != '.png': path = base+'.png'
-            elif dlg.GetFilterIndex() == 1:
-                type = wx.BITMAP_TYPE_TIF
-                if ext != '.tif': path = base+'.tif'
-            elif dlg.GetFilterIndex() == 2:
-                type = wx.BITMAP_TYPE_TIF
-                if ext != '.gif': path = base+'.gif'
-            self.MapWindow.SaveToFile(path, type)
-
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            base, ext = os.path.splitext(path)
+            fileType = ltype[dlg.GetFilterIndex()]['type']
+            extType  = ltype[dlg.GetFilterIndex()]['ext']
+            if ext != extType:
+                path = base + '.' + extType
+            
+            self.MapWindow.SaveToFile(path, fileType,
+                                      width, height)
+            
         dlg.Destroy()
 
     def PrintMenu(self, event):
@@ -3457,8 +1157,7 @@
         printmenu.Destroy()
 
     def OnCloseWindow(self, event):
-        """
-        Window closed.
+        """!Window closed.
         Also close associated layer tree page
         """
         pgnum = None
@@ -3474,45 +1173,49 @@
         if self.toolbars['nviz']:
             self.toolbars['nviz'].OnExit()
         
-        if self.page:
+        if not self._layerManager:
+            self.Destroy()
+        elif self.page:
             pgnum = self.layerbook.GetPageIndex(self.page)
             if pgnum > -1:
                 self.layerbook.DeletePage(pgnum)
         
-        ### self.Destroy()
-        # if event:
-        #    event.Skip() <- causes application crash
-        
     def GetRender(self):
+        """!Returns current instance of render.Map()
         """
-        Returns the current instance of render.Map()
-        """
         return self.Map
 
+    def GetWindow(self):
+        """!Get map window"""
+        return self.MapWindow
+
+    def _OnQuery(self):
+        """!Internal method used by OnQuery*() methods"""
+        # switch GIS Manager to output console to show query results
+        self._layerManager.notebook.SetSelection(1)
+        
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.zoomtype = 0
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+        
     def OnQueryDisplay(self, event):
+        """!Query currrent raster/vector map layers (display mode) -
+        2D view mode
         """
-        Query currrent raster/vector map layers (display mode)
-        """
         if self.toolbars['map'].GetAction() == 'displayAttrb': # select previous action
             self.toolbars['map'].SelectDefault(event)
             return
-
+        
         self.toolbars['map'].action['desc'] = 'displayAttrb'
         
-        # switch GIS Manager to output console to show query results
-        self.gismanager.notebook.SetSelection(1)
-
         self.MapWindow.mouse['use'] = "query"
-        self.MapWindow.mouse['box'] = "point"
-        self.MapWindow.zoomtype = 0
-
-        # change the cursor
-        self.MapWindow.SetCursor(self.cursors["cross"])
-
+        self._OnQuery()
+        
     def OnQueryModify(self, event):
+        """!Query vector map layer (edit mode) - 2D view mode
         """
-        Query vector map layer (edit mode)
-        """
         if self.toolbars['map'].GetAction() == 'modifyAttrb': # select previous action
             self.toolbars['map'].SelectDefault(event)
             return
@@ -3520,16 +1223,34 @@
         self.toolbars['map'].action['desc'] = 'modifyAttrb'
         
         self.MapWindow.mouse['use'] = "queryVector"
-        self.MapWindow.mouse['box'] = "point"
         self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
-        self.MapWindow.zoomtype = 0
+        self._OnQuery()
+        
+    def OnNvizQuerySurface(self, event):
+        """!Query current surface in 3D view mode"""
+        if self.toolbars['map'].GetAction() == 'nvizQuerySurface':
+            self.toolbars['map'].SelectDefault(event)
+            return
+        
+        self.toolbars['map'].action['desc'] = 'nvizQuerySurface'
+        
+        self.MapWindow.mouse['use'] = "nvizQuerySurface"
+        self._OnQuery()
 
-        # change the cursor
-        self.MapWindow.SetCursor(self.cursors["cross"])
+    def OnNvizQueryVector(self, event):
+        """!Query current vector in 3D view mode"""
+        if self.toolbars['map'].GetAction() == 'nvizQueryVector':
+            self.toolbars['map'].SelectDefault(event)
+            return
         
+        self.toolbars['map'].action['desc'] = 'nvizQueryVector'
+        
+        self.MapWindow.mouse['use'] = "nvizQueryVector"
+        self._OnQuery()
+        
     def QueryMap(self, x, y):
         """!Query map layer features
-
+        
         Currently only raster and vector map layers are supported.
         
         @param x,y coordinates
@@ -3537,7 +1258,7 @@
         #set query snap distance for v.what at mapunit equivalent of 10 pixels
         qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
         east, north = self.MapWindow.Pixel2Cell((x, y))
-
+        
         num = 0
         for layer in self.tree.GetSelections():
             type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
@@ -3546,10 +1267,10 @@
                 num += 1
         
         if num < 1:
-            dlg = wx.MessageDialog(parent=self,
-                                   message=_('No raster or vector map layer selected for querying.'),
-                                   caption=_('No map layer selected'),
-                                   style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _('No raster or vector map layer selected for querying.'),
+                                   caption = _('No map layer selected'),
+                                   style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
             dlg.ShowModal()
             dlg.Destroy()
             return
@@ -3557,8 +1278,8 @@
         mapname = None
         raststr = ''
         vectstr = ''
-        rcmd = []
-        vcmd = []
+        rcmd = ['r.what', '--v']
+        vcmd = ['v.what', '--v']
         for layer in self.tree.GetSelections():
             type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
             dcmd = self.tree.GetPyData(layer)[0]['cmd']
@@ -3569,18 +1290,17 @@
                 raststr += "%s," % name
             elif type in ('vector', 'thememap', 'themechart'):
                 vectstr += "%s," % name
-
+        
         # use display region settings instead of computation region settings
         self.tmpreg = os.getenv("GRASS_REGION")
         os.environ["GRASS_REGION"] = self.Map.SetRegion(windres=False)
         
         # build query commands for any selected rasters and vectors
         if raststr != '':
-            rcmd = ['r.what', '--q',
-                    '-f',
-                    'input=%s' % raststr.rstrip(','),
-                    'east_north=%f,%f' % (float(east), float(north))]
-
+            rcmd.append('-f')
+            rcmd.append('input=%s' % raststr.rstrip(','))
+            rcmd.append('east_north=%f,%f' % (float(east), float(north)))
+        
         if vectstr != '':
             # check for vector maps open to be edited
             digitToolbar = self.toolbars['vdigit']
@@ -3589,39 +1309,39 @@
                 vect = []
                 for vector in vectstr.split(','):
                     if map == vector:
-                        self.gismanager.goutput.WriteWarning("Vector map <%s> "
-                                                             "opened for editing - skipped." % map)
+                        self._layerManager.goutput.WriteWarning("Vector map <%s> "
+                                                                "opened for editing - skipped." % map)
                         continue
                     vect.append(vector)
                 vectstr = ','.join(vect)
-                
+            
             if len(vectstr) <= 1:
-                self.gismanager.goutput.WriteCmdLog("Nothing to query.")
+                self._layerManager.goutput.WriteCmdLog("Nothing to query.")
                 return
             
-            vcmd = ['v.what', '--q',
-                    '-a',
-                    'map=%s' % vectstr.rstrip(','),
-                    'east_north=%f,%f' % (float(east), float(north)),
-                    'distance=%f' % qdist]
-
+            vcmd.append('-a')
+            vcmd.append('map=%s' % vectstr.rstrip(','))
+            vcmd.append('east_north=%f,%f' % (float(east), float(north)))
+            vcmd.append('distance=%f' % float(qdist))
+        
         # parse query command(s)
-        if self.gismanager:
-            if rcmd:
-                self.gismanager.goutput.RunCmd(rcmd, compReg=False,
-                                               onDone = self._QueryMapDone)
-            if vcmd:
-                self.gismanager.goutput.RunCmd(vcmd,
-                                               onDone = self._QueryMapDone)
+        if self._layerManager:
+            if raststr:
+                self._layerManager.goutput.RunCmd(rcmd,
+                                                  compReg=False,
+                                                  onDone = self._QueryMapDone)
+            if vectstr:
+                self._layerManager.goutput.RunCmd(vcmd,
+                                                  onDone = self._QueryMapDone)
         else:
-            if rcmd:
-                gcmd.Command(rcmd)
-            if vcmd:
-                gcmd.Command(vcmd)
-
-    def _QueryMapDone(self, returncode):
+            if raststr:
+                gcmd.RunCommand(rcmd)
+            if vectstr:
+                gcmd.RunCommand(vcmd)
+        
+    def _QueryMapDone(self, cmd, returncode):
         """!Restore settings after querying (restore GRASS_REGION)
-
+        
         @param returncode command return code
         """
         if hasattr(self, "tmpreg"):
@@ -3636,8 +1356,7 @@
             del self.tmpreg
         
     def QueryVector(self, x, y):
-        """
-        Query vector map layer features
+        """!Query vector map layer features
 
         Attribute data of selected vector object are displayed in GUI dialog.
         Data can be modified (On Submit)
@@ -3661,27 +1380,30 @@
         posWindow = self.ClientToScreen((x + self.MapWindow.dialogOffset,
                                          y + self.MapWindow.dialogOffset))
 
-        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / \
-                            self.Map.width)
+        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) /
+                        self.Map.width)
+        
         east, north = self.MapWindow.Pixel2Cell((x, y))
-
+        
         mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name
         
         if self.dialogs['attributes'] is None:
-            self.dialogs['attributes'] = dbm.DisplayAttributesDialog(parent=self.MapWindow,
-                                                                     map=mapName,
-                                                                     query=((east, north), qdist),
-                                                                     pos=posWindow,
-                                                                     action="update")
+            self.dialogs['attributes'] = \
+                dbm_dialogs.DisplayAttributesDialog(parent=self.MapWindow,
+                                                    map=mapName,
+                                                    query=((east, north), qdist),
+                                                    pos=posWindow,
+                                                    action="update")
         else:
             # selection changed?
             if not self.dialogs['attributes'].mapDBInfo or \
-                   self.dialogs['attributes'].mapDBInfo.map != mapName:
+                    self.dialogs['attributes'].mapDBInfo.map != mapName:
                 self.dialogs['attributes'].UpdateDialog(map=mapName, query=((east, north), qdist))
             else:
                 self.dialogs['attributes'].UpdateDialog(query=((east, north), qdist))
-
+                
         cats = self.dialogs['attributes'].GetCats()
+        
         try:
             qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)[0]
         except IndexError:
@@ -3695,7 +1417,7 @@
                                                         addLayer=False))
             else:
                 qlayer = self.AddTmpVectorMapLayer(mapName, cats, useId=False)
-
+            
             # set opacity based on queried layer
             opacity = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetOpacity(float=True)
             qlayer.SetOpacity(opacity)
@@ -3709,45 +1431,64 @@
                 self.MapWindow.UpdateMap(render=False, renderVector=False)
             if self.dialogs['attributes'].IsShown():
                 self.dialogs['attributes'].Hide()
-
+        
     def OnQuery(self, event):
-        """Query tools menu"""
+        """!Query tools menu"""
         if self.toolbars['map']:
             self.toolbars['map'].OnTool(event)
             action = self.toolbars['map'].GetAction()
         
         point = wx.GetMousePosition()
         toolsmenu = wx.Menu()
-        # Add items to the menu
-        display = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
-                              text=_("Query raster/vector map(s) (display mode)"),
-                              kind=wx.ITEM_CHECK)
-        toolsmenu.AppendItem(display)
-        self.Bind(wx.EVT_MENU, self.OnQueryDisplay, display)
-        numLayers = 0
-        for layer in self.tree.GetSelections():
-            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
-            if type in ('raster', 'rgb', 'his',
-                        'vector', 'thememap', 'themechart'):
-                numLayers += 1
-        if numLayers < 1:
-            display.Enable(False)
+
+        # add items to the menu
+        if self.toolbars['nviz']:
+            raster = wx.MenuItem(parentMenu = toolsmenu, id = wx.ID_ANY,
+                                 text = _("Query surface (raster map)"),
+                                 kind = wx.ITEM_CHECK)
+            toolsmenu.AppendItem(raster)
+            self.Bind(wx.EVT_MENU, self.OnNvizQuerySurface, raster)
+            if action == "nvizQuerySurface":
+                raster.Check(True)
+            vector = wx.MenuItem(parentMenu = toolsmenu, id = wx.ID_ANY,
+                                 text = _("Query vector map"),
+                                 kind = wx.ITEM_CHECK)
+            toolsmenu.AppendItem(vector)
+            self.Bind(wx.EVT_MENU, self.OnNvizQueryVector, vector)
+            if action == "nvizQueryVector":
+                vector.Check(True)
+        else:
+            display = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
+                                  text=_("Query raster/vector map(s) (display mode)"),
+                                  kind=wx.ITEM_CHECK)
+            toolsmenu.AppendItem(display)
+            self.Bind(wx.EVT_MENU, self.OnQueryDisplay, display)
+            numLayers = 0
+            for layer in self.tree.GetSelections():
+                type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
+                if type in ('raster', 'rgb', 'his',
+                            'vector', 'thememap', 'themechart'):
+                    numLayers += 1
+            if numLayers < 1:
+                display.Enable(False)
+            
+            if action == "displayAttrb":
+                display.Check(True)
+            
+            modify = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
+                                 text=_("Query vector map (edit mode)"),
+                                 kind=wx.ITEM_CHECK)
+            toolsmenu.AppendItem(modify)
+            self.Bind(wx.EVT_MENU, self.OnQueryModify, modify)
+            modify.Enable(False)
         
-        if action == "displayAttrb":
-            display.Check(True)
-        
-        modify = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
-                             text=_("Query vector map (edit mode)"),
-                             kind=wx.ITEM_CHECK)
-        toolsmenu.AppendItem(modify)
-        self.Bind(wx.EVT_MENU, self.OnQueryModify, modify)
-        digitToolbar = self.toolbars['vdigit']
-        if self.tree.layer_selected:
-            layer_selected = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer']
-            if layer_selected.GetType() != 'vector' or \
-                    (digitToolbar and \
-                         digitToolbar.GetLayer() == layer_selected):
-                modify.Enable(False)
+            digitToolbar = self.toolbars['vdigit']
+            if self.tree.layer_selected:
+                layer_selected = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer']
+                if layer_selected.GetType() != 'vector' or \
+                        (digitToolbar and \
+                             digitToolbar.GetLayer() == layer_selected):
+                    modify.Enable(False)
             else:
                 if action == "modifyAttrb":
                     modify.Check(True)
@@ -3755,9 +1496,8 @@
         self.PopupMenu(toolsmenu)
         toolsmenu.Destroy()
 
-    def AddTmpVectorMapLayer(self, name, cats, useId=False, addLayer=True):
-        """
-        Add temporal vector map layer to map composition
+    def AddTmpVectorMapLayer(self, name, cats, useId = False, addLayer = True):
+        """!Add temporal vector map layer to map composition
 
         @param name name of map layer
         @param useId use feature id instead of category 
@@ -3765,14 +1505,28 @@
         # color settings from ATM
         color = UserSettings.Get(group='atm', key='highlight', subkey='color')
         colorStr = str(color[0]) + ":" + \
-        str(color[1]) + ":" + \
-        str(color[2])
+            str(color[1]) + ":" + \
+            str(color[2])
 
+        # icon used in vector display and its size
+        icon = ''
+        size = 0
+        vparam = self.tree.GetPyData(self.tree.layer_selected)[0]['cmd']
+        for p in vparam:
+            if '=' in p:
+                parg,pval = p.split('=')
+                if parg == 'icon': icon = pval
+                elif parg == 'size': size = int(pval)
+
         pattern = ["d.vect",
                    "map=%s" % name,
                    "color=%s" % colorStr,
                    "fcolor=%s" % colorStr,
                    "width=%d"  % UserSettings.Get(group='atm', key='highlight', subkey='width')]
+        if icon != '':
+            pattern.append('icon=%s' % icon)
+        if size > 0:
+            pattern.append('size=%i' % size)
         
         if useId:
             cmd = pattern
@@ -3802,9 +1556,8 @@
             return cmd
 
     def OnAnalyze(self, event):
+        """!Analysis tools menu
         """
-        Analysis tools menu
-        """
         point = wx.GetMousePosition()
         toolsmenu = wx.Menu()
         # Add items to the menu
@@ -3829,16 +1582,13 @@
         toolsmenu.Destroy()
 
     def OnMeasure(self, event):
+        """!Init measurement routine that calculates map distance
+        along transect drawn on map display
         """
-        Init measurement routine that calculates
-        map distance along transect drawn on
-        map display
-        """
-
         self.totaldist = 0.0 # total measured distance
 
         # switch GIS Manager to output console to show measure results
-        self.gismanager.notebook.SetSelection(1)
+        self._layerManager.notebook.SetSelection(1)
 
         # change mouse to draw line for measurement
         self.MapWindow.mouse['use'] = "measure"
@@ -3851,30 +1601,28 @@
         self.MapWindow.SetCursor(self.cursors["pencil"])
 
         # initiating output
-        style = self.gismanager.goutput.cmd_output.StyleWarning
-        self.gismanager.goutput.WriteLog(_('Click and drag with left mouse button '
-                                           'to measure.%s'
-                                           'Double click with left button to clear.') % \
-                                             (os.linesep), style)
+        style = self._layerManager.goutput.cmd_output.StyleWarning
+        self._layerManager.goutput.WriteLog(_('Click and drag with left mouse button '
+                                              'to measure.%s'
+                                              'Double click with left button to clear.') % \
+                                                (os.linesep), style)
         if self.Map.projinfo['proj'] != 'xy':
             units = self.Map.projinfo['units']
-            style = self.gismanager.goutput.cmd_output.StyleCommand
-            self.gismanager.goutput.WriteLog(_('Measuring distance') + ' ('
-                                             + units + '):',
-                                             style)
+            style = self._layerManager.goutput.cmd_output.StyleCommand
+            self._layerManager.goutput.WriteLog(_('Measuring distance') + ' ('
+                                                + units + '):',
+                                                style)
         else:
-            self.gismanager.goutput.WriteLog(_('Measuring distance:'),
-                                             style)
+            self._layerManager.goutput.WriteLog(_('Measuring distance:'),
+                                                style)
 
     def MeasureDist(self, beginpt, endpt):
-        """
-        Calculate map distance from screen distance
+        """!Calculate map distance from screen distance
         and print to output window
         """
+        if self._layerManager.notebook.GetSelection() != 1:
+            self._layerManager.notebook.SetSelection(1)
 
-        if self.gismanager.notebook.GetSelection() != 1:
-            self.gismanager.notebook.SetSelection(1)
-
         dist, (north, east) = self.MapWindow.Distance(beginpt, endpt)
 
         dist = round(dist, 3)
@@ -3898,17 +1646,16 @@
             mstring = 'segment = %s %s\ttotal distance = %s %s' \
                 % (strdist,dunits,strtotdist,tdunits)
 
-        self.gismanager.goutput.WriteLog(mstring)
+        self._layerManager.goutput.WriteLog(mstring)
 
         return dist
 
     def Profile(self, event):
+        """!Init profile canvas and tools
         """
-        Init profile canvas and tools
-        """
         raster = []
         if self.tree.layer_selected and \
-               self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
+                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
             raster.append(self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
 
         self.profile = profile.ProfileFrame(self,
@@ -3920,7 +1667,7 @@
         self.profile.OnSelectRaster(None)
 
     def FormatDist(self, dist):
-        """Format length numbers and units in a nice way,
+        """!Format length numbers and units in a nice way,
         as a function of length. From code by Hamish Bowman
         Grass Development Team 2006"""
 
@@ -3967,9 +1714,8 @@
 
 
     def Histogram(self, event):
+        """!Init histogram display canvas and tools
         """
-        Init histogram display canvas and tools
-        """
         self.histogram = histogram.HistFrame(self,
                                              id=wx.ID_ANY, size=globalvar.HIST_WINDOW_SIZE,
                                              style=wx.DEFAULT_FRAME_STYLE)
@@ -3981,9 +1727,8 @@
 
 
     def OnDecoration(self, event):
+        """!Decorations overlay menu
         """
-        Decorations overlay menu
-        """
         point = wx.GetMousePosition()
         decmenu = wx.Menu()
         # Add items to the menu
@@ -4008,9 +1753,8 @@
         decmenu.Destroy()
 
     def OnAddBarscale(self, event):
+        """!Handler for scale/arrow map decoration menu selection.
         """
-        Handler for scale/arrow map decoration menu selection.
-        """
         if self.dialogs['barscale']:
             return
 
@@ -4041,9 +1785,8 @@
         self.MapWindow.mouse['use'] = 'pointer'        
 
     def OnAddLegend(self, event):
+        """!Handler for legend map decoration menu selection.
         """
-        Handler for legend map decoration menu selection.
-        """
         if self.dialogs['legend']:
             return
         
@@ -4051,7 +1794,7 @@
 
         cmd = ['d.legend', 'at=5,50,2,5']
         if self.tree.layer_selected and \
-               self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
+                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
             cmd.append('map=%s' % self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
 
         # Decoration overlay control dialog
@@ -4072,9 +1815,8 @@
         self.MapWindow.mouse['use'] = 'pointer'
 
     def OnAddText(self, event):
+        """!Handler for text decoration menu selection.
         """
-        Handler for text decoration menu selection.
-        """
         if self.MapWindow.dragid > -1:
             id = self.MapWindow.dragid
         else:
@@ -4085,8 +1827,8 @@
                 id = 101
 
         self.dialogs['text'] = gdialogs.TextLayerDialog(parent=self, ovlId=id, 
-                                                    title=_('Add text layer'),
-                                                    size=(400, 200))
+                                                        title=_('Add text layer'),
+                                                        size=(400, 200))
         self.dialogs['text'].CenterOnParent()
 
         # If OK button pressed in decoration control dialog
@@ -4117,8 +1859,7 @@
         self.MapWindow.mouse['use'] = 'pointer'
 
     def GetOptData(self, dcmd, type, params, propwin):
-        """
-        Callback method for decoration overlay command generated by
+        """!Callback method for decoration overlay command generated by
         dialog created in menuform.py
         """
         # Reset comand and rendering options in render.Map. Always render decoration.
@@ -4128,56 +1869,93 @@
         self.params[type] = params
         self.propwin[type] = propwin
 
-    def OnZoomMenu(self, event):
+    def OnZoomToMap(self, event):
+        """!
+        Set display extents to match selected raster (including NULLs)
+        or vector map.
         """
-        Zoom menu
+        self.MapWindow.ZoomToMap()
+
+    def OnZoomToRaster(self, event):
+        """!Set display extents to match selected raster map (ignore NULLs)
         """
+        self.MapWindow.ZoomToMap(ignoreNulls = True)
+
+    def OnZoomToWind(self, event):
+        """!Set display geometry to match computational region
+        settings (set with g.region)
+        """
+        self.MapWindow.ZoomToWind()
+        
+    def OnZoomToDefault(self, event):
+        """!Set display geometry to match default region settings
+        """
+        self.MapWindow.ZoomToDefault()
+        
+    def OnZoomToSaved(self, event):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        self.MapWindow.ZoomToSaved()
+        
+    def OnDisplayToWind(self, event):
+        """!Set computational region (WIND file) to match display
+        extents
+        """
+        self.MapWindow.DisplayToWind()
+ 
+    def SaveDisplayRegion(self, event):
+        """!Save display extents to named region file.
+        """
+        self.MapWindow.SaveDisplayRegion()
+        
+    def OnZoomMenu(self, event):
+        """!Popup Zoom menu
+        """
         point = wx.GetMousePosition()
         zoommenu = wx.Menu()
         # Add items to the menu
-        zoommap = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to selected map(s)'))
-        zoommenu.AppendItem(zoommap)
-        self.Bind(wx.EVT_MENU, self.MapWindow.OnZoomToMap, zoommap)
 
         zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
         zoommenu.AppendItem(zoomwind)
-        self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToWind, zoomwind)
+        self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
 
         zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
         zoommenu.AppendItem(zoomdefault)
-        self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToDefault, zoomdefault)
+        self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
 
         zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
         zoommenu.AppendItem(zoomsaved)
-        self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToSaved, zoomsaved)
+        self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
 
         savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
         zoommenu.AppendItem(savewind)
-        self.Bind(wx.EVT_MENU, self.MapWindow.DisplayToWind, savewind)
+        self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
 
         savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
         zoommenu.AppendItem(savezoom)
-        self.Bind(wx.EVT_MENU, self.MapWindow.SaveDisplayRegion, savezoom)
+        self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
 
-        # Popup the menu.  If an item is selected then its handler
+        # Popup the menu. If an item is selected then its handler
         # will be called before PopupMenu returns.
         self.PopupMenu(zoommenu)
         zoommenu.Destroy()
-
+        
     def SetProperties(self, render=False, mode=0, showCompExtent=False,
-                      constrainRes=False):
-        """Set properies of map display window"""
-        self.autoRender.SetValue(render)
-        self.toggleStatus.SetSelection(mode)
+                      constrainRes=False, projection=False):
+        """!Set properies of map display window"""
+        self.statusbarWin['render'].SetValue(render)
+        self.statusbarWin['toggle'].SetSelection(mode)
         self.StatusbarUpdate()
-        self.showRegion.SetValue(showCompExtent)
-        self.compResolution.SetValue(constrainRes)
+        self.statusbarWin['region'].SetValue(showCompExtent)
+        self.statusbarWin['resolution'].SetValue(constrainRes)
+        self.statusbarWin['projection'].SetValue(projection)
         if showCompExtent:
             self.MapWindow.regionCoords = []
         
     def IsStandalone(self):
         """!Check if Map display is standalone"""
-        if self.gismanager:
+        if self._layerManager:
             return False
         
         return True
@@ -4188,15 +1966,11 @@
         @return window reference
         @return None (if standalone)
         """
-        return self.gismanager
-
+        return self._layerManager
+    
 # end of class MapFrame
 
 class MapApp(wx.App):
-    """
-    MapApp class
-    """
-
     def OnInit(self):
         wx.InitAllImageHandlers()
         if __name__ == "__main__":
@@ -4204,14 +1978,15 @@
         else:
             Map = None
 
-        self.mapFrm = MapFrame(parent=None, id=wx.ID_ANY, Map=Map, size=(640,480))
+        self.mapFrm = MapFrame(parent=None, id=wx.ID_ANY, Map=Map,
+                               size=globalvar.MAP_WINDOW_SIZE)
         #self.SetTopWindow(Map)
         self.mapFrm.Show()
 
         if __name__ == "__main__":
             # redraw map, if new command appears
             self.redraw = False
-            status = Command(self, Map)
+            status = Command(self, Map, cmdfilename)
             status.start()
             self.timer = wx.PyTimer(self.watcher)
             # check each 0.1s
@@ -4224,10 +1999,10 @@
             # stop the timer
             self.timer.Stop()
             # terminate thread (a bit ugly)
-            os.system("""echo "quit" >> %s""" % (cmdfilename))
+            os.system("""!echo "quit" >> %s""" % (cmdfilename))
 
     def watcher(self):
-        """Redraw, if new layer appears"""
+        """!Redraw, if new layer appears"""
         if self.redraw:
             self.mapFrm.OnDraw(None)
         self.redraw = False
@@ -4245,22 +2020,24 @@
     cmdfilename = sys.argv[2]
 
     import gettext
-    gettext.install("gm_map") # replace with the appropriate catalog name
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
 
-    print "Starting monitor <%s>" % (title)
+    print >> sys.stderr, "\nStarting monitor <%s>...\n" % (title)
 
     gm_map = MapApp(0)
     # set title
-    gm_map.mapFrm.SetTitle ("GRASS GIS - Map Display: " + title + " - Location: " + \
-                                grass.gisenv()['LOCATION_NAME'])
+    gm_map.mapFrm.SetTitle(_("GRASS GIS Map Display: " +
+                             title + 
+                             " - Location: " + grass.gisenv()["LOCATION_NAME"]))
+    
     gm_map.MainLoop()
 
     if grass.gisenv().has_key("MONITOR"):
         os.system("d.mon sel=%s" % grass.gisenv()["MONITOR"])
 
     os.remove(cmdfilename)
-    os.system("""g.gisenv set="GRASS_PYCMDFILE" """)
+    os.system("""!g.gisenv set="GRASS_PYCMDFILE" """)
 
-    print "Stoping monitor <%s>" % (title)
+    print >> sys.stderr, "\nStoping monitor <%s>...\n" % (title)
 
-    sys.exit()
+    sys.exit(0)

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_command.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_command.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_command.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,63 @@
+"""
+ at package mapdisp.py
+
+ at brief Command line useg of GIS map display canvas.view).
+
+Classes:
+ - Command
+
+(C) 2006-2009 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 Jachym Cepicky
+"""
+
+import sys
+import time
+
+from threading import Thread
+
+class Command(Thread):
+    """
+    Creates thread which will observe the command file and see, if
+    there is new command to be executed
+    """
+    def __init__ (self, parent, Map, cmdfile):
+        Thread.__init__(self)
+
+        global cmdfilename
+
+        self.parent = parent
+        self.map = Map
+        self.cmdfile = open(cmdfile, "r")
+
+    def run(self):
+        """
+        Run this in thread
+        """
+        dispcmd = []
+        while 1:
+            self.parent.redraw = False
+            line = self.cmdfile.readline().strip()
+            if line == "quit":
+                break
+
+            if line:
+                try:
+                    Debug.msg (3, "Command.run(): cmd=%s" % (line))
+
+                    self.map.AddLayer(item=None, type="raster",
+                                      name='',
+                                      command=line,
+                                      l_opacity=1)
+
+                    self.parent.redraw =True
+
+                except Exception, e:
+                    print "Command Thread: ",e
+
+            time.sleep(0.1)
+
+        sys.exit()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_command.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_window.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_window.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_window.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,2932 @@
+"""!
+ at package mapdisp_window.py
+
+ at brief GIS map display canvas, buffered window.
+
+Classes:
+ - MapWindow
+ - BufferedWindow
+
+(C) 2006-2009 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 Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import time
+import math
+import sys
+import tempfile
+import traceback
+
+import wx
+
+import grass.script as grass
+
+import dbm
+import dbm_dialogs
+import gdialogs
+import gcmd
+import utils
+import globalvar
+import gselect
+from debug import Debug
+from preferences import globalSettings as UserSettings
+from units import ConvertValue as UnitsConvertValue
+from vdigit import GV_LINES as VDigit_Lines_Type
+from vdigit import VDigitCategoryDialog
+from vdigit import VDigitZBulkDialog
+from vdigit import VDigitDuplicatesDialog
+from vdigit import PseudoDC as VDigitPseudoDC
+
+class MapWindow(object):
+    """!Abstract map window class
+
+    Parent for BufferedWindow class (2D display mode) and
+    GLWindow (3D display mode)
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None, **kwargs):
+        self.parent = parent # MapFrame
+        self.Map    = Map
+        self.tree   = tree
+        self.lmgr   = lmgr
+        
+        # mouse attributes -- position on the screen, begin and end of
+        # dragging, and type of drawing
+        self.mouse = {
+            'begin': [0, 0], # screen coordinates
+            'end'  : [0, 0],
+            'use'  : "pointer",
+            'box'  : "point"
+            }
+        
+    def EraseMap(self):
+        """!Erase the canvas (virtual method)
+        """
+        pass
+
+    def UpdateMap(self):
+        """!Updates the canvas anytime there is a change to the
+        underlaying images or to the geometry of the canvas.
+        """
+        pass
+
+    def OnLeftDown(self, event):
+        pass
+
+    def OnLeftUp(self, event):
+        pass
+
+    def OnKeyDown(self, event):
+        pass
+    
+    def OnMotion(self, event):
+        """!Mouse moved
+        Track mouse motion and update status bar
+        """
+        if self.parent.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            try:
+                e, n = self.Pixel2Cell(event.GetPositionTuple())
+            except (TypeError, ValueError):
+                self.parent.statusbar.SetStatusText("", 0)
+                return
+            
+            if self.parent.toolbars['vdigit'] and \
+                    self.parent.toolbars['vdigit'].GetAction() == 'addLine' and \
+                    self.parent.toolbars['vdigit'].GetAction('type') in ('line', 'boundary') and \
+                    len(self.polycoords) > 0:
+                # for linear feature show segment and total length
+                distance_seg = self.Distance(self.polycoords[-1],
+                                             (e, n), screen=False)[0]
+                distance_tot = distance_seg
+                for idx in range(1, len(self.polycoords)):
+                    distance_tot += self.Distance(self.polycoords[idx-1],
+                                                  self.polycoords[idx],
+                                                  screen=False )[0]
+                self.parent.statusbar.SetStatusText("%.*f, %.*f (seg: %.*f; tot: %.*f)" % \
+                                                 (precision, e, precision, n,
+                                                  precision, distance_seg,
+                                                  precision, distance_tot), 0)
+            else:
+                if self.parent.statusbarWin['projection'].IsChecked():
+                    if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                        self.parent.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                    else:
+                        proj, coord  = utils.ReprojectCoordinates(coord = (e, n),
+                                                                  projOut = UserSettings.Get(group='projection',
+                                                                                             key='statusbar',
+                                                                                             subkey='proj4'),
+                                                                  flags = 'd')
+                    
+                        if coord:
+                            e, n = coord
+                            if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                                self.parent.statusbar.SetStatusText(utils.Deg2DMS(e, n, precision = precision),
+                                                                    0)
+                            else:
+                                self.parent.statusbar.SetStatusText("%.*f; %.*f" % \
+                                                                        (precision, e, precision, n), 0)
+                        else:
+                            self.parent.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
+                else:
+                    if self.parent.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                        self.parent.statusbar.SetStatusText(utils.Deg2DMS(e, n, precision = precision),
+                                                            0)
+                    else:
+                        self.parent.statusbar.SetStatusText("%.*f; %.*f" % \
+                                                                (precision, e, precision, n), 0)
+        event.Skip()
+
+    def OnZoomToMap(self, event):
+        pass
+
+    def OnZoomToRaster(self, event):
+        pass
+    
+    def GetLayerByName(self, name, mapType, dataType = 'layer'):
+        """!Get layer from layer tree by nam
+        
+        @param name layer name
+        @param type 'item' / 'layer' / 'nviz'
+
+        @return layer / map layer properties / nviz properties
+        @return None
+        """
+        if not self.tree:
+            return None
+        
+        try:
+            mapLayer = self.Map.GetListOfLayers(l_type = mapType, l_name = name)[0]
+        except IndexError:
+            return None
+        
+        if dataType == 'layer':
+            return mapLayer
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        if not item:
+            return None
+        if dataType == 'nviz':
+            return self.tree.GetPyData(item)[0]['nviz']
+        
+        return item
+    
+    def GetSelectedLayer(self, type = 'layer', multi = False):
+        """!Get selected layer from layer tree
+
+        @param type 'item' / 'layer' / 'nviz'
+        @param multi return first selected layer or all
+        
+        @return layer / map layer properties / nviz properties
+        @return None / [] on failure
+        """
+        ret = []
+        if not self.tree or \
+                not self.tree.GetSelection():
+            if multi:
+                return []
+            else:
+                return None
+        
+        if multi and \
+                type == 'item':
+            return self.tree.GetSelections()
+        
+        for item in self.tree.GetSelections():
+            if not item.IsChecked():
+                if multi:
+                    continue
+                else:
+                    return None
+
+            if type == 'item': # -> multi = False
+                return item
+        
+            try:
+                if type == 'nviz':
+                    layer = self.tree.GetPyData(item)[0]['nviz']
+                else:
+                    layer = self.tree.GetPyData(item)[0]['maplayer']
+            except:
+                layer = None
+
+            if multi:
+                ret.append(layer)
+            else:
+                return layer
+            
+        return ret
+    
+class BufferedWindow(MapWindow, wx.Window):
+    """!A Buffered window class.
+
+    When the drawing needs to change, you app needs to call the
+    UpdateMap() method. Since the drawing is stored in a bitmap, you
+    can also save the drawing to file by calling the
+    SaveToFile(self,file_name,file_type) method.
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None,
+                 style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        MapWindow.__init__(self, parent, id, Map, tree, lmgr, **kwargs)
+        wx.Window.__init__(self, parent, id, style = style, **kwargs)
+        
+        # flags
+        self.resize = False # indicates whether or not a resize event has taken place
+        self.dragimg = None # initialize variable for map panning
+
+        # variables for drawing on DC
+        self.pen = None      # pen for drawing zoom boxes, etc.
+        self.polypen = None  # pen for drawing polylines (measurements, profiles, etc)
+        # List of wx.Point tuples defining a polyline (geographical coordinates)
+        self.polycoords = []
+        # ID of rubber band line
+        self.lineid = None
+        # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
+        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_MOTION,       self.MouseActions)
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
+        self.Bind(wx.EVT_MOTION,       self.OnMotion)
+        
+        self.processMouse = True
+        
+        # render output objects
+        self.mapfile = None   # image file to be rendered
+        self.img     = None   # wx.Image object (self.mapfile)
+        # used in digitization tool (do not redraw vector map)
+        self.imgVectorMap = None
+        # decoration overlays
+        self.overlays = {}
+        # images and their PseudoDC ID's for painting and dragging
+        self.imagedict = {}   
+        self.select = {}      # selecting/unselecting decorations for dragging
+        self.textdict = {}    # text, font, and color indexed by id
+        self.currtxtid = None # PseudoDC id for currently selected text
+
+        # zoom objects
+        self.zoomhistory  = [] # list of past zoom extents
+        self.currzoom     = 0 # current set of extents in zoom history being used
+        self.zoomtype     = 1   # 1 zoom in, 0 no zoom, -1 zoom out
+        self.hitradius    = 10 # distance for selecting map decorations
+        self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
+
+        # OnSize called to make sure the buffer is initialized.
+        # This might result in OnSize getting called twice on some
+        # platforms at initialization, but little harm done.
+        ### self.OnSize(None)
+
+        self.DefinePseudoDC()
+        # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
+        self.redrawAll = True
+
+        # will store an off screen empty bitmap for saving to file
+        self._buffer = ''
+
+        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
+        self.Bind(wx.EVT_KEY_DOWN , self.OnKeyDown)
+        
+        # vars for handling mouse clicks
+        self.dragid   = -1
+        self.lastpos  = (0, 0)
+
+    def DefinePseudoDC(self, vdigit = False):
+        """!Define PseudoDC class to use
+
+        @vdigit True to use PseudoDC from vdigit
+        """
+        # create PseudoDC used for background map, map decorations like scales and legends
+        self.pdc = self.PseudoDC(vdigit)
+        # used for digitization tool
+        self.pdcVector = None
+        # decorations (region box, etc.)
+        self.pdcDec = self.PseudoDC(vdigit)
+        # pseudoDC for temporal objects (select box, measurement tool, etc.)
+        self.pdcTmp = self.PseudoDC(vdigit)
+        
+    def PseudoDC(self, vdigit = False):
+        """!Create PseudoDC instance"""
+        if vdigit:
+            PseudoDC = VDigitPseudoDC
+        else:
+            PseudoDC = wx.PseudoDC
+        
+        return PseudoDC()
+    
+    def CheckPseudoDC(self):
+        """!Try to draw background
+        
+        @return True on success
+        @return False on failure
+        """
+        try:
+            self.pdc.BeginDrawing()
+            self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+            self.pdc.BeginDrawing()
+        except StandardError, e:
+            traceback.print_exc(file = sys.stderr)
+            return False
+        
+        return True
+    
+    def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0, 0, 0, 0]):
+        """!
+        Draws map and overlay decorations
+        """
+        if drawid == None:
+            if pdctype == 'image' and img:
+                drawid = self.imagedict[img]
+            elif pdctype == 'clear':
+                drawid == None
+            else:
+                drawid = wx.NewId()
+        
+        if img and pdctype == 'image':
+            # self.imagedict[img]['coords'] = coords
+            self.select[self.imagedict[img]['id']] = False # ?
+
+        pdc.BeginDrawing()
+
+        if drawid != 99:
+            bg = wx.TRANSPARENT_BRUSH
+        else:
+            bg = wx.Brush(self.GetBackgroundColour())
+
+        pdc.SetBackground(bg)
+        
+        ### pdc.Clear()
+
+        Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % \
+                       (drawid, pdctype, coords))
+
+        # set PseudoDC id
+        if drawid is not None:
+            pdc.SetId(drawid)
+        
+        if pdctype == 'clear': # erase the display
+            bg = wx.WHITE_BRUSH
+            # bg = wx.Brush(self.GetBackgroundColour())
+            pdc.SetBackground(bg)
+            pdc.RemoveAll()
+            pdc.Clear()
+            pdc.EndDrawing()
+            
+            self.Refresh()
+            return
+
+        if pdctype == 'image': # draw selected image
+            bitmap = wx.BitmapFromImage(img)
+            w,h = bitmap.GetSize()
+            pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
+            pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
+
+        elif pdctype == 'box': # draw a box on top of the map
+            if self.pen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.pen)
+                x2 = max(coords[0],coords[2])
+                x1 = min(coords[0],coords[2])
+                y2 = max(coords[1],coords[3])
+                y1 = min(coords[1],coords[3])
+                rwidth = x2-x1
+                rheight = y2-y1
+                rect = wx.Rect(x1, y1, rwidth, rheight)
+                pdc.DrawRectangleRect(rect)
+                pdc.SetIdBounds(drawid, rect)
+                
+        elif pdctype == 'line': # draw a line on top of the map
+            if self.pen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.pen)
+                pdc.DrawLinePoint(wx.Point(coords[0], coords[1]),wx.Point(coords[2], coords[3]))
+                pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
+                # self.ovlcoords[drawid] = coords
+
+        elif pdctype == 'polyline': # draw a polyline on top of the map
+            if self.polypen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.polypen)
+                ### pdc.DrawLines(coords)
+                if (len(coords) < 2):
+                    return
+                i = 1
+                while i < len(coords):
+                    pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
+                                      wx.Point(coords[i][0], coords[i][1]))
+                    i += 1
+
+                # get bounding rectangle for polyline
+                xlist = []
+                ylist = []
+                if len(coords) > 0:
+                    for point in coords:
+                        x,y = point
+                        xlist.append(x)
+                        ylist.append(y)
+                    x1=min(xlist)
+                    x2=max(xlist)
+                    y1=min(ylist)
+                    y2=max(ylist)
+                    pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
+                    # self.ovlcoords[drawid] = [x1,y1,x2,y2]
+                    
+        elif pdctype == 'point': # draw point
+            if self.pen:
+                pdc.SetPen(self.pen)
+                pdc.DrawPoint(coords[0], coords[1])
+                coordsBound = (coords[0] - 5,
+                               coords[1] - 5,
+                               coords[0] + 5,
+                               coords[1] + 5)
+                pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
+                # self.ovlcoords[drawid] = coords
+
+        elif pdctype == 'text': # draw text on top of map
+            if not img['active']:
+                return #only draw active text
+            if img.has_key('rotation'):
+                rotation = float(img['rotation'])
+            else:
+                rotation = 0.0
+            w, h = self.GetFullTextExtent(img['text'])[0:2]
+            pdc.SetFont(img['font'])
+            pdc.SetTextForeground(img['color'])
+            coords, w, h = self.TextBounds(img)
+            if rotation == 0:
+                pdc.DrawText(img['text'], coords[0], coords[1])
+            else:
+                pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
+            pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], w, h))
+            
+        pdc.EndDrawing()
+        
+        self.Refresh()
+        
+        return drawid
+
+    def TextBounds(self, textinfo):
+        """!
+        Return text boundary data
+
+        @param textinfo text metadata (text, font, color, rotation)
+        @param coords reference point
+        """
+        if textinfo.has_key('rotation'):
+            rotation = float(textinfo['rotation'])
+        else:
+            rotation = 0.0
+        
+        coords = textinfo['coords']
+        
+        Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
+                   (textinfo['text'], rotation))
+
+        self.Update()
+        ### self.Refresh()
+
+        self.SetFont(textinfo['font'])
+
+        w, h = self.GetTextExtent(textinfo['text'])
+
+        if rotation == 0:
+            coords[2], coords[3] = coords[0] + w, coords[1] + h
+            return coords, w, h
+
+        boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
+        boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
+        coords[2] = coords[0] + boxw
+        coords[3] = coords[1] + boxh
+        
+        return coords, boxw, boxh
+
+    def OnKeyDown(self, event):
+        """!Key pressed"""
+        shift = event.ShiftDown()
+        kc = event.GetKeyCode()
+        
+        vdigitToolbar = self.parent.toolbars['vdigit']
+        ### vdigit
+        if vdigitToolbar:
+            event = None
+            if not shift:
+                if kc == ord('P'):
+                    event = wx.CommandEvent(winid = vdigitToolbar.addPoint)
+                    tool = vdigitToolbar.OnAddPoint
+                elif kc == ord('L'):
+                    event = wx.CommandEvent(winid = vdigitToolbar.addLine)
+                    tool = vdigitToolbar.OnAddLine
+            if event:
+                vdigitToolbar.OnTool(event)
+                tool(event)
+        
+    def OnPaint(self, event):
+        """!
+        Draw PseudoDC's to buffered paint DC
+
+        self.pdc for background and decorations
+        self.pdcVector for vector map which is edited
+        self.pdcTmp for temporaly drawn objects (self.polycoords)
+
+        If self.redrawAll is False on self.pdcTmp content is re-drawn
+        """
+        Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
+        
+        dc = wx.BufferedPaintDC(self, self.buffer)
+        
+        ### dc.SetBackground(wx.Brush("White"))
+        dc.Clear()
+        
+        # use PrepareDC to set position correctly
+        self.PrepareDC(dc)
+        
+        # create a clipping rect from our position and size
+        # and update region
+        rgn = self.GetUpdateRegion().GetBox()
+        dc.SetClippingRect(rgn)
+        
+        switchDraw = False
+        if self.redrawAll is None:
+            self.redrawAll = True
+            switchDraw = True
+        
+        if self.redrawAll: # redraw pdc and pdcVector
+            # draw to the dc using the calculated clipping rect
+            self.pdc.DrawToDCClipped(dc, rgn)
+            
+            # draw vector map layer
+            if self.pdcVector:
+                # decorate with GDDC (transparency)
+                try:
+                    gcdc = wx.GCDC(dc)
+                    self.pdcVector.DrawToDCClipped(gcdc, rgn)
+                except NotImplementedError, e:
+                    print >> sys.stderr, e
+                    self.pdcVector.DrawToDCClipped(dc, rgn)
+            
+            self.bufferLast = None
+        else: # do not redraw pdc and pdcVector
+            if self.bufferLast is None:
+                # draw to the dc
+                self.pdc.DrawToDC(dc)
+                
+                if self.pdcVector:
+                    # decorate with GDDC (transparency)
+                    try:
+                        gcdc = wx.GCDC(dc)
+                        self.pdcVector.DrawToDC(gcdc)
+                    except NotImplementedError, e:
+                        print >> sys.stderr, e
+                        self.pdcVector.DrawToDC(dc)
+                        
+                # store buffered image
+                # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
+                self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
+            
+            pdcLast = self.PseudoDC(vdigit = False)
+            pdcLast.DrawBitmap(self.bufferLast, 0, 0, False)
+            pdcLast.DrawToDC(dc)
+        
+        # draw decorations (e.g. region box)
+        try:
+            gcdc = wx.GCDC(dc)
+            self.pdcDec.DrawToDC(gcdc)
+        except NotImplementedError, e:
+            print >> sys.stderr, e
+            self.pdcDec.DrawToDC(dc)
+        
+        # draw temporary object on the foreground
+        ### self.pdcTmp.DrawToDCClipped(dc, rgn)
+        self.pdcTmp.DrawToDC(dc)
+        
+        if switchDraw:
+            self.redrawAll = False
+        
+    def OnSize(self, event):
+        """!Scale map image so that it is the same size as the Window
+        """
+        Debug.msg(3, "BufferedWindow.OnSize():")
+        
+        # set size of the input image
+        self.Map.ChangeMapSize(self.GetClientSize())
+        # align extent based on center point and display resolution
+        # this causes that image is not resized when display windows is resized
+        ### self.Map.AlignExtentFromDisplay()
+        
+        # Make new off screen bitmap: this bitmap will always have the
+        # current drawing in it, so it can be used to save the image to
+        # a file, or whatever.
+        self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+        
+        # get the image to be rendered
+        self.img = self.GetImage()
+        
+        # update map display
+        if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
+            self.img = self.img.Scale(self.Map.width, self.Map.height)
+            if len(self.Map.GetListOfLayers()) > 0:
+                self.UpdateMap()
+
+        # re-render image on idle
+        self.resize = True
+
+        # reposition checkbox in statusbar
+        self.parent.StatusbarReposition()
+
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+    def OnIdle(self, event):
+        """!Only re-render a composite map image from GRASS during
+        idle time instead of multiple times during resizing.
+        """
+        if self.resize:
+            self.UpdateMap(render=True)
+
+        event.Skip()
+
+    def SaveToFile(self, FileName, FileType, width, height):
+        """!This draws the pseudo DC to a buffer that can be saved to
+        a file.
+        
+        @param FileName file name
+        @param FileType type of bitmap
+        @param width image width
+        @param height image height
+        """
+        busy = wx.BusyInfo(message=_("Please wait, exporting image..."),
+                           parent=self)
+        wx.Yield()
+        
+        self.Map.ChangeMapSize((width, height))
+        ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
+        self.Map.Render(force=True, windres = True)
+        img = self.GetImage()
+        self.Draw(self.pdc, img, drawid = 99)
+        dc = wx.BufferedPaintDC(self, ibuffer)
+        dc.Clear()
+        self.PrepareDC(dc)
+        self.pdc.DrawToDC(dc)
+        if self.pdcVector:
+            self.pdcVector.DrawToDC(dc)
+        ibuffer.SaveFile(FileName, FileType)
+        
+        busy.Destroy()
+        
+        self.UpdateMap(render = True)
+        self.Refresh()
+        
+    def GetOverlay(self):
+        """!
+        Converts rendered overlay files to wx.Image
+
+        Updates self.imagedict
+
+        @return list of images
+        """
+        imgs = []
+        for overlay in self.Map.GetListOfLayers(l_type="overlay", l_active=True):
+            if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
+                img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
+                self.imagedict[img] = { 'id' : overlay.id,
+                                        'layer' : overlay }
+                imgs.append(img)
+
+        return imgs
+
+    def GetImage(self):
+        """!Converts redered map files to wx.Image
+
+        Updates self.imagedict (id=99)
+
+        @return wx.Image instance (map composition)
+        """
+        imgId = 99
+        if self.mapfile and self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
+                os.path.getsize(self.Map.mapfile):
+            img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
+        else:
+            img = None
+        
+        self.imagedict[img] = { 'id': imgId }
+        
+        return img
+
+    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.
+        
+        @param render re-render map composition
+        @param renderVector re-render vector map layer enabled for editing (used for digitizer)
+        """
+        start = time.clock()
+        
+        self.resize = False
+        
+        # if len(self.Map.GetListOfLayers()) == 0:
+        #    return False
+        
+        if self.img is None:
+            render = True
+        
+        #
+        # initialize process bar (only on 'render')
+        #
+        if render is True or renderVector is True:
+            self.parent.statusbarWin['progress'].Show()
+            if self.parent.statusbarWin['progress'].GetRange() > 0:
+                self.parent.statusbarWin['progress'].SetValue(1)
+        
+        #
+        # render background image if needed
+        #
+        
+        # update layer dictionary if there has been a change in layers
+        if self.tree and self.tree.reorder == True:
+            self.tree.ReorderLayers()
+        
+        # reset flag for auto-rendering
+        if self.tree:
+            self.tree.rerender = False
+        
+        try:
+            if render:
+                # update display size
+                self.Map.ChangeMapSize(self.GetClientSize())
+                if self.parent.statusbarWin['resolution'].IsChecked():
+                    # use computation region resolution for rendering
+                    windres = True
+                else:
+                    windres = False
+                self.mapfile = self.Map.Render(force = True, mapWindow = self.parent,
+                                               windres = windres)
+            else:
+                self.mapfile = self.Map.Render(force = False, mapWindow = self.parent)
+        except gcmd.GException, e:
+            gcmd.GError(message = e)
+            self.mapfile = None
+        
+        self.img = self.GetImage() # id=99
+            
+        #
+        # clear pseudoDcs
+        #
+        for pdc in (self.pdc,
+                    self.pdcDec,
+                    self.pdcTmp):
+            pdc.Clear()
+            pdc.RemoveAll()
+        
+        #
+        # draw background map image to PseudoDC
+        #
+        if not self.img:
+            self.Draw(self.pdc, pdctype='clear')
+        else:
+            try:
+                id = self.imagedict[self.img]['id']
+            except:
+                return False
+
+            self.Draw(self.pdc, self.img, drawid=id)
+        
+        #
+        # render vector map layer
+        #
+        digitToolbar = self.parent.toolbars['vdigit']
+        if renderVector and digitToolbar and \
+                digitToolbar.GetLayer():
+            # set region
+            self.parent.digit.driver.UpdateRegion()
+            # re-calculate threshold for digitization tool
+            self.parent.digit.driver.GetThreshold()
+            # draw map
+            if self.pdcVector:
+                self.pdcVector.Clear()
+                self.pdcVector.RemoveAll()
+            try:
+                item = self.tree.FindItemByData('maplayer', digitToolbar.GetLayer())
+            except TypeError:
+                item = None
+            
+            if item and self.tree.IsItemChecked(item):
+                self.parent.digit.driver.DrawMap()
+
+            # translate tmp objects (pointer position)
+            if digitToolbar.GetAction() == 'moveLine':
+                if  hasattr(self, "vdigitMove") and \
+                        self.vdigitMove.has_key('beginDiff'):
+                    # move line
+                    for id in self.vdigitMove['id']:
+                        self.pdcTmp.TranslateId(id,
+                                                self.vdigitMove['beginDiff'][0],
+                                                self.vdigitMove['beginDiff'][1])
+                    del self.vdigitMove['beginDiff']
+        
+        #
+        # render overlays
+        #
+        for img in self.GetOverlay():
+            # draw any active and defined overlays
+            if self.imagedict[img]['layer'].IsActive():
+                id = self.imagedict[img]['id']
+                self.Draw(self.pdc, img=img, drawid=id,
+                          pdctype=self.overlays[id]['pdcType'], coords=self.overlays[id]['coords'])
+
+        for id in self.textdict.keys():
+            self.Draw(self.pdc, img=self.textdict[id], drawid=id,
+                      pdctype='text', coords=[10, 10, 10, 10])
+        
+        # optionally draw computational extent box
+        self.DrawCompRegionExtent()
+        
+        #
+        # redraw pdcTmp if needed
+        #
+        if len(self.polycoords) > 0:
+            self.DrawLines(self.pdcTmp)
+        
+        if not self.parent.IsStandalone() and \
+                self.parent.GetLayerManager().georectifying:
+            # -> georectifier (redraw GCPs)
+            if self.parent.toolbars['georect']:
+                coordtype = 'gcpcoord'
+            else:
+                coordtype = 'mapcoord'
+            self.parent.GetLayerManager().georectifying.DrawGCP(coordtype)
+            
+        if not self.parent.IsStandalone() and \
+                self.parent.GetLayerManager().gcpmanagement:
+            # -> georectifier (redraw GCPs)
+            if self.parent.toolbars['gcpdisp']:
+                if self == self.parent.TgtMapWindow:
+                    coordtype = 'target'
+                else:
+                    coordtype = 'source'
+
+                self.parent.DrawGCP(coordtype)
+
+        # 
+        # clear measurement
+        #
+        if self.mouse["use"] == "measure":
+            self.ClearLines(pdc=self.pdcTmp)
+            self.polycoords = []
+            self.mouse['use'] = 'pointer'
+            self.mouse['box'] = 'point'
+            self.mouse['end'] = [0, 0]
+            self.SetCursor(self.parent.cursors["default"])
+            
+        stop = time.clock()
+        
+        #
+        # hide process bar
+        #
+        self.parent.statusbarWin['progress'].Hide()
+
+        #
+        # update statusbar 
+        #
+        ### self.Map.SetRegion()
+        self.parent.StatusbarUpdate()
+        if grass.find_file(name = 'MASK', element = 'cell')['name']:
+            # mask found
+            self.parent.statusbarWin['mask'].SetLabel(_('MASK'))
+        else:
+            self.parent.statusbarWin['mask'].SetLabel('')
+        
+        Debug.msg (2, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
+                   (render, renderVector, (stop-start)))
+        
+        return True
+
+    def DrawCompRegionExtent(self):
+        """!
+        Draw computational region extent in the display
+        
+        Display region is drawn as a blue box inside the computational region,
+        computational region inside a display region as a red box).
+        """
+        if hasattr(self, "regionCoords"):
+            compReg = self.Map.GetRegion()
+            dispReg = self.Map.GetCurrentRegion()
+            reg = None
+            if self.IsInRegion(dispReg, compReg):
+                self.polypen = wx.Pen(colour=wx.Colour(0, 0, 255, 128), width=3, style=wx.SOLID)
+                reg = dispReg
+            else:
+                self.polypen = wx.Pen(colour=wx.Colour(255, 0, 0, 128),
+                                      width=3, style=wx.SOLID)
+                reg = compReg
+            
+            self.regionCoords = []
+            self.regionCoords.append((reg['w'], reg['n']))
+            self.regionCoords.append((reg['e'], reg['n']))
+            self.regionCoords.append((reg['e'], reg['s']))
+            self.regionCoords.append((reg['w'], reg['s']))
+            self.regionCoords.append((reg['w'], reg['n']))
+            # draw region extent
+            self.DrawLines(pdc=self.pdcDec, polycoords=self.regionCoords)
+
+    def IsInRegion(self, region, refRegion):
+        """!
+        Test if 'region' is inside of 'refRegion'
+
+        @param region input region
+        @param refRegion reference region (e.g. computational region)
+
+        @return True if region is inside of refRegion
+        @return False 
+        """
+        if region['s'] >= refRegion['s'] and \
+                region['n'] <= refRegion['n'] and \
+                region['w'] >= refRegion['w'] and \
+                region['e'] <= refRegion['e']:
+            return True
+
+        return False
+
+    def EraseMap(self):
+        """!
+        Erase the canvas
+        """
+        self.Draw(self.pdc, pdctype='clear')
+                  
+        if self.pdcVector:
+            self.Draw(self.pdcVector, pdctype='clear')
+        
+        self.Draw(self.pdcDec, pdctype='clear')
+        self.Draw(self.pdcTmp, pdctype='clear')
+        
+    def DragMap(self, moveto):
+        """!
+        Drag the entire map image for panning.
+
+        @param moveto dx,dy
+        """
+        dc = wx.BufferedDC(wx.ClientDC(self))
+        dc.SetBackground(wx.Brush("White"))
+        dc.Clear()
+        
+        self.dragimg = wx.DragImage(self.buffer)
+        self.dragimg.BeginDrag((0, 0), self)
+        self.dragimg.GetImageRect(moveto)
+        self.dragimg.Move(moveto)
+        
+        self.dragimg.DoDrawImage(dc, moveto)
+        self.dragimg.EndDrag()
+        
+    def DragItem(self, id, event):
+        """!
+        Drag an overlay decoration item
+        """
+        if id == 99 or id == '' or id == None: return
+        Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
+        x, y = self.lastpos
+        dx = event.GetX() - x
+        dy = event.GetY() - y
+        self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+        r = self.pdc.GetIdBounds(id)
+        ### FIXME in vdigit/pseudodc.i
+        if type(r) is list:
+            r = wx.Rect(r[0], r[1], r[2], r[3])
+        if id > 100: # text dragging
+            rtop = (r[0],r[1]-r[3],r[2],r[3])
+            r = r.Union(rtop)
+            rleft = (r[0]-r[2],r[1],r[2],r[3])
+            r = r.Union(rleft)
+        self.pdc.TranslateId(id, dx, dy)
+
+        r2 = self.pdc.GetIdBounds(id)
+        if type(r2) is list:
+            r2 = wx.Rect(r[0], r[1], r[2], r[3])
+        if id > 100: # text
+            self.textdict[id]['coords'] = r2
+        r = r.Union(r2)
+        r.Inflate(4,4)
+        self.RefreshRect(r, False)
+        self.lastpos = (event.GetX(), event.GetY())
+                
+    def MouseDraw(self, pdc=None, begin=None, end=None):
+        """!
+        Mouse box or line from 'begin' to 'end'
+
+        If not given from self.mouse['begin'] to self.mouse['end'].
+
+        """
+#        self.redrawAll = False
+        
+        if not pdc:
+            return
+
+        if begin is None:
+            begin = self.mouse['begin']
+        if end is None:
+            end   = self.mouse['end']
+
+        Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
+                       (self.mouse['use'], self.mouse['box'],
+                        begin[0], begin[1], end[0], end[1]))
+
+        if self.mouse['box'] == "box":
+            boxid = wx.ID_NEW
+            mousecoords = [begin[0], begin[1],
+                           end[0], end[1]]
+            r = pdc.GetIdBounds(boxid)
+            if type(r) is list:
+                r = wx.Rect(r[0], r[1], r[2], r[3])
+            r.Inflate(4, 4)
+            try:
+                pdc.ClearId(boxid)
+            except:
+                pass
+            self.RefreshRect(r, False)
+            pdc.SetId(boxid)
+            self.Draw(pdc, drawid=boxid, pdctype='box', coords=mousecoords)
+        elif self.mouse['box'] == "line" or self.mouse['box'] == 'point':
+            self.lineid = wx.ID_NEW
+            mousecoords = [begin[0], begin[1], \
+                           end[0], end[1]]
+            x1=min(begin[0],end[0])
+            x2=max(begin[0],end[0])
+            y1=min(begin[1],end[1])
+            y2=max(begin[1],end[1])
+            r = wx.Rect(x1,y1,x2-x1,y2-y1)
+            r.Inflate(4,4)
+            try:
+                pdc.ClearId(self.lineid)
+            except:
+                pass
+            self.RefreshRect(r, False)
+            pdc.SetId(self.lineid)
+            self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=mousecoords)
+
+    def DrawLines(self, pdc=None, polycoords=None):
+        """!
+        Draw polyline in PseudoDC
+
+        Set self.pline to wx.NEW_ID + 1
+
+        polycoords - list of polyline vertices, geographical coordinates
+        (if not given, self.polycoords is used)
+
+        """
+        if not pdc:
+            pdc = self.pdcTmp
+
+        if not polycoords:
+            polycoords = self.polycoords
+        
+        if len(polycoords) > 0:
+            self.plineid = wx.ID_NEW + 1
+            # convert from EN to XY
+            coords = []
+            for p in polycoords:
+                coords.append(self.Cell2Pixel(p))
+
+            self.Draw(pdc, drawid=self.plineid, pdctype='polyline', coords=coords)
+            
+            Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
+                           (coords, self.plineid))
+
+            return self.plineid
+
+        return -1
+
+    def DrawCross(self, pdc, coords, size, rotation=0,
+                  text=None, textAlign='lr', textOffset=(5, 5)):
+        """!Draw cross in PseudoDC
+
+        @todo implement rotation
+
+        @param pdc PseudoDC
+        @param coord center coordinates
+        @param rotation rotate symbol
+        @param text draw also text (text, font, color, rotation)
+        @param textAlign alignment (default 'lower-right')
+        @textOffset offset for text (from center point)
+        """
+        Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
+                  (pdc, coords, size))
+        coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
+                       (coords[0], coords[1] - size, coords[0], coords[1] + size))
+
+        self.lineid = wx.NewId()
+        for lineCoords in coordsCross:
+            self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=lineCoords)
+
+        if not text:
+            return self.lineid
+
+        if textAlign == 'ul':
+            coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
+        elif textAlign == 'ur':
+            coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
+        elif textAlign == 'lr':
+            coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
+        else:
+            coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
+        
+        self.Draw(pdc, img=text,
+                  pdctype='text', coords=coord)
+
+        return self.lineid
+
+    def MouseActions(self, event):
+        """!
+        Mouse motion and button click notifier
+        """
+        if not self.processMouse:
+            return
+        
+        ### if self.redrawAll is False:
+        ###    self.redrawAll = True
+        
+        # zoom with mouse wheel
+        if event.GetWheelRotation() != 0:
+            self.OnMouseWheel(event)
+            
+        # left mouse button pressed
+        elif event.LeftDown():
+            self.OnLeftDown(event)
+
+        # left mouse button released
+        elif event.LeftUp():
+            self.OnLeftUp(event)
+
+        # dragging
+        elif event.Dragging():
+            self.OnDragging(event)
+
+        # double click
+        elif event.ButtonDClick():
+            self.OnButtonDClick(event)
+
+        # middle mouse button pressed
+        elif event.MiddleDown():
+            self.OnMiddleDown(event)
+
+        # middle mouse button relesed
+        elif event.MiddleUp():
+            self.OnMiddleUp(event)
+        
+        # right mouse button pressed
+        elif event.RightDown():
+            self.OnRightDown(event)
+
+        # right mouse button released
+        elif event.RightUp():
+            self.OnRightUp(event)
+
+        elif event.Entering():
+            self.OnMouseEnter(event)
+
+        elif event.Moving():
+            self.OnMouseMoving(event)
+        
+        # event.Skip()
+        
+    def OnMouseWheel(self, event):
+        """!
+        Mouse wheel moved
+        """
+        self.processMouse = False
+        current  = event.GetPositionTuple()[:]
+        wheel = event.GetWheelRotation()
+        Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
+        # zoom 1/2 of the screen, centered to current mouse position (TODO: settings)
+        begin = (current[0] - self.Map.width / 4,
+                 current[1] - self.Map.height / 4)
+        end   = (current[0] + self.Map.width / 4,
+                 current[1] + self.Map.height / 4)
+        
+        if wheel > 0:
+            zoomtype = 1
+        else:
+            zoomtype = -1
+
+        # zoom
+        self.Zoom(begin, end, zoomtype)
+
+        # redraw map
+        self.UpdateMap()
+
+        ### self.OnPaint(None)
+
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+        self.Refresh()
+        self.processMouse = True
+#        event.Skip()
+
+    def OnDragging(self, event):
+        """!
+        Mouse dragging with left button down
+        """
+        Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
+        current  = event.GetPositionTuple()[:]
+        previous = self.mouse['begin']
+        move = (current[0] - previous[0],
+                current[1] - previous[1])
+        
+        digitToolbar = self.parent.toolbars['vdigit']
+        
+        # dragging or drawing box with left button
+        if self.mouse['use'] == 'pan' or \
+                event.MiddleIsDown():
+            self.DragMap(move)
+        
+        # dragging decoration overlay item
+        elif (self.mouse['use'] == 'pointer' and 
+                not digitToolbar and 
+                self.dragid != None):
+            self.DragItem(self.dragid, event)
+        
+        # dragging anything else - rubber band box or line
+        else:
+            if (self.mouse['use'] == 'pointer' and 
+                not digitToolbar): return
+            self.mouse['end'] = event.GetPositionTuple()[:]
+            digitClass = self.parent.digit
+            if (event.LeftIsDown() and 
+                not (digitToolbar and 
+                    digitToolbar.GetAction() in ("moveLine",) and 
+                    digitClass.driver.GetSelected() > 0)):
+                # draw box only when left mouse button is pressed
+                self.MouseDraw(pdc=self.pdcTmp)
+        
+        # event.Skip()
+
+    def OnLeftDownVDigitAddLine(self, event):
+        """!
+        Left mouse button down - vector digitizer add new line
+        action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        try:
+            mapLayer = digitToolbar.GetLayer().GetName()
+        except:
+            return
+        
+        if digitToolbar.GetAction('type') in ["point", "centroid"]:
+            # add new point
+            if digitToolbar.GetAction('type') == 'point':
+                point = True
+            else:
+                point = False
+
+            east, north = self.Pixel2Cell(self.mouse['begin'])
+            fid = digitClass.AddPoint(mapLayer, point, east, north)
+            if fid < 0:
+                return
+
+            self.UpdateMap(render=False) # redraw map
+            
+            # add new record into atribute table
+            if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled')  is True:
+                # select attributes based on layer and category
+                cats = { fid : {
+                        UserSettings.Get(group='vdigit', key="layer", subkey='value') :
+                            (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
+                        }}
+                
+                posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                                 self.mouse['end'][1] + self.dialogOffset))
+                
+                addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent=self, map=mapLayer,
+                                                                   cats=cats,
+                                                                   pos=posWindow,
+                                                                   action="add")
+
+                if not point:
+                    self.__geomAttrb(fid, addRecordDlg, 'area', digitClass,
+                                     digitToolbar.GetLayer())
+                    self.__geomAttrb(fid, addRecordDlg, 'perimeter', digitClass,
+                                     digitToolbar.GetLayer())
+
+                if addRecordDlg.mapDBInfo and \
+                        addRecordDlg.ShowModal() == wx.ID_OK:
+                    sqlfile = tempfile.NamedTemporaryFile(mode="w")
+                    for sql in addRecordDlg.GetSQLString():
+                        sqlfile.file.write(sql + ";\n")
+                    sqlfile.file.flush()
+                    
+                    gcmd.RunCommand('db.execute',
+                                    parent = self,
+                                    quiet = True, 
+                                    input = sqlfile.name)
+                
+                if addRecordDlg.mapDBInfo:
+                    self.__updateATM()
+        
+        elif digitToolbar.GetAction('type') in ["line", "boundary"]:
+            # add new point to the line
+            self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
+            self.DrawLines(pdc=self.pdcTmp)
+    
+    def __geomAttrb(self, fid, dialog, attrb, digit, mapLayer):
+        """!Trac geometry attributes?"""
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        vdigit = self.tree.GetPyData(item)[0]['vdigit']
+        if vdigit and \
+                vdigit.has_key('geomAttr') and \
+                vdigit['geomAttr'].has_key(attrb):
+            val = -1
+            if attrb == 'length':
+                val = digit.GetLineLength(fid)
+                type = attrb
+            elif attrb == 'area':
+                val = digit.GetAreaSize(fid)
+                type = attrb
+            elif attrb == 'perimeter':
+                val = digit.GetAreaPerimeter(fid)
+                type = 'length'
+            
+            if val > 0:
+                layer = int(UserSettings.Get(group='vdigit', key="layer", subkey='value'))
+                column = vdigit['geomAttr'][attrb]['column']
+                val = UnitsConvertValue(val, type, vdigit['geomAttr'][attrb]['units'])
+                dialog.SetColumnValue(layer, column, val)
+                dialog.OnReset()
+        
+    def __geomAttrbUpdate(self, fids):
+        """!Update geometry atrributes of currently selected features
+
+        @param fid list feature id
+        """
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        vectorName =  mapLayer.GetName()
+        digit = self.parent.digit
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        vdigit = self.tree.GetPyData(item)[0]['vdigit']
+        
+        if vdigit is None or not vdigit.has_key('geomAttr'):
+            return
+        
+        dbInfo = gselect.VectorDBInfo(vectorName)
+        sqlfile = tempfile.NamedTemporaryFile(mode="w")
+        for fid in fids:
+            for layer, cats in digit.GetLineCats(fid).iteritems():
+                table = dbInfo.GetTable(layer)
+                for attrb, item in vdigit['geomAttr'].iteritems():
+                    val = -1
+                    if attrb == 'length':
+                        val = digit.GetLineLength(fid)
+                        type = attrb
+                    elif attrb == 'area':
+                        val = digit.GetAreaSize(fid)
+                        type = attrb
+                    elif attrb == 'perimeter':
+                        val = digit.GetAreaPerimeter(fid)
+                        type = 'length'
+
+                    if val < 0:
+                        continue
+                    val = UnitsConvertValue(val, type, item['units'])
+                    
+                    for cat in cats:
+                        sqlfile.write('UPDATE %s SET %s = %f WHERE %s = %d;\n' % \
+                                          (table, item['column'], val,
+                                           dbInfo.GetKeyColumn(layer), cat))
+            sqlfile.file.flush()
+            gcmd.RunCommand('db.execute',
+                            parent = True,
+                            quiet = True,
+                            input = sqlfile.name)
+            
+    def __updateATM(self):
+        """!Update open Attribute Table Manager
+
+        @todo: use AddDataRow() instead
+        """
+        # update ATM
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitVector = digitToolbar.GetLayer().GetName()
+                            
+        for atm in self.lmgr.dialogs['atm']:
+            atmVector = atm.GetVectorName()
+            if atmVector == digitVector:
+                layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
+                # TODO: use AddDataRow instead
+                atm.LoadData(layer)
+        
+    def OnLeftDownVDigitEditLine(self, event):
+        """!
+        Left mouse button down - vector digitizer edit linear feature
+        - add new vertex.
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
+        self.vdigitMove['id'].append(wx.NewId())
+        self.DrawLines(pdc=self.pdcTmp)
+
+    def OnLeftDownVDigitMoveLine(self, event):
+        """!
+        Left mouse button down - vector digitizer move feature/vertex,
+        edit linear feature
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        self.vdigitMove = {}
+        # geographic coordinates of initial position (left-down)
+        self.vdigitMove['begin'] = None
+        # list of ids to modify    
+        self.vdigitMove['id'] = []
+        # ids geographic coordinates
+        self.vdigitMove['coord'] = {}
+                
+        if digitToolbar.GetAction() in ["moveVertex", "editLine"]:
+            # set pen
+            pcolor = UserSettings.Get(group='vdigit', key="symbol",
+                                      subkey=["highlight", "color"])
+            self.pen = self.polypen = wx.Pen(colour=pcolor,
+                                             width=2, style=wx.SHORT_DASH)
+            self.pdcTmp.SetPen(self.polypen)
+
+    def OnLeftDownVDigitDisplayCA(self, event):
+        """!
+        Left mouse button down - vector digitizer display categories
+        or attributes action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        try:
+            mapLayer = digitToolbar.GetLayer().GetName()
+        except:
+            return
+        
+        coords = self.Pixel2Cell(self.mouse['begin'])
+        
+        # unselect
+        digitClass.driver.SetSelected([])
+        
+        # select feature by point
+        cats = {}
+        if digitClass.driver.SelectLineByPoint(coords,
+                                               digitClass.GetSelectType()) is None:
+            return
+
+        if UserSettings.Get(group='vdigit', key='checkForDupl',
+                            subkey='enabled'):
+            lines = digitClass.driver.GetSelected()
+        else:
+            lines = (digitClass.driver.GetSelected()[0],) # only first found
+                        
+        for line in lines:
+            cats[line] = digitClass.GetLineCats(line)
+                   
+        posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                         self.mouse['end'][1] + self.dialogOffset))
+    
+        if digitToolbar.GetAction() == "displayAttrs":
+            # select attributes based on coordinates (all layers)
+            if self.parent.dialogs['attributes'] is None:
+                self.parent.dialogs['attributes'] = \
+                    dbm_dialogs.DisplayAttributesDialog(parent=self, map=mapLayer,
+                                                        cats=cats,
+                                                        action="update")
+            else:
+                # upgrade dialog
+                self.parent.dialogs['attributes'].UpdateDialog(cats=cats)
+
+            if self.parent.dialogs['attributes']:
+                if len(cats.keys()) > 0:
+                    # highlight feature & re-draw map
+                    if not self.parent.dialogs['attributes'].IsShown():
+                        self.parent.dialogs['attributes'].Show()
+                else:
+                    if self.parent.dialogs['attributes'] and \
+                            self.parent.dialogs['attributes'].IsShown():
+                        self.parent.dialogs['attributes'].Hide()
+
+        else: # displayCats
+            if self.parent.dialogs['category'] is None:
+                # open new dialog
+                dlg = VDigitCategoryDialog(parent=self,
+                                           map=mapLayer,
+                                           cats=cats,
+                                           pos=posWindow,
+                                           title=_("Update categories"))
+                self.parent.dialogs['category'] = dlg
+            else:
+                # update currently open dialog
+                self.parent.dialogs['category'].UpdateDialog(cats=cats)
+                            
+            if self.parent.dialogs['category']:
+                if len(cats.keys()) > 0:
+                    # highlight feature & re-draw map
+                    if not self.parent.dialogs['category'].IsShown():
+                        self.parent.dialogs['category'].Show()
+                else:
+                    if self.parent.dialogs['category'].IsShown():
+                        self.parent.dialogs['category'].Hide()
+                
+        self.UpdateMap(render=False)
+ 
+    def OnLeftDownVDigitCopyCA(self, event):
+        """!
+        Left mouse button down - vector digitizer copy categories
+        or attributes action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if not hasattr(self, "copyCatsList"):
+            self.copyCatsList = []
+        else:
+            self.copyCatsIds = []
+            self.mouse['box'] = 'box'
+        
+    def OnLeftDownVDigitCopyLine(self, event):
+        """!
+        Left mouse button down - vector digitizer copy lines action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if not hasattr(self, "copyIds"):
+            self.copyIds = []
+            self.layerTmp = None
+        
+    def OnLeftDownVDigitBulkLine(self, event):
+        """!
+        Left mouse button down - vector digitizer label 3d vector
+        lines
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if len(self.polycoords) > 1: # start new line
+            self.polycoords = []
+            self.ClearLines(pdc=self.pdcTmp)
+        self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
+        if len(self.polycoords) == 1:
+            begin = self.Pixel2Cell(self.polycoords[-1])
+            end   = self.Pixel2Cell(self.mouse['end'])
+        else:
+            end   = self.Pixel2Cell(self.polycoords[-1])
+            begin = self.Pixel2Cell(self.mouse['begin'])
+            
+            self.DrawLines(self.pdcTmp, polycoords = (begin, end))
+        
+    def OnLeftDown(self, event):
+        """!
+        Left mouse button pressed
+        """
+        Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
+                   self.mouse["use"])
+
+        self.mouse['begin'] = event.GetPositionTuple()[:]
+        
+        if self.mouse["use"] in ["measure", "profile"]:
+            # measure or profile
+            if len(self.polycoords) == 0:
+                self.mouse['end'] = self.mouse['begin']
+                self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
+                self.ClearLines(pdc=self.pdcTmp)
+            else:
+                self.mouse['begin'] = self.mouse['end']
+        elif self.mouse['use'] == 'zoom':
+            pass
+
+        #
+        # vector digizer
+        #
+        elif self.mouse["use"] == "pointer" and \
+                self.parent.toolbars['vdigit']:
+            digitToolbar = self.parent.toolbars['vdigit']
+            digitClass   = self.parent.digit
+            
+            try:
+                mapLayer = digitToolbar.GetLayer().GetName()
+            except:
+                wx.MessageBox(parent=self,
+                              message=_("No vector map selected for editing."),
+                              caption=_("Vector digitizer"),
+                              style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+                event.Skip()
+                return
+            
+            if digitToolbar.GetAction() not in ("moveVertex",
+                                                "addVertex",
+                                                "removeVertex",
+                                                "editLine"):
+                # set pen
+                self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+                self.polypen = wx.Pen(colour='dark green', width=2, style=wx.SOLID)
+
+            if digitToolbar.GetAction() in ("addVertex",
+                                            "removeVertex",
+                                            "splitLines"):
+                # unselect
+                digitClass.driver.SetSelected([])
+
+            if digitToolbar.GetAction() == "addLine":
+                self.OnLeftDownVDigitAddLine(event)
+            
+            elif digitToolbar.GetAction() == "editLine" and \
+                    hasattr(self, "vdigitMove"):
+                self.OnLeftDownVDigitEditLine(event)
+
+            elif digitToolbar.GetAction() in ("moveLine", 
+                                              "moveVertex",
+                                              "editLine") and \
+                    not hasattr(self, "vdigitMove"):
+                self.OnLeftDownVDigitMoveLine(event)
+
+            elif digitToolbar.GetAction() in ("displayAttrs"
+                                              "displayCats"):
+                self.OnLeftDownVDigitDisplayCA(event)
+            
+            elif digitToolbar.GetAction() in ("copyCats",
+                                              "copyAttrs"):
+                self.OnLeftDownVDigitCopyCA(event)
+            
+            elif digitToolbar.GetAction() == "copyLine":
+                self.OnLeftDownVDigitCopyLine(event)
+            
+            elif digitToolbar.GetAction() == "zbulkLine":
+                self.OnLeftDownVDigitBulkLine(event)
+            
+        elif self.mouse['use'] == 'pointer':
+            # get decoration or text id
+            self.idlist = []
+            self.dragid = ''
+            self.lastpos = self.mouse['begin']
+            idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
+                                          self.hitradius)
+            if 99 in idlist:
+                idlist.remove(99)                             
+            if idlist != []:
+                self.dragid = idlist[0] #drag whatever is on top
+        else:
+            pass
+
+        event.Skip()
+
+    def OnLeftUpVDigitVarious(self, event):
+        """!
+        Left mouse button up - vector digitizer various actions
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        pos2 = self.Pixel2Cell(self.mouse['end'])
+        
+        nselected = 0
+        # -> delete line || move line || move vertex
+        if digitToolbar.GetAction() in ("moveVertex",
+                                        "editLine"):
+            if len(digitClass.driver.GetSelected()) == 0:
+                nselected = digitClass.driver.SelectLineByPoint(pos1, type=VDigit_Lines_Type)
+                
+                if digitToolbar.GetAction() == "editLine":
+                    try:
+                        selVertex = digitClass.driver.GetSelectedVertex(pos1)[0]
+                    except IndexError:
+                        selVertex = None
+                        
+                    if selVertex:
+                        # self.UpdateMap(render=False)
+                        ids = digitClass.driver.GetSelected(grassId=False)
+                        # move this line to tmp layer
+                        self.polycoords = []
+                        for id in ids:
+                            if id % 2: # register only vertices
+                                e, n = self.Pixel2Cell(self.pdcVector.GetIdBounds(id)[0:2])
+                                self.polycoords.append((e, n))
+                        digitClass.driver.DrawSelected(False) 
+                                
+                        if selVertex < ids[-1] / 2:
+                            # choose first or last node of line
+                            self.vdigitMove['id'].reverse()
+                            self.polycoords.reverse()
+                    else:
+                        # unselect
+                        digitClass.driver.SetSelected([])
+                        del self.vdigitMove
+                
+                    self.UpdateMap(render=False)
+            
+        elif digitToolbar.GetAction() in ("copyCats",
+                                          "copyAttrs"):
+            if not hasattr(self, "copyCatsIds"):
+                # 'from' -> select by point
+                nselected = digitClass.driver.SelectLineByPoint(pos1, digitClass.GetSelectType())
+                if nselected:
+                    self.copyCatsList = digitClass.driver.GetSelected()
+            else:
+                # -> 'to' -> select by bbox
+                digitClass.driver.SetSelected([])
+                # return number of selected features (by box/point)
+                nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                               digitClass.GetSelectType())
+                if nselected == 0:
+                    if digitClass.driver.SelectLineByPoint(pos1,
+                                                           digitClass.GetSelectType()) is not None:
+                        nselected = 1
+                        
+                if nselected > 0:
+                    self.copyCatsIds = digitClass.driver.GetSelected()
+
+        elif digitToolbar.GetAction() == "queryLine":
+            selected = digitClass.SelectLinesByQuery(pos1, pos2)
+            nselected = len(selected)
+            if nselected > 0:
+                digitClass.driver.SetSelected(selected)
+
+        else:
+            # -> moveLine || deleteLine, etc. (select by point/box)
+            if digitToolbar.GetAction() == 'moveLine' and \
+                    len(digitClass.driver.GetSelected()) > 0:
+                nselected = 0
+            else:
+                if digitToolbar.GetAction() == 'moveLine':
+                    drawSeg = True
+                else:
+                    drawSeg = False
+
+                nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                               digitClass.GetSelectType(),
+                                                               drawSeg)
+                    
+                if nselected == 0:
+                    if digitClass.driver.SelectLineByPoint(pos1,
+                                                           digitClass.GetSelectType()) is not None:
+                        nselected = 1
+        
+        if nselected > 0:
+            if digitToolbar.GetAction() in ("moveLine",
+                                            "moveVertex"):
+                # get pseudoDC id of objects which should be redrawn
+                if digitToolbar.GetAction() == "moveLine":
+                    # -> move line
+                    self.vdigitMove['id'] = digitClass.driver.GetSelected(grassId=False)
+                    self.vdigitMove['coord'] = digitClass.driver.GetSelectedCoord()
+                else: # moveVertex
+                    self.vdigitMove['id'] = digitClass.driver.GetSelectedVertex(pos1)
+                    if len(self.vdigitMove['id']) == 0: # no vertex found
+                        digitClass.driver.SetSelected([])
+                
+            #
+            # check for duplicates
+            #
+            if UserSettings.Get(group='vdigit', key='checkForDupl', subkey='enabled') is True:
+                dupl = digitClass.driver.GetDuplicates()
+                self.UpdateMap(render=False)
+                    
+                if dupl:
+                    posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                                     self.mouse['end'][1] + self.dialogOffset))
+                    
+                    dlg = VDigitDuplicatesDialog(parent=self, data=dupl, pos=posWindow)
+                    
+                    if dlg.ShowModal() == wx.ID_OK:
+                        digitClass.driver.UnSelect(dlg.GetUnSelected())
+                        # update selected
+                        self.UpdateMap(render=False)
+                
+            if digitToolbar.GetAction() != "editLine":
+                # -> move line || move vertex
+                self.UpdateMap(render=False)
+        
+        else: # no vector object found
+            if not (digitToolbar.GetAction() in ("moveLine",
+                                                 "moveVertex") and \
+                        len(self.vdigitMove['id']) > 0):
+                # avoid left-click when features are already selected
+                self.UpdateMap(render=False, renderVector=False)
+        
+    def OnLeftUpVDigitModifyLine(self, event):
+        """!
+        Left mouse button up - vector digitizer split line, add/remove
+        vertex action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        
+        pointOnLine = digitClass.driver.SelectLineByPoint(pos1,
+                                                          type=VDigit_Lines_Type)
+
+        if not pointOnLine:
+            return
+
+        if digitToolbar.GetAction() in ["splitLine", "addVertex"]:
+            self.UpdateMap(render=False) # highlight object
+            self.DrawCross(pdc=self.pdcTmp, coords=self.Cell2Pixel(pointOnLine),
+                           size=5)
+        else: # removeVertex
+            # get only id of vertex
+            try:
+                id = digitClass.driver.GetSelectedVertex(pos1)[0]
+            except IndexError:
+                id = None
+
+            if id:
+                x, y = self.pdcVector.GetIdBounds(id)[0:2]
+                self.pdcVector.RemoveId(id)
+                self.UpdateMap(render=False) # highlight object
+                self.DrawCross(pdc=self.pdcTmp, coords=(x, y),
+                               size=5)
+            else:
+                # unselect
+                digitClass.driver.SetSelected([])
+                self.UpdateMap(render=False)
+
+    def OnLeftUpVDigitCopyLine(self, event):
+        """!
+        Left mouse button up - vector digitizer copy feature action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        pos2 = self.Pixel2Cell(self.mouse['end'])
+        
+        if UserSettings.Get(group='vdigit', key='bgmap',
+                            subkey='value', internal=True) == '':
+            # no background map -> copy from current vector map layer
+            nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                           digitClass.GetSelectType())
+
+            if nselected > 0:
+                # highlight selected features
+                self.UpdateMap(render=False)
+            else:
+                self.UpdateMap(render=False, renderVector=False)
+        else:
+            # copy features from background map
+            self.copyIds += digitClass.SelectLinesFromBackgroundMap(pos1, pos2)
+            if len(self.copyIds) > 0:
+                color = UserSettings.Get(group='vdigit', key='symbol',
+                                         subkey=['highlight', 'color'])
+                colorStr = str(color[0]) + ":" + \
+                    str(color[1]) + ":" + \
+                    str(color[2])
+                dVectTmp = ['d.vect',
+                            'map=%s' % UserSettings.Get(group='vdigit', key='bgmap',
+                                                        subkey='value', internal=True),
+                            'cats=%s' % utils.ListOfCatsToRange(self.copyIds),
+                            '-i',
+                            'color=%s' % colorStr,
+                            'fcolor=%s' % colorStr,
+                            'type=point,line,boundary,centroid',
+                            'width=2']
+                        
+                if not self.layerTmp:
+                    self.layerTmp = self.Map.AddLayer(type='vector',
+                                                      name=globalvar.QUERYLAYER,
+                                                      command=dVectTmp)
+                else:
+                    self.layerTmp.SetCmd(dVectTmp)
+                
+                self.UpdateMap(render=True, renderVector=False)
+            else:
+                self.UpdateMap(render=False, renderVector=False)
+            
+            self.redrawAll = None
+            
+    def OnLeftUpVDigitBulkLine(self, event):
+        """!
+        Left mouse button up - vector digitizer z-bulk line action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        # select lines to be labeled
+        pos1 = self.polycoords[0]
+        pos2 = self.polycoords[1]
+        nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                       digitClass.GetSelectType())
+
+        if nselected > 0:
+            # highlight selected features
+            self.UpdateMap(render=False)
+            self.DrawLines(pdc=self.pdcTmp) # redraw temp line
+        else:
+            self.UpdateMap(render=False, renderVector=False)
+
+    def OnLeftUpVDigitConnectLine(self, event):
+        """!
+        Left mouse button up - vector digitizer connect line action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if len(digitClass.driver.GetSelected()) > 0:
+            self.UpdateMap(render=False)
+        
+    def OnLeftUp(self, event):
+        """!Left mouse button released
+        """
+        Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
+                       self.mouse["use"])
+        
+        self.mouse['end'] = event.GetPositionTuple()[:]
+        
+        if self.mouse['use'] in ["zoom", "pan"]:
+            # set region in zoom or pan
+            begin = self.mouse['begin']
+            end = self.mouse['end']
+            
+            if self.mouse['use'] == 'zoom':
+                # set region for click (zero-width box)
+                if begin[0] - end[0] == 0 or \
+                        begin[1] - end[1] == 0:
+                    # zoom 1/2 of the screen (TODO: settings)
+                    begin = (end[0] - self.Map.width / 4,
+                             end[1] - self.Map.height / 4)
+                    end   = (end[0] + self.Map.width / 4,
+                             end[1] + self.Map.height / 4)
+            
+            self.Zoom(begin, end, self.zoomtype)
+
+            # redraw map
+            self.UpdateMap(render=True)
+
+            # update statusbar
+            self.parent.StatusbarUpdate()
+
+        elif self.mouse["use"] == "query":
+            # querying
+            self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
+
+        elif self.mouse["use"] == "queryVector":
+            # editable mode for vector map layers
+            self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
+
+            # clear temp canvas
+            self.UpdateMap(render=False, renderVector=False)
+            
+        elif self.mouse["use"] in ["measure", "profile"]:
+            # measure or profile
+            if self.mouse["use"] == "measure":
+                self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
+
+            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
+            self.ClearLines(pdc=self.pdcTmp)
+            self.DrawLines(pdc=self.pdcTmp)
+        
+        elif self.mouse["use"] == "pointer" and \
+                self.parent.GetLayerManager().gcpmanagement:
+            # -> GCP manager
+            if self.parent.toolbars['gcpdisp']:
+                coord = self.Pixel2Cell(self.mouse['end'])
+                if self.parent.MapWindow == self.parent.SrcMapWindow:
+                    coordtype = 'source'
+                else:
+                    coordtype = 'target'
+
+                self.parent.GetLayerManager().gcpmanagement.SetGCPData(coordtype, coord, self, confirm=True)
+                self.UpdateMap(render=False, renderVector=False)
+
+        elif self.mouse["use"] == "pointer" and \
+                self.parent.GetLayerManager().georectifying:
+            # -> georectifying
+            coord = self.Pixel2Cell(self.mouse['end'])
+            if self.parent.toolbars['georect']:
+                coordtype = 'gcpcoord'
+            else:
+                coordtype = 'mapcoord'
+
+            self.parent.GetLayerManager().georectifying.SetGCPData(coordtype, coord, self)
+            self.UpdateMap(render=False, renderVector=False)
+
+        elif self.mouse["use"] == "pointer" and self.parent.toolbars['vdigit']:
+            # digitization tool active
+            digitToolbar = self.parent.toolbars['vdigit']
+            digitClass   = self.parent.digit
+            
+            if hasattr(self, "vdigitMove"):
+                if len(digitClass.driver.GetSelected()) == 0:
+                    self.vdigitMove['begin'] = self.Pixel2Cell(self.mouse['begin']) # left down
+                
+                # eliminate initial mouse moving efect
+                self.mouse['begin'] = self.mouse['end'] 
+
+            if digitToolbar.GetAction() in ("deleteLine",
+                                            "moveLine",
+                                            "moveVertex",
+                                            "copyCats",
+                                            "copyAttrs",
+                                            "editLine",
+                                            "flipLine",
+                                            "mergeLine",
+                                            "snapLine",
+                                            "queryLine",
+                                            "breakLine",
+                                            "typeConv",
+                                            "connectLine"):
+                self.OnLeftUpVDigitVarious(event)
+
+            elif digitToolbar.GetAction() in ("splitLine",
+                                              "addVertex",
+                                              "removeVertex"):
+                self.OnLeftUpVDigitModifyLine(event)
+
+            elif digitToolbar.GetAction() == "copyLine":
+                self.OnLeftUpVDigitCopyLine(event)
+            
+            elif digitToolbar.GetAction() == "zbulkLine" and \
+                    len(self.polycoords) == 2:
+                self.OnLeftUpVDigitBulkLine(event)
+            
+            elif digitToolbar.GetAction() == "connectLine":
+                self.OnLeftUpConnectLine(event)
+            
+            if len(digitClass.driver.GetSelected()) > 0:
+                self.redrawAll = None
+            
+        elif (self.mouse['use'] == 'pointer' and 
+                self.dragid >= 0):
+            # end drag of overlay decoration
+            
+            if self.dragid < 99 and self.overlays.has_key(self.dragid):
+                self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
+            elif self.dragid > 100 and self.textdict.has_key(self.dragid):
+                self.textdict[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
+            else:
+                pass
+            self.dragid = None
+            self.currtxtid = None
+        
+    def OnButtonDClick(self, event):
+        """!
+        Mouse button double click
+        """
+        Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
+                   self.mouse["use"])
+
+        if self.mouse["use"] == "measure":
+            # measure
+            self.ClearLines(pdc=self.pdcTmp)
+            self.polycoords = []
+            self.mouse['use'] = 'pointer'
+            self.mouse['box'] = 'point'
+            self.mouse['end'] = [0, 0]
+            self.Refresh()
+            self.SetCursor(self.parent.cursors["default"])
+
+        elif self.mouse["use"] == "profile":
+            pass
+
+        elif self.mouse['use'] == 'pointer' and \
+                self.parent.toolbars['vdigit']:
+            # vector digitizer
+            pass
+
+        else:
+            # select overlay decoration options dialog
+            clickposition = event.GetPositionTuple()[:]
+            idlist  = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
+            if idlist == []:
+                return
+            self.dragid = idlist[0]
+
+            # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
+            if self.dragid > 100:
+                self.currtxtid = self.dragid
+                self.parent.OnAddText(None)
+            elif self.dragid == 0:
+                self.parent.OnAddBarscale(None)
+            elif self.dragid == 1:
+                self.parent.OnAddLegend(None)
+        
+    def OnRightDown(self, event):
+        """!
+        Right mouse button pressed
+        """
+        Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
+                   self.mouse["use"])
+                   
+        digitToolbar = self.parent.toolbars['vdigit']
+        if digitToolbar:
+            digitClass = self.parent.digit
+            # digitization tool (confirm action)
+            if digitToolbar.GetAction() in ("moveLine",
+                                            "moveVertex") and \
+                    hasattr(self, "vdigitMove"):
+
+                pFrom = self.vdigitMove['begin']
+                pTo = self.Pixel2Cell(event.GetPositionTuple())
+                
+                move = (pTo[0] - pFrom[0],
+                        pTo[1] - pFrom[1])
+                
+                if digitToolbar.GetAction() == "moveLine":
+                    # move line
+                    if digitClass.MoveSelectedLines(move) < 0:
+                        return
+                elif digitToolbar.GetAction() == "moveVertex":
+                    # move vertex
+                    fid = digitClass.MoveSelectedVertex(pFrom, move)
+                    if fid < 0:
+                        return
+
+                    self.__geomAttrbUpdate([fid,])
+                
+                del self.vdigitMove
+                
+        event.Skip()
+
+    def OnRightUp(self, event):
+        """!
+        Right mouse button released
+        """
+        Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
+                   self.mouse["use"])
+
+        digitToolbar = self.parent.toolbars['vdigit']
+        if digitToolbar:
+            digitClass = self.parent.digit
+            # digitization tool (confirm action)
+            if digitToolbar.GetAction() == "addLine" and \
+                    digitToolbar.GetAction('type') in ["line", "boundary"]:
+                # -> add new line / boundary
+                try:
+                    map = digitToolbar.GetLayer().GetName()
+                except:
+                    map = None
+                    wx.MessageBox(parent=self,
+                                  message=_("No vector map selected for editing."),
+                                  caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                    
+                if map:
+                    # mapcoords = []
+                    # xy -> EN
+                    # for coord in self.polycoords:
+                    #    mapcoords.append(self.Pixel2Cell(coord))
+                    if digitToolbar.GetAction('type') == 'line':
+                        line = True
+                    else:
+                        line = False
+
+                    if len(self.polycoords) < 2: # ignore 'one-point' lines
+                        return
+                    
+                    fid = digitClass.AddLine(map, line, self.polycoords)
+                    if fid < 0:
+                        return
+                    
+                    position = self.Cell2Pixel(self.polycoords[-1])
+                    self.polycoords = []
+                    self.UpdateMap(render=False)
+                    self.redrawAll = True
+                    self.Refresh()
+                    
+                    # add new record into atribute table
+                    if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') and \
+                            (line is True or \
+                                 (not line and fid > 0)):
+                        posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
+                                                         position[1] + self.dialogOffset))
+
+                        # select attributes based on layer and category
+                        cats = { fid : {
+                                UserSettings.Get(group='vdigit', key="layer", subkey='value') :
+                                    (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
+                                }}
+                        
+                        addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent=self, map=map,
+                                                                           cats=cats,
+                                                                           pos=posWindow,
+                                                                           action="add")
+
+                        self.__geomAttrb(fid, addRecordDlg, 'length', digitClass,
+                                         digitToolbar.GetLayer())
+                        # auto-placing centroid
+                        self.__geomAttrb(fid, addRecordDlg, 'area', digitClass,
+                                         digitToolbar.GetLayer())
+                        self.__geomAttrb(fid, addRecordDlg, 'perimeter', digitClass,
+                                         digitToolbar.GetLayer())
+
+                        if addRecordDlg.mapDBInfo and \
+                               addRecordDlg.ShowModal() == wx.ID_OK:
+                            sqlfile = tempfile.NamedTemporaryFile(mode="w")
+                            for sql in addRecordDlg.GetSQLString():
+                                sqlfile.file.write(sql + ";\n")
+                            sqlfile.file.flush()
+                            gcmd.RunCommand('db.execute',
+                                            parent = True,
+                                            quiet = True,
+                                            input = sqlfile.name)
+                        
+                        if addRecordDlg.mapDBInfo:
+                            self.__updateATM()
+            
+            elif digitToolbar.GetAction() == "deleteLine":
+                # -> delete selected vector features
+                if digitClass.DeleteSelectedLines() < 0:
+                    return
+                self.__updateATM()
+            elif digitToolbar.GetAction() == "splitLine":
+                # split line
+                if digitClass.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0:
+                    return
+            elif digitToolbar.GetAction() == "addVertex":
+                # add vertex
+                fid = digitClass.AddVertex(self.Pixel2Cell(self.mouse['begin']))
+                if fid < 0:
+                    return
+            elif digitToolbar.GetAction() == "removeVertex":
+                # remove vertex
+                fid = digitClass.RemoveVertex(self.Pixel2Cell(self.mouse['begin']))
+                if fid < 0:
+                    return
+                self.__geomAttrbUpdate([fid,])
+            elif digitToolbar.GetAction() in ("copyCats", "copyAttrs"):
+                try:
+                    if digitToolbar.GetAction() == 'copyCats':
+                        if digitClass.CopyCats(self.copyCatsList,
+                                               self.copyCatsIds, copyAttrb=False) < 0:
+                            return
+                    else:
+                        if digitClass.CopyCats(self.copyCatsList,
+                                               self.copyCatsIds, copyAttrb=True) < 0:
+                            return
+                    
+                    del self.copyCatsList
+                    del self.copyCatsIds
+                except AttributeError:
+                    pass
+                
+                self.__updateATM()
+                
+            elif digitToolbar.GetAction() == "editLine" and \
+                    hasattr(self, "vdigitMove"):
+                line = digitClass.driver.GetSelected()
+                if digitClass.EditLine(line, self.polycoords) < 0:
+                    return
+                
+                del self.vdigitMove
+                
+            elif digitToolbar.GetAction() == "flipLine":
+                if digitClass.FlipLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "mergeLine":
+                if digitClass.MergeLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "breakLine":
+                if digitClass.BreakLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "snapLine":
+                if digitClass.SnapLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "connectLine":
+                if len(digitClass.driver.GetSelected()) > 1:
+                    if digitClass.ConnectLine() < 0:
+                        return
+            elif digitToolbar.GetAction() == "copyLine":
+                if digitClass.CopyLine(self.copyIds) < 0:
+                    return
+                del self.copyIds
+                if self.layerTmp:
+                    self.Map.DeleteLayer(self.layerTmp)
+                    self.UpdateMap(render=True, renderVector=False)
+                del self.layerTmp
+
+            elif digitToolbar.GetAction() == "zbulkLine" and len(self.polycoords) == 2:
+                pos1 = self.polycoords[0]
+                pos2 = self.polycoords[1]
+
+                selected = digitClass.driver.GetSelected()
+                dlg = VDigitZBulkDialog(parent=self, title=_("Z bulk-labeling dialog"),
+                                        nselected=len(selected))
+                if dlg.ShowModal() == wx.ID_OK:
+                    if digitClass.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
+                                             dlg.step.GetValue()) < 0:
+                        return
+                self.UpdateMap(render=False, renderVector=True)
+            elif digitToolbar.GetAction() == "typeConv":
+                # -> feature type conversion
+                # - point <-> centroid
+                # - line <-> boundary
+                if digitClass.TypeConvForSelectedLines() < 0:
+                    return
+
+            if digitToolbar.GetAction() != "addLine":
+                # unselect and re-render
+                digitClass.driver.SetSelected([])
+                self.polycoords = []
+                self.UpdateMap(render=False)
+
+            self.redrawAll = True
+            self.Refresh()
+            
+        event.Skip()
+
+    def OnMiddleDown(self, event):
+        """!
+        Middle mouse button pressed
+        """
+        self.mouse['begin'] = event.GetPositionTuple()[:]
+        
+        digitToolbar = self.parent.toolbars['vdigit']
+        # digitization tool
+        if self.mouse["use"] == "pointer" and digitToolbar:
+            digitClass = self.parent.digit
+            if (digitToolbar.GetAction() == "addLine" and \
+                    digitToolbar.GetAction('type') in ["line", "boundary"]) or \
+                    digitToolbar.GetAction() == "editLine":
+                # add line or boundary -> remove last point from the line
+                try:
+                    removed = self.polycoords.pop()
+                    Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
+                                  [removed,])
+
+                    self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
+                except:
+                    pass
+
+                if digitToolbar.GetAction() == "editLine":
+                    # remove last vertex & line
+                    if len(self.vdigitMove['id']) > 1:
+                        self.vdigitMove['id'].pop()
+
+                self.UpdateMap(render=False, renderVector=False)
+
+            elif digitToolbar.GetAction() in ["deleteLine", "moveLine", "splitLine",
+                                              "addVertex", "removeVertex", "moveVertex",
+                                              "copyCats", "flipLine", "mergeLine",
+                                              "snapLine", "connectLine", "copyLine",
+                                              "queryLine", "breakLine", "typeConv"]:
+                # varios tools -> unselected selected features
+                digitClass.driver.SetSelected([])
+                if digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] and \
+                        hasattr(self, "vdigitMove"):
+
+                    del self.vdigitMove
+                    
+                elif digitToolbar.GetAction() == "copyCats":
+                    try:
+                        del self.copyCatsList
+                        del self.copyCatsIds
+                    except AttributeError:
+                        pass
+                
+                elif digitToolbar.GetAction() == "copyLine":
+                    del self.copyIds
+                    if self.layerTmp:
+                        self.Map.DeleteLayer(self.layerTmp)
+                        self.UpdateMap(render=True, renderVector=False)
+                    del self.layerTmp
+
+                self.polycoords = []
+                self.UpdateMap(render=False) # render vector
+
+            elif digitToolbar.GetAction() == "zbulkLine":
+                # reset polyline
+                self.polycoords = []
+                digitClass.driver.SetSelected([])
+                self.UpdateMap(render=False)
+            
+            self.redrawAll = True
+
+    def OnMiddleUp(self, event):
+        """!
+        Middle mouse button released
+        """
+        self.mouse['end'] = event.GetPositionTuple()[:]
+        
+        # set region in zoom or pan
+        begin = self.mouse['begin']
+        end = self.mouse['end']
+        
+        self.Zoom(begin, end, 0) # no zoom
+        
+        # redraw map
+        self.UpdateMap(render=True)
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+        
+    def OnMouseEnter(self, event):
+        """!
+        Mouse entered window and no mouse buttons were pressed
+        """
+        if self.parent.GetLayerManager().gcpmanagement:
+            if self.parent.toolbars['gcpdisp']:
+                if not self.parent.MapWindow == self:
+                    self.parent.MapWindow = self
+                    self.parent.Map = self.Map
+                    self.parent.UpdateActive(self)
+                    # needed for wingrass
+                    self.SetFocus()
+        else:
+            event.Skip()
+
+    def OnMouseMoving(self, event):
+        """!
+        Motion event and no mouse buttons were pressed
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        if self.mouse["use"] == "pointer" and digitToolbar:
+            digitClass = self.parent.digit
+            self.mouse['end'] = event.GetPositionTuple()[:]
+            Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
+                           (self.mouse['end'][0], self.mouse['end'][1]))
+            if digitToolbar.GetAction() == "addLine" and digitToolbar.GetAction('type') in ["line", "boundary"]:
+                if len(self.polycoords) > 0:
+                    self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1]))
+            elif digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] \
+                    and hasattr(self, "vdigitMove"):
+                dx = self.mouse['end'][0] - self.mouse['begin'][0]
+                dy = self.mouse['end'][1] - self.mouse['begin'][1]
+                
+                if len(self.vdigitMove['id']) > 0:
+                    # draw lines on new position
+                    if digitToolbar.GetAction() == "moveLine":
+                        # move line
+                        for id in self.vdigitMove['id']:
+                            self.pdcTmp.TranslateId(id, dx, dy)
+                    elif digitToolbar.GetAction() in ["moveVertex", "editLine"]:
+                        # move vertex ->
+                        # (vertex, left vertex, left line,
+                        # right vertex, right line)
+
+                        # do not draw static lines
+                        if digitToolbar.GetAction() == "moveVertex":
+                            self.polycoords = []
+                            ### self.pdcTmp.TranslateId(self.vdigitMove['id'][0], dx, dy)
+                            self.pdcTmp.RemoveId(self.vdigitMove['id'][0])
+                            if self.vdigitMove['id'][1] > 0: # previous vertex
+                                x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][1])[0:2])
+                                self.pdcTmp.RemoveId(self.vdigitMove['id'][1]+1)
+                                self.polycoords.append((x, y))
+                            ### x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][0])[0:2])
+                            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
+                            if self.vdigitMove['id'][2] > 0: # next vertex
+                                x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][2])[0:2])
+                                self.pdcTmp.RemoveId(self.vdigitMove['id'][2]-1)
+                                self.polycoords.append((x, y))
+                            
+                            self.ClearLines(pdc=self.pdcTmp)
+                            self.DrawLines(pdc=self.pdcTmp)
+
+                        else: # edit line
+                            try:
+                                if self.vdigitMove['id'][-1] > 0: # previous vertex
+                                    self.MouseDraw(pdc=self.pdcTmp,
+                                                   begin=self.Cell2Pixel(self.polycoords[-1]))
+                            except: # no line
+                                self.vdigitMove['id'] = []
+                                self.polycoords = []
+
+                self.Refresh() # TODO: use RefreshRect()
+                self.mouse['begin'] = self.mouse['end']
+
+            elif digitToolbar.GetAction() == "zbulkLine":
+                if len(self.polycoords) == 1:
+                    # draw mouse moving
+                    self.MouseDraw(self.pdcTmp)
+
+        event.Skip()
+
+    def ClearLines(self, pdc=None):
+        """!
+        Clears temporary drawn lines from PseudoDC
+        """
+        if not pdc:
+            pdc=self.pdcTmp
+        try:
+            pdc.ClearId(self.lineid)
+            pdc.RemoveId(self.lineid)
+        except:
+            pass
+
+        try:
+            pdc.ClearId(self.plineid)
+            pdc.RemoveId(self.plineid)
+        except:
+            pass
+
+        Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
+                  (self.lineid, self.plineid))
+
+        ### self.Refresh()
+
+        return True
+
+    def Pixel2Cell(self, (x, y)):
+        """!Convert image coordinates to real word coordinates
+
+        @param x, y image coordinates
+        
+        @return easting, northing
+        @return None on error
+        """
+        try:
+            x = int(x)
+            y = int(y)
+        except:
+            return None
+        
+        if self.Map.region["ewres"] > self.Map.region["nsres"]:
+            res = self.Map.region["ewres"]
+        else:
+            res = self.Map.region["nsres"]
+        
+        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+        
+        east  = w + x * res
+        north = n - y * res
+        
+        return (east, north)
+
+    def Cell2Pixel(self, (east, north)):
+        """!Convert real word coordinates to image coordinates
+        """
+        try:
+            east  = float(east)
+            north = float(north)
+        except:
+            return None
+        
+        if self.Map.region["ewres"] > self.Map.region["nsres"]:
+            res = self.Map.region["ewres"]
+        else:
+            res = self.Map.region["nsres"]
+        
+        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+        
+        x = (east  - w) / res
+        y = (n - north) / res
+        
+        return (x, y)
+
+    def Zoom(self, begin, end, zoomtype):
+        """!
+        Calculates new region while (un)zoom/pan-ing
+        """
+        x1, y1 = begin
+        x2, y2 = end
+        newreg = {}
+
+        # threshold - too small squares do not make sense
+        # can only zoom to windows of > 5x5 screen pixels
+        if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
+
+            if x1 > x2:
+                x1, x2 = x2, x1
+            if y1 > y2:
+                y1, y2 = y2, y1
+
+            # zoom in
+            if zoomtype > 0:
+                newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
+                newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
+
+            # zoom out
+            elif zoomtype < 0:
+                newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
+                newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + 2 * \
+                                                                (self.Map.width  - x2),
+                                                            self.Map.height + 2 * \
+                                                                (self.Map.height - y2)))
+        # pan
+        elif zoomtype == 0:
+            dx = x1 - x2
+            dy = y1 - y2
+            if dx == 0 and dy == 0:
+                dx = x1 - self.Map.width / 2
+                dy = y1 - self.Map.height / 2
+            newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
+            newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + dx,
+                                                        self.Map.height + dy))
+
+        # if new region has been calculated, set the values
+        if newreg != {}:
+            # LL locations
+            if self.parent.Map.projinfo['proj'] == 'll':
+                if newreg['n'] > 90.0:
+                    newreg['n'] = 90.0
+                if newreg['s'] < -90.0:
+                    newreg['s'] = -90.0
+            
+            ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
+            cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
+            
+            if hasattr(self, "vdigitMove"):
+                # xo = self.Cell2Pixel((self.Map.region['center_easting'], self.Map.region['center_northing']))
+                # xn = self.Cell2Pixel(ce, cn))
+                tmp = self.Pixel2Cell(self.mouse['end'])
+            
+            # calculate new center point and display resolution
+            self.Map.region['center_easting'] = ce
+            self.Map.region['center_northing'] = cn
+            self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
+            self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
+            self.Map.AlignExtentFromDisplay()
+
+            if hasattr(self, "vdigitMove"):
+                tmp1 = self.mouse['end']
+                tmp2 = self.Cell2Pixel(self.vdigitMove['begin'])
+                dx = tmp1[0] - tmp2[0]
+                dy = tmp1[1] - tmp2[1]
+                self.vdigitMove['beginDiff'] = (dx, dy)
+                for id in self.vdigitMove['id']:
+                    self.pdcTmp.RemoveId(id)
+            
+            self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                             self.Map.region['e'], self.Map.region['w'])
+
+        if self.redrawAll is False:
+            self.redrawAll = True
+
+    def ZoomBack(self):
+        """!Zoom to previous extents in zoomhistory list
+        """
+        zoom = list()
+        
+        if len(self.zoomhistory) > 1:
+            self.zoomhistory.pop()
+            zoom = self.zoomhistory[-1]
+        
+        # disable tool if stack is empty
+        if len(self.zoomhistory) < 2: # disable tool
+            if self.parent.GetName() == 'MapWindow':
+                toolbar = self.parent.toolbars['map']
+            elif self.parent.GetName() == 'GRMapWindow':
+                toolbar = self.parent.toolbars['georect']
+            elif self.parent.GetName() == 'GCPMapWindow':
+                toolbar = self.parent.toolbars['gcpdisp']
+            
+            toolbar.Enable('zoomback', enable = False)
+        
+        # zoom to selected region
+        self.Map.GetRegion(n = zoom[0], s = zoom[1],
+                           e = zoom[2], w = zoom[3],
+                           update = True)
+        # update map
+        self.UpdateMap()
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+    def ZoomHistory(self, n, s, e, w):
+        """!
+        Manages a list of last 10 zoom extents
+
+        @param n,s,e,w north, south, east, west
+
+        @return removed history item if exists (or None)
+        """
+        removed = None
+        self.zoomhistory.append((n,s,e,w))
+        
+        if len(self.zoomhistory) > 10:
+            removed = self.zoomhistory.pop(0)
+        
+        if removed:
+            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
+                      (self.zoomhistory, removed))
+        else:
+            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
+                      (self.zoomhistory))
+        
+        # update toolbar
+        if len(self.zoomhistory) > 1:
+            enable = True
+        else:
+            enable = False
+        
+        if self.parent.GetName() == 'MapWindow':
+            toolbar = self.parent.toolbars['map']
+        elif self.parent.GetName() == 'GRMapWindow':
+            toolbar = self.parent.toolbars['georect']
+        elif self.parent.GetName() == 'GCPMapWindow':
+            toolbar = self.parent.toolbars['gcpdisp']
+        
+        toolbar.Enable('zoomback', enable)
+        
+        return removed
+
+    def ResetZoomHistory(self):
+        """!Reset zoom history"""
+        self.zoomhistory = list()
+        
+        
+    def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
+        """!Set display extents to match selected raster
+        or vector map(s).
+
+        @param layer list of layers to be zoom to
+        @param ignoreNulls True to ignore null-values
+        @param render True to re-render display
+        """
+        zoomreg = {}
+
+        if not layers:
+            layers = self.GetSelectedLayer(multi = True)
+        
+        if not layers:
+            return
+        
+        rast = []
+        vect = []
+        updated = False
+        for l in layers:
+            # only raster/vector layers are currently supported
+            if l.type == 'raster':
+                rast.append(l.GetName())
+            elif l.type == 'vector':
+                digitToolbar = self.parent.toolbars['vdigit']
+                if digitToolbar and digitToolbar.GetLayer() == l:
+                    w, s, b, e, n, t = self.parent.digit.driver.GetMapBoundingBox()
+                    self.Map.GetRegion(n=n, s=s, w=w, e=e,
+                                       update=True)
+                    updated = True
+                else:
+                    vect.append(l.name)
+        
+        if not updated:
+            self.Map.GetRegion(rast = rast,
+                               vect = vect,
+                               update = True)
+        
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+        
+        if render:
+            self.UpdateMap()
+
+        self.parent.StatusbarUpdate()
+        
+    def ZoomToWind(self):
+        """!Set display geometry to match computational region
+        settings (set with g.region)
+        """
+        self.Map.region = self.Map.GetRegion()
+        ### self.Map.SetRegion(windres=True)
+
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+
+        self.UpdateMap()
+
+        self.parent.StatusbarUpdate()
+
+    def ZoomToDefault(self):
+        """!Set display geometry to match default region settings
+        """
+        self.Map.region = self.Map.GetRegion(default=True)
+        self.Map.AdjustRegion() # aling region extent to the display
+
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+
+        self.UpdateMap()
+
+        self.parent.StatusbarUpdate()
+        
+    def DisplayToWind(self):
+        """!Set computational region (WIND file) to match display
+        extents
+        """
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+
+        # We ONLY want to set extents here. Don't mess with resolution. Leave that
+        # for user to set explicitly with g.region
+        new = self.Map.AlignResolution()
+        gcmd.RunCommand('g.region',
+                        parent = self,
+                        overwrite = True,
+                        n = new['n'],
+                        s = new['s'],
+                        e = new['e'],
+                        w = new['w'],
+                        rows = int(new['rows']),
+                        cols = int(new['cols']))
+        
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+
+    def ZoomToSaved(self):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        dlg = gdialogs.SavedRegion(parent = self,
+                                   title = _("Zoom to saved region extents"),
+                                   loadsave='load')
+        
+        if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
+            dlg.Destroy()
+            return
+        
+        if not grass.find_file(name = dlg.wind, element = 'windows')['name']:
+            wx.MessageBox(parent = self,
+                          message = _("Region <%s> not found. Operation canceled.") % dlg.wind,
+                          caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
+            dlg.Destroy()
+            return
+        
+        self.Map.GetRegion(regionName = dlg.wind,
+                           update = True)
+        
+        dlg.Destroy()
+        
+        self.ZoomHistory(self.Map.region['n'],
+                         self.Map.region['s'],
+                         self.Map.region['e'],
+                         self.Map.region['w'])
+        
+        self.UpdateMap()
+                
+    def SaveDisplayRegion(self):
+        """!Save display extents to named region file.
+        """
+        dlg = gdialogs.SavedRegion(parent = self,
+                                   title = _("Save display extents to region file"),
+                                   loadsave='save')
+        
+        if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
+            dlg.Destroy()
+            return
+
+        # test to see if it already exists and ask permission to overwrite
+        if grass.find_file(name = dlg.wind, element = 'windows')['name']:
+            overwrite = wx.MessageBox(parent = self,
+                                      message = _("Region file <%s> already exists. "
+                                                  "Do you want to overwrite it?") % (dlg.wind),
+                                      caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
+            if (overwrite == wx.YES):
+                self.SaveRegion(dlg.wind)
+        else:
+            self.SaveRegion(dlg.wind)
+        
+        dlg.Destroy()
+
+    def SaveRegion(self, wind):
+        """!Save region settings
+
+        @param wind region name
+        """
+        new = self.Map.GetCurrentRegion()
+
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+        
+        gcmd.RunCommand('g.region',
+                        overwrite = True,
+                        parent = self,
+                        flags = 'u',
+                        n = new['n'],
+                        s = new['s'],
+                        e = new['e'],
+                        w = new['w'],
+                        rows = int(new['rows']),
+                        cols = int(new['cols']),
+                        save = wind)
+        
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+
+    def Distance(self, beginpt, endpt, screen=True):
+        """!Calculete distance
+
+        LL-locations not supported
+
+        @todo Use m.distance
+
+        @param beginpt first point
+        @param endpt second point
+        @param screen True for screen coordinates otherwise EN
+        """
+        x1, y1 = beginpt
+        x2, y2 = endpt
+        if screen:
+            dEast  = (x2 - x1) * self.Map.region["ewres"]
+            dNorth = (y2 - y1) * self.Map.region["nsres"]
+        else:
+            dEast  = (x2 - x1)
+            dNorth = (y2 - y1)
+        
+        return (math.sqrt(math.pow((dEast),2) + math.pow((dNorth),2)), (dEast, dNorth))


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mapdisp_window.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mcalc_builder.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mcalc_builder.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/mcalc_builder.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -37,15 +37,24 @@
     """!Mapcalc Frame class. Calculator-style window to create and run
     r(3).mapcalc statements
     """
-    def __init__(self, parent, id = wx.ID_ANY, title = _('Map calculator'), 
-                 rast3d = False, style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER, **kwargs):
+    def __init__(self, parent, cmd, id = wx.ID_ANY,
+                 style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER, **kwargs):
         self.parent = parent
         if self.parent:
             self.log = self.parent.GetLogWindow()
         else:
             self.log = None
         
-        self.rast3d = rast3d
+        # grass command
+        self.cmd = cmd
+
+        if self.cmd == 'r.mapcalc':
+            self.rast3d = False
+            title = _('GRASS GIS Raster Map Calculator')
+        if self.cmd == 'r3.mapcalc':
+            self.rast3d = True
+            title = _('GRASS GIS 3D Raster Map Calculator')
+            
         wx.Frame.__init__(self, parent, id = id, title = title, **kwargs)
         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
@@ -304,6 +313,7 @@
         sizer.Add(item = expressSizer, proportion = 1,
                   flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
                   border = 5)
+        
         sizer.Add(item = buttonSizer4, proportion = 0,
                   flag = wx.ALIGN_RIGHT | wx.ALL, border = 1)
         
@@ -351,7 +361,7 @@
         item = event.GetString()
         self._addSomething(item)
         
-    def _addSomething(self,what):
+    def _addSomething(self, what):
         """!Inserts operators, map names, and functions into text area
         """
         self.text_mcalc.SetFocus()
@@ -373,6 +383,8 @@
         newmcalcstr += ' ' + mcalcstr[position:]
         
         self.text_mcalc.SetValue(newmcalcstr)
+        if what == '()':
+            position_offset -= 1
         self.text_mcalc.SetInsertionPoint(position + position_offset)
         self.text_mcalc.Update()
         
@@ -381,30 +393,24 @@
         """
         name = self.newmaptxt.GetValue().strip()
         if not name:
-            gcmd.GMessage(parent = self,
-                          message = _("You must enter the name of a new map to create"),
-                          msgType = 'info')
+            gcmd.GError(parent = self,
+                        message = _("You must enter the name of a new map to create"))
             return
         
         if not self.text_mcalc.GetValue().strip():
-            gcmd.GMessage(parent = self,
-                          message = _("You must enter a mapcalc statement to create a new map"),
-                          msgType = 'info')
+            gcmd.GError(parent = self,
+                        message = _("You must enter a mapcalc statement to create a new map"))
             return
         
         mctxt = self.text_mcalc.GetValue().strip().replace("\n"," ")
         mctxt = mctxt.replace(" " , "")
-        if self.rast3d:
-            prg = 'r3.mapcalc'
-        else:
-            prg = 'r.mapcalc'
 
         if self.log:
-            cmd = [prg, str('%s = %s' % (name, mctxt))]
+            cmd = [self.cmd, str('%s = %s' % (name, mctxt))]
             self.log.RunCmd(cmd)
             self.parent.Raise()
         else:
-            gcmd.RunCommand(prg,
+            gcmd.RunCommand(self.cmd,
                             "%s=%s" % (name, mctxt))
         
     def OnClear(self, event):
@@ -415,7 +421,7 @@
     def OnHelp(self, event):
         """!Launches r.mapcalc help
         """
-        gcmd.RunCommand('g.manual', entry = 'r.mapcalc')
+        gcmd.RunCommand('g.manual', parent = self, entry = self.cmd)
         
     def OnClose(self,event):
         """!Close window"""

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menu.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menu.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menu.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,105 @@
+"""!
+ at package menu.py
+
+ at brief Menu classes for wxGUI
+
+Classes:
+ - Menu
+
+(C) 2010 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 shlex
+
+import wx
+
+import globalvar
+
+class Menu(wx.MenuBar):
+    def __init__(self, parent, data):
+        """!Creates menubar"""
+        wx.MenuBar.__init__(self)
+        self.parent   = parent
+        self.menudata = data
+        self.menucmd  = dict()
+        
+        for eachMenuData in self.menudata.GetMenu():
+            for eachHeading in eachMenuData:
+                menuLabel = eachHeading[0]
+                menuItems = eachHeading[1]
+                self.Append(self._createMenu(menuItems), menuLabel)
+        
+    def _createMenu(self, menuData):
+        """!Creates menu"""
+        menu = wx.Menu()
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                label = eachItem[0]
+                subMenu = self._createMenu(eachItem[1])
+                menu.AppendMenu(wx.ID_ANY, label, subMenu)
+            else:
+                self._createMenuItem(menu, *eachItem)
+        
+        self.parent.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
+        
+        return menu
+
+    def _createMenuItem(self, menu, label, help, handler, gcmd, keywords,
+                        shortcut = '', kind = wx.ITEM_NORMAL):
+        """!Creates menu items"""
+        if not label:
+            menu.AppendSeparator()
+            return
+        
+        if len(gcmd) > 0:
+            helpString = gcmd + ' -- ' + help
+        else:
+            helpString = help
+        
+        if shortcut:
+            label += '\t' + shortcut
+        
+        menuItem = menu.Append(wx.ID_ANY, label, helpString, kind)
+        
+        self.menucmd[menuItem.GetId()] = gcmd
+        
+        if gcmd: 
+            try: 
+                cmd = shlex.split(str(gcmd)) 
+            except UnicodeError: 
+                cmd = shlex.split(utils.EncodeString((gcmd))) 
+            if cmd and cmd[0] not in globalvar.grassCmd['all']: 
+                menuItem.Enable(False)
+        
+        rhandler = eval('self.parent.' + handler)
+        
+        self.parent.Bind(wx.EVT_MENU, rhandler, menuItem)
+
+    def GetData(self):
+        """!Get menu data"""
+        return self.menudata
+    
+    def GetCmd(self):
+        """!Get list of commands
+
+        @return list of commands
+        """
+        return self.menucmd
+        
+    def OnMenuHighlight(self, event):
+        """
+        Default menu help handler
+        """
+         # Show how to get menu item info from this event handler
+        id = event.GetMenuId()
+        item = self.FindItemById(id)
+        if item:
+            text = item.GetText()
+            help = item.GetHelp()
+
+        # but in this case just call Skip so the default is done
+        event.Skip()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menu.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menudata.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menudata.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,16 +1,28 @@
-"""
+"""!
 @package menudata.py
 
- at brief Complex list for main menu entries for GRASS wxPython GUI.
+ at brief Complex list for menu entries for wxGUI.
 
 Classes:
- - Data
+ - MenuData
+ - ManagerData
+ - ModelerData
 
-COPYRIGHT:  (C) 2007-2008 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.
+Usage:
+ at code
+python menudata.py [action] [manager|modeler]
+ at endcode
 
+where <i>action</i>:
+ - strings (default)
+ - tree
+ - commands
+ - dump
+
+(C) 2007-2010 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.
+
 @author Michael Barton (Arizona State University)
 @author Yann Chemin <yann.chemin gmail.com>
 @author Martin Landa <landa.martin gmail.com>
@@ -18,63 +30,236 @@
 """
 
 import os
+import sys
 try:
     import xml.etree.ElementTree as etree
 except ImportError:
     import elementtree.ElementTree as etree # Python <= 2.4
 
-class Data:
-    '''Data object that returns menu descriptions to be used in wxgui.py.'''
-    def __init__(self, gisbase=None):
-        if not gisbase:
-            gisbase = os.getenv('GISBASE')
-	filename = gisbase + '/etc/wxpython/xml/menudata.xml'
+if not os.getenv("GISBASE"):
+    sys.exit("GRASS is not running. Exiting...")
+
+etcwxdir = os.path.join(os.getenv("GISBASE"), "etc", "wxpython")
+
+class MenuData:
+    """!Abstract menu data class"""
+    def __init__(self, filename):
 	self.tree = etree.parse(filename)
 
-    def getMenuItem(self, mi):
+    def _getMenuItem(self, mi):
+        """!Get menu item
+
+        @param mi menu item instance
+        """
 	if mi.tag == 'separator':
-	    return ('', '', '', '')
+	    return ('', '', '', '', '')
 	elif mi.tag == 'menuitem':
-	    label   = _(mi.find('label').text)
-	    help    = _(mi.find('help').text)
-	    handler = mi.find('handler').text
-	    gcmd    = mi.find('command')
+	    label    = _(mi.find('label').text)
+	    help     = _(mi.find('help').text)
+	    handler  = mi.find('handler').text
+	    gcmd     = mi.find('command')  # optional
+            keywords = mi.find('keywords') # optional
+            shortcut = mi.find('shortcut') # optional
 	    if gcmd != None:
 		gcmd = gcmd.text
 	    else:
 		gcmd = ""
-	    return (label, help, handler, gcmd)
+            if keywords != None:
+                keywords = keywords.text
+            else:
+                keywords = ""
+            if shortcut != None:
+                shortcut = shortcut.text
+            else:
+                shortcut = ""
+	    return (label, help, handler, gcmd, keywords, shortcut)
 	elif mi.tag == 'menu':
-	    return self.getMenu(mi)
+	    return self._getMenu(mi)
 	else:
-	    raise Exception()
+	    raise Exception(_("Unknow tag"))
 
-    def getMenu(self, m):
+    def _getMenu(self, m):
+        """!Get menu
+
+        @param m menu
+
+        @return label, menu items
+        """
 	label = _(m.find('label').text)
 	items = m.find('items')
-	return (label, tuple(map(self.getMenuItem, items)))
+	return (label, tuple(map(self._getMenuItem, items)))
+    
+    def _getMenuBar(self, mb):
+        """!Get menu bar
 
-    def getMenuBar(self, mb):
-	return tuple(map(self.getMenu, mb.findall('menu')))
+        @param mb menu bar instance
+        
+        @return menu items
+        """
+	return tuple(map(self._getMenu, mb.findall('menu')))
 
-    def getMenuData(self, md):
-	return list(map(self.getMenuBar, md.findall('menubar')))
+    def _getMenuData(self, md):
+        """!Get menu data
 
+        @param md menu data instace
+        
+        @return menu data
+        """
+	return list(map(self._getMenuBar, md.findall('menubar')))
+
     def GetMenu(self):
-	return self.getMenuData(self.tree.getroot())
+        """!Get menu
 
+        @return menu data
+        """
+	return self._getMenuData(self.tree.getroot())
+
     def PrintStrings(self, fh):
-	fh.write('menustrings = [\n')
+        """!Print menu strings to file (used for localization)
+
+        @param fh file descriptor"""
+        className = str(self.__class__).split('.', 1)[1]
+	fh.write('menustrings_%s = [\n' % className)
 	for node in self.tree.getiterator():
 	    if node.tag in ['label', 'help']:
 		fh.write('     _(%r),\n' % node.text)
 	fh.write('    \'\']\n')
 
+    def PrintTree(self, fh):
+        """!Print menu tree to file
+
+        @param fh file descriptor"""
+        level = 0
+        for eachMenuData in self.GetMenu():
+            for label, items in eachMenuData:
+                fh.write('- %s\n' % label.replace('&', ''))
+                self._PrintTreeItems(fh, level + 1, items)
+        
+    def _PrintTreeItems(self, fh, level, menuData):
+        """!Print menu tree items to file (used by PrintTree)
+
+        @param fh file descriptor
+        @param level menu level
+        @param menuData menu data to print out"""
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                if eachItem[0]:
+                    fh.write('%s - %s\n' % (' ' * level, eachItem[0]))
+                self._PrintTreeItems(fh, level + 1, eachItem[1])
+            else:
+                if eachItem[0]:
+                    fh.write('%s - %s\n' % (' ' * level, eachItem[0]))
+    
+    def PrintCommands(self, fh, itemSep = ' | ', menuSep = ' > '):
+        """!Print commands list (command | menu item > menu item)
+
+        @param fh file descriptor
+        """
+        level = 0
+        for eachMenuData in self.GetMenu():
+            for label, items in eachMenuData:
+                menuItems = [label, ]
+                self._PrintCommandsItems(fh, level + 1, items,
+                                         menuItems, itemSep, menuSep)
+        
+    def _PrintCommandsItems(self, fh, level, menuData,
+                             menuItems, itemSep, menuSep):
+        """!Print commands item (used by PrintCommands)
+
+        @param fh file descriptor
+        @param menuItems list of menu items
+        """
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                if eachItem[0]:
+                    try:
+                        menuItems[level] = eachItem[0]
+                    except IndexError:
+                        menuItems.append(eachItem[0])
+                self._PrintCommandsItems(fh, level + 1, eachItem[1],
+                                          menuItems, itemSep, menuSep)
+            else:
+                try:
+                    del menuItems[level]
+                except IndexError:
+                    pass
+                
+                if eachItem[3]:
+                    fh.write('%s%s' % (eachItem[3], itemSep))
+                    fh.write(menuSep.join(map(lambda x: x.replace('&', ''), menuItems)))
+                    fh.write('%s%s' % (menuSep, eachItem[0]))
+                    fh.write('\n')
+        
+class ManagerData(MenuData):
+    def __init__(self, filename = None):
+        if not filename:
+            gisbase = os.getenv('GISBASE')
+            global etcwxdir
+	    filename = os.path.join(etcwxdir, 'xml', 'menudata.xml')
+        
+        MenuData.__init__(self, filename)
+        
+    def GetModules(self):
+        """!Create dictionary of modules used to search module by
+        keywords, description, etc."""
+        modules = dict()
+        
+        for node in self.tree.getiterator():
+            if node.tag == 'menuitem':
+                module = description = ''
+                keywords = []
+                for child in node.getchildren():
+                    if child.tag == 'help':
+                        description = child.text
+                    if child.tag == 'command':
+                        module = child.text
+                    if child.tag == 'keywords':
+                        if child.text:
+                            keywords = child.text.split(',')
+                    
+                if module:
+                    modules[module] = { 'desc': description,
+                                        'keywords' : keywords }
+                    if len(keywords) < 1:
+                        print >> sys.stderr, "WARNING: Module <%s> has no keywords" % module
+                
+        return modules
+
+class ModelerData(MenuData):
+    def __init__(self, filename = None):
+        if not filename:
+            gisbase = os.getenv('GISBASE')
+            global etcwxdir
+	    filename = os.path.join(etcwxdir, 'xml', 'menudata_modeler.xml')
+        
+        MenuData.__init__(self, filename)
+
 if __name__ == "__main__":
     import sys
-    if len(sys.argv) < 2:
-        sys.exit(1)
+
+    # i18N
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+
+    action = 'strings'
+    menu   = 'manager'
     
-    Data(sys.argv[1]).PrintStrings(sys.stdout)
+    for arg in sys.argv:
+        if arg in ('strings', 'tree', 'commands', 'dump'):
+            action =  arg
+        elif arg in ('manager', 'modeler'):
+            menu = arg
+    
+    if menu == 'manager':
+        data = ManagerData()
+    else:
+        data = ModelerData()
 
+    if action == 'strings':
+        data.PrintStrings(sys.stdout)
+    elif action == 'tree':
+        data.PrintTree(sys.stdout)
+    elif action == 'commands':
+        data.PrintCommands(sys.stdout)
+    
     sys.exit(0)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menuform.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menuform.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/menuform.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -4,7 +4,6 @@
 description.
 
 Classes:
- - testSAXContentHandler
  - grassTask
  - processTask
  - helpPanel
@@ -12,8 +11,9 @@
  - cmdPanel
  - GrassGUIApp
  - GUI
+ - FloatValidator
 
- Copyright (C) 2000-2009 by the GRASS Development Team
+ Copyright (C) 2000-2010 by the GRASS Development Team
 
  This program is free software under the GPL (>=v2) Read the file
  COPYING coming with GRASS for details.
@@ -31,9 +31,9 @@
  Or you set an alias or wrap the call up in a nice shell script, GUI
  environment ... please contribute your idea.
 
- Updated to wxPython 2.8 syntax and contrib widgets.  Methods added to
- make it callable by gui.  Method added to automatically re-run with
- pythonw on a Mac.
+Copyright (C) 2000-2010 by the GRASS Development Team
+This program is free software under the GPL (>=v2) Read the file
+COPYING coming with GRASS for details.
 
 @author Jan-Oliver Wagner <jan at intevation.de>
 @author Bernhard Reiter <bernhard at intevation.de>
@@ -43,7 +43,6 @@
 
 @todo
  - verify option value types
- - use DOM instead of SAX (is it really necessary? ML)
 """
 
 import sys
@@ -54,8 +53,11 @@
 import time
 import copy
 import locale
+import types
 from threading import Thread
 import Queue
+import shlex
+import tempfile
 
 ### i18N
 import gettext
@@ -76,13 +78,13 @@
 from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
 from wx.lib.newevent import NewEvent
 
-# Do the python 2.0 standard xml thing and map it on the old names
-import xml.sax
-import xml.sax.handler
-HandlerBase=xml.sax.handler.ContentHandler
-from xml.sax import make_parser
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
 
-import utils
+import gdialogs
+from ghelp import HelpPanel
 
 gisbase = os.getenv("GISBASE")
 if gisbase is None:
@@ -96,15 +98,18 @@
 imagepath = os.path.join(wxbase, "images")
 sys.path.append(imagepath)
 
-import grassenv
+from grass.script import core as grass
+
 import gselect
 import gcmd
 import goutput
 import utils
 from preferences import globalSettings as UserSettings
+try:
+    import subprocess
+except:
+    from compat import subprocess
 
-from grass.script import core as grass
-
 wxUpdateDialog, EVT_DIALOG_UPDATE = NewEvent()
 
 # From lib/gis/col_str.c, except purple which is mentioned
@@ -143,11 +148,6 @@
             label = _('Select Color')
     return (rgb, label)
 
-
-def normalize_whitespace(text):
-    """Remove redundant whitespace from a string"""
-    return string.join( string.split(text), ' ')
-
 def text_beautify( someString , width=70):
     """
     Make really long texts shorter, clean up whitespace and
@@ -155,17 +155,17 @@
     """
     if width > 0:
         return escape_ampersand( string.strip(
-                os.linesep.join( textwrap.wrap( normalize_whitespace(someString), width ) ),
+                os.linesep.join( textwrap.wrap( utils.normalize_whitespace(someString), width ) ),
                 ".,;:" ) )
     else:
-        return escape_ampersand( string.strip(normalize_whitespace(someString ), ".,;:" ))
+        return escape_ampersand( string.strip(utils.normalize_whitespace(someString ), ".,;:" ))
     
 def escape_ampersand(text):
-    """Escapes ampersands with additional ampersand for GUI"""
+    """!Escapes ampersands with additional ampersand for GUI"""
     return string.replace(text, "&", "&&")
 
 class UpdateThread(Thread):
-    """Update dialog widgets in the thread"""
+    """!Update dialog widgets in the thread"""
     def __init__(self, parent, event, eventId, task):
         Thread.__init__(self)
         
@@ -188,12 +188,11 @@
                 if prompt == 'vector':
                     name = p.get('name', '')
                     if name in ('map', 'input'):
-                        self.eventId = p['wxId']
+                        self.eventId = p['wxId'][0]
             if self.eventId is None:
                 return
         
         p = self.task.get_param(self.eventId, element='wxId', raiseError=False)
-        
         if not p or \
                 not p.has_key('wxId-bind'):
             return
@@ -283,7 +282,7 @@
     return UpdateThread(parent, event, eventId, task)
 
 class UpdateQThread(Thread):
-    """Update dialog widgets in the thread"""
+    """!Update dialog widgets in the thread"""
     requestId = 0
     def __init__(self, parent, requestQ, resultQ, **kwds):
         Thread.__init__(self, **kwds)
@@ -317,87 +316,126 @@
             event = wxUpdateDialog(data = self.request.data)
             wx.PostEvent(self.parent, event)
 
-class testSAXContentHandler(HandlerBase):
-# SAX compliant
-    def characters(self, ch, start, length):
-        pass
-
-def test_for_broken_SAX():
-    ch=testSAXContentHandler()
-    try:
-        xml.sax.parseString("""<?xml version="1.0"?>
-            <child1 name="paul">Text goes here</child1>
-            """,ch)
-    except TypeError:
-        return 1
-    return 0
-
 class grassTask:
+    """!This class holds the structures needed for both filling by the
+    parser and use by the interface constructor.
+    
+    Use as either grassTask() for empty definition or
+    grassTask('grass.command') for parsed filling.
     """
-    This class holds the structures needed for both filling by the parser and
-    use by the interface constructor.
-
-    Use as either grassTask() for empty definition or grassTask( 'grass.command' )
-    for parsed filling.
-    """
     def __init__(self, grassModule = None):
         self.name = _('unknown')
-        self.params = []
+        self.params = list()
         self.description = ''
         self.label = ''
-        self.flags = []
-        self.keywords = []
+        self.flags = list()
+        self.keywords = list()
+        
         if grassModule is not None:
-            xml.sax.parseString( getInterfaceDescription( grassModule ) , processTask( self ) )
+            processTask(tree = etree.fromstring(getInterfaceDescription(grassModule)),
+                        task = self)
+        
+    def get_name(self):
+        """!Get task name"""
+        return self.name
+    
+    def get_list_params(self, element = 'name'):
+        """!Get list of parameters"""
+        params = []
+        for p in self.params:
+            params.append(p['name'])
+        
+        return params
 
+    def get_list_flags(self, element = 'name'):
+        """!Get list of parameters"""
+        flags = []
+        for p in self.flags:
+            flags.append(p['name'])
+        
+        return flags
+    
     def get_param(self, value, element='name', raiseError=True):
-        """Find and return a param by name."""
-        for p in self.params:
-            if p[element] == value:
-                return p
+        """!Find and return a param by name."""
+        try:
+            for p in self.params:
+                val = p[element]
+                if val is None:
+                    continue
+                if type(val) in (types.ListType, types.TupleType):
+                    if value in val:
+                        return p
+                elif type(val) == types.StringType:
+                    if p[element][:len(value)] == value:
+                        return p
+                else:
+                    if p[element] == value:
+                        return p
+        except KeyError:
+            pass
+        
         if raiseError:
-            raise ValueError, _("Parameter not found: %s") % \
-                value
+            raise ValueError, _("Parameter element '%(element)s' not found: '%(value)s'") % \
+                { 'element' : element, 'value' : value }
         else:
             return None
-
-    def set_param(self, aParam, aValue):
+        
+    def set_param(self, aParam, aValue, element = 'value'):
+        """!Set param value/values.
         """
-        Set param value/values.
-        """
-        param = self.get_param(aParam)
-        param['value'] = aValue
+        try:
+            param = self.get_param(aParam)
+        except ValueError:
+            return
+        
+        param[element] = aValue
             
-    def get_flag( self, aFlag ):
+    def get_flag(self, aFlag):
+        """!Find and return a flag by name.
         """
-        Find and return a flag by name.
-        """
         for f in self.flags:
             if f['name'] == aFlag:
                 return f
         raise ValueError, _("Flag not found: %s") % aFlag
 
-    def set_flag(self, aFlag, aValue):
+    def set_flag(self, aFlag, aValue, element = 'value'):
+        """!Enable / disable flag.
         """
-        Enable / disable flag.
+        try:
+            param = self.get_flag(aFlag)
+        except ValueError:
+            return
+        
+        param[element] = aValue
+        
+    def getCmdError(self):
+        """!Get error string produced by getCmd(ignoreErrors = False)
+
+        @return list of errors
         """
-        param = self.get_flag(aFlag)
-        param['value'] = aValue
-            
-
+        errorList = list()
+        
+        for p in self.params:
+            if p.get('value', '') == '' and p.get('required', 'no') != 'no':
+                if p.get('default', '') == '':
+                    desc = p.get('label', '')
+                    if not desc:
+                        desc = p['description']
+                    errorList.append(_("Parameter '%(name)s' (%(desc)s) is missing.") % \
+                                         {'name' : p['name'], 'desc' : desc })
+        
+        return errorList
+    
     def getCmd(self, ignoreErrors = False):
-        """
-        Produce an array of command name and arguments for feeding
+        """!Produce an array of command name and arguments for feeding
         into some execve-like command processor.
-
+        
         If ignoreErrors==True then it will return whatever has been
         built so far, even though it would not be a correct command
         for GRASS.
         """
         cmd = [self.name]
-        errors = 0
-        errStr = ""
-
+        
         for flag in self.flags:
             if 'value' in flag and flag['value']:
                 if len(flag['name']) > 1: # e.g. overwrite
@@ -408,327 +446,170 @@
             if p.get('value','') == '' and p.get('required','no') != 'no':
                 if p.get('default', '') != '':
                     cmd += [ '%s=%s' % ( p['name'], p['default'] ) ]
-                else:
+                elif ignoreErrors is False:
                     cmd += [ '%s=%s' % ( p['name'], _('<required>') ) ]
-                    errStr += _("Parameter %(name)s (%(desc)s) is missing.\n") % \
-                        {'name' : p['name'], 'desc' : p['description']}
-                    errors += 1
             elif p.get('value','') != '' and p['value'] != p.get('default','') :
                 # Output only values that have been set, and different from defaults
                 cmd += [ '%s=%s' % ( p['name'], p['value'] ) ]
-        if errors and not ignoreErrors:
-            raise ValueError, errStr
-
+        
+        errList = self.getCmdError()
+        if ignoreErrors is False and errList:
+            raise ValueError, '\n'.join(errList)
+        
         return cmd
+    
+    def set_options(self, opts):
+        """!Set flags and parameters
 
-class processTask(HandlerBase):
-    """
-    A SAX handler for the --interface-description output, as
-    defined in grass-interface.dtd. Extend or modify this and the
+        @param opts list of flags and parameters"""
+        for opt in opts:
+            if opt[0] == '-': # flag
+                self.set_flag(opt.lstrip('-'), True)
+            else: # parameter
+                key, value = opt.split('=', 1)
+                self.set_param(key, value)
+        
+    def get_options(self):
+        """!Get options"""
+        return { 'flags'  : self.flags,
+                 'params' : self.params }
+    
+    def has_required(self):
+        """!Check if command has at least one required paramater"""
+        for p in self.params:
+            if p.get('required', 'no') == 'yes':
+                return True
+        
+        return False
+
+class processTask:
+    """!A ElementTree handler for the --interface-description output,
+    as defined in grass-interface.dtd. Extend or modify this and the
     DTD if the XML output of GRASS' parser is extended or modified.
-    """
-    def __init__(self, task_description):
-        self.inLabelContent = False
-        self.inDescriptionContent = False
-        self.inDefaultContent = False
-        self.inValueContent = False
-        self.inParameter = False
-        self.inFlag = False
-        self.inGispromptContent = False
-        self.inGuisection = False
-        self.inKeywordsContent = False
-        self.inKeyDesc = False
-        self.addKeyDesc = False
-        self.inFirstParameter = True
-        self.task = task_description
 
-    def startElement(self, name, attrs):
-
-        if name == 'task':
-            self.task.name = attrs.get('name', None)
-
-        if name == 'parameter':
-            self.inParameter = True
-            self.label = '' # tmp variable
-            self.param_label = ''
-            self.param_description = ''
-            self.param_default = ''
-            self.param_values = []
-            self.param_values_description = []
-            self.param_gisprompt = False
-            self.param_age = ''
-            self.param_element = ''
-            self.param_prompt = ''
-            self.param_guisection = ''
-            self.param_key_desc = []
-            # Look for the parameter name, type, requiredness
-            self.param_name = attrs.get('name', None)
-            self.param_type = attrs.get('type', None)
-            self.param_required = attrs.get('required', None)
-            self.param_multiple = attrs.get('multiple', None)
-
-        if name == 'flag':
-            self.inFlag = True
-            self.label = '' # tmp variable
-            self.flag_label = ''
-            self.flag_description = ''
-            self.flag_default = ''
-            self.flag_guisection = ''
-            self.flag_values = []
-            # Look for the flag name
-            self.flag_name = attrs.get('name', None)
-
-        if name == 'label':
-            self.inLabelContent = True
-            self.label = ''
-
-        if name == 'description':
-            self.inDescriptionContent = True
-            self.description = ''
-
-        if name == 'default':
-            self.inDefaultContent = True
-            self.param_default = ''
-
-        if name == 'value':
-            self.inValueContent = True
-            self.value_tmp = ''
-
-        if name == 'gisprompt':
-            self.param_gisprompt = True
-            self.param_age = attrs.get('age', None)
-            self.param_element = attrs.get('element', None)
-            self.param_prompt = attrs.get('prompt', None)
-
-        if name == 'guisection':
-            self.inGuisection = True
-            self.guisection = ''
-
-        if name == 'keywords':
-            self.inKeywordsContent = True
-            self.keyword = ''
-
-        if name == 'keydesc':
-            self.inKeyDesc = True
-            self.key_desc = ''
-            
-        if name == 'item':
-            if self.inKeyDesc:
-                self.addKeyDesc = True
-
-    # works with python 2.0, but is not SAX compliant
-    def characters(self, ch):
-        self.my_characters(ch)
-
-    def my_characters(self, ch):
-        if self.inLabelContent:
-            self.label = self.label + ch
-        if self.inDescriptionContent:
-            self.description = self.description + ch
-        if self.inDefaultContent:
-            self.param_default = self.param_default + ch
-        if self.inValueContent and not self.inDescriptionContent:
-            # Beware: value_tmp will get anything outside of a <description>
-            # so in this snippet:
-            # <values>
-            #   <value>
-            #     <name> a </name>
-            #     <description> a desc </description>
-            #   </value>
-            # </values>
-            # 'a desc' will not be recorded anwhere; this unburdens further
-            # handling of value sets to distinguish between those that do define
-            # descriptions and those that do not.
-            #
-            # TODO: a set of flags to treat this case of a description sub-element
-            self.value_tmp = self.value_tmp + ch
-        if self.inGuisection:
-            self.guisection = self.guisection + ch
-        if self.inKeywordsContent:
-            self.keyword = self.keyword + ch
-        if self.addKeyDesc:
-            self.key_desc = self.key_desc + ch
-
-    def endElement(self, name):
-        # If it's not a parameter element, ignore it
-        if name == 'parameter':
-            self.inParameter = False;
-            # description -> label substitution is delegated to the client;
-            # we deal in the parser only with getting interface-description
-            # verbatim
-            self.task.params.append({
-                "name" : self.param_name,
-                "type" : self.param_type,
-                "required" : self.param_required,
-                "multiple" : self.param_multiple,
-                "label" : self.param_label,
-                "description" : self.param_description,
-                'gisprompt' : self.param_gisprompt,
-                'age' : self.param_age,
-                'element' :self.param_element,
-                'prompt' : self.param_prompt,
-                "guisection" : self.param_guisection,
-                "default" : self.param_default,
-                "values" : self.param_values,
-                "values_desc" : self.param_values_description,
-                "value" : '',
-                "key_desc": self.param_key_desc})
-
-            if self.inFirstParameter:
-                self.task.firstParam = self.param_name # store name of first parameter
-            self.inFirstParameter = False;
-
-        if name == 'flag':
-            self.inFlag = False;
-            self.task.flags.append({
-                "name" : self.flag_name,
-                "label" : self.flag_label,
-                "description" : self.flag_description,
-                "guisection" : self.flag_guisection } )
-
-        if name == 'label':
-            if self.inParameter:
-                self.param_label = normalize_whitespace(self.label)
-            elif self.inFlag:
-                self.flag_label = normalize_whitespace(self.label)
-            else:
-                self.task.label = normalize_whitespace(self.label)
-
-        if name == 'description':
-            if self.inValueContent:
-                self.param_values_description.append(normalize_whitespace(self.description))
-            elif self.inParameter:
-                self.param_description = normalize_whitespace(self.description)
-            elif self.inFlag:
-                self.flag_description = normalize_whitespace(self.description)
-            else:
-                self.task.description = normalize_whitespace(self.description)
-            self.inDescriptionContent = False
-
-        if name == 'default':
-            self.param_default = normalize_whitespace(self.param_default)
-            self.inDefaultContent = False
-
-        if name == 'value':
-            v = normalize_whitespace(self.value_tmp)
-            self.param_values.append(normalize_whitespace(self.value_tmp))
-            self.inValueContent = False
-
-        if name == 'guisection':
-            if self.inParameter:
-                self.param_guisection = normalize_whitespace(self.guisection)
-            elif self.inFlag:
-                self.flag_guisection = normalize_whitespace(self.guisection)
-            self.inGuisection = False
-
-        if name == 'keywords':
-            for keyword in self.keyword.split(','):
-                self.task.keywords.append (normalize_whitespace(keyword))
-            self.inKeywordsContent = False
-
-        if name == 'keydesc':
-            self.inKeyDesc = False
-
-        if name == 'item':
-            if self.inKeyDesc:
-                self.param_key_desc.append(normalize_whitespace(self.key_desc))
-                self.key_desc = ''
-                self.addKeyDesc = False
-
-class helpPanel(wx.html.HtmlWindow):
+    @param tree root tree node
+    @param task grassTask instance or None
+    @return grassTask instance
     """
-    This panel holds the text from GRASS docs.
-
-    GISBASE must be set in the environment to find the html docs dir.
-    The SYNOPSIS section is skipped, since this Panel is supposed to
-    be integrated into the cmdPanel and options are obvious there.
-    """
-    def __init__(self, grass_command = "index", text = None,
-                 skip_description=False, *args, **kwargs):
-        """ If grass_command is given, the corresponding HTML help file will
-        be presented, with all links pointing to absolute paths of
-        local files.
-
-        If 'skip_description' is True, the HTML corresponding to
-        SYNOPSIS will be skipped, thus only presenting the help file
-        from the DESCRIPTION section onwards.
-
-        If 'text' is given, it must be the HTML text to be presented in the Panel.
-        """
-
-        wx.html.HtmlWindow.__init__(self, *args, **kwargs)
-        self.fspath = gisbase + "/docs/html/"
-
-        self.SetStandardFonts ( size = 10 )
-        self.SetBorders(10)
-        wx.InitAllImageHandlers()
-
-        if text is None:
-            if skip_description:
-                self.fillContentsFromFile ( self.fspath + grass_command + ".html",
-                                            skip_description=skip_description )
-                self.Ok = True
-            else:
-                ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
-                # self.LoadPage(self.fspath + grass_command + ".html")
-                self.Ok = False
+    def __init__(self, tree, task = None):
+        if task:
+            self.task = task
         else:
-            self.SetPage( text )
-            self.Ok = True
+            self.task = grassTask()
+        
+        self.root = tree
+        
+        self.__processModule()
+        self.__processParams()
+        self.__processFlags()
 
-    def fillContentsFromFile( self, htmlFile, skip_description=True ):
-        aLink = re.compile( r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE )
-        imgLink = re.compile( r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE )
-        try:
-            # contents = [ '<head><base href="%s"></head>' % self.fspath ]
-            contents = []
-            skip = False
-            for l in file( htmlFile, "rb" ).readlines():
-                if "DESCRIPTION" in l:
-                    skip = False
-                if not skip:
-                    # do skip the options description if requested
-                    if "SYNOPSIS" in l:
-                        skip = skip_description
-                    else:
-                        # FIXME: find only first item
-                        findALink = aLink.search( l )
-                        if findALink is not None: 
-                            contents.append( aLink.sub(findALink.group(1)+
-                                                           self.fspath+findALink.group(2),l) )
-                        findImgLink = imgLink.search( l )
-                        if findImgLink is not None: 
-                            contents.append( imgLink.sub(findImgLink.group(1)+
-                                                         self.fspath+findImgLink.group(2),l) )
+    def __processModule(self):
+        """!Process module description"""
+        self.task.name = self.root.get('name', default = 'unknown')
         
-                        if findALink is None and findImgLink is None:
-                            contents.append( l )
-            self.SetPage( "".join( contents ) )
-            self.Ok = True
-        except: # The Manual file was not found
-            self.Ok = False
-
+        # keywords
+        for keyword in self.__getNodeText(self.root, 'keywords').split(','):
+            self.task.keywords.append(keyword.strip())
+        
+        self.task.label       = self.__getNodeText(self.root, 'label')
+        self.task.description = self.__getNodeText(self.root, 'description')
+        
+    def __processParams(self):
+        """!Process parameters description"""
+        for p in self.root.findall('parameter'):
+            # gisprompt
+            node_gisprompt = p.find('gisprompt')
+            gisprompt = False
+            age = element = prompt = None
+            if node_gisprompt is not None:
+                gisprompt = True
+                age     = node_gisprompt.get('age', '')
+                element = node_gisprompt.get('element', '')
+                prompt  = node_gisprompt.get('prompt', '')
+            
+            # value(s)
+            values = []
+            values_desc = []
+            node_values = p.find('values')
+            if node_values is not None:
+                for pv in node_values.findall('value'):
+                    values.append(self.__getNodeText(pv, 'name'))
+                    desc = self.__getNodeText(pv, 'description')
+                    if desc:
+                        values_desc.append(desc)
+            
+            # keydesc
+            key_desc = []
+            node_key_desc = p.find('keydesc')
+            if node_key_desc is not None:
+                for ki in node_key_desc.findall('item'):
+                    key_desc.append(ki.text)
+            
+            self.task.params.append( {
+                "name"        : p.get('name'),
+                "type"        : p.get('type'),
+                "required"    : p.get('required'),
+                "multiple"    : p.get('multiple'),
+                "label"       : self.__getNodeText(p, 'label'),
+                "description" : self.__getNodeText(p, 'description'),
+                'gisprompt'   : gisprompt,
+                'age'         : age,
+                'element'     : element,
+                'prompt'      : prompt,
+                "guisection"  : self.__getNodeText(p, 'guisection'),
+                "default"     : self.__getNodeText(p, 'default'),
+                "values"      : values,
+                "values_desc" : values_desc,
+                "value"       : '',
+                "key_desc"    : key_desc } )
+            
+    def __processFlags(self):
+        """!Process flags description"""
+        for p in self.root.findall('flag'):
+            self.task.flags.append( {
+                    "name"        : p.get('name'),
+                    "label"       : self.__getNodeText(p, 'label'),
+                    "description" : self.__getNodeText(p, 'description'),
+                    "guisection"  : self.__getNodeText(p, 'guisection') } )
+        
+    def __getNodeText(self, node, tag, default = ''):
+        """!Get node text"""
+        p = node.find(tag)
+        if p is not None:
+            return utils.normalize_whitespace(p.text)
+        
+        return default
+    
+    def GetTask(self):
+        """!Get grassTask instance"""
+        return self.task
+        
+    
 class mainFrame(wx.Frame):
-    """
-    This is the Frame containing the dialog for options input.
+    """!This is the Frame containing the dialog for options input.
 
     The dialog is organized in a notebook according to the guisections
     defined by each GRASS command.
 
-    If run with a parent, it may Apply, Ok or Cancel; the latter two close the dialog.
-    The former two trigger a callback.
+    If run with a parent, it may Apply, Ok or Cancel; the latter two
+    close the dialog.  The former two trigger a callback.
 
     If run standalone, it will allow execution of the command.
 
-    The command is checked and sent to the clipboard when clicking 'Copy'.
+    The command is checked and sent to the clipboard when clicking
+    'Copy'.
     """
-    def __init__(self, parent, ID, task_description, get_dcmd=None, layer=None):
-
+    def __init__(self, parent, ID, task_description,
+                 get_dcmd = None, layer = None):
         self.get_dcmd = get_dcmd
-        self.layer = layer
-        self.task = task_description
-        self.parent = parent # LayerTree | None
-
+        self.layer    = layer
+        self.task     = task_description
+        self.parent   = parent            # LayerTree | Modeler | None | ...
+        if parent and parent.GetName() == 'Modeler':
+            self.modeler = self.parent
+        else:
+            self.modeler = None
+        
         # module name + keywords
         if self.task.name.split('.')[-1] in ('py', 'sh'):
             title = str(self.task.name.rsplit('.',1)[0])
@@ -739,9 +620,10 @@
                 title +=  " [" + ', '.join( self.task.keywords ) + "]"
         except ValueError:
             pass
-
+        
         wx.Frame.__init__(self, parent=parent, id=ID, title=title,
-                          pos=wx.DefaultPosition, style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
+                          pos=wx.DefaultPosition, style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
+                          name = "MainFrame")
 
         self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
 
@@ -757,14 +639,10 @@
 
         # set apropriate output window
         if self.parent:
-            self.standalone   = False
+            self.standalone = False
         else:
             self.standalone = True
-            #             try:
-            #                 self.goutput  = self.parent.GetLogWindow()
-            #             except:
-            #                 self.goutput  = None
-
+        
         # logo+description
         topsizer = wx.BoxSizer(wx.HORIZONTAL)
 
@@ -774,7 +652,7 @@
                                                                        'grass_form.png'),
                                                      type=wx.BITMAP_TYPE_PNG))
         topsizer.Add (item=self.logo, proportion=0, border=3,
-                      flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+                      flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
 
         #
         # put module description
@@ -783,21 +661,19 @@
             module_desc = self.task.label + ' ' + self.task.description
         else:
             module_desc = self.task.description
-        self.description = StaticWrapText (parent=self.panel,
-                                           label=module_desc)
+        self.description = gdialogs.StaticWrapText(parent=self.panel,
+                                                   label=module_desc)
         topsizer.Add (item=self.description, proportion=1, border=5,
                       flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
-
+                      
         guisizer.Add (item=topsizer, proportion=0, flag=wx.EXPAND)
-
+        
         self.panel.SetSizerAndFit(guisizer)
         self.Layout()
 
         # notebooks
-        self.notebookpanel = cmdPanel (parent=self.panel, task=self.task, standalone=self.standalone,
-                                       mainFrame=self)
-        ### add 'command output' tab also for dialog open from menu
-        #         if self.standalone:
+        self.notebookpanel = cmdPanel(parent = self.panel, task = self.task,
+                                      mainFrame = self)
         self.goutput = self.notebookpanel.goutput
         self.notebookpanel.OnUpdateValues = self.updateValuesHook
         guisizer.Add (item=self.notebookpanel, proportion=1, flag=wx.EXPAND)
@@ -814,13 +690,15 @@
         btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
         # cancel
         self.btn_cancel = wx.Button(parent=self.panel, id=wx.ID_CLOSE)
-        self.btn_cancel.SetToolTipString(_("Close this window without executing the command"))
+        self.btn_cancel.SetToolTipString(_("Close this window without executing the command (Ctrl+Q)"))
         btnsizer.Add(item=self.btn_cancel, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10)
         self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
         # help
         self.btn_help = wx.Button(parent=self.panel, id=wx.ID_HELP)
-        self.btn_help.SetToolTipString(_("Show manual page of the command"))
+        self.btn_help.SetToolTipString(_("Show manual page of the command (Ctrl+H)"))
         self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
+        if not hasattr(self.notebookpanel, "manual_tab_id"):
+            self.btn_help.Hide()
         if self.get_dcmd is not None: # A callback has been set up
             btn_apply = wx.Button(parent=self.panel, id=wx.ID_APPLY)
             btn_ok = wx.Button(parent=self.panel, id=wx.ID_OK)
@@ -838,19 +716,19 @@
         else: # We're standalone
             # run
             self.btn_run = wx.Button(parent=self.panel, id=wx.ID_OK, label= _("&Run"))
-            self.btn_run.SetToolTipString(_("Run the command"))
+            self.btn_run.SetToolTipString(_("Run the command (Ctrl+R)"))
             self.btn_run.SetDefault()
             # abort
-            self.btn_abort = wx.Button(parent=self.panel, id=wx.ID_STOP)
-            self.btn_abort.SetToolTipString(_("Abort the running command"))
-            self.btn_abort.Enable(False)
+            ### self.btn_abort = wx.Button(parent=self.panel, id=wx.ID_STOP)
+            ### self.btn_abort.SetToolTipString(_("Abort the running command"))
+            ### self.btn_abort.Enable(False)
             # copy
             self.btn_clipboard = wx.Button(parent=self.panel, id=wx.ID_COPY)
-            self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard"))
+            self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
 
-            btnsizer.Add(item=self.btn_abort, proportion=0,
-                         flag=wx.ALL | wx.ALIGN_CENTER,
-                         border=10)
+            ### btnsizer.Add(item=self.btn_abort, proportion=0,
+            ### flag=wx.ALL | wx.ALIGN_CENTER,
+            ### border=10)
 
             btnsizer.Add(item=self.btn_run, proportion=0,
                          flag=wx.ALL | wx.ALIGN_CENTER,
@@ -861,24 +739,26 @@
                          border=10)
 
             self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
-            self.btn_abort.Bind(wx.EVT_BUTTON, self.OnAbort)
+            ### self.btn_abort.Bind(wx.EVT_BUTTON, self.OnAbort)
             self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
 
         # add help button
         btnsizer.Add(item=self.btn_help, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10)
 
-        guisizer.Add(item=btnsizer, proportion=0, flag=wx.ALIGN_CENTER)
+        guisizer.Add(item=btnsizer, proportion=0, flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
+                     border = 30)
 
-        if self.parent is not None:
-            self.outputType = None
+        if self.parent and not self.modeler:
+            addLayer = False
             for p in self.task.params:
-                if p.get('name', '') == 'output':
-                    self.outputType = p.get('prompt', None)
-                    break
-            if self.outputType in ('raster', 'vector', '3d-raster'):
+                if p.get('age', 'old') == 'new' and \
+                   p.get('prompt', '') in ('raster', 'vector', '3d-raster'):
+                    addLayer = True
+            
+            if addLayer:
                 # add newly created map into layer tree
                 self.addbox = wx.CheckBox(parent=self.panel,
-                                          label=_('Add created map into layer tree'), style = wx.NO_BORDER)
+                                          label=_('Add created map(s) into layer tree'), style = wx.NO_BORDER)
                 self.addbox.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
                 guisizer.Add(item=self.addbox, proportion=0,
                              flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
@@ -893,8 +773,9 @@
                          flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
                          border=5)
 
-        self.Bind(wx.EVT_CLOSE, self.OnCancel)
-
+        self.Bind(wx.EVT_CLOSE,  self.OnCancel)
+        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+        
         #constrained_size = self.notebookpanel.GetSize()
         # 80 takes the tabbar into account
         #self.notebookpanel.SetSize( (constrained_size[0] + 25, constrained_size[1]) ) 
@@ -923,29 +804,82 @@
         self.Layout()
 
         #keep initial window size limited for small screens
-        width,height = self.GetSizeTuple()
+        width, height = self.GetSizeTuple()
         if width > 640: width = 640
         if height > 480: height = 480
-        self.SetSize((width,height))
+        self.SetSize((width, height))
+        
+        # fix goutput's pane size
+        if self.goutput:
+            self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
 
-    def updateValuesHook(self):
-        """Update status bar data"""
-        self.SetStatusText(' '.join(self.notebookpanel.createCmd(ignoreErrors = True)) )
+    def updateValuesHook(self, event = None):
+        """!Update status bar data"""
+        self.SetStatusText(' '.join(self.notebookpanel.createCmd(ignoreErrors = True)))
+        if event:
+            event.Skip()
+        
+    def OnKeyUp(self, event):
+        """!Key released (check hot-keys)"""
+        try:
+            kc = chr(event.GetKeyCode())
+        except ValueError:
+            event.Skip()
+            return
+        
+        if not event.ControlDown():
+            event.Skip()
+            return
+        
+        if kc == 'Q':
+            self.OnCancel(None)
+        elif kc == 'S':
+            self.OnAbort(None)
+        elif kc == 'H':
+            self.OnHelp(None)
+        elif kc == 'R':
+            self.OnRun(None)
+        elif kc == 'C':
+            self.OnCopy(None)
+        
+        event.Skip()
 
+    def OnDone(self, cmd, returncode):
+        """!This function is launched from OnRun() when command is
+        finished
+
+        @param returncode command's return code (0 for success)
+        """
+        if self.parent and self.parent.GetName() not in ('LayerTree', 'LayerManager') or \
+                returncode != 0:
+            return
+        
+        if cmd[0] in globalvar.cmdAutoRender:
+            if self.parent.GetName() == 'LayerTree':
+                display = self.parent.GetMapDisplay()
+            else: # Layer Manager
+                display = self.parent.GetLayerTree().GetMapDisplay()
+            
+            if display and display.IsAutoRendered():
+                display.GetWindow().UpdateMap(render = True)
+        
     def OnOK(self, event):
-        """OK button pressed"""
+        """!OK button pressed"""
         cmd = self.OnApply(event)
         if cmd is not None and self.get_dcmd is not None:
             self.OnCancel(event)
 
     def OnApply(self, event):
-        """Apply the command"""
-        cmd = self.createCmd()
-
+        """!Apply the command"""
+        if self.modeler:
+            cmd = self.createCmd(ignoreErrors = True)
+        else:
+            cmd = self.createCmd()
+            
         if cmd is not None and self.get_dcmd is not None:
             # return d.* command to layer tree for rendering
             self.get_dcmd(cmd, self.layer, {"params": self.task.params, 
-                                            "flags" :self.task.flags},
+                                            "flags" : self.task.flags},
                           self)
             # echo d.* command to output console
             # self.parent.writeDCommand(cmd)
@@ -956,22 +890,21 @@
         """!Run the command"""
         cmd = self.createCmd()
         
-        if cmd == None or len(cmd) < 2:
+        if not cmd or len(cmd) < 1:
             return
-
+        
         if self.standalone or cmd[0][0:2] != "d.":
             # Send any non-display command to parent window (probably wxgui.py)
-            # put to parents
-            # switch to 'Command output'
+            # put to parents switch to 'Command output'
             if self.notebookpanel.notebook.GetSelection() != self.notebookpanel.goutputId:
                 self.notebookpanel.notebook.SetSelection(self.notebookpanel.goutputId)
             
             try:
-                self.goutput.RunCmd(cmd)
+                
+                self.goutput.RunCmd(cmd, onDone = self.OnDone)
             except AttributeError, e:
                 print >> sys.stderr, "%s: Propably not running in wxgui.py session?" % (e)
                 print >> sys.stderr, "parent window is: %s" % (str(self.parent))
-            # Send any other command to the shell.
         else:
             gcmd.Command(cmd)
         
@@ -981,15 +914,14 @@
                     self.btn_clipboard,
                     self.btn_help):
             btn.Enable(False)
-        self.btn_abort.Enable(True)
-
+        
     def OnAbort(self, event):
-        """Abort running command"""
+        """!Abort running command"""
         event = goutput.wxCmdAbort(aborted=True)
         wx.PostEvent(self.goutput, event)
 
     def OnCopy(self, event):
-        """Copy the command"""
+        """!Copy the command"""
         cmddata = wx.TextDataObject()
         # list -> string
         cmdstring = ' '.join(self.createCmd(ignoreErrors=True))
@@ -1002,7 +934,7 @@
                                     (cmdstring))
 
     def OnCancel(self, event):
-        """Cancel button pressed"""
+        """!Cancel button pressed"""
         self.MakeModal(False)
         
         if self.get_dcmd and \
@@ -1023,28 +955,31 @@
             self.Destroy()
 
     def OnHelp(self, event):
-        """Show manual page (switch to the 'Manual' notebook page)"""
+        """!Show manual page (switch to the 'Manual' notebook page)"""
         if hasattr(self.notebookpanel, "manual_tab_id"):
             self.notebookpanel.notebook.SetSelection(self.notebookpanel.manual_tab_id)
             self.notebookpanel.OnPageChange(None)
-            
-        event.Skip()
         
+        if event:    
+            event.Skip()
+        
     def createCmd(self, ignoreErrors = False):
-        """Create command string (python list)"""
+        """!Create command string (python list)"""
         return self.notebookpanel.createCmd(ignoreErrors=ignoreErrors)
 
 class cmdPanel(wx.Panel):
+    """!A panel containing a notebook dividing in tabs the different
+    guisections of the GRASS cmd.
     """
-    A panel containing a notebook dividing in tabs the different guisections of the GRASS cmd.
-    """
-    def __init__( self, parent, task, standalone, mainFrame, *args, **kwargs ):
-        wx.Panel.__init__( self, parent, *args, **kwargs )
-
-        self.parent = mainFrame
+    def __init__(self, parent, task, id = wx.ID_ANY, mainFrame = None, *args, **kwargs):
+        if mainFrame:
+            self.parent = mainFrame
+        else:
+            self.parent = parent
         self.task = task
-        fontsize = 10
         
+        wx.Panel.__init__(self, parent, id = id, *args, **kwargs)
+        
         # Determine tab layout
         sections = []
         is_section = {}
@@ -1097,7 +1032,7 @@
 
         # are we running from command line?
         ### add 'command output' tab regardless standalone dialog
-        if self.parent.get_dcmd is None:
+        if self.parent.GetName() == "MainFrame" and self.parent.get_dcmd is None:
             self.goutput = goutput.GMConsole(parent=self, margin=False,
                                              pageid=self.notebook.GetPageCount())
             self.goutputId = self.notebook.GetPageCount()
@@ -1105,12 +1040,14 @@
         else:
             self.goutput = None
             self.goutputId = -1
-            
-        self.manual_tab = helpPanel(parent = self.notebook, grass_command = self.task.name)
-        self.manual_tabsizer = wx.BoxSizer(wx.VERTICAL)
-        self.notebook.AddPage(self.manual_tab, text=_("Manual"))
-        self.manual_tab_id = self.notebook.GetPageCount() - 1
-
+        
+        self.manual_tab = HelpPanel(parent = self, grass_command = self.task.name)
+        if not self.manual_tab.IsFile():
+            self.manual_tab.Hide()
+        else:
+            self.notebook.AddPage(self.manual_tab, text=_("Manual"))
+            self.manual_tab_id = self.notebook.GetPageCount() - 1
+        
         self.notebook.SetSelection(0)
 
         panelsizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND )
@@ -1137,23 +1074,36 @@
             self.label_id.append(chk.GetId())
             if tooltip:
                 chk.SetToolTipString(tooltip)
-            if 'value' in f:
-                chk.SetValue( f['value'] )
+            chk.SetValue(f.get('value', False))
             title_sizer.Add(item=chk, proportion=1,
                             flag=wx.EXPAND)
             title_sizer.Add(item=rtitle_txt, proportion=0,
                             flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
             which_sizer.Add(item=title_sizer, proportion=0,
                             flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5)
-            f['wxId'] = chk.GetId()
+            f['wxId'] = [ chk.GetId(), ]
             chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+            
+            if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+                parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+                                     label = _("Parameterized in model"))
+                parChk.SetName('ModelParam')
+                parChk.SetValue(f.get('parameterized', False))
+                if f.has_key('wxId'):
+                    f['wxId'].append(parChk.GetId())
+                else:
+                    f['wxId'] = [ parChk.GetId() ]
+                parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+                which_sizer.Add(item = parChk, proportion = 0,
+                                flag = wx.LEFT, border = 20)
+            
             if f['name'] in ('verbose', 'quiet'):
                 chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
                 vq = UserSettings.Get(group='cmd', key='verbosity', subkey='selection')
                 if f['name'] == vq:
                     chk.SetValue(True)
                     f['value'] = True
-            elif f['name'] == 'overwrite':
+            elif f['name'] == 'overwrite' and not f.has_key('value'):
                 chk.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
                 f['value'] = UserSettings.Get(group='cmd', key='overwrite', subkey='enabled')
                 
@@ -1218,7 +1168,7 @@
                 if p.get('multiple', 'no') == 'yes' and \
                         p.get('gisprompt',False) == False and \
                         p.get('type', '') == 'string':
-                    title_txt.SetLabel(" " + title + ": ")
+                    title_txt.SetLabel(" %s: (%s, %s) " % (title, p['name'], p['type']))
                     if len(valuelist) > 6:
                         hSizer=wx.StaticBoxSizer ( box=title_txt, orient=wx.VERTICAL )
                     else:
@@ -1232,7 +1182,7 @@
                         isEnabled[ defval ] = 'yes'
                         # for multi checkboxes, this is an array of all wx IDs
                         # for each individual checkbox
-                        p[ 'wxId' ] = []
+                        p['wxId'] = list()
                     idx = 0
                     for val in valuelist:
                         try:
@@ -1240,8 +1190,9 @@
                         except IndexError:
                             label = val
                         
-                        chkbox = wx.CheckBox( parent=which_panel, label = text_beautify(label) )
-                        p[ 'wxId' ].append( chkbox.GetId() )
+                        chkbox = wx.CheckBox( parent=which_panel,
+                                              label = text_beautify(label))
+                        p['wxId'].append( chkbox.GetId() )
                         if isEnabled.has_key(val):
                             chkbox.SetValue( True )
                         hSizer.Add( item=chkbox, proportion=0,
@@ -1254,10 +1205,10 @@
                                      flag=wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=5 )
                 elif p.get('gisprompt', False) == False:
                     if len(valuelist) == 1: # -> textctrl
-                        title_txt.SetLabel("%s. %s %s" % (title, _('Valid range'),
-                                                          str(valuelist[0]) + ':'))
+                        title_txt.SetLabel("%s (%s %s):" % (title, _('valid range'),
+                                                          str(valuelist[0])))
                         
-                        if p.get('type','') == 'integer' and \
+                        if p.get('type', '') == 'integer' and \
                                 p.get('multiple','no') == 'no':
 
                             # for multiple integers use textctrl instead of spinsctrl
@@ -1281,11 +1232,16 @@
                                 txt2.SetValue(int(p['value']))
                             else:
                                 txt2.SetValue(p['value'])
+                        elif p.get('default', '') != '':
+                            if txt2.GetName() == "SpinCtrl":
+                                txt2.SetValue(int(p['default']))
+                            else:
+                                txt2.SetValue(p['default'])
                         
                         which_sizer.Add(item=txt2, proportion=0,
                                         flag=style, border=5)
 
-                        p['wxId'] = txt2.GetId()
+                        p['wxId'] = [ txt2.GetId(), ]
                         txt2.Bind(wx.EVT_TEXT, self.OnSetValue)
                     else:
                         # list of values (combo)
@@ -1297,7 +1253,7 @@
                             cb.SetValue(p['value']) # parameter previously set
                         which_sizer.Add( item=cb, proportion=0,
                                          flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border=5)
-                        p['wxId'] = cb.GetId()
+                        p['wxId'] = [ cb.GetId(), ]
                         cb.Bind( wx.EVT_COMBOBOX, self.OnSetValue)
                         cb.Bind(wx.EVT_TEXT, self.OnSetValue)
             
@@ -1309,7 +1265,7 @@
 
                 title_txt.SetLabel(title + ':' )
                 if p.get('multiple','yes') == 'yes' or \
-                        p.get('type', 'string') in ('string', 'float') or \
+                        p.get('type', 'string') == 'string' or \
                         len(p.get('key_desc', [])) > 1:
                     txt3 = wx.TextCtrl(parent=which_panel, value = p.get('default',''))
                     
@@ -1321,19 +1277,31 @@
                 else:
                     minValue = -1e9
                     maxValue = 1e9
-                    txt3 = wx.SpinCtrl(parent=which_panel, value=p.get('default',''),
-                                       size=globalvar.DIALOG_SPIN_SIZE,
-                                       min=minValue, max=maxValue)
-                    if p.get('value','') != '':
-                        txt3.SetValue(int(p['value'])) # parameter previously set
-
-                    txt3.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
-                    txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
-                    style = wx.BOTTOM | wx.LEFT | wx.RIGHT
+                    if p.get('type', '') == 'integer':
+                        txt3 = wx.SpinCtrl(parent=which_panel, value=p.get('default',''),
+                                           size=globalvar.DIALOG_SPIN_SIZE,
+                                           min=minValue, max=maxValue)
+                        style = wx.BOTTOM | wx.LEFT | wx.RIGHT
+                        
+                        if p.get('value', '') != '':
+                            txt3.SetValue(int(p['value'])) # parameter previously set
+                        elif p.get('default', '') != '':
+                            txt3.SetValue(int(p['default']))
+                        
+                        txt3.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
+                    else:
+                        txt3 = wx.TextCtrl(parent=which_panel, value = p.get('default',''),
+                                           validator = FloatValidator())
+                        style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
+                        
+                        if p.get('value', '') != '':
+                            txt3.SetValue(str(p['value'])) # parameter previously set
+                    
+                txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
                 
                 which_sizer.Add(item=txt3, proportion=0,
                                 flag=style, border=5)
-                p['wxId'] = txt3.GetId()
+                p['wxId'] = [ txt3.GetId(), ]
 
             #
             # element selection tree combobox (maps, icons, regions, etc.)
@@ -1357,7 +1325,7 @@
                     else:
                         multiple = False
                     if p.get('age', '') == 'new':
-                        mapsets = [grassenv.GetGRASSVariable('MAPSET'),]
+                        mapsets = [grass.gisenv()['MAPSET'],]
                     else:
                         mapsets = None
                     selection = gselect.Select(parent=which_panel, id=wx.ID_ANY,
@@ -1372,7 +1340,7 @@
                     
                     # A select.Select is a combobox with two children: a textctl and a popupwindow;
                     # we target the textctl here
-                    p['wxId'] = selection.GetChildren()[0].GetId()
+                    p['wxId'] = [ selection.GetChildren()[0].GetId(), ]
                     selection.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetValue)
                     if p.get('prompt', '') in ('vector', 'group'):
                         selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
@@ -1421,15 +1389,21 @@
                                 win.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
                                 
                         elif p.get('prompt', '') == 'dbdriver':
-                            win = gselect.DriverSelect(parent=which_panel,
-                                                       choices=p['values'],
-                                                       value=p['default'])
+                            value = p.get('value', '')
+                            if not value:
+                                value = p.get('default', '')
+                            win = gselect.DriverSelect(parent = which_panel,
+                                                       choices = p.get('values', []),
+                                                       value = value)
                             p['wxGetValue'] = win.GetStringSelection
                             win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
                             win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
                         elif p.get('prompt', '') == 'dbname':
-                            win = gselect.DatabaseSelect(parent=which_panel,
-                                                         value=p['default'])
+                            value = p.get('value', '')
+                            if not value:
+                                value = p.get('default', '')
+                            win = gselect.DatabaseSelect(parent = which_panel,
+                                                         value = value)
                             win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
                             win.Bind(wx.EVT_TEXT, self.OnSetValue)
                         elif p.get('prompt', '') == 'dbtable':
@@ -1443,14 +1417,17 @@
                                                   size=globalvar.DIALOG_TEXTCTRL_SIZE)
                                 win.Bind(wx.EVT_TEXT, self.OnSetValue)
                         elif p.get('prompt', '') == 'dbcolumn':
+                            value = p.get('value', '')
+                            if not value:
+                                value = p.get('default', '')
                             win = gselect.ColumnSelect(parent = which_panel,
-                                                       value = p.get('default', ''),
+                                                       value = value,
                                                        param = p)
                             ### p['wxGetValue'] = win.GetStringSelection
                             ### win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
                             win.Bind(wx.EVT_TEXT, self.OnSetValue)
                             
-                    p['wxId'] = win.GetId()
+                    p['wxId'] = [ win.GetId(), ]
                     
                     which_sizer.Add(item=win, proportion=0,
                                     flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border=5)
@@ -1482,7 +1459,6 @@
                             none_check.SetValue(True)
                         else:
                             none_check.SetValue(False)
-                        # none_check.SetFont( wx.Font( fontsize, wx.FONTFAMILY_DEFAULT, wx.NORMAL, text_style, 0, ''))
                         this_sizer.Add(item=none_check, proportion=0,
                                        flag=wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP, border=5)
                         which_sizer.Add( this_sizer )
@@ -1503,11 +1479,44 @@
                         fbb.SetValue(p['value']) # parameter previously set
                     which_sizer.Add(item=fbb, proportion=0,
                                     flag=wx.EXPAND | wx.RIGHT, border=5)
+                    
                     # A file browse button is a combobox with two children:
                     # a textctl and a button;
                     # we have to target the button here
-                    p['wxId'] = fbb.GetChildren()[1].GetId()
+                    p['wxId'] = [ fbb.GetChildren()[1].GetId() ]
 
+                    if p.get('age', 'new_file') == 'old_file':
+                        # widget for interactive input
+                        ifbb = wx.TextCtrl(parent = which_panel, id = wx.ID_ANY,
+                                           style = wx.TE_MULTILINE,
+                                           size = (-1, 75))
+                        if p.get('value', '') and os.path.isfile(p['value']):
+                            f = open(p['value'])
+                            ifbb.SetValue(''.join(f.readlines()))
+                            f.close()
+                    
+                        ifbb.Bind(wx.EVT_TEXT, self.OnFileText)
+                        which_sizer.Add(item = wx.StaticText(parent = which_panel, id = wx.ID_ANY,
+                                                             label = _('or enter values interactively')),
+                                        proportion=0,
+                                        flag=wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM, border=5)
+                        which_sizer.Add(item=ifbb, proportion=0,
+                                        flag=wx.EXPAND | wx.RIGHT | wx.LEFT, border=5)
+                        p['wxId'].append(ifbb.GetId())
+            
+            if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+                parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+                                     label = _("Parameterized in model"))
+                parChk.SetName('ModelParam')
+                parChk.SetValue(p.get('parameterized', False))
+                if p.has_key('wxId'):
+                    p['wxId'].append(parChk.GetId())
+                else:
+                    p['wxId'] = [ parChk.GetId() ]
+                parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+                which_sizer.Add(item = parChk, proportion = 0,
+                                flag = wx.LEFT, border = 20)
+                
             if title_txt is not None:
                 # create tooltip if given
                 if len(p['values_desc']) > 0:
@@ -1523,8 +1532,8 @@
                     title_txt.SetToolTipString(tooltip)
 
             if p == first_param:
-                if type(p['wxId']) == type(1):
-                    self.FindWindowById(p['wxId']).SetFocus()
+                if p.has_key('wxId') and len(p['wxId']) > 0:
+                    self.FindWindowById(p['wxId'][0]).SetFocus()
         
         #
         # set widget relations for OnUpdateSelection
@@ -1564,10 +1573,10 @@
         
         pColumnIds = []
         for p in pColumn:
-            pColumnIds.append(p['wxId'])
+            pColumnIds += p['wxId']
         pLayerIds = []
         for p in pLayer:
-            pLayerIds.append(p['wxId']) 
+            pLayerIds += p['wxId']
         
         if pMap:
             pMap['wxId-bind'] = copy.copy(pColumnIds)
@@ -1578,10 +1587,10 @@
                 p['wxId-bind'] = copy.copy(pColumnIds)
         
         if pDriver and pTable:
-            pDriver['wxId-bind'] = [pTable['wxId'], ]
+            pDriver['wxId-bind'] = pTable['wxId']
 
         if pDatabase and pTable:
-            pDatabase['wxId-bind'] = [pTable['wxId'], ]
+            pDatabase['wxId-bind'] = pTable['wxId']
 
         if pTable and pColumnIds:
             pTable['wxId-bind'] = pColumnIds
@@ -1609,17 +1618,38 @@
             tab[section].SetMinSize( (self.constrained_size[0], self.panelMinHeight) )
             # tab[section].SetMinSize( constrained_size )
 
-        if self.manual_tab.Ok:
+        if self.manual_tab.IsLoaded():
             self.manual_tab.SetMinSize( (self.constrained_size[0], self.panelMinHeight) )
-            # manual_tab.SetMinSize( constrained_size )
-
+        
         self.SetSizer( panelsizer )
         panelsizer.Fit(self.notebook)
 
         self.hasMain = tab.has_key( _('Required') ) # publish, to enclosing Frame for instance
 
         self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
-
+        
+    def OnFileText(self, event):
+        """File input interactively entered"""
+        text = event.GetString()
+        p = self.task.get_param(value = event.GetId(), element = 'wxId', raiseError = False)
+        if not p:
+            return # should not happen
+        win = self.FindWindowById(p['wxId'][0])
+        if text:
+            filename = win.GetValue()
+            if not filename:
+                # outFile = tempfile.NamedTemporaryFile(mode='w+b')
+                filename = grass.tempfile()
+                win.SetValue(filename)
+                
+            f = open(filename, "w")
+            try:
+                f.write(text)
+            finally:
+                f.close()
+        else:
+            win.SetValue('')
+        
     def OnUpdateDialog(self, event):
         for fn, kwargs in event.data.iteritems():
             fn(**kwargs)
@@ -1627,7 +1657,7 @@
         self.parent.updateValuesHook()
         
     def OnVerbosity(self, event):
-        """Verbosity level changed"""
+        """!Verbosity level changed"""
         verbose = self.FindWindowById(self.task.get_flag('verbose')['wxId'])
         quiet = self.FindWindowById(self.task.get_flag('quiet')['wxId'])
         if event.IsChecked():
@@ -1652,17 +1682,16 @@
                 sel == self.manual_tab_id:
             # calling LoadPage() is strangely time-consuming (only first call)
             # FIXME: move to helpPage.__init__()
-            if not self.manual_tab.Ok:
+            if not self.manual_tab.IsLoaded():
                 wx.Yield()
-                self.manual_tab.LoadPage(self.manual_tab.fspath + self.task.name + ".html")
-                self.manual_tab.Ok = True
+                self.manual_tab.LoadPage()
 
         self.Layout()
 
     def OnColorChange( self, event ):
         myId = event.GetId()
         for p in self.task.params:
-            if 'wxId' in p and type( p['wxId'] ) == type( [] ) and myId in p['wxId']:
+            if 'wxId' in p and myId in p['wxId']:
                 has_button = p['wxId'][1] is not None
                 if has_button and wx.FindWindowById( p['wxId'][1] ).GetValue() == True:
                     p[ 'value' ] = 'none'
@@ -1678,12 +1707,13 @@
                     p[ 'value' ] = colorchooser.GetLabel()
         self.OnUpdateValues()
 
-    def OnUpdateValues(self):
-        """
-        If we were part of a richer interface, report back the current command being built.
+    def OnUpdateValues(self, event = None):
+        """!If we were part of a richer interface, report back the
+        current command being built.
 
-        This method should be set by the parent of this panel if needed. It's a hook, actually.
-        Beware of what is 'self' in the method def, though. It will be called with no arguments.
+        This method should be set by the parent of this panel if
+        needed. It's a hook, actually.  Beware of what is 'self' in
+        the method def, though. It will be called with no arguments.
         """
         pass
 
@@ -1694,7 +1724,7 @@
         me = event.GetId()
         theParam = None
         for p in self.task.params:
-            if 'wxId' in p and type( p['wxId'] ) == type( [] ) and me in p['wxId']:
+            if 'wxId' in p and me in p['wxId']:
                 theParam = p
                 myIndex = p['wxId'].index( me )
 
@@ -1721,28 +1751,46 @@
         self.OnUpdateValues()
 
     def OnSetValue(self, event):
+        """!Retrieve the widget value and set the task value field
+        accordingly.
+        
+        Use for widgets that have a proper GetValue() method, i.e. not
+        for selectors.
         """
-        Retrieve the widget value and set the task value field accordingly.
-
-        Use for widgets that have a proper GetValue() method, i.e. not for selectors.
-        """
         myId = event.GetId()
         me = wx.FindWindowById( myId )
+        name = me.GetName()
         
         for porf in self.task.params + self.task.flags:
-            if 'wxId' in porf and type( porf[ 'wxId' ] ) == type( 1 ) and porf['wxId'] == myId:
-                if porf.has_key('wxGetValue') and porf['wxGetValue']:
-                    porf['value'] = porf['wxGetValue']()
+            if not porf.has_key('wxId'):
+                continue
+            found = False
+            for id in porf['wxId']:
+                if id == myId:
+                    found = True
+                    break
+            
+            if found:
+                if name in ('LayerSelect', 'DriverSelect', 'TableSelect'):
+                    porf['value'] = me.GetStringSelection()
+                elif name == 'GdalSelect':
+                    porf['value'] = event.dsn
+                elif name == 'ModelParam':
+                    porf['parameterized'] = me.IsChecked()
                 else:
                     porf['value'] = me.GetValue()
+                
+        self.OnUpdateValues(event)
         
-        self.OnUpdateValues()
-        
         event.Skip()
         
     def OnUpdateSelection(self, event):
         """!Update dialog (layers, tables, columns, etc.)
         """
+        if not hasattr(self.parent, "updateThread"):
+            if event:
+                event.Skip()
+            return
         if event:
             self.parent.updateThread.Update(UpdateDialog,
                                             self,
@@ -1755,17 +1803,16 @@
                                             None,
                                             None,
                                             self.task)
-        
-    def createCmd( self, ignoreErrors = False ):
-        """
-        Produce a command line string (list) or feeding into GRASS.
+            
+    def createCmd(self, ignoreErrors = False):
+        """!Produce a command line string (list) or feeding into GRASS.
 
         If ignoreErrors==True then it will return whatever has been
         built so far, even though it would not be a correct command
         for GRASS.
         """
         try:
-            cmd = self.task.getCmd( ignoreErrors=ignoreErrors )
+            cmd = self.task.getCmd(ignoreErrors=ignoreErrors)
         except ValueError, err:
             dlg = wx.MessageDialog(parent=self,
                                    message=unicode(err),
@@ -1774,7 +1821,7 @@
             dlg.ShowModal()
             dlg.Destroy()
             cmd = None
-
+        
         return cmd
     
     def OnSize(self, event):
@@ -1792,14 +1839,13 @@
             
         event.Skip()
         
-def getInterfaceDescription( cmd ):
-    """
-    Returns the XML description for the GRASS cmd.
+def getInterfaceDescription(cmd):
+    """!Returns the XML description for the GRASS cmd.
 
     The DTD must be located in $GISBASE/etc/grass-interface.dtd,
     otherwise the parser will not succeed.
 
-    Note: 'cmd' is given as string
+    @param cmd command (name of GRASS module)
     """
     try:
         cmdout = grass.Popen([cmd, '--interface-description'], stdout = grass.PIPE).communicate()[0]
@@ -1809,15 +1855,14 @@
     return cmdout.replace('grass-interface.dtd', os.path.join(globalvar.ETCDIR, 'grass-interface.dtd'))
 
 class GrassGUIApp(wx.App):
+    """!Stand-alone GRASS command GUI
     """
-    Stand-alone GRASS command GUI
-    """
     def __init__(self, grass_task):
         self.grass_task = grass_task
         wx.App.__init__(self, False)
-
+        
     def OnInit(self):
-        self.mf = mainFrame(parent=None, ID=wx.ID_ANY, task_description=self.grass_task)
+        self.mf = mainFrame(parent = None, ID = wx.ID_ANY, task_description = self.grass_task)
         self.mf.CentreOnScreen()
         self.mf.Show(True)
         self.SetTopWindow(self.mf)
@@ -1832,17 +1877,34 @@
     def __init__(self, parent=-1):
         self.parent = parent
         self.grass_task = None
+        self.cmd = list()
 
-    def ParseCommand(self, cmd, gmpath=None, completed=None, parentframe=None,
-                     show=True, modal=False):
+    def GetCmd(self):
+        """Get validated command"""
+        return self.cmd
+    
+    def ParseInterface(self, cmd, parser = processTask):
+        """!Parse interface
+
+        @param cmd command to be parsed (given as list)
         """
-        Parse command
-
+        # enc = locale.getdefaultlocale()[1]
+        # if enc and enc.lower() not in ("utf8", "utf-8"):
+        #     tree = etree.fromstring(getInterfaceDescription(cmd[0]).decode(enc).encode("utf-8"))
+        # else:
+        tree = etree.fromstring(getInterfaceDescription(cmd[0]))
+        
+        return processTask(tree).GetTask()
+    
+    def ParseCommand(self, cmd, gmpath = None, completed = None, parentframe = None,
+                     show = True, modal = False, centreOnParent = True, checkError = False):
+        """!Parse command
+        
         Note: cmd is given as list
-
+        
         If command is given with options, return validated cmd list:
-        * add key name for first parameter if not given
-        * change mapname to mapname at mapset
+         - add key name for first parameter if not given
+         - change mapname to mapname at mapset
         """
         start = time.time()
         dcmd_params = {}
@@ -1857,25 +1919,18 @@
                 dcmd_params.update(completed[2])
 
         self.parent = parentframe
-
+        
         # parse the interface decription
-        self.grass_task = grassTask()
-        handler = processTask(self.grass_task)
-        enc = locale.getdefaultlocale()[1]
-        if enc and enc.lower() not in ("utf8", "utf-8"):
-            xml.sax.parseString(getInterfaceDescription(cmd[0]).decode(enc).split('\n',1)[1].replace('', '<?xml version="1.0" encoding="utf-8"?>\n', 1).encode("utf-8"),
-                            handler)
-        else:
-            xml.sax.parseString(getInterfaceDescription(cmd[0]),
-                            handler)
-  
+        self.grass_task = self.ParseInterface(cmd)
+        
         # if layer parameters previously set, re-insert them into dialog
         if completed is not None:
             if 'params' in dcmd_params:
                 self.grass_task.params = dcmd_params['params']
             if 'flags' in dcmd_params:
                 self.grass_task.flags = dcmd_params['flags']
-
+        
+        err = list()
         # update parameters if needed && validate command
         if len(cmd) > 1:
             i = 0
@@ -1892,45 +1947,65 @@
                         key, value = option.split('=', 1)
                     except:
                         if i == 0: # add key name of first parameter if not given
-                            key = self.grass_task.firstParam
+                            key = self.grass_task.get_options()['params'][0]['name']
                             value = option
                         else:
                             raise ValueError, _("Unable to parse command %s") % ' '.join(cmd)
-
-                    if self.grass_task.get_param(key)['element'] in ['cell', 'vector']:
+                    
+                    element = self.grass_task.get_param(key, raiseError = False)
+                    if not element:
+                        err.append(_("%(cmd)s: parameter '%(key)s' not available") % \
+                                       { 'cmd' : cmd[0],
+                                         'key' : key })
+                        continue
+                    element = element['element']
+                    
+                    if element in ['cell', 'vector']:
                         # mapname -> mapname at mapset
                         if '@' not in value:
-                            value = value + '@' + grassenv.GetGRASSVariable('MAPSET')
+                            mapset = grass.find_file(value, element)['mapset']
+                            curr_mapset = grass.gisenv()['MAPSET']
+                            if mapset and mapset != curr_mapset:
+                                value = value + '@' + mapset
+                    
                     self.grass_task.set_param(key, value)
                     cmd_validated.append(key + '=' + value)
                     i = i + 1
-
+            
             # update original command list
             cmd = cmd_validated
-
-        self.mf = mainFrame(parent=self.parent, ID=wx.ID_ANY,
-                            task_description=self.grass_task,
-                            get_dcmd=get_dcmd, layer=layer)
         
+        if show is not None:
+            self.mf = mainFrame(parent=self.parent, ID=wx.ID_ANY,
+                                task_description=self.grass_task,
+                                get_dcmd=get_dcmd, layer=layer)
+        else:
+            self.mf = None
+        
         if get_dcmd is not None:
             # update only propwin reference
             get_dcmd(dcmd=None, layer=layer, params=None,
                      propwin=self.mf)
-
-        self.mf.notebookpanel.OnUpdateSelection(None)
         
-        if show:
-            if self.parent:
-                self.mf.CentreOnParent()
-            self.mf.Show(show)
-            self.mf.MakeModal(modal)
+        if show is not None:
+            self.mf.notebookpanel.OnUpdateSelection(None)
+            if show is True:
+                if self.parent:
+                    self.mf.CentreOnParent()
+                self.mf.Show(show)
+                self.mf.MakeModal(modal)
+            else:
+                self.mf.OnApply(None)
+        
+        self.cmd = cmd
+        
+        if checkError:
+            return self.grass_task, err
         else:
-            self.mf.OnApply(None)
-        
-        return cmd
-
+            return self.grass_task
+    
     def GetCommandInputMapParamKey(self, cmd):
-        """Get parameter key for input raster/vector map
+        """!Get parameter key for input raster/vector map
         
         @param cmd module name
         
@@ -1939,10 +2014,9 @@
         """
         # parse the interface decription
         if not self.grass_task:
-            self.grass_task = grassTask()
-            handler = processTask(self.grass_task)
-            xml.sax.parseString(getInterfaceDescription(cmd), handler)
-
+            tree = etree.fromstring(getInterfaceDescription(cmd))
+            self.grass_task = processTask(tree).GetTask()
+            
             for p in self.grass_task.params:
                 if p.get('name', '') in ('input', 'map'):
                     age = p.get('age', '')
@@ -1954,41 +2028,61 @@
                         return p.get('name', None)
         return None
 
-class StaticWrapText(wx.StaticText):
-    """
-    A Static Text field that wraps its text to fit its width, enlarging its height if necessary.
-    """
-    def __init__(self, parent, id=wx.ID_ANY, label=u'', *args, **kwds):
-        self.originalLabel = label
-        wx.StaticText.__init__(self, parent, id, u'', *args, **kwds)
-        self.SetLabel(label)
-        self.Bind(wx.EVT_SIZE, self.onResize)
-    
-    def SetLabel(self, label):
-        self.originalLabel = label
-        self.wrappedSize = None
-        #self.onResize(None)
+class FloatValidator(wx.PyValidator):
+    """!Validator for floating-point input"""
+    def __init__(self):
+        wx.PyValidator.__init__(self)
         
-    def onResize(self, event):
-        if not getattr(self, "resizing", False):
-            self.resizing = True
-            newSize = self.GetSize()
-            if self.wrappedSize != newSize:
-                wx.StaticText.SetLabel(self, self.originalLabel)
-                self.Wrap(newSize.width)
-                self.wrappedSize = self.GetMinSize()
+        self.Bind(wx.EVT_TEXT, self.OnText) 
+        
+    def Clone(self):
+        """!Clone validator"""
+        return FloatValidator()
 
-                self.SetSize(self.wrappedSize)
-            del self.resizing
+    def Validate(self):
+        """Validate input"""
+        textCtrl = self.GetWindow()
+        text = textCtrl.GetValue()
 
+        if text:
+            try:
+                float(text)
+            except ValueError:
+                textCtrl.SetBackgroundColour("grey")
+                textCtrl.SetFocus()
+                textCtrl.Refresh()
+                return False
+        
+        sysColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
+        textCtrl.SetBackgroundColour(sysColor)
+        
+        textCtrl.Refresh()
+        
+        return True
+
+    def OnText(self, event):
+        """!Do validation"""
+        self.Validate()
+        
+        event.Skip()
+        
+    def TransferToWindow(self):
+        return True # Prevent wxDialog from complaining.
+    
+    def TransferFromWindow(self):
+        return True # Prevent wxDialog from complaining.
+     
 if __name__ == "__main__":
 
     if len(sys.argv) == 1:
-        print _("usage: %s <grass command>") % sys.argv[0]
-        sys.exit()
+        sys.exit(_("usage: %s <grass command>") % sys.argv[0])
     if sys.argv[1] != 'test':
-        q=wx.LogNull()
-        GrassGUIApp( grassTask( sys.argv[1] ) ).MainLoop()
+        q = wx.LogNull()
+        cmd = shlex.split(sys.argv[1])
+        task = grassTask(cmd[0])
+        task.set_options(cmd[1:])
+        app = GrassGUIApp(task)
+        app.MainLoop()
     else: #Test
         # Test grassTask from within a GRASS session
         if os.getenv("GISBASE") is not None:

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,22 +1,24 @@
-"""
+"""!
 @package nviz.py
 
- at brief Nviz extension for wxGUI
+ at brief Nviz (3D view) module
 
-This module enables to visualize data in 2.5/3D space.
+This module implements 3D visualization mode for map display.
 
-Map Display supports standard 2D mode ('mapdisp' module) and 2.5/3D
-mode ('nviz_mapdisp' module).
+Map Display supports standard 2D view mode ('mapdisp' module) and
+2.5/3D mode ('nviz_mapdisp' module).
 
-(C) 2008 by the GRASS Development Team
+(C) 2008, 2010 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> (Google SoC 2008)
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
 """
 
+errorMsg = ''
+
 import os
 import sys
 
@@ -26,10 +28,8 @@
     from wx import glcanvas
     import nviz_mapdisp
     import nviz_tools
-    sys.path.append(os.path.join(globalvar.ETCWXDIR, "nviz"))
-    import grass6_wxnviz as wxnviz
+    import wxnviz
     haveNviz = True
-    errorMsg = ''
 except ImportError, err:
     haveNviz = False
     errorMsg = err

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_mapdisp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_mapdisp.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_mapdisp.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -3,12 +3,12 @@
 
 @brief Nviz extension for wxGUI
 
-This module adds to Map Display 2.5/3D visualization mode.
+This module implements 3D visualization mode of map display.
 
 List of classes:
  - GLWindow
 
-(C) 2008 by the GRASS Development Team
+(C) 2008-2009 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
@@ -21,6 +21,7 @@
 import sys
 import time
 import copy
+import math
 
 from threading import Thread
 
@@ -31,17 +32,17 @@
 
 import gcmd
 import globalvar
-from debug import Debug as Debug
-from preferences import globalSettings as UserSettings
-from mapdisp import MapWindow as MapWindow
-from goutput import wxCmdOutput as wxCmdOutput
-from workspace import Nviz as NvizDefault
+from debug          import Debug
+from mapdisp_window import MapWindow
+from goutput        import wxCmdOutput
+from preferences    import globalSettings as UserSettings
+from workspace      import Nviz as NvizDefault
 
-sys.path.append(os.path.join(globalvar.ETCWXDIR, "nviz"))
-import grass6_wxnviz as wxnviz
+import wxnviz
 
-wxUpdateProperties, EVT_UPDATE_PROP = NewEvent()
-wxUpdateView,       EVT_UPDATE_VIEW = NewEvent()
+wxUpdateProperties, EVT_UPDATE_PROP  = NewEvent()
+wxUpdateView,       EVT_UPDATE_VIEW  = NewEvent()
+wxUpdateLight,      EVT_UPDATE_LIGHT = NewEvent()
 
 class NvizThread(Thread):
     def __init__(self, log, progressbar, window):
@@ -51,32 +52,27 @@
         self.progressbar = progressbar
         self.window = window
         
-        self.nvizClass = None
+        self._display = None
         
         self.setDaemon(True)
-        
+
     def run(self):
-        self.nvizClass = wxnviz.Nviz(self.log)
+        self._display = wxnviz.Nviz(self.log, self.progressbar)
         
+    def GetDisplay(self):
+        """!Get display instance"""
+        return self._display
+    
 class GLWindow(MapWindow, glcanvas.GLCanvas):
-    """OpenGL canvas for Map Display Window"""
-    def __init__(self, parent, id,
-                 pos=wx.DefaultPosition,
-                 size=wx.DefaultSize,
-                 style=wx.NO_FULL_REPAINT_ON_RESIZE,
-                 Map=None, tree=None, gismgr=None):
-
+    """!OpenGL canvas for Map Display Window"""
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None):
         self.parent = parent # MapFrame
-        self.Map = Map
-        self.tree = tree
-        self.gismgr = gismgr
         
         glcanvas.GLCanvas.__init__(self, parent, id)
-        MapWindow.__init__(self, parent, id, pos, size, style,
-                           Map, tree, gismgr)
-
-
-        self.parent = parent # MapFrame
+        MapWindow.__init__(self, parent, id, 
+                           Map, tree, lmgr)
+        self.Hide()
         
         self.init = False
         self.initView = False
@@ -86,250 +82,314 @@
                         # do not render vector lines in quick mode
                         'vlines' : False,
                         'vpoints' : False }
-
+        
         # list of loaded map layers (layer tree items)
-        self.layers = []
+        self.layers  = list()
+        # list of query points
+        self.qpoints = list()
         
         #
         # use display region instead of computational
         #
         os.environ['GRASS_REGION'] = self.Map.SetRegion()
-
+        
         #
         # create nviz instance
         #
-        self.nvizThread = NvizThread(self.gismgr.goutput.cmd_stderr,
-                                     self.parent.onRenderGauge,
-                                     self.gismgr.goutput.cmd_output)
+        if self.lmgr:
+            self.log = self.lmgr.goutput
+            logerr = self.lmgr.goutput.cmd_stderr
+            logmsg = self.lmgr.goutput.cmd_output
+        else:
+            self.log = logmsg = sys.stdout
+            logerr = sys.stderr
+        
+        self.nvizThread = NvizThread(logerr,
+                                     self.parent.statusbarWin['progress'],
+                                     logmsg)
         self.nvizThread.start()
         time.sleep(.1)
-        self.nvizClass =  self.nvizThread.nvizClass
-
+        self._display = self.nvizThread.GetDisplay()
+        
         # GRASS_REGION needed only for initialization
         del os.environ['GRASS_REGION']
-
-        #
-        # set current display
-        #
-        self.nvizClass.SetDisplay(self)
         
+        self.img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
+        
         #
         # default values
         #
-        self.view = copy.deepcopy(UserSettings.Get(group='nviz', key='view')) # copy
-        self.iview = UserSettings.Get(group='nviz', key='view', internal=True)
+        self.view = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'view')) # copy
+        self.iview = UserSettings.Get(group = 'nviz', key = 'view', internal = True)
+        
         self.nvizDefault = NvizDefault()
+        self.light = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'light')) # copy
         
-        self.size = None
         self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
-        self.Bind(wx.EVT_SIZE, self.OnSize)
-        self.Bind(wx.EVT_PAINT, self.OnPaint)
-        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
-        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
-        self.Bind(wx.EVT_MOTION, self.OnMouseAction)
-        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseAction)
-
-        self.Bind(EVT_UPDATE_PROP, self.UpdateMapObjProperties)
-        self.Bind(EVT_UPDATE_VIEW, self.UpdateView)
+        self.Bind(wx.EVT_SIZE,             self.OnSize)
+        self.Bind(wx.EVT_PAINT,            self.OnPaint)
+        self.Bind(wx.EVT_LEFT_UP,          self.OnLeftUp)
+        self.Bind(wx.EVT_MOUSE_EVENTS,     self.OnMouseAction)
+        self.Bind(wx.EVT_MOTION,           self.OnMotion)
         
+        self.Bind(EVT_UPDATE_PROP,  self.UpdateMapObjProperties)
+        self.Bind(EVT_UPDATE_VIEW,  self.UpdateView)
+        self.Bind(EVT_UPDATE_LIGHT, self.UpdateLight)
+        
         self.Bind(wx.EVT_CLOSE, self.OnClose)
         
     def OnClose(self, event):
         # cleanup when window actually closes (on quit) and not just is hidden
         self.Reset()
-
+        
     def OnEraseBackground(self, event):
         pass # do nothing, to avoid flashing on MSW
-
+    
     def OnSize(self, event):
-        self.size = self.parent.GetClientSize()
+        size = self.GetClientSize()
         if self.GetContext():
-            Debug.msg(3, "GLCanvas.OnSize(): w=%d, h=%d" % \
-                      (self.size.width, self.size.height))
+            Debug.msg(3, "GLCanvas.OnSize(): w = %d, h = %d" % \
+                      (size.width, size.height))
             self.SetCurrent()
-            self.nvizClass.ResizeWindow(self.size.width,
-                                        self.size.height)
+            self._display.ResizeWindow(size.width,
+                                       size.height)
         
         event.Skip()
-
+        
     def OnPaint(self, event):
-        Debug.msg(3, "GLCanvas.OnPaint()")
-
+        Debug.msg(1, "GLCanvas.OnPaint()")
+        
         dc = wx.PaintDC(self)
         self.SetCurrent()
         
         if not self.initView:
-            self.nvizClass.InitView()
+            self._display.InitView()
             self.initView = True
-
+        
         self.LoadDataLayers()
         self.UnloadDataLayers()
         
         if not self.init:
             self.ResetView()
             
-            if hasattr(self.parent, "nvizToolWin"):
-                self.parent.nvizToolWin.UpdatePage('view')
+            if hasattr(self.lmgr, "nviz"):
+                self.lmgr.nviz.UpdatePage('view')
+                self.lmgr.nviz.UpdatePage('light')
                 layer = self.GetSelectedLayer()
                 if layer:
-                    if layer.type == 'raster':
-                        self.parent.nvizToolWin.UpdatePage('surface')
-                    elif layer.type == 'vector':
-                        self.parent.nvizToolWin.UpdatePage('vector')
-
-                self.parent.nvizToolWin.UpdateSettings()
-
+                    if layer.type ==  'raster':
+                        self.lmgr.nviz.UpdatePage('surface')
+                        self.lmgr.nviz.UpdatePage('fringe')
+                    elif layer.type ==  'vector':
+                        self.lmgr.nviz.UpdatePage('vector')
+                
+                ### self.lmgr.nviz.UpdateSettings()
+                
                 # update widgets
-                win = self.parent.nvizToolWin.FindWindowById( \
-                    self.parent.nvizToolWin.win['vector']['lines']['surface'])
+                win = self.lmgr.nviz.FindWindowById( \
+                    self.lmgr.nviz.win['vector']['lines']['surface'])
                 win.SetItems(self.GetLayerNames('raster'))
-
+            
             self.init = True
-                        
+        
         self.UpdateMap()
-
+                
     def OnMouseAction(self, event):
-        # change position
-        if event.Dragging() and event.LeftIsDown():
-            ### self.lastX = self.lastY = self.x = self.y
-            ### self.x, self.y = event.GetPosition()
-            ### self.Refresh(False)
-            pass
-
         # change perspective with mouse wheel
         wheel = event.GetWheelRotation()
-
-        if wheel != 0:
+        
+        if wheel !=  0:
             current  = event.GetPositionTuple()[:]
-            Debug.msg (5, "GLWindow.OnMouseMotion(): wheel=%d" % wheel)
+            Debug.msg (5, "GLWindow.OnMouseMotion(): wheel = %d" % wheel)
             prev_value = self.view['persp']['value']
             if wheel > 0:
                 value = -1 * self.view['persp']['step']
             else:
                 value = self.view['persp']['step']
-            self.view['persp']['value'] += value
+            self.view['persp']['value'] +=  value
             if self.view['persp']['value'] < 1:
                 self.view['persp']['value'] = 1
             elif self.view['persp']['value'] > 100:
                 self.view['persp']['value'] = 100
-
-            if prev_value != self.view['persp']['value']:
-                if hasattr(self.parent, "nvizToolWin"):
-                    self.parent.nvizToolWin.UpdateSettings()
-
-                    self.nvizClass.SetView(self.view['pos']['x'], self.view['pos']['y'],
-                                           self.iview['height']['value'],
-                                           self.view['persp']['value'],
-                                           self.view['twist']['value'])
-
+            
+            if prev_value !=  self.view['persp']['value']:
+                if hasattr(self.lmgr, "nviz"):
+                    self.lmgr.nviz.UpdateSettings()
+                    
+                    self._display.SetView(self.view['position']['x'], self.view['position']['y'],
+                                          self.iview['height']['value'],
+                                          self.view['persp']['value'],
+                                          self.view['twist']['value'])
+                
                 # redraw map
                 self.OnPaint(None)
-
+                
                 # update statusbar
                 ### self.parent.StatusbarUpdate()
+        
+        event.Skip()
 
-    def OnLeftDown(self, event):
-        self.CaptureMouse()
-        ### self.x, self.y = self.lastX, self.lastY = event.GetPosition()
+    def Pixel2Cell(self, (x, y)):
+        """!Convert image coordinates to real word coordinates
+
+        @param x, y image coordinates
         
+        @return easting, northing
+        @return None on error
+        """
+        size = self.GetClientSize()
+        # UL -> LL
+        sid, x, y, z = self._display.GetPointOnSurface(x, y)
+        
+        if not sid:
+            return None
+        
+        return (x, y)
+    
     def OnLeftUp(self, event):
         self.ReleaseMouse()
-
+        if self.mouse["use"] == "nvizQuerySurface":
+            self.OnQuerySurface(event)
+        elif self.mouse["use"] == "nvizQueryVector":
+            self.OnQueryVector(event)
+    
+    def OnQuerySurface(self, event):
+        """!Query surface on given position"""
+        result = self._display.QueryMap(event.GetX(), event.GetY())
+        if result:
+            self.qpoints.append((result['x'], result['y'], result['z']))
+            self.log.WriteLog("%-30s: %.3f" % (_("Easting"),   result['x']))
+            self.log.WriteLog("%-30s: %.3f" % (_("Northing"),  result['y']))
+            self.log.WriteLog("%-30s: %.3f" % (_("Elevation"), result['z']))
+            self.log.WriteLog("%-30s: %s" % (_("Surface map elevation"), result['elevation']))
+            self.log.WriteLog("%-30s: %s" % (_("Surface map color"), result['color']))
+            if len(self.qpoints) > 1:
+                prev = self.qpoints[-2]
+                curr = self.qpoints[-1]
+                dxy = math.sqrt(pow(prev[0]-curr[0], 2) +
+                                pow(prev[1]-curr[1], 2))
+                dxyz = math.sqrt(pow(prev[0]-curr[0], 2) +
+                                 pow(prev[1]-curr[1], 2) +
+                                 pow(prev[2]-curr[2], 2))
+                self.log.WriteLog("%-30s: %.3f" % (_("XY distance from previous"), dxy))
+                self.log.WriteLog("%-30s: %.3f" % (_("XYZ distance from previous"), dxyz))
+                self.log.WriteLog("%-30s: %.3f" % (_("Distance along surface"),
+                                              self._display.GetDistanceAlongSurface(result['id'],
+                                                                                    (curr[0], curr[1]),
+                                                                                    (prev[0], prev[1]),
+                                                                                    useExag = False)))
+                self.log.WriteLog("%-30s: %.3f" % (_("Distance along exag. surface"),
+                                              self._display.GetDistanceAlongSurface(result['id'],
+                                                                                    (curr[0], curr[1]),
+                                                                                    (prev[0], prev[1]),
+                                                                                      useExag = True)))
+            self.log.WriteCmdLog('-' * 80)
+        else:
+            self.log.WriteLog(_("No point on surface"))
+            self.log.WriteCmdLog('-' * 80)
+    
+    def OnQueryVector(self, event):
+        """!Query vector on given position"""
+        self.log.WriteWarning(_("Function not implemented yet"))
+        self.log.WriteCmdLog('-' * 80)
+        
     def UpdateView(self, event):
-        """Change view settings"""
-        self.nvizClass.SetView(self.view['pos']['x'], self.view['pos']['y'],
-                               self.iview['height']['value'],
-                               self.view['persp']['value'],
-                               self.view['twist']['value'])
+        """!Change view settings"""
+        data = self.view
+        self._display.SetView(data['position']['x'], data['position']['y'],
+                              self.iview['height']['value'],
+                              data['persp']['value'],
+                              data['twist']['value'])
+        
+        if event and event.zExag and data['z-exag'].has_key('value'):
+            self._display.SetZExag(data['z-exag']['value'])
+        
+        if event:
+            event.Skip()
 
-        if event and event.zExag:
-            self.nvizClass.SetZExag(self.view['z-exag']['value'])
+    def UpdateLight(self, event):
+        """!Change light settings"""
+        data = self.light
+        self._display.SetLight(x = data['position']['x'], y = data['position']['y'],
+                               z = data['position']['z'], color = data['color'],
+                               bright = data['bright'] / 100.,
+                               ambient = data['ambient'] / 100.)
+        self._display.DrawLightingModel()
         
-        if event: event.Skip()
+    def UpdateMap(self, render = True):
+        """!Updates the canvas anytime there is a change to the
+        underlaying images or to the geometry of the canvas.
         
-    def UpdateMap(self, render=True):
-        """
-        Updates the canvas anytime there is a change to the
-        underlaying images or to the geometry of the canvas.
-
         @param render re-render map composition
         """
         start = time.clock()
-
+        
         self.resize = False
         
         if self.render['quick'] is False:
-            self.parent.onRenderGauge.Show()
-            self.parent.onRenderGauge.SetRange(2)
-            self.parent.onRenderGauge.SetValue(0)
-            
+            self.parent.statusbarWin['progress'].Show()
+            self.parent.statusbarWin['progress'].SetRange(2)
+            self.parent.statusbarWin['progress'].SetValue(0)
+        
         if self.render['quick'] is False:
-            self.parent.onRenderGauge.SetValue(1)
-            self.nvizClass.Draw(False, -1)
+            self.parent.statusbarWin['progress'].SetValue(1)
+            self._display.Draw(False, -1)
         elif self.render['quick'] is True:
             # quick
             mode = wxnviz.DRAW_QUICK_SURFACE | wxnviz.DRAW_QUICK_VOLUME
             if self.render['vlines']:
-                mode |= wxnviz.DRAW_QUICK_VLINES
+                mode |=  wxnviz.DRAW_QUICK_VLINES
             if self.render['vpoints']:
-                mode |= wxnviz.DRAW_QUICK_VPOINTS
-            self.nvizClass.Draw(True, mode)
+                mode |=  wxnviz.DRAW_QUICK_VPOINTS
+            self._display.Draw(True, mode)
         else: # None -> reuse last rendered image
             pass # TODO
-
+        
         self.SwapBuffers()
-
+        
         stop = time.clock()
-
+        
         if self.render['quick'] is False:
-            self.parent.onRenderGauge.SetValue(2)
+            self.parent.statusbarWin['progress'].SetValue(2)
             # hide process bar
-            self.parent.onRenderGauge.Hide()
-
-        #
-        # update statusbar
-        #
-        # self.parent.StatusbarUpdate()
-
-        Debug.msg(3, "GLWindow.UpdateMap(): quick=%d, -> time=%g" % \
+            self.parent.statusbarWin['progress'].Hide()
+        
+        Debug.msg(3, "GLWindow.UpdateMap(): quick = %d, -> time = %g" % \
                       (self.render['quick'], (stop-start)))
-
-        # print stop-start
-
+        
     def EraseMap(self):
+        """!Erase the canvas
         """
-        Erase the canvas
-        """
-        self.nvizClass.EraseMap()
+        self._display.EraseMap()
         self.SwapBuffers()
-
+        
     def IsLoaded(self, item):
-        """Check if layer (item) is already loaded
-
+        """!Check if layer (item) is already loaded
+        
         @param item layer item
         """
         layer = self.tree.GetPyData(item)[0]['maplayer']
         data = self.tree.GetPyData(item)[0]['nviz']
-
+        
         if not data:
             return 0
-
-        if layer.type == 'raster':
+        
+        if layer.type ==  'raster':
             if not data['surface'].has_key('object'):
                 return 0
-        elif layer.type == 'vector':
+        elif layer.type ==  'vector':
             if not data['vlines'].has_key('object') and \
                     not data['points'].has_key('object'):
                 return 0
-
+        
         return 1
 
     def _GetDataLayers(self, item, litems):
-        """Return get list of enabled map layers"""
+        """!Return get list of enabled map layers"""
         # load raster & vector maps
         while item and item.IsOk():
             type = self.tree.GetPyData(item)[0]['type']
-            if type == 'group':
+            if type ==  'group':
                 subItem = self.tree.GetFirstChild(item)[0]
                 self._GetDataLayers(subItem, litems)
                 item = self.tree.GetNextSibling(item)
@@ -338,55 +398,59 @@
                     type not in ('raster', 'vector', '3d-raster'):
                 item = self.tree.GetNextSibling(item)
                 continue
-
+            
             litems.append(item)
-
+            
             item = self.tree.GetNextSibling(item)
         
     def LoadDataLayers(self):
-        """Load raster/vector from current layer tree
-
+        """!Load raster/vector from current layer tree
+        
         @todo volumes
         """
+        if not self.tree:
+            return
+        
         listOfItems = []
         item = self.tree.GetFirstChild(self.tree.root)[0]
         self._GetDataLayers(item, listOfItems)
         
         start = time.time()
-
+        
         while(len(listOfItems) > 0):
             item = listOfItems.pop()
             type = self.tree.GetPyData(item)[0]['type']
-            if item not in self.layers:
-                try:
-                    if type == 'raster':
-                        self.LoadRaster(item)
-                    elif type == '3d-raster':
-                        self.LoadRaster3d(item)
-                except gcmd.NvizError, e:
-                    print >> sys.stderr, "Nviz:" + e.message
-
-                try:
-                    if type == 'vector':
-                        data = self.tree.GetPyData(item)[0]['nviz']
-                        vecType = []
-                        if data and data.has_key('vector'):
-                            for v in ('lines', 'points'):
-                                if data['vector'][v]:
-                                    vecType.append(v)
-                        self.LoadVector(item, vecType)
-                except gcmd.NvizError, e:
-                    print >> sys.stderr, "Nviz:" + e.message
-                self.init = False
-            
+            if item in self.layers:
+                continue
+            try:
+                if type ==  'raster':
+                    self.LoadRaster(item)
+                elif type ==  '3d-raster':
+                    self.LoadRaster3d(item)
+                elif type ==  'vector':
+                    # data = self.tree.GetPyData(item)[0]['nviz']
+                    # vecType = []
+                    # if data and data.has_key('vector'):
+                    #     for v in ('lines', 'points'):
+                    #         if data['vector'][v]:
+                    #             vecType.append(v)
+                    layer = self.tree.GetPyData(item)[0]['maplayer']
+                    npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(layer)
+                    if npoints > 0:
+                        self.LoadVector(item, points = True)
+                    if nlines > 0:
+                        self.LoadVector(item, points = False)
+            except gcmd.GException, e:
+                GError(parent = self,
+                       message = e)
+            self.init = False
+        
         stop = time.time()
         
-        Debug.msg(3, "GLWindow.LoadDataLayers(): time=%f" % (stop-start))
-
-        # print stop - start
-        
+        Debug.msg(3, "GLWindow.LoadDataLayers(): time = %f" % (stop-start))
+                
     def UnloadDataLayers(self):
-        """Unload any layers that have been deleted from layer tree"""
+        """!Unload any layers that have been deleted from layer tree"""
         if not self.tree:
             return
         
@@ -400,35 +464,55 @@
             if layer not in listOfItems:
                 ltype = self.tree.GetPyData(layer)[0]['type']
                 try:
-                    if ltype == 'raster':
+                    if ltype ==  'raster':
                         self.UnloadRaster(layer)
-                    elif ltype == '3d-raster':
+                    elif ltype ==  '3d-raster':
                         self.UnloadRaster3d(layer) 
-                    elif ltype == 'vector':
+                    elif ltype ==  'vector':
                         data = self.tree.GetPyData(layer)[0]['nviz']
                         vecType = []
                         if data and data.has_key('vector'):
                             for v in ('lines', 'points'):
                                 if data['vector'][v]:
                                     vecType.append(v)
-                        self.UnloadVector(layer, vecType)
+                        self.UnloadVector(layer, True)
+                        self.UnloadVector(layer, False)
                     
                     self.UpdateView(None)
-                except gcmd.NvizError, e:
-                    print >> sys.stderr, "Nviz:" + e.message
-
-                self.parent.nvizToolWin.UpdateSettings()        
-                            
+                except gcmd.GException, e:
+                    gcmd.GError(parent = self,
+                                message = e)
+                
+                self.lmgr.nviz.UpdateSettings()        
+        
         stop = time.time()
         
-        Debug.msg(3, "GLWindow.UnloadDataLayers(): time=%f" % (stop-start))        
+        Debug.msg(3, "GLWindow.UnloadDataLayers(): time = %f" % (stop-start))        
 
+    def SetVectorFromCmd(self, item, data):
+        """!Set 3D view properties from cmd (d.vect)
+
+        @param item Layer Tree item
+        @param nviz data
+        """
+        cmd = self.tree.GetPyData(item)[0]['cmd']
+        if cmd[0] != 'd.vect':
+            return
+        for opt in cmd[1:]:
+            try:
+                key, value = opt.split('=')
+            except ValueError:
+                continue
+            if key == 'color':
+                data['lines']['color']['value'] = value
+                data['points']['color']['value'] = value
+
     def SetMapObjProperties(self, item, id, nvizType):
-        """Set map object properties
-
+        """!Set map object properties
+        
         Properties must be afterwards updated by
         UpdateMapObjProperties().
-
+        
         @param item layer item
         @param id nviz layer id (or -1)
         @param nvizType nviz data type (surface, points, vector)
@@ -437,28 +521,29 @@
         # reference to original layer properties (can be None)
         data = self.tree.GetPyData(item)[0]['nviz']
         
-        if data is None:
+        if not data:
             # init data structure
             self.tree.GetPyData(item)[0]['nviz'] = {}
             data = self.tree.GetPyData(item)[0]['nviz']
-
-            if type == 'raster':
+            
+            if type ==  'raster':
                 # reset to default properties
                 data[nvizType] = self.nvizDefault.SetSurfaceDefaultProp()
                         
-            elif type == 'vector':
+            elif type ==  'vector':
                 # reset to default properties (lines/points)
                 data['vector'] = self.nvizDefault.SetVectorDefaultProp()
-
-            elif type == '3d-raster':
+                self.SetVectorFromCmd(item, data['vector'])
+                
+            elif type ==  '3d-raster':
                 # reset to default properties 
                 data[nvizType] = self.nvizDefault.SetVolumeDefaultProp()
         
         else:
             # complete data (use default values)
-            if type == 'raster':
+            if type ==  'raster':
                 data['surface'] = self.nvizDefault.SetSurfaceDefaultProp()
-            if type == 'vector':
+            if type ==  'vector':
                 if not data['vector']['lines']:
                     self.nvizDefault.SetVectorLinesDefaultProp(data['vector']['lines'])
                 if not data['vector']['points']:
@@ -468,53 +553,53 @@
             for sec in data.keys():
                 for sec1 in data[sec].keys():
                     for sec2 in data[sec][sec1].keys():
-                        if sec2 != 'all':
+                        if sec2 !=  'all':
                             data[sec][sec1][sec2]['update'] = None
-
-            event = wxUpdateProperties(data=data)
+            
+            event = wxUpdateProperties(data = data)
             wx.PostEvent(self, event)
-                            
+        
         # set id
         if id > 0:
             if type in ('raster', '3d-raster'):
                data[nvizType]['object'] = { 'id' : id,
                                             'init' : False }
-            elif type == 'vector':
+            elif type ==  'vector':
                 data['vector'][nvizType]['object'] = { 'id' : id,
                                                        'init' : False }
         
         return data
 
     def LoadRaster(self, item):
-        """Load 2d raster map and set surface attributes
-
+        """!Load 2d raster map and set surface attributes
+        
         @param layer item
         """
         return self._loadRaster(item)
-
+    
     def LoadRaster3d(self, item):
-        """Load 3d raster map and set surface attributes
-
+        """!Load 3d raster map and set surface attributes
+        
         @param layer item
         """
         return self._loadRaster(item)
-
+    
     def _loadRaster(self, item):
-        """Load 2d/3d raster map and set its attributes
-
+        """!Load 2d/3d raster map and set its attributes
+        
         @param layer item
         """
         layer = self.tree.GetPyData(item)[0]['maplayer']
-
+        
         if layer.type not in ('raster', '3d-raster'):
             return
-
-        if layer.type == 'raster':
-            id = self.nvizClass.LoadSurface(str(layer.name), None, None)
+        
+        if layer.type ==  'raster':
+            id = self._display.LoadSurface(str(layer.name), None, None)
             nvizType = 'surface'
             errorMsg = _("Loading raster map")
-        elif layer.type == '3d-raster':
-            id = self.nvizClass.LoadVolume(str(layer.name), None, None)
+        elif layer.type ==  '3d-raster':
+            id = self._display.LoadVolume(str(layer.name), None, None)
             nvizType = 'volume'
             errorMsg = _("Loading 3d raster map")
         else:
@@ -522,9 +607,9 @@
         
         if id < 0:
             if layer.type in ('raster', '3d-raster'):
-                print >> sys.stderr, "Nviz:" + "%s <%s> %s" % (errorMsg, layer.name, _("failed"))
+                self.log.WriteError("%s <%s> %s" % (errorMsg, layer.name, _("failed")))
             else:
-                print >> sys.stderr, "Nviz:" + _("Unsupported layer type '%s'") % layer.type
+                self.log.WriteError(_("Unsupported layer type '%s'") % layer.type)
         
         self.layers.append(item)
         
@@ -532,240 +617,229 @@
         data = self.SetMapObjProperties(item, id, nvizType)
         
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self, event)
         
         # update tools window
-        if hasattr(self.parent, "nvizToolWin") and \
-                item == self.GetSelectedLayer(type='item'):
-            toolWin = self.parent.nvizToolWin
-            if layer.type == 'raster':
+        if hasattr(self.lmgr, "nviz") and \
+                item ==  self.GetSelectedLayer(type = 'item'):
+            toolWin = self.lmgr.nviz
+            if layer.type ==  'raster':
                 win = toolWin.FindWindowById( \
                     toolWin.win['vector']['lines']['surface'])
                 win.SetItems(self.GetLayerNames(layer.type))
-
-            toolWin.UpdatePage(nvizType)
-            toolWin.SetPage(nvizType)
             
+            #toolWin.UpdatePage(nvizType)
+            #toolWin.SetPage(nvizType)
+        
         return id
-
+    
     def UnloadRaster(self, item):
-        """Unload 2d raster map
-
+        """!Unload 2d raster map
+        
         @param layer item
         """
         return self._unloadRaster(item)
-
+    
     def UnloadRaster3d(self, item):
-        """Unload 3d raster map
-
+        """!Unload 3d raster map
+        
         @param layer item
         """
         return self._unloadRaster(item)
-
+    
     def _unloadRaster(self, item):
-        """Unload 2d/3d raster map
-
+        """!Unload 2d/3d raster map
+        
         @param item layer item
         """
         layer = self.tree.GetPyData(item)[0]['maplayer']
-
+        
         if layer.type not in ('raster', '3d-raster'):
             return
-
+        
         data = self.tree.GetPyData(item)[0]['nviz']
-
-        if layer.type == 'raster':
+        
+        if layer.type ==  'raster':
             nvizType = 'surface'
-            unloadFn = self.nvizClass.UnloadSurface
+            unloadFn = self._display.UnloadSurface
             errorMsg = _("Unable to unload raster map")
             successMsg = _("Raster map")
         else:
             nvizType = 'volume'
-            unloadFn = self.nvizClass.UnloadVolume
+            unloadFn = self._display.UnloadVolume
             errorMsg = _("Unable to unload 3d raster map")
             successMsg = _("3d raster map")
-
+        
         id = data[nvizType]['object']['id']
-
-        if unloadFn(id) == 0:
-            print >> sys.stderr, "Nviz:" + "%s <%s>" % (errorMsg, layer.name)
+        
+        if unloadFn(id) ==  0:
+            self.log.WriteError("%s <%s>" % (errorMsg, layer.name))
         else:
-            print "Nviz:" + "%s <%s> %s" % (successMsg, layer.name, _("unloaded successfully"))
-
+            self.log.WriteLog("%s <%s> %s" % (successMsg, layer.name, _("unloaded successfully")))
+        
         data[nvizType].pop('object')
-
+        
         self.layers.remove(item)
         
         # update tools window
-        if hasattr(self.parent, "nvizToolWin") and \
-                layer.type == 'raster':
-            toolWin = self.parent.nvizToolWin
+        if hasattr(self.lmgr, "nviz") and \
+                layer.type ==  'raster':
+            toolWin = self.lmgr.nviz
             win = toolWin.FindWindowById( \
                 toolWin.win['vector']['lines']['surface'])
             win.SetItems(self.GetLayerNames(layer.type))
-
-            # remove surface page
-            if toolWin.notebook.GetSelection() == toolWin.page[nvizType]['id']:
-                toolWin.notebook.RemovePage(toolWin.page[nvizType]['id'])
-                toolWin.page[nvizType]['id'] = -1
-                toolWin.page['settings']['id'] = 1
-
-    def LoadVector(self, item, vecType=None):
-        """Load 2D or 3D vector map overlay
-
+            
+    def LoadVector(self, item, points = None):
+        """!Load 2D or 3D vector map overlay
+        
         @param item layer item
-        @param vecType vector type (lines / points)
+        @param points True to load points, False to load lines, None
+        to load both
         """
         layer = self.tree.GetPyData(item)[0]['maplayer']
-
-        if layer.type != 'vector':
+        if layer.type !=  'vector':
             return
-
-        if vecType is None:
-            # load data type by default
-            vecType = []
-            for v in ('lines', 'points'):
-                if UserSettings.Get(group='nviz', key='vector',
-                                    subkey=[v, 'show']):
-                    vecType.append(v)
-
+        
         # set default properties
-        self.SetMapObjProperties(item, -1, 'lines')
-        self.SetMapObjProperties(item, -1, 'points')
-
+        if points is None:
+            self.SetMapObjProperties(item, -1, 'lines')
+            self.SetMapObjProperties(item, -1, 'points')
+            vecTypes = ('points', 'lines')
+        elif points:
+            self.SetMapObjProperties(item, -1, 'points')
+            vecTypes = ('points', )
+        else:
+            self.SetMapObjProperties(item, -1, 'lines')
+            vecTypes = ('lines', )
+        
         id = -1
-        for type in vecType:
-            if type == 'lines':
-                id = self.nvizClass.LoadVector(str(layer.name), False)
+        for vecType in vecTypes:
+            if vecType == 'lines':
+                id = self._display.LoadVector(str(layer.GetName()), False)
             else:
-                id = self.nvizClass.LoadVector(str(layer.name), True)
-
+                id = self._display.LoadVector(str(layer.GetName()), True)
             if id < 0:
-                print >> sys.stderr, "Nviz:" + _("Loading vector map <%(name)s> (%(type)s) failed") % \
-                    { 'name' : layer.name, 'type' : type }
-                continue
-
+                self.log.WriteError(_("Loading vector map <%(name)s> (%(type)s) failed") % \
+                    { 'name' : layer.name, 'type' : vecType })
             # update layer properties
-            self.SetMapObjProperties(item, id, type)
+            self.SetMapObjProperties(item, id, vecType)
         
         self.layers.append(item)
         
         # update properties
         data = self.tree.GetPyData(item)[0]['nviz']
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self, event)
         
         # update tools window
-        if hasattr(self.parent, "nvizToolWin") and \
-                item == self.GetSelectedLayer(type='item'):
-            toolWin = self.parent.nvizToolWin
-
+        if hasattr(self.lmgr, "nviz") and \
+                item ==  self.GetSelectedLayer(type = 'item'):
+            toolWin = self.lmgr.nviz
+            
             toolWin.UpdatePage('vector')
-            toolWin.SetPage('vector')
+            ### toolWin.SetPage('vector')
         
         return id
 
-    def UnloadVector(self, item, vecType=None):
-        """Unload vector map overlay
-
+    def UnloadVector(self, item, points = None):
+        """!Unload vector map overlay
+        
         @param item layer item
-        @param vecType vector type (lines, points)
+        @param points,lines True to unload given feature type
         """
         layer = self.tree.GetPyData(item)[0]['maplayer']
         data = self.tree.GetPyData(item)[0]['nviz']['vector']
-
-        if vecType is None:
-            vecType = []
-            for v in ('lines', 'points'):
-                if UserSettings.Get(group='nviz', key='vector',
-                                    subkey=[v, 'show']):
-                    vecType.append(v)
-
-        for vtype in vecType:
-            if not data[vtype].has_key('object'):
+        
+        # if vecType is None:
+        #     vecType = []
+        #     for v in ('lines', 'points'):
+        #         if UserSettings.Get(group = 'nviz', key = 'vector',
+        #                             subkey = [v, 'show']):
+        #             vecType.append(v)
+        
+        if points is None:
+            vecTypes = ('points', 'lines')
+        elif points:
+            vecTypes = ('points', )
+        else:
+            vecTypes = ('lines', )
+        
+        for vecType in vecTypes:
+            if not data[vecType].has_key('object'):
                 continue
-
-            id = data[vtype]['object']['id']
-
-            if vtype == 'lines':
-                ret = self.nvizClass.UnloadVector(id, False)
+            
+            id = data[vecType]['object']['id']
+            
+            if vecType ==  'lines':
+                ret = self._display.UnloadVector(id, False)
             else:
-                ret = self.nvizClass.UnloadVector(id, True)
-            if ret == 0:
-                print >> sys.stderr, "Nviz:" + _("Unable to unload vector map <%(name)s> (%(type)s)") % \
-                    { 'name': layer.name, 'type' : vtype }
+                ret = self._display.UnloadVector(id, True)
+            if ret ==  0:
+                self.log.WriteError(_("Unable to unload vector map <%(name)s> (%(type)s)") % \
+                    { 'name': layer.name, 'type' : vecType })
             else:
-                print "Nviz:" + _("Vector map <%(name)s> (%(type)s) unloaded successfully") % \
-                    { 'name' : layer.name, 'type' : vtype }
-
+                self.log.WriteLog(_("Vector map <%(name)s> (%(type)s) unloaded successfully") % \
+                    { 'name' : layer.name, 'type' : vecType })
             
-            data[vtype].pop('object')
-
-            self.layers.remove(id)
+            data[vecType].pop('object')
             
-        # update tools window
-        if hasattr(self.parent, "nvizToolWin") and \
-                vecType is None:
-            toolWin = self.parent.nvizToolWin
-            # remove surface page
-            if toolWin.notebook.GetSelection() == toolWin.page['surface']['id']:
-                toolWin.notebook.RemovePage(toolWin.page['surface']['id'])
-                toolWin.page['surface']['id'] = -1
-                toolWin.page['settings']['id'] = 1
-    
+            ### self.layers.remove(id)
+        
     def Reset(self):
-        """Reset (unload data)"""
+        """!Reset (unload data)"""
         for item in self.layers:
             type = self.tree.GetPyData(item)[0]['maplayer'].type
-            if type == 'raster':
+            if type ==  'raster':
                 self.UnloadRaster(item)
-            elif type == '3d-raster':
+            elif type ==  '3d-raster':
                 self.UnloadRaster3d(item)
-            elif type == 'vector':
+            elif type ==  'vector':
                 self.UnloadVector(item)
-            
+        
         self.init = False
 
     def OnZoomToMap(self, event):
-        """
-        Set display extents to match selected raster
-        or vector map or volume.
-
+        """!Set display extents to match selected raster or vector
+        map or volume.
+        
         @todo vector, volume
         """
         layer = self.GetSelectedLayer()
-
+        
         if layer is None:
             return
-
-        Debug.msg (3, "GLWindow.OnZoomToMap(): layer=%s, type=%s" % \
+        
+        Debug.msg (3, "GLWindow.OnZoomToMap(): layer = %s, type = %s" % \
                        (layer.name, layer.type))
+        
+        self._display.SetViewportDefault()
 
-        self.nvizClass.SetViewportDefault()
-
     def ResetView(self):
-        """Reset to default view"""
+        """!Reset to default view"""
         self.view['z-exag']['value'], \
             self.iview['height']['value'], \
             self.iview['height']['min'], \
-            self.iview['height']['max'] = self.nvizClass.SetViewDefault()
+            self.iview['height']['max'] = self._display.SetViewDefault()
         
-        self.view['pos']['x'] = UserSettings.Get(group='nviz', key='view',
-                                                 subkey=('pos', 'x'))
-        self.view['pos']['y'] = UserSettings.Get(group='nviz', key='view',
-                                                 subkey=('pos', 'x'))
-        self.view['persp']['value'] = UserSettings.Get(group='nviz', key='view',
-                                                       subkey=('persp', 'value'))
-
-        self.view['twist']['value'] = UserSettings.Get(group='nviz', key='view',
-                                                       subkey=('twist', 'value'))
-
-        event = wxUpdateView(zExag=False)
+        self.view['z-exag']['min'] = 0
+        self.view['z-exag']['max'] = self.view['z-exag']['value'] * 10
+        
+        self.view['position']['x'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                 subkey = ('position', 'x'))
+        self.view['position']['y'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                 subkey = ('position', 'y'))
+        self.view['persp']['value'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                       subkey = ('persp', 'value'))
+        
+        self.view['twist']['value'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                       subkey = ('twist', 'value'))
+        
+        event = wxUpdateView(zExag = False)
         wx.PostEvent(self, event)
         
     def UpdateMapObjProperties(self, event):
-        """Generic method to update data layer properties"""
+        """!Generic method to update data layer properties"""
         data = event.data
         
         if data.has_key('surface'):
@@ -773,13 +847,13 @@
             self.UpdateSurfaceProperties(id, data['surface'])
             # -> initialized
             data['surface']['object']['init'] = True
-
+            
         elif data.has_key('volume'):
             id = data['volume']['object']['id']
             self.UpdateVolumeProperties(id, data['volume'])
             # -> initialized
             data['volume']['object']['init'] = True
-
+            
         elif data.has_key('vector'):
             for type in ('lines', 'points'):
                 if data['vector'][type].has_key('object'):
@@ -789,80 +863,80 @@
                     data['vector'][type]['object']['init'] = True
         
     def UpdateSurfaceProperties(self, id, data):
-        """Update surface map object properties"""
+        """!Update surface map object properties"""
         # surface attributes
         for attrb in ('topo', 'color', 'mask',
                      'transp', 'shine', 'emit'):
             if not data['attribute'].has_key(attrb) or \
                     not data['attribute'][attrb].has_key('update'):
                 continue
-
+            
             map = data['attribute'][attrb]['map']
             value = data['attribute'][attrb]['value']
-
+            
             if map is None: # unset
                 # only optional attributes
-                if attrb == 'mask':
+                if attrb ==  'mask':
                     # TODO: invert mask
                     # TODO: broken in NVIZ
-                    self.nvizClass.UnsetSurfaceMask(id)
-                elif attrb == 'transp':
-                    self.nvizClass.UnsetSurfaceTransp(id)
-                elif attrb == 'emit':
-                    self.nvizClass.UnsetSurfaceEmit(id) 
+                    self._display.UnsetSurfaceMask(id)
+                elif attrb ==  'transp':
+                    self._display.UnsetSurfaceTransp(id)
+                elif attrb ==  'emit':
+                    self._display.UnsetSurfaceEmit(id) 
             else:
-                if type(value) == type('') and \
-                        len(value) <= 0: # ignore empty values (TODO: warning)
+                if type(value) ==  type('') and \
+                        len(value) <=  0: # ignore empty values (TODO: warning)
                     continue
-                if attrb == 'topo':
-                    self.nvizClass.SetSurfaceTopo(id, map, str(value)) 
-                elif attrb == 'color':
-                    self.nvizClass.SetSurfaceColor(id, map, str(value))
-                elif attrb == 'mask':
+                if attrb ==  'topo':
+                    self._display.SetSurfaceTopo(id, map, str(value)) 
+                elif attrb ==  'color':
+                    self._display.SetSurfaceColor(id, map, str(value))
+                elif attrb ==  'mask':
                     # TODO: invert mask
                     # TODO: broken in NVIZ
-                    self.nvizClass.SetSurfaceMask(id, False, str(value))
-                elif attrb == 'transp':
-                    self.nvizClass.SetSurfaceTransp(id, map, str(value)) 
-                elif attrb == 'shine':
-                    self.nvizClass.SetSurfaceShine(id, map, str(value)) 
-                elif attrb == 'emit':
-                    self.nvizClass.SetSurfaceEmit(id, map, str(value)) 
+                    self._display.SetSurfaceMask(id, False, str(value))
+                elif attrb ==  'transp':
+                    self._display.SetSurfaceTransp(id, map, str(value)) 
+                elif attrb ==  'shine':
+                    self._display.SetSurfaceShine(id, map, str(value)) 
+                elif attrb ==  'emit':
+                    self._display.SetSurfaceEmit(id, map, str(value)) 
             data['attribute'][attrb].pop('update')
-
+        
         # draw res
         if data['draw']['resolution'].has_key('update'):
             coarse = data['draw']['resolution']['coarse']
             fine   = data['draw']['resolution']['fine']
-
+            
             if data['draw']['all']:
-                self.nvizClass.SetSurfaceRes(-1, fine, coarse)
+                self._display.SetSurfaceRes(-1, fine, coarse)
             else:
-                self.nvizClass.SetSurfaceRes(id, fine, coarse)
+                self._display.SetSurfaceRes(id, fine, coarse)
             data['draw']['resolution'].pop('update')
         
         # draw style
         if data['draw']['mode'].has_key('update'):
             if data['draw']['mode']['value'] < 0: # need to calculate
                 data['draw']['mode']['value'] = \
-                    self.nvizDefault.GetDrawMode(mode=data['draw']['mode']['desc']['mode'],
-                                                 style=data['draw']['mode']['desc']['style'],
-                                                 shade=data['draw']['mode']['desc']['shading'],
-                                                 string=True)
+                    self.nvizDefault.GetDrawMode(mode = data['draw']['mode']['desc']['mode'],
+                                                 style = data['draw']['mode']['desc']['style'],
+                                                 shade = data['draw']['mode']['desc']['shading'],
+                                                 string = True)
             style = data['draw']['mode']['value']
             if data['draw']['all']:
-                self.nvizClass.SetSurfaceStyle(-1, style)
+                self._display.SetSurfaceStyle(-1, style)
             else:
-                self.nvizClass.SetSurfaceStyle(id, style)
+                self._display.SetSurfaceStyle(id, style)
             data['draw']['mode'].pop('update')
-
+        
         # wire color
         if data['draw']['wire-color'].has_key('update'):
             color = data['draw']['wire-color']['value']
             if data['draw']['all']:
-                self.nvizClass.SetWireColor(-1, str(color))
+                self._display.SetWireColor(-1, str(color))
             else:
-                self.nvizClass.SetWireColor(id, str(color))
+                self._display.SetWireColor(id, str(color))
             data['draw']['wire-color'].pop('update')
         
         # position
@@ -870,23 +944,20 @@
             x = data['position']['x']
             y = data['position']['y']
             z = data['position']['z']
-            self.nvizClass.SetSurfacePosition(id, x, y, z)
+            self._display.SetSurfacePosition(id, x, y, z)
             data['position'].pop('update')
         
-    def UpdateVolumeProperties(self, id, data, isosurfId=None):
-        """Update volume (isosurface/slice) map object properties"""
-        #
-        # draw
-        #
+    def UpdateVolumeProperties(self, id, data, isosurfId = None):
+        """!Update volume (isosurface/slice) map object properties"""
         if data['draw']['resolution'].has_key('update'):
-            self.nvizClass.SetIsosurfaceRes(id, data['draw']['resolution']['value'])
+            self._display.SetIsosurfaceRes(id, data['draw']['resolution']['value'])
             data['draw']['resolution'].pop('update')
         
         if data['draw']['shading'].has_key('update'):
             if data['draw']['shading']['value'] < 0: # need to calculate
                 data['draw']['shading']['value'] = \
-                    self.nvizDefault.GetDrawMode(shade=data['draw']['shading'],
-                                                 string=False)
+                    self.nvizDefault.GetDrawMode(shade = data['draw']['shading'],
+                                                 string = False)
             data['draw']['shading'].pop('update')
         
         #
@@ -901,66 +972,66 @@
                     continue
                 map = isosurf[attrb]['map']
                 value = isosurf[attrb]['value']
-
+                
                 if map is None: # unset
                     # only optional attributes
-                    if attrb == 'mask':
+                    if attrb ==  'mask':
                         # TODO: invert mask
                         # TODO: broken in NVIZ
-                        self.nvizClass.UnsetIsosurfaceMask(id, isosurfId)
-                    elif attrb == 'transp':
-                        self.nvizClass.UnsetIsosurfaceTransp(id, isosurfId)
-                    elif attrb == 'emit':
-                        self.nvizClass.UnsetIsosurfaceEmit(id, isosurfId) 
+                        self._display.UnsetIsosurfaceMask(id, isosurfId)
+                    elif attrb ==  'transp':
+                        self._display.UnsetIsosurfaceTransp(id, isosurfId)
+                    elif attrb ==  'emit':
+                        self._display.UnsetIsosurfaceEmit(id, isosurfId) 
                 else:
-                    if type(value) == type('') and \
-                            len(value) <= 0: # ignore empty values (TODO: warning)
+                    if type(value) ==  type('') and \
+                            len(value) <=  0: # ignore empty values (TODO: warning)
                         continue
-                    elif attrb == 'color':
-                        self.nvizClass.SetIsosurfaceColor(id, isosurfId, map, str(value))
-                    elif attrb == 'mask':
+                    elif attrb ==  'color':
+                        self._display.SetIsosurfaceColor(id, isosurfId, map, str(value))
+                    elif attrb ==  'mask':
                         # TODO: invert mask
                         # TODO: broken in NVIZ
-                        self.nvizClass.SetIsosurfaceMask(id, isosurfId, False, str(value))
-                    elif attrb == 'transp':
-                        self.nvizClass.SetIsosurfaceTransp(id, isosurfId, map, str(value)) 
-                    elif attrb == 'shine':
-                        self.nvizClass.SetIsosurfaceShine(id, isosurfId, map, str(value)) 
-                    elif attrb == 'emit':
-                        self.nvizClass.SetIsosurfaceEmit(id, isosurfId, map, str(value)) 
+                        self._display.SetIsosurfaceMask(id, isosurfId, False, str(value))
+                    elif attrb ==  'transp':
+                        self._display.SetIsosurfaceTransp(id, isosurfId, map, str(value)) 
+                    elif attrb ==  'shine':
+                        self._display.SetIsosurfaceShine(id, isosurfId, map, str(value)) 
+                    elif attrb ==  'emit':
+                        self._display.SetIsosurfaceEmit(id, isosurfId, map, str(value)) 
                 isosurf[attrb].pop('update')
-            isosurfId += 1
+            isosurfId +=  1
         
     def UpdateVectorProperties(self, id, data, type):
-        """Update vector layer properties
-
+        """!Update vector layer properties
+        
         @param id layer id
         @param data properties
         @param type lines/points
         """
-        if type == 'points':
+        if type ==  'points':
             self.UpdateVectorPointsProperties(id, data[type])
         else:
             self.UpdateVectorLinesProperties(id, data[type])
-    
+        
     def UpdateVectorLinesProperties(self, id, data):
-        """Update vector line map object properties"""
+        """!Update vector line map object properties"""
         # mode
         if data['color'].has_key('update') or \
                 data['width'].has_key('update') or \
                 data['mode'].has_key('update'):
             width = data['width']['value']
             color = data['color']['value']
-            if data['mode']['type'] == 'flat':
+            if data['mode']['type'] ==  'flat':
                 flat = True
                 if data.has_key('surface'):
                     data.pop('surface')
             else:
                 flat = False
-                
-            self.nvizClass.SetVectorLineMode(id, color,
-                                             width, flat)
             
+            self._display.SetVectorLineMode(id, color,
+                                            width, flat)
+            
             if data['color'].has_key('update'):
                 data['color'].pop('update')
             if data['width'].has_key('update'):
@@ -970,102 +1041,118 @@
         
         # height
         if data['height'].has_key('update'):
-            self.nvizClass.SetVectorLineHeight(id,
-                                               data['height']['value'])
+            self._display.SetVectorLineHeight(id,
+                                              data['height']['value'])
             data['height'].pop('update')
         
         # surface
         if data['mode'].has_key('update'):
-            sid = self.GetLayerId(type='raster', name=data['mode']['surface'])
+            sid = self.GetLayerId(type = 'raster', name = data['mode']['surface'])
             if sid > -1:
-                self.nvizClass.SetVectorLineSurface(id, sid)
+                self._display.SetVectorLineSurface(id, sid)
             
             data['mode'].pop('update')
         
     def UpdateVectorPointsProperties(self, id, data):
-        """Update vector point map object properties"""
+        """!Update vector point map object properties"""
         if data['size'].has_key('update') or \
                 data['width'].has_key('update') or \
                 data['marker'].has_key('update') or \
                 data['color'].has_key('update'):
-            ret = self.nvizClass.SetVectorPointMode(id, data['color']['value'],
-                                                    data['width']['value'], float(data['size']['value']),
-                                                    data['marker']['value'] + 1)
-
+            ret = self._display.SetVectorPointMode(id, data['color']['value'],
+                                                   data['width']['value'], float(data['size']['value']),
+                                                   data['marker']['value'] + 1)
+            
             error = None
-            if ret == -1:
-                error = _("Vector point layer not found (id=%d)") % id
-            elif ret == -2:
-                error = _("Unable to set data layer properties (id=%d)") % id
+            if ret ==  -1:
+                error = _("Vector point layer not found (id = %d)") % id
+            elif ret ==  -2:
+                error = _("Unable to set data layer properties (id = %d)") % id
 
             if error:
-                raise gcmd.NvizError(parent=self.parent,
-                                     message=_("Setting data layer properties failed.\n\n%s") % error)
-
+                raise gcmd.GException(_("Setting data layer properties failed.\n\n%s") % error)
+            
             for prop in ('size', 'width', 'marker', 'color'):
                 if data[prop].has_key('update'):
                     data[prop].pop('update')
         
         # height
         if data['height'].has_key('update'):
-            self.nvizClass.SetVectorPointHeight(id,
-                                                data['height']['value'])
+            self._display.SetVectorPointHeight(id,
+                                               data['height']['value'])
             data['height'].pop('update')
         
         # surface
         if data['mode'].has_key('update'):
-            sid = self.GetLayerId(type='raster', name=data['mode']['surface'])
+            sid = self.GetLayerId(type = 'raster', name = data['mode']['surface'])
             if sid > -1:
-                self.nvizClass.SetVectorPointSurface(id, sid)
+                self._display.SetVectorPointSurface(id, sid)
             
             data['mode'].pop('update')
-
+            
     def GetLayerNames(self, type):
-        """Return list of map layer names of given type"""
+        """!Return list of map layer names of given type"""
         layerName = []
         
         for item in self.layers:
             mapLayer = self.tree.GetPyData(item)[0]['maplayer']
-            if type != mapLayer.GetType():
+            if type !=  mapLayer.GetType():
                 continue
             
             layerName.append(mapLayer.GetName())
         
         return layerName
     
-    def GetLayerData(self, type, name):
-        """Return layer item data
-
-        @return {} if no layer item found
-        """
-        for item in self.layers:
-            mapLayer = self.tree.GetPyData(item)[0]['maplayer'].GetName()
-            if mapLayer == name:
-                return self.tree.GetPyData(item)[0]['nviz']
-        
-        return {}
-    
     def GetLayerId(self, type, name):
-        """Get layer object id or -1"""
+        """!Get layer object id or -1"""
         if len(name) < 1:
             return -1
         
         for item in self.layers:
             mapLayer = self.tree.GetPyData(item)[0]['maplayer']
-            if type != mapLayer.GetType() or \
-                    name != mapLayer.GetName():
+            if type !=  mapLayer.GetType() or \
+                    name !=  mapLayer.GetName():
                 continue
-
+            
             data = self.tree.GetPyData(item)[0]['nviz']
             
-            if type == 'raster':
+            if type ==  'raster':
                 return data['surface']['object']['id']
-            elif type == 'vpoint':
+            elif type ==  'vpoint':
                 return data['vector']['points']['object']['id']
-            elif type == 'vline':
+            elif type ==  'vline':
                 return data['vector']['lines']['object']['id']
-            elif type == '3d-raster':
+            elif type ==  '3d-raster':
                 return data['volume']['object']['id']
+        
+        return -1
+    
+    def SaveToFile(self, FileName, FileType, width, height):
+        """!This draws the DC to a buffer that can be saved to a file.
+        
+        @todo fix BufferedPaintDC
+        
+        @param FileName file name
+        @param FileType type of bitmap
+        @param width image width
+        @param height image height
+        """
+        self._display.SaveToFile(FileName, width, height)
+                
+        # pbuffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+        # dc = wx.BufferedPaintDC(self, pbuffer)
+        # dc.Clear()
+        # self.SetCurrent()
+        # self._display.Draw(False, -1)
+        # pbuffer.SaveFile(FileName, FileType)
+        # self.SwapBuffers()
+        
+    def GetDisplay(self):
+        """!Get display instance"""
+        return self._display
+        
+    def ZoomToMap(self):
+        """!Reset view
+        """
+        self.lmgr.nviz.OnResetView(None)
 
-        return -1
-            


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_mapdisp.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_preferences.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_preferences.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_preferences.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,475 @@
+"""
+ at package nviz_preferences.py
+
+ at brief Nviz (3D view) preferences window
+
+Classes:
+ - NvizPreferencesDialog
+
+(C) 2008-2010 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> (Google SoC 2008/2010)
+ at author Enhancements by Michael Barton <michael.barton at asu.edu>
+"""
+
+import types
+
+import wx
+import wx.lib.colourselect as csel
+
+import globalvar
+from preferences import globalSettings as UserSettings
+from preferences import PreferencesBaseDialog
+
+class NvizPreferencesDialog(PreferencesBaseDialog):
+    """!Nviz preferences dialog"""
+    def __init__(self, parent, title = _("3D view settings"),
+                 settings = UserSettings):
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        self.toolWin = self.parent.GetLayerManager().nviz
+        self.win = dict()
+        
+        # create notebook pages
+        self._createViewPage(self.notebook)
+        self._createVectorPage(self.notebook)
+        
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+        
+    def _createViewPage(self, notebook):
+        """!Create notebook page for general settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("View"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.win['general'] = {}
+        self.win['view'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("View")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # perspective
+        self.win['view']['persp'] = {}
+        pvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp')
+        ipvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp', internal = True)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Perspective:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(value)")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = pvals['value'],
+                           min = ipvals['min'],
+                           max = ipvals['max'])
+        self.win['view']['persp']['value'] = pval.GetId()
+        gridSizer.Add(item = pval, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = pvals['step'],
+                           min = ipvals['min'],
+                           max = ipvals['max']-1)
+        self.win['view']['persp']['step'] = pstep.GetId()
+        gridSizer.Add(item = pstep, pos = (0, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # position
+        self.win['view']['pos'] = {}
+        posvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'position')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Position:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(x)")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['x'] * 100,
+                           min = 0,
+                           max = 100)
+        self.win['view']['pos']['x'] = px.GetId()
+        gridSizer.Add(item = px, pos = (1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = "(y)"),
+                      pos = (1, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['y'] * 100,
+                           min = 0,
+                           max = 100)
+        self.win['view']['pos']['y'] = py.GetId()
+        gridSizer.Add(item = py, pos = (1, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # height
+        self.win['view']['height'] = {}
+        hvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'height')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        hstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = hvals['step'],
+                           min = 1,
+                           max = 1e6)
+        self.win['view']['height']['step'] = hstep.GetId()
+        gridSizer.Add(item = hstep, pos = (2, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # twist
+        self.win['view']['twist'] = {}
+        tvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist')
+        itvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist', internal = True)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Twist:")),
+                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(value)")),
+                      pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        tval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = tvals['value'],
+                           min = itvals['min'],
+                           max = itvals['max'])
+        self.win['view']['twist']['value'] = tval.GetId()
+        gridSizer.Add(item = tval, pos = (3, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (3, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        tstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = tvals['step'],
+                           min = itvals['min'],
+                           max = itvals['max']-1)
+        self.win['view']['twist']['step'] = tstep.GetId()
+        gridSizer.Add(item = tstep, pos = (3, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # z-exag
+        self.win['view']['z-exag'] = {}
+        zvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'z-exag')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Z-exag:")),
+                      pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(value)")),
+                      pos = (4, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        zval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           min = -1e6,
+                           max = 1e6)
+        self.win['view']['z-exag']['value'] = zval.GetId()
+        gridSizer.Add(item = zval, pos = (4, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (4, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        zstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = zvals['step'],
+                           min = -1e6,
+                           max = 1e6)
+        self.win['view']['z-exag']['step'] = zstep.GetId()
+        gridSizer.Add(item = zstep, pos = (4, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Image Appearance")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # background color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Background color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'settings',
+                                                            subkey = ['general', 'bgcolor']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['general']['bgcolor'] = color.GetId()
+        gridSizer.Add(item = color, pos = (0, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
+    def _createVectorPage(self, notebook):
+        """!Create notebook page for general settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Vector"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # vector lines
+        self.win['vector'] = {}
+        self.win['vector']['lines'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector lines")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # show
+        row = 0
+        showLines = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = _("Show lines"))
+        self.win['vector']['lines']['show'] = showLines.GetId()
+        showLines.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                            subkey = ['lines', 'show']))
+        gridSizer.Add(item = showLines, pos = (row, 0))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        # vector points
+        self.win['vector']['points'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector points")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 5)
+        
+        # show
+        row = 0
+        showPoints = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Show points"))
+        showPoints.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                             subkey = ['points', 'show']))
+        self.win['vector']['points']['show'] = showPoints.GetId()
+        gridSizer.Add(item = showPoints, pos = (row, 0), span = (1, 8))
+        
+        # icon size
+        row += 1 
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Size:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        isize = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 100,
+                            min = 1,
+                            max = 1e6)
+        self.win['vector']['points']['size'] = isize.GetId()
+        isize.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                        subkey = ['points', 'size']))
+        gridSizer.Add(item = isize, pos = (row, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon width
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      pos = (row, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 2,
+                            min = 1,
+                            max = 1e6)
+        self.win['vector']['points']['width'] = isize.GetId()
+        iwidth.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                         subkey = ['points', 'width']))
+        gridSizer.Add(item = iwidth, pos = (row, 3),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon symbol
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Marker:")),
+                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                          choices = UserSettings.Get(group = 'nviz', key = 'vector',
+                                                   subkey = ['points', 'marker'], internal = True))
+        isym.SetName("selection")
+        self.win['vector']['points']['marker'] = isym.GetId()
+        isym.SetSelection(UserSettings.Get(group = 'nviz', key = 'vector',
+                                           subkey = ['points', 'marker']))
+        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 5))
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (row, 6), flag = wx.ALIGN_CENTER_VERTICAL)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY)
+        icolor.SetName("color")
+        self.win['vector']['points']['color'] = icolor.GetId()
+        icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['points', 'color']))
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 7))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
+    def OnDefault(self, event):
+        """Restore default settings"""
+        settings = copy.deepcopy(UserSettings.GetDefaultSettings()['nviz'])
+        UserSettings.Set(group = 'nviz',
+                         value = settings)
+        
+        for subgroup, key in settings.iteritems(): # view, surface, vector...
+            if subgroup != 'view':
+                continue
+            for subkey, value in key.iteritems():
+                for subvalue in value.keys():
+                    win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
+                    val = settings[subgroup][subkey][subvalue]
+                    if subkey == 'position':
+                        val = int(val * 100)
+                    
+                    win.SetValue(val)
+        
+        event.Skip()
+        
+    def OnApply(self, event):
+        """Apply Nviz settings for current session"""
+        settings = UserSettings.Get(group = 'nviz')
+        for subgroup, key in settings.iteritems(): # view, surface, vector...
+            for subkey, value in key.iteritems():
+                if type(value) == types.DictType:
+                    for subvalue in value.keys():
+                        try: # TODO
+                            win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
+                        except:
+                            # print 'e', subgroup, subkey, subvalue
+                            continue
+                        
+                        if win.GetName() == "selection":
+                            value = win.GetSelection()
+                        elif win.GetName() == "color":
+                            value = tuple(win.GetColour())
+                        else:
+                            value = win.GetValue()
+                        if subkey == 'pos':
+                            value = float(value) / 100
+                            
+                        settings[subgroup][subkey][subvalue] = value
+        
+    def OnSave(self, event):
+        """!Apply changes, update map and save settings of selected
+        layer
+        """
+        # apply changes
+        self.OnApply(None)
+        
+        if self.GetSelection() == self.page['id']:
+            fileSettings = {}
+            UserSettings.ReadSettingsFile(settings = fileSettings)
+            fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
+            file = UserSettings.SaveToFile(fileSettings)
+            self.parent.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)
+        
+    def OnLoad(self, event):
+        """!Apply button pressed"""
+        self.LoadSettings()
+        
+        if event:
+            event.Skip()
+
+    def LoadSettings(self):
+        """!Load saved Nviz settings and apply to current session"""
+        UserSettings.ReadSettingsFile()
+        settings = copy.deepcopy(UserSettings.Get(group = 'nviz'))
+        
+        for subgroup, key in settings.iteritems(): # view, surface, vector...
+            for subkey, value in key.iteritems():
+                for subvalue in value.keys():
+                    if subvalue == 'step':
+                        continue
+                    else:
+                        insetting = value[subvalue]                                                    
+                    if subgroup == 'view':
+                        for viewkey, viewitem in self.mapWindow.view[subkey].iteritems(): 
+                            if viewkey == subvalue:
+                                self.mapWindow.view[subkey][viewkey] = insetting 
+                            else:
+                                continue
+                    else:
+                        for otherkey, otheritem in self.win[subgroup][subkey].iteritems():
+                            if type(otheritem) == data:
+                                for endkey, enditem in otheritem.iteritems():
+                                    if endkey == subvalue:
+                                        paramwin = self.FindWindowById(enditem)
+                                    else:
+                                        continue
+                            else:
+                                if otherkey == subvalue:
+                                    paramwin = self.FindWindowById(otheritem)
+                                else:
+                                    continue
+                            if type(insetting) in [tuple, list] and len(insetting) > 2:
+                                insetting = tuple(insetting)
+                                paramwin.SetColour(insetting)
+                            else:
+                                try:
+                                    paramwin.SetValue(insetting)
+                                except:
+                                    try:
+                                        paramwin.SetStringSelection(insetting)
+                                    except:
+                                        continue
+                                
+        self.toolWin.UpdateSettings()
+        self.FindWindowById(self.win['view']['pos']).Draw()
+        self.FindWindowById(self.win['view']['pos']).Refresh(False)
+        
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+    def OnSave(self, event):
+        """!Save button pressed
+        
+        Save settings to configuration file
+        """
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings = fileSettings)
+        fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
+        
+        fileName = UserSettings.SaveToFile(fileSettings)
+        self.parent.GetLayerManager().goutput.WriteLog(_('3D view settings saved to file <%s>.') % fileName)
+        
+        self.Destroy()
+        


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_preferences.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_tools.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_tools.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_tools.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,198 +1,195 @@
-"""
+"""!
 @package nviz_tools.py
 
- at brief Nviz tools window
+ at brief Nviz (3D view) tools window
 
 Classes:
  - NvizToolWindow
+ - PositionWindow
  - ViewPositionWindow
+ - LightPositionWindow
 
-(C) 2008-2009 by the GRASS Development Team
+(C) 2008-2010 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> (Google SoC 2008)
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Enhancements by Michael Barton <michael.barton at asu.edu>
 """
 
 import os
 import sys
 import copy
+import types
 
 import wx
 import wx.lib.colourselect as csel
+import wx.lib.scrolledpanel as SP
+try:
+    import wx.lib.agw.flatnotebook as FN
+except ImportError:
+    import wx.lib.flatnotebook as FN
 
 import globalvar
 import gselect
 import gcmd
 from preferences import globalSettings as UserSettings
-from nviz_mapdisp import wxUpdateView as wxUpdateView
-from nviz_mapdisp import wxUpdateProperties as wxUpdateProperties
+from preferences import PreferencesBaseDialog
+try:
+    from nviz_mapdisp import wxUpdateView, wxUpdateLight, wxUpdateProperties
+    import wxnviz
+except ImportError:
+    pass
+from debug import Debug
 
-sys.path.append(os.path.join(globalvar.ETCWXDIR, "nviz"))
-import grass6_wxnviz as wxnviz
-
-class NvizToolWindow(wx.Frame):
-    """Experimental window for Nviz tools
-
-    @todo integrate with Map display
+class NvizToolWindow(FN.FlatNotebook):
+    """!Nviz (3D view) tools panel
     """
-    def __init__(self, parent=None, id=wx.ID_ANY, title=_("3D View Tools"),
-                 pos=wx.DefaultPosition, size=wx.DefaultSize,
-                 mapWindow=None, 
-                 style=wx.CAPTION|wx.MINIMIZE_BOX|wx.RESIZE_BORDER):
+    def __init__(self, parent, display, id = wx.ID_ANY,
+                 style = globalvar.FNPageStyle, **kwargs):
+        self.parent     = parent # GMFrame
+        self.mapDisplay = display
+        self.mapWindow  = display.GetWindow()
+        self._display   = self.mapWindow.GetDisplay()
         
-        self.parent = parent # MapFrame
-        self.lmgr = self.parent.gismanager # GMFrame
-        self.mapWindow = mapWindow
+        if globalvar.hasAgw:
+            kwargs['agwStyle'] = style
+        else:
+            kwargs['style'] = style
+        FN.FlatNotebook.__init__(self, parent, id, **kwargs)
+        self.SetTabAreaColour(globalvar.FNPageColor)
+        
+        self.win  = {} # window ids
+        self.page = {} # page ids
 
-        wx.Frame.__init__(self, parent, id, title, pos, size, style)
+        # view page
+        self.AddPage(page = self._createViewPage(),
+                     text = " %s " % _("View"))
 
-        #
-        # icon
-        #
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_nviz.ico'), wx.BITMAP_TYPE_ICO))
+        # data page
+        self.AddPage(page = self._createDataPage(),
+                     text = " %s " % _("Data"))
 
-        #
-        # dialog body
-        #
-        mainSizer = wx.BoxSizer(wx.VERTICAL)
-
-        self.win = {} # window ids
-
-        #
-        # notebook
-        #
-        self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
-
-        self.page = {}
-        # view page
-        self.__createViewPage()
-        self.page['view'] = { 'id' : 0 }
-        # surface page
-        size = self.__createSurfacePage()
-        size = (size[0] + 25, size[0] + 20)
-        # vector page
-        self.__createVectorPage()
-        # volume page
-        self.__createVolumePage()
-        # settings page
-        self.__createSettingsPage()
-        self.page['settings'] = { 'id' : 1 }
-        self.UpdatePage('settings')
+        # appearance page
+        self.AddPage(page = self._createAppearancePage(),
+                     text = " %s " % _("Appearance"))
+        
+        self.UpdateSettings()
         self.pageChanging = False
-
-        mainSizer.Add(item=self.notebook, proportion=1,
-                      flag=wx.EXPAND | wx.ALL, border=5)
-
-        #
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
         # bindings
-        #
         self.Bind(wx.EVT_CLOSE, self.OnClose)
-        # avoid focusing map display window
-        self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
         
-        #
-        # layout
-        #
-        self.SetSizer(mainSizer)
-        mainSizer.Fit(self)
-
-        self.SetSize(size)
-
+        self.Update()
+        wx.CallAfter(self.SetPage, 'view')
+        wx.CallAfter(self.notebookData.SetSelection, 0)
+        wx.CallAfter(self.notebookAppearance.SetSelection, 0)
+        
     def OnPageChanged(self, event):
         new = event.GetSelection()
-        self.notebook.ChangeSelection(new)
+        # self.ChangeSelection(new)
     
-    def PostViewEvent(self, zExag=False):
-        """Change view settings"""
-        event = wxUpdateView(zExag=zExag)
+    def PostViewEvent(self, zExag = False):
+        """!Change view settings"""
+        event = wxUpdateView(zExag = zExag)
         wx.PostEvent(self.mapWindow, event)
 
-    def __createViewPage(self):
-        """Create view settings page"""
-        panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
-        self.notebook.AddPage(page=panel,
-                              text=" %s " % _("View"))
+    def _createViewPage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['view'] = { 'id' : 0,
+                              'notebook' : self.GetId()}
         
         pageSizer = wx.BoxSizer(wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
-
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Control View")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
         self.win['view'] = {}
-
+        
         # position
-        posSizer = wx.GridBagSizer(vgap=3, hgap=3)
-        posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("W")),
-                     pos=(1, 0), flag=wx.ALIGN_CENTER)
-        posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("N")),
-                     pos=(0, 1), flag=wx.ALIGN_CENTER | wx.ALIGN_BOTTOM)
-        viewPos = ViewPositionWindow(panel, id=wx.ID_ANY, size=(175, 175),
-                                     mapwindow=self.mapWindow)
-        self.win['view']['pos'] = viewPos.GetId()
-        posSizer.Add(item=viewPos,
-                     pos=(1, 1), flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
-        posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("S")),
-                     pos=(2, 1), flag=wx.ALIGN_CENTER | wx.ALIGN_TOP)
-        posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("E")),
-                     pos=(1, 2), flag=wx.ALIGN_CENTER)
-        gridSizer.Add(item=posSizer, pos=(0, 0))
+        posSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("W")),
+                     pos = (1, 0), flag = wx.ALIGN_CENTER)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("N")),
+                     pos = (0, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_BOTTOM)
+        view = ViewPositionWindow(panel, size = (175, 175),
+                                  mapwindow = self.mapWindow)
+        self.win['view']['position'] = view.GetId()
+        posSizer.Add(item = view,
+                     pos = (1, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("S")),
+                     pos = (2, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_TOP)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("E")),
+                     pos = (1, 2), flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = posSizer, pos = (0, 0))
                   
         # perspective
-        range = UserSettings.Get(group='nviz', key='view', subkey='persp', internal=True)
-        self.CreateControl(panel, dict=self.win['view'], name='persp',
-                           range=(range['min'], range['max']),
-                           bind=(self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
-        gridSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Perspective:")),
-                      pos=(1, 0), flag=wx.ALIGN_CENTER)
-        gridSizer.Add(item=self.FindWindowById(self.win['view']['persp']['slider']), pos=(2, 0))
-        gridSizer.Add(item=self.FindWindowById(self.win['view']['persp']['spin']), pos=(3, 0),
-                      flag=wx.ALIGN_CENTER)        
-
+        # set initial defaults here (or perhaps in a default values file), not in user settings
+        self._createControl(panel, data = self.win['view'], name = 'persp',
+                            range = (1,100),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Perspective:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['slider']), pos = (2, 0))
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['spin']), pos = (3, 0),
+                      flag = wx.ALIGN_CENTER)        
+        
         # twist
-        range = UserSettings.Get(group='nviz', key='view', subkey='twist', internal=True)
-        self.CreateControl(panel, dict=self.win['view'], name='twist',
-                           range=(range['min'], range['max']),
-                           bind=(self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
-        gridSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Twist:")),
-                      pos=(1, 1), flag=wx.ALIGN_CENTER)
-        gridSizer.Add(item=self.FindWindowById(self.win['view']['twist']['slider']), pos=(2, 1))
-        gridSizer.Add(item=self.FindWindowById(self.win['view']['twist']['spin']), pos=(3, 1),
-                      flag=wx.ALIGN_CENTER)        
-
+        self._createControl(panel, data = self.win['view'], name = 'twist',
+                            range = (-180,180),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Twist:")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['slider']), pos = (2, 1))
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['spin']), pos = (3, 1),
+                      flag = wx.ALIGN_CENTER)        
+        
         # height + z-exag
-        self.CreateControl(panel, dict=self.win['view'], name='height', sliderHor=False,
-                           range=(0, 1),
-                           bind=(self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
-        self.CreateControl(panel, dict=self.win['view'], name='z-exag', sliderHor=False,
-                           range=(0, 1),
-                           bind=(self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
-        heightSizer = wx.GridBagSizer(vgap=3, hgap=3)
-        heightSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Height:")),
-                      pos=(0, 0), flag=wx.ALIGN_LEFT, span=(1, 2))
-        heightSizer.Add(item=self.FindWindowById(self.win['view']['height']['slider']),
-                        flag=wx.ALIGN_RIGHT, pos=(1, 0))
-        heightSizer.Add(item=self.FindWindowById(self.win['view']['height']['spin']),
-                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
-                        wx.BOTTOM | wx.RIGHT, pos=(1, 1))
-        heightSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Z-exag:")),
-                      pos=(0, 2), flag=wx.ALIGN_LEFT, span=(1, 2))
-        heightSizer.Add(item=self.FindWindowById(self.win['view']['z-exag']['slider']),
-                        flag=wx.ALIGN_RIGHT, pos=(1, 2))
-        heightSizer.Add(item=self.FindWindowById(self.win['view']['z-exag']['spin']),
-                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
-                        wx.BOTTOM | wx.RIGHT, pos=(1, 3))
-
-        gridSizer.Add(item=heightSizer, pos=(0, 1), flag=wx.ALIGN_RIGHT)
-
+        self._createControl(panel, data = self.win['view'], name = 'height', sliderHor = False,
+                            range = (0, 1),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+        
+        self._createControl(panel, data = self.win['view'], name = 'z-exag', sliderHor = False,
+                            range = (0, 5),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+        self.FindWindowById(self.win['view']['z-exag']['slider']).SetValue(1)
+        self.FindWindowById(self.win['view']['z-exag']['spin']).SetValue(1)
+        
+        heightSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:")),
+                      pos = (0, 0), flag = wx.ALIGN_LEFT, span = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['slider']),
+                        flag = wx.ALIGN_RIGHT, pos = (1, 0))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['spin']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
+                        wx.BOTTOM | wx.RIGHT, pos = (1, 1))
+        heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Z-exag:")),
+                      pos = (0, 2), flag = wx.ALIGN_LEFT, span = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['slider']),
+                        flag = wx.ALIGN_RIGHT, pos = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['spin']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
+                        wx.BOTTOM | wx.RIGHT, pos = (1, 3))
+        
+        gridSizer.Add(item = heightSizer, pos = (0, 1), flag = wx.ALIGN_RIGHT)
+        
         # view setup + reset
         viewSizer = wx.BoxSizer(wx.HORIZONTAL)
-
-        viewSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY,
-                                         label=_("Look at:")),
-                      flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
         
-        viewType = wx.Choice (parent=panel, id=wx.ID_ANY, size=(125, -1),
+        viewSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY,
+                                           label = _("Look at:")),
+                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+        
+        viewType = wx.Choice (parent = panel, id = wx.ID_ANY, size = (125, -1),
                               choices = [_("top"),
                                          _("north"),
                                          _("south"),
@@ -205,52 +202,146 @@
         viewType.SetSelection(0)
         viewType.Bind(wx.EVT_CHOICE, self.OnLookAt)
         # self.win['lookAt'] = viewType.GetId()
-        viewSizer.Add(item=viewType, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL,
-                      border=5)
-
-        reset = wx.Button(panel, id=wx.ID_ANY, label=_("Reset"))
+        viewSizer.Add(item = viewType,
+                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+        
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
         reset.SetToolTipString(_("Reset to default view"))
         # self.win['reset'] = reset.GetId()
         reset.Bind(wx.EVT_BUTTON, self.OnResetView)
-
-        viewSizer.Add(item=reset, proportion=1,
-                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT,
-                      border=5)
-
-        gridSizer.AddGrowableCol(3)
-        gridSizer.Add(item=viewSizer, pos=(4, 0), span=(1, 2),
-                      flag=wx.EXPAND)
-
+        
+        viewSizer.Add(item = wx.Size(-1, -1), proportion = 1,
+                      flag = wx.EXPAND)
+        viewSizer.Add(item = reset, proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_RIGHT,
+                      border = 5)
+        
+        gridSizer.AddGrowableCol(2)
+        gridSizer.Add(item = viewSizer, pos = (4, 0), span = (1, 2),
+                      flag = wx.EXPAND)
+        
         # body
-        pageSizer.Add(item=gridSizer, proportion=1,
-                      flag=wx.EXPAND | wx.ALL,
-                      border=5)
-
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 2)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Image Appearance")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # background color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Background color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'view',
+                                                            subkey = ['background', 'color']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['view']['bgcolor'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnBgColor)
+        gridSizer.Add(item = color, pos = (0, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
+                      border = 3)
+        
         panel.SetSizer(pageSizer)
+        
+        return panel
 
-        return panel.GetBestSize()
+    def _createDataPage(self):
+        """!Create data (surface, vector, volume) settings page"""
+        if globalvar.hasAgw:
+            self.notebookData = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                                agwStyle = globalvar.FNPageDStyle)
+        else:
+            self.notebookData = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                                style = globalvar.FNPageDStyle)
+        
+        # surface page
+        self.notebookData.AddPage(page = self._createSurfacePage(),
+                                  text = " %s " % _("Surface"))
+        self.EnablePage('surface', False)
+        
+        # vector page
+        self.notebookData.AddPage(page = self._createVectorPage(),
+                                  text = " %s " % _("Vector"))
+        self.EnablePage('vector', False)
+        
+        # volume page
+        self.notebookData.AddPage(page = self._createVolumePage(),
+                                  text = " %s " % _("Volume"))
+        self.EnablePage('volume', False)
+        
+        return self.notebookData
+    
+    def _createAppearancePage(self):
+        """!Create data (surface, vector, volume) settings page"""
+        if globalvar.hasAgw:
+            self.notebookAppearance = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                                      agwStyle = globalvar.FNPageDStyle)
+        else:
+            self.notebookAppearance = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                                      style = globalvar.FNPageDStyle)
+        
+        # light page
+        self.notebookAppearance.AddPage(page = self._createLightPage(),
+                                        text = " %s " % _("Lighting"))
+    
+        # fringe page
+        self.notebookAppearance.AddPage(page = self._createFringePage(),
+                                        text = " %s " % _("Fringe"))
+        self.EnablePage('fringe', False)
 
-    def __createSurfacePage(self):
-        """Create view settings page"""
-        panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
-        self.page['surface'] = {}
-        self.page['surface']['id'] = -1
-        self.page['surface']['panel'] = panel.GetId()
-
-        # panel = scrolled.ScrolledPanel(parent=self.notebook, id=wx.ID_ANY)
-        # panel.SetupScrolling(scroll_x=True, scroll_y=True)
-
+        return self.notebookAppearance
+    
+    def _createSurfacePage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['surface'] = { 'id' : 0,
+                                 'panel' : panel.GetId(),
+                                 'notebook' : self.notebookData.GetId() }
         pageSizer = wx.BoxSizer(wx.VERTICAL)
-
+        
         self.win['surface'] = {}
+        
+        # selection
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Raster map")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        rmaps = gselect.Select(parent = panel, type = 'raster',
+                               onPopup = self.GselectOnPopup)
+        rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster)
+        self.win['surface']['map'] = rmaps.GetId()
+        desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['surface']['desc'] = desc.GetId()
+        boxSizer.Add(item = rmaps, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        boxSizer.Add(item = desc, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         #
         # surface attributes
         #
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Surface attributes")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Surface attributes")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
-
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
         # type 
         self.win['surface']['attr'] = {}
         row = 0
@@ -261,87 +352,88 @@
                            ('shine', _("Shininess")),
                            ('emit', _("Emission"))):
             self.win['surface'][code] = {} 
-            gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                             label=attrb + ':'),
-                          pos=(row, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-            use = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
+            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                             label = attrb + ':'),
+                          pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+            use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                              choices = [_("map")])
+            
             if code not in ('topo', 'color', 'shine'):
-                use.Insert(item=_("unset"), pos=0)
+                use.Insert(item = _("unset"), pos = 0)
                 self.win['surface'][code]['required'] = False
             else:
                 self.win['surface'][code]['required'] = True
             if code != 'mask':
-                use.Append(item=_('constant'))
+                use.Append(item = _('constant'))
             self.win['surface'][code]['use'] = use.GetId()
             use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
-            gridSizer.Add(item=use, flag=wx.ALIGN_CENTER_VERTICAL,
-                          pos=(row, 1))
-
-            map = gselect.Select(parent=panel, id=wx.ID_ANY,
-                                 # size=globalvar.DIALOG_GSELECT_SIZE,
-                                 size=(200, -1),
-                                 type="raster")
+            gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 1))
+            
+            map = gselect.Select(parent = panel, id = wx.ID_ANY,
+                                 # size = globalvar.DIALOG_GSELECT_SIZE,
+                                 size = (200, -1),
+                                 type = "raster")
             self.win['surface'][code]['map'] = map.GetId() - 1 # FIXME
             map.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
             # changing map topography not allowed
             if code == 'topo':
                 map.Enable(False)
-            gridSizer.Add(item=map, flag=wx.ALIGN_CENTER_VERTICAL,
-                          pos=(row, 2))
-
+            gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 2))
+            
             if code == 'color':
-                value = csel.ColourSelect(panel, id=wx.ID_ANY,
-                                          colour=UserSettings.Get(group='nviz', key='surface',
-                                                                  subkey=['color', 'value']))
+                value = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                          colour = (0,0,0),
+                                          size = globalvar.DIALOG_COLOR_SIZE)
                 value.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceMap)
             elif code == 'mask':
                 value = None
             else:
-                value = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                                    initial=0)
+                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                                    initial = 0)
                 if code == 'topo':
-                    value.SetRange(minVal=-1e9, maxVal=1e9)
+                    value.SetRange(minVal = -1e9, maxVal = 1e9)
                 elif code in ('shine', 'transp', 'emit'):
-                    value.SetRange(minVal=0, maxVal=255)
+                    value.SetRange(minVal = 0, maxVal = 255)
                 else:
-                    value.SetRange(minVal=0, maxVal=100)
+                    value.SetRange(minVal = 0, maxVal = 100)
                 value.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
             
             if value:
                 self.win['surface'][code]['const'] = value.GetId()
                 value.Enable(False)
-                gridSizer.Add(item=value, flag=wx.ALIGN_CENTER_VERTICAL,
-                              pos=(row, 3))
+                gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 3))
             else:
                 self.win['surface'][code]['const'] = None
-
-            self.SetMapObjUseMap(nvizType='surface',
-                                 attrb=code) # -> enable map / disable constant
+            
+            self.SetMapObjUseMap(nvizType = 'surface',
+                                 attrb = code) # -> enable map / disable constant
                 
             row += 1
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.ALL,
-                      border=5)
-
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
         #
         # draw
         #
         self.win['surface']['draw'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Draw")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Draw")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
-        gridSizer.AddGrowableCol(4)
-
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(6)
+        
         # mode
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Mode:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        mode = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Mode:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        mode = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
                           choices = [_("coarse"),
                                      _("fine"),
                                      _("both")])
@@ -349,507 +441,584 @@
         mode.SetName("selection")
         mode.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
         self.win['surface']['draw']['mode'] = mode.GetId()
-        gridSizer.Add(item=mode, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 1))
-
-        # resolution (mode)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Resolution:")),
-                      pos=(0, 2), flag=wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 1))
+        
+        # shading
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Shading:")),
+                      pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        shade = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                           choices = [_("flat"),
+                                      _("gouraud")])
+        shade.SetName("selection")
+        self.win['surface']['draw']['shading'] = shade.GetId()
+        shade.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
+        gridSizer.Add(item = shade, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 3))
+        
+        # set to all
+        all = wx.Button(panel, id = wx.ID_ANY, label = _("Set to all"))
+        all.SetToolTipString(_("Use draw settings for all loaded surfaces"))
+        all.Bind(wx.EVT_BUTTON, self.OnSurfaceModeAll)
+        gridSizer.Add(item = all, flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (0, 4), span = (1,2), border = 3 )
+        
+        # resolution coarse
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Coarse:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         resSizer = wx.BoxSizer(wx.HORIZONTAL)
-        resSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                        label=_("coarse:")),
-                     flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
-        resC = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=1,
-                           min=1,
-                           max=100)
+        resSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = _("res.")),
+                     flag = wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 
+                     border = 3)
+        resC = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = 6,
+                           min = 1,
+                           max = 100)
         resC.SetName("value")
+        resC.SetValue(6)
+        
         self.win['surface']['draw']['res-coarse'] = resC.GetId()
         resC.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
-        resSizer.Add(item=resC, flag=wx.ALL, border=3)
+        resSizer.Add(item = resC, flag = wx.ALL | wx.ALIGN_LEFT | 
+                      wx.ALIGN_CENTER_VERTICAL, border = 3)
+        gridSizer.Add(item = resSizer, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
         
-        resSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                        label=_("fine:")),
-                     flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
-        resF = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=1,
-                           min=1,
-                           max=100)
-        resF.SetName("value")
-        self.win['surface']['draw']['res-fine'] = resF.GetId()
-        resF.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
-        resSizer.Add(item=resF, flag=wx.ALL, border=3)
-
-        gridSizer.Add(item=resSizer, pos=(0, 3), span=(1, 2))
-
-        # style
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Coarse style:")),
-                      pos=(1, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        style = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
+        # Coarse style
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("style")),
+                      pos = (1, 2), flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
+        style = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                           choices = [_("wire"),
                                      _("surface")])
         style.SetName("selection")
         self.win['surface']['draw']['style'] = style.GetId()
         style.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
-        gridSizer.Add(item=style, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(1, 1))
-
-        # shading
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Shading:")),
-                      pos=(1, 2), flag=wx.ALIGN_CENTER_VERTICAL)
-        shade = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
-                           choices = [_("flat"),
-                                      _("gouraud")])
-        shade.SetName("selection")
-        self.win['surface']['draw']['shading'] = shade.GetId()
-        shade.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
-        gridSizer.Add(item=shade, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(1, 3))
-
+        gridSizer.Add(item = style, flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 3))
+        
         # color
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Wire color:")),
-                      pos=(2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        color = csel.ColourSelect(panel, id=wx.ID_ANY)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("wire color")),
+                      pos = (1, 4), flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_RIGHT | wx.LEFT, border = 3)
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetColour((136,136,136))
         color.SetName("colour")
         color.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceWireColor)
         self.win['surface']['draw']['wire-color'] = color.GetId()
-        gridSizer.Add(item=color, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(2, 1))
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
-
-        all = wx.Button(panel, id=wx.ID_ANY, label=_("Set to all"))
-        all.SetToolTipString(_("Use draw settings for all loaded surfaces"))
-        all.Bind(wx.EVT_BUTTON, self.OnSurfaceModeAll)
-        gridSizer.Add(item=all, flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
-                      pos=(2, 4))
-
+        gridSizer.Add(item = color, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT,
+                      pos = (1, 5))
+        
+        # resolution fine
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Fine:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        resSizer = wx.BoxSizer(wx.HORIZONTAL)
+        resSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = _("res.")),
+                     flag = wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 
+                     border = 3)
+        resF = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = 3,
+                           min = 1,
+                           max = 100)
+        resF.SetName("value")
+        resF.SetValue(3)
+        self.win['surface']['draw']['res-fine'] = resF.GetId()
+        resF.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
+        resSizer.Add(item = resF, flag = wx.ALL | wx.ALIGN_LEFT | 
+                      wx.ALIGN_CENTER_VERTICAL, border = 3)
+        gridSizer.Add(item = resSizer, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         #
         # mask
         #
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Mask")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Mask")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
-
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Mask zeros:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        elev = wx.CheckBox(parent=panel, id=wx.ID_ANY,
-                           label=_("by elevation"))
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Mask zeros:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        elev = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                           label = _("by elevation"))
         elev.Enable(False) # TODO: not implemented yet
-        gridSizer.Add(item=elev, pos=(0, 1))
-
-        color = wx.CheckBox(parent=panel, id=wx.ID_ANY,
-                           label=_("by color"))
+        gridSizer.Add(item = elev, pos = (0, 1))
+        
+        color = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                           label = _("by color"))
         color.Enable(False) # TODO: not implemented yet
-        gridSizer.Add(item=color, pos=(0, 2))
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
-
+        gridSizer.Add(item = color, pos = (0, 2))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         #
         # position
         #
         self.win['surface']['position'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Position")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Position")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
-
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        
         # position
-        axis = wx.Choice (parent=panel, id=wx.ID_ANY, size=(75, -1),
+        self._createControl(panel, data = self.win['surface'], name = 'position',
+                            range = (-10000, 10000),
+                            bind = (self.OnSurfacePosition, self.OnSurfacePosition, self.OnSurfacePosition))
+        
+        axis = wx.Choice (parent = panel, id = wx.ID_ANY, size = (75, -1),
                           choices = ["X",
                                      "Y",
                                      "Z"])
+        
+        self.win['surface']['position']['axis'] = axis.GetId()
         axis.SetSelection(0)
-        self.win['surface']['position']['axis'] = axis.GetId()
         axis.Bind(wx.EVT_CHOICE, self.OnSurfaceAxis)
-        gridSizer.Add(item=axis, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 0))
-        value = wx.Slider(parent=panel, id=wx.ID_ANY,
-                          value=0,
-                          minValue=-1e4,
-                          maxValue=1e4,
-                          style=wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \
-                              wx.SL_TOP | wx.SL_LABELS,
-                          size=(350, -1))
-        self.win['surface']['position']['pos'] = value.GetId()
-        value.Bind(wx.EVT_SCROLL, self.OnSurfacePosition)
-        gridSizer.Add(item=value, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 1))
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
         
+        pslide = self.FindWindowById(self.win['surface']['position']['slider'])
+        pspin = self.FindWindowById(self.win['surface']['position']['spin'])
+        
+        gridSizer.Add(item = axis, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        gridSizer.Add(item = pslide, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 1))
+        gridSizer.Add(item = pspin, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 2))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        box.SetSizer(boxSizer)
+        box.Layout()
+        
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         panel.SetSizer(pageSizer)
         
-        return panel.GetBestSize()
+        return panel
 
-    def __createVectorPage(self):
-        """Create view settings page"""
-        panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
-        self.page['vector'] = {}
-        self.page['vector']['id'] = -1
-        self.page['vector']['panel'] = panel.GetId()
-
+    def _createVectorPage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['vector'] = { 'id' : 1,
+                                'panel' : panel.GetId(),
+                                'notebook' : self.notebookData.GetId() }
         pageSizer = wx.BoxSizer(wx.VERTICAL)
-
+        
         self.win['vector'] = {}
-
-        #
-        # desc
-        #
-        desc = wx.StaticText(parent=panel, id=wx.ID_ANY,
-                             label="")
+        
+        # selection
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector map")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vmaps = gselect.Select(parent = panel, type = 'vector',
+                               onPopup = self.GselectOnPopup)
+        vmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetVector)
+        self.win['vector']['map'] = vmaps.GetId()
+        desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
         self.win['vector']['desc'] = desc.GetId()
-        pageSizer.Add(item=desc, proportion=0,
-                      flag=wx.EXPAND | wx.ALL,
-                      border=10)
-
+        boxSizer.Add(item = vmaps, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        boxSizer.Add(item = desc, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         #
         # vector lines
         #
         self.win['vector']['lines'] = {}
-
-        showLines = wx.CheckBox(parent=panel, id=wx.ID_ANY,
-                                label=_("Show vector lines"))
+        
+        showLines = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = _("Show vector lines"))
+        showLines.SetValue(True)
+        
         self.win['vector']['lines']['show'] = showLines.GetId()
         showLines.Bind(wx.EVT_CHECKBOX, self.OnVectorShow)
-
-        pageSizer.Add(item=showLines, proportion=0,
-                      flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
-
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Vector lines")))
+        
+        pageSizer.Add(item = showLines, proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector lines")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
-
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        
         # width
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Width:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        width = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                            initial=1,
-                            min=1,
-                            max=100)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Line:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("width")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_RIGHT)
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 1,
+                            min = 0,
+                            max = 100)
+        width.SetValue(1)
         self.win['vector']['lines']['width'] = width.GetId()
         width.Bind(wx.EVT_SPINCTRL, self.OnVectorLines)
-        gridSizer.Add(item=width, pos=(0, 1),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        gridSizer.AddGrowableCol(2)
-
+        gridSizer.Add(item = width, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        
         # color
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Color:")),
-                      pos=(0, 3), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        color = csel.ColourSelect(panel, id=wx.ID_ANY,
-                                  colour=UserSettings.Get(group='nviz', key='vector',
-                                                          subkey=['lines', 'color']))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("color")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = (0,0,0),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
         self.win['vector']['lines']['color'] = color.GetId()
         color.Bind(csel.EVT_COLOURSELECT, self.OnVectorLines)
 
-        gridSizer.Add(item=color, pos=(0, 4))
-
-        gridSizer.AddGrowableCol(5)
-
+        gridSizer.Add(item = color, pos = (0, 4), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_LEFT)
+        
         # display
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Display:")),
-                      pos=(0, 6), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        display = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("display")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        
+        display = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                              choices = [_("on surface"),
                                         _("flat")])
         self.win['vector']['lines']['flat'] = display.GetId()
         display.Bind(wx.EVT_CHOICE, self.OnVectorDisplay)
-
-        gridSizer.Add(item=display, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 7))
-
+        
+        gridSizer.Add(item = display, flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_LEFT, pos = (1, 2), span = (1,2))
+        
         # height
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Height above surface:")),
-                      pos=(1, 0), flag=wx.ALIGN_CENTER_VERTICAL,
-                      span=(1, 2))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height above surface:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL,
+                      span = (1, 3))
         
-        surface = wx.ComboBox(parent=panel, id=wx.ID_ANY, size=(250, -1),
-                              style=wx.CB_SIMPLE | wx.CB_READONLY,
-                              choices=[])
+        surface = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (250, -1),
+                              style = wx.CB_SIMPLE | wx.CB_READONLY,
+                              choices = [])
         surface.Bind(wx.EVT_COMBOBOX, self.OnVectorSurface)
         self.win['vector']['lines']['surface'] = surface.GetId()
-        gridSizer.Add(item=surface, 
-                      pos=(1, 2), span=(1, 6),
-                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-
-        self.CreateControl(panel, dict=self.win['vector']['lines'], name='height', size=300,
-                           range=(0, 1000),
-                           bind=(self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightSpin))
-        gridSizer.Add(item=self.FindWindowById(self.win['vector']['lines']['height']['slider']),
-                      pos=(2, 2), span=(1, 6))
-        gridSizer.Add(item=self.FindWindowById(self.win['vector']['lines']['height']['spin']),
-                      pos=(3, 4),
-                      flag=wx.ALIGN_CENTER)
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                     flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
-
+        gridSizer.Add(item = surface, 
+                      pos = (2, 3), span = (1, 6),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        self._createControl(panel, data = self.win['vector']['lines'], name = 'height', size = 300,
+                            range = (0, 1000),
+                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightSpin))
+        self.FindWindowById(self.win['vector']['lines']['height']['slider']).SetValue(0)
+        self.FindWindowById(self.win['vector']['lines']['height']['spin']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['slider']),
+                      pos = (3, 0), span = (1, 7))
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['spin']),
+                      pos = (3, 7),
+                      flag = wx.ALIGN_CENTER)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         #
         # vector points
         #
         self.win['vector']['points'] = {}
-
-        showPoints = wx.CheckBox(parent=panel, id=wx.ID_ANY,
-                                 label=_("Show vector points"))
+        
+        showPoints = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Show vector points"))
+        showPoints.SetValue(True)
         self.win['vector']['points']['show'] = showPoints.GetId()
         showPoints.Bind(wx.EVT_CHECKBOX, self.OnVectorShow)
-
-        pageSizer.Add(item=showPoints, proportion=0,
-                      flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
-
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Vector points")))
+        
+        pageSizer.Add(item = showPoints, proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector points")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
-
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        
         # icon size
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Icon size:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        isize = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                            initial=1,
-                            min=1,
-                            max=1e6)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Icon:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("size")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        
+        isize = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 1,
+                            min = 1,
+                            max = 1e6)
         isize.SetName('value')
+        isize.SetValue(100)
         self.win['vector']['points']['size'] = isize.GetId()
         isize.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints)
         isize.Bind(wx.EVT_TEXT, self.OnVectorPoints)
-        gridSizer.Add(item=isize, pos=(0, 1),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = isize, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("color")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        icolor.SetName("color")
+        icolor.SetColour((0,0,255))
+        self.win['vector']['points']['color'] = icolor.GetId()
+        icolor.Bind(csel.EVT_COLOURSELECT, self.OnVectorPoints)
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_LEFT,
+                      pos = (0, 4))
 
         # icon width
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("width:")),
-                      pos=(0, 2), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        iwidth = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                             initial=1,
-                             min=1,
-                             max=1e6)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("width")),
+                      pos = (0, 5), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        
+        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                             initial = 1,
+                             min = 1,
+                             max = 1e6)
         iwidth.SetName('value')
+        iwidth.SetValue(100)
         self.win['vector']['points']['width'] = iwidth.GetId()
         iwidth.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints)
         iwidth.Bind(wx.EVT_TEXT, self.OnVectorPoints)
-        gridSizer.Add(item=iwidth, pos=(0, 3),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
+        gridSizer.Add(item = iwidth, pos = (0, 6),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        
         # icon symbol
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("symbol:")),
-                      pos=(0, 4), flag=wx.ALIGN_CENTER_VERTICAL)
-        isym = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
-                          choices=UserSettings.Get(group='nviz', key='vector',
-                                                   subkey=['points', 'marker'], internal=True))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("symbol")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                          choices = UserSettings.Get(group = 'nviz', key = 'vector',
+                                                   subkey = ['points', 'marker'], internal = True))
         isym.SetName("selection")
         self.win['vector']['points']['marker'] = isym.GetId()
         isym.Bind(wx.EVT_CHOICE, self.OnVectorPoints)
-        gridSizer.Add(item=isym, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 5))
-
-        # icon color
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("color:")),
-                      pos=(0, 6), flag=wx.ALIGN_CENTER_VERTICAL)
-        icolor = csel.ColourSelect(panel, id=wx.ID_ANY)
-        icolor.SetName("color")
-        self.win['vector']['points']['color'] = icolor.GetId()
-        icolor.Bind(csel.EVT_COLOURSELECT, self.OnVectorPoints)
-        gridSizer.Add(item=icolor, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 7))
-
+        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 2), span = (1,2))
+        
         # high
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Height above surface:")),
-                      pos=(1, 0), flag=wx.ALIGN_CENTER_VERTICAL,
-                      span=(1, 2))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height above surface:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL,
+                      span = (1, 3))
         
-        surface = wx.ComboBox(parent=panel, id=wx.ID_ANY, size=(250, -1),
-                              style=wx.CB_SIMPLE | wx.CB_READONLY,
-                              choices=[])
+        surface = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (250, -1),
+                              style = wx.CB_SIMPLE | wx.CB_READONLY,
+                              choices = [])
         surface.Bind(wx.EVT_COMBOBOX, self.OnVectorSurface)
         self.win['vector']['points']['surface'] = surface.GetId()
-        gridSizer.Add(item=surface, 
-                      pos=(1, 2), span=(1, 6),
-                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        self.CreateControl(panel, dict=self.win['vector']['points'], name='height', size=300,
-                           range=(0, 1000),
-                           bind=(self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightSpin))
-        gridSizer.Add(item=self.FindWindowById(self.win['vector']['points']['height']['slider']),
-                      pos=(2, 2), span=(1, 6))
-        gridSizer.Add(item=self.FindWindowById(self.win['vector']['points']['height']['spin']),
-                      pos=(3, 4),
-                      flag=wx.ALIGN_CENTER)
-
+        gridSizer.Add(item = surface, 
+                      pos = (2, 3), span = (1, 5),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
         
-        boxSizer.Add(item=gridSizer, proportion=1,
-                     flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
-
-
+        self._createControl(panel, data = self.win['vector']['points'], name = 'height', size = 300,
+                            range = (0, 1000),
+                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightSpin))
+        
+        self.FindWindowById(self.win['vector']['points']['height']['slider']).SetValue(0)
+        self.FindWindowById(self.win['vector']['points']['height']['spin']).SetValue(0)
+        
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['slider']),
+                      pos = (3, 0), span = (1, 7))
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['spin']),
+                      pos = (3, 7),
+                      flag = wx.ALIGN_CENTER)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         panel.SetSizer(pageSizer)
 
-        return panel.GetBestSize()
+        return panel
 
-    def __createVolumePage(self):
-        """Create view settings page"""
-        panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
-        self.page['volume'] = {}
-        self.page['volume']['id'] = -1
-        self.page['volume']['panel'] = panel.GetId()
-        
+    def GselectOnPopup(self, ltype, exclude = False):
+        """Update gselect.Select() items"""
+        maps = list()
+        for layer in self.mapWindow.Map.GetListOfLayers(l_type = ltype):
+            maps.append(layer.GetName())
+        return maps, exclude
+    
+    def _createVolumePage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['volume'] = { 'id' : 2,
+                                'panel' : panel.GetId(),
+                                'notebook' : self.notebookData.GetId() }
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         
         self.win['volume'] = {}
-
+        
+        # selection
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("3D raster map")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        rmaps = gselect.Select(parent = panel, type = 'raster3D',
+                               onPopup = self.GselectOnPopup)
+        rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster3D)
+        self.win['volume']['map'] = rmaps.GetId()
+        desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['volume']['desc'] = desc.GetId()
+        boxSizer.Add(item = rmaps, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        boxSizer.Add(item = desc, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+                
         #
         # draw
         #
         self.win['volume']['draw'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Draw")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Draw")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
         gridSizer.AddGrowableCol(4)
-
+        
         # mode
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Mode:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        mode = wx.Choice (parent=panel, id=wx.ID_ANY, size=(150, -1),
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Mode:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        mode = wx.Choice (parent = panel, id = wx.ID_ANY, size = (150, -1),
                           choices = [_("isosurfaces"),
                                      _("slides")])
         mode.SetSelection(0)
         mode.SetName("selection")
         # mode.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
         self.win['volume']['draw']['mode'] = mode.GetId()
-        gridSizer.Add(item=mode, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 1))
-
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 1))
+        
         # shading
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Shading:")),
-                      pos=(0, 2), flag=wx.ALIGN_CENTER_VERTICAL)
-        shade = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Shading:")),
+                      pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        shade = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                            choices = [_("flat"),
                                       _("gouraud")])
         shade.SetName("selection")
         self.win['volume']['draw']['shading'] = shade.GetId()
         shade.Bind(wx.EVT_CHOICE, self.OnVolumeIsosurfMode)
-        gridSizer.Add(item=shade, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(0, 3))
-
+        gridSizer.Add(item = shade, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 3))
+        
         # resolution (mode)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Resolution:")),
-                      pos=(0, 4), flag=wx.ALIGN_CENTER_VERTICAL)
-        resol = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                            initial=1,
-                            min=1,
-                            max=100)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Resolution:")),
+                      pos = (0, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        resol = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 1,
+                            min = 1,
+                            max = 100)
         resol.SetName("value")
         self.win['volume']['draw']['resolution'] = resol.GetId()
         resol.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfResolution)
         resol.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfResolution)
-        gridSizer.Add(item=resol, pos=(0, 5))
+        gridSizer.Add(item = resol, pos = (0, 5))
         
-        boxSizer.Add(item=gridSizer, proportion=1,
-                     flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.ALL,
-                      border=5)
-
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
         #
         # manage isosurfaces
         #
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("List of isosurfaces")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("List of isosurfaces")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
-
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
         # list
-        isolevel = wx.CheckListBox(parent=panel, id=wx.ID_ANY,
-                                   size=(300, 150))
+        isolevel = wx.CheckListBox(parent = panel, id = wx.ID_ANY,
+                                   size = (300, 150))
         self.Bind(wx.EVT_CHECKLISTBOX, self.OnVolumeIsosurfCheck, isolevel)
         self.Bind(wx.EVT_LISTBOX, self.OnVolumeIsosurfSelect, isolevel)
-
+        
         self.win['volume']['isosurfs'] = isolevel.GetId()
-        gridSizer.Add(item=isolevel, pos=(0, 0), span=(4, 1))
+        gridSizer.Add(item = isolevel, pos = (0, 0), span = (4, 1))
         
         # buttons (add, delete, move up, move down)
-        btnAdd = wx.Button(parent=panel, id=wx.ID_ADD)
+        btnAdd = wx.Button(parent = panel, id = wx.ID_ADD)
         self.win['volume']['btnIsosurfAdd'] = btnAdd.GetId()
         btnAdd.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfAdd)
-        gridSizer.Add(item=btnAdd,
-                      pos=(0, 1))
-        btnDelete = wx.Button(parent=panel, id=wx.ID_DELETE)
+        gridSizer.Add(item = btnAdd,
+                      pos = (0, 1))
+        btnDelete = wx.Button(parent = panel, id = wx.ID_DELETE)
         self.win['volume']['btnIsosurfDelete'] = btnDelete.GetId()
         btnDelete.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfDelete)
         btnDelete.Enable(False)
-        gridSizer.Add(item=btnDelete,
-                      pos=(1, 1))
-        btnMoveUp = wx.Button(parent=panel, id=wx.ID_UP)
+        gridSizer.Add(item = btnDelete,
+                      pos = (1, 1))
+        btnMoveUp = wx.Button(parent = panel, id = wx.ID_UP)
         self.win['volume']['btnIsosurfMoveUp'] = btnMoveUp.GetId()
         btnMoveUp.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfMoveUp)
         btnMoveUp.Enable(False)
-        gridSizer.Add(item=btnMoveUp,
-                      pos=(2, 1))
-        btnMoveDown = wx.Button(parent=panel, id=wx.ID_DOWN)
+        gridSizer.Add(item = btnMoveUp,
+                      pos = (2, 1))
+        btnMoveDown = wx.Button(parent = panel, id = wx.ID_DOWN)
         self.win['volume']['btnIsosurfMoveDown'] = btnMoveDown.GetId()
         btnMoveDown.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfMoveDown)
         btnMoveDown.Enable(False)
-        gridSizer.Add(item=btnMoveDown,
-                      pos=(3, 1))
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                     flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
+        gridSizer.Add(item = btnMoveDown,
+                      pos = (3, 1))
         
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         #
         # isosurface attributes
         #
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Isosurface attributes")))
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Isosurface attributes")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
-
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
         self.win['volume']['attr'] = {}
         row = 0
         for code, attrb in (('topo', _("Topography level")),
@@ -860,44 +1029,44 @@
                             ('emit', _("Emission"))):
             self.win['volume'][code] = {} 
             # label
-            gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                             label=attrb + ':'),
-                          pos=(row, 0), flag=wx.ALIGN_CENTER_VERTICAL)
+            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                             label = attrb + ':'),
+                          pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
             if code != 'topo':
-                use = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
+                use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                                  choices = [_("map")])
             else:
                 use = None
             # check for required properties
             if code not in ('topo', 'color', 'shine'):
-                use.Insert(item=_("unset"), pos=0)
+                use.Insert(item = _("unset"), pos = 0)
                 self.win['volume'][code]['required'] = False
             else:
                 self.win['volume'][code]['required'] = True
             if use and code != 'mask':
-                use.Append(item=_('constant'))
+                use.Append(item = _('constant'))
             if use:
                 self.win['volume'][code]['use'] = use.GetId()
                 use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
-                gridSizer.Add(item=use, flag=wx.ALIGN_CENTER_VERTICAL,
-                              pos=(row, 1))
+                gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 1))
             
             if code != 'topo':
-                map = gselect.Select(parent=panel, id=wx.ID_ANY,
-                                     # size=globalvar.DIALOG_GSELECT_SIZE,
-                                     size=(200, -1),
-                                     type="grid3")
+                map = gselect.Select(parent = panel, id = wx.ID_ANY,
+                                     # size = globalvar.DIALOG_GSELECT_SIZE,
+                                     size = (200, -1),
+                                     type = "grid3")
                 self.win['volume'][code]['map'] = map.GetId() - 1 # FIXME
                 map.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
-                gridSizer.Add(item=map, flag=wx.ALIGN_CENTER_VERTICAL,
-                              pos=(row, 2))
+                gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 2))
             else:
                 map = None
             
             if code == 'color':
-                value = csel.ColourSelect(panel, id=wx.ID_ANY,
-                                          colour=UserSettings.Get(group='nviz', key='volume',
-                                                                  subkey=['color', 'value']))
+                value = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                          colour = (0,0,0),
+                                          size = globalvar.DIALOG_COLOR_SIZE)
                 value.Bind(csel.EVT_COLOURSELECT, self.OnVolumeIsosurfMap)
             elif code == 'mask':
                 value = None
@@ -906,379 +1075,300 @@
                     size = (200, -1)
                 else:
                     size = (65, -1)
-                value = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=size,
-                                    initial=0)
+                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = size,
+                                    initial = 0)
                 if code == 'topo':
-                    value.SetRange(minVal=-1e9, maxVal=1e9)
+                    value.SetRange(minVal = -1e9, maxVal = 1e9)
                 elif code in ('shine', 'transp', 'emit'):
-                    value.SetRange(minVal=0, maxVal=255)
+                    value.SetRange(minVal = 0, maxVal = 255)
                 else:
-                    value.SetRange(minVal=0, maxVal=100)
+                    value.SetRange(minVal = 0, maxVal = 100)
                 value.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfMap)
                 value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
             
             if value:
                 self.win['volume'][code]['const'] = value.GetId()
                 if code == 'topo':
-                    gridSizer.Add(item=value, flag=wx.ALIGN_CENTER_VERTICAL,
-                                  pos=(row, 2))
+                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                                  pos = (row, 2))
                 else:
                     value.Enable(False)
-                    gridSizer.Add(item=value, flag=wx.ALIGN_CENTER_VERTICAL,
-                                  pos=(row, 3))
+                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                                  pos = (row, 3))
             else:
                 self.win['volume'][code]['const'] = None
             
             if code != 'topo':
-                self.SetMapObjUseMap(nvizType='volume',
-                                     attrb=code) # -> enable map / disable constant
-                
+                self.SetMapObjUseMap(nvizType = 'volume',
+                                     attrb = code) # -> enable map / disable constant
+            
             row += 1
         
-        boxSizer.Add(item=gridSizer, proportion=1,
-                     flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
         
         panel.SetSizer(pageSizer)
         
-        return panel.GetBestSize()
+        return panel
     
-    def __createSettingsPage(self):
-        """Create settings page"""
-        panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
-        self.notebook.AddPage(page=panel,
-                              text=" %s " % _("Settings"))
+    def _createLightPage(self):
+        """!Create light page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
         
+        self.page['light'] = { 'id' : 0, 
+                               'notebook' : self.notebookAppearance.GetId() }
+        self.win['light'] = {}
+        
         pageSizer = wx.BoxSizer(wx.VERTICAL)
-
-        self.win['settings'] = {}
-
-        #
-        # general
-        #
-        self.win['settings']['general'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("General")))
+        
+        show = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                           label = _("Show light model"))
+        show.Bind(wx.EVT_CHECKBOX, self.OnShowLightModel)
+        pageSizer.Add(item = show, proportion = 0,
+                      flag = wx.ALL, border = 3)
+        surface = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                              label = _("Follow source viewpoint"))
+        pageSizer.Add(item = surface, proportion = 0,
+                      flag = wx.ALL, border = 3)
+        
+        # position
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Light source position")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
-
-        # background color
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Background color:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-
-        color = csel.ColourSelect(panel, id=wx.ID_ANY,
-                                  colour=UserSettings.Get(group='nviz', key='settings',
-                                                          subkey=['general', 'bgcolor']))
-        self.win['settings']['general']['bgcolor'] = color.GetId()
-        color.Bind(csel.EVT_COLOURSELECT, self.OnBgColor)
-        gridSizer.Add(item=color, pos=(0, 1))
-
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.ALL,
-                      border=5)
-
-        #
-        # view
-        #
-        self.win['settings']['view'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("View")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
-
-
-        # perspective
-        self.win['settings']['view']['persp'] = {}
-        pvals = UserSettings.Get(group='nviz', key='view', subkey='persp')
-        ipvals = UserSettings.Get(group='nviz', key='view', subkey='persp', internal=True)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Perspective:")),
-                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(value)")),
-                      pos=(0, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        pval = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=pvals['value'],
-                           min=ipvals['min'],
-                           max=ipvals['max'])
-        self.win['settings']['view']['persp']['value'] = pval.GetId()
-        gridSizer.Add(item=pval, pos=(0, 2),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(step)")),
-                      pos=(0, 3), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        pstep = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=pvals['step'],
-                           min=ipvals['min'],
-                           max=ipvals['max']-1)
-        self.win['settings']['view']['persp']['step'] = pstep.GetId()
-        gridSizer.Add(item=pstep, pos=(0, 4),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        # position
-        self.win['settings']['view']['pos'] = {}
-        posvals = UserSettings.Get(group='nviz', key='view', subkey='pos')
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Position:")),
-                      pos=(1, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(x)")),
-                      pos=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        px = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=posvals['x'] * 100,
-                           min=0,
-                           max=100)
-        self.win['settings']['view']['pos']['x'] = px.GetId()
-        gridSizer.Add(item=px, pos=(1, 2),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label="(y)"),
-                      pos=(1, 3), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        py = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=posvals['y'] * 100,
-                           min=0,
-                           max=100)
-        self.win['settings']['view']['pos']['y'] = py.GetId()
-        gridSizer.Add(item=py, pos=(1, 4),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
+        
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        posSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("W")),
+                     pos = (1, 0), flag = wx.ALIGN_CENTER)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("N")),
+                     pos = (0, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_BOTTOM)
+        pos = LightPositionWindow(panel, id = wx.ID_ANY, size = (175, 175),
+                                  mapwindow = self.mapWindow)
+        self.win['light']['position'] = pos.GetId()
+        posSizer.Add(item = pos,
+                     pos = (1, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("S")),
+                     pos = (2, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_TOP)
+        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("E")),
+                     pos = (1, 2), flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = posSizer, pos = (0, 0))
+        
         # height
-        self.win['settings']['view']['height'] = {}
-        hvals = UserSettings.Get(group='nviz', key='view', subkey='height')
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Height:")),
-                      pos=(2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(step)")),
-                      pos=(2, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        hstep = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=hvals['step'],
-                           min=1,
-                           max=1e6)
-        self.win['settings']['view']['height']['step'] = hstep.GetId()
-        gridSizer.Add(item=hstep, pos=(2, 2),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        # twist
-        self.win['settings']['view']['twist'] = {}
-        tvals = UserSettings.Get(group='nviz', key='view', subkey='twist')
-        itvals = UserSettings.Get(group='nviz', key='view', subkey='twist', internal=True)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Twist:")),
-                      pos=(3, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(value)")),
-                      pos=(3, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        tval = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=tvals['value'],
-                           min=itvals['min'],
-                           max=itvals['max'])
-        self.win['settings']['view']['twist']['value'] = tval.GetId()
-        gridSizer.Add(item=tval, pos=(3, 2),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(step)")),
-                      pos=(3, 3), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        tstep = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=tvals['step'],
-                           min=itvals['min'],
-                           max=itvals['max']-1)
-        self.win['settings']['view']['twist']['step'] = tstep.GetId()
-        gridSizer.Add(item=tstep, pos=(3, 4),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        # z-exag
-        self.win['settings']['view']['z-exag'] = {}
-        zvals = UserSettings.Get(group='nviz', key='view', subkey='z-exag')
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Z-exag:")),
-                      pos=(4, 0), flag=wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(value)")),
-                      pos=(4, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        zval = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=zvals['value'],
-                           min=-1e6,
-                           max=1e6)
-        self.win['settings']['view']['z-exag']['value'] = zval.GetId()
-        gridSizer.Add(item=zval, pos=(4, 2),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("(step)")),
-                      pos=(4, 3), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-
-        zstep = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                           initial=zvals['step'],
-                           min=-1e6,
-                           max=1e6)
-        self.win['settings']['view']['z-exag']['step'] = zstep.GetId()
-        gridSizer.Add(item=zstep, pos=(4, 4),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
+        self._createControl(panel, data = self.win['light'], name = 'z', sliderHor = False,
+                            range = (0, 100),
+                            bind = (self.OnLightChange, None, self.OnLightChange))
         
-        #
-        # vector lines
-        #
-        self.win['settings']['vector'] = {}
-        self.win['settings']['vector']['lines'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Vector lines")))
+        heightSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:")),
+                      pos = (0, 0), flag = wx.ALIGN_LEFT, span = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['slider']),
+                        flag = wx.ALIGN_RIGHT, pos = (1, 0))
+        heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['spin']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
+                        wx.BOTTOM | wx.RIGHT, pos = (1, 1))
+        
+        gridSizer.Add(item = heightSizer, pos = (0, 1), flag = wx.ALIGN_RIGHT)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 2)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        # position
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Light color and intensity")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
 
-        # show
-        row = 0
-        showLines = wx.CheckBox(parent=panel, id=wx.ID_ANY,
-                                label=_("Show lines"))
-        self.win['settings']['vector']['lines']['show'] = showLines.GetId()
-        showLines.SetValue(UserSettings.Get(group='nviz', key='vector',
-                                            subkey=['lines', 'show']))
-        gridSizer.Add(item=showLines, pos=(row, 0))
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'light',
+                                                            subkey = 'color'),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.Bind(csel.EVT_COLOURSELECT, self.OnLightColor)
+        gridSizer.Add(item = color, pos = (0, 2))
 
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Brightness:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['light'], name = 'bright', size = 300,
+                            range = (0, 100),
+                            bind = (self.OnLightValue, None, self.OnLightValue))
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['slider']),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['spin']),
+                      pos = (1, 2),
+                      flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Ambient:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['light'], name = 'ambient', size = 300,
+                            range = (0, 100),
+                            bind = (self.OnLightValue, None, self.OnLightValue))
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['slider']),
+                      pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['spin']),
+                      pos = (2, 2),
+                      flag = wx.ALIGN_CENTER)
 
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 2)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        # reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+        # reset.SetToolTipString(_("Reset to default view"))
+        # # self.win['reset'] = reset.GetId()
+        # reset.Bind(wx.EVT_BUTTON, self.OnResetView)
+        
+        # viewSizer.Add(item = reset, proportion = 1,
+        #               flag = wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT,
+        #               border = 5)
+        
+        # gridSizer.AddGrowableCol(3)
+        # gridSizer.Add(item = viewSizer, pos = (4, 0), span = (1, 2),
+        #               flag = wx.EXPAND)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
 
-        #
-        # vector points
-        #
-        self.win['settings']['vector']['points'] = {}
-        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
-                            label=" %s " % (_("Vector points")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap=3, hgap=5)
+    def _createFringePage(self):
+        """!Create fringe page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        
+        self.page['fringe'] = { 'id' : 1,
+                                'notebook' : self.notebookAppearance.GetId() }
+        self.win['fringe'] = {}
 
-        # show
-        row = 0
-        showPoints = wx.CheckBox(parent=panel, id=wx.ID_ANY,
-                                 label=_("Show points"))
-        showPoints.SetValue(UserSettings.Get(group='nviz', key='vector',
-                                             subkey=['points', 'show']))
-        self.win['settings']['vector']['points']['show'] = showPoints.GetId()
-        gridSizer.Add(item=showPoints, pos=(row, 0), span=(1, 8))
-
-        # icon size
-        row += 1 
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Size:")),
-                      pos=(row, 0), flag=wx.ALIGN_CENTER_VERTICAL)
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
         
-        isize = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                            initial=100,
-                            min=1,
-                            max=1e6)
-        self.win['settings']['vector']['points']['size'] = isize.GetId()
-        isize.SetValue(UserSettings.Get(group='nviz', key='vector',
-                                        subkey=['points', 'size']))
-        gridSizer.Add(item=isize, pos=(row, 1),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
+        # selection
+        rbox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Surface")))
+        rboxSizer = wx.StaticBoxSizer(rbox, wx.VERTICAL)
+        rmaps = gselect.Select(parent = panel, type = 'raster',
+                               onPopup = self.GselectOnPopup)
+        rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetSurface)
+        self.win['fringe']['map'] = rmaps.GetId()
+        rboxSizer.Add(item = rmaps, proportion = 0,
+                      flag = wx.ALL,
+                      border = 3)
+        pageSizer.Add(item = rboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        ebox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Edges with fringe")))
+        eboxSizer = wx.StaticBoxSizer(ebox, wx.HORIZONTAL)
+        for edge in [(_("N && W"), "nw"),
+                     (_("N && E"), "ne"),
+                     (_("S && W"), "sw"),
+                     (_("S && E"), "se")]:
+            chkbox = wx.CheckBox(parent = panel,
+                                 label = edge[0],
+                                 name = edge[1])
+            self.win['fringe'][edge[1]] = chkbox.GetId()
+            eboxSizer.Add(item = chkbox, proportion = 0,
+                         flag = wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT, border = 5)
+            chkbox.Bind(wx.EVT_CHECKBOX, self.OnFringe)
+        
+        pageSizer.Add(item = eboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
 
-
-        # icon width
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Width:")),
-                      pos=(row, 2), flag=wx.ALIGN_CENTER_VERTICAL)
+        sbox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Settings")))
+        sboxSizer = wx.StaticBoxSizer(sbox, wx.HORIZONTAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
         
-        iwidth = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(65, -1),
-                            initial=2,
-                            min=1,
-                            max=1e6)
-        self.win['settings']['vector']['points']['width'] = isize.GetId()
-        iwidth.SetValue(UserSettings.Get(group='nviz', key='vector',
-                                         subkey=['points', 'width']))
-        gridSizer.Add(item=iwidth, pos=(row, 3),
-                      flag=wx.ALIGN_CENTER_VERTICAL)
-
-        # icon symbol
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Marker:")),
-                      pos=(row, 4), flag=wx.ALIGN_CENTER_VERTICAL)
-        isym = wx.Choice (parent=panel, id=wx.ID_ANY, size=(100, -1),
-                          choices=UserSettings.Get(group='nviz', key='vector',
-                                                   subkey=['points', 'marker'], internal=True))
-        isym.SetName("selection")
-        self.win['settings']['vector']['points']['marker'] = isym.GetId()
-        isym.SetSelection(UserSettings.Get(group='nviz', key='vector',
-                                           subkey=['points', 'marker']))
-        gridSizer.Add(item=isym, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(row, 5))
-
-        # icon color
-        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
-                                         label=_("Color:")),
-                      pos=(row, 6), flag=wx.ALIGN_CENTER_VERTICAL)
-        icolor = csel.ColourSelect(panel, id=wx.ID_ANY)
-        icolor.SetName("color")
-        self.win['settings']['vector']['points']['color'] = icolor.GetId()
-        icolor.SetColour(UserSettings.Get(group='nviz', key='vector',
-                                          subkey=['points', 'color']))
-        gridSizer.Add(item=icolor, flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(row, 7))
-
-        boxSizer.Add(item=gridSizer, proportion=1,
-                  flag=wx.ALL | wx.EXPAND, border=3)
-        pageSizer.Add(item=boxSizer, proportion=0,
-                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border=5)
-
-        #
-        # buttons
-        #
-        btnDefault = wx.Button(panel, wx.ID_CANCEL, label=_("Default"))
-        btnSave = wx.Button(panel, wx.ID_SAVE)
-        btnApply = wx.Button(panel, wx.ID_APPLY)
-
-        btnDefault.Bind(wx.EVT_BUTTON, self.OnDefault)
-        btnDefault.SetToolTipString(_("Restore default settings"))
-        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
-        btnApply.SetToolTipString(_("Apply changes for the current session"))
-        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
-        btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
-        btnSave.SetDefault()
-
-        btnSizer = wx.StdDialogButtonSizer()
-        btnSizer.AddButton(btnDefault)
-        btnSizer.AddButton(btnApply)
-        btnSizer.AddButton(btnSave)
-        btnSizer.Realize()
-
-        pageSizer.Add(item=btnSizer, proportion=1,
-                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM,
-                      border=5)
-
+        # elevation
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Elevation of fringe from bottom:")),
+                      pos = (0, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        spin = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                           size = (65, -1), min = -1e6, max = 1e6)
+        spin.SetValue(UserSettings.Get(group = 'nviz', key = 'fringe', subkey = 'elev'))
+        spin.Bind(wx.EVT_SPINCTRL, self.OnFringe)
+        self.win['fringe']['elev'] = spin.GetId()
+        gridSizer.Add(item = spin, pos = (0, 1))
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Color:")),
+                      pos = (1, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetColour(UserSettings.Get(group = 'nviz', key = 'fringe',
+                                         subkey = 'color'))
+        color.Bind(csel.EVT_COLOURSELECT, self.OnFringe)
+        self.win['fringe']['color'] = color.GetId()
+        gridSizer.Add(item = color, pos = (1, 1))
+        
+        sboxSizer.Add(item = gridSizer, proportion = 1,
+                      flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = sboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
         panel.SetSizer(pageSizer)
 
-        return panel.GetBestSize()
+        return panel
 
-    def CreateControl(self, parent, dict, name, range, bind, sliderHor=True, size=200):
-        """Add control (Slider + SpinCtrl)"""
-        dict[name] = {}
+    def GetLayerData(self, nvizType):
+        """!Get nviz data"""
+        name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
+        
+        if nvizType == 'surface' or nvizType == 'fringe':
+            return self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
+        elif nvizType == 'vector':
+            return self.mapWindow.GetLayerByName(name, mapType = 'vector', dataType = 'nviz')
+        elif nvizType == 'volume':
+            return self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
+        
+        return None
+    
+    def OnFringe(self, event):
+        """!Show/hide fringe"""
+        enabled = event.IsChecked()
+        win = self.FindWindowById(event.GetId())
+        
+        data = self.GetLayerData('fringe')['surface']
+        
+        sid = data['object']['id']
+        elev = self.FindWindowById(self.win['fringe']['elev']).GetValue()
+        color = self.FindWindowById(self.win['fringe']['color']).GetValue()
+        
+        self._display.SetFringe(sid, color, elev,
+                                self.FindWindowById(self.win['fringe']['nw']).IsChecked(),
+                                self.FindWindowById(self.win['fringe']['ne']).IsChecked(),
+                                self.FindWindowById(self.win['fringe']['sw']).IsChecked(),
+                                self.FindWindowById(self.win['fringe']['se']).IsChecked())
+        self.mapWindow.Refresh(False)
+        
+    def OnScroll(self, event, win, data):
+        """!Generic scrolling handler"""
+        winName = self.__GetWindowName(win, event.GetId())
+        if not winName:
+            return
+        data[winName] = event.GetInt()
+        for w in win[winName].itervalues():
+            self.FindWindowById(w).SetValue(data[winName])
+        
+        event.Skip()
+        
+    def _createControl(self, parent, data, name, range, bind = (None, None, None),
+                       sliderHor = True, size = 200):
+        """!Add control (Slider + SpinCtrl)"""
+        data[name] = dict()
         if sliderHor:
             style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \
                 wx.SL_BOTTOM
@@ -1287,240 +1377,278 @@
             style = wx.SL_VERTICAL | wx.SL_AUTOTICKS | \
                 wx.SL_INVERSE
             sizeW = (-1, size)
-        try:
-            val = self.mapWindow.view[name]['value']
-        except KeyError:
-            val=-1
-        slider = wx.Slider(parent=parent, id=wx.ID_ANY,
-                           value=val,
-                           minValue=range[0],
-                           maxValue=range[1],
-                           style=style,
-                           size=sizeW)
+        
+        slider = wx.Slider(parent = parent, id = wx.ID_ANY,
+                           minValue = range[0],
+                           maxValue = range[1],
+                           style = style,
+                           size = sizeW)
         slider.SetName('slider')
-        slider.Bind(wx.EVT_SCROLL, bind[0])
-        slider.Bind(wx.EVT_SCROLL_CHANGED, bind[1])
-        dict[name]['slider'] = slider.GetId()
-
-        spin = wx.SpinCtrl(parent=parent, id=wx.ID_ANY, size=(65, -1),
-                           initial=val,
-                           min=range[0],
-                           max=range[1])
-        #         spin = wx.SpinButton(parent=parent, id=wx.ID_ANY)
-        #         spin.SetValue (self.mapWindow.view[name]['value'])
-        #         spin.SetRange(self.mapWindow.view[name]['min'],
-        #                      self.mapWindow.view[name]['max'])
-
+        if bind[0]:
+            slider.Bind(wx.EVT_SCROLL, bind[0])
+        
+        # slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, bind[1])
+        if bind[1]:
+            slider.Bind(wx.EVT_SCROLL_CHANGED, bind[1]) # this only works in MSW
+        data[name]['slider'] = slider.GetId()
+        
+        spin = wx.SpinCtrl(parent = parent, id = wx.ID_ANY, size = (65, -1),
+                           min = range[0],
+                           max = range[1])
+        
         # no 'changed' event ... (FIXME)
         spin.SetName('spin')
-        spin.Bind(wx.EVT_SPINCTRL, bind[2])
-        ### spin.Bind(wx.EVT_TEXT, bind[2])
-        dict[name]['spin'] = spin.GetId()
+        if bind[2]:
+            spin.Bind(wx.EVT_SPINCTRL, bind[2])
+        
+        data[name]['spin'] = spin.GetId()
+        
+    def __GetWindowName(self, data, id):
+        for name in data.iterkeys():
+            if type(data[name]) is type({}):
+                for win in data[name].itervalues():
+                    if win == id:
+                        return name
+            else:
+                if data[name] == id:
+                    return name
+        
+        return None
 
     def UpdateSettings(self):
-        """Update dialog settings"""
+        """!Update view from settings values 
+        stored in self.mapWindow.view dictionary"""
         for control in ('height',
                         'persp',
                         'twist',
                         'z-exag'):
-            for win in self.win['view'][control].itervalues():
+            for win in self.win['view'][control].itervalues():                
                 if control == 'height':
-                    value = UserSettings.Get(group='nviz', key='view',
-                                             subkey=['height', 'value'], internal=True)
+                    value = UserSettings.Get(group = 'nviz', key = 'view',
+                                             subkey = ['height', 'value'], internal = True)
                 else:
-                    value = self.mapWindow.view[control]['value']
+                    try:
+                        value = self.mapWindow.view[control]['value']
+                    except KeyError:
+                        value = -1
+                
                 self.FindWindowById(win).SetValue(value)
+        
+        viewWin = self.FindWindowById(self.win['view']['position'])
+        x, y = viewWin.UpdatePos(self.mapWindow.view['position']['x'],
+                                 self.mapWindow.view['position']['y'])
+        viewWin.Draw(pos = (x, y), scale = True)
+        viewWin.Refresh(False)
+        
+        # bgcolor = self.FindWindowById(self.win['settings']['general']['bgcolor']).GetColour()
+        # self.OnBgColor(event = bgcolor)
+        self.Update()
+        
+        self.mapWindow.Refresh(eraseBackground = False)
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+    def OnShowLightModel(self, event):
+        """!Show light model"""
+        self._display.showLight = event.IsChecked()
+        self._display.DrawLightingModel()
+        
+    def OnLightChange(self, event):
+        """!Position of the light changed"""
+        winName = self.__GetWindowName(self.win['light'], event.GetId())
+        if not winName:
+            return
+        
+        val = event.GetInt()
+        self.mapWindow.light['position']['z'] = val / 100.
+        for win in self.win['light'][winName].itervalues():
+            self.FindWindowById(win).SetValue(val)
+        
+        event = wxUpdateLight()
+        wx.PostEvent(self.mapWindow, event)
+        
+        event.Skip()
 
-        self.FindWindowById(self.win['view']['pos']).Draw()
-        self.FindWindowById(self.win['view']['pos']).Refresh(False)
+    def OnLightColor(self, event):
+        """!Color of the light changed"""
+        self.mapWindow.light['color'] = event.GetValue()
         
-        self.Refresh(False)
+        event = wxUpdateLight()
+        wx.PostEvent(self.mapWindow, event)
+        
+        event.Skip()
+        
+    def OnLightValue(self, event):
+        """!Light brightness changed"""
+        data = self.mapWindow.light
+        self.OnScroll(event, self.win['light'], data)
+        
+        event = wxUpdateLight()
+        wx.PostEvent(self.mapWindow, event)
+        
+        event.Skip()
+        
+    def OnBgColor(self, event):
+        """!Background color changed"""
+        color = event.GetValue()
+        color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+        
+        self._display.SetBgColor(str(color))
+        
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
+            self.mapWindow.Refresh(False)
+        
+    def OnSetSurface(self, event):
+        """!Surface selected, currently used for fringes"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')['surface']
+        except:
+            self.EnablePage('fringe', False)
+            return
+        
+        layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
+        self.EnablePage('fringe', True)
+        
+    def OnSetRaster(self, event):
+        """!Raster map selected, update surface page"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')['surface']
+        except:
+            self.EnablePage('surface', False)
+            return
+        
+        layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
+        self.EnablePage('surface', True)
+        self.UpdateSurfacePage(layer, data, updateName = False)
+        
+    def OnSetVector(self, event):
+        """!Vector map selected, update properties page"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = 'vector', dataType = 'nviz')['vector']
+        except:
+            self.EnablePage('vector', False)
+            return
+        
+        layer = self.mapWindow.GetLayerByName(name, mapType = 'vector')
+        self.EnablePage('vector', True)
+        self.UpdateVectorPage(layer, data, updateName = False)
 
-    def __GetWindowName(self, dict, id):
-        for name in dict.iterkeys():
-            if type(dict[name]) is type({}):
-                for win in dict[name].itervalues():
-                    if win == id:
-                        return name
-            else:
-                if dict[name] == id:
-                    return name
-
-        return None
-
+    def OnSetRaster3D(self, event):
+        """!3D Raster map selected, update surface page"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')['volume']
+        except:
+            self.EnablePage('volume', False)
+            return
+        
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        self.EnablePage('volume', True)
+        self.UpdateVolumePage(layer, data, updateName = False)
+        
     def OnViewChange(self, event):
-        """Change view, render in quick mode"""
+        """!Change view, render in quick mode"""
         # find control
         winName = self.__GetWindowName(self.win['view'], event.GetId())
         if not winName:
             return
-
+        
         if winName == 'height':
             view = self.mapWindow.iview # internal
         else:
             view = self.mapWindow.view
-
+        
+        if winName == 'z-exag' and event.GetInt() >= 0:
+            self.PostViewEvent(zExag = True)
+        else:
+            self.PostViewEvent(zExag = False)
+        
         view[winName]['value'] = event.GetInt()
-
+        
         for win in self.win['view'][winName].itervalues():
             self.FindWindowById(win).SetValue(view[winName]['value'])
-
-        if winName == 'z-exag':
-            zExag = True
-        else:
-            zExag = False
-        self.PostViewEvent(zExag)
-        
+                
         self.mapWindow.render['quick'] = True
         self.mapWindow.Refresh(False)
-
+        
+        event.Skip()
+        
     def OnViewChanged(self, event):
-        """View changed, render in full resolution"""
+        """!View changed, render in full resolution"""
         self.mapWindow.render['quick'] = False
         self.mapWindow.Refresh(False)
-
+        
+        self.UpdateSettings()
+        
     def OnViewChangedSpin(self, event):
-        """View changed, render in full resolution"""
-        # TODO: use step value instead
-
+        """!View changed, render in full resolution"""
+        self.mapWindow.render['quick'] = False
         self.OnViewChange(event)
         self.OnViewChanged(None)
-
+        self.Update()
+        
+        event.Skip()
+        
     def OnResetView(self, event):
-        """Reset to default view (view page)"""
+        """!Reset to default view (view page)"""
         self.mapWindow.ResetView()
         self.UpdateSettings()
         self.mapWindow.Refresh(False)
-
+        
     def OnLookAt(self, event):
-        """Look at (view page)"""
+        """!Look at (view page)"""
         sel = event.GetSelection()
         if sel == 0: # top
-            self.mapWindow.view['pos']['x'] = 0.5
-            self.mapWindow.view['pos']['y'] = 0.5
+            self.mapWindow.view['position']['x'] = 0.5
+            self.mapWindow.view['position']['y'] = 0.5
         elif sel == 1: # north
-            self.mapWindow.view['pos']['x'] = 0.5
-            self.mapWindow.view['pos']['y'] = 0.0
+            self.mapWindow.view['position']['x'] = 0.5
+            self.mapWindow.view['position']['y'] = 0.0
         elif sel == 2: # south
-            self.mapWindow.view['pos']['x'] = 0.5
-            self.mapWindow.view['pos']['y'] = 1.0
+            self.mapWindow.view['position']['x'] = 0.5
+            self.mapWindow.view['position']['y'] = 1.0
         elif sel == 3: # east
-            self.mapWindow.view['pos']['x'] = 1.0
-            self.mapWindow.view['pos']['y'] = 0.5
+            self.mapWindow.view['position']['x'] = 1.0
+            self.mapWindow.view['position']['y'] = 0.5
         elif sel == 4: # west
-            self.mapWindow.view['pos']['x'] = 0.0
-            self.mapWindow.view['pos']['y'] = 0.5
+            self.mapWindow.view['position']['x'] = 0.0
+            self.mapWindow.view['position']['y'] = 0.5
         elif sel == 5: # north-west
-            self.mapWindow.view['pos']['x'] = 0.0
-            self.mapWindow.view['pos']['y'] = 0.0
+            self.mapWindow.view['position']['x'] = 0.0
+            self.mapWindow.view['position']['y'] = 0.0
         elif sel == 6: # north-east
-            self.mapWindow.view['pos']['x'] = 1.0
-            self.mapWindow.view['pos']['y'] = 0.0
+            self.mapWindow.view['position']['x'] = 1.0
+            self.mapWindow.view['position']['y'] = 0.0
         elif sel == 7: # south-east
-            self.mapWindow.view['pos']['x'] = 1.0
-            self.mapWindow.view['pos']['y'] = 1.0
+            self.mapWindow.view['position']['x'] = 1.0
+            self.mapWindow.view['position']['y'] = 1.0
         elif sel == 8: # south-west
-            self.mapWindow.view['pos']['x'] = 0.0
-            self.mapWindow.view['pos']['y'] = 1.0
-
-        self.PostViewEvent(zExag=True)
+            self.mapWindow.view['position']['x'] = 0.0
+            self.mapWindow.view['position']['y'] = 1.0
         
+        self.PostViewEvent(zExag = True)
+        
         self.UpdateSettings()
-
+        self.mapWindow.render['quick'] = False
         self.mapWindow.Refresh(False)
 
-    def OnDefault(self, event):
-        """Restore default settings"""
-        settings = copy.deepcopy(UserSettings.GetDefaultSettings()['nviz'])
-        UserSettings.Set(group='nviz',
-                         value=settings)
-        
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            if subgroup != 'view':
-                continue
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    win = self.FindWindowById(self.win['settings'][subgroup][subkey][subvalue])
-                    val = settings[subgroup][subkey][subvalue]
-                    if subkey == 'pos':
-                        val = int(val * 100)
-
-                    win.SetValue(val)
-        
-        event.Skip()
-
-    def OnApply(self, event):
-        """Apply button pressed"""
-        if self.notebook.GetSelection() == self.page['settings']['id']:
-            self.ApplySettings()
-        
-        if event:
-            event.Skip()
-
-    def ApplySettings(self):
-        """Apply Nviz settings for current session"""
-        settings = UserSettings.Get(group='nviz')
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    try: # TODO
-                        win = self.FindWindowById(self.win['settings'][subgroup][subkey][subvalue])
-                    except:
-                        # print 'e', subgroup, subkey, subvalue
-                        continue
-                    
-                    if win.GetName() == "selection":
-                        value = win.GetSelection()
-                    elif win.GetName() == "color":
-                        value = tuple(win.GetColour())
-                    else:
-                        value = win.GetValue()
-                    if subkey == 'pos':
-                        value = float(value) / 100
-                    
-                    settings[subgroup][subkey][subvalue] = value
-                    
-    def OnSave(self, event):
-        """OK button pressed
-        
-        Apply changes, update map and save settings of selected layer
-        """
-        #
-        # apply changes
-        #
-        self.OnApply(None)
-
-        if self.notebook.GetSelection() == self.page['settings']['id']:
-            fileSettings = {}
-            UserSettings.ReadSettingsFile(settings=fileSettings)
-            fileSettings['nviz'] = UserSettings.Get(group='nviz')
-            file = UserSettings.SaveToFile(fileSettings)
-            self.lmgr.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)
-
-    def OnBgColor(self, event):
-        """Background color changed"""
-        color = event.GetValue()
-        color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
-
-        self.mapWindow.nvizClass.SetBgColor(str(color))
-
-        if self.parent.autoRender.IsChecked():
-            self.mapWindow.Refresh(False)
-        
     def OnClose(self, event):
-        """Close button pressed
+        """!Close button pressed
         
         Close dialog
         """
         self.Hide()
-
+        
     def OnMapObjUse(self, event):
-        """Set surface attribute -- use -- map/constant"""
+        """!Set surface attribute -- use -- map/constant"""
         if not self.mapWindow.init:
             return
-
+        
         wx.Yield()
-
+        
         # find attribute row
         attrb = self.__GetWindowName(self.win['surface'], event.GetId())
         if not attrb:
@@ -1545,36 +1673,51 @@
                 value = self._getColorString(value)
             else:
                 value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue()
-
-        self.SetMapObjUseMap(nvizType=nvizType,
-                             attrb=attrb, map=useMap)
         
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        self.SetMapObjUseMap(nvizType = nvizType,
+                             attrb = attrb, map = useMap)
+        
+        name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
         if nvizType == 'surface':
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
             data[nvizType]['attribute'][attrb] = { 'map' : useMap,
                                                    'value' : str(value),
-                                                   'update' : None, }
+                                                   'update' : None }
         else: # volume / isosurface
+            data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
             list = self.FindWindowById(self.win['volume']['isosurfs'])
             id = list.GetSelection()
             data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
                                                         'value' : str(value),
-                                                        'update' : None, }
+                                                        'update' : None }
         
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
-
-    def SetMapObjUseMap(self, nvizType, attrb, map=None):
-        """Update dialog widgets when attribute type changed"""
+        
+    def EnablePage(self, name, enabled = True):
+        """!Enable/disable all widgets on page"""
+        for key, item in self.win[name].iteritems():
+            if key == 'map' or key == 'surface':
+                continue
+            if type(item) == types.DictType:
+                for sitem in self.win[name][key].itervalues():
+                    if type(sitem) == types.IntType:
+                        self.FindWindowById(sitem).Enable(enabled)
+            else:
+                if type(item) == types.IntType:
+                    self.FindWindowById(item).Enable(enabled)
+        
+    def SetMapObjUseMap(self, nvizType, attrb, map = None):
+        """!Update dialog widgets when attribute type changed"""
         if attrb in ('topo', 'color', 'shine'):
             incSel = -1 # decrement selection (no 'unset')
         else:
             incSel = 0
-
+        
         if map is True: # map
             if attrb != 'topo': # changing map topography not allowed
                 # not sure why, but here must be disabled both ids, should be fixed!
@@ -1592,27 +1735,27 @@
             if self.win[nvizType][attrb]['const']:
                 self.FindWindowById(self.win[nvizType][attrb]['const']).Enable(False)
             self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(0)
-
+        
     def OnSurfaceMap(self, event):
-        """Set surface attribute"""
-        self.SetMapObjAttrb(nvizType='surface', winId=event.GetId())
+        """!Set surface attribute"""
+        self.SetMapObjAttrb(nvizType = 'surface', winId = event.GetId())
         
     def SetMapObjAttrb(self, nvizType, winId):
-        """Set map object (surface/isosurface) attribute (map/constant)"""
+        """!Set map object (surface/isosurface) attribute (map/constant)"""
         if not self.mapWindow.init:
             return
-
+        
         attrb = self.__GetWindowName(self.win[nvizType], winId) 
         if not attrb:
             return
-
+        
         if nvizType == 'volume' and attrb == 'topo':
             return
         
         selection = self.FindWindowById(self.win[nvizType][attrb]['use']).GetSelection()
         if self.win[nvizType][attrb]['required']:
             selection += 1
-
+        
         if selection == 0: # unset
             useMap = None
             value = ''
@@ -1629,48 +1772,51 @@
             useMap = False
         
         if not self.pageChanging:
-            data = self.mapWindow.GetSelectedLayer(type='nviz')
+            name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
             if nvizType == 'surface':
+                data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
                 data[nvizType]['attribute'][attrb] = { 'map' : useMap,
                                                        'value' : str(value),
-                                                       'update' : None, }
-            else: # volume / isosurface
+                                                       'update' : None }
+            else:
+                data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
                 list = self.FindWindowById(self.win['volume']['isosurfs'])
                 id = list.GetSelection()
-                data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
-                                                            'value' : str(value),
-                                                            'update' : None, }
-                
+                if id > -1:
+                    data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
+                                                                'value' : str(value),
+                                                                'update' : None }
+            
             # update properties
-            event = wxUpdateProperties(data=data)
+            event = wxUpdateProperties(data = data)
             wx.PostEvent(self.mapWindow, event)
             
-            if self.parent.autoRender.IsChecked():
+            if self.mapDisplay.statusbarWin['render'].IsChecked():
                 self.mapWindow.Refresh(False)
-
+        
     def OnSurfaceResolution(self, event):
-        """Draw resolution changed"""
+        """!Draw resolution changed"""
         self.SetSurfaceResolution()
-
-        if apply and self.parent.autoRender.IsChecked():
+        
+        if apply and self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
-
+        
     def SetSurfaceResolution(self):
-        """Set draw resolution"""
+        """!Set draw resolution"""
         coarse = self.FindWindowById(self.win['surface']['draw']['res-coarse']).GetValue()
         fine = self.FindWindowById(self.win['surface']['draw']['res-fine']).GetValue()
         
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        data = self.GetLayerData('surface')
         data['surface']['draw']['resolution'] = { 'coarse' : coarse,
                                                   'fine' : fine,
-                                                  'update' : None, }
+                                                  'update' : None }
         
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
     def SetSurfaceMode(self):
-        """Set draw mode
+        """!Set draw mode
 
         @param apply allow auto-rendering
         """
@@ -1684,109 +1830,117 @@
         else: # both
             self.FindWindowById(self.win['surface']['draw']['res-coarse']).Enable(True)
             self.FindWindowById(self.win['surface']['draw']['res-fine']).Enable(True)
-
+        
         style = self.FindWindowById(self.win['surface']['draw']['style']).GetSelection()
-
+        
         shade = self.FindWindowById(self.win['surface']['draw']['shading']).GetSelection()
-
+        
         value, desc = self.mapWindow.nvizDefault.GetDrawMode(mode, style, shade)
-
+        
         return value, desc
 
     def OnSurfaceMode(self, event):
-        """Set draw mode"""
+        """!Set draw mode"""
         value, desc = self.SetSurfaceMode()
         
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        data = self.GetLayerData('surface')
         data['surface']['draw']['mode'] = { 'value' : value,
                                             'desc' : desc,
-                                            'update' : None, }
+                                            'update' : None }
         
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if apply and self.parent.autoRender.IsChecked():
+        if apply and self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
 
     def OnSurfaceModeAll(self, event):
-        """Set draw mode (including wire color) for all loaded surfaces"""
+        """!Set draw mode (including wire color) for all loaded surfaces"""
         value, desc = self.SetSurfaceMode()
         coarse = self.FindWindowById(self.win['surface']['draw']['res-coarse']).GetValue()
         fine = self.FindWindowById(self.win['surface']['draw']['res-fine']).GetValue()
         color = self.FindWindowById(self.win['surface']['draw']['wire-color']).GetColour()
         cvalue = self._getColorString(color)
         
-        for name in self.mapWindow.GetLayerNames(type='raster'):
-            data = self.mapWindow.GetLayerData(type='raster', name=name)
+        for name in self.mapWindow.GetLayerNames(type = 'raster'):
+            data = self.GetLayerData('surface')
             if not data:
                 continue # shouldy no happen
             
             data['surface']['draw']['mode'] = { 'value' : value,
                                                 'desc' : desc,
-                                                'update' : None, }
+                                                'update' : None }
             data['surface']['draw']['resolution'] = { 'coarse' : coarse,
                                                       'fine' : fine,
-                                                      'update' : None, }
+                                                      'update' : None }
             data['surface']['draw']['wire-color'] = { 'value' : cvalue,
                                                       'update' : None }
             
             # update properties
-            event = wxUpdateProperties(data=data)
+            event = wxUpdateProperties(data = data)
             wx.PostEvent(self.mapWindow, event)
             
-        if apply and self.parent.autoRender.IsChecked():
+        if apply and self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
-
+        
     def _getColorString(self, color):
-        """Set wire color"""
+        """!Set wire color"""
         return str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
-
+    
     def OnSurfaceWireColor(self, event):
-        """Set wire color"""
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        """!Set wire color"""
+        data = self.GetLayerData('surface')
         value = self._getColorString(event.GetValue())
         data['surface']['draw']['wire-color'] = { 'value' : value,
                                                   'update' : None }
-                
+        
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
-
+        
     def OnSurfaceAxis(self, event):
-        """Surface position, axis changed"""
-        mapLayer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
-        id = data['object']['id']
-
+        """!Surface position, axis changed"""
+        data = self.GetLayerData('surface')
+        id = data['surface']['object']['id']
+        
         axis = self.FindWindowById(self.win['surface']['position']['axis']).GetSelection()
-        win = self.FindWindowById(self.win['surface']['position']['pos'])
-
-        x, y, z = self.mapWindow.nvizClass.GetSurfacePosition(id)
-
+        slider = self.FindWindowById(self.win['surface']['position']['slider'])
+        spin = self.FindWindowById(self.win['surface']['position']['spin'])
+        
+        x, y, z = self._display.GetSurfacePosition(id)
+        
         if axis == 0: # x
-            win.SetRange(-1e4, 1e4)
-            win.SetValue(x)
+            slider.SetValue(x)
+            spin.SetValue(x)
         elif axis == 1: # y
-            win.SetRange(-1e4, 1e4)
-            win.SetValue(y)
+            slider.SetValue(y)
+            spin.SetValue(y)
         else: # z
-            win.SetRange(-1e3, 1e3)
-            win.SetValue(z)
-
+            slider.SetValue(z)
+            spin.SetValue(z)
+        
     def OnSurfacePosition(self, event):
-        """Surface position"""
+        """!Surface position"""
+        winName = self.__GetWindowName(self.win['surface'], event.GetId())
+        if not winName:
+            return
         axis = self.FindWindowById(self.win['surface']['position']['axis']).GetSelection()
         value = event.GetInt()
-
-        mapLayer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        
+        for win in self.win['surface']['position'].itervalues():
+            if win == self.win['surface']['position']['axis']:
+                continue
+            else:
+                self.FindWindowById(win).SetValue(value)
+        
+        data = self.GetLayerData('surface')
         id = data['surface']['object']['id']
-        x, y, z = self.mapWindow.nvizClass.GetSurfacePosition(id)
-
+        x, y, z = self._display.GetSurfacePosition(id)
+        
         if axis == 0: # x
             x = value
         elif axis == 1: # y
@@ -1794,26 +1948,27 @@
         else: # z
             z = value
         
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        data = self.GetLayerData('surface')
         data['surface']['position']['x'] = x
         data['surface']['position']['y'] = y
         data['surface']['position']['z'] = z
         data['surface']['position']['update'] = None
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
+        #        self.UpdatePage('surface')
 
     def UpdateVectorShow(self, vecType, enabled):
-        """Enable/disable lines/points widgets
-
+        """!Enable/disable lines/points widgets
+        
         @param vecType vector type (lines, points)
         """
         if vecType != 'lines' and vecType != 'points':
             return False
-
+        
         for win in self.win['vector'][vecType].keys():
             if win == 'show':
                 continue
@@ -1828,25 +1983,28 @@
                     self.FindWindowById(self.win['vector'][vecType][win]).Enable(True)
                 else:
                     self.FindWindowById(self.win['vector'][vecType][win]).Enable(False)
-
+        
         return True
     
     def OnVectorShow(self, event):
-        """Show vector lines/points"""
+        """!Show vector lines/points"""
         winId = event.GetId()
         if winId == self.win['vector']['lines']['show']:
             vecType = 'lines'
+            points = False
         else: # points
-            vecType = 'points'
-
+            vecType = 'points' 
+            points = True
+       
         checked = event.IsChecked()
-        item = self.mapWindow.GetSelectedLayer(type='item')
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['vector']
+        name = self.FindWindowById(self.win['vector']['map']).GetValue()
+        item = self.mapWindow.GetLayerByName(name, mapType = 'vector', dataType = 'item')
+        data = self.GetLayerData('vector')['vector']
         
         if checked:
-            self.mapWindow.LoadVector(item, (vecType,))
+            self.mapWindow.LoadVector(item, points = points)
         else:
-            self.mapWindow.UnloadVector(item, (vecType,))
+            self.mapWindow.UnloadVector(item, points = points)
         
         self.UpdateVectorShow(vecType, checked)
         
@@ -1855,46 +2013,46 @@
                 id = data[vecType]['object']['id']
             except KeyError:
                 id = -1
-
+            
             if id > 0:
                 self.mapWindow.SetMapObjProperties(item, id, vecType)
-        
+                
                 # update properties
-                event = wxUpdateProperties(data=data)
+                event = wxUpdateProperties(data = data)
                 wx.PostEvent(self.mapWindow, event)
-                
-        if self.parent.autoRender.IsChecked():
+        
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
         event.Skip()
     
     def OnVectorDisplay(self, event):
-        """Display vector lines on surface/flat"""
+        """!Display vector lines on surface/flat"""
+        rasters = self.mapWindow.GetLayerNames('raster')
         if event.GetSelection() == 0: # surface
-            if len(self.mapWindow.layers['raster']['name']) < 1:
-                event.Veto()
+            if len(rasters) < 1:
+                self.FindWindowById(self.win['vector']['lines']['surface']).Enable(False)
+                self.FindWindowById(self.win['vector']['lines']['flat']).SetSelection(1)
                 return
-
+            
             self.FindWindowById(self.win['vector']['lines']['surface']).Enable(True)
             # set first found surface
-            data = self.mapWindow.GetSelectedLayer(type='nviz')
-            data['vector']['lines']['mode']['surface'] = self.mapWindow.layers['raster']['name'][0]
+            data = self.GetLayerData('vector')
+            data['vector']['lines']['mode']['surface'] = rasters[0]
             self.FindWindowById(self.win['vector']['lines']['surface']).SetStringSelection( \
-                self.mapWindow.layers['raster']['name'][0])
+                rasters[0])
         else: # flat
             self.FindWindowById(self.win['vector']['lines']['surface']).Enable(False)
-
+        
         self.OnVectorLines(event)
-
+        
         event.Skip()
 
     def OnVectorLines(self, event):
-        """Set vector lines mode, apply changes if auto-rendering is enabled"""
+        """!Set vector lines mode, apply changes if auto-rendering is enabled"""
+        data = self.GetLayerData('vector')
         width = self.FindWindowById(self.win['vector']['lines']['width']).GetValue()
-
-        color = self.FindWindowById(self.win['vector']['lines']['color']).GetColour()
-        color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
-
+        
         mode = {}
         if self.FindWindowById(self.win['vector']['lines']['flat']).GetSelection() == 0:
             mode['type'] = 'surface'
@@ -1903,18 +2061,25 @@
         else:
             mode['type'] = 'flat'
         
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
-        for attrb in ('width', 'color', 'mode'):
+        for attrb in ('width', 'mode'):
             data['vector']['lines'][attrb]['update'] = None
         data['vector']['lines']['width']['value'] = width
-        data['vector']['lines']['color']['value'] = color
         data['vector']['lines']['mode']['value'] = mode
         
+        color = self.FindWindowById(self.win['vector']['lines']['color']).GetColour()
+        
+        if isinstance(color, csel.ColourSelect):
+            pass #color picker not yet instantiated
+        else:
+            color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+            data['vector']['lines']['color']['update'] = None
+            data['vector']['lines']['color']['value'] = color
+        
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
                         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
     def OnVectorHeight(self, event):
@@ -1933,84 +2098,92 @@
             # spin
             win = self.FindWindowById(self.win['vector'][vtype]['height']['slider'])
         win.SetValue(value)
-        
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['vector'][vtype]
+
+        data = self.GetLayerData('vector')['vector'][vtype]
         data['height'] = { 'value' : value,
                            'update' : None }
         
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
         self.mapWindow.render['quick'] = True
         self.mapWindow.render['v' + vtype] = True
         self.mapWindow.Refresh(False)
+        
+        event.Skip()
     
     def OnVectorHeightFull(self, event):
-        """Vector height changed, render in full resolution"""
+        """!Vector height changed, render in full resolution"""
+        self.OnVectorHeight(event)
+        self.OnVectorSurface(event)
         id = event.GetId()
         if id == self.win['vector']['lines']['height']['spin'] or \
                 id == self.win['vector']['lines']['height']['slider']:
             vtype = 'lines'
         else:
             vtype = 'points'
-
+        
         self.mapWindow.render['quick'] = False
         self.mapWindow.render['v' + vtype] = False
         self.mapWindow.Refresh(False)
 
     def OnVectorHeightSpin(self, event):
-        """Vector height changed, render in full resolution"""
+        """!Vector height changed, render in full resolution"""
         # TODO: use step value instead
-
-        self.OnVectorHeight(event)
+        
+        #        self.OnVectorHeight(event)
         self.OnVectorHeightFull(event)
-
+        
     def OnVectorSurface(self, event):
-        """Reference surface for vector map (lines/points)"""
+        """!Reference surface for vector map (lines/points)"""
         id = event.GetId()
         if id == self.win['vector']['lines']['surface']:
             vtype = 'lines'
         else:
             vtype = 'points'
-
-        data['vector'][vtype]['mode']['surface'] = { 'value' : event.GetValue(),
+        data = self.GetLayerData('vector')
+        data['vector'][vtype]['mode']['surface'] = { 'value' : event.GetString(),
                                                      'update' : None }
         
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
     def OnVectorPoints(self, event):
-        """Set vector points mode, apply changes if auto-rendering is enabled"""
+        """!Set vector points mode, apply changes if auto-rendering is enabled"""
+        data = self.GetLayerData('vector')
+        
         size  = self.FindWindowById(self.win['vector']['points']['size']).GetValue()
-        width = self.FindWindowById(self.win['vector']['points']['width']).GetValue()
-
-        color = self.FindWindowById(self.win['vector']['points']['color']).GetColour()
-        color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
-
         marker = self.FindWindowById(self.win['vector']['points']['marker']).GetSelection()
+        #        width = self.FindWindowById(self.win['vector']['points']['width']).GetValue()
         
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
-        for attrb in ('size', 'width', 'color', 'marker'):
+        for attrb in ('size', 'marker'):
             data['vector']['points'][attrb]['update'] = None
         data['vector']['points']['size']['value'] = size
-        data['vector']['points']['width']['value'] = width
-        data['vector']['points']['color']['value'] = color
+        #        data['vector']['points']['width']['value'] = width
         data['vector']['points']['marker']['value'] = marker
-
+        
+        color = self.FindWindowById(self.win['vector']['points']['color']).GetColour()
+        if isinstance(color, csel.ColourSelect):
+            pass #color picker not yet instantiated
+        else:
+            color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+            data['vector']['points']['color']['update'] = None
+            data['vector']['points']['color']['value'] = color
+        
         # update properties
-        event = wxUpdateProperties(data=data)
+        event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
 
     def UpdateIsosurfButtons(self, list):
-        """Enable/disable buttons 'add', 'delete',
+        """!Enable/disable buttons 'add', 'delete',
         'move up', 'move down'"""
         nitems = list.GetCount()
         add = self.parent.FindWindowById(self.win['volume']['btnIsosurfAdd'])
@@ -2022,19 +2195,19 @@
             add.Enable(False)
         else:
             add.Enable(True)
-
+        
         if nitems < 1:
             # disable 'delete' if only one item in the lis
             delete.Enable(False)
         else:
             delete.Enable(True)
-
+        
         if list.GetSelection() >= nitems - 1:
             # disable 'move-down' if last
             moveDown.Enable(False)
         else:
             moveDown.Enable(True)
-
+        
         if list.GetSelection() < 1:
             # disable 'move-up' if first
             moveUp.Enable(False)
@@ -2042,64 +2215,64 @@
             moveUp.Enable(True)
         
     def OnVolumeIsosurfMode(self, event):
-        """Set isosurface draw mode"""
+        """!Set isosurface draw mode"""
         self.SetIsosurfaceMode(event.GetSelection())
     
     def SetIsosurfaceMode(self, selection):
-        """Set isosurface draw mode"""
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        """!Set isosurface draw mode"""
+        data = self.GetLayerData('volume')['volume']
         id = data['object']['id']
-
+        
         mode = 0
         if selection == 0:
             mode |= wxnviz.DM_FLAT
         else:
             mode |= wxnviz.DM_GOURAUD
         
-        self.mapWindow.nvizClass.SetIsosurfaceMode(id, mode)
+        self._display.SetIsosurfaceMode(id, mode)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
     def OnVolumeIsosurfResolution(self, event):
-        """Set isosurface draw resolution"""
+        """!Set isosurface draw resolution"""
         self.SetIsosurfaceResolution(event.GetInt())
-    
+        
     def SetIsosurfaceResolution(self, res):
-        """Set isosurface draw resolution"""
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        """!Set isosurface draw resolution"""
+        data = self.GetLayerData('volume')['volume']
+        
         id = data['object']['id']
-        self.mapWindow.nvizClass.SetIsosurfaceRes(id, res)
+        self._display.SetIsosurfaceRes(id, res)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
-
+        
     def OnVolumeIsosurfMap(self, event):
-        """Set surface attribute"""
-        self.SetMapObjAttrb(nvizType='volume', winId=event.GetId())
-
+        """!Set surface attribute"""
+        self.SetMapObjAttrb(nvizType = 'volume', winId = event.GetId())
+        
     def OnVolumeIsosurfCheck(self, event):
-        """Isosurface checked (->load) or unchecked (->unload)"""
+        """!Isosurface checked (->load) or unchecked (->unload)"""
         index = event.GetSelection()
         list = self.FindWindowById(self.win['volume']['isosurfs'])
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        
+        data = self.GetLayerData('volume')['volume']
         id = data['object']['id']
         
         isosurfId = event.GetSelection()
         
         if list.IsChecked(index):
-            self.mapWindow.nvizClass.SetIsosurfaceTransp(id, isosurfId, False, "0")
+            self._display.SetIsosurfaceTransp(id, isosurfId, False, "0")
         else:
             # disable -> make transparent
-            self.mapWindow.nvizClass.SetIsosurfaceTransp(id, isosurfId, False, "255")
+            self._display.SetIsosurfaceTransp(id, isosurfId, False, "255")
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
     def OnVolumeIsosurfSelect(self, event):
-        """Isosurface item selected"""
+        """!Isosurface item selected"""
         winUp = self.FindWindowById(self.win['volume']['btnIsosurfMoveUp'])
         winDown = self.FindWindowById(self.win['volume']['btnIsosurfMoveDown'])
         selection = event.GetSelection()
@@ -2118,29 +2291,31 @@
                 winUp.Enable()
         
         # update dialog
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']['isosurface'][selection]
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']['isosurface'][selection]
         
         self.UpdateVolumeIsosurfPage(layer, data)
-
+        
     def OnVolumeIsosurfAdd(self, event):
-        """Add new isosurface to the list"""
+        """!Add new isosurface to the list"""
         list = self.FindWindowById(self.win['volume']['isosurfs'])
         level = self.FindWindowById(self.win['volume']['topo']['const']).GetValue()
         
         sel = list.GetSelection()
         if sel < 0 or sel >= list.GetCount() - 1:
-            item = list.Append(item="%s %s" % (_("Level"), str(level)))
+            item = list.Append(item = "%s %s" % (_("Level"), str(level)))
         else:
-            list.Insert(item="%s %s" % (_("Level"), str(level)),
-                        pos=sel+1) # append
+            list.Insert(item = "%s %s" % (_("Level"), str(level)),
+                        pos = sel+1) # append
             item = sel + 1
         
         list.Check(item)
         list.SetSelection(item)
         
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
         id = data['object']['id']
         
         # collect properties
@@ -2158,7 +2333,7 @@
                     sel += 1
                 if sel == 0: # unset
                     continue
-
+                
                 isosurfData[attrb] = {}
                 if sel == 1: # map
                     isosurfData[attrb]['map'] = True
@@ -2172,24 +2347,24 @@
                     else:
                         value = vwin.GetValue()
                 isosurfData[attrb]['value'] = value
-
+        
         data['isosurface'].insert(item, isosurfData)
         
         # add isosurface        
-        self.mapWindow.nvizClass.AddIsosurface(id, level)
+        self._display.AddIsosurface(id, level)
         # use by default 3d raster map for color
-        self.mapWindow.nvizClass.SetIsosurfaceColor(id, item, True, str(layer.name))
-
+        self._display.SetIsosurfaceColor(id, item, True, str(layer.name))
+        
         # update buttons
         self.UpdateIsosurfButtons(list)
-
-        if self.parent.autoRender.IsChecked():
+        
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
-
+        
         event.Skip()
         
     def OnVolumeIsosurfDelete(self, event):
-        """Remove isosurface from list"""
+        """!Remove isosurface from list"""
         list = self.FindWindowById(self.win['volume']['isosurfs'])
         
         # remove item from list
@@ -2199,138 +2374,162 @@
         if list.GetCount() > 0:
             list.SetSelection(list.GetCount()-1)
         
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+
         id = data['object']['id']
-
+        
         # delete isosurface
         del data['isosurface'][isosurfId]
         
-        self.mapWindow.nvizClass.DeleteIsosurface(id, isosurfId)
-
+        self._display.DeleteIsosurface(id, isosurfId)
+        
         # update buttons
         self.UpdateIsosurfButtons(list)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
         event.Skip()
         
     def OnVolumeIsosurfMoveUp(self, event):
-        """Move isosurface up in the list"""
+        """!Move isosurface up in the list"""
         list = self.FindWindowById(self.win['volume']['isosurfs'])
         sel = list.GetSelection()
-
+        
         if sel < 1:
             return # this should not happen
-
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+        
         id = data['object']['id']
-
+        
         # move item up
         text = list.GetStringSelection()
-        list.Insert(item=text, pos=sel-1)
+        list.Insert(item = text, pos = sel-1)
         list.Check(sel-1)
         list.SetSelection(sel-1)
         list.Delete(sel+1)
         data['isosurface'].insert(sel-1, data['isosurface'][sel])
         del data['isosurface'][sel+1]
-        self.mapWindow.nvizClass.MoveIsosurface(id, sel, True)
+        self._display.MoveIsosurface(id, sel, True)
         
         # update buttons
         self.UpdateIsosurfButtons(list)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
         event.Skip()
         
     def OnVolumeIsosurfMoveDown(self, event):
-        """Move isosurface dowm in the list"""
+        """!Move isosurface dowm in the list"""
         list = self.FindWindowById(self.win['volume']['isosurfs'])
         sel = list.GetSelection()
-
+        
         if sel >= list.GetCount() - 1:
             return # this should not happen
-
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')['volume']
+        
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+        
         id = data['object']['id']
-
+        
         # move item up
         text = list.GetStringSelection()
-        list.Insert(item=text, pos=sel+2)
+        list.Insert(item = text, pos = sel+2)
         list.Check(sel+2)
         list.SetSelection(sel+2)
         list.Delete(sel)
         data['isosurface'].insert(sel+2, data['isosurface'][sel])
         del data['isosurface'][sel]
-        self.mapWindow.nvizClass.MoveIsosurface(id, sel, False)
+        self._display.MoveIsosurface(id, sel, False)
         
         # update buttons
         self.UpdateIsosurfButtons(list)
         
-        if self.parent.autoRender.IsChecked():
+        if self.mapDisplay.statusbarWin['render'].IsChecked():
             self.mapWindow.Refresh(False)
         
         event.Skip()
         
     def UpdatePage(self, pageId):
-        """Update dialog (selected page)"""
+        """!Update dialog (selected page)"""
         self.pageChanging = True
-        layer = self.mapWindow.GetSelectedLayer()
-        data = self.mapWindow.GetSelectedLayer(type='nviz')
+        Debug.msg(1, "NvizToolWindow.UpdatePage(): %s", pageId)
         
         if pageId == 'view':
-            max = self.mapWindow.view['z-exag']['value'] * 10
+            self.SetPage('view')
             hmin = self.mapWindow.iview['height']['min']
             hmax = self.mapWindow.iview['height']['max']
+            hval = self.mapWindow.iview['height']['value']
+            zmin = self.mapWindow.view['z-exag']['min']
+            zmax = self.mapWindow.view['z-exag']['max']
+            zval = self.mapWindow.view['z-exag']['value']
+            
             for control in ('spin', 'slider'):
-                self.FindWindowById(self.win['view']['z-exag'][control]).SetRange(1,
-                                                                                  max)
                 self.FindWindowById(self.win['view']['height'][control]).SetRange(hmin,
                                                                                   hmax)
+                self.FindWindowById(self.win['view']['height'][control]).SetValue(hval)                                      
+                self.FindWindowById(self.win['view']['z-exag'][control]).SetRange(zmin,
+                                                                                  zmax)
+                self.FindWindowById(self.win['view']['z-exag'][control]).SetValue(zval)                                      
+        
         elif pageId in ('surface', 'vector', 'volume'):
-            if self.notebook.GetSelection() != self.page[pageId]['id']:
-                for page in ('surface', 'vector', 'volume'):
-                    if self.page[page]['id'] > -1:
-                        self.notebook.RemovePage(self.page[page]['id'])
-                        self.page[page]['id'] = -1
-                        oldpanel = wx.FindWindowById(self.page[page]['panel'])
-                        oldpanel.Hide()
-
-                self.page[pageId]['id'] = 1
-                self.page['settings']['id'] = 2
-
-                panel = wx.FindWindowById(self.page[pageId]['panel'])
-                self.notebook.InsertPage(n=self.page[pageId]['id'],
-                                         page=panel,
-                                         text=" %s " % _("Layer properties"),
-                                         select=True)
-            if pageId == 'surface':
-                self.UpdateSurfacePage(layer, data['surface'])
-            elif pageId == 'vector':
-                self.UpdateVectorPage(layer, data['vector'])
-            elif pageId == 'volume':
-                self.UpdateVectorPage(layer, data['vector'])
+            name = self.FindWindowById(self.win[pageId]['map']).GetValue()
+            data = self.GetLayerData(pageId)
+            if data:
+                if pageId == 'surface':
+                    layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
+                    self.UpdateSurfacePage(layer, data['surface'])
+                elif pageId == 'vector':
+                    layer = self.mapWindow.GetLayerByName(name, mapType = 'vector')
+                    self.UpdateVectorPage(layer, data['vector'])
+                elif pageId == 'volume':
+                    layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+                    self.UpdateVectorPage(layer, data['vector'])
+        elif pageId == 'light':
+            zval = self.mapWindow.light['position']['z']
+            bval = self.mapWindow.light['bright']
+            aval = self.mapWindow.light['ambient']
+            for control in ('spin', 'slider'):
+                self.FindWindowById(self.win['light']['z'][control]).SetValue(zval)
+                self.FindWindowById(self.win['light']['bright'][control]).SetValue(bval)
+                self.FindWindowById(self.win['light']['ambient'][control]).SetValue(aval)
+        elif pageId == 'fringe':
+            win = self.FindWindowById(self.win['fringe']['map'])
+            win.SetValue(self.FindWindowById(self.win['surface']['map']).GetValue())
         
-            
-        self.notebook.Update()
+        self.Update()
         self.pageChanging = False
         
-    def UpdateSurfacePage(self, layer, data):
-        #
+    def UpdateSurfacePage(self, layer, data, updateName = True):
+        """!Update surface page"""
+        ret = gcmd.RunCommand('r.info',
+                              read = True,
+                              flags = 'm',
+                              map = layer.name)
+        if ret:
+            desc = ret.split('=')[1].rstrip('\n')
+        else:
+            desc = None
+        if updateName:
+            self.FindWindowById(self.win['surface']['map']).SetValue(layer.name)
+        self.FindWindowById(self.win['surface']['desc']).SetLabel(desc)
+        
         # attributes
-        #
         for attr in ('topo', 'color'): # required
             if layer and layer.type == 'raster':
                 self.FindWindowById(self.win['surface'][attr]['map']).SetValue(layer.name)
             else:
                 self.FindWindowById(self.win['surface'][attr]['map']).SetValue('')
-            self.SetMapObjUseMap(nvizType='surface',
-                                 attrb=attr, map=True) # -> map
-
+            self.SetMapObjUseMap(nvizType = 'surface',
+                                 attrb = attr, map = True) # -> map
+        
         if data['attribute'].has_key('color'):
             value = data['attribute']['color']['value']
             if data['attribute']['color']['map']:
@@ -2338,53 +2537,53 @@
             else: # constant
                 color = map(int, value.split(':'))
                 self.FindWindowById(self.win['surface']['color']['const']).SetColour(color)
-            self.SetMapObjUseMap(nvizType='surface',
-                                 attrb=attr, map=data['attribute']['color']['map'])
-
-        self.SetMapObjUseMap(nvizType='surface',
-                             attrb='shine', map=data['attribute']['shine']['map'])
+            self.SetMapObjUseMap(nvizType = 'surface',
+                                 attrb = attr, map = data['attribute']['color']['map'])
+        
+        self.SetMapObjUseMap(nvizType = 'surface',
+                             attrb = 'shine', map = data['attribute']['shine']['map'])
         value = data['attribute']['shine']['value']
         if data['attribute']['shine']['map']:
             self.FindWindowById(self.win['surface']['shine']['map']).SetValue(value)
         else:
             self.FindWindowById(self.win['surface']['shine']['const']).SetValue(value)
-
+        
         #
         # draw
         #
-        for control, dict in data['draw'].iteritems():
+        for control, data in data['draw'].iteritems():
             if control == 'all': # skip 'all' property
                 continue
             if control == 'resolution':
-                self.FindWindowById(self.win['surface']['draw']['res-coarse']).SetValue(dict['coarse'])
-                self.FindWindowById(self.win['surface']['draw']['res-fine']).SetValue(dict['fine'])
+                self.FindWindowById(self.win['surface']['draw']['res-coarse']).SetValue(data['coarse'])
+                self.FindWindowById(self.win['surface']['draw']['res-fine']).SetValue(data['fine'])
                 continue
-
+            
             if control == 'mode':
-                if dict['desc']['mode'] == 'coarse':
+                if data['desc']['mode'] == 'coarse':
                     self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(0)
-                elif dict['desc']['mode'] == 'fine':
+                elif data['desc']['mode'] == 'fine':
                     self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(1)
                 else: # both
                     self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(2)
-                    
-                if dict['desc']['style'] == 'wire':
+                
+                if data['desc']['style'] == 'wire':
                     self.FindWindowById(self.win['surface']['draw']['style']).SetSelection(0)
                 else: # surface
                     self.FindWindowById(self.win['surface']['draw']['style']).SetSelection(1)
-
-                if dict['desc']['shading'] == 'flat':
+                
+                if data['desc']['shading'] == 'flat':
                     self.FindWindowById(self.win['surface']['draw']['shading']).SetSelection(0)
                 else: # gouraud
                     self.FindWindowById(self.win['surface']['draw']['shading']).SetSelection(1)
                 
                 continue
-
-            value = dict['value']
+            
+            value = data['value']
             win = self.FindWindowById(self.win['surface']['draw'][control])
             
             name = win.GetName()
-
+            
             if name == "selection":
                 win.SetSelection(value)
             elif name == "colour":
@@ -2395,43 +2594,65 @@
         # enable/disable res widget + set draw mode
         self.SetSurfaceMode()
         color = self.FindWindowById(self.win['surface']['draw']['wire-color'])
+
+    def VectorInfo(self, layer):
+        """!Get number of points/lines
         
-    def UpdateVectorPage(self, layer, data):
-        vInfo = gcmd.Command(['v.info',
-                              '-t',
-                              'map=%s' % layer.name])
-        npoints = nprimitives = 0
-        for line in vInfo.ReadStdOutput():
+        @param layer MapLayer instance
+        
+        @return num of points/features (expect of points)
+        @return None
+        """
+        vInfo = gcmd.RunCommand('v.info',
+                                parent = self,
+                                read = True,
+                                flags = 't',
+                                map = layer.GetName())
+        
+        if not vInfo:
+            return None
+            
+        npoints = nlines = nprimitives = 0
+        for line in vInfo.splitlines():
             key, value = line.split('=')
             if key == 'map3d':
                 mapIs3D = int(value)
-            
             elif key == 'points':
                 npoints = int(value)
-                nprimitives = npoints
-            elif key in ('lines',
-                         'boundaries',
+                nprimitives += npoints
+            elif key == 'lines':
+                nlines = int(value)
+                nprimitives += nlines
+            elif key in ('boundaries',
                          'centroids',
                          'faces',
                          'kernels'):
                 nprimitives += int(value)
         
+        return (npoints, nlines, nprimitives, mapIs3D)
+        
+    def UpdateVectorPage(self, layer, data, updateName = True):
+        """!Update vector page"""
+        npoints, nlines, nfeatures, mapIs3D = self.VectorInfo(layer)
         if mapIs3D:
-            desc = _("Vector map <%s> is 3D") % layer.name
+            desc = _("Vector map is 3D")
             enable = False
         else:
-            desc = _("Vector map <%s> is 2D") % layer.name
+            desc = _("Vector map is 2D")
             enable = True
-        desc += " - " + _("%(primitives)d primitives (%(points)d points)") % \
-            { 'primitives' : nprimitives, 'points' : npoints }
-
+        desc += " - " + _("%(features)d features (%(points)d points)") % \
+            { 'features' : nfeatures, 'points' : npoints }
+        
+        if updateName:
+            self.FindWindowById(self.win['vector']['map']).SetValue(layer.name)
+        self.FindWindowById(self.win['vector']['desc']).SetLabel(desc)
+        
         self.FindWindowById(self.win['vector']['lines']['flat']).Enable(enable)
         for v in ('lines', 'points'):
             self.FindWindowById(self.win['vector'][v]['surface']).Enable(enable)
             self.FindWindowById(self.win['vector'][v]['height']['slider']).Enable(enable)
             self.FindWindowById(self.win['vector'][v]['height']['spin']).Enable(enable)
             
-        self.FindWindowById(self.win['vector']['desc']).SetLabel(desc)
         #
         # lines
         #
@@ -2440,20 +2661,20 @@
             showLines.SetValue(True)
         else:
             showLines.SetValue(False)
-            if nprimitives - npoints > 0:
+            if nlines > 0:
                 showLines.Enable(True)
             else:
                 showLines.Enable(False)
-
+        
         self.UpdateVectorShow('lines',
                               showLines.IsChecked())
-
+        
         width = self.FindWindowById(self.win['vector']['lines']['width'])
         width.SetValue(data['lines']['width']['value'])
-
+        
         color = self.FindWindowById(self.win['vector']['lines']['color'])
         color.SetValue(map(int, data['lines']['color']['value'].split(':')))
-
+        
         for vtype in ('lines', 'points'):
             if vtype == 'lines':
                 display = self.FindWindowById(self.win['vector']['lines']['flat'])
@@ -2461,18 +2682,21 @@
                     display.SetSelection(1)
                 else:
                     display.SetSelection(0)
-
+            
             if data[vtype]['mode']['type'] == 'surface':
                 rasters = self.mapWindow.GetLayerNames('raster')
                 surface = self.FindWindowById(self.win['vector'][vtype]['surface'])
                 surface.SetItems(rasters)
                 if len(rasters) > 0:
-                    surface.SetStringSelection(data[vtype]['mode']['surface'])
-                
+                    try:
+                        surface.SetStringSelection(data[vtype]['mode']['surface'])
+                    except:
+                        pass
+        
         for type in ('slider', 'spin'):
             win = self.FindWindowById(self.win['vector']['lines']['height'][type])
             win.SetValue(data['lines']['height']['value'])
-
+        
         #
         # points
         #
@@ -2490,7 +2714,7 @@
         self.UpdateVectorShow('points',
                               showPoints.IsChecked())
         # size, width, marker, color
-        for prop in ('size', 'width', 'marker', 'color'):
+        for prop in ('size', 'marker', 'color'):
             win = self.FindWindowById(self.win['vector']['points'][prop])
             name = win.GetName()
             if name == 'selection':
@@ -2504,40 +2728,40 @@
         for type in ('slider', 'spin'):
             win = self.FindWindowById(self.win['vector']['points']['height'][type])
             win.SetValue(data['points']['height']['value'])
-
-    def UpdateVolumePage(self, layer, data):
-        """Update volume layer properties page"""
+        
+    def UpdateVolumePage(self, layer, data, updateName = True):
+        """!Update volume page"""
+        if updateName:
+            self.FindWindowById(self.win['volume']['map']).SetValue(layer.name)
         list = self.FindWindowById(self.win['volume']['isosurfs'])
-
-        #
+        
         # draw
-        #
-        for control, dict in data['draw'].iteritems():
+        for control, idata in data['draw'].iteritems():
             if control == 'all': # skip 'all' property
                 continue
-
+            
             win = self.FindWindowById(self.win['volume']['draw'][control])
-
+            
             if control == 'shading':
                 if data['draw']['shading']['desc'] == 'flat':
                     value = 0
                 else:
                     value = 1
             else:
-                value = dict['value']
-
+                value = idata['value']
+            
             if win.GetName() == "selection":
                 win.SetSelection(value)
             else:
                 win.SetValue(value)
-
+        
         self.SetIsosurfaceMode(data['draw']['shading']['value'])
         self.SetIsosurfaceResolution(data['draw']['resolution']['value'])
-            
+        
         self.UpdateVolumeIsosurfPage(layer, data['attribute'])
         
     def UpdateVolumeIsosurfPage(self, layer, data):
-        """Update dialog -- isosurface attributes"""
+        """!Update dialog -- isosurface attributes"""
         #
         # isosurface attributes
         #
@@ -2552,10 +2776,10 @@
                     self.FindWindowById(self.win['volume'][attrb]['map']).SetValue(layer.name)
                 else:
                     self.FindWindowById(self.win['volume'][attrb]['map']).SetValue('')
-                self.SetMapObjUseMap(nvizType='volume',
-                                     attrb=attrb, map=True) # -> map
+                self.SetMapObjUseMap(nvizType = 'volume',
+                                     attrb = attrb, map = True) # -> map
                 continue
-
+            
             # skip empty attributes
             if not data.has_key(attrb):
                 continue
@@ -2574,82 +2798,653 @@
                     win = self.FindWindowById(self.win['volume'][attrb]['const'])
                 win.SetValue(value)
             
-            self.SetMapObjUseMap(nvizType='volume',
-                                 attrb=attrb, map=data[attrb]['map'])
-            
+            self.SetMapObjUseMap(nvizType = 'volume',
+                                 attrb = attrb, map = data[attrb]['map'])
+        
     def SetPage(self, name):
-        """Get named page"""
-        self.notebook.SetSelection(self.page[name]['id'])
+        """!Get named page"""
+        if name == 'view':
+            self.SetSelection(0)
+        elif name in ('surface', 'vector', 'volume'):
+            self.SetSelection(1)
+        else:
+            self.SetSelection(2)
+        win = self.FindWindowById(self.page[name]['notebook'])
+        
+        win.SetSelection(self.page[name]['id'])
 
-class ViewPositionWindow(wx.Window):
-    """Position control window (for NvizToolWindow)"""
-    def __init__(self, parent, id, mapwindow,
-                 pos=wx.DefaultPosition,
-                 size=wx.DefaultSize):
+class PositionWindow(wx.Window):
+    """!Abstract position control window, see subclasses
+    ViewPostionWindow and LightPositionWindow"""
+    def __init__(self, parent, mapwindow, id = wx.ID_ANY,
+                 **kwargs):
         self.mapWindow = mapwindow
-
-        wx.Window.__init__(self, parent, id, pos, size)
-
+        self.quick = True
+        
+        wx.Window.__init__(self, parent, id, **kwargs)
+        
         self.SetBackgroundColour("WHITE")
-
+        
         self.pdc = wx.PseudoDC()
+        
+        self.pdc.SetBrush(wx.Brush(colour = 'dark green', style = wx.SOLID))
+        self.pdc.SetPen(wx.Pen(colour = 'dark green', width = 2, style = wx.SOLID))
 
-        self.pdc.SetBrush(wx.Brush(colour='dark green', style=wx.SOLID))
-        self.pdc.SetPen(wx.Pen(colour='dark green', width=2, style=wx.SOLID))
-
-        self.Draw()
-
         self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
         self.Bind(wx.EVT_PAINT, self.OnPaint)
         # self.Bind(wx.EVT_MOTION,       self.OnMouse)
         self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
-
-    def Draw(self, pos=None):
+        
+    def Draw(self, pos, scale = False):
         w, h = self.GetClientSize()
-
-        if pos is None:
-            x = self.mapWindow.view['pos']['x']
-            y = self.mapWindow.view['pos']['y']
+        x, y = pos
+        if scale:
             x = x * w
             y = y * h
-        else:
-            x, y = pos
-
         self.pdc.Clear()
         self.pdc.BeginDrawing()
         self.pdc.DrawLine(w / 2, h / 2, x, y)
         self.pdc.DrawCircle(x, y, 5)
         self.pdc.EndDrawing()
-
+        
     def OnPaint(self, event):
         dc = wx.BufferedPaintDC(self)
         dc.SetBackground(wx.Brush("White"))
         dc.Clear()
-
+        
         self.PrepareDC(dc)
         self.pdc.DrawToDC(dc)
+        
+    def UpdatePos(self, xcoord, ycoord):
+        """!Update position coordinates (origin: UL)"""
+        if xcoord < 0.0:
+            xcoord = 0.0
+        elif xcoord > 1.0:
+            xcoord = 1.0
+        if ycoord < 0.0:
+            ycoord = 0.0
+        elif ycoord > 1.0:
+            ycoord = 1.0
 
+        self.data['position']['x'] = xcoord        
+        self.data['position']['y'] = ycoord
+        
+        return xcoord, ycoord
+    
     def OnMouse(self, event):
         if event.LeftIsDown():
             x, y = event.GetPosition()
-            self.Draw(pos=(x, y))
-            self.Refresh(False)
+            self.Draw(pos = (x, y))
             w, h = self.GetClientSize()
             x = float(x) / w
             y = float(y) / h
-            if x >= 0 and x <= 1.0:
-                self.mapWindow.view['pos']['x'] = x
-            if y >= 0 and y <= 1.0:
-                self.mapWindow.view['pos']['y'] = y
-            event = wxUpdateView(zExag=False)
-            wx.PostEvent(self.mapWindow, event)
-            
-            self.mapWindow.render['quick'] = True
-            self.mapWindow.Refresh(eraseBackground=False)
+            self.UpdatePos(x, y)
+            self.Refresh(False)
+        
+        event.Skip()
+        
+    def PostDraw(self):
+        x, y = self.UpdatePos(self.data['position']['x'],
+                              self.data['position']['y'])
+        self.Draw(pos = (x, y), scale = True)
 
+class ViewPositionWindow(PositionWindow):
+    """!View position control widget"""
+    def __init__(self, parent, mapwindow, id = wx.ID_ANY,
+                 **kwargs):
+        PositionWindow.__init__(self, parent, mapwindow, id, **kwargs)
+
+        self.data = self.mapWindow.view
+        self.PostDraw()
+        
+    def UpdatePos(self, xcoord, ycoord):
+        x, y = PositionWindow.UpdatePos(self, xcoord, ycoord)
+        
+        event = wxUpdateView(zExag = True)
+        wx.PostEvent(self.mapWindow, event)
+
+        return x, y
+
+    def OnMouse(self, event):
+        PositionWindow.OnMouse(self, event)
+        if event.LeftIsDown():
+            self.mapWindow.render['quick'] = self.quick
+            self.mapWindow.Refresh(eraseBackground = False)
         elif event.LeftUp():
             self.mapWindow.render['quick'] = False
-            self.mapWindow.Refresh(eraseBackground=False)
+            self.mapWindow.Refresh(eraseBackground = False)
         
         event.Skip()
-  
+
+class LightPositionWindow(PositionWindow):
+    """!Light position control widget"""
+    def __init__(self, parent, mapwindow, id = wx.ID_ANY,
+                 **kwargs):
+        PositionWindow.__init__(self, parent, mapwindow, id, **kwargs)
+
+        self.data = self.mapWindow.light
+        self.quick = False
+        self.PostDraw()
+
+    def UpdatePos(self, xcoord, ycoord):
+        x, y = PositionWindow.UpdatePos(self, xcoord, ycoord)
+        
+        event = wxUpdateLight()
+        wx.PostEvent(self.mapWindow, event)
+    
+        return x, y
+
+    def OnMouse(self, event):
+        PositionWindow.OnMouse(self, event)
+        if event.LeftUp():
+            self.mapWindow.render['quick'] = False
+            self.mapWindow.Refresh(eraseBackground = False)
+
+class NvizPreferencesDialog(PreferencesBaseDialog):
+    """!Nviz preferences dialog"""
+    def __init__(self, parent, title = _("3D view settings"),
+                 settings = UserSettings):
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        self.toolWin = self.parent.GetLayerManager().nviz
+        self.win = dict()
+        
+        # create notebook pages
+        self._createViewPage(self.notebook)
+        self._createVectorPage(self.notebook)
+        
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+        
+    def _createViewPage(self, notebook):
+        """!Create notebook page for general settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("View"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.win['general'] = {}
+        self.win['view'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("View")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # perspective
+        self.win['view']['persp'] = {}
+        pvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp')
+        ipvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp', internal = True)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Perspective:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(value)")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = pvals['value'],
+                           min = ipvals['min'],
+                           max = ipvals['max'])
+        self.win['view']['persp']['value'] = pval.GetId()
+        gridSizer.Add(item = pval, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = pvals['step'],
+                           min = ipvals['min'],
+                           max = ipvals['max']-1)
+        self.win['view']['persp']['step'] = pstep.GetId()
+        gridSizer.Add(item = pstep, pos = (0, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # position
+        self.win['view']['position'] = {}
+        posvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'position')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Position:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(x)")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['x'] * 100,
+                           min = 0,
+                           max = 100)
+        self.win['view']['position']['x'] = px.GetId()
+        gridSizer.Add(item = px, pos = (1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = "(y)"),
+                      pos = (1, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['y'] * 100,
+                           min = 0,
+                           max = 100)
+        self.win['view']['position']['y'] = py.GetId()
+        gridSizer.Add(item = py, pos = (1, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # height
+        self.win['view']['height'] = {}
+        hvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'height')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        hstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = hvals['step'],
+                           min = 1,
+                           max = 1e6)
+        self.win['view']['height']['step'] = hstep.GetId()
+        gridSizer.Add(item = hstep, pos = (2, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # twist
+        self.win['view']['twist'] = {}
+        tvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist')
+        itvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist', internal = True)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Twist:")),
+                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(value)")),
+                      pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        tval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = tvals['value'],
+                           min = itvals['min'],
+                           max = itvals['max'])
+        self.win['view']['twist']['value'] = tval.GetId()
+        gridSizer.Add(item = tval, pos = (3, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (3, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        tstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = tvals['step'],
+                           min = itvals['min'],
+                           max = itvals['max']-1)
+        self.win['view']['twist']['step'] = tstep.GetId()
+        gridSizer.Add(item = tstep, pos = (3, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # z-exag
+        self.win['view']['z-exag'] = {}
+        zvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'z-exag')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Z-exag:")),
+                      pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(value)")),
+                      pos = (4, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        zval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = zvals['value'],
+                           min = -1e6,
+                           max = 1e6)
+        self.win['view']['z-exag']['value'] = zval.GetId()
+        gridSizer.Add(item = zval, pos = (4, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("(step)")),
+                      pos = (4, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        zstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = zvals['step'],
+                           min = -1e6,
+                           max = 1e6)
+        self.win['view']['z-exag']['step'] = zstep.GetId()
+        gridSizer.Add(item = zstep, pos = (4, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Image Appearance")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # background color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Background color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'settings',
+                                                            subkey = ['general', 'bgcolor']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['general']['bgcolor'] = color.GetId()
+        gridSizer.Add(item = color, pos = (0, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
+    def _createVectorPage(self, notebook):
+        """!Create notebook page for general settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Vector"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # vector lines
+        self.win['vector'] = {}
+        self.win['vector']['lines'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector lines")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # show
+        row = 0
+        showLines = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = _("Show lines"))
+        self.win['vector']['lines']['show'] = showLines.GetId()
+        showLines.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                            subkey = ['lines', 'show']))
+        gridSizer.Add(item = showLines, pos = (row, 0))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        # vector points
+        self.win['vector']['points'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector points")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 5)
+        
+        # show
+        row = 0
+        showPoints = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Show points"))
+        showPoints.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                             subkey = ['points', 'show']))
+        self.win['vector']['points']['show'] = showPoints.GetId()
+        gridSizer.Add(item = showPoints, pos = (row, 0), span = (1, 8))
+        
+        # icon size
+        row += 1 
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Size:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        isize = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 100,
+                            min = 1,
+                            max = 1e6)
+        self.win['vector']['points']['size'] = isize.GetId()
+        isize.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                        subkey = ['points', 'size']))
+        gridSizer.Add(item = isize, pos = (row, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon width
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      pos = (row, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 2,
+                            min = 1,
+                            max = 1e6)
+        self.win['vector']['points']['width'] = isize.GetId()
+        iwidth.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                         subkey = ['points', 'width']))
+        gridSizer.Add(item = iwidth, pos = (row, 3),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon symbol
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Marker:")),
+                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                          choices = UserSettings.Get(group = 'nviz', key = 'vector',
+                                                   subkey = ['points', 'marker'], internal = True))
+        isym.SetName("selection")
+        self.win['vector']['points']['marker'] = isym.GetId()
+        isym.SetSelection(UserSettings.Get(group = 'nviz', key = 'vector',
+                                           subkey = ['points', 'marker']))
+        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 5))
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (row, 6), flag = wx.ALIGN_CENTER_VERTICAL)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY)
+        icolor.SetName("color")
+        self.win['vector']['points']['color'] = icolor.GetId()
+        icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['points', 'color']))
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 7))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
+    def OnDefault(self, event):
+        """Restore default settings"""
+        settings = copy.deepcopy(UserSettings.GetDefaultSettings()['nviz'])
+        UserSettings.Set(group = 'nviz',
+                         value = settings)
+        
+        for subgroup, key in settings.iteritems(): # view, surface, vector...
+            if subgroup != 'view':
+                continue
+            for subkey, value in key.iteritems():
+                for subvalue in value.keys():
+                    win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
+                    val = settings[subgroup][subkey][subvalue]
+                    if subkey == 'position':
+                        val = int(val * 100)
+                    
+                    win.SetValue(val)
+        
+        event.Skip()
+        
+    def OnApply(self, event):
+        """Apply Nviz settings for current session"""
+        settings = UserSettings.Get(group = 'nviz')
+        for subgroup, key in settings.iteritems(): # view, surface, vector...
+            for subkey, value in key.iteritems():
+                for subvalue in value.keys():
+                    try: # TODO
+                        win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
+                    except:
+                        # print 'e', subgroup, subkey, subvalue
+                        continue
+                    
+                    if win.GetName() == "selection":
+                        value = win.GetSelection()
+                    elif win.GetName() == "color":
+                        value = tuple(win.GetColour())
+                    else:
+                        value = win.GetValue()
+                    if subkey == 'position':
+                        value = float(value) / 100
+                    
+                    settings[subgroup][subkey][subvalue] = value
+                    
+    def OnSave(self, event):
+        """!Apply changes, update map and save settings of selected
+        layer
+        """
+        # apply changes
+        self.OnApply(None)
+        
+        if self.GetSelection() == self.page['id']:
+            fileSettings = {}
+            UserSettings.ReadSettingsFile(settings = fileSettings)
+            fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
+            file = UserSettings.SaveToFile(fileSettings)
+            self.parent.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)
+        
+    def OnLoad(self, event):
+        """!Apply button pressed"""
+        self.LoadSettings()
+        
+        if event:
+            event.Skip()
+
+    def LoadSettings(self):
+        """!Load saved Nviz settings and apply to current session"""
+        UserSettings.ReadSettingsFile()
+        settings = copy.deepcopy(UserSettings.Get(group = 'nviz'))
+        
+        for subgroup, key in settings.iteritems(): # view, surface, vector...
+            for subkey, value in key.iteritems():
+                for subvalue in value.keys():
+                    if subvalue == 'step':
+                        continue
+                    else:
+                        insetting = value[subvalue]                                                    
+                    if subgroup == 'view':
+                        for viewkey, viewitem in self.mapWindow.view[subkey].iteritems(): 
+                            if viewkey == subvalue:
+                                self.mapWindow.view[subkey][viewkey] = insetting 
+                            else:
+                                continue
+                    else:
+                        for otherkey, otheritem in self.win[subgroup][subkey].iteritems():
+                            if type(otheritem) == data:
+                                for endkey, enditem in otheritem.iteritems():
+                                    if endkey == subvalue:
+                                        paramwin = self.FindWindowById(enditem)
+                                    else:
+                                        continue
+                            else:
+                                if otherkey == subvalue:
+                                    paramwin = self.FindWindowById(otheritem)
+                                else:
+                                    continue
+                            if type(insetting) in [tuple, list] and len(insetting) > 2:
+                                insetting = tuple(insetting)
+                                paramwin.SetColour(insetting)
+                            else:
+                                try:
+                                    paramwin.SetValue(insetting)
+                                except:
+                                    try:
+                                        paramwin.SetStringSelection(insetting)
+                                    except:
+                                        continue
+                                
+        self.toolWin.UpdateSettings()
+        self.FindWindowById(self.win['view']['position']).Draw()
+        self.FindWindowById(self.win['view']['position']).Refresh(False)
+        
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+    def OnSave(self, event):
+        """!Save button pressed
+        
+        Save settings to configuration file
+        """
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings = fileSettings)
+        
+        self.toolWin.UpdateSettings()
+        
+        nvsettings = UserSettings.Get(group = 'nviz')
+        for subgroup, key in nvsettings.iteritems(): # view, surface, vector...
+            for subkey, value in key.iteritems():
+                if subkey == 'height': continue
+                for subvalue in value.keys():
+                    if subvalue == 'step':
+                        #no way to change steps for sliders or spinctrls on non-MSW systems
+                        nvsettings[subgroup][subkey][subvalue] = 1 
+                    else:
+                        if subgroup == 'view':
+                            nvsettings[subgroup][subkey][subvalue] = self.mapWindow.view[subkey][subvalue]                            
+                        elif subvalue == 'map':
+                            if subkey == 'shine': 
+                                nvsettings[subgroup][subkey][subvalue] = False
+                            if subkey == 'color': 
+                                nvsettings[subgroup][subkey][subvalue] = True
+                        else:
+                            for otherkey, otheritem in self.win[subgroup][subkey].iteritems():
+                                if type(otheritem) == data:
+                                    for endkey, enditem in otheritem.iteritems():
+                                        if endkey == subvalue:
+                                            if self.FindWindowById(enditem).GetClassName() == 'wxChoice':
+                                                outsetting = self.FindWindowById(enditem).GetSelection()
+                                            else:
+                                                try:
+                                                    outsetting = self.FindWindowById(enditem).GetColour()
+                                                    outsetting = str(outsetting.Red())+':'+str(outsetting.Green())+':'+str(outsetting.Blue())
+                                                except:
+                                                    try:
+                                                        outsetting = self.FindWindowById(enditem).GetValue()
+                                                    except:
+                                                        try:
+                                                            outsetting = self.FindWindowById(enditem).GetString()
+                                                        except:
+                                                            outsetting = ''
+                                            if (type(outsetting) == list or type(outsetting) == tuple) and len(outsetting) > 2:
+                                                outsetting = str(outsetting[0])+':'+str(outsetting[1])+':'+str(outsetting[2])
+                                                
+                                            nvsettings[subgroup][subkey][subvalue][endkey] = outsetting
+                                else:
+                                    if otherkey == subvalue:
+                                        if self.FindWindowById(otheritem).GetClassName() == 'wxChoice':
+                                            outsetting = self.FindWindowById(otheritem).GetSelection()
+                                        else:
+                                            try:
+                                                outsetting = self.FindWindowById(otheritem).GetColour()
+                                                outsetting = str(outsetting.Red())+':'+str(outsetting.Green())+':'+str(outsetting.Blue())
+                                            except:
+                                                try:
+                                                    outsetting = self.FindWindowById(otheritem).GetValue()
+                                                except:
+                                                    try:
+                                                        outsetting = self.FindWindowById(enditem).GetString()
+                                                    except:
+                                                        outsetting = ''
+                                        if (type(outsetting) == list or type(outsetting) == tuple) and len(outsetting) > 2:
+                                            outsetting = str(outsetting[0])+':'+str(outsetting[1])+':'+str(outsetting[2])
+
+                                        nvsettings[subgroup][subkey][subvalue] = outsetting
+                               
+        UserSettings.Set(group = 'nviz', value = nvsettings)
+        file = UserSettings.SaveToFile()
+        self.parent.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/nviz_tools.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ogc_services.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ogc_services.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ogc_services.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,298 @@
+"""!
+ at package ogc_services.py
+
+ at brief Dialogs for OGC services
+
+Currently only implemeted WMS.
+
+List of classes:
+ - WMSDialog
+ - LayersList
+
+(C) 2009 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
+
+import gcmd
+
+from preferences import globalSettings as 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 = gcmd.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():
+            key, value = line.split(':', 1)
+            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 not layers[lastLayer].has_key('style'):
+                    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 data[layer].has_key('style'):
+                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


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/ogc_services.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/preferences.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/preferences.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/preferences.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package preferences
 
 @brief User preferences dialog
@@ -6,23 +6,27 @@
 Sets default display font, etc.
 
 Classes:
+ - Settings
+ - PreferencesBaseDialog
  - PreferencesDialog
  - DefaultFontDialog
  - MapsetAccess
+ - NvizPreferencesDialog
 
-(C) 2007-2009 by the GRASS Development Team
+(C) 2007-2010 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.
 
 @author Michael Barton (Arizona State University)
-Martin Landa <landa.martin gmail.com>
+ at author Martin Landa <landa.martin gmail.com>
 """
 
 import os
 import sys
 import copy
 import stat
+import types
 try:
     import pwd
     havePwd = True
@@ -37,16 +41,16 @@
 import wx.lib.filebrowsebutton as filebrowse
 import wx.lib.colourselect as csel
 import wx.lib.mixins.listctrl as listmix
-from wx.lib.wordwrap import wordwrap
 
+from grass.script import core as grass
+
 import gcmd
-import grassenv
 import utils
 import globalvar
 from debug import Debug as Debug
 
 class Settings:
-    """Generic class where to store settings"""
+    """!Generic class where to store settings"""
     def __init__(self):
         #
         # settings filename
@@ -59,6 +63,11 @@
         #
         self.sep = ';'
 
+        try:
+            projFile = utils.PathJoin(os.environ["GRASS_PROJSHARE"], 'epsg')
+        except KeyError:
+            projFile = ''
+        
         #
         # default settings
         #
@@ -99,6 +108,10 @@
                     'type' : '',
                     'encoding': 'ISO-8859-1',
                     },
+                'outputfont' : {
+                    'type' : 'Courier New',
+                    'size': '10',
+                    },
                 'driver': {
                     'type': 'default'
                     },
@@ -108,14 +121,31 @@
                 'autoRendering': {
                     'enabled' : True
                     },
+                'autoZooming' : {
+                    'enabled' : False
+                    },
                 'statusbarMode': {
                     'selection' : 0
                     },
                 'bgcolor': {
                     'color' : (255, 255, 255, 255),
-                    }
+                    },
                 },
             #
+            # projection
+            #
+            'projection' : {
+                'statusbar' : {
+                    'proj4'    : '',
+                    'epsg'     : '',
+                    'projFile' : projFile,
+                    },
+                'format' : {
+                    'll'  : 'DMS',
+                    'precision' : 2,
+                    },
+                },
+            #
             # advanced
             #
             'advanced' : {
@@ -164,6 +194,10 @@
                 'rasterOverlay' : {
                     'enabled' : True
                     },
+                'rasterColorTable' : {
+                    'enabled'   : False,
+                    'selection' : 'rainbow',
+                    },
                 # d.vect
                 'showType': {
                     'point' : {
@@ -186,7 +220,7 @@
                         },
                     },
                 'addNewLayer' : {
-                    'enabled' : False
+                    'enabled' : True,
                     },
                 },
             #
@@ -297,6 +331,14 @@
                 'delRecord' : {
                     'enabled' : True
                     },
+                # add centroid to left/right area
+                'addCentroid' : {
+                    'enabled' : False
+                    },
+                # do not attach category to boundary
+                'catBoundary' : {
+                    'enabled' : False
+                    },
                 # query tool
                 'query' : {
                     'selection' : 0,
@@ -416,12 +458,12 @@
             'nviz' : {
                 'view' : {
                     'persp' : {
-                        'value' : 40,
+                        'value' : 20,
                         'step' : 5,
                         },
-                    'pos' : {
-                        'x' : 0.85,
-                        'y' : 0.85,
+                    'position' : {
+                        'x' : 0.84,
+                        'y' : 0.16,
                         },
                     'height' : {
                         'step' : 100,
@@ -431,9 +473,11 @@
                         'step' : 5,
                         },
                     'z-exag' : {
-                        'value': 1,
                         'step' : 1,
                         },
+                    'background' : {
+                        'color' : (255, 255, 255, 255), # white
+                        },
                     },
                 'surface' : {
                     'shine': {
@@ -481,21 +525,71 @@
                         'value' : (0, 0, 0, 255), # constant: black
                         },
                     'draw' : {
-                        'mode' : 0, # isosurfaces
-                        'shading' : 1, # gouraud
+                        'mode'       : 0, # isosurfaces
+                        'shading'    : 1, # gouraud
                         'resolution' : 3, # polygon resolution
                         },
                     'shine': {
                         'map' : False,
-                        'value' : 60.0,
+                        'value' : 60,
                         },
                     },
-                'settings': {
-                    'general' : {
-                        'bgcolor' : (255, 255, 255, 255), # white
+                'light' : {
+                    'position' : {
+                        'x' : 0.68,
+                        'y' : 0.68,
+                        'z' : 80,
                         },
+                    'bright'  : 80,
+                    'color'   : (255, 255, 255, 255), # white
+                    'ambient' : 20,
                     },
+                'fringe' : {
+                    'elev'   : 55,
+                    'color'  : (128, 128, 128, 255), # grey
+                    },
                 },
+            'modeler' : {
+                'action' : {
+                    'color' : {
+                        'valid'   :  (180, 234, 154, 255), # light green
+                        'invalid' :  (255, 255, 255, 255), # white
+                        'running' :  (255, 0, 0, 255),     # red
+                        'disabled' : (211, 211, 211, 255), # light grey
+                        },
+                    'size' : {
+                        'width'  : 100,
+                        'height' : 50,
+                        },
+                    'width': {
+                        'parameterized' : 2,
+                        'default'       : 1,
+                        },
+                    },
+                'data' : { 
+                    'color': {
+                        'raster'   : (215, 215, 248, 255), # light blue
+                        'raster3d' : (215, 248, 215, 255), # light green
+                        'vector'   : (248, 215, 215, 255), # light red
+                        },
+                    'size' : {
+                        'width' : 175,
+                        'height' : 50,
+                        },
+                    },
+                'loop' : {
+                    'size' : {
+                        'width' : 175,
+                        'height' : 40,
+                        },
+                    },
+                'if-else' : {
+                    'size' : {
+                        'width' : 150,
+                        'height' : 40,
+                        },
+                    },
+                },
             }
         
         #
@@ -504,8 +598,8 @@
         self.userSettings = copy.deepcopy(self.defaultSettings)
         try:
             self.ReadSettingsFile()
-        except gcmd.SettingsError, e:
-            print >> sys.stderr, e.message
+        except gcmd.GException, e:
+            print >> sys.stderr, e.value
 
         #
         # internal settings (based on user settings)
@@ -563,18 +657,16 @@
         self.internalSettings['vdigit']['bgmap']['value'] = ''
         
     def ReadSettingsFile(self, settings=None):
-        """Reads settings file (mapset, location, gisdbase)"""
+        """!Reads settings file (mapset, location, gisdbase)"""
         if settings is None:
             settings = self.userSettings
 
         # look for settings file
-        # -> mapser
-        #  -> location
-        #   -> gisdbase
-        gisdbase = grassenv.GetGRASSVariable("GISDBASE")
-        location_name = grassenv.GetGRASSVariable("LOCATION_NAME")
-        mapset_name = grassenv.GetGRASSVariable("MAPSET")
-
+        gisenv = grass.gisenv()
+        gisdbase = gisenv['GISDBASE']
+        location_name = gisenv['LOCATION_NAME']
+        mapset_name = gisenv['MAPSET']
+        
         mapset_file = os.path.join(gisdbase, location_name, mapset_name, self.fileName)
         location_file = os.path.join(gisdbase, location_name, self.fileName)
         gisdbase_file = os.path.join(gisdbase, self.fileName)
@@ -599,7 +691,7 @@
                                                 key='font', subkey='encoding')
         
     def __ReadFile(self, filename, settings=None):
-        """Read settings from file to dict"""
+        """!Read settings from file to dict"""
         if settings is None:
             settings = self.userSettings
 
@@ -635,15 +727,17 @@
         file.close()
 
     def SaveToFile(self, settings=None):
-        """Save settings to the file"""
+        """!Save settings to the file"""
         if settings is None:
             settings = self.userSettings
         
         loc = self.Get(group='advanced', key='settingsFile', subkey='type')
         home = os.path.expanduser("~") # MS Windows fix ?
-        gisdbase = grassenv.GetGRASSVariable("GISDBASE")
-        location_name = grassenv.GetGRASSVariable("LOCATION_NAME")
-        mapset_name = grassenv.GetGRASSVariable("MAPSET")
+        
+        gisenv = grass.gisenv()
+        gisdbase = gisenv['GISDBASE']
+        location_name = gisenv['LOCATION_NAME']
+        mapset_name = gisenv['MAPSET']
         filePath = None
         if loc == 'home':
             filePath = os.path.join(home, self.fileName)
@@ -665,7 +759,7 @@
                     subkeys = settings[group][key].keys()
                     for idx in range(len(subkeys)):
                         value = settings[group][key][subkeys[idx]]
-                        if type(value) == type({}):
+                        if type(value) == types.DictType:
                             if idx > 0:
                                 file.write('%s%s%s%s%s' % (os.linesep, group, self.sep, key, self.sep))
                             file.write('%s%s' % (subkeys[idx], self.sep))
@@ -677,25 +771,28 @@
                                                        svalue))
                                 if sidx < len(kvalues) - 1:
                                     file.write('%s' % self.sep)
+                            if idx < len(subkeys) - 1:
+                                file.write('%s%s%s%s%s' % (os.linesep, group, self.sep, key, self.sep))
                         else:
                             value = self.__parseValue(settings[group][key][subkeys[idx]])
                             file.write('%s%s%s' % (subkeys[idx], self.sep, value))
-                            if idx < len(subkeys) - 1:
+                            if idx < len(subkeys) - 1 and \
+                                    type(settings[group][key][subkeys[idx + 1]]) != types.DictType:
                                 file.write('%s' % self.sep)
-                    file.write('%s' % os.linesep)
+                    file.write(os.linesep)
         except IOError, e:
-            raise gcmd.SettingsError(message=e)
+            raise gcmd.GException(e)
         except StandardError, e:
-            raise gcmd.SettingsError(message=_('Writing settings to file <%(file)s> failed.'
-                                               '\n\nDetails: %(detail)s') % { 'file' : filePath,
-                                                                              'detail' : e })
+            raise gcmd.GException(_('Writing settings to file <%(file)s> failed.'
+                                    '\n\nDetails: %(detail)s') % { 'file' : filePath,
+                                                                   'detail' : e })
         
         file.close()
         
         return filePath
 
     def __parseValue(self, value, read=False):
-        """Parse value to be store in settings file"""
+        """!Parse value to be store in settings file"""
         if read: # -> read settings (cast values)
             if value == 'True':
                 value = True
@@ -725,7 +822,7 @@
         return value
 
     def Get(self, group, key=None, subkey=None, internal=False):
-        """Get value by key/subkey
+        """!Get value by key/subkey
 
         Raise KeyError if key is not found
         
@@ -748,21 +845,19 @@
                 else:
                     return settings[group][key]
             else:
-                if type(subkey) == type([]) or \
-                        type(subkey) == type(()):
+                if type(subkey) == type(tuple()) or \
+                        type(subkey) == type(list()):
                     return settings[group][key][subkey[0]][subkey[1]]
                 else:
                     return settings[group][key][subkey]  
 
         except KeyError:
-            #raise gcmd.SettingsError("%s %s:%s:%s." % (_("Unable to get value"),
-            #                                           group, key, subkey))
             print >> sys.stderr, "Settings: unable to get value '%s:%s:%s'\n" % \
                 (group, key, subkey)
         
-    def Set(self, group, value, key=None, subkey=None, internal=False):
-        """Set value of key/subkey
-
+    def Set(self, group, value, key = None, subkey = None, internal = False):
+        """!Set value of key/subkey
+        
         Raise KeyError if group/key is not found
         
         @param group settings group
@@ -775,7 +870,7 @@
             settings = self.internalSettings
         else:
             settings = self.userSettings
-
+        
         try:
             if subkey is None:
                 if key is None:
@@ -783,15 +878,16 @@
                 else:
                     settings[group][key] = value
             else:
-                if type(subkey) == type([]):
+                if type(subkey) == type(tuple()) or \
+                        type(subkey) == type(list()):
                     settings[group][key][subkey[0]][subkey[1]] = value
                 else:
                     settings[group][key][subkey] = value
         except KeyError:
-            raise gcmd.SettingsError("%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey))
-
+            raise gcmd.GException("%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey))
+        
     def Append(self, dict, group, key, subkey, value):
-        """Set value of key/subkey
+        """!Set value of key/subkey
 
         Create group/key/subkey if not exists
         
@@ -803,92 +899,199 @@
         """
         if not dict.has_key(group):
             dict[group] = {}
-
+        
         if not dict[group].has_key(key):
             dict[group][key] = {}
-
-        if type(subkey) == type([]):
+        
+        if type(subkey) == types.ListType:
             # TODO: len(subkey) > 2
             if not dict[group][key].has_key(subkey[0]):
                 dict[group][key][subkey[0]] = {}
-            dict[group][key][subkey[0]][subkey[1]] = value
+            try:
+                dict[group][key][subkey[0]][subkey[1]] = value
+            except TypeError:
+                print >> sys.stderr, _("Unable to parse settings '%s'") % value + \
+                    ' (' + group + ':' + key + ':' + subkey[0] + ':' + subkey[1] + ')'
         else:
-            dict[group][key][subkey] = value
-
+            try:
+                dict[group][key][subkey] = value
+            except TypeError:
+                print >> sys.stderr, _("Unable to parse settings '%s'") % value + \
+                    ' (' + group + ':' + key + ':' + subkey + ')'
+        
     def GetDefaultSettings(self):
-        """Get default user settings"""
+        """!Get default user settings"""
         return self.defaultSettings
 
 globalSettings = Settings()
 
-class PreferencesDialog(wx.Dialog):
-    """User preferences dialog"""
-    def __init__(self, parent, title=_("User GUI settings"),
-                 settings=globalSettings,
-                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
-        self.parent = parent # GMFrame
-        self.title = title
-        wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title,
-                           style=style, size=(-1, -1))
-
+class PreferencesBaseDialog(wx.Dialog):
+    """!Base preferences dialog"""
+    def __init__(self, parent, settings, title = _("User settings"),
+                 size = (500, 375),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        self.parent = parent # ModelerFrame
+        self.title  = title
+        self.size   = size
         self.settings = settings
+        
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+                           style = style)
+        
         # notebook
-        notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
-
+        self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
+        
         # dict for window ids
         self.winId = {}
-
+        
         # create notebook pages
-        self.__CreateGeneralPage(notebook)
-        self.__CreateDisplayPage(notebook)
-        self.__CreateCmdPage(notebook)
-        self.__CreateAttributeManagerPage(notebook)
-        self.__CreateWorkspacePage(notebook)
-        self.__CreateAdvancedPage(notebook)
-
+        
         # buttons
-        btnDefault = wx.Button(self, wx.ID_ANY, _("Set to default"))
-        btnSave = wx.Button(self, wx.ID_SAVE)
-        btnApply = wx.Button(self, wx.ID_APPLY)
-        btnCancel = wx.Button(self, wx.ID_CANCEL)
-        btnSave.SetDefault()
-
+        self.btnDefault = wx.Button(self, wx.ID_ANY, _("Set to default"))
+        self.btnSave = wx.Button(self, wx.ID_SAVE)
+        self.btnApply = wx.Button(self, wx.ID_APPLY)
+        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+        self.btnSave.SetDefault()
+        
         # bindigs
-        btnDefault.Bind(wx.EVT_BUTTON, self.OnDefault)
-        btnDefault.SetToolTipString(_("Revert settings to default and apply changes"))
-        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
-        btnApply.SetToolTipString(_("Apply changes for the current session"))
-        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
-        btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
-        btnSave.SetDefault()
-        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
-        btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+        self.btnDefault.Bind(wx.EVT_BUTTON, self.OnDefault)
+        self.btnDefault.SetToolTipString(_("Revert settings to default and apply changes"))
+        self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        self.btnApply.SetToolTipString(_("Apply changes for the current session"))
+        self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        self.btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+        self.btnSave.SetDefault()
+        self.btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        self.btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
 
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+        self._layout()
+        
+    def _layout(self):
+        """!Layout window"""
         # sizers
         btnSizer = wx.BoxSizer(wx.HORIZONTAL)
-        btnSizer.Add(item=btnDefault, proportion=1,
+        btnSizer.Add(item=self.btnDefault, proportion=1,
                      flag=wx.ALL, border=5)
         btnStdSizer = wx.StdDialogButtonSizer()
-        btnStdSizer.AddButton(btnCancel)
-        btnStdSizer.AddButton(btnSave)
-        btnStdSizer.AddButton(btnApply)
+        btnStdSizer.AddButton(self.btnCancel)
+        btnStdSizer.AddButton(self.btnSave)
+        btnStdSizer.AddButton(self.btnApply)
         btnStdSizer.Realize()
         
         mainSizer = wx.BoxSizer(wx.VERTICAL)
-        mainSizer.Add(item=notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
         mainSizer.Add(item=btnSizer, proportion=0,
                       flag=wx.EXPAND, border=0)
         mainSizer.Add(item=btnStdSizer, proportion=0,
                       flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
-
+        
         self.SetSizer(mainSizer)
         mainSizer.Fit(self)
+        
+    def OnDefault(self, event):
+        """!Button 'Set to default' pressed"""
+        self.settings.userSettings = copy.deepcopy(self.settings.defaultSettings)
+        
+        # update widgets
+        for gks in self.winId.keys():
+            try:
+                group, key, subkey = gks.split(':')
+                value = self.settings.Get(group, key, subkey)
+            except ValueError:
+                group, key, subkey, subkey1 = gks.split(':')
+                value = self.settings.Get(group, key, [subkey, subkey1])
+            win = self.FindWindowById(self.winId[gks])
+            if win.GetName() in ('GetValue', 'IsChecked'):
+                value = win.SetValue(value)
+            elif win.GetName() == 'GetSelection':
+                value = win.SetSelection(value)
+            elif win.GetName() == 'GetStringSelection':
+                value = win.SetStringSelection(value)
+            else:
+                value = win.SetValue(value)
+        
+    def OnApply(self, event):
+        """!Button 'Apply' pressed"""
+        if self._updateSettings():
+            self.parent.goutput.WriteLog(_('Settings applied to current session but not saved'))
+            self.Close()
 
+    def OnCloseWindow(self, event):
+        self.Hide()
+        
+    def OnCancel(self, event):
+        """!Button 'Cancel' pressed"""
+        self.Close()
+        
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        if self._updateSettings():
+            file = self.settings.SaveToFile()
+            self.parent.goutput.WriteLog(_('Settings saved to file \'%s\'.') % file)
+            self.Close()
+
+    def _updateSettings(self):
+        """!Update user settings"""
+        for item in self.winId.keys():
+            try:
+                group, key, subkey = item.split(':')
+                subkey1 = None
+            except ValueError:
+                group, key, subkey, subkey1 = item.split(':')
+            
+            id = self.winId[item]
+            win = self.FindWindowById(id)
+            if win.GetName() == 'GetValue':
+                value = win.GetValue()
+            elif win.GetName() == 'GetSelection':
+                value = win.GetSelection()
+            elif win.GetName() == 'IsChecked':
+                value = win.IsChecked()
+            elif win.GetName() == 'GetStringSelection':
+                value = win.GetStringSelection()
+            elif win.GetName() == 'GetColour':
+                value = tuple(win.GetValue())
+            else:
+                value = win.GetValue()
+
+            if key == 'keycolumn' and value == '':
+                wx.MessageBox(parent=self,
+                              message=_("Key column cannot be empty string."),
+                              caption=_("Error"), style=wx.OK | wx.ICON_ERROR)
+                win.SetValue(self.settings.Get(group='atm', key='keycolumn', subkey='value'))
+                return False
+
+            if subkey1:
+                self.settings.Set(group, value, key, [subkey, subkey1])
+            else:
+                self.settings.Set(group, value, key, subkey)
+        
+        return True
+
+class PreferencesDialog(PreferencesBaseDialog):
+    """!User preferences dialog"""
+    def __init__(self, parent, title = _("GUI settings"),
+                 settings = globalSettings):
+        
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        
+        # create notebook pages
+        self._CreateGeneralPage(self.notebook)
+        self._CreateDisplayPage(self.notebook)
+        self._CreateCmdPage(self.notebook)
+        self._CreateAttributeManagerPage(self.notebook)
+        self._CreateProjectionPage(self.notebook)
+        self._CreateWorkspacePage(self.notebook)
+        self._CreateAdvancedPage(self.notebook)
+        
         self.SetMinSize(self.GetBestSize())
-        self.SetSize((500, 375))
-
-    def __CreateGeneralPage(self, notebook):
-        """Create notebook page for general settings"""
+        self.SetSize(self.size)
+        
+    def _CreateGeneralPage(self, notebook):
+        """!Create notebook page for general settings"""
         panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
         notebook.AddPage(page=panel, text=_("General"))
 
@@ -979,8 +1182,9 @@
         
         return panel
 
-    def __CreateDisplayPage(self, notebook):
-        """Create notebook page for display settings"""
+    def _CreateDisplayPage(self, notebook):
+        """!Create notebook page for display settings"""
+   
         panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
         notebook.AddPage(page=panel, text=_("Display"))
 
@@ -1011,6 +1215,22 @@
         sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
         border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
 
+        row = 1
+        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                         label=_("Font for command output:")),
+                      flag=wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        outfontButton = wx.Button(parent=panel, id=wx.ID_ANY,
+                               label=_("Set font"), size=(100, -1))
+        gridSizer.Add(item=outfontButton,
+                      flag=wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 1))
+
+        #
+        # display settings
+        #
         box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Default display settings"))
         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
 
@@ -1029,10 +1249,13 @@
         listOfDrivers = self.settings.Get(group='display', key='driver', subkey='choices', internal=True)
         # check if cairo is available
         if 'cairo' not in listOfDrivers:
-            for line in gcmd.Command(['d.mon', '-l']).ReadStdOutput():
+            for line in gcmd.RunCommand('d.mon',
+                                        flags = 'l',
+                                        read = True).splitlines():
                 if 'cairo' in line:
                     listOfDrivers.append('cairo')
                     break
+        
         driver = wx.Choice(parent=panel, id=wx.ID_ANY, size=(150, -1),
                            choices=listOfDrivers,
                            name="GetStringSelection")
@@ -1075,7 +1298,7 @@
                       pos=(row, 0))
         bgColor = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
                                     colour=self.settings.Get(group='display', key='bgcolor', subkey='color'),
-                                    size=(35, 35))
+                                    size=globalvar.DIALOG_COLOR_SIZE)
         bgColor.SetName('GetColour')
         self.winId['display:bgcolor:color'] = bgColor.GetId()
         
@@ -1108,19 +1331,33 @@
 
         gridSizer.Add(item=autoRendering,
                       pos=(row, 0), span=(1, 2))
+        
+        #
+        # auto-zoom
+        #
+        row += 1
+        autoZooming = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                  label=_("Enable auto-zooming to selected map layer"),
+                                  name="IsChecked")
+        autoZooming.SetValue(self.settings.Get(group='display', key='autoZooming', subkey='enabled'))
+        self.winId['display:autoZooming:enabled'] = autoZooming.GetId()
 
+        gridSizer.Add(item=autoZooming,
+                      pos=(row, 0), span=(1, 2))
+
         sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
         border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
-
+        
         panel.SetSizer(border)
-        
+                
         # bindings
         fontButton.Bind(wx.EVT_BUTTON, self.OnSetFont)
+        outfontButton.Bind(wx.EVT_BUTTON, self.OnSetOutputFont)
         
         return panel
 
-    def __CreateCmdPage(self, notebook):
-        """Create notebook page for commad dialog settings"""
+    def _CreateCmdPage(self, notebook):
+        """!Create notebook page for commad dialog settings"""
         panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
         notebook.AddPage(page=panel, text=_("Command"))
         
@@ -1204,7 +1441,30 @@
         
         gridSizer.Add(item=rasterOverlay,
                       pos=(row, 0), span=(1, 2))
+
+        # default color table
+        row += 1
+        rasterCTCheck = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                    label=_("Default color table"),
+                                    name='IsChecked')
+        rasterCTCheck.SetValue(self.settings.Get(group='cmd', key='rasterColorTable', subkey='enabled'))
+        self.winId['cmd:rasterColorTable:enabled'] = rasterCTCheck.GetId()
+        rasterCTCheck.Bind(wx.EVT_CHECKBOX, self.OnCheckColorTable)
         
+        gridSizer.Add(item=rasterCTCheck,
+                      pos=(row, 0))
+        
+        rasterCTName = wx.Choice(parent=panel, id=wx.ID_ANY, size=(200, -1),
+                               choices=utils.GetColorTables(),
+                               name="GetStringSelection")
+        rasterCTName.SetStringSelection(self.settings.Get(group='cmd', key='rasterColorTable', subkey='selection'))
+        self.winId['cmd:rasterColorTable:selection'] = rasterCTName.GetId()
+        if not rasterCTCheck.IsChecked():
+            rasterCTName.Enable(False)
+        
+        gridSizer.Add(item=rasterCTName,
+                      pos=(row, 1))
+        
         sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
         border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
         
@@ -1236,8 +1496,8 @@
         
         return panel
 
-    def __CreateAttributeManagerPage(self, notebook):
-        """Create notebook page for 'Attribute Table Manager' settings"""
+    def _CreateAttributeManagerPage(self, notebook):
+        """!Create notebook page for 'Attribute Table Manager' settings"""
         panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
         notebook.AddPage(page=panel, text=_("Attributes"))
 
@@ -1256,7 +1516,7 @@
         label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color:"))
         hlColor = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
                                     colour=self.settings.Get(group='atm', key='highlight', subkey='color'),
-                                    size=(35, 35))
+                                    size=globalvar.DIALOG_COLOR_SIZE)
         hlColor.SetName('GetColour')
         self.winId['atm:highlight:color'] = hlColor.GetId()
 
@@ -1365,9 +1625,144 @@
 
         return panel
 
-    def __CreateWorkspacePage(self, notebook):
-        """Create notebook page for workspace settings"""
+    def _CreateProjectionPage(self, notebook):
+        """!Create notebook page for workspace settings"""
         panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
+        notebook.AddPage(page=panel, text=_("Projection"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #
+        # projections statusbar settings
+        #
+        box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Projection statusbar settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(1)
+
+        # epsg
+        row = 0
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                              label=_("EPSG code:"))
+        epsgCode = wx.ComboBox(parent=panel, id=wx.ID_ANY,
+                               name="GetValue",
+                               size = (150, -1))
+        self.epsgCodeDict = dict()
+        epsgCode.SetValue(str(self.settings.Get(group='projection', key='statusbar', subkey='epsg')))
+        self.winId['projection:statusbar:epsg'] = epsgCode.GetId()
+        
+        gridSizer.Add(item=label,
+                      pos=(row, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item=epsgCode,
+                      pos=(row, 1), span=(1, 2))
+        
+        # proj
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                              label=_("Proj.4 string (required):"))
+        projString = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+                                 value=self.settings.Get(group='projection', key='statusbar', subkey='proj4'),
+                                 name="GetValue", size=(400, -1))
+        self.winId['projection:statusbar:proj4'] = projString.GetId()
+
+        gridSizer.Add(item=label,
+                      pos=(row, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item=projString,
+                      pos=(row, 1), span=(1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # epsg file
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                              label=_("EPSG file:"))
+        projFile = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+                               value = self.settings.Get(group='projection', key='statusbar', subkey='projFile'),
+                               name="GetValue", size=(400, -1))
+        self.winId['projection:statusbar:projFile'] = projFile.GetId()
+        gridSizer.Add(item=label,
+                      pos=(row, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item=projFile,
+                      pos=(row, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # note + button
+        row += 1
+        note = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                             label = _("Load EPSG codes (be patient), enter EPSG code or "
+                                       "insert Proj.4 string directly."))
+        gridSizer.Add(item=note,
+                      span = (1, 2),
+                      pos=(row, 0))
+
+        row += 1
+        epsgLoad = wx.Button(parent=panel, id=wx.ID_ANY,
+                             label=_("&Load EPSG codes"))
+        gridSizer.Add(item=epsgLoad,
+                      flag = wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
+
+        #
+        # format
+        #
+        box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Coordinates format"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(2)
+
+        row = 0
+        # ll format
+        ll = wx.RadioBox(parent = panel, id = wx.ID_ANY,
+                         label = " %s " % _("LL projections"),
+                         choices = ["DMS", "DEG"],
+                         name = "GetStringSelection")
+        self.winId['projection:format:ll'] = ll.GetId()
+        if self.settings.Get(group = 'projection', key = 'format', subkey = 'll') == 'DMS':
+            ll.SetSelection(0)
+        else:
+            ll.SetSelection(1)
+        
+        # precision
+        precision =  wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                                 min = 0, max = 12,
+                                 name = "GetValue")
+        precision.SetValue(int(self.settings.Get(group = 'projection', key = 'format', subkey = 'precision')))
+        self.winId['projection:format:precision'] = precision.GetId()
+                
+        gridSizer.Add(item=ll,
+                      pos=(row, 0))
+        gridSizer.Add(item=wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Precision:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+                      border = 20,
+                      pos=(row, 1))
+        gridSizer.Add(item=precision,
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 2))
+        
+        
+        sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
+        border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
+        
+        panel.SetSizer(border)
+
+        # bindings
+        epsgLoad.Bind(wx.EVT_BUTTON, self.OnLoadEpsgCodes)
+        epsgCode.Bind(wx.EVT_COMBOBOX, self.OnSetEpsgCode)
+        epsgCode.Bind(wx.EVT_TEXT_ENTER, self.OnSetEpsgCode)
+        
+        return panel
+
+    def _CreateWorkspacePage(self, notebook):
+        """!Create notebook page for workspace settings"""
+        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
         notebook.AddPage(page=panel, text=_("Workspace"))
 
         border = wx.BoxSizer(wx.VERTICAL)
@@ -1409,8 +1804,8 @@
         
         return panel
 
-    def __CreateAdvancedPage(self, notebook):
-        """Create notebook page for advanced settings"""
+    def _CreateAdvancedPage(self, notebook):
+        """!Create notebook page for advanced settings"""
         panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
         notebook.AddPage(page=panel, text=_("Advanced"))
 
@@ -1478,11 +1873,82 @@
         
         return panel
 
+    def OnCheckColorTable(self, event):
+        """!Set/unset default color table"""
+        win = self.FindWindowById(self.winId['cmd:rasterColorTable:selection'])
+        if event.IsChecked():
+            win.Enable()
+        else:
+            win.Enable(False)
+        
+    def OnLoadEpsgCodes(self, event):
+        """!Load EPSG codes from the file"""
+        win = self.FindWindowById(self.winId['projection:statusbar:projFile'])
+        path = win.GetValue()
+
+        self.epsgCodeDict = utils.ReadEpsgCodes(path)
+        list = self.FindWindowById(self.winId['projection:statusbar:epsg'])
+        if type(self.epsgCodeDict) == type(''):
+            wx.MessageBox(parent=self,
+                          message=_("Unable to read EPSG codes: %s") % self.epsgCodeDict,
+                          caption=_("Error"),  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            self.epsgCodeDict = dict()
+            list.SetItems([])
+            list.SetValue('')
+            self.FindWindowById(self.winId['projection:statusbar:proj4']).SetValue('')
+            return
+        
+        choices = map(str, self.epsgCodeDict.keys())
+
+        list.SetItems(choices)
+        try:
+            code = int(list.GetValue())
+        except ValueError:
+            code = -1
+        win = self.FindWindowById(self.winId['projection:statusbar:proj4'])
+        if self.epsgCodeDict.has_key(code):
+            win.SetValue(self.epsgCodeDict[code][1])
+        else:
+            list.SetSelection(0)
+            code = int(list.GetStringSelection())
+            win.SetValue(self.epsgCodeDict[code][1])
+    
+    def OnSetEpsgCode(self, event):
+        """!EPSG code selected"""
+        winCode = self.FindWindowById(event.GetId())
+        win = self.FindWindowById(self.winId['projection:statusbar:proj4'])
+        if not self.epsgCodeDict:
+            wx.MessageBox(parent=self,
+                          message=_("EPSG code %s not found") % event.GetString(),
+                          caption=_("Error"),  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            winCode.SetValue('')
+            win.SetValue('')
+        
+        try:
+            code = int(event.GetString())
+        except ValueError:
+            wx.MessageBox(parent=self,
+                          message=_("EPSG code %s not found") % str(code),
+                          caption=_("Error"),  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            winCode.SetValue('')
+            win.SetValue('')
+        
+        
+        try:
+            win.SetValue(self.epsgCodeDict[code][1].replace('<>', '').strip())
+        except KeyError:
+            wx.MessageBox(parent=self,
+                          message=_("EPSG code %s not found") % str(code),
+                          caption=_("Error"),  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            winCode.SetValue('')
+            win.SetValue('')
+        
     def OnSetFont(self, event):
         """'Set font' button pressed"""
         dlg = DefaultFontDialog(parent=self, id=wx.ID_ANY,
                                 title=_('Select default display font'),
-                                style=wx.DEFAULT_DIALOG_STYLE)
+                                style=wx.DEFAULT_DIALOG_STYLE,
+                                type='font')
         
         if dlg.ShowModal() == wx.ID_OK:
             # set default font and encoding environmental variables
@@ -1500,81 +1966,54 @@
         dlg.Destroy()
         
         event.Skip()
+
+    def OnSetOutputFont(self, event):
+        """'Set output font' button pressed"""
         
-    def OnSave(self, event):
-        """Button 'Save' pressed"""
-        if self.__UpdateSettings():
-            file = self.settings.SaveToFile()
-            self.parent.goutput.WriteLog(_('Settings saved to file \'%s\'.') % file)
-            self.Close()
+        dlg = DefaultFontDialog(parent=self, id=wx.ID_ANY,
+                                title=_('Select output font'),
+                                style=wx.DEFAULT_DIALOG_STYLE,
+                                type='outputfont')
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            # set output font and font size variables
+            if dlg.font:
+                self.settings.Set(group='display', value=dlg.font,
+                                  key='outputfont', subkey='type')
 
-    def OnApply(self, event):
-        """Button 'Apply' pressed"""
-        if self.__UpdateSettings():
-            self.Close()
+                self.settings.Set(group='display', value=dlg.fontsize,
+                                  key='outputfont', subkey='size')
 
-    def OnCancel(self, event):
-        """Button 'Cancel' pressed"""
-        self.Close()
-
-    def OnDefault(self, event):
-        """Button 'Set to default' pressed"""
-        self.settings.userSettings = copy.deepcopy(self.settings.defaultSettings)
+# Standard font dialog broken for Mac in OS X 10.6
+#        type = self.settings.Get(group='display', key='outputfont', subkey='type')   
+                           
+#        size = self.settings.Get(group='display', key='outputfont', subkey='size')
+#        if size == None or size == 0: size = 10
+#        size = float(size)
         
-        # update widgets
-        for gks in self.winId.keys():
-            try:
-                group, key, subkey = gks.split(':')
-                value = self.settings.Get(group, key, subkey)
-            except ValueError:
-                group, key, subkey, subkey1 = gks.split(':')
-                value = self.settings.Get(group, key, [subkey, subkey1])
-            win = self.FindWindowById(self.winId[gks])
-            if win.GetName() in ('GetValue', 'IsChecked'):
-                value = win.SetValue(value)
-            elif win.GetName() == 'GetSelection':
-                value = win.SetSelection(value)
-            elif win.GetName() == 'GetStringSelection':
-                value = win.SetStringSelection(value)
-            else:
-                value = win.SetValue(value)
+#        data = wx.FontData()
+#        data.EnableEffects(True)
+#        data.SetInitialFont(wx.Font(pointSize=size, family=wx.FONTFAMILY_MODERN, faceName=type, style=wx.NORMAL, weight=0))
 
-    def __UpdateSettings(self):
-        """Update user settings"""
-        for item in self.winId.keys():
-            try:
-                group, key, subkey = item.split(':')
-                subkey1 = None
-            except ValueError:
-                group, key, subkey, subkey1 = item.split(':')
-            
-            id = self.winId[item]
-            win = self.FindWindowById(id)
-            if win.GetName() == 'GetValue':
-                value = win.GetValue()
-            elif win.GetName() == 'GetSelection':
-                value = win.GetSelection()
-            elif win.GetName() == 'IsChecked':
-                value = win.IsChecked()
-            elif win.GetName() == 'GetStringSelection':
-                value = win.GetStringSelection()
-            elif win.GetName() == 'GetColour':
-                value = tuple(win.GetValue())
-            else:
-                value = win.GetValue()
+#        dlg = wx.FontDialog(self, data)
 
-            if key == 'keycolumn' and value == '':
-                wx.MessageBox(parent=self,
-                              message=_("Key column cannot be empty string."),
-                              caption=_("Error"), style=wx.OK | wx.ICON_ERROR)
-                win.SetValue(self.settings.Get(group='atm', key='keycolumn', subkey='value'))
-                return False
+#        if dlg.ShowModal() == wx.ID_OK:
+#            data = dlg.GetFontData()
+#            font = data.GetChosenFont()
 
-            if subkey1:
-                self.settings.Set(group, value, key, [subkey, subkey1])
-            else:
-                self.settings.Set(group, value, key, subkey)
-            
+#            self.settings.Set(group='display', value=font.GetFaceName(),
+#                                  key='outputfont', subkey='type')
+#            self.settings.Set(group='display', value=font.GetPointSize(),
+#                                  key='outputfont', subkey='size')
+                
+        dlg.Destroy()
+
+        event.Skip()
+        
+    def _updateSettings(self):
+        """!Update user settings"""
+        PreferencesBaseDialog._updateSettings(self)
+        
         #
         # update default window dimension
         #
@@ -1604,25 +2043,19 @@
     """
     def __init__(self, parent, id, title,
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
-                 style=wx.DEFAULT_DIALOG_STYLE,
-                 settings=globalSettings):
+                 style=wx.DEFAULT_DIALOG_STYLE |
+                 wx.RESIZE_BORDER,
+                 settings=globalSettings,
+                 type='font'):
         
         self.settings = settings
+        self.type = type
         
         wx.Dialog.__init__(self, parent, id, title, pos, size, style)
 
         panel = wx.Panel(parent=self, id=wx.ID_ANY)
         
-        if "GRASS_FONT" in os.environ:
-            self.font = os.environ["GRASS_FONT"]
-        else:
-            self.font = self.settings.Get(group='display',
-                                          key='font', subkey='type')
-
         self.fontlist = self.GetFonts()
-
-        self.encoding = self.settings.Get(group='display',
-                                          key='font', subkey='encoding')
         
         border = wx.BoxSizer(wx.VERTICAL)
         box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Font settings"))
@@ -1642,24 +2075,57 @@
                                  style=wx.LB_SINGLE|wx.LB_SORT)
         self.Bind(wx.EVT_LISTBOX, self.EvtListBox, self.fontlb)
         self.Bind(wx.EVT_LISTBOX_DCLICK, self.EvtListBoxDClick, self.fontlb)
-        if self.font:
-            self.fontlb.SetStringSelection(self.font, True)
 
         gridSizer.Add(item=self.fontlb,
-                flag=wx.EXPAND, pos=(0, 1))
+                flag=wx.EXPAND, pos=(1, 0))
 
-        label = wx.StaticText(parent=panel, id=wx.ID_ANY,
-                              label=_("Character encoding:"))
-        gridSizer.Add(item=label,
+        if self.type == 'font':
+            if "GRASS_FONT" in os.environ:
+                self.font = os.environ["GRASS_FONT"]
+            else:
+                self.font = self.settings.Get(group='display',
+                                              key='font', subkey='type')
+            self.encoding = self.settings.Get(group='display',
+                                          key='font', subkey='encoding')
+
+            label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                  label=_("Character encoding:"))
+            gridSizer.Add(item=label,
+                          flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos=(2, 0))
+
+            self.textentry = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+                                         value=self.encoding)
+            gridSizer.Add(item=self.textentry,
+                    flag=wx.EXPAND, pos=(3, 0))
+
+            self.textentry.Bind(wx.EVT_TEXT, self.OnEncoding)
+
+        elif self.type == 'outputfont':
+            self.font = self.settings.Get(group='display',
+                                              key='outputfont', subkey='type')
+            self.fontsize = self.settings.Get(group='display',
+                                          key='outputfont', subkey='size')
+            label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                              label=_("Font size:"))
+            gridSizer.Add(item=label,
                       flag=wx.ALIGN_CENTER_VERTICAL,
-                      pos=(1, 0))
+                      pos=(2, 0))
+                      
+            self.spin = wx.SpinCtrl(parent=panel, id=wx.ID_ANY)
+            if self.fontsize:
+                self.spin.SetValue(self.fontsize)
+            self.spin.Bind(wx.EVT_SPINCTRL, self.OnSizeSpin)
+            self.spin.Bind(wx.EVT_TEXT, self.OnSizeSpin)
+            gridSizer.Add(item=self.spin,
+                      flag=wx.ALIGN_CENTER_VERTICAL,
+                      pos=(3, 0))
 
-        self.textentry = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
-                                     value=self.encoding)
-        gridSizer.Add(item=self.textentry,
-                flag=wx.EXPAND, pos=(1, 1))
+        else: 
+            return
 
-        self.textentry.Bind(wx.EVT_TEXT, self.OnEncoding)
+        if self.font:
+            self.fontlb.SetStringSelection(self.font, True)
 
         sizer.Add(item=gridSizer, proportion=1,
                   flag=wx.EXPAND | wx.ALL,
@@ -1706,7 +2172,11 @@
     def EvtListBoxDClick(self, event):
         self.font = event.GetString()
         event.Skip()
-
+        
+    def OnSizeSpin(self, event):
+        self.fontsize = self.spin.GetValue()
+        event.Skip()
+    
     def GetFonts(self):
         """
         parses fonts directory or fretypecap file to get a list of fonts for the listbox
@@ -1715,9 +2185,14 @@
 
         cmd = ["d.font", "-l"]
 
-        p = gcmd.Command(cmd, stderr=None)
+        ret = gcmd.RunCommand('d.font',
+                              read = True,
+                              flags = 'l')
 
-        dfonts = p.ReadStdOutput()
+        if not ret:
+            return fontlist
+
+        dfonts = ret.splitlines()
         dfonts.sort(lambda x,y: cmp(x.lower(), y.lower()))
         for item in range(len(dfonts)):
            # ignore duplicate fonts and those starting with #
@@ -1728,30 +2203,35 @@
         return fontlist
 
 class MapsetAccess(wx.Dialog):
+    """!Controls setting options and displaying/hiding map overlay
+    decorations
     """
-    Controls setting options and displaying/hiding map overlay decorations
-    """
-    def __init__(self, parent, id, title=_('Set/unset access to mapsets in current location'),
-                 pos=wx.DefaultPosition, size=(350, 400),
-                 style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title=_('Set/unset access to mapsets in current location'),
+                 size = (350, 400),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
         
-        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+        wx.Dialog.__init__(self, parent, id, title, size = size, style = style, **kwargs)
 
         self.all_mapsets_ordered = utils.ListOfMapsets(get = 'ordered')
-        self.accessible_mapsets = utils.ListOfMapsets(get = 'accessible')
-        self.curr_mapset = grassenv.GetGRASSVariable('MAPSET')
+        self.accessible_mapsets  = utils.ListOfMapsets(get = 'accessible')
+        self.curr_mapset = grass.gisenv()['MAPSET']
 
         # make a checklistbox from available mapsets and check those that are active
         sizer = wx.BoxSizer(wx.VERTICAL)
 
         label = wx.StaticText(parent=self, id=wx.ID_ANY,
-                              label=_("Check a mapset to make it accessible, uncheck it to hide it.%s"
-                                      "Note: The current mapset is always accessible.") % os.linesep)
+                              label=_("Check a mapset to make it accessible, uncheck it to hide it.\n"
+                                      "  Notes:\n"
+                                      "    - The current mapset is always accessible.\n"
+                                      "    - You may only write to the current mapset.\n"
+                                      "    - You may only write to mapsets which you own."))
+        
         sizer.Add(item=label, proportion=0,
                   flag=wx.ALL, border=5)
 
         self.mapsetlb = CheckListMapset(parent=self)
-        self.mapsetlb.LoadData(self.all_mapsets_ordered)
+        self.mapsetlb.LoadData()
         
         sizer.Add(item=self.mapsetlb, proportion=1,
                   flag=wx.ALL | wx.EXPAND, border=5)
@@ -1760,6 +2240,9 @@
         for mset in self.accessible_mapsets:
             self.mapsetlb.CheckItem(self.all_mapsets_ordered.index(mset), True)
 
+        # FIXME (howto?): grey-out current mapset
+        #self.mapsetlb.Enable(0, False)
+
         # dialog buttons
         line = wx.StaticLine(parent=self, id=wx.ID_ANY,
                              style=wx.LI_HORIZONTAL)
@@ -1786,7 +2269,7 @@
         self.SetMinSize(size)
         
     def GetMapsets(self):
-        """Get list of checked mapsets"""
+        """!Get list of checked mapsets"""
         ms = []
         i = 0
         for mset in self.all_mapsets_ordered:
@@ -1797,7 +2280,7 @@
         return ms
 
 class CheckListMapset(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
-    """List of mapset/owner/group"""
+    """!List of mapset/owner/group"""
     def __init__(self, parent, pos=wx.DefaultPosition,
                  log=None):
         self.parent = parent
@@ -1810,15 +2293,14 @@
         # setup mixins
         listmix.ListCtrlAutoWidthMixin.__init__(self)
 
-    def LoadData(self, mapsets):
-        """Load data into list"""
+    def LoadData(self):
+        """!Load data into list"""
         self.InsertColumn(0, _('Mapset'))
         self.InsertColumn(1, _('Owner'))
         ### self.InsertColumn(2, _('Group'))
+        gisenv = grass.gisenv()
+        locationPath = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'])
 
-        locationPath = os.path.join(grassenv.GetGRASSVariable('GISDBASE'),
-                                    grassenv.GetGRASSVariable('LOCATION_NAME'))
-        
         for mapset in self.parent.all_mapsets_ordered:
             index = self.InsertStringItem(sys.maxint, mapset)
             mapsetPath = os.path.join(locationPath,
@@ -1837,7 +2319,7 @@
         ### self.SetColumnWidth(col=1, width=wx.LIST_AUTOSIZE)
         
     def OnCheckItem(self, index, flag):
-        """Mapset checked/unchecked"""
+        """!Mapset checked/unchecked"""
         mapset = self.parent.all_mapsets_ordered[index]
         if mapset == self.parent.curr_mapset:
             self.CheckItem(index, True)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/profile.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/profile.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/profile.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package profile
 
 Profile analysis of GRASS raster maps and images.
@@ -11,13 +11,13 @@
  - TextDialog
  - OptDialog
 
-COPYRIGHT: (C) 2007-2008 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.
+(C) 2007-2008 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.
+
 @author Michael Barton
-Various updates: Martin Landa <landa.martin gmail.com>
+ at author Various updates by Martin Landa <landa.martin gmail.com>
 """
 
 import os
@@ -31,22 +31,14 @@
     import numpy
     import wx.lib.plot as plot
 except ImportError:
-    msg= """
-    This module requires the NumPy module, which could not be
-    imported. It probably is not installed (it's not part of the
-    standard Python distribution). See the Numeric Python site
-    (http://numpy.scipy.org) for information on downloading source or
-    binaries."""
+    msg= _("This module requires the NumPy module, which could not be "
+           "imported. It probably is not installed (it's not part of the "
+           "standard Python distribution). See the Numeric Python site "
+           "(http://numpy.scipy.org) for information on downloading source or "
+           "binaries.")
     print >> sys.stderr, "profile.py: " + msg
 
 import globalvar
-try:
-    import subprocess
-except:
-    CompatPath = os.path.join(globalvar.ETCWXDIR)
-    sys.path.append(CompatPath)
-    from compat import subprocess as subprocess
-
 import render
 import menuform
 import disp_print
@@ -59,10 +51,8 @@
 from grass.script import core as grass
 
 class ProfileFrame(wx.Frame):
+    """!Mainframe for displaying profile of raster map. Uses wx.lib.plot.
     """
-    Mainframe for displaying profile of raster map. Uses wx.lib.plot.
-    """
-
     def __init__(self, parent=None, id=wx.ID_ANY, title=_("Profile Analysis"),
                  rasterList=[],
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
@@ -86,12 +76,12 @@
         #
         # Icon
         #
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
         
         #
         # Add toolbar
         #
-        self.toolbar = toolbars.ProfileToolbar(parent=self, tbframe=self).GetToolbar()
+        self.toolbar = toolbars.ProfileToolbar(parent=self)
         self.SetToolBar(self.toolbar)
         
         #
@@ -221,9 +211,8 @@
         self.CentreOnScreen()
 
     def OnDrawTransect(self, event):
+        """!Draws transect to profile in map display
         """
-        Draws transect to profile in map display
-        """
         self.mapwin.polycoords = []
         self.seglist = []
         self.mapwin.ClearLines(self.mapwin.pdc)
@@ -239,10 +228,8 @@
         self.mapwin.SetCursor(self.Parent.cursors["cross"])
 
     def OnSelectRaster(self, event):
+        """!Select raster map(s) to profile
         """
-        Select raster map(s) to profile
-        """
-        
         dlg = SetRasterDialog(parent=self)
 
         if dlg.ShowModal() == wx.ID_OK:
@@ -256,11 +243,9 @@
         dlg.Destroy()
 
     def SetRaster(self):
-        """
-        Create coordinate string for profiling. Create segment list for
+        """!Create coordinate string for profiling. Create segment list for
         transect segment markers.
         """
-
         #
         # create list of coordinate points for r.profile
         #
@@ -329,12 +314,16 @@
             r['datalist'] = self.CreateDatalist(r['name'], self.coordstr)
             r['plegend'] = _('Profile of %s') % r['name']
 
-            p = gcmd.Command(['r.info',
-                              'map=%s' % r['name'],
-                              '-u',
-                              '--quiet'])
-            r['units'] = p.ReadStdOutput()[0].split('=')[1]
+            ret = gcmd.RunCommand('r.info',
+                                  parent = self,
+                                  read = True,
+                                  quiet = True,
+                                  flags = 'u',
+                                  map = r['name'])
 
+            if ret:
+                r['units'] = ret.splitlines()[0].split('=')[1]
+
             # update title
             self.ptitle += ' %s and' % r['name']
 
@@ -358,9 +347,8 @@
             self.ylabel = self.ylabel.rstrip(',')
 
     def SetGraphStyle(self):
+        """!Set plot and text options
         """
-        Set plot and text options
-        """
         self.client.SetFont(self.properties['font']['wxfont'])
         self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
         self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
@@ -418,49 +406,49 @@
         # self.client.SetPointLabelFunc(self.DrawPointLabel())
 
     def CreateDatalist(self, raster, coords):
+        """!Build a list of distance, value pairs for points along transect
         """
-        Build a list of distance, value pairs for points along transect
-        """
         datalist = []
-        import subprocess
-                
+        
         # keep total number of transect points to 500 or less to avoid 
         # freezing with large, high resolution maps
         region = grass.region()
         curr_res = min(float(region['nsres']),float(region['ewres']))
         transect_rec = 0
-        print "transect length = "+str(self.transect_length)
-        print "current resolution = "+str(curr_res)
         if self.transect_length / curr_res > 500:
             transect_res = self.transect_length / 500
         else: transect_res = curr_res
-        print "transect resolution = "+str(transect_res)
-                
+        
         try:
-            p = grass.read_command("r.profile",
-                       input=raster,
-                       profile=coords,
-                       res=transect_res,
-                       null="nan",
-                       quiet=True
-                       )
-            for outline in p.strip().split('\n'):
-                dist, elev = outline.split(' ')
-                if elev != 'nan': datalist.append((dist,elev))
+            ret = gcmd.RunCommand("r.profile",
+                             input=raster,
+                             profile=coords,
+                             res=transect_res,
+                             null="nan",
+                             quiet=True,
+                             read = True)
+            
+            if not ret:
+                return dataset
+            
+            for line in ret.splitlines():
+                dist, elev = line.split(' ')
+                if elev != 'nan':
+                    datalist.append((dist,elev))
 
             return datalist
-        except gcmd.CmdError, e:
-            print e
+        except gcmd.GException, e:
+            gcmd.GError(parent = self,
+                        message = e)
             return None
 
     def OnCreateProfile(self, event):
+        """!Main routine for creating a profile. Uses r.profile to
+        create a list of distance,cell value pairs. This is passed to
+        plot to create a line graph of the profile. If the profile
+        transect is in multiple segments, these are drawn as
+        points. Profile transect is drawn, using methods in mapdisp.py
         """
-        Main routine for creating a profile. Uses r.profile to create a list
-        of distance,cell value pairs. This is passed to plot to create a
-        line graph of the profile. If the profile transect is in multiple
-        segments, these are drawn as points. Profile transect is drawn, using
-        methods in mapdisp.py
-        """
     
         if len(self.mapwin.polycoords) == 0 or self.raster[0]['name'] == '':
             dlg = wx.MessageDialog(parent=self,
@@ -485,11 +473,9 @@
         self.mapwin.mouse['box'] = 'point'
 
     def DrawPlot(self):
-        """
-        Draw line and point plot from transect datalist and
+        """!Draw line and point plot from transect datalist and
         transect segment endpoint coordinates.
         """
-
         # graph the distance, value pairs for the transect
         self.plotlist = []
         if len(self.seglist) > 0 :
@@ -537,44 +523,36 @@
                          self.properties['y-axis']['axis'])
 
     def OnZoom(self, event):
+        """!Enable zooming and disable dragging
         """
-        Enable zooming and disable dragging
-        """
-
         self.zoom = True
         self.drag = False
         self.client.SetEnableZoom(self.zoom)
         self.client.SetEnableDrag(self.drag)
 
     def OnDrag(self, event):
+        """!Enable dragging and disable zooming
         """
-        Enable dragging and disable zooming
-        """
-
         self.zoom = False
         self.drag = True
         self.client.SetEnableDrag(self.drag)
         self.client.SetEnableZoom(self.zoom)
 
     def OnRedraw(self, event):
+        """!Redraw the profile window. Unzoom to original size
         """
-        Redraw the profile window. Unzoom to original size
-        """
         self.client.Reset()
         self.client.Redraw()
 
     def Update(self):
+        """!Update profile after changing options
         """
-        Update profile after changing options
-        """
-
         self.SetGraphStyle()
         self.DrawPlot()
 
     def OnErase(self, event):
+        """!Erase the profile window
         """
-        Erase the profile window
-        """
         self.client.Clear()
         self.mapwin.ClearLines(self.mapwin.pdc)
         self.mapwin.ClearLines(self.mapwin.pdcTmp)
@@ -588,21 +566,53 @@
         #            pass
 
     def SaveToFile(self, event):
+        """!Save profile to graphics file
         """
-        Save profile to graphics file
-        """
         self.client.SaveFile()
 
+    def SaveProfileToFile(self, event):
+        """!Save r.profile data to a csv file
+        """    
+        wildcard = _("Comma separated value (*.csv)|*.csv")
+        
+        dlg = wx.FileDialog(
+            self, message=_("Path and prefix (for raster name) to save profile values..."),
+            defaultDir=os.getcwd(), 
+            defaultFile="",  wildcard=wildcard, style=wx.SAVE
+            )
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            
+            for r in self.raster.itervalues():
+                if r['name'] == '':
+                    continue
+
+                print 'path = '+str(path)
+                pfile = path+'_'+str(r['name'])+'.csv'
+                print 'pfile1 = '+str(pfile)
+                try:
+                    file = open(pfile, "w")
+                except IOError:
+                    wx.MessageBox(parent=self,
+                                  message=_("Unable to open file <%s> for writing.") % pfile,
+                                  caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                    return False
+                for datapair in r['datalist']:
+                    file.write('%d,%d\n' % (float(datapair[0]),float(datapair[1])))
+                                        
+                file.close()
+
+        dlg.Destroy()
+    
     def DrawPointLabel(self, dc, mDataDict):
-        """This is the fuction that defines how the pointLabels are plotted
-            dc - DC that will be passed
-            mDataDict - Dictionary of data that you want to use for the pointLabel
+        """!This is the fuction that defines how the pointLabels are
+            plotted dc - DC that will be passed mDataDict - Dictionary
+            of data that you want to use for the pointLabel
 
-            As an example I have decided I want a box at the curve point
-            with some text information about the curve plotted below.
-            Any wxDC method can be used.
+            As an example I have decided I want a box at the curve
+            point with some text information about the curve plotted
+            below.  Any wxDC method can be used.
         """
-        # ----------
         dc.SetPen(wx.Pen(wx.BLACK))
         dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
 
@@ -615,7 +625,6 @@
         #make a string to display
         s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
         dc.DrawText(s, sx , sy+1)
-        # -----------
 
     def OnMouseLeftDown(self,event):
         s= "Left Mouse Down at Point: (%.4f, %.4f)" % self.client._getXY(event)
@@ -640,10 +649,8 @@
         event.Skip()           #go to next handler
 
     def ProfileOptionsMenu(self, event):
+        """!Popup menu for profile and text options
         """
-        Popup menu for profile and text options
-        """
-
         point = wx.GetMousePosition()
         popt = wx.Menu()
         # Add items to the menu
@@ -661,19 +668,18 @@
         popt.Destroy()
 
     def NotFunctional(self):
+        """!Creates a 'not functional' message dialog
         """
-        Creates a 'not functional' message dialog
-        """
-
-        dlg = wx.MessageDialog(self, 'This feature is not yet functional',
-                           'Under Construction', wx.OK | wx.ICON_INFORMATION)
+        dlg = wx.MessageDialog(parent = self,
+                               message = _('This feature is not yet functional'),
+                               caption = _('Under Construction'),
+                               style = wx.OK | wx.ICON_INFORMATION)
         dlg.ShowModal()
         dlg.Destroy()
 
     def OnPText(self, dlg):
+        """!Use user's provided profile text settings.
         """
-        Use user's provided profile text settings.
-        """
         self.ptitle = dlg.ptitle
         self.xlabel = dlg.xlabel
         self.ylabel = dlg.ylabel
@@ -691,10 +697,8 @@
         self.OnRedraw(event=None)
     
     def PText(self, event):
+        """!Set custom text values for profile title and axis labels.
         """
-        Set custom text values for profile
-        title and axis labels.
-        """
         dlg = TextDialog(parent=self, id=wx.ID_ANY, title=_('Profile text settings'))
 
         if dlg.ShowModal() == wx.ID_OK:
@@ -703,11 +707,10 @@
         dlg.Destroy()
 
     def POptions(self, event):
+        """!Set various profile options, including: line width, color,
+        style; marker size, color, fill, and style; grid and legend
+        options.  Calls OptDialog class.
         """
-        Set various profile options, including: line width, color, style;
-        marker size, color, fill, and style; grid and legend options.
-        Calls OptDialog class.
-        """
         dlg = OptDialog(parent=self, id=wx.ID_ANY, title=_('Profile settings'))
         btnval = dlg.ShowModal()
 
@@ -719,9 +722,8 @@
             dlg.Destroy()
 
     def PrintMenu(self, event):
+        """!Print options and output menu
         """
-        Print options and output menu
-        """
         point = wx.GetMousePosition()
         printmenu = wx.Menu()
         # Add items to the menu
@@ -773,9 +775,8 @@
     def __init__(self, parent, id=wx.ID_ANY, title=_("Select raster map to profile"),
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_DIALOG_STYLE):
+        """!Dialog to select raster maps to profile.
         """
-        Dialog to select raster maps to profile.
-        """
 
         wx.Dialog.__init__(self, parent, id, title, pos, size, style)
 
@@ -855,11 +856,10 @@
 class TextDialog(wx.Dialog):
     def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
                  style=wx.DEFAULT_DIALOG_STYLE):
-        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
-        """
-        Dialog to set profile text options: font, title
+        """!Dialog to set profile text options: font, title
         and font size, axis labels and font size
         """
+        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
         #
         # initialize variables
         #
@@ -896,7 +896,7 @@
         self._do_layout()
         
     def _do_layout(self):
-        """Do layout"""
+        """!Do layout"""
         # dialog layout
         sizer = wx.BoxSizer(wx.VERTICAL)
 
@@ -1082,17 +1082,17 @@
         self.properties['font']['wxfont'].SetWeight(weight)
 
     def OnSave(self, event):
-        """Button 'Save' pressed"""
+        """!Button 'Save' pressed"""
         self.UpdateSettings()
         fileSettings = {}
         UserSettings.ReadSettingsFile(settings=fileSettings)
         fileSettings['profile'] = UserSettings.Get(group='profile')
         file = UserSettings.SaveToFile(fileSettings)
-        self.parent.parent.gismanager.goutput.WriteLog(_('Profile settings saved to file \'%s\'.') % file)
+        self.parent.parent.GetLayerManager().goutput.WriteLog(_('Profile settings saved to file \'%s\'.') % file)
         self.EndModal(wx.ID_OK)
 
     def OnApply(self, event):
-        """Button 'Apply' pressed"""
+        """!Button 'Apply' pressed"""
         self.UpdateSettings()
         self.parent.OnPText(self)
         
@@ -1102,17 +1102,17 @@
         self.EndModal(wx.ID_OK)
 
     def OnCancel(self, event):
-        """Button 'Cancel' pressed"""
+        """!Button 'Cancel' pressed"""
         self.EndModal(wx.ID_CANCEL)
         
 class OptDialog(wx.Dialog):
     def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
-            style=wx.DEFAULT_DIALOG_STYLE):
+                 style=wx.DEFAULT_DIALOG_STYLE): 
+        """!Dialog to set various profile options, including: line
+        width, color, style; marker size, color, fill, and style; grid
+        and legend options.
+        """
         wx.Dialog.__init__(self, parent, id, title, pos, size, style)
-        """
-        Dialog to set various profile options, including: line width, color, style;
-        marker size, color, fill, and style; grid and legend options.
-        """
         # init variables
         self.pstyledict = parent.pstyledict
         self.ptfilldict = parent.ptfilldict
@@ -1141,7 +1141,7 @@
         self._do_layout()
 
     def _do_layout(self):
-        """Do layout"""
+        """!Do layout"""
         # dialog layout
         sizer = wx.BoxSizer(wx.VERTICAL)
 
@@ -1432,13 +1432,13 @@
         self.properties['legend']['enabled'] = self.FindWindowById(self.wxId['legend']['enabled']).IsChecked()
 
     def OnSave(self, event):
-        """Button 'Save' pressed"""
+        """!Button 'Save' pressed"""
         self.UpdateSettings()
         fileSettings = {}
         UserSettings.ReadSettingsFile(settings=fileSettings)
         fileSettings['profile'] = UserSettings.Get(group='profile')
         file = UserSettings.SaveToFile(fileSettings)
-        self.parent.parent.gismanager.goutput.WriteLog(_('Profile settings saved to file \'%s\'.') % file)
+        self.parent.parent.GetLayerManager().goutput.WriteLog(_('Profile settings saved to file \'%s\'.') % file)
         self.parent.SetGraphStyle()
         if self.parent.profile:
             self.parent.DrawPlot()
@@ -1452,5 +1452,5 @@
             self.parent.DrawPlot()
         
     def OnCancel(self, event):
-        """Button 'Cancel' pressed"""
+        """!Button 'Cancel' pressed"""
         self.Close()

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/prompt.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/prompt.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/prompt.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,983 @@
+"""!
+ at package prompt.py
+
+ at brief wxGUI command prompt
+
+Classes:
+ - PromptListCtrl
+ - TextCtrlAutoComplete
+ - GPrompt
+ - GPromptPopUp
+ - GPromptSTC
+
+(C) 2009-2010 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 Michael Barton <michael.barton at asu.edu>
+"""
+
+import os
+import sys
+import shlex
+import copy
+
+import wx
+import wx.stc
+import wx.lib.mixins.listctrl as listmix
+
+from grass.script import core as grass
+
+import globalvar
+import menudata
+import menuform
+import gcmd
+import utils
+
+class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
+    """!PopUp window used by GPromptPopUp"""
+    def __init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition,
+                 size = wx.DefaultSize, style = 0):
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        
+class TextCtrlAutoComplete(wx.ComboBox, listmix.ColumnSorterMixin):
+    """!Auto complete text area used by GPromptPopUp"""
+    def __init__ (self, parent, statusbar,
+                  id = wx.ID_ANY, choices = [], **kwargs):
+        """!Constructor works just like wx.TextCtrl except you can pass in a
+        list of choices.  You can also change the choice list at any time
+        by calling setChoices.
+        
+        Inspired by http://wiki.wxpython.org/TextCtrlAutoComplete
+        """
+        self.statusbar = statusbar
+        
+        if kwargs.has_key('style'):
+            kwargs['style'] = wx.TE_PROCESS_ENTER | kwargs['style']
+        else:
+            kwargs['style'] = wx.TE_PROCESS_ENTER
+        
+        wx.ComboBox.__init__(self, parent, id, **kwargs)
+        
+        # some variables
+        self._choices = choices
+        self._hideOnNoMatch = True
+        self._module = None      # currently selected module
+        self._choiceType = None  # type of choice (module, params, flags, raster, vector ...)
+        self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
+        self._historyItem = 0   # last item
+        
+        # sort variable needed by listmix
+        self.itemDataMap = dict()
+        
+        # widgets
+        try:
+            self.dropdown = wx.PopupWindow(self)
+        except NotImplementedError:
+            self.Destroy()
+            raise NotImplementedError
+        
+        # create the list and bind the events
+        self.dropdownlistbox = PromptListCtrl(parent = self.dropdown,
+                                              style = wx.LC_REPORT | wx.LC_SINGLE_SEL | \
+                                                  wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER,
+                                              pos = wx.Point(0, 0))
+        
+        listmix.ColumnSorterMixin.__init__(self, 1)
+        
+        # set choices (list of GRASS modules)
+        self._choicesCmd = globalvar.grassCmd['all']
+        self._choicesMap = dict()
+        for type in ('raster', 'vector'):
+            self._choicesMap[type] = grass.list_strings(type = type[:4])
+        # first search for GRASS module
+        self.SetChoices(self._choicesCmd)
+        
+        self.SetMinSize(self.GetSize())
+        
+        # bindings...
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged)
+        self.Bind(wx.EVT_TEXT, self.OnEnteredText)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd)
+        self.Bind(wx.EVT_KEY_DOWN , self.OnKeyDown)
+        ### self.Bind(wx.EVT_LEFT_DOWN, self.OnClick)
+
+        # if need drop down on left click
+        self.dropdown.Bind(wx.EVT_LISTBOX , self.OnListItemSelected, self.dropdownlistbox)
+        self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.OnListClick)
+        self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.OnListDClick)
+        self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.OnListColClick)
+
+        self.Bind(wx.EVT_COMBOBOX, self.OnCommandSelect)
+        
+    def _updateDataList(self, choices):
+        """!Update data list"""
+        # delete, if need, all the previous data
+        if self.dropdownlistbox.GetColumnCount() != 0:
+            self.dropdownlistbox.DeleteAllColumns()
+            self.dropdownlistbox.DeleteAllItems()
+        # and update the dict
+        if choices:
+            for numVal, data in enumerate(choices):
+                self.itemDataMap[numVal] = data
+        else:
+            numVal = 0
+        self.SetColumnCount(numVal)
+    
+    def _setListSize(self):
+        """!Set list size"""
+        choices = self._choices
+        longest = 0
+        for choice in choices:
+            longest = max(len(choice), longest)
+        longest += 3
+        itemcount = min(len( choices ), 7) + 2
+        charheight = self.dropdownlistbox.GetCharHeight()
+        charwidth = self.dropdownlistbox.GetCharWidth()
+        self.popupsize = wx.Size(charwidth*longest, charheight*itemcount)
+        self.dropdownlistbox.SetSize(self.popupsize)
+        self.dropdown.SetClientSize(self.popupsize)
+        
+    def _showDropDown(self, show = True):
+        """!Either display the drop down list (show = True) or hide it
+        (show = False).
+        """
+        if show:
+            size = self.dropdown.GetSize()
+            width, height = self.GetSizeTuple()
+            x, y = self.ClientToScreenXY(0, height)
+            if size.GetWidth() != width:
+                size.SetWidth(width)
+                self.dropdown.SetSize(size)
+                self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
+            if (y + size.GetHeight()) < self._screenheight:
+                self.dropdown.SetPosition(wx.Point(x, y))
+            else:
+                self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
+        
+        self.dropdown.Show(show)
+    
+    def _listItemVisible(self):
+        """!Moves the selected item to the top of the list ensuring it is
+        always visible.
+        """
+        toSel = self.dropdownlistbox.GetFirstSelected()
+        if toSel == -1:
+            return
+        self.dropdownlistbox.EnsureVisible(toSel)
+
+    def _setModule(self, name):
+        """!Set module's choices (flags, parameters)""" 
+        # get module's description
+        if name in self._choicesCmd and not self._module:
+            try:
+                self._module = menuform.GUI().ParseInterface(cmd = [name])
+            except IOError:
+                self._module = None
+             
+        # set choices (flags)
+        self._choicesMap['flag'] = self._module.get_list_flags()
+        for idx in range(len(self._choicesMap['flag'])):
+            item = self._choicesMap['flag'][idx]
+            desc = self._module.get_flag(item)['label']
+            if not desc:
+                desc = self._module.get_flag(item)['description']
+            
+            self._choicesMap['flag'][idx] = '%s (%s)' % (item, desc)
+        
+        # set choices (parameters)
+        self._choicesMap['param'] = self._module.get_list_params()
+        for idx in range(len(self._choicesMap['param'])):
+            item = self._choicesMap['param'][idx]
+            desc = self._module.get_param(item)['label']
+            if not desc:
+                desc = self._module.get_param(item)['description']
+            
+            self._choicesMap['param'][idx] = '%s (%s)' % (item, desc)
+    
+    def _setValueFromSelected(self):
+         """!Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
+         Will do nothing if no item is selected in the wx.ListCtrl.
+         """
+         sel = self.dropdownlistbox.GetFirstSelected()
+         if sel < 0:
+             return
+         
+         if self._colFetch != -1:
+             col = self._colFetch
+         else:
+             col = self._colSearch
+         itemtext = self.dropdownlistbox.GetItem(sel, col).GetText()
+         
+         cmd = shlex.split(str(self.GetValue()))
+         if len(cmd) > 0 and cmd[0] in self._choicesCmd:
+             # -> append text (skip last item)
+             if self._choiceType == 'param':
+                 itemtext = itemtext.split(' ')[0]
+                 self.SetValue(' '.join(cmd) + ' ' + itemtext + '=')
+                 optType = self._module.get_param(itemtext)['prompt']
+                 if optType in ('raster', 'vector'):
+                     # -> raster/vector map
+                     self.SetChoices(self._choicesMap[optType], optType)
+             elif self._choiceType == 'flag':
+                 itemtext = itemtext.split(' ')[0]
+                 if len(itemtext) > 1:
+                     prefix = '--'
+                 else:
+                     prefix = '-'
+                 self.SetValue(' '.join(cmd[:-1]) + ' ' + prefix + itemtext)
+             elif self._choiceType in ('raster', 'vector'):
+                 self.SetValue(' '.join(cmd[:-1]) + ' ' + cmd[-1].split('=', 1)[0] + '=' + itemtext)
+         else:
+             # -> reset text
+             self.SetValue(itemtext + ' ')
+             
+             # define module
+             self._setModule(itemtext)
+             
+             # use parameters as default choices
+             self._choiceType = 'param'
+             self.SetChoices(self._choicesMap['param'], type = 'param')
+         
+         self.SetInsertionPointEnd()
+         
+         self._showDropDown(False)
+         
+    def GetListCtrl(self):
+        """!Method required by listmix.ColumnSorterMixin"""
+        return self.dropdownlistbox
+    
+    def SetChoices(self, choices, type = 'module'):
+        """!Sets the choices available in the popup wx.ListBox.
+        The items will be sorted case insensitively.
+
+        @param choices list of choices
+        @param type type of choices (module, param, flag, raster, vector)
+        """
+        self._choices = choices
+        self._choiceType = type
+        
+        self.dropdownlistbox.SetWindowStyleFlag(wx.LC_REPORT | wx.LC_SINGLE_SEL |
+                                                wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER)
+        if not isinstance(choices, list):
+            self._choices = [ x for x in choices ]
+        if self._choiceType not in ('raster', 'vector'):
+            # do not sort raster/vector maps
+            utils.ListSortLower(self._choices)
+        
+        self._updateDataList(self._choices)
+        
+        self.dropdownlistbox.InsertColumn(0, "")
+        for num, colVal in enumerate(self._choices):
+            index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)
+            self.dropdownlistbox.SetStringItem(index, 0, colVal)
+            self.dropdownlistbox.SetItemData(index, num)
+        self._setListSize()
+        
+        # there is only one choice for both search and fetch if setting a single column:
+        self._colSearch = 0
+        self._colFetch = -1
+
+    def OnClick(self, event):
+        """Left mouse button pressed"""
+        sel = self.dropdownlistbox.GetFirstSelected()
+        if not self.dropdown.IsShown():
+            if sel > -1:
+                self.dropdownlistbox.Select(sel)
+            else:
+                self.dropdownlistbox.Select(0)
+            self._listItemVisible()
+            self._showDropDown()
+        else:
+            self.dropdown.Hide()
+        
+    def OnCommandSelect(self, event):
+        """!Command selected from history"""
+        self._historyItem = event.GetSelection() - len(self.GetItems())
+        self.SetFocus()
+        
+    def OnListClick(self, evt):
+        """!Left mouse button pressed"""
+        toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() )
+        #no values on poition, return
+        if toSel == -1: return
+        self.dropdownlistbox.Select(toSel)
+
+    def OnListDClick(self, evt):
+        """!Mouse button double click"""
+        self._setValueFromSelected()
+
+    def OnListColClick(self, evt):
+        """!Left mouse button pressed on column"""
+        col = evt.GetColumn()
+        # reverse the sort
+        if col == self._colSearch:
+            self._ascending = not self._ascending
+        self.SortListItems( evt.GetColumn(), ascending=self._ascending )
+        self._colSearch = evt.GetColumn()
+        evt.Skip()
+
+    def OnListItemSelected(self, event):
+        """!Item selected"""
+        self._setValueFromSelected()
+        event.Skip()
+
+    def OnEnteredText(self, event):
+        """!Text entered"""
+        text = event.GetString()
+        
+        if not text:
+            # control is empty; hide dropdown if shown:
+            if self.dropdown.IsShown():
+                self._showDropDown(False)
+            event.Skip()
+            return
+        
+        try:
+            cmd = shlex.split(str(text))
+        except ValueError, e:
+            self.statusbar.SetStatusText(str(e))
+            cmd = text.split(' ')
+        pattern = str(text)
+        
+        if len(cmd) > 0 and cmd[0] in self._choicesCmd and not self._module:
+            self._setModule(cmd[0])
+        elif len(cmd) > 1 and cmd[0] in self._choicesCmd:
+            if self._module:
+                if len(cmd[-1].split('=', 1)) == 1:
+                    # new option
+                    if cmd[-1][0] == '-':
+                        # -> flags
+                        self.SetChoices(self._choicesMap['flag'], type = 'flag')
+                        pattern = cmd[-1].lstrip('-')
+                    else:
+                        # -> options
+                        self.SetChoices(self._choicesMap['param'], type = 'param')
+                        pattern = cmd[-1]
+                else:
+                    # value
+                    pattern = cmd[-1].split('=', 1)[1]
+        else:
+            # search for GRASS modules
+            if self._module:
+                # -> switch back to GRASS modules list
+                self.SetChoices(self._choicesCmd)
+                self._module = None
+                self._choiceType = None
+        
+        self._choiceType
+        self._choicesMap
+        found = False
+        choices = self._choices
+        for numCh, choice in enumerate(choices):
+            if choice.lower().startswith(pattern):
+                found = True
+            if found:
+                self._showDropDown(True)
+                item = self.dropdownlistbox.GetItem(numCh)
+                toSel = item.GetId()
+                self.dropdownlistbox.Select(toSel)
+                break
+        
+        if not found:
+            self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False)
+            if self._hideOnNoMatch:
+                self._showDropDown(False)
+                if self._module and '=' not in cmd[-1]:
+                    message = ''
+                    if cmd[-1][0] == '-': # flag
+                        message = _("Warning: flag <%(flag)s> not found in '%(module)s'") % \
+                            { 'flag' : cmd[-1][1:], 'module' : self._module.name }
+                    else: # option
+                        message = _("Warning: option <%(param)s> not found in '%(module)s'") % \
+                            { 'param' : cmd[-1], 'module' : self._module.name }
+                    self.statusbar.SetStatusText(message)
+        
+        if self._module and len(cmd[-1]) == 2 and cmd[-1][-2] == '=':
+            optType = self._module.get_param(cmd[-1][:-2])['prompt']
+            if optType in ('raster', 'vector'):
+                # -> raster/vector map
+                self.SetChoices(self._choicesMap[optType], optType)
+        
+        self._listItemVisible()
+        
+        event.Skip()
+        
+    def OnKeyDown (self, event):
+        """!Do some work when the user press on the keys: up and down:
+        move the cursor left and right: move the search
+        """
+        skip = True
+        sel = self.dropdownlistbox.GetFirstSelected()
+        visible = self.dropdown.IsShown()
+        KC = event.GetKeyCode()
+        
+        if KC == wx.WXK_RIGHT:
+            # right -> show choices
+            if sel < (self.dropdownlistbox.GetItemCount() - 1):
+                self.dropdownlistbox.Select(sel + 1)
+                self._listItemVisible()
+            self._showDropDown()
+            skip = False
+        elif KC == wx.WXK_UP:
+            if visible:
+                if sel > 0:
+                    self.dropdownlistbox.Select(sel - 1)
+                    self._listItemVisible()
+                self._showDropDown()
+                skip = False
+            else:
+                self._historyItem -= 1
+                try:
+                    self.SetValue(self.GetItems()[self._historyItem])
+                except IndexError:
+                    self._historyItem += 1
+        elif KC == wx.WXK_DOWN:
+            if visible:
+                if sel < (self.dropdownlistbox.GetItemCount() - 1):
+                    self.dropdownlistbox.Select(sel + 1)
+                    self._listItemVisible()
+                self._showDropDown()
+                skip = False
+            else:
+                if self._historyItem < -1:
+                    self._historyItem += 1
+                    self.SetValue(self.GetItems()[self._historyItem])
+        
+        if visible:
+            if event.GetKeyCode() == wx.WXK_RETURN:
+                self._setValueFromSelected()
+                skip = False
+            if event.GetKeyCode() == wx.WXK_ESCAPE:
+                self._showDropDown(False)
+                skip = False
+        if skip:
+            event.Skip()
+        
+    def OnControlChanged(self, event):
+        """!Control changed"""
+        if self.IsShown():
+            self._showDropDown(False)
+        
+        event.Skip()
+
+class GPrompt(object):
+    """!Abstract class for interactive wxGUI prompt
+
+    See subclass GPromptPopUp and GPromptSTC.
+    """
+    def __init__(self, parent):
+        self.parent = parent                 # GMConsole
+        self.panel  = self.parent.GetPanel()
+        
+        if self.parent.parent.GetName() not in ("LayerManager", "Modeler"):
+            self.standAlone = True
+        else:
+            self.standAlone = False
+        
+        # dictionary of modules (description, keywords, ...)
+        if not self.standAlone:
+            if self.parent.parent.GetName() == 'Modeler':
+                self.moduleDesc = menudata.ManagerData().GetModules()
+            else:
+                self.moduleDesc = parent.parent.menubar.GetData().GetModules()
+            self.moduleList = self._getListOfModules()
+            self.mapList = self._getListOfMaps()
+        else:
+            self.moduleDesc = self.moduleList = self.mapList = None
+        
+        # auto complete items
+        self.autoCompList   = list()
+        self.autoCompFilter = None
+        
+        # command description (menuform.grassTask)
+        self.cmdDesc = None
+        self.cmdbuffer = self._readHistory()
+        self.cmdindex = len(self.cmdbuffer)
+
+    def CheckKey(self, text, keywords):
+        """!Check if text is in keywords (unused)"""
+        found = 0
+        keys = text.split(',')
+        if len(keys) > 1: # -> multiple keys
+            for k in keys[:-1]:
+                k = k.strip()
+                for key in keywords: 
+                    if k == key: # full match
+                        found += 1
+                        break
+            k = keys[-1].strip()
+            for key in keywords:
+                if k in key: # partial match
+                    found +=1
+                    break
+        else:
+            for key in keywords:
+                if text in key: # partial match
+                    found +=1
+                    break
+        
+        if found == len(keys):
+            return True
+        
+        return False
+        
+    def _readHistory(self):
+        """!Get list of commands from history file"""
+        hist = list()
+        env = grass.gisenv()
+        try:
+            fileHistory = open(os.path.join(env['GISDBASE'],
+                                            env['LOCATION_NAME'],
+                                            env['MAPSET'],
+                                            '.bash_history'), 'r')
+        except IOError:
+            return hist
+        
+        try:
+            for line in fileHistory.readlines():
+                hist.append(line.replace('\n', ''))
+        finally:
+            fileHistory.close()
+        
+        return hist
+
+    def GetCommandDesc(self, cmd):
+        """!Get description for given command"""
+        if self.moduleDesc.has_key(cmd):
+            return self.moduleDesc[cmd]['desc']
+        
+        return ''
+            
+    def GetCommandItems(self):
+        """!Get list of available commands"""
+        items = list()
+        
+        if self.autoCompFilter is not None:
+            mList = self.autoCompFilter
+        else:
+            mList = self.moduleList
+        
+        if not mList:
+            return items
+        
+        prefixes = mList.keys()
+        prefixes.sort()
+        
+        for prefix in prefixes:
+            for command in mList[prefix]:
+                name = prefix + '.' + command
+                items.append(name)
+        
+        items.sort()
+        
+        return items
+    
+    def _getListOfModules(self):
+        """!Get list of modules"""
+        result = dict()
+        for module in globalvar.grassCmd['all']:
+            try:
+                group, name = module.split('.', 1)
+            except ValueError:
+                continue # TODO
+            
+            if not result.has_key(group):
+                result[group] = list()
+            result[group].append(name)
+        
+        # sort list of names
+        for group in result.keys():
+            result[group].sort()
+        
+        return result
+    
+    def _getListOfMaps(self):
+        """!Get list of maps"""
+        result = dict()
+        result['raster'] = grass.list_strings('rast')
+        result['vector'] = grass.list_strings('vect')
+        
+        return result
+
+    def OnRunCmd(self, event):
+        """!Run command"""
+        cmdString = event.GetString()
+        
+        if self.standAlone:
+            return
+        
+        if cmdString[:2] == 'd.' and not self.parent.curr_page:
+            self.parent.NewDisplay(show=True)
+        
+        cmd = shlex.split(str(cmdString))
+        if len(cmd) > 1:
+            self.parent.RunCmd(cmd, switchPage = True)
+        else:
+            self.parent.RunCmd(cmd, switchPage = False)
+        
+        self.OnUpdateStatusBar(None)
+        
+    def OnUpdateStatusBar(self, event):
+        """!Update Layer Manager status bar"""
+        if self.standAlone:
+            return
+        
+        if event is None:
+            self.parent.parent.statusbar.SetStatusText("")
+        else:
+            self.parent.parent.statusbar.SetStatusText(_("Type GRASS command and run by pressing ENTER"))
+            event.Skip()
+        
+    def GetPanel(self):
+        """!Get main widget panel"""
+        return self.panel
+
+    def GetInput(self):
+        """!Get main prompt widget"""
+        return self.input
+
+class GPromptPopUp(GPrompt, TextCtrlAutoComplete):
+    """!Interactive wxGUI prompt - popup version"""
+    def __init__(self, parent):
+        GPrompt.__init__(self, parent)
+        
+        ### todo: fix TextCtrlAutoComplete to work also on Macs
+        ### reason: missing wx.PopupWindow()
+        try:
+            TextCtrlAutoComplete.__init__(self, parent = self.panel, id = wx.ID_ANY,
+                                          value = "",
+                                          style = wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
+                                          statusbar = self.parent.parent.statusbar)
+            self.SetItems(self._readHistory())
+        except NotImplementedError:
+            # wx.PopupWindow may be not available in wxMac
+            # see http://trac.wxwidgets.org/ticket/9377
+            wx.TextCtrl.__init__(parent = self.panel, id = wx.ID_ANY,
+                                 value = "",
+                                 style=wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
+                                 size = (-1, 25))
+            self.searchBy.Enable(False)
+            self.search.Enable(False)
+        
+        self.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, ''))
+        
+        wx.CallAfter(self.SetInsertionPoint, 0)
+        
+        # bidnings
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd)
+        self.Bind(wx.EVT_TEXT,       self.OnUpdateStatusBar)
+        
+    def OnCmdErase(self, event):
+        """!Erase command prompt"""
+        self.input.SetValue('')
+
+class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
+    """!Styled wxGUI prompt with autocomplete and calltips"""    
+    def __init__(self, parent, id = wx.ID_ANY, margin = False):
+        GPrompt.__init__(self, parent)
+        wx.stc.StyledTextCtrl.__init__(self, self.panel, id)
+        
+        #
+        # styles
+        #                
+        self.SetWrapMode(True)
+        self.SetUndoCollection(True)        
+        
+        #
+        # create command and map lists for autocompletion
+        #
+        self.AutoCompSetIgnoreCase(False) 
+        
+        #
+        # line margins
+        #
+        # TODO print number only from cmdlog
+        self.SetMarginWidth(1, 0)
+        self.SetMarginWidth(2, 0)
+        if margin:
+            self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
+            self.SetMarginWidth(0, 30)
+        else:
+            self.SetMarginWidth(0, 0)
+        
+        #
+        # miscellaneous
+        #
+        self.SetViewWhiteSpace(False)
+        self.SetUseTabs(False)
+        self.UsePopUp(True)
+        self.SetSelBackground(True, "#FFFF00")
+        self.SetUseHorizontalScrollBar(True)
+        
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
+        self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected)
+
+    def SetFilter(self, items):
+        """!Sets filter
+        
+        @param choices list of items to be filtered
+        """
+        self.autoCompFilter = items
+        
+    def OnItemSelected(self, event):
+        """!Item selected from the list"""
+        text = self.GetTextLeft()[:self.AutoCompPosStart()] + event.GetText() + ' '
+        self.SetText(text)
+        pos = len(text)
+        self.SetSelectionStart(pos)
+        self.SetCurrentPos(pos)
+        
+        cmd = text.split()[0]
+        if not self.cmdDesc or cmd != self.cmdDesc.get_name():
+            try:
+                self.cmdDesc = menuform.GUI().ParseInterface(cmd = [cmd])
+            except IOError:
+                self.cmdDesc = None
+        
+    def OnKeyPressed(self, event):
+        """!Key press capture for autocompletion, calltips, and command history
+
+        @todo event.ControlDown() for manual autocomplete
+        """
+        # keycodes used: "." = 46, "=" = 61, "," = 44 
+        
+        if event.GetKeyCode() == 46 and not event.ShiftDown():
+            # GRASS command autocomplete when '.' is pressed after 'r', 'v', 'i', 'g', 'db', or 'd'
+            self.autoCompList = list()
+            pos = self.GetCurrentPos()
+            self.InsertText(pos, '.')
+            self.CharRight()
+            
+            entry = self.GetTextLeft()
+            if entry not in ['r.', 'v.', 'i.', 'g.', 'db.', 'd.']:
+                return
+            
+            if self.autoCompFilter:
+                self.autoCompList = self.autoCompFilter[entry[:-1]]
+            else:
+                self.autoCompList = self.moduleList[entry[:-1]]
+            if len(self.autoCompList) > 0:
+                self.AutoCompShow(lenEntered = 0, itemList = ' '.join(self.autoCompList))
+            
+        elif event.GetKeyCode() == wx.WXK_TAB:
+            # show GRASS command calltips (to hide press 'ESC')
+            
+            pos = self.GetCurrentPos()
+            entry = self.GetTextLeft()
+            try:
+                cmd = entry.split()[0].strip()
+            except IndexError:
+                cmd = ''
+            
+            if cmd not in globalvar.grassCmd['all']:
+                return
+            
+            usage, description = self.GetCommandUsage(cmd)
+                                        
+            self.CallTipSetBackground("#f4f4d1")
+            self.CallTipSetForeground("BLACK")
+            self.CallTipShow(pos, usage + '\n\n' + description)
+            
+        elif (event.GetKeyCode() == wx.WXK_SPACE and event.ControlDown()) or \
+                event.GetKeyCode() == 61 or event.GetKeyCode() == 44:
+            # Autocompletion for map/data file name entry after '=', ',', or manually
+            pos = self.GetCurrentPos()
+            entry = self.GetTextLeft()
+            if event.GetKeyCode() != 44:
+                self.promptType = None
+            
+            if not self.cmdDesc:
+                # No partial or complete GRASS command found
+                return
+            
+            try:
+                # find last typed option
+                arg = entry.rstrip('=').rsplit(' ', 1)[1]
+            except:
+                arg = ''
+            
+            try:
+                self.promptType = self.cmdDesc.get_param(arg)['prompt']
+            except:
+                pass
+            
+            if event.GetKeyCode() == 61:
+                # autocompletion after '='
+                # insert the '=' and move to after the '=', ready for a map name
+                self.InsertText(pos, '=')
+                self.CharRight()
+                
+            elif event.GetKeyCode() == 44:
+                # autocompletion after ','
+                # if comma is pressed, use the same maptype as previous for multiple map entries
+                
+                # insert the comma and move to after the comma ready for a map name
+                self.InsertText(pos,',')
+                self.CharRight()
+                
+                #must apply to an entry where '=[string]' has already been entered
+                if '=' not in arg:
+                    return
+
+            elif event.GetKeyCode() == wx.WXK_SPACE and event.ControlDown():
+                # manual autocompletion
+                # map entries without arguments (as in r.info [mapname]) use ctrl-shift
+                if not self.cmdDesc:
+                    return
+                
+                try:
+                    param = self.cmdDesc.get_list_params()[0]
+                    self.promptType = self.cmdDesc.get_param(param)['prompt']
+                except IndexError:
+                    return
+            
+            if self.promptType and self.promptType in ('raster', 'raster3d', 'vector'):
+                self.autoCompList = self.mapList[self.promptType]
+                self.AutoCompShow(lenEntered = 0, itemList = ' '.join(self.autoCompList))
+            
+        elif event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and \
+                 not self.AutoCompActive():
+            # Command history using up and down   
+            if len(self.cmdbuffer) < 1:
+                return
+            
+            self.DocumentEnd()
+            
+            # move through command history list index values
+            if event.GetKeyCode() == wx.WXK_UP:
+                self.cmdindex = self.cmdindex - 1
+            if event.GetKeyCode() == wx.WXK_DOWN:
+                self.cmdindex = self.cmdindex + 1
+            if self.cmdindex < 0:
+                self.cmdindex = 0
+            if self.cmdindex > len(self.cmdbuffer) - 1:
+                self.cmdindex = len(self.cmdbuffer) - 1
+            
+            try:
+                txt = self.cmdbuffer[self.cmdindex]
+            except:
+                txt = ''
+            
+            # clear current line and insert command history    
+            self.DelLineLeft()
+            self.DelLineRight()
+            pos = self.GetCurrentPos()            
+            self.InsertText(pos,txt)
+            self.LineEnd()
+            
+        elif event.GetKeyCode() == wx.WXK_RETURN and \
+                self.AutoCompActive() == False:
+            if self.parent.GetName() == "ModelerDialog":
+                self.parent.OnOk(None)
+                return
+            
+            # Run command on line when <return> is pressed    
+            
+            # find the command to run
+            line = self.GetCurLine()[0].strip()
+            if len(line) == 0:
+                return
+            
+            # parse command into list
+            try:
+                cmd = shlex.split(str(line))
+            except UnicodeError:
+                cmd = shlex.split(utils.EncodeString((line)))
+            
+            # send the command list to the processor 
+            self.parent.RunCmd(cmd)
+            
+            # add command to history    
+            self.cmdbuffer.append(line)
+            
+            # keep command history to a managable size
+            if len(self.cmdbuffer) > 200:
+                del self.cmdbuffer[0]
+            self.cmdindex = len(self.cmdbuffer)
+            
+            # reset command-line
+            self.OnCmdErase(None)
+            
+        elif event.GetKeyCode() == wx.WXK_SPACE:
+            items = self.GetTextLeft().split()
+            if len(items) == 1:
+                cmd = items[0].strip()
+                if not self.cmdDesc or cmd != self.cmdDesc.get_name():
+                    try:
+                        self.cmdDesc = menuform.GUI().ParseInterface(cmd = [cmd])
+                    except IOError:
+                        self.cmdDesc = None
+            event.Skip()
+        
+        else:
+            event.Skip()
+        
+    def GetTextLeft(self):
+        """!Returns all text left of the caret"""
+        pos = self.GetCurrentPos()
+        self.HomeExtend()
+        entry = self.GetSelectedText()
+        self.SetCurrentPos(pos)
+        
+        return entry
+
+    def GetCommandUsage(self, command):
+        """!Returns command syntax by running command help"""
+        usage = ''
+        description = ''
+
+        ret, out  = gcmd.RunCommand(command, 'help', getErrorMsg = True)
+               
+        if ret == 0:
+            cmdhelp = out.splitlines()
+            addline = False
+            helplist = []
+            description = ''
+            for line in cmdhelp:
+                if "Usage:" in line:
+                    addline = True
+                    continue
+                elif "Flags:" in line:
+                    addline = False
+                    break
+                elif addline == True:
+                    line = line.strip()
+                    helplist.append(line)
+
+            for line in cmdhelp:
+                if "Description:" in line:
+                    addline = True
+                    continue
+                elif "Keywords:" in line:
+                    addline = False
+                    break
+                elif addline == True:
+                    description += (line + ' ')
+                
+            description = description.strip()
+
+            for line in helplist:
+                usage += line + '\n'
+
+            return usage.strip(), description
+        else:
+            return ''   
+        
+    def OnDestroy(self, event):
+        """!The clipboard contents can be preserved after
+        the app has exited"""
+        wx.TheClipboard.Flush()
+        event.Skip()
+
+    def OnCmdErase(self, event):
+        """!Erase command prompt"""
+        self.Home()
+        self.DelLineRight()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/prompt.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/render.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/render.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/render.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package render
 
 Rendering map layers and overlays into map composition image
@@ -9,15 +9,14 @@
  - Overlay
  - Map
 
-C) 2006-2008 by the GRASS Development Team
+(C) 2006-2010 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 Michael Barton, Jachym Cepicky,
-Martin Landa <landa.martin gmail.com>
-
- at date 2006-2008
+ at author Michael Barton
+ at author Jachym Cepicky,
+ at author Martin Landa <landa.martin gmail.com>
 """
 
 import os
@@ -37,6 +36,8 @@
 import wx
 from wx.lib.newevent import NewEvent
 
+from grass.script import core as grass
+
 import globalvar
 import utils
 import gcmd
@@ -52,16 +53,17 @@
 USE_GPNMCOMP = True
 
 class Layer(object):
-    """Virtual class which stores information about layers (map layers and
+    """!Virtual class which stores information about layers (map layers and
     overlays) of the map composition.
-
+    
     For map layer use MapLayer class.
     For overlays use Overlay class.
     """
-
     def __init__(self, type, cmd, name=None,
                  active=True, hidden=False, opacity=1.0):
-        """
+        """!
+        @todo pass cmd as tuple instead of list
+        
         @param type 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']
@@ -70,10 +72,16 @@
         @param hidden layer is hidden, won't be listed in Layer Manager if True
         @param opacity layer opacity <0;1>
         """
-        self.type    = type
-        self.name    = name
-        self.cmdlist = cmd
-
+        self.type = type
+        self.name = name
+        
+        if self.type == 'command':
+            self.cmd = []
+            for c in cmd:
+                self.cmd.append(utils.CmdToTuple(c))
+        else:
+            self.cmd = utils.CmdToTuple(cmd)
+        
         self.active  = active
         self.hidden  = hidden
         self.opacity = opacity
@@ -84,7 +92,7 @@
                        "active=%d, opacity=%d, hidden=%d" % \
                        (self.type, self.GetCmd(string=True), self.name, self.active,
                         self.opacity, self.hidden))
-
+        
         # generated file for each layer
         self.gtemp = tempfile.mkstemp()[1]
         self.maskfile = self.gtemp + ".pgm"
@@ -92,18 +100,18 @@
             self.mapfile  = self.gtemp + ".png"
         else:
             self.mapfile  = self.gtemp + ".ppm"
-
+        
     def __del__(self):
         Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" %
                    (self.name, self.GetCmd(string=True)))
 
     def Render(self):
-        """Render layer to image
-
+        """!Render layer to image
+        
         @return rendered image filename
         @return None on error
         """
-        if len(self.cmdlist) == 0:
+        if not self.cmd:
             return None
         
         # ignore in 2D
@@ -116,24 +124,26 @@
         #
         # prepare command for each layer
         #
-        layertypes = ['raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum',
+        layertypes = ('raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum',
                       'vector','thememap','themechart',
                       'grid', 'geodesic', 'rhumb', 'labels',
                       'command',
-                      'overlay']
+                      'overlay')
         
         if self.type not in layertypes:
-            raise gcmd.GStdError(_("<%(name)s>: layer type <%(type)s> is not supported yet.") % \
-                                     {'type' : self.type, 'name' : self.name})
+            raise gcmd.GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
+                                      {'type' : self.type, 'name' : self.name})
         
         #
         # start monitor
         #
         if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
             os.environ["GRASS_CAIROFILE"] = self.mapfile
-            if 'cairo' not in gcmd.Command(['d.mon', '-p']).ReadStdOutput()[0]:
-                gcmd.Command(['d.mon',
-                              'start=cairo'], stderr=None)
+            if 'cairo' not in gcmd.RunCommand('d.mon',
+                                              flags='p',
+                                              read = True):
+                gcmd.RunCommand('d.mon',
+                                start = 'cairo')
         else:
             if not self.mapfile:
                 self.gtemp = tempfile.mkstemp()[1]
@@ -152,20 +162,22 @@
         try:
             if self.type == 'command':
                 read = False
-                for cmd in self.cmdlist:
-                    runcmd = gcmd.Command(cmd=cmd + ['--q'],
-                                          stderr=None)
-                    if runcmd.returncode != 0:
+                for c in self.cmd:
+                    ret = gcmd.RunCommand(c[0],
+                                          quiet = True,
+                                          **c[1])
+                    if ret != 0:
                         break
                     if not read:
                         os.environ["GRASS_PNG_READ"] = "TRUE"
                 
                 os.environ["GRASS_PNG_READ"] = "FALSE"
             else:
-                runcmd = gcmd.Command(cmd=self.cmdlist + ['--q'],
-                                      stderr=None)
-            
-            if runcmd.returncode != 0:
+                ret = gcmd.RunCommand(self.cmd[0],
+                                      quiet = True,
+                                      **self.cmd[1])
+                
+            if ret != 0:
                 #clean up after probley
                 try:
                     os.remove(self.mapfile)
@@ -176,7 +188,7 @@
                 self.mapfile = None
                 self.maskfile = None
         
-        except gcmd.CmdError, e:
+        except gcmd.GException, e:
             print >> sys.stderr, e
             # clean up after problems
             try:
@@ -192,8 +204,8 @@
         # stop monitor
         #
         if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
-            gcmd.Command(['d.mon',
-                          'stop=cairo'], stderr=None)
+            gcmd.RunCommand('d.mon',
+                            stop = 'cairo')
             del os.environ["GRASS_CAIROFILE"]
         elif os.environ.has_key("GRASS_PNGFILE"):
             del os.environ["GRASS_PNGFILE"]
@@ -205,32 +217,33 @@
     def GetCmd(self, string=False):
         """
         Get GRASS command as list of string.
-
+        
         @param string get command as string if True otherwise as list
-
+        
         @return command list/string
         """
         if string:
             if self.type == 'command':
-                cmdStr = ''
-                for cmd in self.cmdlist:
-                    cmdStr += ' '.join(cmd) + ';'
-                return cmdStr.rstrip(';')
+                scmd = []
+                for c in self.cmd:
+                    scmd.append(utils.GetCmdString(c))
+                
+                return ';'.join(scmd)
             else:
-                return ' '.join(self.cmdlist)
+                return utils.GetCmdString(self.cmd)
         else:
-            return self.cmdlist
+            return self.cmd
 
     def GetType(self):
-        """Get map layer type"""
+        """!Get map layer type"""
         return self.type
     
     def GetOpacity(self, float=False):
         """
         Get layer opacity level
-
+        
         @param float get opacity level in <0,1> otherwise <0,100>
-
+        
         @return opacity level
         """
         if float:
@@ -239,7 +252,7 @@
         return int (self.opacity * 100)
 
     def GetName(self, fullyQualified=True):
-        """Get map layer name
+        """!Get map layer name
 
         @param fullyQualified if True return 'name at mapset' otherwise
         ('name', 'mapset')
@@ -255,38 +268,34 @@
                          'mapset' : '' }
         
     def IsActive(self):
-        """Check if layer is activated for rendering"""
+        """!Check if layer is activated for rendering"""
         return self.active
-
+    
     def SetType(self, type):
-        """Set layer type"""
+        """!Set layer type"""
         if type not in ('raster', '3d-raster', 'vector',
                         'overlay', 'command',
                         'shaded', 'rgb', 'his', 'rastarrow', 'rastnum',
                         'thememap', 'themechart', 'grid', 'labels',
                         'geodesic','rhumb'):
-            raise gcmd.GStdError(_("Unsupported map layer type '%s'") % str(type))
+            raise gcmd.GException(_("Unsupported map layer type '%s'") % str(type))
         
         self.type = type
 
     def SetName(self, name):
-        """Set layer name"""
+        """!Set layer name"""
         self.name = name
-
-    def SetCmd(self, cmd):
-        """Set layer name"""
-        self.cmdlist = cmd
-
+        
     def SetActive(self, enable=True):
-        """Active or deactive layer"""
+        """!Active or deactive layer"""
         self.active = bool(enable)
 
     def SetHidden(self, enable=False):
-        """Hide or show map layer in Layer Manager"""
+        """!Hide or show map layer in Layer Manager"""
         self.hidden = bool(enable)
 
     def SetOpacity(self, value):
-        """Set opacity value"""
+        """!Set opacity value"""
         if value < 0:
             value = 0.
         elif value > 1:
@@ -295,18 +304,23 @@
         self.opacity = float(value)
         
     def SetCmd(self, cmd):
-        """Set new command for layer"""
-        self.cmdlist = cmd
+        """!Set new command for layer"""
+        if self.type == 'command':
+            self.cmd = []
+            for c in cmd:
+                self.cmd.append(utils.CmdToTuple(c))
+        else:
+            self.cmd  = utils.CmdToTuple(cmd)
         Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string=True))
         
         # for re-rendering
         self.force_render = True
         
 class MapLayer(Layer):
-    """Represents map layer in the map canvas"""
     def __init__(self, type, cmd, name=None,
-                 active=True, hidden=False, opacity=1.0):
-        """
+                 active=True, hidden=False, opacity=1.0): 
+        """!Represents map layer in the map canvas
+        
         @param type layer type ('raster', 'vector', 'command', etc.)
         @param cmd GRASS command to render layer,
         given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
@@ -317,29 +331,26 @@
         """
         Layer.__init__(self, type, cmd, name,
                        active, hidden, opacity)
-
-        #self.mapfile = self.gtemp + ".ppm"
-
+        
     def GetMapset(self):
-        """
-        Get mapset of map layer
-
+        """!Get mapset of map layer
+        
         @return mapset name
         @return '' on error (no name given)
         """
         if not self.name:
             return ''
-
+        
         try:
             return self.name.split('@')[1]
         except IndexError:
             return self.name
-
+        
 class Overlay(Layer):
-    """Represents overlay displayed in map canvas"""
     def __init__(self, id, type, cmd,
                  active=True, hidden=True, opacity=1.0):
-        """
+        """!Represents overlay displayed in map canvas
+        
         @param id overlay id (for PseudoDC)
         @param type overlay type ('barscale', 'legend', etc.)
         @param cmd GRASS command to render overlay,
@@ -350,117 +361,97 @@
         """
         Layer.__init__(self, 'overlay', cmd, type,
                        active, hidden, opacity)
-
+        
         self.id = id
-        #self.mapfile = self.gtemp + ".png"
-
+        
 class Map(object):
+    """!Map composition (stack of map layers and overlays)
     """
-    Map composition (stack of map layers and overlays)
-    """
-    def __init__(self, gisrc=None):
-        # 
+    def __init__(self, gisrc = None):
         # region/extent settigns
-        #
-        self.wind      = {}    # WIND settings (wind file)
-        self.region    = {}    # region settings (g.region)
-        self.width     = 640   # map width
-        self.height    = 480   # map height
-
-        #
+        self.wind      = dict() # WIND settings (wind file)
+        self.region    = dict() # region settings (g.region)
+        self.width     = 640    # map width
+        self.height    = 480    # map height
+        
         # list of layers
-        #
-        self.layers    = []  # stack of available GRASS layer
-
-        self.overlays  = []  # stack of available overlays
-        self.ovlookup  = {}  # lookup dictionary for overlay items and overlays
-
-        #
+        self.layers    = list()  # stack of available GRASS layer
+        
+        self.overlays  = list()  # stack of available overlays
+        self.ovlookup  = dict()  # lookup dictionary for overlay items and overlays
+        
         # environment settings
-        #
         # environment variables, like MAPSET, LOCATION_NAME, etc.
-        self.env         = {}
+        self.env   = dict()
         # path to external gisrc
         self.gisrc = gisrc
         
-        # 
         # generated file for g.pnmcomp output for rendering the map
-        #
-        #self.mapfile   = utils.GetTempfile()
         self.mapfile = tempfile.mkstemp(suffix='.ppm')[1]
-
+        
         # setting some initial env. variables
-        self.InitGisEnv() # g.gisenv
-        self.InitRegion()
-
-        #
+        self._initGisEnv() # g.gisenv
+        self.GetWindow()
         # GRASS environment variable (for rendering)
-        #
         os.environ["GRASS_TRANSPARENT"] = "TRUE"
         os.environ["GRASS_BACKGROUNDCOLOR"] = "ffffff"
-
-        self.projinfo = self.ProjInfo()
         
-    def InitRegion(self):
-        """
-        Initialize current region settings.
-
-        Set up 'self.region' using g.region command and
-        self.wind according to the wind file.
-
-        Adjust self.region based on map window size.
-        """
-
-        #
-        # setting region ('g.region -upg')
-        #
-        ### not needed here (MapFrame.OnSize())
-        # self.region = self.GetRegion()
-
-        #
-        # read WIND file
-        #
-        self.GetWindow()
-
-        #
-        # setting resolution
-        #
-        # not needed here (MapFrame.OnSize())
-        # self.SetRegion()
-
-    def InitGisEnv(self):
-        """
-        Stores GRASS variables (g.gisenv) to self.env variable
-        """
-
-        if not os.getenv("GISBASE"):
-            print >> sys.stderr, _("GISBASE not set. You must be in GRASS GIS to run this program.")
-            sys.exit(1)
-            
+        # projection info
+        self.projinfo = self._projInfo()
+        
+    def _runCommand(self, cmd, **kwargs):
+        """!Run command in environment defined by self.gisrc if
+        defined"""
         # use external gisrc if defined
         gisrc_orig = os.getenv("GISRC")
         if self.gisrc:
             os.environ["GISRC"] = self.gisrc
-
-        gisenvCmd = gcmd.Command(["g.gisenv"])
-
-        for line in gisenvCmd.ReadStdOutput():
-            line = line.strip()
-            key, val = line.split("=", 1)
-            val = val.replace(";","")
-            val = val.replace("'","")
-            self.env[key] = val
-
+        
+        ret = cmd(**kwargs)
+        
         # back to original gisrc
         if self.gisrc:
             os.environ["GISRC"] = gisrc_orig
-
+        
+        return ret
+    
+    def _initGisEnv(self):
+        """!Stores GRASS variables (g.gisenv) to self.env variable
+        """
+        if not os.getenv("GISBASE"):
+            sys.exit(_("GISBASE not set. You must be in GRASS GIS to run this program."))
+        
+        self.env = self._runCommand(grass.gisenv)
+            
+    def GetProjInfo(self):
+        """!Get projection info"""
+        return self.projinfo
+    
+    def _projInfo(self):
+        """!Return region projection and map units information
+        """
+        projinfo = dict()
+        
+        ret = self._runCommand(gcmd.RunCommand, prog = 'g.proj',
+                               read = True, flags = 'p')
+        
+        if not ret:
+            return projinfo
+        
+        for line in ret.splitlines():
+            if ':' in line:
+                key, val = line.split(':')
+                projinfo[key.strip()] = val.strip()
+            elif "XY location (unprojected)" in line:
+                projinfo['proj'] = 'xy'
+                projinfo['units'] = ''
+                break
+        
+        return projinfo
+    
     def GetWindow(self):
-        """Read WIND file and set up self.wind dictionary"""
+        """!Read WIND file and set up self.wind dictionary"""
         # FIXME: duplicated region WIND == g.region (at least some values)
-        if not self.env.has_key('GISDBASE'):
-            sys.exit(_("Quit wxGUI. GRASS is not running."))
-        
         filename = os.path.join (self.env['GISDBASE'],
                                  self.env['LOCATION_NAME'],
                                  self.env['MAPSET'],
@@ -468,98 +459,90 @@
         try:
             windfile = open (filename, "r")
         except IOError, e:
-            sys.stderr.write("%s: %s <%s>: %s\n%s\n" % (_("Error"), _("Unable to open file"),
-                                                    filename, e,
-                                                    _("wxGUI closed.")))
-            sys.exit(1)
-
+            sys.exit(_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") % \
+                         { 'file' : filename, 'ret' : e})
+        
         for line in windfile.readlines():
             line = line.strip()
-            key, value = line.split(":",1)
-            key = key.strip()
-            value = value.strip()
-            self.wind[key] = value
-            
+            key, value = line.split(":", 1)
+            self.wind[key.strip()] = value.strip()
+        
         windfile.close()
-
+        
         return self.wind
         
     def AdjustRegion(self):
+        """!Adjusts display resolution to match monitor size in
+        pixels. Maintains constant display resolution, not related to
+        computational region. Do NOT use the display resolution to set
+        computational resolution. Set computational resolution through
+        g.region.
         """
-        Adjusts display resolution to match monitor size in pixels.
-        Maintains constant display resolution, not related to computational
-        region. Do NOT use the display resolution to set computational
-        resolution. Set computational resolution through g.region.
-        """
-
         mapwidth    = abs(self.region["e"] - self.region["w"])
         mapheight   = abs(self.region['n'] - self.region['s'])
-
+        
         self.region["nsres"] =  mapheight / self.height
         self.region["ewres"] =  mapwidth  / self.width
         self.region['rows']  = round(mapheight / self.region["nsres"])
         self.region['cols']  = round(mapwidth / self.region["ewres"])
         self.region['cells'] = self.region['rows'] * self.region['cols']
-
+        
         Debug.msg (3, "Map.AdjustRegion(): %s" % self.region)
-
+        
         return self.region
 
     def AlignResolution(self):
+        """!Sets display extents to even multiple of current
+        resolution defined in WIND file from SW corner. This must be
+        done manually as using the -a flag can produce incorrect
+        extents.
         """
-        Sets display extents to even multiple of
-        current resolution defined in WIND file from SW corner.
-        This must be done manually as using the -a flag
-        can produce incorrect extents.
-        """
-
         # new values to use for saving to region file
         new = {}
         n = s = e = w = 0.0
         nwres = ewres = 0.0
-
+        
         # Get current values for region and display
         nsres = self.GetRegion()['nsres']
         ewres = self.GetRegion()['ewres']
-
+        
         n = float(self.region['n'])
         s = float(self.region['s'])
         e = float(self.region['e'])
         w = float(self.region['w'])
-
+        
         # Calculate rows, columns, and extents
         new['rows'] = math.fabs(round((n-s)/nsres))
         new['cols'] = math.fabs(round((e-w)/ewres))
-
+        
         # Calculate new extents
         new['s'] = nsres * round(s/nsres)
         new['w'] = ewres * round(w/ewres)
         new['n'] = new['s'] + (new['rows'] * nsres)
         new['e'] = new['w'] + (new['cols'] * ewres)
-
+        
         return new
 
     def AlignExtentFromDisplay(self):
-        """Align region extent based on display size from center point"""
-
+        """!Align region extent based on display size from center point"""
         # calculate new bounding box based on center of display
         if self.region["ewres"] > self.region["nsres"]:
             res = self.region["ewres"]
         else:
             res = self.region["nsres"]
-
+        
         Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \
                       (self.width, self.height, res, self.region['center_easting'],
                        self.region['center_northing']))
             
         ew = (self.width / 2) * res
         ns = (self.height / 2) * res
-
+        
         self.region['n'] = self.region['center_northing'] + ns
         self.region['s'] = self.region['center_northing'] - ns
         self.region['e'] = self.region['center_easting'] + ew
         self.region['w'] = self.region['center_easting'] - ew
-
+        
         # LL locations
         if self.projinfo['proj'] == 'll':
             if self.region['n'] > 90.0:
@@ -568,7 +551,7 @@
                 self.region['s'] = -90.0
         
     def ChangeMapSize(self, (width, height)):
-        """Change size of rendered map.
+        """!Change size of rendered map.
         
         @param width,height map size
 
@@ -585,12 +568,12 @@
             self.width  = 640
             self.height = 480
             return False
-
+        
     def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None,
                   n = None, s = None, e = None, w = None, default = False,
                   update = False):
         """!Get region settings (g.region -upgc)
-
+        
         Optionally extent, raster or vector map layer can be given.
         
         @param rast list of raster maps
@@ -605,74 +588,74 @@
         'n':'4928010', 's':'4913700', 'w':'589980',...}
         """
         region = {}
-
+        
         tmpreg = os.getenv("GRASS_REGION")
         if tmpreg:
             del os.environ["GRASS_REGION"]
-
+        
         # use external gisrc if defined
         gisrc_orig = os.getenv("GISRC")
         if self.gisrc:
             os.environ["GISRC"] = self.gisrc
-
+        
         # do not update & shell style output
-        cmdList = ["g.region", "-u", "-g", "-p", "-c"]
-
+        cmd = {}
+        cmd['flags'] = 'ugpc'
+        
         if default:
-            cmdList.append('-d')
+            cmd['flags'] += 'd'
         
         if regionName:
-            cmdList.append('region=%s' % regionName)
+            cmd['region'] = regionName
         
         if n:
-            cmdList.append('n=%s' % n)
+            cmd['n'] = n
         if s:
-            cmdList.append('s=%s' % s)
+            cmd['s'] = s
         if e:
-            cmdList.append('e=%s' % e)
+            cmd['e'] = e
         if w:
-            cmdList.append('w=%s' % w)
-
+            cmd['w'] = w
+        
         if rast:
             if zoom:
-                cmdList.append('zoom=%s' % ','.join(rast))
+                cmd['zoom'] = rast[0]
             else:
-                cmdList.append('rast=%s' % ','.join(rast))
-
+                cmd['rast'] = ','.join(rast)
+        
         if vect:
-            cmdList.append('vect=%s' % ','.join(vect))
+            cmd['vect'] = ','.join(vect)
         
-        try:
-            cmdRegion = gcmd.Command(cmdList)
-        except gcmd.CmdError, e:
+        ret = gcmd.RunCommand('g.region',
+                              read = True,
+                              **cmd)
+        if not ret:
             if rast:
-                e.message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
-                '%s%s' % (os.linesep, os.linesep) + e.message
+                message = _("Unable to zoom to raster map <%s>.") % rast[0]
             elif vect:
-                e.message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
-                '%s%s' % (os.linesep, os.linesep) + e.message
+                message = _("Unable to zoom to vector map <%s>.") % vect[0]
             else:
-                e.message = _("Unable to get current geographic extent. "
-                              "Force quiting wxGUI. Please run manually g.region to "
-                              "fix the problem.")
-            e.Show()
+                message = _("Unable to get current geographic extent. "
+                            "Force quiting wxGUI. Please run manually g.region to "
+                            "fix the problem.")
+            gcmd.GError(message)
             return self.region
-
-        for reg in cmdRegion.ReadStdOutput():
+        
+        for reg in ret.splitlines():
             key, val = reg.split("=", 1)
             try:
                 region[key] = float(val)
             except ValueError:
                 region[key] = val
-
+        
         # back to original gisrc
         if self.gisrc:
             os.environ["GISRC"] = gisrc_orig
-
+        
         # restore region
         if tmpreg:
             os.environ["GRASS_REGION"] = tmpreg
-
+        
         Debug.msg (3, "Map.GetRegion(): %s" % region)
         
         if update:
@@ -681,21 +664,21 @@
         return region
 
     def GetCurrentRegion(self):
-        """Get current display region settings"""
+        """!Get current display region settings"""
         return self.region
 
     def SetRegion(self, windres=False):
-        """
-        Render string for GRASS_REGION env. variable, so that the images will be rendered
-        from desired zoom level.
+        """!Render string for GRASS_REGION env. variable, so that the
+        images will be rendered from desired zoom level.
+        
+        @param windres uses resolution from WIND file rather than
+        display (for modules that require set resolution like
+        d.rast.num)
 
-        @param windres uses resolution from WIND file rather than display (for modules that require set
-        resolution like d.rast.num)
-
         @return String usable for GRASS_REGION variable or None
         """
         grass_region = ""
-
+        
         if windres:
             compRegion = self.GetRegion()
             region = copy.copy(self.region)
@@ -743,72 +726,39 @@
                     continue
                 else:
                     grass_region += key + ": "  + self.wind[key] + "; "
-
+            
             Debug.msg (3, "Map.SetRegion(): %s" % grass_region)
-
+            
             return grass_region
-
+        
         except:
             return None
-
-    def ProjInfo(self):
-        """
-        Return region projection and map units information
-        """
-
-        projinfo = {}
         
-        gisrc_orig = os.getenv("GISRC")
-        if self.gisrc:
-            os.environ["GISRC"] = self.gisrc
-        
-        p = gcmd.Command(['g.proj', '-p'])
-        
-        # back to original gisrc
-        if self.gisrc:
-            os.environ["GISRC"] = gisrc_orig
-        
-        if p.returncode == 0:
-            for line in p.ReadStdOutput():
-                if ':' in line:
-                    key,val = line.split(':')
-                    key = key.strip()
-                    val = val.strip()
-                    projinfo[key] = val
-                elif "XY location (unprojected)" in line:
-                    projinfo['proj'] = "xy"
-                    projinfo['units'] = ''
-            return projinfo
-        else:
-            return None
-
     def GetListOfLayers(self, l_type=None, l_mapset=None, l_name=None,
                         l_active=None, l_hidden=None):
-        """
-        Returns list of layers of selected properties or list of all
-        layers. 
+        """!Returns list of layers of selected properties or list of
+        all layers.
 
         @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
         @param l_mapset all layers from given mapset (only for maplayers)
         @param l_name all layers with given name
         @param l_active only layers with 'active' attribute set to True or False
         @param l_hidden only layers with 'hidden' attribute set to True or False
-
+        
         @return list of selected layers
         """
-
         selected = []
-
+        
         if type(l_type) == type(''):
             one_type = True
         else:
             one_type = False
-
+        
         if one_type and l_type == 'overlay':
             list = self.overlays
         else:
             list = self.layers
-
+        
         # ["raster", "vector", "wms", ... ]
         for layer in list:
             # specified type only
@@ -817,39 +767,39 @@
                     continue
                 elif not one_type and layer.type not in l_type:
                     continue
-
+            
             # mapset
             if (l_mapset != None and type != 'overlay') and \
                     layer.GetMapset() != l_mapset:
                 continue
-
+            
             # name
             if l_name != None and layer.name != l_name:
                 continue
-
+            
             # hidden and active layers
             if l_active != None and \
                    l_hidden != None:
                 if layer.active == l_active and \
                        layer.hidden == l_hidden:
                     selected.append(layer)
-
+            
             # active layers
             elif l_active != None:
                 if layer.active == l_active:
                     selected.append(layer)
-
+            
             # hidden layers
             elif l_hidden != None:
                 if layer.hidden == l_hidden:
                     selected.append(layer)
-
+            
             # all layers
             else:
                 selected.append(layer)
-
+        
         Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))
-
+        
         return selected
 
     def _renderLayers(self, force, mapWindow, maps, masks, opacities):
@@ -882,11 +832,10 @@
                 
             Debug.msg (3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
             ilayer += 1
-    
+        
     def Render(self, force=False, mapWindow=None, windres=False):
-        """
-        Creates final image composite
-
+        """!Creates final image composite
+        
         This function can conditionaly use high-level tools, which
         should be avaliable in wxPython library
         
@@ -899,12 +848,12 @@
         maps = []
         masks =[]
         opacities = []
-
+        
         # use external gisrc if defined
         gisrc_orig = os.getenv("GISRC")
         if self.gisrc:
             os.environ["GISRC"] = self.gisrc
-
+        
         tmp_region = os.getenv("GRASS_REGION")
         os.environ["GRASS_REGION"] = self.SetRegion(windres)
         os.environ["GRASS_WIDTH"]  = str(self.width)
@@ -937,7 +886,7 @@
                 maskstr += item.replace('\\', '/')
             maskstr = maskstr.rstrip(',')
             mapoutstr = self.mapfile.replace('\\', '/')
-
+        
         # compose command
         bgcolor = ':'.join(map(str, UserSettings.Get(group='display', key='bgcolor',
                                                      subkey='color')))
@@ -959,12 +908,20 @@
         
         if maps:
             # run g.pngcomp to get composite image
-            try:
-                gcmd.Command(complist)
-            except gcmd.CmdError, e:
-                print >> sys.stderr, e
+            ret = gcmd.RunCommand('g.pnmcomp',
+                                  input = '%s' % ",".join(maps),
+                                  mask = '%s' % ",".join(masks),
+                                  opacity = '%s' % ",".join(opacities),
+                                  background = bgcolor,
+                                  width = self.width,
+                                  height = self.height,
+                                  output = self.mapfile)
+            
+            if ret != 0:
+                print >> sys.stderr, _("ERROR: Rendering failed")
                 return None
-            Debug.msg (2, "Map.Render() force=%s file=%s" % (force, self.mapfile))
+            
+            Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile))
         
         # back to original gisrc
         if self.gisrc:
@@ -978,9 +935,8 @@
     def AddLayer(self, type, command, name=None,
                  l_active=True, l_hidden=False, l_opacity=1.0, l_render=False,
                  pos=-1):
-        """
-        Adds generic map layer to list of layers
-
+        """!Adds generic map layer to list of layers
+        
         @param type layer type ('raster', 'vector', etc.)
         @param command  GRASS command given as list
         @param name layer name
@@ -989,18 +945,16 @@
         @param l_opacity opacity level range from 0(transparent) - 1(not transparent)
         @param l_render render an image if True
         @param pos position in layer list (-1 for append)
-
+        
         @return new layer on success
         @return None on failure
-
         """
         # l_opacity must be <0;1>
         if l_opacity < 0: l_opacity = 0
         elif l_opacity > 1: l_opacity = 1
-
         layer = MapLayer(type=type, name=name, cmd=command,
                          active=l_active, hidden=l_hidden, opacity=l_opacity)
-
+        
         # add maplayer to the list of layers
         if pos > -1:
             self.layers.insert(pos, layer)
@@ -1010,27 +964,25 @@
         Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
         if l_render:
             if not layer.Render():
-                raise gcmd.GStdError(_("Unable to render map layer <%s>.") % (name))
-
+                raise gcmd.GException(_("Unable to render map layer <%s>.") % (name))
+        
         return layer
 
     def DeleteLayer(self, layer, overlay=False):
-        """
-        Removes layer from list of layers
-
+        """!Removes layer from list of layers
+        
         @param layer layer instance in layer tree
         @param overlay delete overlay (use self.DeleteOverlay() instead)
 
         @return removed layer on success or None
         """
-
         Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)
-
+        
         if overlay:
             list = self.overlays
         else:
             list = self.layers
-
+        
         if layer in list:
             if layer.mapfile:
                 base = os.path.split(layer.mapfile)[0]
@@ -1042,29 +994,27 @@
                 for f in glob.glob(basefile):
                     os.remove(f)
             list.remove(layer)
-
+            
             return layer
-
+        
         return None
 
     def ReorderLayers(self, layerList):
-        """
-        Reorder list to match layer tree
-
+        """!Reorder list to match layer tree
+        
         @param layerList list of layers
         """
         self.layers = layerList
-
+        
         layerNameList = ""
         for layer in self.layers:
             if layer.name:
                 layerNameList += layer.name + ','
         Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
                    (layerNameList))
-
+        
     def ChangeLayer(self, layer, render=False, **kargs):
-        """
-        Change map layer properties
+        """!Change map layer properties
 
         @param layer map layer instance
         @param type layer type ('raster', 'vector', etc.)
@@ -1075,7 +1025,6 @@
         @param opacity opacity level range from 0(transparent) - 1(not transparent)
         @param render render an image if True
         """
-
         Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)
         
         if kargs.has_key('type'):
@@ -1086,25 +1035,24 @@
         
         if kargs.has_key('name'):
             layer.SetName(kargs['name'])
-
+        
         if kargs.has_key('active'):
             layer.SetActive(kargs['active'])
-
+        
         if kargs.has_key('hidden'):
             layer.SetHidden(kargs['hidden'])
-
+        
         if kargs.has_key('opacity'):
             layer.SetOpacity(kargs['opacity'])
-            
+        
         if render and not layer.Render():
             raise gcmd.GException(_("Unable to render map layer <%s>.") % 
                                   (name))
-
+        
         return layer
 
     def ChangeOpacity(self, layer, l_opacity):
-        """
-        Changes opacity value of map layer
+        """!Changes opacity value of map layer
 
         @param layer layer instance in layer tree
         @param l_opacity opacity level <0;1>
@@ -1112,27 +1060,25 @@
         # l_opacity must be <0;1>
         if l_opacity < 0: l_opacity = 0
         elif l_opacity > 1: l_opacity = 1
-
+        
         layer.opacity = l_opacity
         Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
                    (layer.name, layer.opacity))
 
     def ChangeLayerActive(self, layer, active):
-        """
-        Enable or disable map layer
-
+        """!Enable or disable map layer
+        
         @param layer layer instance in layer tree
         @param active to be rendered (True)
         """
         layer.active = active
-
+        
         Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
                    (layer.name, layer.active))
 
     def ChangeLayerName (self, layer, name):
-        """
-        Change name of the layer
-
+        """!Change name of the layer
+        
         @param layer layer instance in layer tree
         @param name  layer name to set up
         """
@@ -1141,18 +1087,16 @@
         layer.name =  name
 
     def RemoveLayer(self, name=None, id=None):
-        """
-        Removes layer from layer list
-
+        """!Removes layer from layer list
+        
         Layer is defined by name at mapset or id.
-
+        
         @param name layer name (must be unique)
         @param id layer index in layer list
 
         @return removed layer on success
         @return None on failure
         """
-
         # delete by name
         if name:
             retlayer = None
@@ -1166,13 +1110,12 @@
         # del by id
         elif id != None:
             return self.layers.pop(id)
-
+        
         return None
 
     def GetLayerIndex(self, layer, overlay=False):
-        """
-        Get index of layer in layer list.
-
+        """!Get index of layer in layer list.
+        
         @param layer layer instace in layer tree
         @param overlay use list of overlays instead
         
@@ -1186,13 +1129,13 @@
             
         if layer in list:
             return list.index(layer)
-
+        
         return -1
 
     def AddOverlay(self, id, type, command,
                    l_active=True, l_hidden=True, l_opacity=1.0, l_render=False):
-        """
-        Adds overlay (grid, barscale, legend, etc.) to list of overlays
+        """!Adds overlay (grid, barscale, legend, etc.) to list of
+        overlays
         
         @param id overlay id (PseudoDC)
         @param type overlay type (barscale, legend)
@@ -1200,61 +1143,65 @@
         @param l_active overlay activated (True) or disabled (False)
         @param l_hidden overlay is not shown in layer tree (if True)
         @param l_render render an image (if True)
-
+        
         @return new layer on success
         @retutn None on failure
         """
-
         Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
         overlay = Overlay(id=id, type=type, cmd=command,
                           active=l_active, hidden=l_hidden, opacity=l_opacity)
-
+        
         # add maplayer to the list of layers
         self.overlays.append(overlay)
-
+        
         if l_render and command != '' and not overlay.Render():
             raise gcmd.GException(_("Unable render overlay <%s>.") % 
                                   (name))
-
+        
         return self.overlays[-1]
 
-    def ChangeOverlay(self, id, type, command,
-                      l_active=True, l_hidden=False, l_opacity=1, l_render=False):
-        """
-        Change overlay properities
-
+    def ChangeOverlay(self, id, render=False, **kargs):
+        """!Change overlay properities
+        
         Add new overlay if overlay with 'id' doesn't exist.
-
+        
         @param id overlay id (PseudoDC)
         @param type overlay type (barscale, legend)
         @param command GRASS command to render overlay
         @param l_active overlay activated (True) or disabled (False)
         @param l_hidden overlay is not shown in layer tree (if True)
         @param l_render render an image (if True)
-
+        
         @return new layer on success
         """
         overlay = self.GetOverlay(id, list=False)
         if  overlay is None:
-            overlay = Overlay(id, type, command,
-                              l_active, l_hidden, l_opacity)
-        else:
-            overlay.id = id
-            overlay.name = type
-            overlay.cmdlist = command
-            overlay.active = l_active
-            overlay.hidden = l_hidden
-            overlay.opacity = l_opacity
-
-        if l_render and command != [] and not overlay.Render():
+            overlay = Overlay(id, type = None, cmd = None)
+        
+        if kargs.has_key('type'):
+            overlay.SetName(kargs['type']) # type -> overlay
+        
+        if kargs.has_key('command'):
+            overlay.SetCmd(kargs['command'])
+        
+        if kargs.has_key('active'):
+            overlay.SetActive(kargs['active'])
+        
+        if kargs.has_key('hidden'):
+            overlay.SetHidden(kargs['hidden'])
+        
+        if kargs.has_key('opacity'):
+            overlay.SetOpacity(kargs['opacity'])
+        
+        if render and command != [] and not overlay.Render():
             raise gcmd.GException(_("Unable render overlay <%s>") % 
                                   (name))
-
+        
         return overlay
 
     def GetOverlay(self, id, list=False):
-        """Return overlay(s) with 'id'
-
+        """!Return overlay(s) with 'id'
+        
         @param id overlay id
         @param list return list of overlays of True
         otherwise suppose 'id' to be unique
@@ -1273,23 +1220,22 @@
                 return None
             else:
                 return ovl[0]
-
+        
         return ovl
 
     def DeleteOverlay(self, overlay):
-        """Delete overlay
-
+        """!Delete overlay
+        
         @param id overlay id
-
+        
         @return removed overlay on success or None
         """
         return self.DeleteLayer(overlay, overlay=True)
 
     def Clean(self):
-        """
-        Clean layer stack - go trough all layers and remove them from layer list
-        Removes also l_mapfile and l_maskfile
-
+        """!Clean layer stack - go trough all layers and remove them
+        from layer list Removes also l_mapfile and l_maskfile
+        
         @return 1 on faulure
         @return None on success
         """
@@ -1307,7 +1253,7 @@
                     for f in glob.glob(removepath):
                         os.remove(f)
                 self.layers.remove(layer)
-                
+            
             for overlay in self.overlays:
                 if overlay.mapfile:
                     dir = os.path.dirname(overlay.mapfile)
@@ -1322,37 +1268,36 @@
         self.layers = []
 
     def ReverseListOfLayers(self):
-        """Reverse list of layers"""
+        """!Reverse list of layers"""
         return self.layers.reverse()
 
 if __name__ == "__main__":
-    """
-    Test of Display class.
+    """!Test of Display class.
     Usage: display=Render()
     """
-
     import gettext
     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
-
+    
     print "Initializing..."
-    os.system("g.region -d")
-
+    grass.run_command("g.region", flags="d")
+    
     map = Map()
     map.width = 640
     map.height = 480
-
+    
     map.AddLayer(item=None,
                  type="raster",
                  name="elevation.dem",
                  command = ["d.rast", "elevation.dem at PERMANENT", "catlist=1000-1500", "-i"],
                  l_opacity=.7)
-
+    
     map.AddLayer(item=None,
                  type="vector",
                  name="streams",
                  command = ["d.vect", "streams at PERMANENT", "color=red", "width=3", "type=line"])
-
+    
     image = map.Render(force=True)
-
+    
     if image:
         os.system("display %s" % image)
+    

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/rules.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/rules.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/rules.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -162,9 +162,9 @@
             self.rules = self.rules + '%s' % os.linesep
 
     def OnHelp(self, event):
-        gcmd.Command(['g.manual',
-                      '--quiet', 
-                      '%s' % self.cmd[0]])
+        gcmd.RunCommand('g.manual',
+                        quiet = True,
+                        entry = self.cmd[0])
 
     def OnOverwrite(self, event):
         self.overwrite = event.IsChecked()

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/sqlbuilder.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/sqlbuilder.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/sqlbuilder.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -39,9 +39,10 @@
 import wx
 
 import grass.script as grass
+grass.set_fatal_exit(False)
 
-import dbm
 import gcmd
+import dbm_base
 
 class SQLFrame(wx.Frame):
     """!SQL Frame class"""
@@ -66,7 +67,7 @@
         
         # db info
         self.layer = layer
-        self.dbInfo = dbm.VectorDBInfo(self.vectmap)
+        self.dbInfo = dbm_base.VectorDBInfo(self.vectmap)
         self.tablename = self.dbInfo.GetTable(self.layer)
         self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
         
@@ -183,7 +184,7 @@
         databasebox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
                                    label = " %s " % _("Database connection"))
         databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
-        databaseboxsizer.Add(item=dbm.createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
+        databaseboxsizer.Add(item=dbm_base.createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
                              proportion=1,
                              flag=wx.EXPAND | wx.ALL,
                              border=3)
@@ -293,14 +294,12 @@
         
         querystring = "SELECT %s FROM %s" % (column, self.tablename)
         
-        cmd = gcmd.Command(['db.select',
-                            '-c',
-                            'table=%s' % self.tablename,
-                            'sql=%s' % querystring,
-                            'database=%s' % self.database,
-                            'driver=%s' % self.driver])
+        data = grass.db_select(table = self.tablename,
+                               sql = querystring,
+                               database = self.database,
+                               driver = self.driver)
         i = 0
-        for line in cmd.ReadStdOutput():
+        for line in data:
             if justsample and i < 256 or \
                not justsample:
                 self.list_values.Append(line.strip())

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/toolbars.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/toolbars.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,7 +1,7 @@
 """!
 @package toolbar
 
- at brief Toolbars for Map Display window
+ at brief wxGUI toolbar widgets
 
 Classes:
  - AbstractToolbar
@@ -11,85 +11,88 @@
  - VDigitToolbar
  - ProfileToolbar
  - NvizToolbar
+ - ModelToolbar
+ - HistogramToolbar
+ - LayerManagerToolbar
 
-(C) 2007-2010 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.
+(C) 2007-2010 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.
 
 @author Michael Barton
 @author Jachym Cepicky
 @author Martin Landa <landa.martin gmail.com>
 """
 
-import wx
-import os, sys
+import os
+import sys
 import platform
 
+from grass.script import core as grass
+
+import wx
+
 import globalvar
 import gcmd
-import grassenv
 import gdialogs
 import vdigit
 from vdigit import VDigitSettingsDialog, haveVDigit
 from debug import Debug
-from icon import Icons
 from preferences import globalSettings as UserSettings
 from nviz import haveNviz
+from nviz_preferences import NvizPreferencesDialog
 
 gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
 sys.path.append(gmpath)
+from icon import Icons as Icons
 
-class AbstractToolbar(object):
-    """Abstract toolbar class"""
-    def __init__(self):
-        pass
+class AbstractToolbar(wx.ToolBar):
+    """!Abstract toolbar class"""
+    def __init__(self, parent):
+        self.parent = parent
+        wx.ToolBar.__init__(self, parent = self.parent, id = wx.ID_ANY)
     
-    def InitToolbar(self, parent, toolbar, toolData):
-        """Initialize toolbar, i.e. add tools to the toolbar
-
-        @return list of ids (of added tools)
+        self.action = dict()
+        
+        self.Bind(wx.EVT_TOOL, self.OnTool)
+        
+        self.SetToolBitmapSize(globalvar.toolbarSize)
+        
+    def InitToolbar(self, toolData):
+        """!Initialize toolbar, add tools to the toolbar
         """
-
         for tool in toolData:
-            self.CreateTool(parent, toolbar, *tool)
-
-        self._toolbar = toolbar
+            self.CreateTool(*tool)
+        
         self._data = toolData
         
-        self.parent = parent
-        
     def ToolbarData(self):
-        """Toolbar data"""
+        """!Toolbar data (virtual)"""
         return None
-
-    def CreateTool(self, parent, toolbar, tool, label, bitmap, kind,
+    
+    def CreateTool(self, tool, label, bitmap, kind,
                    shortHelp, longHelp, handler):
-        """Add tool to the toolbar
-
+        """!Add tool to the toolbar
+        
         @return id of tool
         """
-
         bmpDisabled=wx.NullBitmap
-
+        
         if label:
             Debug.msg(3, "CreateTool(): tool=%d, label=%s bitmap=%s" % \
                   (tool, label, bitmap))
-            toolWin = toolbar.AddLabelTool(tool, label, bitmap,
-                                           bmpDisabled, kind,
-                                           shortHelp, longHelp)
-            parent.Bind(wx.EVT_TOOL, handler, toolWin)
-        else: # add separator
-            toolbar.AddSeparator()
-
+            toolWin = self.AddLabelTool(tool, label, bitmap,
+                                        bmpDisabled, kind,
+                                        shortHelp, longHelp)
+            self.Bind(wx.EVT_TOOL, handler, toolWin)
+        else: # separator
+            self.AddSeparator()
+        
         return tool
-
-    def GetToolbar(self):
-        """Get toolbar widget reference"""
-        return self._toolbar
-
+    
     def EnableLongHelp(self, enable=True):
-        """Enable/disable long help
-
+        """!Enable/disable long help
+        
         @param enable True for enable otherwise disable
         """
         for tool in self._data:
@@ -97,81 +100,95 @@
                 continue
             
             if enable:
-                self._toolbar.SetToolLongHelp(tool[0], tool[5])
+                self.SetToolLongHelp(tool[0], tool[5])
             else:
-                self._toolbar.SetToolLongHelp(tool[0], "")
-
+                self.SetToolLongHelp(tool[0], "")
+        
     def OnTool(self, event):
-        """Tool selected"""
+        """!Tool selected"""
+        if self.parent.GetName() == "GCPFrame":
+            return
+        
         if self.parent.toolbars['vdigit']:
             # update vdigit toolbar (unselect currently selected tool)
             id = self.parent.toolbars['vdigit'].GetAction(type='id')
-            self.parent.toolbars['vdigit'].GetToolbar().ToggleTool(id, False)
+            self.parent.toolbars['vdigit'].ToggleTool(id, False)
         
         if event:
             # deselect previously selected tool
             id = self.action.get('id', -1)
             if id != event.GetId():
-                self._toolbar.ToggleTool(self.action['id'], False)
+                self.ToggleTool(self.action['id'], False)
             else:
-                self._toolbar.ToggleTool(self.action['id'], True)
+                self.ToggleTool(self.action['id'], True)
             
             self.action['id'] = event.GetId()
             
             event.Skip()
         else:
             # initialize toolbar
-            self._toolbar.ToggleTool(self.action['id'], True)
-    
+            self.ToggleTool(self.action['id'], True)
+        
     def GetAction(self, type='desc'):
-        """Get current action info"""
+        """!Get current action info"""
         return self.action.get(type, '')
-
+    
     def SelectDefault(self, event):
-        """Select default tool"""
-        self._toolbar.ToggleTool(self.defaultAction['id'], True)
+        """!Select default tool"""
+        self.ToggleTool(self.defaultAction['id'], True)
         self.defaultAction['bind'](event)
         self.action = { 'id' : self.defaultAction['id'],
                         'desc' : self.defaultAction.get('desc', '') }
         
     def FixSize(self, width):
-	"""Fix toolbar width on Windows
+	"""!Fix toolbar width on Windows
         
 	@todo Determine why combobox causes problems here
 	"""
         if platform.system() == 'Windows':
-            size = self._toolbar.GetBestSize()
-            self._toolbar.SetSize((size[0] + width, size[1]))
+            size = self.GetBestSize()
+            self.SetSize((size[0] + width, size[1]))
+
+    def Enable(self, tool, enable = True):
+        """!Enable defined tool
         
+        @param tool name
+        @param enable True to enable otherwise disable tool
+        """
+        try:
+            id = getattr(self, tool)
+        except AttributeError:
+            return
+        
+        self.EnableTool(id, enable)
+        
 class MapToolbar(AbstractToolbar):
+    """!Map Display toolbar
     """
-    Main Map Display toolbar
-    """
+    def __init__(self, parent, mapcontent):
+        """!Map Display constructor
 
-    def __init__(self, mapdisplay, map):
-        AbstractToolbar.__init__(self)
-
-        self.mapcontent = map
-        self.mapdisplay = mapdisplay
-
-        self.toolbar = wx.ToolBar(parent=self.mapdisplay, id=wx.ID_ANY)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.mapdisplay, self.toolbar, self.ToolbarData())
+        @param parent reference to MapFrame
+        @param mapcontent reference to render.Map (registred by MapFrame)
+        """
+        self.mapcontent = mapcontent # render.Map
+        AbstractToolbar.__init__(self, parent = parent) # MapFrame
         
+        self.InitToolbar(self.ToolbarData())
+        
         # optional tools
         choices = [ _('2D view'), ]
         self.toolId = { '2d' : 0 }
-        log = self.parent.gismanager.goutput
+        log = self.parent.GetLayerManager().GetLogWindow()
         if haveNviz:
             choices.append(_('3D view'))
             self.toolId['3d'] = 1
         else:
             from nviz import errorMsg
             log.WriteCmdLog(_('3D view mode not available'))
-            log.WriteWarning(_('Reason: %s') % errorMsg)
+            log.WriteWarning(_('Reason: %s') % str(errorMsg))
             log.WriteLog(_('Note that the wxGUI\'s 3D view mode is currently disabled '
-                           '(hopefully this will be fixed soon). '
+                           'on MS Windows (hopefully this will be fixed soon). '
                            'Please keep an eye out for updated versions of GRASS. '
                            'In the meantime you can use "NVIZ" from the File menu.'), wrap = 60)
             
@@ -193,32 +210,33 @@
             
             self.toolId['vdigit'] = -1
         
-        self.combo = wx.ComboBox(parent=self.toolbar, id=wx.ID_ANY, value=_('2D view'),
-                                 choices=choices,
-                                 style=wx.CB_READONLY, size=(90, -1))
+        self.combo = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                 choices = choices,
+                                 style = wx.CB_READONLY, size = (90, -1))
+        self.combo.SetSelection(0)
         
-        self.comboid = self.toolbar.AddControl(self.combo)
-        self.mapdisplay.Bind(wx.EVT_COMBOBOX, self.OnSelectTool, self.comboid)
-
+        self.comboid = self.AddControl(self.combo)
+        self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectTool, self.comboid)
+        
         # realize the toolbar
-        self.toolbar.Realize()
+        self.Realize()
         
         # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
         self.combo.Hide()
         self.combo.Show()
         
-        # default action
         self.action = { 'id' : self.pointer }
         self.defaultAction = { 'id' : self.pointer,
-                               'bind' : self.mapdisplay.OnPointer }
+                               'bind' : self.parent.OnPointer }
         
         self.OnTool(None)
         
+        self.EnableTool(self.zoomback, False)
+        
         self.FixSize(width = 90)
         
     def ToolbarData(self):
-        """Toolbar data"""
-
+        """!Toolbar data"""
         self.displaymap = wx.NewId()
         self.rendermap = wx.NewId()
         self.erase = wx.NewId()
@@ -229,92 +247,95 @@
         self.zoomout = wx.NewId()
         self.zoomback = wx.NewId()
         self.zoommenu = wx.NewId()
+        self.zoomextent = wx.NewId()
         self.analyze = wx.NewId()
         self.dec = wx.NewId()
         self.savefile = wx.NewId()
         self.printmap = wx.NewId()
-
+        
         # tool, label, bitmap, kind, shortHelp, longHelp, handler
         return (
             (self.displaymap, "displaymap", Icons["displaymap"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["displaymap"].GetLabel(), Icons["displaymap"].GetDesc(),
-             self.mapdisplay.OnDraw),
+             self.parent.OnDraw),
             (self.rendermap, "rendermap", Icons["rendermap"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["rendermap"].GetLabel(), Icons["rendermap"].GetDesc(),
-             self.mapdisplay.OnRender),
+             self.parent.OnRender),
             (self.erase, "erase", Icons["erase"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["erase"].GetLabel(), Icons["erase"].GetDesc(),
-             self.mapdisplay.OnErase),
+             self.parent.OnErase),
             ("", "", "", "", "", "", ""),
             (self.pointer, "pointer", Icons["pointer"].GetBitmap(),
              wx.ITEM_CHECK, Icons["pointer"].GetLabel(), Icons["pointer"].GetDesc(),
-             self.mapdisplay.OnPointer),
+             self.parent.OnPointer),
             (self.query, "query", Icons["query"].GetBitmap(),
              wx.ITEM_CHECK, Icons["query"].GetLabel(), Icons["query"].GetDesc(),
-             self.mapdisplay.OnQuery),
+             self.parent.OnQuery),
             (self.pan, "pan", Icons["pan"].GetBitmap(),
              wx.ITEM_CHECK, Icons["pan"].GetLabel(), Icons["pan"].GetDesc(),
-             self.mapdisplay.OnPan),
+             self.parent.OnPan),
             (self.zoomin, "zoom_in", Icons["zoom_in"].GetBitmap(),
              wx.ITEM_CHECK, Icons["zoom_in"].GetLabel(), Icons["zoom_in"].GetDesc(),
-             self.mapdisplay.OnZoomIn),
+             self.parent.OnZoomIn),
             (self.zoomout, "zoom_out", Icons["zoom_out"].GetBitmap(),
              wx.ITEM_CHECK, Icons["zoom_out"].GetLabel(), Icons["zoom_out"].GetDesc(),
-             self.mapdisplay.OnZoomOut),
+             self.parent.OnZoomOut),
+            (self.zoomextent, "zoom_extent", Icons["zoom_extent"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["zoom_extent"].GetLabel(), Icons["zoom_extent"].GetDesc(),
+             self.parent.OnZoomToMap),
             (self.zoomback, "zoom_back", Icons["zoom_back"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["zoom_back"].GetLabel(), Icons["zoom_back"].GetDesc(),
-             self.mapdisplay.OnZoomBack),
+             self.parent.OnZoomBack),
             (self.zoommenu, "zoommenu", Icons["zoommenu"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["zoommenu"].GetLabel(), Icons["zoommenu"].GetDesc(),
-             self.mapdisplay.OnZoomMenu),
+             self.parent.OnZoomMenu),
             ("", "", "", "", "", "", ""),
             (self.analyze, "analyze", Icons["analyze"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["analyze"].GetLabel(), Icons["analyze"].GetDesc(),
-             self.mapdisplay.OnAnalyze),
+             self.parent.OnAnalyze),
             ("", "", "", "", "", "", ""),
             (self.dec, "overlay", Icons["overlay"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["overlay"].GetLabel(), Icons["overlay"].GetDesc(),
-             self.mapdisplay.OnDecoration),
+             self.parent.OnDecoration),
             ("", "", "", "", "", "", ""),
             (self.savefile, "savefile", Icons["savefile"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["savefile"].GetLabel(), Icons["savefile"].GetDesc(),
-             self.mapdisplay.SaveToFile),
+             self.parent.SaveToFile),
             (self.printmap, "printmap", Icons["printmap"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["printmap"].GetLabel(), Icons["printmap"].GetDesc(),
-             self.mapdisplay.PrintMenu),
+             self.parent.PrintMenu),
             ("", "", "", "", "", "", "")
             )
-
+    
     def OnSelectTool(self, event):
+        """!Select / enable tool available in tools list
         """
-        Select / enable tool available in tools list
-        """
         tool =  event.GetSelection()
         
         if tool == self.toolId['2d']:
             self.ExitToolbars()
             self.Enable2D(True)
-
+        
         elif tool == self.toolId['3d'] and \
-                not self.mapdisplay.toolbars['nviz']:
+                not self.parent.toolbars['nviz']:
             self.ExitToolbars()
-            self.mapdisplay.AddToolbar("nviz")
+            self.parent.AddToolbar("nviz")
             
         elif tool == self.toolId['vdigit'] and \
-                not self.mapdisplay.toolbars['vdigit']:
+                not self.parent.toolbars['vdigit']:
             self.ExitToolbars()
-            self.mapdisplay.AddToolbar("vdigit")
-
+            self.parent.AddToolbar("vdigit")
+            self.parent.MapWindow.SetFocus()
+        
     def ExitToolbars(self):
-        if self.mapdisplay.toolbars['vdigit']:
-            self.mapdisplay.toolbars['vdigit'].OnExit()
-        if self.mapdisplay.toolbars['nviz']:       
-            self.mapdisplay.toolbars['nviz'].OnExit()
-
+        if self.parent.toolbars['vdigit']:
+            self.parent.toolbars['vdigit'].OnExit()
+        if self.parent.toolbars['nviz']:       
+            self.parent.toolbars['nviz'].OnExit()
+        
     def Enable2D(self, enabled):
-        """Enable/Disable 2D display mode specific tools"""
+        """!Enable/Disable 2D display mode specific tools"""
         for tool in (self.pointer,
-                     self.query,
                      self.pan,
                      self.zoomin,
                      self.zoomout,
@@ -322,9 +343,8 @@
                      self.zoommenu,
                      self.analyze,
                      self.dec,
-                     self.savefile,
                      self.printmap):
-            self.toolbar.EnableTool(tool, enabled)
+            self.EnableTool(tool, enabled)
         
 class GCPManToolbar(AbstractToolbar):
     """!
@@ -333,16 +353,12 @@
     @param parent reference to GCP widget
     """
     def __init__(self, parent):
-        AbstractToolbar.__init__(self)
-        self.parent = parent
+        AbstractToolbar.__init__(self, parent)
         
-        self.toolbar = wx.ToolBar(parent=self.parent, id=wx.ID_ANY)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.parent, self.toolbar, self.ToolbarData())
+        self.InitToolbar(self.ToolbarData())
         
         # realize the toolbar
-        self.toolbar.Realize()
+        self.Realize()
 
     def ToolbarData(self):
         self.gcpSave = wx.NewId()
@@ -383,33 +399,28 @@
     """
     GCP Display toolbar
     """
-    def __init__(self, mapdisplay):
+    def __init__(self, parent):
         """!
         GCP Display toolbar constructor
         """
-        AbstractToolbar.__init__(self)
-
-        self.parent = mapdisplay
-
-        self.toolbar = wx.ToolBar(parent=self.parent, id=wx.ID_ANY)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.parent, self.toolbar, self.ToolbarData())
-
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
         # add tool to toggle active map window
         self.togglemapid = wx.NewId()
-        self.togglemap = wx.Choice(parent=self.toolbar, id=self.togglemapid,
+        self.togglemap = wx.Choice(parent=self, id=self.togglemapid,
 						    choices = [_('source'), _('target')],
 						    style=wx.CB_READONLY)
 
-        self.toolbar.InsertControl(10, self.togglemap)
+        self.InsertControl(10, self.togglemap)
 
-        self.toolbar.SetToolShortHelp(self.togglemapid, '%s %s %s' % (_('Set map canvas for '),
+        self.SetToolShortHelp(self.togglemapid, '%s %s %s' % (_('Set map canvas for '),
                                                               Icons["zoom_back"].GetLabel(),
                                                               _(' / Zoom to map')))
 
         # realize the toolbar
-        self.toolbar.Realize()
+        self.Realize()
         
         self.action = { 'id' : self.gcpset }
         self.defaultAction = { 'id' : self.gcpset,
@@ -417,7 +428,7 @@
         
         self.OnTool(None)
         
-        self.toolbar.EnableTool(self.zoomback, False)
+        self.EnableTool(self.zoomback, False)
         
     def ToolbarData(self):
         """!Toolbar data"""
@@ -484,32 +495,33 @@
     
 class GRToolbar(AbstractToolbar):
     """
-    Georectification Display toolbar
+    Georectification toolbar
     """
+    def __init__(self, parent, mapcontent):
+        """!
+        Georectification toolbar constructor
 
-    def __init__(self, mapdisplay, map):
-        self.mapcontent = map
-        self.mapdisplay = mapdisplay
-
-        self.toolbar = wx.ToolBar(parent=self.mapdisplay, id=wx.ID_ANY)
-
-        # self.SetToolBar(self.toolbar)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.mapdisplay, self.toolbar, self.ToolbarData())
-
+        @param parent reference to MapFrame
+        @param mapcontent reference to render.Map (registred by MapFrame)
+        """
+        self.mapcontent = mapcontent
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
         # realize the toolbar
-        self.toolbar.Realize()
-
+        self.Realize()
+        
         self.action = { 'id' : self.gcpset }
         self.defaultAction = { 'id' : self.gcpset,
                                'bind' : self.parent.OnPointer }
         
         self.OnTool(None)
-                
+        
+        self.EnableTool(self.zoomback, False)
+        
     def ToolbarData(self):
-        """Toolbar data"""
-
+        """!Toolbar data"""
         self.displaymap = wx.NewId()
         self.rendermap = wx.NewId()
         self.erase = wx.NewId()
@@ -519,67 +531,60 @@
         self.zoomout = wx.NewId()
         self.zoomback = wx.NewId()
         self.zoomtomap = wx.NewId()
-
+        
         # tool, label, bitmap, kind, shortHelp, longHelp, handler
         return (
             (self.displaymap, "displaymap", Icons["displaymap"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["displaymap"].GetLabel(), Icons["displaymap"].GetDesc(),
-             self.mapdisplay.OnDraw),
+             self.parent.OnDraw),
             (self.rendermap, "rendermap", Icons["rendermap"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["rendermap"].GetLabel(), Icons["rendermap"].GetDesc(),
-             self.mapdisplay.OnRender),
+             self.parent.OnRender),
             (self.erase, "erase", Icons["erase"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["erase"].GetLabel(), Icons["erase"].GetDesc(),
-             self.mapdisplay.OnErase),
+             self.parent.OnErase),
             ("", "", "", "", "", "", ""),
             (self.gcpset, "grGcpSet", Icons["grGcpSet"].GetBitmap(),
              wx.ITEM_RADIO, Icons["grGcpSet"].GetLabel(), Icons["grGcpSet"].GetDesc(),
-             self.mapdisplay.OnPointer),
+             self.parent.OnPointer),
             (self.pan, "pan", Icons["pan"].GetBitmap(),
              wx.ITEM_RADIO, Icons["pan"].GetLabel(), Icons["pan"].GetDesc(),
-             self.mapdisplay.OnPan),
+             self.parent.OnPan),
             (self.zoomin, "zoom_in", Icons["zoom_in"].GetBitmap(),
              wx.ITEM_RADIO, Icons["zoom_in"].GetLabel(), Icons["zoom_in"].GetDesc(),
-             self.mapdisplay.OnZoomIn),
+             self.parent.OnZoomIn),
             (self.zoomout, "zoom_out", Icons["zoom_out"].GetBitmap(),
              wx.ITEM_RADIO, Icons["zoom_out"].GetLabel(), Icons["zoom_out"].GetDesc(),
-             self.mapdisplay.OnZoomOut),
+             self.parent.OnZoomOut),
             (self.zoomback, "zoom_back", Icons["zoom_back"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["zoom_back"].GetLabel(), Icons["zoom_back"].GetDesc(),
-             self.mapdisplay.OnZoomBack),
+             self.parent.OnZoomBack),
             (self.zoomtomap, "zoomtomap", Icons["zoommenu"].GetBitmap(),
              wx.ITEM_NORMAL, _("Zoom to map"), _("Zoom to displayed map"),
              self.OnZoomMap),
             )
-
+    
     def OnZoomMap(self, event):
-        """Zoom to selected map"""
-        layer = self.mapcontent.GetListOfLayers()
-        self.mapdisplay.MapWindow.ZoomToMap(layer=layer)
-        
+        """!Zoom to selected map"""
+        self.parent.MapWindow.ZoomToMap(layers = self.mapcontent.GetListOfLayers())
         if event:
             event.Skip()
         
 class GCPToolbar(AbstractToolbar):
-    """
+    """!
     Toolbar for managing ground control points during georectification
+
+    @param parent reference to GCP widget
     """
-    def __init__(self, parent, tbframe):
-        self.parent  = parent # GCP
-        self.tbframe = tbframe
-
-        self.toolbar = wx.ToolBar(parent=self.tbframe, id=wx.ID_ANY)
-
-        # self.SetToolBar(self.toolbar)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.tbframe, self.toolbar, self.ToolbarData())
-
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
         # realize the toolbar
-        self.toolbar.Realize()
+        self.Realize()
 
     def ToolbarData(self):
-        
         self.gcpSave = wx.NewId()
         self.gcpAdd = wx.NewId()
         self.gcpDelete = wx.NewId()
@@ -627,38 +632,26 @@
     """
     Toolbar for digitization
     """
-
-    def __init__(self, parent, map, layerTree=None, log=None):
-        self.mapcontent    = map       # Map class instance
-        self.parent        = parent    # MapFrame
-        self.layerTree     = layerTree # reference to layer tree associated to map display
-        self.log           = log       # log area
+    def __init__(self, parent, mapcontent, layerTree=None, log=None):
+        self.mapcontent    = mapcontent # Map class instance
+        self.layerTree     = layerTree  # reference to layer tree associated to map display
+        self.log           = log        # log area
+        AbstractToolbar.__init__(self, parent)
         
         # currently selected map layer for editing (reference to MapLayer instance)
         self.mapLayer = None
         # list of vector layers from Layer Manager (only in the current mapset)
         self.layers   = [] 
-
+        
         self.comboid    = None
-
+        
         # only one dialog can be open
         self.settingsDialog   = None
-
+        
         # create toolbars (two rows optionally)
-        self.toolbar = []
-        self.numOfRows = 1 # number of rows for toolbar
-        for row in range(0, self.numOfRows):
-            self.toolbar.append(wx.ToolBar(parent=self.parent, id=wx.ID_ANY))
-            self.toolbar[row].SetToolBitmapSize(globalvar.toolbarSize)
-            self.toolbar[row].Bind(wx.EVT_TOOL, self.OnTool)
-            
-            # create toolbar
-            if self.numOfRows ==  1:
-                rowdata=None
-            else:
-                rowdata = row
-            self.InitToolbar(self.parent, self.toolbar[row], self.ToolbarData(rowdata))
-
+        self.InitToolbar(self.ToolbarData())
+        self.Bind(wx.EVT_TOOL, self.OnTool)
+        
         # default action (digitize new point, line, etc.)
         self.action = { 'desc' : 'addLine',
                         'type' : 'point',
@@ -666,141 +659,135 @@
         
         # list of available vector maps
         self.UpdateListOfLayers(updateTool=True)
-
+        
         # realize toolbar
-        for row in range(0, self.numOfRows):
-            self.toolbar[row].Realize()
-            # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
-            self.combo.Hide()
-            self.combo.Show()
-
-
+        self.Realize()
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        self.combo.Hide()
+        self.combo.Show()
+        
         # disable undo/redo
-        self.toolbar[0].EnableTool(self.undo, False)
+        self.EnableTool(self.undo, False)
         
         # toogle to pointer by default
         self.OnTool(None)
         
         self.FixSize(width = 105)
         
-    def ToolbarData(self, row=None):
-        """
+    def ToolbarData(self):
+        """!
         Toolbar data
         """
         data = []
-        if row is None or row == 0:
-            self.addPoint = wx.NewId()
-            self.addLine = wx.NewId()
-            self.addBoundary = wx.NewId()
-            self.addCentroid = wx.NewId()
-            self.moveVertex = wx.NewId()
-            self.addVertex = wx.NewId()
-            self.removeVertex = wx.NewId()
-            self.splitLine = wx.NewId()
-            self.editLine = wx.NewId()
-            self.moveLine = wx.NewId()
-            self.deleteLine = wx.NewId()
-            self.additionalTools = wx.NewId()
-            self.displayCats = wx.NewId()
-            self.displayAttr = wx.NewId()
-            self.copyCats = wx.NewId()
-
-            data = [("", "", "", "", "", "", ""),
-                    (self.addPoint, "digAddPoint", Icons["digAddPoint"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digAddPoint"].GetLabel(), Icons["digAddPoint"].GetDesc(),
-                     self.OnAddPoint),
-                    (self.addLine, "digAddLine", Icons["digAddLine"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digAddLine"].GetLabel(), Icons["digAddLine"].GetDesc(),
-                     self.OnAddLine),
-                    (self.addBoundary, "digAddBoundary", Icons["digAddBoundary"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digAddBoundary"].GetLabel(), Icons["digAddBoundary"].GetDesc(),
-                     self.OnAddBoundary),
-                    (self.addCentroid, "digAddCentroid", Icons["digAddCentroid"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digAddCentroid"].GetLabel(), Icons["digAddCentroid"].GetDesc(),
-                     self.OnAddCentroid),
-                    (self.moveVertex, "digMoveVertex", Icons["digMoveVertex"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digMoveVertex"].GetLabel(), Icons["digMoveVertex"].GetDesc(),
-                     self.OnMoveVertex),
-                    (self.addVertex, "digAddVertex", Icons["digAddVertex"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digAddVertex"].GetLabel(), Icons["digAddVertex"].GetDesc(),
-                     self.OnAddVertex),
-                    (self.removeVertex, "digRemoveVertex", Icons["digRemoveVertex"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digRemoveVertex"].GetLabel(), Icons["digRemoveVertex"].GetDesc(),
-                     self.OnRemoveVertex),
-                    (self.splitLine, "digSplitLine", Icons["digSplitLine"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digSplitLine"].GetLabel(), Icons["digSplitLine"].GetDesc(),
-                     self.OnSplitLine),
-                    (self.editLine, "digEditLine", Icons["digEditLine"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digEditLine"].GetLabel(), Icons["digEditLine"].GetDesc(),
-                     self.OnEditLine),
-                    (self.moveLine, "digMoveLine", Icons["digMoveLine"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digMoveLine"].GetLabel(), Icons["digMoveLine"].GetDesc(),
-                     self.OnMoveLine),
-                    (self.deleteLine, "digDeleteLine", Icons["digDeleteLine"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digDeleteLine"].GetLabel(), Icons["digDeleteLine"].GetDesc(),
-                     self.OnDeleteLine),
-                    (self.displayCats, "digDispCats", Icons["digDispCats"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digDispCats"].GetLabel(), Icons["digDispCats"].GetDesc(),
-                     self.OnDisplayCats),
-                    (self.copyCats, "digCopyCats", Icons["digCopyCats"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digCopyCats"].GetLabel(), Icons["digCopyCats"].GetDesc(),
-                     self.OnCopyCA),
-                    (self.displayAttr, "digDispAttr", Icons["digDispAttr"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digDispAttr"].GetLabel(), Icons["digDispAttr"].GetDesc(),
-                     self.OnDisplayAttr),
-                    (self.additionalTools, "digAdditionalTools", Icons["digAdditionalTools"].GetBitmap(),
-                     wx.ITEM_CHECK, Icons["digAdditionalTools"].GetLabel(),
-                     Icons["digAdditionalTools"].GetDesc(),
-                     self.OnAdditionalToolMenu)]
-
-        if row is None or row == 1:
-            self.undo = wx.NewId()
-            self.settings = wx.NewId()
-            self.exit = wx.NewId()
-
-            data.append(("", "", "", "", "", "", ""))
-            data.append((self.undo, "digUndo", Icons["digUndo"].GetBitmap(),
-                         wx.ITEM_NORMAL, Icons["digUndo"].GetLabel(), Icons["digUndo"].GetDesc(),
-                         self.OnUndo))
-            # data.append((self.undo, "digRedo", Icons["digRedo"].GetBitmap(),
-            #             wx.ITEM_NORMAL, Icons["digRedo"].GetLabel(), Icons["digRedo"].GetDesc(),
-            #             self.OnRedo))
-            data.append((self.settings, "digSettings", Icons["digSettings"].GetBitmap(),
-                         wx.ITEM_NORMAL, Icons["digSettings"].GetLabel(), Icons["digSettings"].GetDesc(),
-                         self.OnSettings))
-            data.append((self.exit, "digExit", Icons["quit"].GetBitmap(),
-                         wx.ITEM_NORMAL, Icons["digExit"].GetLabel(), Icons["digExit"].GetDesc(),
-                         self.OnExit))
-
+        
+        self.addPoint = wx.NewId()
+        self.addLine = wx.NewId()
+        self.addBoundary = wx.NewId()
+        self.addCentroid = wx.NewId()
+        self.moveVertex = wx.NewId()
+        self.addVertex = wx.NewId()
+        self.removeVertex = wx.NewId()
+        self.splitLine = wx.NewId()
+        self.editLine = wx.NewId()
+        self.moveLine = wx.NewId()
+        self.deleteLine = wx.NewId()
+        self.additionalTools = wx.NewId()
+        self.displayCats = wx.NewId()
+        self.displayAttr = wx.NewId()
+        self.copyCats = wx.NewId()
+        self.undo = wx.NewId()
+        self.settings = wx.NewId()
+        self.exit = wx.NewId()
+        
+        data = [("", "", "", "", "", "", ""),
+                (self.addPoint, "digAddPoint", Icons["digAddPoint"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digAddPoint"].GetLabel(), Icons["digAddPoint"].GetDesc(),
+                 self.OnAddPoint),
+                (self.addLine, "digAddLine", Icons["digAddLine"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digAddLine"].GetLabel(), Icons["digAddLine"].GetDesc(),
+                 self.OnAddLine),
+                (self.addBoundary, "digAddBoundary", Icons["digAddBoundary"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digAddBoundary"].GetLabel(), Icons["digAddBoundary"].GetDesc(),
+                 self.OnAddBoundary),
+                (self.addCentroid, "digAddCentroid", Icons["digAddCentroid"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digAddCentroid"].GetLabel(), Icons["digAddCentroid"].GetDesc(),
+                 self.OnAddCentroid),
+                (self.moveVertex, "digMoveVertex", Icons["digMoveVertex"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digMoveVertex"].GetLabel(), Icons["digMoveVertex"].GetDesc(),
+                 self.OnMoveVertex),
+                (self.addVertex, "digAddVertex", Icons["digAddVertex"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digAddVertex"].GetLabel(), Icons["digAddVertex"].GetDesc(),
+                 self.OnAddVertex),
+                (self.removeVertex, "digRemoveVertex", Icons["digRemoveVertex"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digRemoveVertex"].GetLabel(), Icons["digRemoveVertex"].GetDesc(),
+                 self.OnRemoveVertex),
+                (self.splitLine, "digSplitLine", Icons["digSplitLine"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digSplitLine"].GetLabel(), Icons["digSplitLine"].GetDesc(),
+                 self.OnSplitLine),
+                (self.editLine, "digEditLine", Icons["digEditLine"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digEditLine"].GetLabel(), Icons["digEditLine"].GetDesc(),
+                 self.OnEditLine),
+                (self.moveLine, "digMoveLine", Icons["digMoveLine"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digMoveLine"].GetLabel(), Icons["digMoveLine"].GetDesc(),
+                 self.OnMoveLine),
+                (self.deleteLine, "digDeleteLine", Icons["digDeleteLine"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digDeleteLine"].GetLabel(), Icons["digDeleteLine"].GetDesc(),
+                 self.OnDeleteLine),
+                (self.displayCats, "digDispCats", Icons["digDispCats"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digDispCats"].GetLabel(), Icons["digDispCats"].GetDesc(),
+                 self.OnDisplayCats),
+                (self.copyCats, "digCopyCats", Icons["digCopyCats"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digCopyCats"].GetLabel(), Icons["digCopyCats"].GetDesc(),
+                 self.OnCopyCA),
+                (self.displayAttr, "digDispAttr", Icons["digDispAttr"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digDispAttr"].GetLabel(), Icons["digDispAttr"].GetDesc(),
+                 self.OnDisplayAttr),
+                (self.additionalTools, "digAdditionalTools", Icons["digAdditionalTools"].GetBitmap(),
+                 wx.ITEM_CHECK, Icons["digAdditionalTools"].GetLabel(),
+                 Icons["digAdditionalTools"].GetDesc(),
+                 self.OnAdditionalToolMenu),
+                ("", "", "", "", "", "", ""),
+                (self.undo, "digUndo", Icons["digUndo"].GetBitmap(),
+                 wx.ITEM_NORMAL, Icons["digUndo"].GetLabel(), Icons["digUndo"].GetDesc(),
+                 self.OnUndo),
+                # data.append((self.undo, "digRedo", Icons["digRedo"].GetBitmap(),
+                #             wx.ITEM_NORMAL, Icons["digRedo"].GetLabel(), Icons["digRedo"].GetDesc(),
+                #             self.OnRedo))
+                (self.settings, "digSettings", Icons["digSettings"].GetBitmap(),
+                 wx.ITEM_NORMAL, Icons["digSettings"].GetLabel(), Icons["digSettings"].GetDesc(),
+                 self.OnSettings),
+                (self.exit, "digExit", Icons["quit"].GetBitmap(),
+                 wx.ITEM_NORMAL, Icons["digExit"].GetLabel(), Icons["digExit"].GetDesc(),
+                 self.OnExit)]
+        
         return data
-
+    
     def OnTool(self, event):
-        """Tool selected -> disable selected tool in map toolbar"""
-        # update map toolbar (unselect currently selected tool)
+        """!Tool selected -> disable selected tool in map toolbar"""
         id = self.parent.toolbars['map'].GetAction(type='id')
-        self.parent.toolbars['map'].toolbar.ToggleTool(id, False)
-
+        self.parent.toolbars['map'].ToggleTool(id, False)
+        
         # set cursor
         cursor = self.parent.cursors["cross"]
         self.parent.MapWindow.SetCursor(cursor)
-
+        
         # pointer
         self.parent.OnPointer(None)
-
+        
         if event:
             # deselect previously selected tool
             id = self.action.get('id', -1)
             if id != event.GetId():
-                self.toolbar[0].ToggleTool(self.action['id'], False)
+                self.ToggleTool(self.action['id'], False)
             else:
-                self.toolbar[0].ToggleTool(self.action['id'], True)
+                self.ToggleTool(self.action['id'], True)
             
             self.action['id'] = event.GetId()
+            
             event.Skip()
-        else:
-            # initialize toolbar
-            self.toolbar[0].ToggleTool(self.action['id'], True)
-
+        
+        self.ToggleTool(self.action['id'], True)
+        
         # clear tmp canvas
         if self.action['id'] != id:
             self.parent.MapWindow.ClearLines(pdc=self.parent.MapWindow.pdcTmp)
@@ -809,25 +796,28 @@
                 # cancel action
                 self.parent.MapWindow.OnMiddleDown(None)
         
+        # set focus
+        self.parent.MapWindow.SetFocus()
+        
     def OnAddPoint(self, event):
-        """Add point to the vector map Laier"""
+        """!Add point to the vector map Laier"""
         Debug.msg (2, "VDigitToolbar.OnAddPoint()")
         self.action = { 'desc' : "addLine",
                         'type' : "point",
                         'id'   : self.addPoint }
         self.parent.MapWindow.mouse['box'] = 'point'
-
+        
     def OnAddLine(self, event):
-        """Add line to the vector map layer"""
+        """!Add line to the vector map layer"""
         Debug.msg (2, "VDigitToolbar.OnAddLine()")
         self.action = { 'desc' : "addLine",
                         'type' : "line",
                         'id'   : self.addLine }
         self.parent.MapWindow.mouse['box'] = 'line'
         ### self.parent.MapWindow.polycoords = [] # reset temp line
-
+                
     def OnAddBoundary(self, event):
-        """Add boundary to the vector map layer"""
+        """!Add boundary to the vector map layer"""
         Debug.msg (2, "VDigitToolbar.OnAddBoundary()")
         if self.action['desc'] != 'addLine' or \
                 self.action['type'] != 'boundary':
@@ -836,9 +826,9 @@
                         'type' : "boundary",
                         'id'   : self.addBoundary }
         self.parent.MapWindow.mouse['box'] = 'line'
-
+        
     def OnAddCentroid(self, event):
-        """Add centroid to the vector map layer"""
+        """!Add centroid to the vector map layer"""
         Debug.msg (2, "VDigitToolbar.OnAddCentroid()")
         self.action = { 'desc' : "addLine",
                         'type' : "centroid",
@@ -846,90 +836,88 @@
         self.parent.MapWindow.mouse['box'] = 'point'
 
     def OnExit (self, event=None):
-        """Quit digitization tool"""
+        """!Quit digitization tool"""
         # stop editing of the currently selected map layer
         if self.mapLayer:
             self.StopEditing()
-
+        
         # close dialogs if still open
         if self.settingsDialog:
             self.settingsDialog.OnCancel(None)
-        
-        # disable the toolbar
-        self.parent.RemoveToolbar ("vdigit")
-
+            
         # set default mouse settings
         self.parent.MapWindow.mouse['use'] = "pointer"
         self.parent.MapWindow.mouse['box'] = "point"
         self.parent.MapWindow.polycoords = []
         
+        # disable the toolbar
+        self.parent.RemoveToolbar("vdigit")
+                
     def OnMoveVertex(self, event):
-        """Move line vertex"""
+        """!Move line vertex"""
         Debug.msg(2, "Digittoolbar.OnMoveVertex():")
         self.action = { 'desc' : "moveVertex",
                         'id'   : self.moveVertex }
         self.parent.MapWindow.mouse['box'] = 'point'
 
     def OnAddVertex(self, event):
-        """Add line vertex"""
+        """!Add line vertex"""
         Debug.msg(2, "Digittoolbar.OnAddVertex():")
         self.action = { 'desc' : "addVertex",
                         'id'   : self.addVertex }
         self.parent.MapWindow.mouse['box'] = 'point'
-
-
+        
     def OnRemoveVertex(self, event):
-        """Remove line vertex"""
+        """!Remove line vertex"""
         Debug.msg(2, "Digittoolbar.OnRemoveVertex():")
         self.action = { 'desc' : "removeVertex",
                         'id'   : self.removeVertex }
         self.parent.MapWindow.mouse['box'] = 'point'
 
-
     def OnSplitLine(self, event):
-        """Split line"""
+        """!Split line"""
         Debug.msg(2, "Digittoolbar.OnSplitLine():")
         self.action = { 'desc' : "splitLine",
                         'id'   : self.splitLine }
         self.parent.MapWindow.mouse['box'] = 'point'
 
     def OnEditLine(self, event):
-        """Edit line"""
+        """!Edit line"""
         Debug.msg(2, "Digittoolbar.OnEditLine():")
         self.action = { 'desc' : "editLine",
                         'id'   : self.editLine }
         self.parent.MapWindow.mouse['box'] = 'line'
 
     def OnMoveLine(self, event):
-        """Move line"""
+        """!Move line"""
         Debug.msg(2, "Digittoolbar.OnMoveLine():")
         self.action = { 'desc' : "moveLine",
                         'id'   : self.moveLine }
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnDeleteLine(self, event):
-        """Delete line"""
+        """!Delete line"""
         Debug.msg(2, "Digittoolbar.OnDeleteLine():")
         self.action = { 'desc' : "deleteLine",
                         'id'   : self.deleteLine }
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnDisplayCats(self, event):
-        """Display/update categories"""
+        """!Display/update categories"""
         Debug.msg(2, "Digittoolbar.OnDisplayCats():")
         self.action = { 'desc' : "displayCats",
                         'id'   : self.displayCats }
         self.parent.MapWindow.mouse['box'] = 'point'
 
     def OnDisplayAttr(self, event):
-        """Display/update attributes"""
+        """!Display/update attributes"""
         Debug.msg(2, "Digittoolbar.OnDisplayAttr():")
         self.action = { 'desc' : "displayAttrs",
                         'id'   : self.displayAttr }
         self.parent.MapWindow.mouse['box'] = 'point'
 
     def OnCopyCA(self, event):
-        """Copy categories/attributes menu"""
+        """!Copy categories/attributes menu"""
         point = wx.GetMousePosition()
         toolMenu = wx.Menu()
         # Add items to the menu
@@ -940,7 +928,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnCopyCats, cats)
         if self.action['desc'] == "copyCats":
             cats.Check(True)
-
+        
         attrb = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                             text=_('Duplicate attributes'),
                             kind=wx.ITEM_CHECK)
@@ -948,20 +936,20 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnCopyAttrb, attrb)
         if self.action['desc'] == "copyAttrs":
             attrb.Check(True)
-
+        
         # Popup the menu.  If an item is selected then its handler
         # will be called before PopupMenu returns.
         self.parent.MapWindow.PopupMenu(toolMenu)
         toolMenu.Destroy()
         
         if self.action['desc'] == "addPoint":
-            self.toolbar[0].ToggleTool(self.copyCats, False)
+            self.ToggleTool(self.copyCats, False)
         
     def OnCopyCats(self, event):
-        """Copy categories"""
+        """!Copy categories"""
         if self.action['desc'] == 'copyCats': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.copyCats, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.copyCats, False)
             self.OnAddPoint(event)
             return
         
@@ -972,8 +960,8 @@
 
     def OnCopyAttrb(self, event):
         if self.action['desc'] == 'copyAttrs': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.copyCats, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.copyCats, False)
             self.OnAddPoint(event)
             return
         
@@ -983,38 +971,40 @@
         self.parent.MapWindow.mouse['box'] = 'point'
         
     def OnUndo(self, event):
-        """Undo previous changes"""
+        """!Undo previous changes"""
         self.parent.digit.Undo()
-
+        
         event.Skip()
 
     def EnableUndo(self, enable=True):
-        """Enable 'Undo' in toolbar
-
+        """!Enable 'Undo' in toolbar
+        
         @param enable False for disable
         """
         if enable:
-            if self.toolbar[0].GetToolEnabled(self.undo) is False:
-                self.toolbar[0].EnableTool(self.undo, True)
+            if self.GetToolEnabled(self.undo) is False:
+                self.EnableTool(self.undo, True)
         else:
-            if self.toolbar[0].GetToolEnabled(self.undo) is True:
-                self.toolbar[0].EnableTool(self.undo, False)
+            if self.GetToolEnabled(self.undo) is True:
+                self.EnableTool(self.undo, False)
         
     def OnSettings(self, event):
-        """Show settings dialog"""
-
+        """!Show settings dialog"""
         if self.parent.digit is None:
             reload(vdigit)
-            from vdigit import Digit as Digit
-            self.parent.digit = Digit(mapwindow=self.parent.MapWindow)
-            
+            from vdigit import VDigit as VDigit
+            try:
+                self.parent.digit = VDigit(mapwindow=self.parent.MapWindow)
+            except SystemExit:
+                self.parent.digit = None
+        
         if not self.settingsDialog:
             self.settingsDialog = VDigitSettingsDialog(parent=self.parent, title=_("Digitization settings"),
                                                        style=wx.DEFAULT_DIALOG_STYLE)
             self.settingsDialog.Show()
 
     def OnAdditionalToolMenu(self, event):
-        """Menu for additional tools"""
+        """!Menu for additional tools"""
         point = wx.GetMousePosition()
         toolMenu = wx.Menu()
         # Add items to the menu
@@ -1025,7 +1015,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnCopy, copy)
         if self.action['desc'] == "copyLine":
             copy.Check(True)
-
+        
         flip = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                            text=_('Flip selected lines/boundaries'),
                            kind=wx.ITEM_CHECK)
@@ -1033,7 +1023,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnFlip, flip)
         if self.action['desc'] == "flipLine":
             flip.Check(True)
-
+        
         merge = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                             text=_('Merge selected lines/boundaries'),
                             kind=wx.ITEM_CHECK)
@@ -1041,7 +1031,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnMerge, merge)
         if self.action['desc'] == "mergeLine":
             merge.Check(True)
-
+        
         breakL = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                              text=_('Break selected lines/boundaries at intersection'),
                              kind=wx.ITEM_CHECK)
@@ -1049,7 +1039,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnBreak, breakL)
         if self.action['desc'] == "breakLine":
             breakL.Check(True)
-
+        
         snap = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                            text=_('Snap selected lines/boundaries (only to nodes)'),
                            kind=wx.ITEM_CHECK)
@@ -1057,7 +1047,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnSnap, snap)
         if self.action['desc'] == "snapLine":
             snap.Check(True)
-
+        
         connect = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                               text=_('Connect selected lines/boundaries'),
                               kind=wx.ITEM_CHECK)
@@ -1065,7 +1055,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnConnect, connect)
         if self.action['desc'] == "connectLine":
             connect.Check(True)
-
+        
         query = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                             text=_('Query features'),
                             kind=wx.ITEM_CHECK)
@@ -1073,7 +1063,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnQuery, query)
         if self.action['desc'] == "queryLine":
             query.Check(True)
-
+        
         zbulk = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                             text=_('Z bulk-labeling of 3D lines'),
                             kind=wx.ITEM_CHECK)
@@ -1081,7 +1071,7 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnZBulk, zbulk)
         if self.action['desc'] == "zbulkLine":
             zbulk.Check(True)
-
+        
         typeconv = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY,
                                text=_('Feature type conversion'),
                                kind=wx.ITEM_CHECK)
@@ -1089,20 +1079,20 @@
         self.parent.MapWindow.Bind(wx.EVT_MENU, self.OnTypeConversion, typeconv)
         if self.action['desc'] == "typeConv":
             typeconv.Check(True)
-
+        
         # Popup the menu.  If an item is selected then its handler
         # will be called before PopupMenu returns.
         self.parent.MapWindow.PopupMenu(toolMenu)
         toolMenu.Destroy()
         
         if self.action['desc'] == 'addPoint':
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.additionalTools, False)
         
     def OnCopy(self, event):
-        """Copy selected features from (background) vector map"""
+        """!Copy selected features from (background) vector map"""
         if self.action['desc'] == 'copyLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1112,10 +1102,10 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnFlip(self, event):
-        """Flip selected lines/boundaries"""
+        """!Flip selected lines/boundaries"""
         if self.action['desc'] == 'flipLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1125,10 +1115,10 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnMerge(self, event):
-        """Merge selected lines/boundaries"""
+        """!Merge selected lines/boundaries"""
         if self.action['desc'] == 'mergeLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1138,10 +1128,10 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnBreak(self, event):
-        """Break selected lines/boundaries"""
+        """!Break selected lines/boundaries"""
         if self.action['desc'] == 'breakLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1151,10 +1141,10 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnSnap(self, event):
-        """Snap selected features"""
+        """!Snap selected features"""
         if self.action['desc'] == 'snapLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1164,10 +1154,10 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnConnect(self, event):
-        """Connect selected lines/boundaries"""
+        """!Connect selected lines/boundaries"""
         if self.action['desc'] == 'connectLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1177,10 +1167,10 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnQuery(self, event):
-        """Query selected lines/boundaries"""
+        """!Query selected lines/boundaries"""
         if self.action['desc'] == 'queryLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1191,7 +1181,7 @@
         self.parent.MapWindow.mouse['box'] = 'box'
 
     def OnZBulk(self, event):
-        """Z bulk-labeling selected lines/boundaries"""
+        """!Z bulk-labeling selected lines/boundaries"""
         if not self.parent.digit.driver.Is3D():
             wx.MessageBox(parent=self.parent,
                           message=_("Vector map is not 3D. Operation canceled."),
@@ -1199,8 +1189,8 @@
             return
         
         if self.action['desc'] == 'zbulkLine': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1210,15 +1200,15 @@
         self.parent.MapWindow.mouse['box'] = 'line'
 
     def OnTypeConversion(self, event):
-        """Feature type conversion
+        """!Feature type conversion
 
         Supported conversions:
          - point <-> centroid
          - line <-> boundary
         """
         if self.action['desc'] == 'typeConv': # select previous action
-            self.toolbar[0].ToggleTool(self.addPoint, True)
-            self.toolbar[0].ToggleTool(self.additionalTools, False)
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
             self.OnAddPoint(event)
             return
         
@@ -1241,8 +1231,10 @@
             else:
                 openVectorMap = None
             mapName = gdialogs.CreateNewVector(self.parent,
-                                               exceptMap=openVectorMap, log=self.log,
-                                               cmdDef=(['v.edit', 'tool=create'], "map"),
+                                               exceptMap = openVectorMap, log = self.log,
+                                               cmd = (('v.edit',
+                                                       { 'tool' : 'create' },
+                                                       'map')),
                                                disableAdd=True)[0]
             if mapName:
                 # add layer to map layer tree
@@ -1262,60 +1254,66 @@
                 return 
         else:
             selection = event.GetSelection() - 1 # first option is 'New vector map'
-
+        
         # skip currently selected map
         if self.layers[selection] == self.mapLayer:
             return False
-
+        
         if self.mapLayer:
             # deactive map layer for editing
             self.StopEditing()
-
+        
         # select the given map layer for editing
         self.StartEditing(self.layers[selection])
-
+        
         event.Skip()
 
         return True
     
     def StartEditing (self, mapLayer):
-        """
-        Start editing selected vector map layer.
-
+        """!Start editing selected vector map layer.
+        
         @param mapLayer reference to MapLayer instance
         """
         # deactive layer
         self.mapcontent.ChangeLayerActive(mapLayer, False)
-
-        # clean map canvas
-        ### self.parent.MapWindow.EraseMap()
-
+        
         # unset background map if needed
-        if UserSettings.Get(group='vdigit', key='bgmap',
-                            subkey='value', internal=True) == mapLayer.GetName():
-            UserSettings.Set(group='vdigit', key='bgmap',
-                             subkey='value', value='', internal=True)
+        if mapLayer:
+            if UserSettings.Get(group='vdigit', key='bgmap',
+                                subkey='value', internal=True) == mapLayer.GetName():
+                UserSettings.Set(group='vdigit', key='bgmap',
+                                 subkey='value', value='', internal=True)
+            
+            self.parent.statusbar.SetStatusText(_("Please wait, "
+                                                  "opening vector map <%s> for editing...") % \
+                                                    mapLayer.GetName(),
+                                                0)
         
-        self.parent.statusbar.SetStatusText(_("Please wait, "
-                                              "opening vector map <%s> for editing...") % \
-                                                mapLayer.GetName(),
-                                            0)
-        
         # reload vdigit module
         reload(vdigit)
-        from vdigit import Digit as Digit
-        self.parent.digit = Digit(mapwindow=self.parent.MapWindow)
+        from vdigit import VDigit as VDigit
+        # use vdigit's PseudoDC
+        self.parent.MapWindow.DefinePseudoDC(vdigit = True)
+        self.parent.digit = VDigit(mapwindow=self.parent.MapWindow)
         
         self.mapLayer = mapLayer
         
         # open vector map
         try:
+            if not self.parent.MapWindow.CheckPseudoDC():
+                raise gcmd.GException(_("Unable to initialize display driver of vector "
+                                        "digitizer. See 'Command output' for details."))
             self.parent.digit.SetMapName(mapLayer.GetName())
-        except gcmd.DigitError, e:
+        except gcmd.GException, e:
             self.mapLayer = None
-            print >> sys.stderr, e # wxMessageBox
+            self.StopEditing()
+            GError(parent = self.parent,
+                   message = e)
             return False
         
+        self.parent.digit.SetMapName(mapLayer.GetName())
+        
         # update toolbar
         self.combo.SetValue(mapLayer.GetName())
         self.parent.toolbars['map'].combo.SetValue (_('Digitize'))
@@ -1329,10 +1327,10 @@
         # create pseudoDC for drawing the map
         self.parent.MapWindow.pdcVector = vdigit.PseudoDC()
         self.parent.digit.driver.SetDevice(self.parent.MapWindow.pdcVector)
-
+        
         if not self.parent.MapWindow.resize:
             self.parent.MapWindow.UpdateMap(render=True)
-
+        
         opacity = mapLayer.GetOpacity(float=True)
         if opacity < 1.0:
             alpha = int(opacity * 255)
@@ -1340,50 +1338,51 @@
         
         return True
 
-    def StopEditing (self):
-        """Stop editing of selected vector map layer.
+    def StopEditing(self):
+        """!Stop editing of selected vector map layer.
 
         @return True on success
         @return False on failure
         """
-        if not self.mapLayer:
-            return False
+        # use wx's PseudoDC
+        self.parent.MapWindow.DefinePseudoDC(vdigit = False)
         
-        Debug.msg (4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName())
         self.combo.SetValue (_('Select vector map'))
         
         # save changes
-        if UserSettings.Get(group='vdigit', key='saveOnExit', subkey='enabled') is False:
-            if self.parent.digit.GetUndoLevel() > -1:
-                dlg = wx.MessageDialog(parent=self.parent,
-                                       message=_("Do you want to save changes "
-                                                 "in vector map <%s>?") % self.mapLayer.GetName(),
-                                       caption=_("Save changes?"),
-                                       style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-                if dlg.ShowModal() == wx.ID_NO:
-                    # revert changes
-                    self.parent.digit.Undo(0)
-                dlg.Destroy()
+        if self.mapLayer:
+            Debug.msg (4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName())
+            if UserSettings.Get(group='vdigit', key='saveOnExit', subkey='enabled') is False:
+                if self.parent.digit.GetUndoLevel() > -1:
+                    dlg = wx.MessageDialog(parent=self.parent,
+                                           message=_("Do you want to save changes "
+                                                     "in vector map <%s>?") % self.mapLayer.GetName(),
+                                           caption=_("Save changes?"),
+                                           style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+                    if dlg.ShowModal() == wx.ID_NO:
+                        # revert changes
+                        self.parent.digit.Undo(0)
+                    dlg.Destroy()
+            
+            self.parent.statusbar.SetStatusText(_("Please wait, "
+                                                  "closing and rebuilding topology of "
+                                                  "vector map <%s>...") % self.mapLayer.GetName(),
+                                                0)
         
-        self.parent.statusbar.SetStatusText(_("Please wait, "
-                                              "closing and rebuilding topology of "
-                                              "vector map <%s>...") % self.mapLayer.GetName(),
-                                            0)
+            self.parent.digit.SetMapName(None) # -> close map
         
-        self.parent.digit.SetMapName(None) # -> close map
+            # re-active layer 
+            item = self.parent.tree.FindItemByData('maplayer', self.mapLayer)
+            if item and self.parent.tree.IsItemChecked(item):
+                self.mapcontent.ChangeLayerActive(self.mapLayer, True)
         
-        # re-active layer 
-        item = self.parent.tree.FindItemByData('maplayer', self.mapLayer)
-        if item and self.parent.tree.IsItemChecked(item):
-            self.mapcontent.ChangeLayerActive(self.mapLayer, True)
-        
         # change cursor
         self.parent.MapWindow.SetCursor(self.parent.cursors["default"])
         
         # disable pseudodc for vector map layer
         self.parent.MapWindow.pdcVector = None
         self.parent.digit.driver.SetDevice(None)
-
+        
         # close dialogs
         for dialog in ('attributes', 'category'):
             if self.parent.dialogs[dialog]:
@@ -1400,29 +1399,28 @@
         return True
     
     def UpdateListOfLayers (self, updateTool=False):
-        """
+        """!
         Update list of available vector map layers.
         This list consists only editable layers (in the current mapset)
 
         Optionally also update toolbar
         """
-
         Debug.msg (4, "VDigitToolbar.UpdateListOfLayers(): updateTool=%d" % \
                    updateTool)
-
+        
         layerNameSelected = None
          # name of currently selected layer
         if self.mapLayer:
             layerNameSelected = self.mapLayer.GetName()
-
+        
         # select vector map layer in the current mapset
         layerNameList = []
         self.layers = self.mapcontent.GetListOfLayers(l_type="vector",
-                                                      l_mapset=grassenv.GetGRASSVariable('MAPSET'))
+                                                      l_mapset=grass.gisenv()['MAPSET'])
         for layer in self.layers:
             if not layer.name in layerNameList: # do not duplicate layer
                 layerNameList.append (layer.GetName())
-
+        
         if updateTool: # update toolbar
             if not self.mapLayer:
                 value = _('Select vector map')
@@ -1430,43 +1428,35 @@
                 value = layerNameSelected
 
             if not self.comboid:
-                self.combo = wx.ComboBox(self.toolbar[self.numOfRows-1], id=wx.ID_ANY, value=value,
-                                         choices=[_('New vector map'), ] + layerNameList, size=(85, -1),
+                self.combo = wx.ComboBox(self, id=wx.ID_ANY, value=value,
+                                         choices=[_('New vector map'), ] + layerNameList, size=(115, -1),
                                          style=wx.CB_READONLY)
-                self.comboid = self.toolbar[self.numOfRows-1].InsertControl(0, self.combo)
+                self.comboid = self.InsertControl(0, self.combo)
                 self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectMap, self.comboid)
             else:
                 self.combo.SetItems([_('New vector map'), ] + layerNameList)
             
-            self.toolbar[self.numOfRows-1].Realize()
-
+            self.Realize()
+        
         return layerNameList
 
     def GetLayer(self):
-        """Get selected layer for editing -- MapLayer instance"""
+        """!Get selected layer for editing -- MapLayer instance"""
         return self.mapLayer
-
+    
 class ProfileToolbar(AbstractToolbar):
-    """
-    Toolbar for profiling raster map
+    """!Toolbar for profiling raster map
     """ 
-    def __init__(self, parent, tbframe):
-        self.parent  = parent # GCP
-        self.tbframe = tbframe
-
-        self.toolbar = wx.ToolBar(parent=self.tbframe, id=wx.ID_ANY)
-
-        # self.SetToolBar(self.toolbar)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.tbframe, self.toolbar, self.ToolbarData())
-
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
         # realize the toolbar
-        self.toolbar.Realize()
-
+        self.Realize()
+        
     def ToolbarData(self):
-        """Toolbar data"""
-
+        """!Toolbar data"""
         self.transect = wx.NewId()
         self.addraster = wx.NewId()
         self.draw = wx.NewId()
@@ -1475,6 +1465,7 @@
         self.zoom = wx.NewId()
         self.unzoom = wx.NewId()
         self.erase = wx.NewId()
+        self.datasave = wx.NewId()
         self.save = wx.NewId()
         self.printer = wx.NewId()
         self.quit = wx.NewId()
@@ -1487,12 +1478,13 @@
             (self.transect, 'transect', Icons["transect"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["transect"].GetLabel(), Icons["transect"].GetDesc(),
              self.parent.OnDrawTransect),
+            ("", "", "", "", "", "", ""),
             (self.draw, 'profiledraw', Icons["profiledraw"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["profiledraw"].GetLabel(), Icons["profiledraw"].GetDesc(),
              self.parent.OnCreateProfile),
-            (self.options, 'options', Icons["profileopt"].GetBitmap(),
-             wx.ITEM_NORMAL, Icons["profileopt"].GetLabel(), Icons["profileopt"].GetDesc(),
-             self.parent.ProfileOptionsMenu),
+            (self.erase, 'erase', Icons["erase"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["erase"].GetLabel(), Icons["erase"].GetDesc(),
+             self.parent.OnErase),
             (self.drag, 'drag', Icons['pan'].GetBitmap(),
              wx.ITEM_NORMAL, Icons["pan"].GetLabel(), Icons["pan"].GetDesc(),
              self.parent.OnDrag),
@@ -1502,74 +1494,349 @@
             (self.unzoom, 'unzoom', Icons['zoom_back'].GetBitmap(),
              wx.ITEM_NORMAL, Icons["zoom_back"].GetLabel(), Icons["zoom_back"].GetDesc(),
              self.parent.OnRedraw),
-            (self.erase, 'erase', Icons["erase"].GetBitmap(),
-             wx.ITEM_NORMAL, Icons["erase"].GetLabel(), Icons["erase"].GetDesc(),
-             self.parent.OnErase),
             ("", "", "", "", "", "", ""),
-            (self.save, 'save', Icons["savefile"].GetBitmap(),
+            (self.datasave, 'save data', Icons["datasave"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["datasave"].GetLabel(), Icons["datasave"].GetDesc(),
+             self.parent.SaveProfileToFile),
+            (self.save, 'save image', Icons["savefile"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["savefile"].GetLabel(), Icons["savefile"].GetDesc(),
              self.parent.SaveToFile),
             (self.printer, 'print', Icons["printmap"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["printmap"].GetLabel(), Icons["printmap"].GetDesc(),
              self.parent.PrintMenu),
+            ("", "", "", "", "", "", ""),
+            (self.options, 'options', Icons["profileopt"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["profileopt"].GetLabel(), Icons["profileopt"].GetDesc(),
+             self.parent.ProfileOptionsMenu),
             (self.quit, 'quit', Icons["quit"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["quit"].GetLabel(), Icons["quit"].GetDesc(),
              self.parent.OnQuit),
             )
     
 class NvizToolbar(AbstractToolbar):
+    """!Nviz toolbar
     """
-    Nviz toolbar
-    """
-    def __init__(self, parent, map):
-        self.parent     = parent
-        self.mapcontent = map
-
-        self.toolbar = wx.ToolBar(parent=self.parent, id=wx.ID_ANY)
-
-        # self.SetToolBar(self.toolbar)
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        self.InitToolbar(self.parent, self.toolbar, self.ToolbarData())
-
+    def __init__(self, parent, mapcontent):
+        self.mapcontent = mapcontent
+        self.lmgr = parent.GetLayerManager()
+        
+        AbstractToolbar.__init__(self, parent)
+        
+        # only one dialog can be open
+        self.settingsDialog   = None
+        
+        self.InitToolbar(self.ToolbarData())
+        
         # realize the toolbar
-        self.toolbar.Realize()
-
+        self.Realize()
+        
     def ToolbarData(self):
-        """Toolbar data"""
-
+        """!Toolbar data"""
+        self.view = wx.NewId()
+        self.surface = wx.NewId()
+        self.vector = wx.NewId()
+        self.volume = wx.NewId()
+        self.light = wx.NewId()
+        self.fringe = wx.NewId()
         self.settings = wx.NewId()
+        self.help = wx.NewId()
         self.quit = wx.NewId()
-                
+        
         # tool, label, bitmap, kind, shortHelp, longHelp, handler
         return   (
+            (self.view, "view", Icons["nvizView"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizView"].GetLabel(), Icons["nvizView"].GetDesc(),
+             self.OnShowPage),
+            ("", "", "", "", "", "", ""),
+            (self.surface, "surface", Icons["nvizSurface"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizSurface"].GetLabel(), Icons["nvizSurface"].GetDesc(),
+             self.OnShowPage),
+            (self.vector, "vector", Icons["nvizVector"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizVector"].GetLabel(), Icons["nvizVector"].GetDesc(),
+             self.OnShowPage),
+            (self.volume, "volume", Icons["nvizVolume"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizVolume"].GetLabel(), Icons["nvizVolume"].GetDesc(),
+             self.OnShowPage),
+            ("", "", "", "", "", "", ""),
+            (self.light, "light", Icons["nvizLight"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizLight"].GetLabel(), Icons["nvizLight"].GetDesc(),
+             self.OnShowPage),
+            (self.fringe, "fringe", Icons["nvizFringe"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizFringe"].GetLabel(), Icons["nvizFringe"].GetDesc(),
+             self.OnShowPage),
+            ("", "", "", "", "", "", ""),
             (self.settings, "settings", Icons["nvizSettings"].GetBitmap(),
              wx.ITEM_NORMAL, Icons["nvizSettings"].GetLabel(), Icons["nvizSettings"].GetDesc(),
              self.OnSettings),
-            (self.quit, 'quit', Icons["quit"].GetBitmap(),
-             wx.ITEM_NORMAL, Icons["quit"].GetLabel(), Icons["quit"].GetDesc(),
+            (self.help, "help", Icons["nvizHelp"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizHelp"].GetLabel(), Icons["nvizHelp"].GetDesc(),
+             self.OnHelp),
+            ("", "", "", "", "", "", ""),
+            (self.quit, 'quit', Icons["nvizQuit"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["nvizQuit"].GetLabel(), Icons["nvizQuit"].GetDesc(),
              self.OnExit),
             )
+    
+    def OnShowPage(self, event):
+        """!Go to the selected page"""
+        if not self.lmgr or not hasattr(self.lmgr, "nviz"):
+            event.Skip()
+            return
+        
+        self.lmgr.notebook.SetSelection(3)
+        eId = event.GetId()
+        if eId == self.view:
+            self.lmgr.nviz.SetPage('view')
+        elif eId == self.surface:
+            self.lmgr.nviz.SetPage('surface')
+        elif eId == self.surface:
+            self.lmgr.nviz.SetPage('surface')
+        elif eId == self.vector:
+            self.lmgr.nviz.SetPage('vector')
+        elif eId == self.volume:
+            self.lmgr.nviz.SetPage('volume')
+        elif eId == self.light:
+            self.lmgr.nviz.SetPage('light')
+        elif eId == self.fringe:
+            self.lmgr.nviz.SetPage('fringe')
+        
+        self.lmgr.Raise()
 
+    def OnHelp(self, event):
+        """!Show 3D view mode help"""
+        if not self.lmgr:
+            gcmd.RunCommand('g.manual',
+                            entry = 'wxGUI.Nviz')
+        else:
+            log = self.lmgr.GetLogWindow()
+            log.RunCmd(['g.manual',
+                        'entry=wxGUI.Nviz'])
+        
     def OnSettings(self, event):
-        win = self.parent.nvizToolWin
-        if not win.IsShown():
-            self.parent.nvizToolWin.Show()
-        else:
-            self.parent.nvizToolWin.Hide()
-
+        """!Show nviz notebook page"""
+        if not self.settingsDialog:
+            self.settingsDialog = NvizPreferencesDialog(parent = self.parent)
+        self.settingsDialog.Show()
+            
     def OnExit (self, event=None):
-        """Quit nviz tool (swith to 2D mode)"""
-
-        # hide dialogs if still open
-        if self.parent.nvizToolWin:
-            self.parent.nvizToolWin.Hide()
-
-        # disable the toolbar
-        self.parent.RemoveToolbar ("nviz")
-
+        """!Quit nviz tool (swith to 2D mode)"""
         # set default mouse settings
         self.parent.MapWindow.mouse['use'] = "pointer"
         self.parent.MapWindow.mouse['box'] = "point"
         self.parent.MapWindow.polycoords = []
+        
+        # disable the toolbar
+        self.parent.RemoveToolbar("nviz")
+        
+class ModelToolbar(AbstractToolbar):
+    """!Graphical modeler toolbar (see gmodeler.py)
+    """
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def ToolbarData(self):
+        """!Toolbar data"""
+        self.new = wx.NewId()
+        self.open = wx.NewId()
+        self.save = wx.NewId()
+        self.image = wx.NewId()
+        self.python = wx.NewId()
+        self.action = wx.NewId()
+        self.data = wx.NewId()
+        self.relation = wx.NewId()
+        self.run = wx.NewId()
+        self.validate = wx.NewId()
+        self.settings = wx.NewId()
+        self.variables = wx.NewId()
+        self.quit = wx.NewId()
+        self.redraw = wx.NewId()
+        self.help = wx.NewId()
+        
+        # tool, label, bitmap, kind, shortHelp, longHelp, handler
+        return (
+            (self.new, 'new', Icons['modelNew'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelNew'].GetLabel(), Icons['modelNew'].GetDesc(),
+             self.parent.OnModelNew),
+            (self.open, 'open', Icons['modelOpen'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelOpen'].GetLabel(), Icons['modelOpen'].GetDesc(),
+             self.parent.OnModelOpen),
+            (self.save, 'save', Icons['modelSave'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelSave'].GetLabel(), Icons['modelSave'].GetDesc(),
+             self.parent.OnModelSave),
+            (self.image, 'image', Icons['modelToImage'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelToImage'].GetLabel(), Icons['modelToImage'].GetDesc(),
+             self.parent.OnExportImage),
+            (self.python, 'python', Icons['modelToPython'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelToPython'].GetLabel(), Icons['modelToPython'].GetDesc(),
+             self.parent.OnExportPython),
+            ('', '', '', '', '', '', ''),
+            (self.action, 'action', Icons['modelActionAdd'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelActionAdd'].GetLabel(), Icons['modelActionAdd'].GetDesc(),
+             self.parent.OnAddAction),
+            (self.data, 'data', Icons['modelDataAdd'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelDataAdd'].GetLabel(), Icons['modelDataAdd'].GetDesc(),
+             self.parent.OnAddData),
+            (self.relation, 'relation', Icons['modelRelation'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelRelation'].GetLabel(), Icons['modelRelation'].GetDesc(),
+             self.parent.OnDefineRelation),
+            ('', '', '', '', '', '', ''),
+            (self.redraw, 'redraw', Icons['modelRedraw'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelRedraw'].GetLabel(), Icons['modelRedraw'].GetDesc(),
+             self.parent.OnCanvasRefresh),
+            (self.validate, 'validate', Icons['modelValidate'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelValidate'].GetLabel(), Icons['modelValidate'].GetDesc(),
+             self.parent.OnValidateModel),
+            (self.run, 'run', Icons['modelRun'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['modelRun'].GetLabel(), Icons['modelRun'].GetDesc(),
+             self.parent.OnRunModel),
+            ('', '', '', '', '', '', ''),
+            (self.variables, "variables", Icons["modelVariables"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["modelVariables"].GetLabel(), Icons["modelVariables"].GetDesc(),
+             self.parent.OnVariables),
+            (self.settings, "settings", Icons["modelSettings"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["modelSettings"].GetLabel(), Icons["modelSettings"].GetDesc(),
+             self.parent.OnPreferences),
+            (self.help, "help", Icons["modelHelp"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["modelHelp"].GetLabel(), Icons["modelHelp"].GetDesc(),
+             self.parent.OnHelp),
+            ('', '', '', '', '', '', ''),
+            (self.quit, 'quit', Icons['quit'].GetBitmap(),
+             wx.ITEM_NORMAL, Icons['quit'].GetLabel(), Icons['quit'].GetDesc(),
+             self.parent.OnCloseWindow),
+            )
+    
+class HistogramToolbar(AbstractToolbar):
+    """!Histogram toolbar (see histogram.py)
+    """
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def ToolbarData(self):
+        """!Toolbar data"""
+        self.histogram = wx.NewId()
+        self.rendermap = wx.NewId()
+        self.erase = wx.NewId()
+        self.font = wx.NewId()
+        self.save = wx.NewId()
+        self.hprint = wx.NewId()
+        self.quit = wx.NewId()
+        
+        # tool, label, bitmap, kind, shortHelp, longHelp, handler
+        return (
+            (self.histogram, 'histogram', Icons["histogram"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["histogram"].GetLabel(), Icons["histogram"].GetDesc(),
+             self.parent.OnOptions),
+            (self.rendermap, 'rendermap', Icons["displaymap"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["displaymap"].GetLabel(), Icons["displaymap"].GetDesc(),
+             self.parent.OnRender),
+            (self.erase, 'erase', Icons["erase"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["erase"].GetLabel(), Icons["erase"].GetDesc(),
+             self.parent.OnErase),
+            (self.font, 'font', Icons["font"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["font"].GetLabel(), Icons["font"].GetDesc(),
+             self.parent.SetHistFont),
+            ('', '', '', '', '', '', ''),
+            (self.save, 'save', Icons["savefile"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["savefile"].GetLabel(), Icons["savefile"].GetDesc(),
+             self.parent.SaveToFile),
+            (self.hprint, 'print', Icons["printmap"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["printmap"].GetLabel(), Icons["printmap"].GetDesc(),
+             self.parent.PrintMenu),
+            ('', '', '', '', '', '', ''),
+            (self.quit, 'quit', Icons["quit"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["quit"].GetLabel(), Icons["quit"].GetDesc(),
+             self.parent.OnQuit)
+            )
 
+class LayerManagerToolbar(AbstractToolbar):
+    """!Layer Manager toolbar (see wxgui.py)
+    """
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self.ToolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def ToolbarData(self):
+        """!Toolbar data"""
+        self.newdisplay = wx.NewId()
+        self.workspaceLoad = wx.NewId()
+        self.workspaceOpen = wx.NewId()
+        self.workspaceSave = wx.NewId()
+        self.addrast = wx.NewId()
+        self.addrast3d = wx.NewId()
+        self.addshaded = wx.NewId()
+        self.addvect = wx.NewId()
+        self.addthematic = wx.NewId()
+        self.addgrp = wx.NewId()
+        self.addovl = wx.NewId()
+        self.delcmd = wx.NewId()
+        self.attribute = wx.NewId()
+        self.preferences = wx.NewId()
+        self.modeler = wx.NewId() 
+        
+        # tool, label, bitmap, kind, shortHelp, longHelp, handler
+        return (
+            (self.newdisplay, 'newdisplay', Icons["newdisplay"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["newdisplay"].GetLabel(), Icons["newdisplay"].GetDesc(),
+             self.parent.OnNewDisplay),
+            ('', '', '', '', '', '', ''),
+            (self.workspaceLoad, 'workspaceLoad', Icons["workspaceLoad"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["workspaceLoad"].GetLabel(), Icons["workspaceLoad"].GetDesc(),
+             self.parent.OnWorkspace),
+            (self.workspaceOpen, 'workspaceOpen', Icons["workspaceOpen"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["workspaceOpen"].GetLabel(), Icons["workspaceOpen"].GetDesc(),
+             self.parent.OnWorkspaceOpen),
+            (self.workspaceSave, 'workspaceSave', Icons["workspaceSave"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["workspaceSave"].GetLabel(), Icons["workspaceSave"].GetDesc(),
+             self.parent.OnWorkspaceSave),
+            ('', '', '', '', '', '', ''),
+            (self.addrast, 'addrast', Icons["addrast"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["addrast"].GetLabel(), Icons["addrast"].GetDesc(),
+             self.parent.OnAddRaster),
+            (self.addrast3d, 'addrast3d', Icons["addrast3d"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["addrast3d"].GetLabel(), Icons["addrast3d"].GetDesc(),
+             self.parent.OnAddRaster3D),
+            (self.addshaded, 'addshaded', Icons["addshaded"].GetBitmap(),
+             wx.ITEM_NORMAL, _("Add various raster-based map layers"), "",
+             self.parent.OnAddRasterMisc),
+            (self.addvect, 'addvect', Icons["addvect"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["addvect"].GetLabel(), Icons["addvect"].GetDesc(),
+             self.parent.OnAddVector),
+            (self.addthematic, 'addthematic', Icons["addthematic"].GetBitmap(),
+             wx.ITEM_NORMAL, _("Add various vector-based map layers"), "",
+             self.parent.OnAddVectorMisc),
+            (self.addgrp, 'addgrp',  Icons["addgrp"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["addgrp"].GetLabel(), Icons["addgrp"].GetDesc(),
+             self.parent.OnAddGroup),
+            (self.addovl, 'addovl',  Icons["addovl"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["addovl"].GetLabel(), Icons["addovl"].GetDesc(),
+             self.parent.OnAddOverlay),
+            (self.delcmd, 'delcmd',  Icons["delcmd"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["delcmd"].GetLabel(), Icons["delcmd"].GetDesc(),
+             self.parent.OnDeleteLayer),
+            ('', '', '', '', '', '', ''),
+            (self.attribute, 'attrtable', Icons["attrtable"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["attrtable"].GetLabel(), Icons["attrtable"].GetDesc(),
+             self.parent.OnShowAttributeTable),
+            ('', '', '', '', '', '', ''),
+            (self.modeler, 'modeler', Icons["modeler"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["modeler"].GetLabel(), Icons["modeler"].GetDesc(),
+             self.parent.OnGModeler),
+            (self.preferences, 'preferences', Icons["settings"].GetBitmap(),
+             wx.ITEM_NORMAL, Icons["settings"].GetLabel(), Icons["settings"].GetDesc(),
+             self.parent.OnPreferences)
+            )
+    

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/units.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/units.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/units.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,118 @@
+"""!
+ at package units
+
+ at brief Units management
+
+Probably will be replaced by Python SWIG fns in the near future(?)
+
+Usage:
+
+ at code
+from units import Units
+ at endcode
+
+Classes:
+ - BaseUnits
+
+(C) 2009 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>
+"""
+
+class BaseUnits:
+    def __init__(self):
+        self._units = dict()
+        self._units['length'] = { 0 : { 'key' : 'mu', 'label' : _('map units') },
+                             1 : { 'key' : 'me', 'label' : _('meters') },
+                             2 : { 'key' : 'km', 'label' : _('kilometers') },
+                             3 : { 'key' : 'mi', 'label' : _('miles') },
+                             4 : { 'key' : 'ft', 'label' : _('feet') } }
+        
+        self._units['area']   = { 0 : { 'key' : 'mu', 'label' : _('sq map units') },
+                             1 : { 'key' : 'me', 'label' : _('sq meters') },
+                             2 : { 'key' : 'km', 'label' : _('sq kilometers') },
+                             3 : { 'key' : 'ar', 'label' : _('acres') },
+                             4 : { 'key' : 'ht', 'label' : _('hectares') } }
+
+    def GetUnitsList(self, type):
+        """!Get list of units (their labels)
+        
+        @param type units type ('length' or 'area')
+        
+        @return list of units labels
+        """
+        result = list()
+        try:
+            keys = self._units[type].keys()
+            keys.sort()
+            for idx in keys:
+                result.append(self._units[type][idx]['label'])
+        except KeyError:
+            pass
+        
+        return result
+
+    def GetUnitsKey(self, type, index):
+        """!Get units key based on index
+        
+        @param type units type ('length' or 'area')
+        @param index units index
+        """
+        return self._units[type][index]['key']
+
+    def GetUnitsIndex(self, type, key):
+        """!Get units index based on key
+        
+        @param type units type ('length' or 'area')
+        @param key units key, e.g. 'me' for meters
+
+        @return index
+        """
+        for k, u in self._units[type].iteritems():
+            if u['key'] == key:
+                return k
+        return 0
+
+Units = BaseUnits()
+
+def ConvertValue(value, type, units):
+    """!Convert value from map units to given units
+
+    Inspired by vector/v.to.db/units.c
+
+    @param value value to be converted
+    @param type units type ('length', 'area')
+    @param unit  destination units
+    """
+    # get map units
+    # TODO
+    
+    f = 1
+    if type == 'length':
+        if units == 'me':
+            f = 1.0
+        elif units == 'km':
+            f = 1.0e-3
+        elif units == 'mi':
+            f = 6.21371192237334e-4
+        elif units == 'ft':
+            f = 3.28083989501312
+    else: # -> area
+        if units == 'me':
+            f = 1.0
+        elif units == 'km':
+            f = 1.0e-6
+        elif units == 'mi':
+            f = 3.86102158542446e-7
+        elif units == 'ft':
+            f = 10.7639104167097
+        elif units == 'ar':
+            f = 2.47105381467165e-4
+        elif units == 'ht':
+            f = 1.0e-4
+
+    return f * value


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/units.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/utils.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/utils.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/utils.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,39 +1,36 @@
-"""
+"""!
 @package utils.py
 
- at brief Misc utilities for GRASS wxPython GUI
+ at brief Misc utilities for wxGUI
 
-(C) 2007-2008 by the GRASS Development Team
+(C) 2007-2009 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.
+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, Jachym Cepicky
-
- at date 2007-2008
+ at author Martin Landa <landa.martin gmail.com>
+ at author Jachym Cepicky
 """
 
 import os
 import sys
 import platform
+import string
+import glob
 import locale
 
 import globalvar
-grassPath = os.path.join(globalvar.ETCDIR, "python")
-sys.path.append(grassPath)
+sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
+
 from grass.script import core as grass
 
 import gcmd
-import grassenv
-try:
-    import subprocess
-except:
-    compatPath = os.path.join(globalvar.ETCWXDIR, "compat")
-    sys.path.append(compatPath)
-    import subprocess
 from debug import Debug
 
+def normalize_whitespace(text):
+    """!Remove redundant whitespace from a string"""
+    return string.join(string.split(text), ' ')
+
 def GetTempfile(pref=None):
     """
     Creates GRASS temporary file using defined prefix.
@@ -46,10 +43,11 @@
     """
     import gcmd
 
-    tempfileCmd = gcmd.Command(["g.tempfile",
-                                "pid=%d" % os.getpid()])
+    ret = gcmd.RunCommand('g.tempfile',
+                          read = True,
+                          pid = os.getpid())
 
-    tempfile = tempfileCmd.ReadStdOutput()[0].strip()
+    tempfile = ret.splitlines()[0].strip()
 
     # FIXME
     # ugly hack for MSYS (MS Windows)
@@ -64,9 +62,9 @@
     except:
         return None
 
-def GetLayerNameFromCmd(dcmd, fullyQualified=False, param=None,
-                        layerType=None):
-    """Get map name from GRASS command
+def GetLayerNameFromCmd(dcmd, fullyQualified = False, param = None,
+                        layerType = None):
+    """!Get map name from GRASS command
 
     @param dcmd GRASS command (given as list)
     @param fullyQualified change map name to be fully qualified
@@ -89,53 +87,69 @@
     elif 'd.rhumbline' in dcmd[0]:
         mapname = 'rhumb'
     elif 'labels=' in dcmd[0]:
-        mapname = dcmd[idx].split('=')[1]+' labels'
+        mapname = dcmd[idx].split('=')[1] + ' labels'
     else:
+        params = list()
         for idx in range(len(dcmd)):
-            if param and param in dcmd[idx]:
+            try:
+                p, v = dcmd[idx].split('=', 1)
+            except ValueError:
+                continue
+            
+            if p == param:
+                params = [(idx, p, v)]
                 break
-            elif not param:
-                if 'map=' in dcmd[idx] or \
-                        'input=' in dcmd[idx] or \
-                        'red=' in dcmd[idx] or \
-                        'h_map=' in dcmd[idx] or \
-                        'reliefmap' in dcmd[idx]:
-                    break
+            
+            if p in ('map', 'input',
+                     'red', 'blue', 'green',
+                     'h_map', 's_map', 'i_map',
+                     'reliefmap'):
+                params.append((idx, p, v))
         
-        if idx < len(dcmd):
-            try:
-                mapname = dcmd[idx].split('=')[1]
-            except IndexError:
-                if idx == 1:
-                    mapname = dcmd[idx]
+        if len(params) < 1:
+            if len(dcmd) > 1 and '=' not in dcmd[1]:
+                params.append((1, None, dcmd[1]))
+            else:
+                return mapname
+        
+        mapname = params[0][2]
+        mapset = ''
+        if fullyQualified and '@' not in mapname:
+            if layerType in ('raster', 'vector', '3d-raster', 'rgb', 'his'):
+                try:
+                    if layerType in ('raster', 'rgb', 'his'):
+                        findType = 'cell'
+                    else:
+                        findType = layerType
+                    result = grass.find_file(mapname, element=findType)
+                except AttributeError, e: # not found
+                    return ''
+                if result:
+                    mapset = result['mapset']
                 else:
-                    return ''
+                    mapset = grass.gisenv()['MAPSET']
+            else:
+                mapset = grass.gisenv()['MAPSET']
             
-            if fullyQualified and '@' not in mapname:
-                if layerType in ('raster', 'vector', '3d-raster'):
-                    try:
-                        if layerType == 'raster':
-                            findType = 'cell'
-                        else:
-                            findType = layerType
-                        result = grass.find_file(mapname, element=findType)
-                    except AttributeError, e: # not found
-                        return ''
-                    if result:
-                        mapname = result['fullname']
-                    else:
-                        mapname += '@' + grassenv.GetGRASSVariable('MAPSET')
+            # update dcmd
+            for i, p, v in params:
+                if p:
+                    dcmd[i] = p + '=' + v + '@' + mapset
                 else:
-                    mapname += '@' + grassenv.GetGRASSVariable('MAPSET')
-                if '=' in dcmd[idx]:
-                    dcmd[idx] = dcmd[idx].split('=')[0] + '=' + mapname
-                else:
-                    dcmd[idx] = mapname
+                    dcmd[i] = v + '@' + mapset
+        
+        maps = list()
+        for i, p, v in params:
+            if not p:
+                maps.append(v)
+            else:
+                maps.append(dcmd[i].split('=', 1)[1])
+        mapname = '\n'.join(maps)
     
     return mapname
 
 def GetValidLayerName(name):
-    """Make layer name SQL compliant, based on G_str_to_sql()
+    """!Make layer name SQL compliant, based on G_str_to_sql()
     
     @todo: Better use directly GRASS Python SWIG...
     """
@@ -167,7 +181,7 @@
     return retName
 
 def ListOfCatsToRange(cats):
-    """Convert list of category number to range(s)
+    """!Convert list of category number to range(s)
 
     Used for example for d.vect cats=[range]
 
@@ -247,19 +261,24 @@
     return mapsets
 
 def ListSortLower(list):
-    """Sort list items (not case-sensitive)"""
+    """!Sort list items (not case-sensitive)"""
     list.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
 
 def GetVectorNumberOfLayers(vector):
-    """Get list of vector layers"""
+    """!Get list of vector layers"""
     layers = []
     if not vector:
         return layers
     
+    fullname = grass.find_file(name = vector, element = 'vector')['fullname']
+    if not fullname:
+        Debug.msg(5, "utils.GetVectorNumberOfLayers(): vector map '%s' not found" % vector)
+        return layers
+    
     ret = gcmd.RunCommand('v.db.connect',
                           flags = 'g',
                           read = True,
-                          map = vector,
+                          map = fullname,
                           fs = ';')
         
     if not ret:
@@ -275,24 +294,30 @@
             pass
     
     Debug.msg(3, "utils.GetVectorNumberOfLayers(): vector=%s -> %s" % \
-                  (vector, ','.join(layers)))
+                  (fullname, ','.join(layers)))
     
     return layers
 
-def Deg2DMS(lon, lat):
-    """Convert deg value to dms string
+def Deg2DMS(lon, lat, string = True, hemisphere = True, precision = 3):
+    """!Convert deg value to dms string
 
-    @param lat latitude
-    @param lon longitude
-
-    @return DMS string
+    @param lon longitude (x)
+    @param lat latitude (y)
+    @param string True to return string otherwise tuple
+    @param hemisphere print hemisphere
+    @param precision seconds precision
+    
+    @return DMS string or tuple of values
     @return empty string on error
     """
     try:
         flat = float(lat)
         flon = float(lon)
     except ValueError:
-        return ''
+        if string:
+            return ''
+        else:
+            return None
 
     # fix longitude
     while flon > 180.0:
@@ -301,48 +326,162 @@
         flon += 360.0
 
     # hemisphere
-    if flat < 0.0:
+    if hemisphere:
+        if flat < 0.0:
+            flat = abs(flat)
+            hlat = 'S'
+        else:
+            hlat = 'N'
+
+        if flon < 0.0:
+            hlon = 'W'
+            flon = abs(flon)
+        else:
+            hlon = 'E'
+    else:
         flat = abs(flat)
-        hlat = 'S'
-    else:
-        hlat = 'N'
-
-    if flon < 0.0:
-        hlon = 'W'
         flon = abs(flon)
-    else:
-        hlon = 'E'
+        hlon = ''
+        hlat = ''
+    
+    slat = __ll_parts(flat, precision = precision)
+    slon = __ll_parts(flon, precision = precision)
 
-    slat = __ll_parts(flat)
-    slon = __ll_parts(flon)
+    if string:
+        return slon + hlon + '; ' + slat + hlat
+    
+    return (slon + hlon, slat + hlat)
 
-    return slon + hlon + '; ' + slat + hlat
+def DMS2Deg(lon, lat):
+    """!Convert dms value to deg
 
-def __ll_parts(value):
-    """Converts deg to d:m:s string"""
-    if value == 0.0:
-        return '00:00:00.0000'
+    @param lon longitude (x)
+    @param lat latitude (y)
     
-    d = int(int(value))
-    m = int((value - d) * 60)
-    s = ((value - d) * 60 - m) * 60
-    if m < 0:
-        m = '00'
-    elif m < 10:
-        m = '0' + str(m)
-    else:
-        m = str(m)
-    if s < 0:
-        s = '00.0000'
-    elif s < 10.0:
-        s = '0%.4f' % s
-    else:
-        s = '%.4f' % s
+    @return tuple of converted values
+    @return ValueError on error
+    """
+    x = __ll_parts(lon, reverse = True)
+    y = __ll_parts(lat, reverse = True)
     
-    return str(d) + ':' + m + ':' + s
+    return (x, y)
 
+def __ll_parts(value, reverse = False, precision = 3):
+    """!Converts deg to d:m:s string
+
+    @param value value to be converted
+    @param reverse True to convert from d:m:s to deg
+    @param precision seconds precision (ignored if reverse is True)
+    
+    @return converted value (string/float)
+    @return ValueError on error (reverse == True)
+    """
+    if not reverse:
+        if value == 0.0:
+            return '%s%.*f' % ('00:00:0', precision, 0.0)
+    
+        d = int(int(value))
+        m = int((value - d) * 60)
+        s = ((value - d) * 60 - m) * 60
+        if m < 0:
+            m = '00'
+        elif m < 10:
+            m = '0' + str(m)
+        else:
+            m = str(m)
+        if s < 0:
+            s = '00.0000'
+        elif s < 10.0:
+            s = '0%.*f' % (precision, s)
+        else:
+            s = '%.*f' % (precision, s)
+        
+        return str(d) + ':' + m + ':' + s
+    else: # -> reverse
+        try:
+            d, m, s = value.split(':')
+            hs = s[-1]
+            s = s[:-1]
+        except ValueError:
+            try:
+                d, m = value.split(':')
+                hs = m[-1]
+                m = m[:-1]
+                s = '0.0'
+            except ValueError:
+                try:
+                    d = value
+                    hs = d[-1]
+                    d = d[:-1]
+                    m = '0'
+                    s = '0.0'
+                except ValueError:
+                    raise ValueError
+        
+        if hs not in ('N', 'S', 'E', 'W'):
+            raise ValueError
+        
+        coef = 1.0
+        if hs in ('S', 'W'):
+            coef = -1.0
+        
+        fm = int(m) / 60.0
+        fs = float(s) / (60 * 60)
+        
+        return coef * (float(d) + fm + fs)
+    
+def GetCmdString(cmd):
+    """
+    Get GRASS command as string.
+    
+    @param cmd GRASS command given as dictionary
+    
+    @return command string
+    """
+    scmd = ''
+    if not cmd:
+        return scmd
+    
+    scmd = cmd[0]
+    
+    if cmd[1].has_key('flags'):
+        for flag in cmd[1]['flags']:
+            scmd += ' -' + flag
+    for flag in ('verbose', 'quiet', 'overwrite'):
+        if cmd[1].has_key(flag) and cmd[1][flag] is True:
+            scmd += ' --' + flag
+    
+    for k, v in cmd[1].iteritems():
+        if k in ('flags', 'verbose', 'quiet', 'overwrite'):
+            continue
+        scmd += ' %s=%s' % (k, v)
+            
+    return scmd
+
+def CmdToTuple(cmd):
+    """!Convert command list to tuple for gcmd.RunCommand()"""
+    if len(cmd) < 1:
+        return None
+        
+    dcmd = {}
+    for item in cmd[1:]:
+        if '=' in item: # params
+            key, value = item.split('=', 1)
+            dcmd[str(key)] = str(value)
+        elif item[:2] == '--': # long flags
+            flag = item[2:]
+            if flag in ('verbose', 'quiet', 'overwrite'):
+                dcmd[str(flag)] = True
+        else: # -> flags
+            if not dcmd.has_key('flags'):
+                dcmd['flags'] = ''
+            dcmd['flags'] += item.replace('-', '')
+                
+    return (cmd[0],
+            dcmd)
+
 def PathJoin(*args):
-    """Check path created by os.path.join"""
+    """!Check path created by os.path.join"""
     path = os.path.join(*args)
     if platform.system() == 'Windows' and \
             '/' in path:
@@ -350,6 +489,152 @@
     
     return path
 
+def ReadEpsgCodes(path):
+    """!Read EPSG code from the file
+
+    @param path full path to the file with EPSG codes
+
+    @return dictionary of EPSG code
+    @return string on error
+    """
+    epsgCodeDict = dict()
+    try:
+        try:
+            f = open(path, "r")
+        except IOError:
+            return _("failed to open '%s'" % path)
+        
+        i = 0
+        code = None
+        for line in f.readlines():
+            line = line.strip()
+            if len(line) < 1:
+                continue
+                
+            if line[0] == '#':
+                descr = line[1:].strip()
+            elif line[0] == '<':
+                code, params = line.split(" ", 1)
+                try:
+                    code = int(code.replace('<', '').replace('>', ''))
+                except ValueError:
+                    return e
+            
+            if code is not None:
+                epsgCodeDict[code] = (descr, params)
+                code = None
+            i += 1
+        
+        f.close()
+    except StandardError, e:
+        return e
+    
+    return epsgCodeDict
+
+def ReprojectCoordinates(coord, projOut, projIn = None, flags = ''):
+    """!Reproject coordinates
+
+    @param coord coordinates given as tuple
+    @param projOut output projection
+    @param projIn input projection (use location projection settings)
+
+    @return reprojected coordinates (returned as tuple)
+    """
+    if not projIn:
+        projIn = gcmd.RunCommand('g.proj',
+                                 flags = 'jf',
+                                 read = True)
+    coors = gcmd.RunCommand('m.proj',
+                            flags = flags,
+                            proj_in = projIn,
+                            proj_out = projOut,
+                            stdin = '%f|%f' % (coord[0], coord[1]),
+                            read = True)
+    if coors:
+        coors = coors.split('\t')
+        e = coors[0]
+        n = coors[1].split(' ')[0].strip()
+        try:
+            proj = projOut.split(' ')[0].split('=')[1]
+        except IndexError:
+            proj = ''
+        if proj in ('ll', 'latlong', 'longlat') and 'd' not in flags:
+            return (proj, (e, n))
+        else:
+            try:
+                return (proj, (float(e), float(n)))
+            except ValueError:
+                return (None, None)
+    
+    return (None, None)
+
+def GetListOfLocations(dbase):
+    """!Get list of GRASS locations in given dbase
+
+    @param dbase GRASS database path
+
+    @return list of locations (sorted)
+    """
+    listOfLocations = list()
+
+    try:
+        for location in glob.glob(os.path.join(dbase, "*")):
+            try:
+                if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
+                    listOfLocations.append(os.path.basename(location))
+            except:
+                pass
+    except UnicodeEncodeError, e:
+        raise e
+    
+    ListSortLower(listOfLocations)
+    
+    return listOfLocations
+
+def GetListOfMapsets(dbase, location, selectable = False):
+    """!Get list of mapsets in given GRASS location
+
+    @param dbase      GRASS database path
+    @param location   GRASS location
+    @param selectable True to get list of selectable mapsets, otherwise all
+
+    @return list of mapsets - sorted (PERMANENT first)
+    """
+    listOfMapsets = list()
+    
+    if selectable:
+        ret = gcmd.RunCommand('g.mapset',
+                              read = True,
+                              flags = 'l',
+                              location = location,
+                              gisdbase = dbase)
+        
+        if not ret:
+            return listOfMapsets
+            
+        for line in ret.rstrip().splitlines():
+            listOfMapsets += line.split(' ')
+    else:
+        for mapset in glob.glob(os.path.join(dbase, location, "*")):
+            if os.path.isdir(mapset) and \
+                    os.path.isfile(os.path.join(dbase, location, mapset, "WIND")) and \
+                    os.path.basename(mapset) != 'PERMANENT':
+                listOfMapsets.append(EncodeString(os.path.basename(mapset)))
+        ListSortLower(listOfMapsets)
+        listOfMapsets.insert(0, 'PERMANENT')
+    
+    return listOfMapsets
+
+def GetColorTables():
+    """!Get list of color tables"""
+    ret = gcmd.RunCommand('r.colors',
+                          read = True,
+                          flags = 'l')
+    if not ret:
+        return list()
+    
+    return ret.splitlines()
+
 def EncodeString(string):
     """!Return encoded string
 
@@ -378,3 +663,64 @@
         return unicode(string, enc)
     
     return string
+
+def _getGDALFormats():
+    """!Get dictionary of avaialble GDAL drivers"""
+    ret = grass.read_command('r.in.gdal',
+                             quiet = True,
+                             flags = 'f')
+    
+    return _parseFormats(ret)
+
+def _getOGRFormats():
+    """!Get dictionary of avaialble OGR drivers"""
+    ret = grass.read_command('v.in.ogr',
+                             quiet = True,
+                             flags = 'f')
+    
+    return _parseFormats(ret)
+
+def _parseFormats(output):
+    """!Parse r.in.gdal/v.in.ogr -f output"""
+    formats = { 'file'     : list(),
+                'database' : list(),
+                'protocol' : list()
+                }
+    
+    if not output:
+        return formats
+    
+    for line in output.splitlines():
+        format = line.strip().rsplit(':', -1)[1].strip()
+        if format in ('Memory', 'Virtual Raster', 'In Memory Raster'):
+            continue
+        if format in ('PostgreSQL', 'SQLite',
+                      'ODBC', 'ESRI Personal GeoDatabase',
+                      'Rasterlite',
+                      'PostGIS WKT Raster driver'):
+            formats['database'].append(format)
+        elif format in ('GeoJSON',
+                        'OGC Web Coverage Service',
+                        'OGC Web Map Service',
+                        'HTTP Fetching Wrapper'):
+            formats['protocol'].append(format)
+        else:
+            formats['file'].append(format)
+    
+    for items in formats.itervalues():
+        items.sort()
+    
+    return formats
+
+formats = None
+
+def GetFormats():
+    """!Get GDAL/OGR formats"""
+    global formats
+    if not formats:
+        formats = {
+            'gdal' : _getGDALFormats(),
+            'ogr' : _getOGRFormats()
+            }
+    
+    return formats

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vclean.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vclean.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vclean.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,541 @@
+"""
+ at package vclean.py
+
+ at brief Dialog for interactive construction of vector cleaning operations
+
+Classes:
+ - VectorCleaningFrame
+
+(C) 2010 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 Markus Metz
+"""
+
+import os
+import sys
+import shutil
+
+import wx
+import wx.lib.scrolledpanel as scrolled
+
+from grass.script import core as grass
+
+import dbm
+import gcmd
+import globalvar
+import gselect
+import render
+import utils
+from debug import Debug as Debug
+from preferences import globalSettings as UserSettings
+
+class VectorCleaningFrame(wx.Frame):
+    def __init__(self, parent, id=wx.ID_ANY, title=_('set up vector cleaning tools'),
+                 pos=wx.DefaultPosition, size=(-1, -1),
+                 style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
+                 **kwargs):
+        """!
+        Dialog for interactively defining vector cleaning tools
+        """
+        wx.Frame.__init__(self, parent, id, title, pos, size, style)
+
+        self.parent = parent # GMFrame
+        if self.parent:
+            self.log = self.parent.GetLogWindow()
+        else:
+            self.log = None
+  
+        # grass command
+        self.cmd = 'v.clean'
+        
+        # statusbar
+        self.CreateStatusBar()
+
+	# icon
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+	# self.panel not set as in colorrules
+        # self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        # input map to clean
+        self.inmap = ''
+        
+        # cleaned output map
+        self.outmap = ''
+
+	self.ftype = ''
+
+        # cleaning tools
+        self.toolslines = {}
+
+        self.tool_desc_list = [
+            _('break lines/boundaries'),
+            _('remove duplicates'),
+            _('remove dangles'),
+            _('change boundary dangles to lines'),
+            _('remove bridges'),
+	    _('change bridges to lines'),
+	    _('snap lines/boundaries'),
+	    _('remove duplicate area centroids'),
+	    _('break polygons'),
+	    _('prune lines/boundaries'),
+	    _('remove small areas'),
+	    _('remove lines/boundaries of zero length'),
+	    _('remove small angles at nodes')
+	    ]
+
+        self.tool_list = [
+            'break',
+            'rmdupl',
+            'rmdangle',
+            'chdangle',
+            'rmbridge',
+	    'chbridge',
+	    'snap',
+	    'rmdac',
+	    'bpol',
+	    'prune',
+	    'rmarea',
+	    'rmline',
+	    'rmsa'
+	    ]
+
+	self.ftype = [
+	    'point',
+	    'line',
+	    'boundary',
+	    'centroid',
+	    'area',
+	    'face']
+
+	self.n_ftypes = 6
+        
+	self.tools_string = ''
+	self.thresh_string = ''
+	self.ftype_string = ''
+
+	self.SetTitle(_('Set up vector cleaning tools'))
+	self.SetStatusText(_("Set up vector cleaning tools"))
+	self.elem = 'vector'
+	self.ctlabel = _('Choose cleaning tools and set thresholds')
+
+        # top controls
+        self.inmaplabel = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label= _('Select input vector map:'))
+        self.selectionInput = gselect.Select(parent=self, id=wx.ID_ANY,
+                                             size=globalvar.DIALOG_GSELECT_SIZE,
+                                             type='vector')
+	self.ftype_check = {}
+        ftypeBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
+                                label=_(' Feature type: '))
+        self.ftypeSizer = wx.StaticBoxSizer(ftypeBox, wx.HORIZONTAL)
+
+        self.outmaplabel = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                         label= _('Select output vector map:'))
+        self.selectionOutput = gselect.Select(parent=self, id=wx.ID_ANY,
+                                             size=globalvar.DIALOG_GSELECT_SIZE,
+                                             type='vector')
+        
+        self.overwrite = wx.CheckBox(parent=self, id=wx.ID_ANY,
+                                       label=_('Allow output files to overwrite existing files'))
+        self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
+
+        # cleaning tools
+        self.ct_label = wx.StaticText(parent=self, id=wx.ID_ANY,
+                                      label=self.ctlabel)
+
+        self.ct_panel = self.__toolsPanel()
+
+	# buttons to manage cleaning tools
+        self.btn_add = wx.Button(parent=self, id=wx.ID_ADD)
+        self.btn_remove = wx.Button(parent=self, id=wx.ID_REMOVE)
+        self.btn_moveup = wx.Button(parent=self, id=wx.ID_UP)
+        self.btn_movedown = wx.Button(parent=self, id=wx.ID_DOWN)
+
+        # add one tool as default
+        self.AddTool()
+	self.selected = -1
+        
+        # Buttons
+        self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
+        self.btn_run = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Run"))
+        self.btn_run.SetDefault()
+	self.btn_clipboard = wx.Button(parent=self, id=wx.ID_COPY)
+	self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
+        self.btn_help = wx.Button(parent = self, id = wx.ID_HELP)
+        
+        # bindings
+        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+        self.btn_run.Bind(wx.EVT_BUTTON, self.OnCleaningRun)
+	self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
+        self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
+
+        self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddTool)
+        self.btn_remove.Bind(wx.EVT_BUTTON, self.OnClearTool)
+        self.btn_moveup.Bind(wx.EVT_BUTTON, self.OnMoveToolUp)
+        self.btn_movedown.Bind(wx.EVT_BUTTON, self.OnMoveToolDown)
+
+        self.SetMinSize(self.GetBestSize())
+
+        # layout
+        self._layout()
+
+        self.CentreOnScreen()
+        self.Show()
+        
+    def _layout(self):
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        #
+        # input output
+        #
+	inSizer = wx.GridBagSizer(hgap=5, vgap=5)
+
+        inSizer.Add(item=self.inmaplabel, pos=(0, 0),
+                       flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1)
+        inSizer.Add(item=self.selectionInput, pos=(1, 0),
+                       flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1)
+
+	self.ftype_check = [
+	    wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('point')),
+	    wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('line')),
+	    wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('boundary')),
+	    wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('centroid')),
+	    wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('area')),
+	    wx.CheckBox(parent=self, id=wx.ID_ANY, label=_('face'))
+	    ]
+
+	typeoptSizer = wx.BoxSizer(wx.HORIZONTAL)
+	for num in range(0, self.n_ftypes):
+	    type_box = self.ftype_check[num]
+	    typeoptSizer.Add(item=type_box, flag=wx.ALIGN_LEFT, border=1)
+
+	self.ftypeSizer.Add(item = typeoptSizer,
+	           flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=2)
+
+	outSizer = wx.GridBagSizer(hgap=5, vgap=5)
+
+        outSizer.Add(item=self.outmaplabel, pos=(0, 0),
+                       flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1)
+        outSizer.Add(item=self.selectionOutput, pos=(1, 0),
+                       flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border=1)
+        replaceSizer = wx.BoxSizer(wx.HORIZONTAL)
+        replaceSizer.Add(item=self.overwrite, proportion=1,
+                         flag=wx.ALL | wx.EXPAND, border=1)
+
+        outSizer.Add(item=replaceSizer, pos=(2, 0),
+                       flag=wx.ALL | wx.EXPAND, border=1)
+
+        #
+        # tools selection
+        #
+        bodySizer = wx.GridBagSizer(hgap=5, vgap=5)
+
+        bodySizer.Add(item=self.ct_label, pos=(0, 0), span=(1, 2),
+                      flag=wx.ALL, border=5)
+
+        bodySizer.Add(item=self.ct_panel, pos=(1, 0), span=(1, 2))
+
+	manageBoxSizer = wx.GridBagSizer(hgap=10, vgap=1)
+	# start with row 1 for nicer layout
+        manageBoxSizer.Add(item=self.btn_add, pos=(1, 0), border=2, flag=wx.ALL | wx.EXPAND)
+        manageBoxSizer.Add(item=self.btn_remove, pos=(2, 0), border=2, flag=wx.ALL | wx.EXPAND)
+        manageBoxSizer.Add(item=self.btn_moveup, pos=(3, 0), border=2, flag=wx.ALL | wx.EXPAND)
+        manageBoxSizer.Add(item=self.btn_movedown, pos=(4, 0), border=2, flag=wx.ALL | wx.EXPAND)
+
+        bodySizer.Add(item=manageBoxSizer, pos=(1, 2),
+                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
+
+        bodySizer.AddGrowableCol(2)
+        
+        #
+        # standard buttons
+        #
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(self.btn_close,
+                     flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(self.btn_run,
+                     flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(self.btn_clipboard,
+                     flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(self.btn_help,
+                     flag=wx.LEFT | wx.RIGHT, border=5)
+        
+        #
+        # put it all together
+        #
+        sizer.Add(item=inSizer, proportion=0,
+                  flag=wx.ALL | wx.EXPAND, border=5)
+        
+        sizer.Add(item=self.ftypeSizer, proportion=0,
+                  flag=wx.ALL | wx.EXPAND, border=5)
+
+        sizer.Add(item=outSizer, proportion=0,
+                  flag=wx.ALL | wx.EXPAND, border=5)
+
+        sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY,
+                  style=wx.LI_HORIZONTAL), proportion=0,
+                  flag=wx.EXPAND | wx.ALL, border=5) 
+        
+        sizer.Add(item=bodySizer, proportion=1,
+                  flag=wx.ALL | wx.EXPAND, border=5)
+
+        sizer.Add(item=wx.StaticLine(parent=self, id=wx.ID_ANY,
+                  style=wx.LI_HORIZONTAL), proportion=0,
+                  flag=wx.EXPAND | wx.ALL, border=5) 
+        
+        sizer.Add(item=btnSizer, proportion=0,
+                  flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+        
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+        self.Layout()
+        
+    def __toolsPanel(self):
+        ct_panel = scrolled.ScrolledPanel(parent=self, id=wx.ID_ANY,
+	                                  size=(500, 240),
+                                          style=wx.SUNKEN_BORDER)
+
+        self.ct_sizer = wx.GridBagSizer(vgap=2, hgap=4)
+        
+        ct_panel.SetSizer(self.ct_sizer)
+        ct_panel.SetAutoLayout(True)
+        
+        return ct_panel        
+
+    def OnAddTool(self, event):
+        """!Add tool button pressed"""
+	self.AddTool()
+
+    def AddTool(self):
+        snum = len(self.toolslines.keys())
+	num = snum + 1
+	# tool number
+	tool_no = wx.StaticText(parent = self.ct_panel, id = 3000+num,
+                                         label= str(num)+'.')
+	# tool
+	tool_cbox = wx.ComboBox(parent = self.ct_panel, id=1000+num, 
+				size = (300, -1), choices = self.tool_desc_list,
+				style = wx.CB_DROPDOWN |
+				wx.CB_READONLY | wx.TE_PROCESS_ENTER)
+	self.Bind(wx.EVT_COMBOBOX, self.OnSetTool, tool_cbox)
+	# threshold
+	txt_ctrl = wx.TextCtrl(parent=self.ct_panel, id=2000+num, value='0.00',
+			       size=(100,-1),
+			       style=wx.TE_NOHIDESEL)
+	self.Bind(wx.EVT_TEXT, self.OnThreshValue, txt_ctrl)
+
+	# select
+	select = wx.CheckBox(parent=self.ct_panel, id=num)
+	select.SetValue(False)
+	self.Bind(wx.EVT_CHECKBOX, self.OnSelect, select)
+
+	# start with row 1 and col 1 for nicer layout
+	self.ct_sizer.Add(item=tool_no, pos=(num, 1),
+			  flag=wx.ALIGN_CENTER_VERTICAL, border=5)
+	self.ct_sizer.Add(item=tool_cbox, pos=(num, 2),
+			  flag=wx.ALIGN_CENTER | wx.RIGHT, border=5)
+	self.ct_sizer.Add(item=txt_ctrl, pos=(num, 3),
+			  flag=wx.ALIGN_CENTER | wx.RIGHT, border=5)
+	self.ct_sizer.Add(item=select, pos=(num, 4),
+			  flag=wx.ALIGN_CENTER | wx.RIGHT)
+
+	self.toolslines[num] = {
+	    'tool_desc' : '' ,
+	    'tool' : '' ,
+	    'thresh' : '0.00' }
+        
+        self.ct_panel.Layout()
+        self.ct_panel.SetupScrolling()
+        
+    def OnClearTool(self, event):
+        """!Remove tool button pressed"""
+        id = self.selected
+
+	if id > 0:
+	    self.FindWindowById(id+1000).SetValue('')
+	    self.toolslines[id]['tool_desc'] = ''
+	    self.toolslines[id]['tool'] = ''
+	    self.SetStatusText(_("%s. cleaning tool removed, will be ignored") % id)
+	else:
+	    self.SetStatusText(_("Please select a cleaning tool to remove"))
+
+    def OnMoveToolUp(self, event):
+        """!Move up tool button pressed"""
+        id = self.selected
+
+	if id > 1:
+	    id_up = id - 1
+	    this_toolline = self.toolslines[id]
+	    up_toolline = self.toolslines[id_up]
+	    
+	    self.FindWindowById(id_up).SetValue(True)
+	    self.FindWindowById(id_up+1000).SetValue(this_toolline['tool_desc'])
+	    self.FindWindowById(id_up+2000).SetValue(this_toolline['thresh'])
+	    self.toolslines[id_up] = this_toolline
+	    
+	    self.FindWindowById(id).SetValue(False)
+	    self.FindWindowById(id+1000).SetValue(up_toolline['tool_desc'])
+	    self.FindWindowById(id+2000).SetValue(up_toolline['thresh'])
+	    self.toolslines[id] = up_toolline
+	    self.selected = id_up
+	    self.SetStatusText(_("%s. cleaning tool moved up") % id)
+	elif id == 1:
+	    self.SetStatusText(_("1. cleaning tool can not be moved up "))
+	elif id == -1:
+	    self.SetStatusText(_("Please select a cleaning tool to move up"))
+
+
+    def OnMoveToolDown(self, event):
+        """!Move down tool button pressed"""
+        id = self.selected
+        snum = len(self.toolslines.keys())
+
+	if id > 0 and id < snum:
+	    id_down = id + 1
+	    this_toolline = self.toolslines[id]
+	    down_toolline = self.toolslines[id_down]
+	    
+	    self.FindWindowById(id_down).SetValue(True)
+	    self.FindWindowById(id_down+1000).SetValue(this_toolline['tool_desc'])
+	    self.FindWindowById(id_down+2000).SetValue(this_toolline['thresh'])
+	    self.toolslines[id_down] = this_toolline
+	    
+	    self.FindWindowById(id).SetValue(False)
+	    self.FindWindowById(id+1000).SetValue(down_toolline['tool_desc'])
+	    self.FindWindowById(id+2000).SetValue(down_toolline['thresh'])
+	    self.toolslines[id] = down_toolline
+	    self.selected = id_down
+	    self.SetStatusText(_("%s. cleaning tool moved down") % id)
+	elif id == snum:
+	    self.SetStatusText(_("Last cleaning tool can not be moved down "))
+	elif id == -1:
+	    self.SetStatusText(_("Please select a cleaning tool to move down"))
+
+    def OnSetTool(self, event):
+        """!Tool was defined"""
+        id = event.GetId()
+	tool_no = id-1000
+	num = self.FindWindowById(id).GetCurrentSelection()
+
+	self.toolslines[tool_no]['tool_desc'] = self.tool_desc_list[num]
+	self.toolslines[tool_no]['tool'] = self.tool_list[num]
+
+	self.SetStatusText( str(tool_no) + '. ' + _("cleaning tool: '%s'") % (self.tool_list[num]))
+
+    def OnThreshValue(self, event):
+        """!Threshold value was entered"""
+        id = event.GetId()
+	num = id-2000
+	self.toolslines[num]['thresh'] = self.FindWindowById(id).GetValue()
+
+	self.SetStatusText(_("Threshold for %(num)s. tool '%(tool)s': %(thresh)s") % \
+                               { 'num' : num,
+                                 'tool' : self.toolslines[num]['tool'],
+                                 'thresh' : self.toolslines[num]['thresh'] })
+
+    def OnSelect(self, event):
+        """!Tool was selected"""
+        id = event.GetId()
+
+	if self.selected > -1 and self.selected != id:
+	    win = self.FindWindowById(self.selected)
+	    win.SetValue(False)
+
+	if self.selected != id:
+	    self.selected = id
+	else:
+	    self.selected = -1
+
+    def OnCleaningRun(self, event):
+        """!Builds options and runs v.clean
+        """
+	self.SetStatusText(_("Executing selected cleaning operations..."))
+        snum = len(self.toolslines.keys())
+	self.GetCmdStrings()
+
+        if self.log:
+	    cmdstring = '%s' % (self.cmd)
+	    # list -> string
+	    cmdstring += ' input=%s output=%s type=%s tool=%s thres=%s' % \
+		(self.inmap, self.outmap, self.ftype_string, self.tools_string, self.thresh_string)
+	    if self.overwrite.IsChecked():
+		cmdstring += ' --overwrite'
+
+            self.log.RunCmd(cmdstring)
+            self.parent.Raise()
+        else:
+	    if self.overwrite.IsChecked():
+		overwrite = True
+	    else:
+		overwrite = False
+
+	    gcmd.RunCommand(self.cmd,
+			    input = self.inmap,
+			    output = self.outmap,
+			    type = self.ftype_string,
+			    tool = self.tools_string,
+			    thresh = self.thresh_string,
+			    overwrite = overwrite)
+
+    def OnClose(self, event):
+        self.Destroy()
+        
+    def OnHelp(self, event):
+        """!Show GRASS manual page"""
+        gcmd.RunCommand('g.manual',
+                        quiet = True,
+                        parent = self,
+                        entry = self.cmd)
+        
+    def OnCopy(self, event):
+        """!Copy the command"""
+        cmddata = wx.TextDataObject()
+	# get tool and thresh strings
+	self.GetCmdStrings()
+        cmdstring = '%s' % (self.cmd)
+        # list -> string
+        cmdstring += ' input=%s output=%s type=%s tool=%s thres=%s' % \
+	    (self.inmap, self.outmap, self.ftype_string, self.tools_string, self.thresh_string)
+	if self.overwrite.IsChecked():
+	    cmdstring += ' --overwrite'
+ 
+        cmddata.SetText(cmdstring)
+        if wx.TheClipboard.Open():
+            wx.TheClipboard.SetData(cmddata)
+            wx.TheClipboard.Close()
+            self.SetStatusText(_("Vector cleaning command copied to clipboard"))
+
+    def GetCmdStrings(self):
+	self.tools_string = ''
+	self.thresh_string = ''
+	self.ftype_string = ''
+	# feature types
+	first = 1
+        for num in range(0, self.n_ftypes - 1):
+	    if self.ftype_check[num].IsChecked():
+		if first:
+		    self.ftype_string = '%s' % self.ftype[num]
+		    first = 0
+		else:
+		    self.ftype_string += ',%s' % self.ftype[num]
+		    
+	    
+	# cleaning tools
+	first = 1
+        snum = len(self.toolslines.keys())
+        for num in range(1, snum + 1):
+	    if self.toolslines[num]['tool']:
+		if first:
+		    self.tools_string = '%s' % self.toolslines[num]['tool']
+		    self.thresh_string = '%s' % self.toolslines[num]['thresh']
+		    first = 0
+		else:
+		    self.tools_string += ',%s' % self.toolslines[num]['tool']
+		    self.thresh_string += ',%s' % self.toolslines[num]['thresh']
+
+	self.inmap = self.selectionInput.GetValue()
+	self.outmap = self.selectionOutput.GetValue()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vclean.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vdigit.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vdigit.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/vdigit.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,14 +1,8 @@
 """!
 @package vdigit
 
- at brief Vector digitizer extension
+ at brief wxGUI vector digitizer user interface
 
-Usage:
-
- at code
-  from vdigit import VDigit as VDigit
- at endcode
-
 Classes:
  - AbstractDigit 
  - VDigit
@@ -16,6 +10,7 @@
  - CDisplayDriver
  - VDigitSettingsDialog
  - VDigitCategoryDialog
+ - CategoryListCtrl
  - VDigitZBulkDialog
  - VDigitDuplicatesDialog
  - VDigitVBuildDialog
@@ -31,6 +26,7 @@
 import sys
 import string
 import copy
+import textwrap
 import traceback
 
 from threading import Thread
@@ -44,6 +40,7 @@
 from debug import Debug as Debug
 import gselect
 import globalvar
+from units import Units
 from preferences import globalSettings as UserSettings
 try:
     digitPath = os.path.join(globalvar.ETCWXDIR, "vdigit")
@@ -60,9 +57,8 @@
     errorMsg = err
     
 class AbstractDigit:
+    """!Abstract digitization class
     """
-    Abstract digitization class
-    """
     def __init__(self, mapwindow):
         """!Initialization
 
@@ -115,28 +111,26 @@
         try:
             ret = self.driver.Reset(self.map)
         except StandardError, e:
-            raise gcmd.DigitError(parent=self.mapWindow.parent,
-                                  message="%s %s (%s)" % (_("Unable to initialize display driver of vector "
-                                                            "digitizer. See 'Command output' for details.\n\n"
-                                                            "Details:"), e, errorMsg))
+            raise gcmd.GException(_("Unable to initialize display driver of vector "
+                              "digitizer. See 'Command output' for details.\n\n"
+                              "Details: ") + e)
         
         if map and ret == -1:
-            raise gcmd.DigitError(parent=self.mapWindow.parent,
-                                  message=_('Unable to open vector map <%s> for editing.\n\n'
-                                            'Data are probably corrupted, '
-                                            'try to run v.build to rebuild '
-                                            'the topology (Vector->Develop vector map->'
-                                            'Create/rebuild topology).') % map)
+            raise gcmd.GException(_('Unable to open vector map <%s> for editing.\n\n'
+                                    'Data are probably corrupted, '
+                                    'try to run v.build to rebuild '
+                                    'the topology (Vector->Develop vector map->'
+                                    'Create/rebuild topology).') % map)
         if not map and ret != 0:
-            raise gcmd.DigitError(parent=self.mapWindow.parent,
-                                  message=_('Unable to open vector map <%s> for editing.\n\n'
-                                            'Data are probably corrupted, '
-                                            'try to run v.build to rebuild '
-                                            'the topology (Vector->Develop vector map->'
-                                            'Create/rebuild topology).') % map)
-            
-        self.digit.InitCats()
+            raise gcmd.GException(_('Unable to open vector map <%s> for editing.\n\n'
+                                    'Data are probably corrupted, '
+                                    'try to run v.build to rebuild '
+                                    'the topology (Vector->Develop vector map->'
+                                    'Create/rebuild topology).') % map)
         
+        if self.digit:
+            self.digit.InitCats()
+        
     def SelectLinesByQueryThresh(self):
         """!Generic method used for SelectLinesByQuery()
         -- to get threshold value"""
@@ -182,16 +176,29 @@
 
         x1, y1 = pos1
         x2, y2 = pos2
+        ret = gcmd.RunCommand('v.edit',
+                              parent = self,
+                              quiet = True,
+                              read = True,
+                              map = bgmap,
+                              tool = 'select',
+                              bbox = '%f,%f,%f,%f' % (x1, y1, x2, y2))
 
-        vEditCmd = gcmd.Command(['v.edit',
-                                 '--q',
-                                 'map=%s' % bgmap,
-                                 'tool=select',
-                                 'bbox=%f,%f,%f,%f' % (pos1[0], pos1[1], pos2[0], pos2[1])])
-                                 #'polygon=%f,%f,%f,%f,%f,%f,%f,%f,%f,%f' % \
-                                 #    (x1, y1, x2, y1, x2, y2, x1, y2, x1, y1)])
-                                             
-        output = vEditCmd.ReadStdOutput()[0] # first line
+        if not ret:
+            x, y = pos1
+            ret = gcmd.RunCommand('v.edit',
+                                  parent = self,
+                                  quiet = True,
+                                  read = True,
+                                  map = bgmap,
+                                  tool = 'select',
+                                  coords = '%f,%f' % (x, y),
+                                  thresh = self.driver.GetThreshold(type='selectThresh'))
+        
+        if not ret:
+            return []
+        
+        output = ret.splitlines()[0] # first line
         ids = output.split(',') 
         ids = map(int, ids) # str -> int
         
@@ -201,14 +208,13 @@
         return ids
 
 class VDigit(AbstractDigit):
-    """!Prototype of digitization class based on v.digit reimplementation
-    Under development (wxWidgets C/C++ background)
+    """!Prototype of digitization class based on v.digit
+    reimplementation (wxWidgets C/C++)
     """
     def __init__(self, mapwindow):
         """!VDigit constructor
         
         @param mapwindow reference to mapwindow (MapFrame) instance
-        @param settings  initial settings of digitization tool
         """
         AbstractDigit.__init__(self, mapwindow)
         
@@ -222,11 +228,10 @@
         try:
             self.digit = wxvdigit.Digit(self.driver.GetDevice(),
                                         mapwindow)
-        except (ImportError, NameError, TypeError):
+        except (ImportError, NameError, TypeError), e:
             # print traceback
             traceback.print_exc(file = self.log)
             self.digit = None
-        
         self.UpdateSettings()
         
     def __del__(self):
@@ -343,7 +348,7 @@
         @param coords click coordinates
         @param move   X,Y direction
 
-        @return 1 vertex moved
+        @return id of new feature
         @return 0 vertex not moved (not found, line is not selected)
         """
         snap, thresh = self.__getSnapThreshold()
@@ -366,7 +371,7 @@
 
         @param coords coordinates to add vertex
 
-        @return 1 vertex added
+        @return id of new feature
         @return 0 nothing changed
         @return -1 on failure
         """
@@ -383,7 +388,7 @@
 
         @param coords coordinates to remove vertex
 
-        @return 1 vertex removed
+        @return id of new feature
         @return 0 nothing changed
         @return -1 on failure
         """
@@ -594,6 +599,36 @@
         """
         return dict(self.digit.GetLineCats(line))
 
+    def GetLineLength(self, line):
+        """!Get line length
+
+        @param line feature id
+
+        @return line length
+        @return -1 on error
+        """
+        return self.digit.GetLineLength(line)
+
+    def GetAreaSize(self, centroid):
+        """!Get area size
+
+        @param centroid centroid id
+
+        @return area size
+        @return -1 on error
+        """
+        return self.digit.GetAreaSize(centroid)
+        
+    def GetAreaPerimeter(self, centroid):
+        """!Get area perimeter
+
+        @param centroid centroid id
+
+        @return area size
+        @return -1 on error
+        """
+        return self.digit.GetAreaPerimeter(centroid)
+
     def SetLineCats(self, line, layer, cats, add=True):
         """!Set categories for given line and layer
 
@@ -646,7 +681,7 @@
             ret = -2
 
         if ret == -2:
-            raise gcmd.DigitError, _("Undo failed, data corrupted.")
+            raise gcmd.GException(_("Undo failed, data corrupted."))
 
         self.mapWindow.UpdateMap(render=False)
         
@@ -658,14 +693,23 @@
         
         Note: Changesets starts wiht 0
         """
+        if not self.digit:
+            return -1
+        
         return self.digit.GetUndoLevel()
 
     def UpdateSettings(self):
-        """Update digit settigs"""
-        if self.digit:
-            self.digit.UpdateSettings(UserSettings.Get(group='vdigit', key='breakLines',
-                                                       subkey='enabled'))
-
+        """!Update digit settigs"""
+        if not self.digit:
+            return
+        
+        self.digit.UpdateSettings(UserSettings.Get(group='vdigit', key='breakLines',
+                                                   subkey='enabled'),
+                                  UserSettings.Get(group='vdigit', key='addCentroid',
+                                                   subkey='enabled'),
+                                  UserSettings.Get(group='vdigit', key='catBoundary',
+                                                   subkey='enabled'))
+        
     def ZBulkLines(self, pos1, pos2, start, step):
         """!Z-bulk labeling
 
@@ -682,7 +726,7 @@
         
         if ret > 0:
             self.toolbar.EnableUndo()
-
+        
         return ret
     
     def __getSnapThreshold(self):
@@ -702,15 +746,6 @@
 
         return (snap, thresh)
 
-class Digit(VDigit):
-    """Default digit class"""
-    def __init__(self, mapwindow):
-        VDigit.__init__(self, mapwindow)
-        self.type = 'vdigit'
-        
-    def __del__(self):
-        VDigit.__del__(self)
-        
 class AbstractDisplayDriver:
     """!Abstract classs for display driver"""
     def __init__(self, parent, mapwindow):
@@ -770,10 +805,16 @@
 
         self.mapWindow = mapwindow
 
+        if not self.mapwindow.parent.IsStandalone():
+            logerr = self.mapwindow.parent.GetLayerManager().goutput.cmd_stderr
+        else:
+            logerr = None
+
         # initialize wx display driver
         try:
             self.__display = wxvdigit.DisplayDriver(mapwindow.pdcVector,
-                                                    mapwindow.pdcTmp)
+                                                    mapwindow.pdcTmp,
+                                                    logerr)
         except:
             self.__display = None
             
@@ -788,7 +829,8 @@
 
         @param pdc wx.PseudoDC instance
         """
-        self.__display.SetDevice(pdc)
+        if self.__display:
+            self.__display.SetDevice(pdc)
             
     def Reset(self, map):
         """!Reset map
@@ -826,6 +868,9 @@
 
         @return wx.Image instance
         """
+        if not self.__display:
+            return 0
+        
         nlines = self.__display.DrawMap(True) # force
         Debug.msg(3, "CDisplayDriver.DrawMap(): nlines=%d" % nlines)
 
@@ -878,6 +923,9 @@
         
         @param grassId if grassId is True returns GRASS ids, otherwise
         internal ids of objects drawn in PseudoDC"""
+        if not self.__display:
+            return list()
+        
         if grassId:
             selected = self.__display.GetSelected(True)
         else:
@@ -964,6 +1012,8 @@
         """!Set geographical region
         
         Needed for 'cell2pixel' conversion"""
+        if not self.__display:
+            return
         
         map = self.mapwindow.Map
         reg = map.region
@@ -1139,7 +1189,7 @@
             textLabel = wx.StaticText(panel, wx.ID_ANY, label)
             color = csel.ColourSelect(panel, id=wx.ID_ANY,
                                       colour=UserSettings.Get(group='vdigit', key='symbol',
-                                                              subkey=[key, 'color']), size=(25, 25))
+                                                              subkey=[key, 'color']), size=globalvar.DIALOG_COLOR_SIZE)
             isEnabled = UserSettings.Get(group='vdigit', key='symbol',
                                          subkey=[key, 'enabled'])
             if isEnabled is not None:
@@ -1220,7 +1270,7 @@
                                       label=_("Snap also to vertex"))
         self.snapVertex.SetValue(UserSettings.Get(group='vdigit', key="snapToVertex", subkey='enabled'))
         vertexSizer.Add(item=self.snapVertex, proportion=0, flag=wx.EXPAND)
-        self.mapUnits = self.parent.MapWindow.Map.ProjInfo()['units']
+        self.mapUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
         self.snappingInfo = wx.StaticText(parent=panel, id=wx.ID_ANY,
                                           label=_("Snapping threshold is %(value).1f %(units)s") % \
                                               {'value' : self.parent.digit.driver.GetThreshold(),
@@ -1321,7 +1371,7 @@
         box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Choose query tool"))
         sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
 
-        LocUnits = self.parent.MapWindow.Map.ProjInfo()['units']
+        LocUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
 
         self.queryBox = wx.CheckBox(parent=panel, id=wx.ID_ANY, label=_("Select by box"))
         self.queryBox.SetValue(UserSettings.Get(group='vdigit', key="query", subkey='box'))
@@ -1444,6 +1494,26 @@
                    flag=wx.ALL | wx.EXPAND, border=5)
 
         #
+        # digitize new area
+        #
+        box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Digitize new area"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        # add centroid
+        self.addCentroid = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                       label=_("Add centroid to left/right area"))
+        self.addCentroid.SetValue(UserSettings.Get(group='vdigit', key="addCentroid", subkey='enabled'))
+        sizer.Add(item=self.addCentroid, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
+        
+        # attach category to boundary
+        self.catBoundary = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                       label=_("Do not attach category to boundaries"))
+        self.catBoundary.SetValue(UserSettings.Get(group='vdigit', key="catBoundary", subkey='enabled'))
+        sizer.Add(item=self.catBoundary, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
+        border.Add(item=sizer, proportion=0,
+                   flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
+        
+        #
         # delete existing record
         #
         box   = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Delete existing feature(s)"))
@@ -1457,6 +1527,94 @@
         border.Add(item=sizer, proportion=0,
                    flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
 
+        #
+        # geometry attributes (currently only length and area are supported)
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Geometry attributes"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap=3, vgap=3)
+        gridSizer.AddGrowableCol(0)
+        self.geomAttrb = { 'length' : { 'label' : _('length') },
+                           'area' : { 'label' : _('area') },
+                           'perimeter' : { 'label' : _('perimeter') } }
+
+        digitToolbar = self.parent.toolbars['vdigit']
+        try:
+            vectorName = digitToolbar.GetLayer().GetName()
+        except AttributeError:
+            vectorName = None # no vector selected for editing
+        layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        tree = self.parent.tree
+        item = tree.FindItemByData('maplayer', mapLayer)
+        row = 0
+        for attrb in ['length', 'area', 'perimeter']:
+            # checkbox
+            check = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = self.geomAttrb[attrb]['label'])
+            ### self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
+            check.Bind(wx.EVT_CHECKBOX, self.OnGeomAttrb)
+            # column (only numeric)
+            column = gselect.ColumnSelect(parent = panel, size=(200, -1))
+            column.InsertColumns(vector = vectorName,
+                                 layer = layer, excludeKey = True,
+                                 type = ['integer', 'double precision'])
+            # units 
+            if attrb == 'area':
+                choices = Units.GetUnitsList('area')
+            else:
+                choices = Units.GetUnitsList('length')
+            win_units = wx.Choice(parent = panel, id = wx.ID_ANY,
+                                  choices = choices, size=(120, -1))
+            
+            # default values
+            check.SetValue(False)
+            if item and tree.GetPyData(item)[0]['vdigit'] and \
+                    tree.GetPyData(item)[0]['vdigit'].has_key('geomAttr') and \
+                    tree.GetPyData(item)[0]['vdigit']['geomAttr'].has_key(attrb):
+                check.SetValue(True)
+                column.SetStringSelection(tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['column'])
+                if attrb == 'area':
+                    type = 'area'
+                else:
+                    type = 'length'
+                unitsIdx = Units.GetUnitsIndex(type, 
+                                                tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['units'])
+                win_units.SetSelection(unitsIdx)
+
+            if not vectorName:
+                check.Enable(False)
+                column.Enable(False)
+            
+            if not check.IsChecked():
+                column.Enable(False)
+            
+            self.geomAttrb[attrb]['check']  = check.GetId()
+            self.geomAttrb[attrb]['column'] = column.GetId()
+            self.geomAttrb[attrb]['units']  = win_units.GetId()
+
+            gridSizer.Add(item = check,
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 0))
+            gridSizer.Add(item = column,
+                          pos = (row, 1))
+            gridSizer.Add(item = win_units,
+                          pos = (row, 2))
+            row += 1
+        
+        note = '\n'.join(textwrap.wrap(_("Note: These settings are stored "
+                                         " in the workspace not in the vector digitizer "
+                                         "preferences."), 55))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = note),
+                      pos = (3, 0), span=(1, 3))
+                      
+        sizer.Add(item=gridSizer, proportion=1,
+                  flag=wx.ALL | wx.EXPAND, border=1)
+        border.Add(item=sizer, proportion=0,
+                   flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
+
         # bindings
         self.Bind(wx.EVT_CHECKBOX, self.OnChangeAddRecord, self.addRecord)
         self.Bind(wx.EVT_CHOICE, self.OnChangeCategoryMode, self.categoryMode)
@@ -1491,6 +1649,22 @@
             (_("Area (closed boundary + centroid)"), "area"),
             (_("Direction"), "direction"),)
 
+    def OnGeomAttrb(self, event):
+        """!Register geometry attributes (enable/disable)"""
+        checked = event.IsChecked()
+        id = event.GetId()
+        key = None
+        for attrb, val in self.geomAttrb.iteritems():
+            if val['check'] == id:
+                key = attrb
+                break
+        
+        column = self.FindWindowById(self.geomAttrb[key]['column'])
+        if checked:
+            column.Enable()
+        else:
+            column.Enable(False)
+        
     def OnChangeCategoryMode(self, event):
         """!Change category mode"""
 
@@ -1591,7 +1765,7 @@
         fileSettings['vdigit'] = UserSettings.Get(group='vdigit')
         
         file = UserSettings.SaveToFile(fileSettings)
-        self.parent.gismanager.goutput.WriteLog(_('Vector digitizer settings saved to file <%s>.') % file)
+        self.parent.GetLayerManager().goutput.WriteLog(_('Vector digitizer settings saved to file <%s>.') % file)
         
         self.Destroy()
 
@@ -1647,10 +1821,40 @@
         UserSettings.Set(group='vdigit', key="categoryMode", subkey='selection',
                          value=self.categoryMode.GetSelection())
 
+        # digitize new area
+        UserSettings.Set(group='vdigit', key="addCentroid", subkey='enabled',
+                         value=self.addCentroid.IsChecked())
+        UserSettings.Set(group='vdigit', key="catBoundary", subkey='enabled',
+                         value=self.catBoundary.IsChecked())
+        
         # delete existing feature
         UserSettings.Set(group='vdigit', key="delRecord", subkey='enabled',
                          value=self.deleteRecord.IsChecked())
 
+        # geometry attributes (workspace)
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        tree = self.parent.tree
+        item = tree.FindItemByData('maplayer', mapLayer)
+        for key, val in self.geomAttrb.iteritems():
+            checked = self.FindWindowById(val['check']).IsChecked()
+            column  = self.FindWindowById(val['column']).GetValue()
+            unitsIdx = self.FindWindowById(val['units']).GetSelection()
+            if item and not tree.GetPyData(item)[0]['vdigit']: 
+                tree.GetPyData(item)[0]['vdigit'] = { 'geomAttr' : dict() }
+            
+            if checked: # enable
+                if key == 'area':
+                    type = key
+                else:
+                    type = 'length'
+                unitsKey = Units.GetUnitsKey(type, unitsIdx)
+                tree.GetPyData(item)[0]['vdigit']['geomAttr'][key] = { 'column' : column,
+                                                                       'units' : unitsKey }
+            else:
+                if item and tree.GetPyData(item)[0]['vdigit'] and \
+                        tree.GetPyData(item)[0]['vdigit']['geomAttr'].has_key(key):
+                    del tree.GetPyData(item)[0]['vdigit']['geomAttr'][key]
+        
         # snapping threshold
         self.parent.digit.threshold = self.parent.digit.driver.GetThreshold()
 
@@ -1700,7 +1904,7 @@
         self.parent.digit.UpdateSettings()
         
         # redraw map if auto-rendering is enabled
-        if self.parent.autoRender.GetValue(): 
+        if self.parent.statusbarWin['render'].GetValue(): 
             self.parent.OnRender(None)
 
 class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
@@ -2003,17 +2207,18 @@
 
         Return True line found or False if not found"""
         
-        cmdWhat = gcmd.Command(cmd=['v.what',
-                                   '--q',
-                                   'map=%s' % self.map,
-                                   'east_north=%f,%f' % \
-                                       (float(coords[0]), float(coords[1])),
-                                   'distance=%f' % qdist])
+        ret = gcmd.RunCommand('v.what',
+                              parent = self,
+                              quiet = True,
+                              map = self.map,
+                              east_north = '%f,%f' % \
+                                  (float(coords[0]), float(coords[1])),
+                              distance = qdist)
 
-        if cmdWhat.returncode != 0:
+        if not ret:
             return False
 
-        for item in cmdWhat.ReadStdOutput():
+        for item in ret.splitlines():
             litem = item.lower()
             if "id:" in litem: # get line id
                 self.line = int(item.split(':')[1].strip())

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/workspace.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/workspace.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/workspace.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -27,338 +27,396 @@
 # from xml.parsers.xmlproc import xmlproc
 # from xml.parsers.xmlproc import xmlval
 # from xml.parsers.xmlproc import xmldtd
-import xml.sax
-import xml.sax.handler
-HandlerBase=xml.sax.handler.ContentHandler
-from xml.sax import make_parser
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
 
+import utils
 import globalvar
 from preferences import globalSettings as UserSettings
 
-sys.path.append(os.path.join(globalvar.ETCWXDIR, "nviz"))
 try:
-    import grass6_wxnviz as wxnviz
+    import wxnviz
 except ImportError:
     wxnviz = None
 
-class ProcessWorkspaceFile(HandlerBase):
-    """
-    A SAX handler for the GXW XML file, as
-    defined in grass-gxw.dtd.
-    """
-    def __init__(self):
-        self.inTag = {}
-        for tag in ('gxw', 'layer', 'task', 'parameter',
-                    'flag', 'value', 'group', 'display',
-                    'layer_manager',
-                    'nviz',
-                    # surface
-                    'surface', 'attribute', 'draw', 'resolution',
-                    'wire_color', 'position', 'x', 'y', 'z',
-                    # vector lines
-                    'vlines', 'color', 'width', 'mode',
-                    'map', 'height',
-                    # vector points
-                    'vpoints', 'size'):
-            self.inTag[tag] = False
-
+class ProcessWorkspaceFile:
+    def __init__(self, tree):
+        """!A ElementTree handler for the GXW XML file, as defined in
+        grass-gxw.dtd.
+        """
+        self.tree = tree
+        self.root = self.tree.getroot()
+        
         #
         # layer manager properties
         #
         self.layerManager = {}
-        self.layerManager['pos'] = None # window position
+        self.layerManager['pos']  = None # window position
         self.layerManager['size'] = None # window size
-
+        
+        #
         # list of mapdisplays
+        #
         self.displays = []
+        #
         # list of map layers
+        #
         self.layers = []
-
-        self.cmd    = []
+        
         self.displayIndex = -1 # first display has index '0'
-
+        
+        self.__processFile()
+        
         self.nvizDefault = Nviz()
         
     def __filterValue(self, value):
-        """Translate value"""
+        """!Filter value
+        
+        @param value
+        """
         value = value.replace('&lt;', '<')
         value = value.replace('&gt;', '>')
         
         return value
+
+    def __getNodeText(self, node, tag, default = ''):
+        """!Get node text"""
+        p = node.find(tag)
+        if p is not None:
+            return utils.normalize_whitespace(p.text)
+        
+        return default
     
-    def startElement(self, name, attrs):
-        if name == 'display':
+    def __processFile(self):
+        """!Process workspace file"""
+        #
+        # layer manager
+        #
+        node_lm = self.root.find('layer_manager')
+        if node_lm is not None:
+            posAttr = node_lm.get('dim', '')
+            if posAttr:
+                posVal = map(int, posAttr.split(','))
+                try:
+                    self.layerManager['pos']  = (posVal[0], posVal[1])
+                    self.layerManager['size'] = (posVal[2], posVal[3])
+                except:
+                    pass
+        
+        #
+        # displays
+        #
+        for display in self.root.findall('display'):
             self.displayIndex += 1
-
+            
             # window position and size
-            posAttr = attrs.get('dim', '')
+            posAttr = display.get('dim', '')
             if posAttr:
                 posVal = map(int, posAttr.split(','))
                 try:
-                    pos = (posVal[0], posVal[1])
+                    pos  = (posVal[0], posVal[1])
                     size = (posVal[2], posVal[3])
                 except:
-                    pos = None
+                    pos  = None
                     size = None
             else:
-                pos = None
+                pos  = None
                 size = None
-
-            extentAttr = attrs.get('extent', '')
+            
+            extentAttr = display.get('extent', '')
             if extentAttr:
                 # w, s, e, n
                 extent = map(float, extentAttr.split(','))
             else:
                 extent = None
+            
+            # projection
+            node_projection = display.find('projection')
+            if node_projection is not None:
+                projection = { 'enabled' : True,
+                               'epsg' : node_projection.get('epsg', ''),
+                               'proj' : self.__getNodeText(node_projection, 'value') }
+            else:
+                projection = { 'enabled' : False }
+            
+            self.displays.append( {
+                    "render"         : bool(int(display.get('render', "0"))),
+                    "mode"           : int(display.get('mode', 0)),
+                    "showCompExtent" : bool(int(display.get('showCompExtent', "0"))),
+                    "pos"            : pos,
+                    "size"           : size,
+                    "extent"         : extent,
+                    "constrainRes"   : bool(int(display.get('constrainRes', "0"))),
+                    "projection"     : projection, } )
+            
+            # process all layers/groups in the display
+            self.__processLayers(display)
 
-            self.displays.append({
-                "render"         : bool(int(attrs.get('render', "0"))),
-                "mode"           : int(attrs.get('mode', 0)),
-                "showCompExtent" : bool(int(attrs.get('showCompExtent', "0"))),
-                "pos"            : pos,
-                "size"           : size,
-                "extent"         : extent,
-                "constrainRes"   : bool(int(attrs.get('constrainRes', "0")))})
+    def __processLayers(self, node, inGroup = -1):
+        """!Process layers/groups of selected display
+        
+        @param node display tree node
+        @param inGroup in group -> index of group item otherwise -1
+        """
+        for item in node.getchildren():
+            if item.tag == 'group':
+                # -> group
+                self.layers.append( {
+                        "type"    : 'group',
+                        "name"    : item.get('name', ''),
+                        "checked" : bool(int(item.get('checked', "0"))),
+                        "opacity" : None,
+                        "cmd"     : None,
+                        "group"   : inGroup,
+                        "display" : self.displayIndex,
+                        "vdigit"  : None,
+                        "nviz"    : None})
+                
+                self.__processLayers(item, inGroup = len(self.layers) - 1) # process items in group
+                
+            elif item.tag == 'layer':
+                cmd, selected, vdigit, nviz = self.__processLayer(item)
+                
+                self.layers.append( {
+                        "type"     : item.get('type', None),
+                        "name"     : item.get('name', None),
+                        "checked"  : bool(int(item.get('checked', "0"))),
+                        "opacity"  : float(item.get('opacity', '1.0')),
+                        "cmd"      : cmd,
+                        "group"    : inGroup,
+                        "display"  : self.displayIndex,
+                        "selected" : selected,
+                        "vdigit"   : vdigit,
+                        "nviz"     : nviz } )
             
-        elif name == 'group':
-            self.groupName    = attrs.get('name', None)
-            self.groupChecked = attrs.get('checked', None)
-            self.layers.append({
-                "type"    : 'group',
-                "name"    : self.groupName,
-                "checked" : int(self.groupChecked),
-                "opacity" : None,
-                "cmd"     : None,
-                "group"   : self.inTag['group'],
-                "display" : self.displayIndex,
-                "nviz"    : None})
+    def __processLayer(self, layer):
+        """!Process layer item
 
-        elif name == 'layer':
-            self.layerType     = attrs.get('type', None)
-            self.layerName     = attrs.get('name', None)
-            self.layerChecked  = attrs.get('checked', None)
-            self.layerOpacity  = attrs.get('opacity', None)
-            self.layerSelected = False
-            self.layerNviz     = None
-            self.cmd = []
-
-        elif name == 'task':
-            name = attrs.get('name', None)
-            self.cmd.append(name)
-
-        elif name == 'parameter':
-            self.parameterName = attrs.get('name', None)
-
-        elif name in ('value', 'x', 'y', 'z', 'map'):
-            self.value = ''
-
-        elif name == 'flag':
-            flag = attrs.get('name', None)
+        @param layer tree node
+        """
+        cmd = list()
+        
+        #
+        # layer attributes (task) - 2D settings
+        #
+        node_task = layer.find('task')
+        cmd.append(node_task.get('name', "unknown"))
+        
+        # flags
+        for p in node_task.findall('flag'):
+            flag = p.get('name', '')
             if len(flag) > 1:
-                self.cmd.append('--' + flag)
+                cmd.append('--' + flag)
             else:
-                self.cmd.append('-' + flag)
+                cmd.append('-' + flag)
         
-        elif name == 'selected':
-            if self.inTag['layer']:
-                self.layerSelected = True
-
-        elif name == 'layer_manager':
-            posAttr = attrs.get('dim', '')
-            if posAttr:
-                posVal = map(int, posAttr.split(','))
-                try:
-                    self.layerManager['pos'] = (posVal[0], posVal[1])
-                    self.layerManager['size'] = (posVal[2], posVal[3])
-                except:
-                    pass
-            else:
-                pass
-
+        # parameters
+        for p in node_task.findall('parameter'):
+            cmd.append('%s=%s' % (p.get('name', ''),
+                                  self.__filterValue(self.__getNodeText(p, 'value'))))
+        
+        if layer.find('selected') is not None:
+            selected = True
+        else:
+            selected = False
+        
         #
-        # Nviz section
+        # Vector digitizer settings
         #
-        elif name == 'nviz':
-            # init nviz layer properties (use default values)
-            self.layerNviz = {}
-            if self.layerType == 'raster':
-                self.layerNviz['surface'] = \
-                    self.nvizDefault.SetSurfaceDefaultProp()
-            elif self.layerType == 'vector':
-                self.layerNviz['vector'] = \
-                    self.nvizDefault.SetVectorDefaultProp()
-            
-        elif name == 'attribute':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                tagName = str(name)
-                attrbName = str(attrs.get('name', ''))
-                self.layerNviz['surface'][tagName][attrbName] = {}
-                if attrs.get('map', '0') == '0':
-                    self.layerNviz['surface'][tagName][attrbName]['map'] = False
-                else:
-                    self.layerNviz['surface'][tagName][attrbName]['map'] = True
-
-                self.refAttribute = self.layerNviz['surface'][tagName][attrbName]
+        node_vdigit = layer.find('vdigit')
+        if node_vdigit is not None:
+            vdigit = self.__processLayerVdigit(node_vdigit)
+        else:
+            vdigit = None
         
-        elif name == 'draw':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                tagName = str(name)
-                self.layerNviz['surface'][tagName]['all'] = False
-                self.layerNviz['surface'][tagName]['mode'] = {}
-                self.layerNviz['surface'][tagName]['mode']['value'] = -1 # to be calculated
-                self.layerNviz['surface'][tagName]['mode']['desc'] = {}
-                self.layerNviz['surface'][tagName]['mode']['desc']['shading'] = \
-                    str(attrs.get('shading', ''))
-                self.layerNviz['surface'][tagName]['mode']['desc']['style'] = \
-                    str(attrs.get('style', ''))
-                self.layerNviz['surface'][tagName]['mode']['desc']['mode'] = \
-                    str(attrs.get('mode', ''))
+        #
+        # Nviz (3D settings)
+        #
+        node_nviz = layer.find('nviz')
+        if node_nviz is not None:
+            nviz = self.__processLayerNviz(node_nviz)
+        else:
+            nviz = None
+        
+        return (cmd, selected, vdigit, nviz)
 
-        elif name == 'resolution':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                self.resolutionType = str(attrs.get('type', ''))
-                if not self.layerNviz['surface']['draw'].has_key(str(name)):
-                    self.layerNviz['surface']['draw'][str(name)] = {}
+    def __processLayerVdigit(self, node_vdigit):
+        """!Process vector digitizer layer settings
 
-        elif name == 'position':
-            if self.inTag['nviz'] and inTag['surface']:
-                self.layerNviz['surface']['position'] = {}
+        @param node_vdigit vdigit node
+        """
+        # init nviz layer properties
+        vdigit = dict()
+        for node in node_vdigit.findall('geometryAttribute'):
+            if not vdigit.has_key('geomAttr'):
+                vdigit['geomAttr'] = dict()
+            type = node.get('type')
+            vdigit['geomAttr'][type] = dict()
+            vdigit['geomAttr'][type]['column'] = node.get('column') # required
+            # default map units
+            vdigit['geomAttr'][type]['units'] = node.get('units', 'mu')
         
-        elif name == 'mode':
-            if self.inTag['nviz']:
-                if self.inTag['vlines']:
-                    self.layerNviz['vector']['lines']['mode'] = {}
-                    self.layerNviz['vector']['lines']['mode']['type'] = str(attrs.get('type', ''))
-                    self.layerNviz['vector']['lines']['mode']['surface'] = ''
-                elif self.inTag['vpoints']:
-                    self.layerNviz['vector']['points']['mode'] = {}
-                    self.layerNviz['vector']['points']['mode']['type'] = str(attrs.get('type', ''))
-                    self.layerNviz['vector']['points']['mode']['surface'] = ''
+        return vdigit
+    
+    def __processLayerNviz(self, node_nviz):
+        """!Process 3D layer settings
 
-        elif name == 'vpoints':
-            if self.inTag['nviz']:
-                marker = str(attrs.get('marker', ''))
+        @param node_nviz nviz node
+        """
+        # init nviz layer properties
+        nviz = {}
+        if node_nviz.find('surface') is not None: # -> raster
+            nviz['surface'] = {}
+            for sec in ('attribute', 'draw', 'mask', 'position'):
+                nviz['surface'][sec] = {}
+        elif node_nviz.find('vlines') is not None or \
+                node_nviz.find('vpoints') is not None: # -> vector
+            nviz['vector'] = {}
+            for sec in ('lines', 'points'):
+                nviz['vector'][sec] = {}
+        
+        if nviz.has_key('surface'):
+            node_surface = node_nviz.find('surface')
+            # attributes
+            for attrb in node_surface.findall('attribute'):
+                tagName = str(attrb.tag)
+                attrbName = attrb.get('name', '')
+                dc = nviz['surface'][tagName][attrbName] = {}
+                if attrb.get('map', '0') == '0':
+                    dc['map'] = False
+                else:
+                    dc['map'] = True
+                value = self.__getNodeText(attrb, 'value')
+                try:
+                    dc['value'] = int(value)
+                except ValueError:
+                    try:
+                        dc['value'] = float(value)
+                    except ValueError:
+                        dc['value'] = str(value)
+            
+            # draw
+            node_draw = node_surface.find('draw')
+            if node_draw is not None:
+                tagName = str(node_draw.tag)
+                nviz['surface'][tagName]['all'] = False
+                nviz['surface'][tagName]['mode'] = {}
+                nviz['surface'][tagName]['mode']['value'] = -1 # to be calculated
+                nviz['surface'][tagName]['mode']['desc'] = {}
+                nviz['surface'][tagName]['mode']['desc']['shading'] = \
+                    str(node_draw.get('shading', ''))
+                nviz['surface'][tagName]['mode']['desc']['style'] = \
+                    str(node_draw.get('style', ''))
+                nviz['surface'][tagName]['mode']['desc']['mode'] = \
+                    str(node_draw.get('mode', ''))
+                
+                # resolution
+                for node_res in node_draw.findall('resolution'):
+                    resType = str(node_res.get('type', ''))
+                    if not nviz['surface']['draw'].has_key('resolution'):
+                        nviz['surface']['draw']['resolution'] = {}
+                    value = int(self.__getNodeText(node_res, 'value'))
+                    nviz['surface']['draw']['resolution'][resType] = value
+                
+                # wire-color
+                node_wire_color = node_draw.find('wire_color')
+                if node_wire_color is not None:
+                    nviz['surface']['draw']['wire-color'] = {}
+                    value = str(self.__getNodeText(node_wire_color, 'value'))
+                    nviz['surface']['draw']['wire-color']['value'] = value
+                
+            # position
+            node_pos = node_surface.find('position')
+            if node_pos is not None:
+                dc = self.nviz['surface']['position'] = {}
+                for coor in ['x', 'y', 'z']:
+                    node = node_pos.find(coor)
+                    if node is None:
+                        continue
+                    value = int(self.__getNodeText(node, 'value'))
+                    dc[coor] = value
+            
+        elif nviz.has_key('vector'):
+            # vpoints
+            node_vpoints = node_nviz.find('vpoints')
+            if node_vpoints is not None:
+                marker = str(node_vpoints.get('marker', ''))
                 markerId = list(UserSettings.Get(group='nviz', key='vector',
                                                  subkey=['points', 'marker'], internal=True)).index(marker)
-                self.layerNviz['vector']['points']['marker'] = markerId
-        
-        self.inTag[name] = True
-        
-    def endElement(self, name):
-        if name == 'group':
-            self.groupName = self.groupChecked = None
-
-        elif name == 'layer':
-            self.layers.append({
-                    "type"     : self.layerType,
-                    "name"     : self.layerName,
-                    "checked"  : int(self.layerChecked),
-                    "opacity"  : 1.0,
-                    "cmd"      : None,
-                    "group"    : self.inTag['group'],
-                    "display"  : self.displayIndex,
-                    "selected" : self.layerSelected,
-                    "nviz"     : self.layerNviz})
+                nviz['vector']['points']['marker'] = { 'value' : markerId }
+                
+                node_mode = node_vpoints.find('mode')
+                if node_mode is not None:
+                    nviz['vector']['points']['mode'] = {}
+                    nviz['vector']['points']['mode']['type'] = str(node_mode.get('type', ''))
+                    nviz['vector']['points']['mode']['surface'] = \
+                        self.__processLayerNvizNode(node_mode, 'map', str)
+                
+                # color
+                self.__processLayerNvizNode(node_vpoints, 'color', str,
+                                            nviz['vector']['points'])
+                
+                # width
+                self.__processLayerNvizNode(node_vpoints, 'width', int,
+                                            nviz['vector']['points'])
+                
+                # height
+                self.__processLayerNvizNode(node_vpoints, 'height', int,
+                                            nviz['vector']['points'])
+                
+                # height
+                self.__processLayerNvizNode(node_vpoints, 'size', int,
+                                            nviz['vector']['points'])
             
-            if self.layerOpacity:
-                self.layers[-1]["opacity"] = float(self.layerOpacity)
-            if self.cmd:
-                self.layers[-1]["cmd"] = self.cmd
+            # vlines
+            node_vlines = node_nviz.find('vlines')
+            if node_vlines is not None:
+                node_mode = node_vlines.find('mode')
+                if node_mode is not None:
+                    nviz['vector']['lines']['mode'] = {}
+                    nviz['vector']['lines']['mode']['type'] = str(node_mode.get('type', ''))
+                    nviz['vector']['lines']['mode']['surface'] = ''
+                    
+                    # map
+                    nviz['vector']['lines']['mode']['surface'] = \
+                        self.__processLayerNvizNode(node_mode, 'map', str)
+                
+                # color
+                self.__processLayerNvizNode(node_vlines, 'color', str,
+                                            nviz['vector']['lines'])
+                
+                # width
+                self.__processLayerNvizNode(node_vlines, 'width', int,
+                                            nviz['vector']['lines'])
+                
+                # height
+                self.__processLayerNvizNode(node_vlines, 'height', int,
+                                            nviz['vector']['lines'])
             
-            self.layerType = self.layerName = self.Checked = \
-                self.Opacity = self.cmd = None
-
-        elif name == 'parameter':
-            self.cmd.append('%s=%s' % (self.parameterName,
-                                       self.__filterValue(self.value)))
-            self.parameterName = self.value = None
-
-        #
-        # Nviz section
-        #
-        elif name == 'attribute':
-            if self.inTag['nviz'] and self.inTag['surface']:
+        return nviz
+    
+    def __processLayerNvizNode(self, node, tag, cast, dc = None):
+        """!Process given tag nviz/vector"""
+        node_tag = node.find(tag)
+        if node_tag is not None:
+            if node_tag.find('value') is not None:
+                value = cast(self.__getNodeText(node_tag, 'value'))
+            else:
                 try:
-                    self.refAttribute['value'] = int(self.value)
+                    value = cast(node_tag.text)
                 except ValueError:
-                    try:
-                        self.refAttribute['value'] = float(self.value)
-                    except ValueError:
-                        self.refAttribute['value'] = str(self.value)
-
-        elif name == 'resolution':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                self.layerNviz['surface']['draw']['resolution'][self.resolutionType] = int(self.value)
-                del self.resolutionType
-
-        elif name == 'wire_color':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                self.layerNviz['surface']['draw']['wire-color'] = {}
-                self.layerNviz['surface']['draw']['wire-color']['value'] = str(self.value)
-
-        elif name == 'x':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                self.layerNviz['surface']['position']['x'] = int(self.value)
-
-        elif name == 'y':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                self.layerNviz['surface']['position']['y'] = int(self.value)
-
-        elif name == 'z':
-            if self.inTag['nviz'] and self.inTag['surface']:
-                self.layerNviz['surface']['position']['z'] = int(self.value)
-        
-        elif name == 'color':
-            if self.inTag['nviz']:
-                if self.inTag['vlines']:
-                    self.layerNviz['vector']['lines']['color'] = dict()
-                    self.layerNviz['vector']['lines']['color']['value'] = str(self.value)
-                elif self.inTag['vpoints']:
-                    self.layerNviz['vector']['points']['color'] = dict()
-                    self.layerNviz['vector']['points']['color']['value'] = str(self.value)
-                    
-        elif name == 'width':
-            if self.inTag['nviz']:
-                if self.inTag['vlines']:
-                    self.layerNviz['vector']['lines']['width'] = dict()
-                    self.layerNviz['vector']['lines']['width']['value'] = int(self.value)
-                elif self.inTag['vpoints']:
-                    self.layerNviz['vector']['points']['width'] = dict()
-                    self.layerNviz['vector']['points']['width']['value'] = int(self.value)
-
-        elif name == 'height':
-            if self.inTag['nviz']:
-                if self.inTag['vlines']:
-                    self.layerNviz['vector']['lines']['height'] = dict()
-                    self.layerNviz['vector']['lines']['height']['value'] = int(self.value)
-                elif self.inTag['vpoints']:
-                    self.layerNviz['vector']['points']['height'] = dict()
-                    self.layerNviz['vector']['points']['height']['value'] = int(self.value)
-        
-        elif name == 'size':
-            if self.inTag['nviz'] and self.inTag['vpoints']:
-                self.layerNviz['vector']['points']['size']['value'] = int(self.value)
-
-        elif name == 'map':
-            if self.inTag['nviz']:
-                if self.inTag['vlines']:
-                    self.layerNviz['vector']['lines']['mode']['surface'] = str(self.value)
-                elif self.inTag['vpoints']:
-                    self.layerNviz['vector']['points']['mode']['surface'] = str(self.value)
-
-        self.inTag[name] = False
-
-    def characters(self, ch):
-        self.my_characters(ch)
-
-    def my_characters(self, ch):
-        if self.inTag['value'] or \
-                self.inTag['x'] or \
-                self.inTag['y'] or \
-                self.inTag['z'] or \
-                self.inTag['map']:
-            self.value += ch
-
+                    if cast == str:
+                        value = ''
+                    else:
+                        value = None
+            if dc:
+                dc[tag] = dict()
+                dc[tag]['value'] = value
+            else:
+                return value
+    
 class Nviz:
     def __init__(self):
         """Default 3D settings"""
@@ -402,11 +460,11 @@
 
             data['draw'][control] = { 'value' : value }
             data['draw'][control]['update'] = None
-            
+        
         value, desc = self.GetDrawMode(UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'mode']),
                                        UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'style']),
                                        UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'shading']))
-
+    
         data['draw']['mode'] = { 'value' : value,
                                  'desc' : desc, 
                                  'update': None }
@@ -604,12 +662,12 @@
         return (value, desc)
     
 class WriteWorkspaceFile(object):
-    """Generic class for writing workspace file"""
+    """!Generic class for writing workspace file"""
     def __init__(self, lmgr, file):
         self.file =  file
         self.lmgr = lmgr
         self.indent = 0
-
+        
         # write header
         self.file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
         self.file.write('<!DOCTYPE gxw SYSTEM "grass-gxw.dtd">\n')
@@ -642,10 +700,10 @@
                        'constrainRes="%d" '
                        'dim="%d,%d,%d,%d" '
                        'extent="%f,%f,%f,%f">\n' % (' ' * self.indent,
-                                                    int(mapTree.mapdisplay.autoRender.IsChecked()),
-                                                    mapTree.mapdisplay.toggleStatus.GetSelection(),
-                                                    int(mapTree.mapdisplay.showRegion.IsChecked()),
-                                                    int(mapTree.mapdisplay.compResolution.IsChecked()),
+                                                    int(mapTree.mapdisplay.statusbarWin['render'].IsChecked()),
+                                                    mapTree.mapdisplay.statusbarWin['toggle'].GetSelection(),
+                                                    int(mapTree.mapdisplay.statusbarWin['region'].IsChecked()),
+                                                    int(mapTree.mapdisplay.statusbarWin['resolution'].IsChecked()),
                                                     displayPos[0],
                                                     displayPos[1],
                                                     displaySize[0],
@@ -655,24 +713,39 @@
                                                     region['e'],
                                                     region['n']
                                                     ))
+            # projection statusbar info
+            if mapTree.mapdisplay.statusbarWin['projection'].IsChecked() and \
+                    UserSettings.Get(group='display', key='projection', subkey='proj4'):
+                self.indent += 4
+                file.write('%s<projection' % (' ' * self.indent))
+                epsg = UserSettings.Get(group='display', key='projection', subkey='epsg')
+                if epsg:
+                    file.write(' epsg="%s"' % epsg)
+                file.write('>\n')
+                proj = UserSettings.Get(group='display', key='projection', subkey='proj4')
+                self.indent += 4 
+                file.write('%s<value>%s</value>\n' % (' ' * self.indent, proj))
+                self.indent -= 4
+                file.write('%s</projection>\n' % (' ' * self.indent))
+                self.indent -= 4
             
             # list of layers
             item = mapTree.GetFirstChild(mapTree.root)[0]
             self.__writeLayer(mapTree, item)
             file.write('%s</display>\n' % (' ' * self.indent))
-
+        
         self.indent =- 4
         file.write('%s</gxw>\n' % (' ' * self.indent))
 
     def __filterValue(self, value):
-        """Make value XML-valid"""
+        """!Make value XML-valid"""
         value = value.replace('<', '&lt;')
         value = value.replace('>', '&gt;')
         
         return value
     
     def __writeLayer(self, mapTree, item):
-        """Write bunch of layers to GRASS Workspace XML file"""
+        """!Write bunch of layers to GRASS Workspace XML file"""
         self.indent += 4
         itemSelected = mapTree.GetSelections()
         while item and item.IsOk():
@@ -714,40 +787,54 @@
                 # layer properties
                 self.file.write('%s<task name="%s">\n' % (' ' * self.indent, cmd[0]))
                 self.indent += 4
-                for option in cmd[1:]:
-                    if option[0] == '-': # flag
-                        if option[1] == '-':
+                for key, val in cmd[1].iteritems():
+                    if key == 'flags':
+                        for f in val:
                             self.file.write('%s<flag name="%s" />\n' %
-                                            (' ' * self.indent, option[2:]))
-                        else:
-                            self.file.write('%s<flag name="%s" />\n' %
-                                            (' ' * self.indent, option[1]))
+                                            (' ' * self.indent, f))
+                    elif val in (True, False):
+                        self.file.write('%s<flag name="%s" />\n' %
+                                        (' ' * self.indent, key))
                     else: # parameter
-                        key, value = option.split('=', 1)
                         self.file.write('%s<parameter name="%s">\n' %
-                                   (' ' * self.indent, key))
+                                        (' ' * self.indent, key))
                         self.indent += 4
                         self.file.write('%s<value>%s</value>\n' %
-                                   (' ' * self.indent, self.__filterValue(value)))
+                                        (' ' * self.indent, self.__filterValue(val)))
                         self.indent -= 4
                         self.file.write('%s</parameter>\n' % (' ' * self.indent));
                 self.indent -= 4
                 self.file.write('%s</task>\n' % (' ' * self.indent));
+                # vector digitizer
+                vdigit = mapTree.GetPyData(item)[0]['vdigit']
+                if vdigit:
+                    self.file.write('%s<vdigit>\n' % (' ' * self.indent))
+                    if vdigit.has_key('geomAttr'):
+                        self.indent += 4
+                        for type, val in vdigit['geomAttr'].iteritems():
+                            units = ''
+                            if val['units'] != 'mu':
+                                units = ' units="%s"' % val['units']
+                            self.file.write('%s<geometryAttribute type="%s" column="%s"%s />\n' % \
+                                                (' ' * self.indent, type, val['column'], units))
+                        self.indent -= 4
+                    self.file.write('%s</vdigit>\n' % (' ' * self.indent))
+                # nviz
                 nviz = mapTree.GetPyData(item)[0]['nviz']
                 if nviz:
-                    self.file.write('%s<nviz>\n' % (' ' * self.indent));
+                    self.file.write('%s<nviz>\n' % (' ' * self.indent))
                     if maplayer.type == 'raster':
                         self.__writeNvizSurface(nviz['surface'])
                     elif maplayer.type == 'vector':
                         self.__writeNvizVector(nviz['vector'])
-                    self.file.write('%s</nviz>\n' % (' ' * self.indent));
+                    self.file.write('%s</nviz>\n' % (' ' * self.indent))
                 self.indent -= 4
-                self.file.write('%s</layer>\n' % (' ' * self.indent));
+                self.file.write('%s</layer>\n' % (' ' * self.indent))
             item = mapTree.GetNextSibling(item)
         self.indent -= 4
-
+        
     def __writeNvizSurface(self, data):
-        """Save Nviz raster layer properties to workspace
+        """!Save Nviz raster layer properties to workspace
 
         @param data Nviz layer properties
         """
@@ -821,7 +908,7 @@
         self.indent -= 4
 
     def __writeNvizVector(self, data):
-        """Save Nviz vector layer properties (lines/points) to workspace
+        """!Save Nviz vector layer properties (lines/points) to workspace
 
         @param data Nviz layer properties
         """
@@ -835,9 +922,9 @@
             if attrb == 'lines':
                 self.file.write('%s<v%s>\n' % (' ' * self.indent, attrb))
             elif attrb == 'points':
-                markerId = data[attrb]['marker']
-                marker = UserSettings.Get(group='nviz', key='vector',
-                                          subkey=['points', 'marker'], internal=True)[markerId]
+                markerId = data[attrb]['marker']['value']
+                marker = UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['points', 'marker'], internal = True)[markerId]
                 self.file.write('%s<v%s marker="%s">\n' % (' ' * self.indent,
                                                            attrb,
                                                            marker))
@@ -857,7 +944,7 @@
                 else:
                     self.file.write('%s<%s>\n' % (' ' * self.indent, name))
                     self.indent += 4
-                    self.file.write('%s<value>%s</value>\n' % (' ' * self.indent, data[attrb][name]))
+                    self.file.write('%s<value>%s</value>\n' % (' ' * self.indent, data[attrb][name]['value']))
                     self.indent -= 4
                     self.file.write('%s</%s>\n' % (' ' * self.indent, name))
             self.indent -= 4
@@ -867,7 +954,7 @@
 
 class ProcessGrcFile(object):
     def __init__(self, filename):
-        """Process GRC file"""
+        """!Process GRC file"""
         self.filename = filename
 
         # elements
@@ -883,7 +970,7 @@
         self.num_error = 0
 
     def read(self, parent):
-        """Read GRC file
+        """!Read GRC file
 
         @param parent parent window
 
@@ -917,7 +1004,7 @@
         return self.layers
 
     def process_line(self, line, line_id):
-        """Process line definition"""
+        """!Process line definition"""
         element = self._get_element(line)
         if element == 'Group':
             self.groupName = self._get_value(line)
@@ -1128,18 +1215,18 @@
             self.num_error += 1
 
     def _get_value(self, line):
-        """Get value of element"""
+        """!Get value of element"""
         try:
             return line.strip(' ').split(' ')[1].strip(' ')
         except:
             return ''
 
     def _get_element(self, line):
-        """Get element tag"""
+        """!Get element tag"""
         return line.strip(' ').split(' ')[0].strip(' ')
 
     def _get_cmd_param_index(self, cmd, name):
-        """Get index of parameter in cmd list
+        """!Get index of parameter in cmd list
 
         @param cmd cmd list
         @param name parameter name
@@ -1160,7 +1247,7 @@
         return -1
 
     def _color_name_to_rgb(self, value):
-        """Convert color name (#) to rgb values"""
+        """!Convert color name (#) to rgb values"""
         col = wx.NamedColour(value)
         return str(col.Red()) + ':' + \
             str(col.Green()) + ':' + \

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/wxnviz.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/wxnviz.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/wxnviz.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,1227 @@
+"""!
+ at package wxnviz.py
+
+ at brief wxGUI 3D view mode
+
+This module implements 3D visualization mode for map display.
+
+List of classes:
+ - Nviz
+
+(C) 2008-2010 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> (Google SoC 2008/2010)
+ at author Pythonized by Glynn Clements
+"""
+
+import sys
+from threading import Thread
+
+from ctypes import *
+from grass.lib.grass import *
+from grass.lib.g3d   import *
+from grass.lib.ogsf  import *
+from grass.lib.nviz  import *
+
+from debug import Debug
+
+log      = None
+progress = None
+
+def print_error(msg, type):
+    """!Redirect stderr"""
+    global log
+    if log:
+        log.write(msg)
+    else:
+        print msg
+    
+    return 0
+
+def print_progress(value):
+    """!Redirect progress info"""
+    global progress
+    if progress:
+        progress.SetValue(value)
+    else:
+        print value
+    
+    return 0
+
+errtype = CFUNCTYPE(UNCHECKED(c_int), String, c_int)
+errfunc = errtype(print_error)
+pertype = CFUNCTYPE(UNCHECKED(c_int), c_int)
+perfunc = pertype(print_progress)
+
+class Nviz(object):
+    def __init__(self, glog, gprogress):
+        """!Initialize Nviz class instance
+        
+        @param log logging area
+        """
+        global errfunc, perfunc, log, progress
+        log = glog
+        progress = gprogress
+        
+        G_gisinit("")
+        G_set_error_routine(errfunc)
+        G_set_percent_routine(perfunc)
+        
+        GS_libinit()
+        GVL_libinit()
+        
+        self.data_obj = nv_data()
+        self.data = pointer(self.data_obj)
+        self.width = self.height = -1
+        self.showLight = False
+        
+        Debug.msg(1, "Nviz::Nviz()")
+        
+    def __del__(self):
+        """!Destroy Nviz class instance"""
+        G_unset_error_routine()
+        G_unset_percent_routine()
+        del self.data
+        del self.data_obj
+        self.log = None
+        
+    def ResizeWindow(self, width, height):
+        """!GL canvas resized
+        
+        @param width window width
+        @param height window height
+        
+        @return 1 on success
+        @return 0 on failure (window resized by default to 20x20 px)
+        """
+        self.width  = width
+        self.height = height
+        Debug.msg(3, "Nviz::ResizeWindow(): width=%d height=%d",
+                  width, height)
+        return Nviz_resize_window(width, height)
+    
+    def SetViewDefault(self):
+        """!Set default view (based on loaded data)
+        
+        @return z-exag value, default, min and max height
+        """
+        # determine z-exag
+        z_exag = Nviz_get_exag()
+        Nviz_change_exag(self.data, z_exag)
+        
+        # determine height
+        hdef = c_double()
+        hmin = c_double()
+        hmax = c_double()
+        Nviz_get_exag_height(byref(hdef), byref(hmin), byref(hmax))
+        
+        Debug.msg(1, "Nviz::SetViewDefault(): hdef=%f, hmin=%f, hmax=%f",
+                  hdef.value, hmin.value, hmax.value)
+        
+        return (z_exag, hdef.value, hmin.value, hmax.value)
+    
+    def SetView(self, x, y, height, persp, twist):
+        """!Change view settings
+        @param x,y position
+        @param height
+        @param persp perpective
+        @param twist
+        """
+        Nviz_set_viewpoint_height(height)
+        Nviz_set_viewpoint_position(x, y)
+        Nviz_set_viewpoint_twist(twist)
+        Nviz_set_viewpoint_persp(persp)
+        
+        Debug.msg(3, "Nviz::SetView(): x=%f, y=%f, height=%f, persp=%f, twist=%f",
+                  x, y, height, persp, twist)
+        
+    def SetZExag(self, z_exag):
+        """!Set z-exag value
+        
+        @param z_exag value
+        
+        @return 1
+        """
+        Debug.msg(3, "Nviz::SetZExag(): z_exag=%f", z_exag)
+        return Nviz_change_exag(self.data, z_exag)
+    
+    def Draw(self, quick, quick_mode):
+        """!Draw canvas
+        
+        Draw quick mode:
+         - DRAW_QUICK_SURFACE
+         - DRAW_QUICK_VLINES
+         - DRAW_QUICK_VPOINTS
+         - DRAW_QUICK_VOLUME
+        
+        @param quick if true draw in wiremode
+        @param quick_mode quick mode
+        """
+        Debug.msg(3, "Nviz::Draw(): quick=%d", quick)
+        
+        Nviz_draw_cplane(self.data, -1, -1) # ?
+        
+        if quick:
+            Nviz_draw_quick(self.data, quick_mode)
+        else:
+            Nviz_draw_all(self.data)
+        
+    def EraseMap(self):
+        """!Erase map display (with background color)
+        """
+        Debug.msg(1, "Nviz::EraseMap()")
+        GS_clear(Nviz_get_bgcolor(self.data))
+        
+    def InitView(self):
+        """!Initialize view"""
+        # initialize nviz data
+        Nviz_init_data(self.data)
+        
+        # define default attributes for map objects
+        Nviz_set_surface_attr_default()
+        # set background color
+        Nviz_set_bgcolor(self.data, Nviz_color_from_str("white"))
+        
+        GS_clear(Nviz_get_bgcolor(self.data))
+        # initialize view, lights
+        Nviz_init_view(self.data)
+        
+        Debug.msg(1, "Nviz::InitView()")
+        
+    def SetBgColor(self, color_str):
+        """!Set background color
+        
+        @param color_str color string
+        """
+        Nviz_set_bgcolor(self.data, Nviz_color_from_str(color_str))
+        
+    def SetLight(self, x, y, z, color, bright, ambient, w = 0, lid = 1):
+        """!Change lighting settings
+        @param x,y,z position
+        @param color light color (as string)
+        @param bright light brightness
+        @param ambient light ambient
+        @param w local coordinate (default to 0)
+        """
+        Nviz_set_light_position(self.data, lid, x, y, z, w)
+        Nviz_set_light_bright(self.data, lid, bright)
+        Nviz_set_light_color(self.data, lid, int(color[0]), int(color[1]), int(color[2]))
+        Nviz_set_light_ambient(self.data, lid, ambient)
+                             
+    def LoadSurface(self, name, color_name, color_value):
+        """!Load raster map (surface)
+        
+        @param name raster map name
+        @param color_name raster map for color (None for color_value)
+        @param color_value color string (named color or RGB triptet)
+        
+        @return object id
+        @return -1 on failure
+        """
+        mapset = G_find_cell2(name, "")
+        if mapset is None:
+            G_warning(_("Raster map <%s> not found"), name)
+            return -1
+        
+        # topography
+        id = Nviz_new_map_obj(MAP_OBJ_SURF,
+                              G_fully_qualified_name(name, mapset), 0.0,
+                              self.data)
+        
+        if color_name:      # check for color map
+            mapset = G_find_cell2(color_name, "")
+            if mapset is None:
+                G_warning(_("Raster map <%s> not found"), color_name)
+                GS_delete_surface(id)
+                return -1
+            
+            Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(color_name, mapset), -1.0,
+                          self.data)
+        
+        elif color_value:   # check for color value
+            Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, CONST_ATT,
+                          None, Nviz_color_from_str(color_value),
+                          self.data)
+        
+        else:               # use by default elevation map for coloring
+            Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(name, mapset), -1.0,
+                          self.data)
+        
+        # if (i > 1)
+        #     set_default_wirecolors(self.data, i)
+        
+        # focus on loaded self.data
+        Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1)
+        
+        Debug.msg(1, "Nviz::LoadRaster(): name=%s -> id=%d", name, id)
+        
+        return id
+
+    def UnloadSurface(self, id):
+        """!Unload surface
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return 0 on failure
+        """
+        if not GS_surf_exists(id):
+            return 0
+        
+        Debug.msg(1, "Nviz::UnloadSurface(): id=%d", id)
+        
+        if GS_delete_surface(id) < 0:
+            return 0
+        
+        return 1
+    
+    def LoadVector(self, name, points):
+        """!Load vector map overlay
+        
+        @param name vector map name
+        @param points if true load 2d points rather then 2d lines
+        
+        @return object id
+        @return -1 on failure
+        """
+        if GS_num_surfs() == 0:     # load base surface if no loaded
+            Nviz_new_map_obj(MAP_OBJ_SURF, None, 0.0, self.data)
+            
+            nsurf = c_int()
+            surf_list = GS_get_surf_list(byref(nsurf))
+            GS_set_att_const(surf_list[0], ATT_TRANSP, 255)
+        
+        mapset = G_find_vector2 (name, "")
+        if mapset is None:
+            G_warning(_("Vector map <%s> not found"),
+                      name)
+        
+        if points:
+            id = Nviz_new_map_obj(MAP_OBJ_SITE,
+                                  G_fully_qualified_name(name, mapset), 0.0,
+                                  self.data)
+        else:
+            id = Nviz_new_map_obj(MAP_OBJ_VECT,
+                                  G_fully_qualified_name(name, mapset), 0.0,
+                                  self.data)
+        
+        Debug.msg(1, "Nviz::LoadVector(): name=%s -> id=%d", name, id)
+        
+        return id
+    
+    def UnloadVector(self, id, points):
+        """!Unload vector set
+        
+        @param id vector set id
+        @param points vector points or lines set
+        
+        @return 1 on success
+        @return 0 on failure
+        """
+        Debug.msg(1, "Nviz::UnloadVector(): id=%d", id)
+        
+        if points:
+            if not GP_site_exists(id):
+                return 0
+            if GP_delete_site(id) < 0:
+                return 0
+        else:
+            if not GV_vect_exists(id):
+                return 0
+            if GV_delete_vector(id) < 0:
+                return 0
+        
+        return 1
+
+    def LoadVolume(self, name, color_name, color_value):
+        """!Load 3d raster map (volume)
+        
+        @param name 3d raster map name
+        @param color_name 3d raster map for color (None for color_value)
+        @param color_value color string (named color or RGB triptet)
+        
+        @return object id
+        @return -1 on failure
+        """
+        mapset = G_find_grid3(name, "")
+        if mapset is None:
+            G_warning(_("3d raster map <%s> not found"),
+                      name)
+            return -1
+        
+        # topography
+        id = Nviz_new_map_obj(MAP_OBJ_VOL,
+                              G_fully_qualified_name(name, mapset), 0.0,
+                              self.data)
+        
+        if color_name:      # check for color map
+            mapset = G_find_grid3(color_name, "")
+            if mapset is None:
+                G_warning(_("3d raster map <%s> not found"),
+                          color_name)
+                GVL_delete_vol(id)
+                return -1
+            
+            Nviz_set_attr(id, MAP_OBJ_VOL, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(color_name, mapset), -1.0,
+                          self.data)
+        elif color_value:   # check for color value
+            Nviz_set_attr(id, MAP_OBJ_VOL, ATT_COLOR, CONST_ATT,
+                          None, Nviz_color_from_str(color_value),
+                          self.data)
+        else:               # use by default elevation map for coloring
+            Nviz_set_attr(id, MAP_OBJ_VOL, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(name, mapset), -1.0,
+                          self.data)
+        
+        Debug.msg(1, "Nviz::LoadVolume(): name=%s -> id=%d", name, id)
+        
+        return id
+
+    def UnloadVolume(self, id):
+        """!Unload volume
+        
+        @param id volume id
+        
+        @return 1 on success
+        @return 0 on failure
+        """
+        if not GVL_vol_exists(id):
+            return 0
+        
+        Debug.msg(1, "Nviz::UnloadVolume(): id=%d", id)
+        
+        if GVL_delete_vol(id) < 0:
+          return 0
+        
+        return 1
+    
+    def SetSurfaceTopo(self, id, map, value):
+        """!Set surface topography
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_TOPO, map, value)
+    
+    def SetSurfaceColor(self, id, map, value):
+        """!Set surface color
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_COLOR, map, value)
+    
+    def SetSurfaceMask(self, id, invert, value):
+        """!Set surface mask
+        
+        @todo invert
+        
+        @param id surface id
+        @param invert if true invert mask 
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_MASK, true, value)
+    
+    def SetSurfaceTransp(self, id, map, value):
+        """!Set surface mask
+        
+        @todo invert
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_TRANSP, map, value)
+    
+    def SetSurfaceShine(self, id, map, value):
+        """!Set surface shininess
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_SHINE, map, value)
+    
+    def SetSurfaceEmit(self, id, map, value):
+        """!Set surface emission
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_EMIT, map, value)
+    
+    def SetSurfaceAttr(self, id, attr, map, value):
+        """!Set surface attribute
+        
+        @param id surface id
+        @param attr attribute desc
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        if not GS_surf_exists(id):
+            return -1
+        
+        if map:
+            ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, MAP_ATT,
+                                value, -1.0, self.data)
+        else:
+            if attr == ATT_COLOR:
+                val = Nviz_color_from_str(value)
+            else:
+                val = float(value)
+            
+            ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, CONST_ATT,
+                                None, val, self.data)
+        
+        Debug.msg(3, "Nviz::SetSurfaceAttr(): id=%d, attr=%d, map=%d, value=%s",
+                  id, attr, map, value)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def UnsetSurfaceMask(self, id):
+        """!Unset surface mask
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        @return -1 on failure
+        """
+        return self.UnsetSurfaceAttr(id, ATT_MASK)
+    
+    def UnsetSurfaceTransp(self, id):
+        """!Unset surface transparency
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.UnsetSurfaceAttr(id, ATT_TRANSP)
+    
+    def UnsetSurfaceEmit(self, id):
+        """!Unset surface emission
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.UnsetSurfaceAttr(id, ATT_EMIT)
+    
+    def UnsetSurfaceAttr(self, id, attr):
+        """!Unset surface attribute
+        
+        @param id surface id
+        @param attr attribute descriptor
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        if not GS_surf_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::UnsetSurfaceAttr(): id=%d, attr=%d",
+                  id, attr)
+        
+        ret = Nviz_unset_attr(id, MAP_OBJ_SURF, attr)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+
+    def SetSurfaceRes(self, id, fine, coarse):
+        """!Set surface resolution
+        
+        @param id surface id
+        @param fine x/y fine resolution
+        @param coarse x/y coarse resolution
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        Debug.msg(3, "Nviz::SetSurfaceRes(): id=%d, fine=%d, coarse=%d",
+                  id, fine, coarse)
+        
+        if id > 0:
+            if not GS_surf_exists(id):
+                return -1
+            
+            if GS_set_drawres(id, fine, fine, coarse, coarse) < 0:
+                return -2
+        else:
+            GS_setall_drawres(fine, fine, coarse, coarse)
+        
+        return 1
+
+    def SetSurfaceStyle(self, id, style):
+        """!Set draw style
+        
+        Draw styles:
+         - DM_GOURAUD
+         - DM_FLAT
+         - DM_FRINGE
+         - DM_WIRE
+         - DM_COL_WIRE
+         - DM_POLY
+         - DM_WIRE_POLY
+         - DM_GRID_WIRE
+         - DM_GRID_SURF
+         
+        @param id surface id (<= 0 for all)
+        @param style draw style
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        Debug.msg(3, "Nviz::SetSurfaceStyle(): id=%d, style=%d",
+                  id, style)
+        
+        if id > 0:
+            if not GS_surf_exists(id):
+                return -1
+            
+            if GS_set_drawmode(id, style) < 0:
+                return -2
+            
+            return 1
+        
+        if GS_setall_drawmode(style) < 0:
+            return -2
+        
+        return 1
+    
+    def SetWireColor(self, id, color_str):
+        """!Set color of wire
+        
+        @todo all
+         
+        @param surface id (< 0 for all)
+        @param color color string (R:G:B)
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        @return 1 on success
+        @return 0 on failure
+        """
+        Debug.msg(3, "Nviz::SetWireColor(): id=%d, color=%s",
+                  id, color_str)
+        
+        color = Nviz_color_from_str(color_str)
+        
+        if id > 0:
+            if not GS_surf_exists(id):
+                return -1
+            
+            GS_set_wire_color(id, color)
+        else:
+            nsurfs = c_int()
+            surf_list = GS_get_surf_list(byref(nsurfs))
+            for i in xrange(nsurfs.value):
+                id = surf_list[i]
+                GS_set_wire_color(id, color)
+            
+            G_free(surf_list)
+            surf_list = None
+        
+        return 1
+    
+    def GetSurfacePosition(self, id):
+        """!Get surface position
+        
+        @param id surface id
+        
+        @return x,y,z
+        @return zero-length vector on error
+        """
+        if not GS_surf_exists(id):
+            return []
+        
+        x, y, z = c_float(), c_float(), c_float()
+        GS_get_trans(id, byref(x), byref(y), byref(z))
+        
+        Debug.msg(3, "Nviz::GetSurfacePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x.value, y.value, z.value)
+        
+        return [x.value, y.value, z.value]
+
+    def SetSurfacePosition(self, id, x, y, z):
+        """!Set surface position
+        
+        @param id surface id
+        @param x,y,z translation values
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting position failed
+        """
+        if not GS_surf_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetSurfacePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x, y, z)
+        
+        GS_set_trans(id, x, y, z)
+        
+        return 1
+
+    def SetVectorLineMode(self, id, color_str, width, flat):
+        """!Set mode of vector line overlay
+        
+        @param id vector id
+        @param color_str color string
+        @param width line width
+        @param flat display flat or on surface
+        
+        @return -1 vector set not found
+        @return -2 on failure
+        @return 1 on success
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorMode(): id=%d, color=%s, width=%d, flat=%d",
+                  id, color_str, width, flat)
+        
+        color = Nviz_color_from_str(color_str)
+        
+        # use memory by default
+        if GV_set_vectmode(id, 1, color, width, flat) < 0:
+            return -2
+        
+        return 1
+
+    def SetVectorLineHeight(self, id, height):
+        """!Set vector height above surface (lines)
+        
+        @param id vector set id
+        @param height
+        
+        @return -1 vector set not found
+        @return 1 on success
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorLineHeight(): id=%d, height=%f",
+                  id, height)
+        
+        GV_set_trans(id, 0.0, 0.0, height)
+        
+        return 1
+
+    def SetVectorLineSurface(self, id, surf_id):
+        """!Set reference surface of vector set (lines)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GV_select_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+
+    def SetVectorPointMode(self, id, color_str, width, size, marker):
+        """!Set mode of vector point overlay
+        
+        @param id vector id
+        @param color_str color string
+        @param width line width
+        @param flat
+        
+        @return -1 vector set not found
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorPointMode(): id=%d, color=%s, "
+                  "width=%d, size=%f, marker=%d",
+                  id, color_str, width, size, marker)
+        
+        color = Nviz_color_from_str(color_str)
+        
+        ### TODO
+        # if GP_set_style(id, color, width, size, marker) < 0:
+        #    return -2
+        
+        return 1
+
+    def SetVectorPointHeight(self, id, height):
+        """!Set vector height above surface (points)
+        
+        @param id vector set id
+        @param height
+        
+        @return -1 vector set not found
+        @return 1 on success
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorPointHeight(): id=%d, height=%f",
+                  id, height)
+        
+        GP_set_trans(id, 0.0, 0.0, height)
+        
+        return 1
+
+    def SetVectorPointSurface(self, id, surf_id):
+        """!Set reference surface of vector set (points)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GP_select_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+
+    def AddIsosurface(self, id, level):
+        """!Add new isosurface
+        
+        @param id volume id
+        @param level isosurface level (topography)
+        
+        @return -1 on failure
+        @return 1 on success
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if GVL_isosurf_add(id) < 0:
+            return -1
+        
+        # set topography level
+        nisosurfs = GVL_isosurf_num_isosurfs(id)
+        
+        return GVL_isosurf_set_att_const(id, nisosurfs - 1, ATT_TOPO, level)
+    
+    def DeleteIsosurface(self, id, isosurf_id):
+        """!Delete isosurface
+        
+        @param id volume id
+        @param isosurf_id isosurface id
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id):
+            return -2
+        
+        ret = GVL_isosurf_del(id, isosurf_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+    
+    def MoveIsosurface(self, id, isosurf_id, up):
+        """!Move isosurface up/down in the list
+        
+        @param id volume id
+        @param isosurf_id isosurface id
+        @param up if true move up otherwise down
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id):
+            return -2
+        
+        if up:
+            ret = GVL_isosurf_move_up(id, isosurf_id)
+        else:
+            ret = GVL_isosurf_move_down(id, isosurf_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+
+    def SetIsosurfaceColor(self, id, isosurf_id, map, value):
+        """!Set isosurface color
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_COLOR, map, value)
+    
+    def SetIsosurfaceMask(self, id, isosurf_id, invert, value):
+        """!Set isosurface mask
+        
+        @todo invert
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param invert true for invert mask
+        @param value map name to be used for mask
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_MASK, true, value)
+    
+    def SetIsosurfaceTransp(self, id, isosurf_id, map, value):
+        """!Set isosurface transparency
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP, map, value)
+    
+    def SetIsosurfaceShine(self, id, isosurf_id, map, value):
+        """!Set isosurface shininess
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_SHINE, map, value)
+    
+    def SetIsosurfaceEmit(self, id, isosurf_id, map, value):
+        """!Set isosurface emission
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_EMIT, map, value)
+    
+    def SetIsosurfaceAttr(self, id, isosurf_id, attr, map, value):
+        """!Set isosurface attribute
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param attr attribute desc
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1:
+            return -2
+        
+        if map:
+            ret = GVL_isosurf_set_att_map(id, isosurf_id, attr, value)
+        else:
+            if attr == ATT_COLOR:
+                val = Nviz_color_from_str(value)
+            else:
+                val = float(value)
+            
+            ret = GVL_isosurf_set_att_const(id, isosurf_id, attr, val)
+        
+        Debug.msg(3, "Nviz::SetIsosurfaceAttr(): id=%d, isosurf=%d, "
+                  "attr=%d, map=%d, value=%s",
+                  id, isosurf_id, attr, map, value)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def UnsetIsosurfaceMask(self, id, isosurf_id):
+        """!Unset isosurface mask
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_MASK)
+    
+    def UnsetIsosurfaceTransp(self, id, isosurf_id):
+        """!Unset isosurface transparency
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP)
+    
+    def UnsetIsosurfaceEmit(self, id, isosurf_id):
+        """!Unset isosurface emission
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_EMIT)
+    
+    def UnsetIsosurfaceAttr(self, id, isosurf_id, attr):
+        """!Unset surface attribute
+        
+        @param id surface id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param attr attribute descriptor
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1:
+            return -2
+        
+        Debug.msg(3, "Nviz::UnsetSurfaceAttr(): id=%d, isosurf_id=%d, attr=%d",
+                  id, isosurf_id, attr)
+        
+        ret = GVL_isosurf_unset_att(id, isosurf_id, attr)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+
+    def SetIsosurfaceMode(self, id, mode):
+        """!Set draw mode for isosurfaces
+        
+        @param mode
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_isosurf_set_drawmode(id, mode)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetIsosurfaceRes(self, id, res):
+        """!Set draw resolution for isosurfaces
+        
+        @param res resolution value
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_isosurf_set_drawres(id, res, res, res)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+
+    def SaveToFile(self, filename, width = 20, height = 20, itype = 'ppm'):
+        """!Save current GL screen to ppm/tif file
+
+        @param filename file name
+        @param width image width
+        @param height image height
+        @param itype image type ('ppm' or 'tif')
+        """
+        widthOrig  = self.width
+        heightOrig = self.height
+        
+        self.ResizeWindow(width, height)
+        GS_clear(Nviz_get_bgcolor(self.data))
+        self.Draw(False, -1)
+        if itype == 'ppm':
+            GS_write_ppm(filename)
+        else:
+            GS_write_tif(filename)
+        
+        self.ResizeWindow(widthOrig, heightOrig)
+
+    def DrawLightingModel(self):
+        """!Draw lighting model"""
+        if self.showLight:
+            GS_draw_lighting_model()
+
+    def SetFringe(self, sid, color, elev, nw = False, ne = False, sw = False, se = False):
+        """!Set fringe
+
+        @param sid surface id
+        @param color color
+        @param elev elevation (height)
+        @param nw,ne,sw,se fringe edges (turn on/off)
+        """
+        scolor = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+
+        Nviz_set_fringe(self.data,
+                        sid, Nviz_color_from_str(scolor),
+                        elev, int(nw), int(ne), int(sw), int(se))
+        
+    def GetPointOnSurface(self, sx, sy):
+        """!Get point on surface
+
+        @param sx,sy canvas coordinates (LL)
+        """
+        sid = c_int()
+        x   = c_float()
+        y   = c_float()
+        z   = c_float()
+        Debug.msg(5, "GLWindow.GetPointOnSurface(): sx=%d sy=%d" % (sx, sy))
+        num = GS_get_selected_point_on_surface(sx, sy, byref(sid), byref(x), byref(y), byref(z))
+        if num == 0:
+            return (None, None, None, None)
+        
+        return (sid.value, x.value, y.value, z.value)
+
+    def QueryMap(self, sx, sy):
+        """!Query surface map
+
+        @param sx,sy canvas coordinates (LL)
+        """
+        sid, x, y, z = self.GetPointOnSurface(sx, sy)
+        if not sid:
+            return None
+        
+        catstr = create_string_buffer(256)
+        valstr = create_string_buffer(256)
+        GS_get_cat_at_xy(sid, ATT_TOPO, catstr, x, y)
+        GS_get_val_at_xy(sid, ATT_COLOR, valstr, x, y)
+        
+        return { 'id' : sid,
+                 'x'  : x,
+                 'y'  : y,
+                 'z'  : z,
+                 'elevation' : catstr.value.replace('(', '').replace(')', ''),
+                 'color'     : valstr.value }
+    
+    def GetDistanceAlongSurface(self, sid, p1, p2, useExag = True):
+        """!Get distance measured along surface"""
+        d = c_float()
+        
+        GS_get_distance_alongsurf(sid, p1[0], p1[1], p2[0], p2[1],
+                                  byref(d), int(useExag))
+        
+        return d.value


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/wxnviz.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,6 +1,7 @@
 """
 New GRASS icon set
 http://robert.szczepanek.pl/icons.php
+https://svn.osgeo.org/osgeo/graphics/toolbar-icons/24x24/
 """
 __author__ = "Robert Szczepanek"
 
@@ -21,10 +22,10 @@
     "printmap"   : 'print.png',
     "pan"        : 'pan.png', 
     # zoom (mapdisplay)
-    "zoom_in"    : 'zoom-in.png',
-    "zoom_out"   : 'zoom-out.png',
-    "zoom_back"  : 'zoom-last.png',
-    "zoommenu"   : 'zoom-more.png',
+    "zoom_in"     : 'zoom-in.png',
+    "zoom_out"    : 'zoom-out.png',
+    "zoom_back"   : 'zoom-last.png',
+    "zoommenu"    : 'zoom-more.png',
     "zoom_extent" : 'zoom-extent.png',
     # analyze raster (mapdisplay)
     "analyze"    : 'layer-raster-analyze.png',
@@ -60,14 +61,13 @@
     "digDispAttr" : 'attributes-display.png',
     ## general
     "digUndo" : 'undo.png',
-    "digSettings" : 'settings.png',
     "digAdditionalTools" : 'tools.png',
     # layer manager
     "newdisplay" : 'monitor-create.png',
-    "workspaceNew" : 'create.png',
-    "workspaceLoad" : 'layer-open.png',
-    "workspaceOpen" : 'open.png',
-    "workspaceSave" : 'save.png',
+    "fileNew"    : 'create.png',
+    "fileLoad"   : 'layer-open.png',
+    "fileOpen"   : 'open.png',
+    "fileSave"   : 'save.png',
     "addrast"    : 'layer-raster-add.png',
     "addrast3d"  : 'layer-raster3d-add.png',
     "addshaded"  : 'layer-shaded-relief-add.png',
@@ -86,21 +86,38 @@
     "addthematic": 'layer-vector-thematic-add.png',
     "addchart"   : 'layer-vector-chart-add.png',
     "layeropts"  : 'options.png',
+    "modeler"    : 'modeler-main.png',
     # profile analysis
     "transect"     : 'layer-raster-profile.png',
     "profiledraw"  : 'show.png',
-    "profileopt"   : 'settings.png',
     # georectify
     "grGcpSet"     : 'gcp-create.png',
     'grGcpClear'   : 'gcp-remove.png',
     'grGeorect'    : 'georectify.png',
     'grGcpRms'     : 'gcp-rms.png',
-    'grGcpRefresh' : 'redraw.png',
     "grGcpSave"    : 'gcp-save.png',
     "grGcpAdd"     : 'gcp-add.png',
     "grGcpDelete"  : 'gcp-delete.png',
     "grGcpReload"  : 'reload.png',
-    "grSettings"   : 'settings.png',
-    # nviz
-    "nvizSettings"   : 'settings.png',
+    # modeler
+    "modelActionAdd" : 'module-add.png',
+    "modelDataAdd"   : 'data-add.png',
+    "modelRelation"  : 'relation-create.png',
+    "modelRun"       : 'execute.png',
+    "modelValidate"  : 'check.png',
+    "imageSave"      : 'image-export.png',
+    "pythonSave"     : 'python-export.png',
+    "modelProperties" : 'options.png',
+    "modelVariables" : 'modeler-variables.png',
+    # 3d view
+    "nvizView"       : '3d-view.png',
+    "nvizSurface"    : '3d-raster.png',
+    "nvizVector"     : '3d-vector.png',
+    "nvizVolume"     : '3d-volume.png',
+    "nvizLight"      : '3d-light.png',
+    "nvizFringe"     : '3d-fringe.png',
+    # various
+    "settings"       : 'settings.png',
+    "redraw"         : 'redraw.png',
+    "help"           : 'help.png',
     }

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -49,14 +49,13 @@
     "digDispAttr" : 'display.attributes.gif',
     ## general
     "digUndo" : wx.ART_ERROR, # FIXME
-    "digSettings" : 'settings.gif',
     "digAdditionalTools" : wx.ART_ERROR, # FIXME
     # layer manager
     "newdisplay" : 'gui-startmon.gif',
-    "workspaceNew" : 'file-new.gif',
-    "workspaceLoad" : 'file-new.gif', # change the icon if possible
-    "workspaceOpen" : 'file-open.gif',
-    "workspaceSave" : 'file-save.gif',
+    "fileNew"    : 'file-new.gif',
+    "fileLoad"   : 'file-new.gif', # change the icon if possible
+    "fileOpen"   : 'file-open.gif',
+    "fileSave"   : 'file-save.gif',
     "addrast"    : 'element-cell.gif',
     "addrast3d"  : 'element-grid3.gif',
     "addvect"    : 'element-vector.gif',
@@ -80,6 +79,7 @@
     "addbarscale": 'module-d.barscale.gif',
     "addlegend"  : 'module-d.legend.gif',
     "quit"       : 'gui-exit.gif',
+    "modeler"    : wx.ART_ERROR,
     # analyze raster
     "analyze"    : 'gui-rastanalyze.gif',
     "measure"    : 'gui-measure.gif',
@@ -90,7 +90,6 @@
     # profile 
     "profile"    : 'gui-profile.gif',
     "transect"   : 'gui-profiledefine.gif',
-    # "profiledraw": 'gui-profiledraw.gif',
     "profiledraw" : 'gui-display.gif',
     "profileopt" : 'gui-profileopt.gif',
     # georectify
@@ -98,12 +97,29 @@
     'grGcpSet'     : 'gui-gcpset.gif',
     'grGeorect'    : 'gui-georect.gif',
     'grGcpRms'     : 'gui-rms.gif',
-    'grGcpRefresh' : 'gui-display.gif',
     "grGcpSave"    : 'file-save.gif', 
     "grGcpAdd"     : wx.ART_NEW, # FIXME
     "grGcpDelete"  : wx.ART_DELETE, # FIXME
     "grGcpReload"  : 'gui-redraw.gif',
-    "grSettings"   : 'edit-color.gif', 
-    # nviz 
-    "nvizSettings" : 'edit-color.gif',   
+    # modeler
+    "modelActionAdd" : wx.ART_ERROR,
+    "modelDataAdd"   : wx.ART_ERROR,
+    "modelRelation"  : wx.ART_ERROR,
+    "modelRun"       : wx.ART_ERROR,
+    "modelValidate"  : wx.ART_ERROR,
+    "imageSave"      : wx.ART_ERROR,
+    "pythonSave"     : wx.ART_ERROR,
+    "modelProperties" : wx.ART_ERROR,
+    "modelVariables" : wx.ART_ERROR,
+    # 3d view
+    "nvizView"       : wx.ART_ERROR,
+    "nvizSurface"    : wx.ART_ERROR,
+    "nvizVector"     : wx.ART_ERROR,
+    "nvizVolume"     : wx.ART_ERROR,
+    "nvizLight"      : wx.ART_ERROR,
+    "nvizFringe"     : wx.ART_ERROR,
+    # various
+    "settings"       : 'edit-color.gif',
+    "redraw"         : 'gui-display.gif',
+    "help"           : wx.ART_ERROR,
     }

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,4 +1,4 @@
-"""
+"""!
 @package icon
 
 @brief Icon themes
@@ -10,7 +10,7 @@
 Classes:
  - MetaIcon
 
-(C) 2007-2008 by the GRASS Development Team
+(C) 2007-2008, 2010 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.
@@ -21,10 +21,8 @@
 import os
 import sys
 
-gmPath = os.path.join(os.getenv("GISBASE"), "etc", "wxpython", "gui_modules")
-sys.path.append(gmPath)
+sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "wxpython", "gui_modules"))
 
-import grassenv
 import globalvar
 if not os.getenv("GRASS_WXBUNDLED"):
     globalvar.CheckForWx()
@@ -83,10 +81,9 @@
     sys.exit(1)
 
 class MetaIcon:
+    """!Handle icon metadata (image path, tooltip, ...)
     """
-    Handle icon metadata (image path, tooltip, ...)
-    """
-    def __init__(self, img, label, desc=None):
+    def __init__(self, img, label, desc = None):
         self.imagepath = img
         if not self.imagepath:
             self.type = 'unknown'
@@ -95,41 +92,41 @@
                 self.type = 'wx'
             else:
                 self.type = 'img'
-
+        
         self.label = label
-
+        
         if desc:
             self.description = desc
         else:
             self.description = ''
-
+        
     def __str__(self):
-        """Debugging"""
+        """!Debugging"""
         return "label=%s, img=%s, type=%s" % (self.label, self.imagepath, self.type)
 
-    def GetBitmap(self, size=None):
-        """Get bitmap"""
+    def GetBitmap(self, size = None):
+        """!Get bitmap"""
         bmp = None
-
+        
         if self.type == 'wx':
-            bmp = wx.ArtProvider.GetBitmap(id=self.imagepath, client=wx.ART_TOOLBAR, size=size)
+            bmp = wx.ArtProvider.GetBitmap(id = self.imagepath, client = wx.ART_TOOLBAR, size = size)
         elif self.type == 'img':
             if os.path.isfile(self.imagepath) and os.path.getsize(self.imagepath):
                 if size and len(size) == 2:
-                    image = wx.Image (name=self.imagepath)
-                    image.Rescale (size[0], size[1])
+                    image = wx.Image(name = self.imagepath)
+                    image.Rescale(size[0], size[1])
                     bmp = image.ConvertToBitmap()
                 elif self.imagepath:
-                    bmp = wx.Bitmap (name=self.imagepath)
-
+                    bmp = wx.Bitmap(name = self.imagepath)
+        
         return bmp
-
+    
     def GetLabel(self):
         return self.label
-
+    
     def GetDesc(self):
         return self.description
-
+    
     def GetImageName(self):
         return os.path.basename(self.imagepath)
 
@@ -139,10 +136,11 @@
 Icons = {
     # map display
     "displaymap" : MetaIcon (img=Icons["displaymap"],
-                             label=_("Display map")),
+                             label=_("Display map"),
+                             desc = _("Re-render modified map layers")),
     "rendermap"  : MetaIcon (img=Icons["rendermap"],
                              label=_("Re-render map"),
-                             desc=_("Force re-rendering of all layers")),
+                             desc=_("Force re-rendering all map layers")),
     "erase"      : MetaIcon (img=Icons["erase"],
                              label=_("Erase display")),
     "pointer"    : MetaIcon (img=Icons["pointer"],
@@ -177,38 +175,37 @@
                              label=_("Save display to graphic file")),
     "printmap"   : MetaIcon (img=Icons["printmap"],
                              label=_("Print display")),
-    # gis manager
+    # layer manager
     "newdisplay" : MetaIcon (img=Icons["newdisplay"],
                              label=_("Start new display")),
-    "workspaceNew" : MetaIcon (img=Icons["workspaceNew"],
-                               label=_("Create new workspace file")),
-    "workspaceLoad" : MetaIcon (img=Icons["workspaceLoad"],
-                                label=_("Load map layers into workspace")),
-    "workspaceOpen" : MetaIcon (img=Icons["workspaceOpen"],
-                                label=_("Open existing workspace file")),
-    "workspaceSave" : MetaIcon (img=Icons["workspaceSave"],
-                                label=_("Save current workspace to file")),
-    # TODO: "layer" is not conformant with GRASS vocabulary (vector layer: 1..x) ! 
+    "workspaceNew" : MetaIcon (img=Icons["fileNew"],
+                               label=_("Create new workspace file (Ctrl+N)")),
+    "workspaceLoad" : MetaIcon (img=Icons["fileLoad"],
+                                label=_("Load map layers into workspace (Ctrl+L)")),
+    "workspaceOpen" : MetaIcon (img=Icons["fileOpen"],
+                                label=_("Open existing workspace file (Ctrl+O)")),
+    "workspaceSave" : MetaIcon (img=Icons["fileSave"],
+                                label=_("Save current workspace to file (Ctrl+S)")),
     "addrast"    : MetaIcon (img=Icons["addrast"],
-                             label=_("Add raster map layer")),
+                             label=_("Add raster map layer (Ctrl+R)")),
     "addvect"    : MetaIcon (img=Icons["addvect"],
-                             label=_("Add vector map layer")),
+                             label=_("Add vector map layer (Ctrl+V)")),
     "addcmd"     : MetaIcon (img=Icons["addcmd"],
                              label=_("Add command layer")),
     "addgrp"     : MetaIcon (img=Icons["addgrp"],
-                             label=_("Add layer group")),
+                             label=_("Add group")),
     "addovl"     : MetaIcon (img=Icons["addovl"],
                              label=_("Add grid or vector labels overlay")),
     "delcmd"     : MetaIcon (img=Icons["delcmd"],
-                             label=_("Delete selected layer")),
+                             label=_("Delete selected map layer")),
     "quit"       : MetaIcon (img=Icons["quit"],
                              label=_("Quit")),
     "attrtable"  : MetaIcon (img=Icons["attrtable"],
                              label=_("Show attribute table")),
     "addrgb"     : MetaIcon (img=Icons["addrgb"],
-                             label=_("Add RGB layer")),
+                             label=_("Add RGB map layer")),
     "addhis"     : MetaIcon (img=Icons["addhis"],
-                             label=_("Add HIS layer")),
+                             label=_("Add HIS map layer")),
     "addshaded"  : MetaIcon (img=Icons["addshaded"],
                              label=_("Add shaded relief map layer")),
     "addrarrow"  : MetaIcon (img=Icons["addrarrow"],
@@ -230,7 +227,10 @@
     "addtext"    : MetaIcon (img=Icons["addtext"],
                              label=_("Add text layer")),
     "addrast3d"  : MetaIcon (img=Icons["addrast3d"],
-                             label=_("Add 3D raster map")),
+                             label=_("Add 3D raster map layer"),
+                             desc = _("Note that 3D raster data are rendered only in 3D view mode")),
+    "settings"   : MetaIcon (img=Icons["settings"],
+                             label=_("Show GUI settings")),
     # digit
     "digAddPoint": MetaIcon (img=Icons["digAddPoint"],
                              label=_("Digitize new point"),
@@ -271,7 +271,7 @@
     "digRemoveVertex": MetaIcon (img=Icons["digRemoveVertex"],
                                  label=_("Remove vertex"),
                                  desc=_("Left: Select; Middle: Unselect; Right: Confirm")),
-    "digSettings": MetaIcon (img=Icons["digSettings"],
+    "digSettings": MetaIcon (img=Icons["settings"],
                              label=_("Settings"),
                              desc=_("Settings dialog for digitization tool")),
     "digSplitLine": MetaIcon (img=Icons["digSplitLine"],
@@ -297,8 +297,10 @@
                              label=_("Profile surface map")),
     "profiledraw": MetaIcon (img=Icons["profiledraw"],
                              label=_("Draw/re-draw profile")),
-    "profileopt" : MetaIcon (img=Icons["profileopt"],
+    "profileopt" : MetaIcon (img=Icons["settings"],
                              label=_("Profile options")),
+    "datasave"   : MetaIcon (img=Icons["fileSave"],
+                             label=_("Save profile data to csv file")),
     "histogram"  : MetaIcon (img=Icons["histogram"],
                              label=_("Create histogram of image or raster file")),
     "font"       : MetaIcon (img=Icons["font"],
@@ -317,8 +319,6 @@
                              label=_("Georectify")),
     'grGcpRms'        : MetaIcon (img=Icons["grGcpRms"],
                                   label=_("Recalculate RMS error")),
-    'grGcpRefresh' : MetaIcon (img=Icons["grGcpRefresh"],
-                               label=_("Redraw GCP markers in map displays")),
     'grGcpSave' : MetaIcon (img=Icons["grGcpSave"],
                             label=_("Save GCPs to POINTS file")),
     'grGcpAdd' : MetaIcon (img=Icons["grGcpAdd"],
@@ -328,20 +328,77 @@
     'grGcpClear' : MetaIcon (img=Icons["grGcpClear"],
                              label=_("Clear selected GCP")),
     'grGcpReload' : MetaIcon (img=Icons["grGcpReload"],
-                              label=_("Reload GCPs from selected POINTS file")),
+                              label=_("Reload GCPs from POINTS file")),
     'grGcpQuit' : MetaIcon (img=Icons["quit"],
                             label=_("Quit georectification module")),
-    "grSettings": MetaIcon (img=Icons["grSettings"],
+    "grSettings": MetaIcon (img=Icons["settings"],
                             label=_("Settings"),
                             desc=_("Settings dialog for georectification tool")),
     # nviz
-    "nvizSettings": MetaIcon (img=Icons["nvizSettings"],
-                              label=_("Settings"),
-                              desc=_("Show Nviz settings dialog")),
+    "nvizView": MetaIcon (img=Icons["nvizView"],
+                          label=_("Switch to view control page"),
+                          desc=_("Change view settings")),
+    "nvizSurface": MetaIcon (img=Icons["nvizSurface"],
+                             label=_("Switch to surface (raster) control page"),
+                             desc=_("Change surface (loaded raster maps) settings")),
+    "nvizVector": MetaIcon (img=Icons["nvizVector"],
+                            label=_("Switch to vector (2D/3D) control page"),
+                            desc=_("Change 2D/3D vector settings")),
+    "nvizVolume": MetaIcon (img=Icons["nvizVolume"],
+                            label=_("Switch to volume (3D raster) control page"),
+                            desc=_("Change volume (loaded 3D raster maps) settings")),
+    "nvizLight": MetaIcon (img=Icons["nvizLight"],
+                           label=_("Switch to lighting control page"),
+                           desc=_("Change lighting settings")),
+    "nvizFringe": MetaIcon (img=Icons["nvizFringe"],
+                            label=_("Switch to fringe control page"),
+                            desc=_("Switch on/off fringes")),
+    "nvizSettings": MetaIcon (img=Icons["settings"],
+                              label=_("3D view mode tools"),
+                              desc=_("Show/hide 3D view mode settings dialog")),
+    "nvizHelp"   : MetaIcon (img=Icons["help"],
+                             label=_("Show help"),
+                             desc = _("Display 3D view mode manual page")),
+    "nvizQuit": MetaIcon (img=Icons["quit"],
+                          label=_("Quit 3D view mode"),
+                          desc=_("Switch back to 2D view mode")),
+    # modeler
+    "modeler" : MetaIcon (img=Icons["modeler"],
+                          label=_("Start Graphical Modeler")),
+    "modelNew" : MetaIcon (img=Icons["fileNew"],
+                           label=_("Create new model (Ctrl+N)")),
+    "modelOpen" : MetaIcon (img=Icons["fileOpen"],
+                                label=_("Load model from file (Ctrl+O)")),
+    "modelSave" : MetaIcon (img=Icons["fileSave"],
+                                label=_("Save current model to file (Ctrl+S)")),
+    "modelToImage" : MetaIcon (img=Icons["imageSave"],
+                                label=_("Export model to image")),
+    "modelToPython" : MetaIcon (img=Icons["pythonSave"],
+                                label=_("Export model to Python script")),
+    "modelActionAdd" : MetaIcon (img=Icons["modelActionAdd"],
+                                 label=_("Add action (GRASS module) to model")),
+    "modelDataAdd" : MetaIcon (img=Icons["modelDataAdd"],
+                                 label=_("Add data item to model")),
+    "modelRelation" : MetaIcon (img=Icons["modelRelation"],
+                                label=_("Define relation between data and action items")),
+    "modelRun" : MetaIcon (img=Icons["modelRun"],
+                           label=_("Run model")),
+    "modelValidate" : MetaIcon (img=Icons["modelValidate"],
+                                label=_("Validate model")),
+    "modelSettings" : MetaIcon (img=Icons["settings"],
+                                label=_("Show modeler settings")),
+    "modelProperties" : MetaIcon (img=Icons["modelProperties"],
+                                  label=_("Show model properties")),
+    "modelVariables" : MetaIcon (img=Icons["modelVariables"],
+                                 label=_("Manage model variables")),
+    "modelRedraw" : MetaIcon (img=Icons["redraw"],
+                              label=_("Redraw model canvas")),
+    "modelHelp"   : MetaIcon (img=Icons["help"],
+                             label=_("Show help"),
+                             desc = _("Display Graphical Modeler manual page")),
     }
 
 # testing ...
 if __name__ == "__main__":
     for k, v in Icons.iteritems():
         print v.GetImageName()
-

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -8,6 +8,8 @@
 
 import os
 
+import wx
+
 import globalvar
 
 iconpath = os.path.join(globalvar.ETCDIR, "gui", "icons", "silk")
@@ -62,14 +64,13 @@
     "digDispAttr" : 'table.png',
     ## general
     "digUndo" : 'arrow_undo.png',
-    "digSettings" : 'color_swatch.png',
     "digAdditionalTools" : 'plugin.png',
     # layer manager
     "newdisplay" : 'application_add.png',
-    "workspaceNew" : 'page_white.png',
-    "workspaceLoad" : 'page_white_get.png',
-    "workspaceOpen" : 'folder.png',
-    "workspaceSave" : 'page_save.png',
+    "fileNew"    : 'page_white.png',
+    "fileLoad"   : 'page_white_get.png',
+    "fileOpen"   : 'folder.png',
+    "fileSave"   : 'page_save.png',
     "addrast"    : 'image_add.png',
     "addrast3d"  : 'bricks.png',
     "addshaded"  : 'picture_empty.png',
@@ -88,6 +89,7 @@
     "addthematic": 'thematic.png',
     "addchart"   : 'chart_bar.png',
     "layeropts"  : 'map_edit.png',
+    "modeler"    : wx.ART_ERROR,
     # profile analysis
     "transect"   : 'image_edit.png',
     "profiledraw"  : 'arrow_refresh.png',
@@ -97,12 +99,29 @@
     'grGcpClear'   : 'cross.png',
     'grGeorect'    : 'application_lightning.png',
     'grGcpRms'     : 'error.png',
-    'grGcpRefresh' : 'arrow_refresh.png',
     "grGcpSave"    : 'picture_save.png',
     "grGcpAdd"     : 'bullet_add.png', 
     "grGcpDelete"  : 'bullet_delete.png',
     "grGcpReload"  : 'arrow_refresh.png',
-    "grSettings"   : 'color_swatch.png',
-    # nviz
-    "nvizSettings"   : 'color_swatch.png',
+    # modeler
+    "modelActionAdd" : wx.ART_ERROR,
+    "modelDataAdd"   : wx.ART_ERROR,
+    "modelRelation"  : wx.ART_ERROR,
+    "modelRun"       : wx.ART_ERROR,
+    "modelValidate"  : wx.ART_ERROR,
+    "imageSave"      : wx.ART_ERROR,
+    "pythonSave"     : wx.ART_ERROR,
+    "modelProperties" : wx.ART_ERROR,
+    "modelVariables" : wx.ART_ERROR,
+    # 3d view
+    "nvizView"       : wx.ART_ERROR,
+    "nvizSurface"    : wx.ART_ERROR,
+    "nvizVector"     : wx.ART_ERROR,
+    "nvizVolume"     : wx.ART_ERROR,
+    "nvizLight"      : wx.ART_ERROR,
+    "nvizFringe"     : wx.ART_ERROR,
+    # various
+    "settings"       : 'color_swatch.png',
+    "redraw"         : 'arrow_refresh.png',
+    "help"           : wx.ART_ERROR,
     }

Modified: grass/branches/releasebranch_6_4/gui/wxpython/scripts/Makefile
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/Makefile	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/Makefile	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,30 +1,26 @@
 MODULE_TOPDIR = ../../..
 
-include $(MODULE_TOPDIR)/include/Make/Platform.make
 include $(MODULE_TOPDIR)/include/Make/Dir.make
+include $(MODULE_TOPDIR)/include/Make/Python.make
 
-ETCDIR = $(ETC)/gui/scripts
+SCRIPTDIR = $(ETC)/wxpython/scripts
 
-default: install_scripts
-
 ifdef MINGW
-SCRIPT_ACTIONS += create.bat
-PYEXT = .py
+PYFILES := $(patsubst %.py, $(SCRIPTDIR)/%.py, $(filter %.py, $(PY_SOURCES)))
 else
-PYEXT = 
+PYFILES := $(patsubst %.py, $(SCRIPTDIR)/%, $(filter %.py, $(PY_SOURCES)))
 endif
 
-install_scripts: $(SCRIPT_ACTIONS)
-	if [ ! -d $(ETC)/gui ] ; then $(MKDIR) $(ETC)/gui ; fi
-	if [ ! -d $(ETCDIR) ] ; then $(MKDIR) $(ETCDIR) ; fi
-	$(INSTALL) d.rast3d $(ETCDIR)/d.rast3d$(PYEXT)
+default: install_scripts
 
-create.bat:
-	if [ ! -d $(ETC)/wxpython ] ; then $(MKDIR) $(ETC)/wxpython ; fi
-	if [ ! -d $(ETC)/wxpython/scripts ] ; then $(MKDIR) $(ETC)/wxpython/scripts ; fi
-	for file in p.* ; do \
-	sed -e "s#SCRIPT_NAME#$$file#" $(MODULE_TOPDIR)/gui/scripts/windows_launch.bat \
-		| sed -e "s#etc/gui/#etc/wxpython/#" \
-		> $(ETC)/wxpython/scripts/$$file.bat ; done
+install_scripts:
+	$(MAKE) $(PYFILES)
 
-clean: cleansubdirs
+$(SCRIPTDIR)/%: %.py | $(SCRIPTDIR)
+	$(INSTALL) $< $@
+
+$(SCRIPTDIR)/%.py: %.py | $(SCRIPTDIR)
+	$(INSTALL) $< $@
+
+$(SCRIPTDIR):
+	-test -d $@ | $(MKDIR) $@

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/d.rast3d.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/d.rast3d.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/d.rast3d.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#
+############################################################################
+#
+# MODULE:       d.rast3d
+#
+# AUTHOR(S):    Martin Landa <landa.martin gmail.com>
+#
+# PURPOSE:	Wrapper for wxGUI to add 3D raster map into layer tree
+#
+# COPYRIGHT:	(C) 2008, 2010 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.
+#
+#############################################################################
+
+#%module
+#% description: Displays 3D raster map layer.
+#% keywords: display, raster3d
+#%end
+
+#%option
+#% key: map
+#% type: string
+#% gisprompt: old,grid3,3d-raster
+#% description: 3D raster map to be displayed
+#% required : yes
+#%end
+
+from grass.script import core as grass
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/d.rast3d.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.cmd.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.cmd.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.cmd.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+############################################################################
+#
+# MODULE:       p.cmd
+# AUTHOR(S):    Martin Landa, Hamish Bowman
+#               Converted to Python by Huidae Cho
+# PURPOSE:      Wrapper for display commands and pX monitors
+# COPYRIGHT:    (C) 2009 by The GRASS Development Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+############################################################################
+
+#%Module
+#% description: Wrapper for display commands and pX monitors
+#% keywords: display
+#%End
+
+#%Option
+#% key: cmd
+#% type: string
+#% required: yes
+#% multiple: no
+#% label: Command to be performed
+#% description: Example: "d.rast map=elevation.dem at PERMANENT catlist=1300-1400 -i"
+#%End
+
+#%Option
+#% key: opacity
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: val
+#% description: Opacity level in percentage
+#% answer: 100
+#%End
+
+import os
+import grass.script as grass
+
+def main():
+    cmd_file = grass.gisenv()["GRASS_PYCMDFILE"]
+
+    if cmd_file == "" or os.path.exists(cmd_file) == False:
+        grass.message(_("GRASS_PYCMDFILE - File not found. Run p.mon."), "e")
+        return
+
+    cmd = options["cmd"]
+    opacity = options["opacity"]
+
+    fp = open(cmd_file, "a")
+    fp.write("%s opacity=%s\n" % (cmd, opacity))
+    fp.close()
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.cmd.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.db.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.db.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.db.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+############################################################################
+#
+# MODULE:       p.db
+# AUTHOR(S):    Jachym Cepicky, Markus Neteler, Hamish Bowman
+#               Converted to Python by Huidae Cho
+# PURPOSE:      Start stand-alone attribute table manager
+# COPYRIGHT:    (C) 2009 by The GRASS Development Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+############################################################################
+
+#%Module
+#% description: Start stand-alone attribute table manager
+#% keywords: database
+#%End
+#%Option
+#% key: table
+#% type: string
+#% required: yes
+#% multiple: no
+#% description: Table name
+#%End
+
+import os
+import grass.script as grass
+
+def main():
+    table = options["table"]
+
+    os.spawnlp(os.P_NOWAIT, os.environ["GRASS_PYTHON"], os.environ["GRASS_PYTHON"], "%s/etc/wxpython/gui_modules/dbm.py" % os.environ["GISBASE"], table)
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.db.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.mon.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.mon.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.mon.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+############################################################################
+#
+# MODULE:       p.mon
+# AUTHOR(S):    Jachym Cepicky, Michael Barton, Martin Landa, Markus Neteler,
+#               Hamish Bowman
+#               Converted to Python by Huidae Cho
+# PURPOSE:      To establish and control use of a graphics display monitor.
+# COPYRIGHT:    (C) 2009 by The GRASS Development Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+############################################################################
+
+#%Module
+#% description: To establish and control use of a graphics display monitor.
+#% keywords: display
+#%End
+
+##%Flag
+##% key: l
+##% description: List all monitors
+##%End
+
+##%Flag
+##% key: L
+##% description: List all monitors (with current status)
+##%End
+
+##%Flag
+##% key: p
+##% description: Print name of currently selected monitor
+##%End
+
+##%Flag
+##% key: r
+##% description: Release currently selected monitor
+##%End
+
+##%Flag
+##% key: s
+##% description: Do not automatically select when starting
+##%End
+
+#%Option
+#% key: start
+#% type: string
+#% required: no
+#% multiple: no
+#% description: Name of graphics monitor to start (p0-p9)
+#%End
+
+##%Option
+##% key: stop
+##% type: string
+##% required: no
+##% multiple: no
+##% description: Name of graphics monitor to stop
+##%End
+
+##%Option
+##% key: select
+##% type: string
+##% required: no
+##% multiple: no
+##% description: Name of graphics monitor to select
+##%End
+
+##%Option
+##% key: unlock
+##% type: string
+##% required: no
+##% multiple: no
+##% description: Name of graphics monitor to unlock
+##%End
+
+import os
+import grass.script as grass
+
+def main():
+    start = options["start"]
+#    select = options["select"]
+#    stop = options["stop"]
+#    unlock = options["unlock"]
+
+    # create the command file
+    command_file = grass.tempfile()
+    os.system("g.gisenv set=GRASS_PYCMDFILE=%s" % command_file)
+
+    if start != "":
+        os.spawnlp(os.P_NOWAIT, os.environ["GRASS_PYTHON"], os.environ["GRASS_PYTHON"], "%s/etc/wxpython/gui_modules/mapdisp.py" % os.environ["GISBASE"], start, command_file)
+        return
+
+#    if stop != "" or select != "" or unlock != "":
+#        grass.message(_("Not implemented yet"), "w")
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.mon.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.rast.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.rast.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.rast.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+############################################################################
+#
+# MODULE:       p.rast
+# AUTHOR(S):    Jachym Cepicky, Martin Landa, Hamish Bowman
+#               Converted to Python by Huidae Cho
+# PURPOSE:      Displays raster map layer in the active map display window.
+# COPYRIGHT:    (C) 2009 by The GRASS Development Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+############################################################################
+
+#%module
+#% description: Displays raster map layer in the active map display window.
+#% keywords: display, raster
+#%end
+#%flag
+#% key: n
+#% description: Make null cells opaque
+#%end
+#%flag
+#% key: i
+#% description: Invert catlist
+#% guisection: Selection
+#%end
+#%option
+#% key: map
+#% type: string
+#% required: yes
+#% multiple: no
+#% key_desc: name
+#% description: Raster map to be displayed
+#% gisprompt: old,cell,raster
+#%end
+#%option
+#% key: catlist
+#% type: string
+#% required: no
+#% multiple: yes
+#% key_desc: cat[-cat]
+#% description: List of categories to be displayed (INT maps)
+#% guisection: Selection
+#%end
+#%option
+#% key: vallist
+#% type: string
+#% required: no
+#% multiple: yes
+#% key_desc: val[-val]
+#% description: List of values to be displayed (FP maps)
+#% guisection: Selection
+#%end
+#%option
+#% key: bg
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: color
+#% description: Background color (for null)
+#% gisprompt: old_color,color,color
+#%end
+#%option
+#% key: opacity
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: val
+#% answer: 100
+#% description: Set opacity between 0-100%
+#%end
+
+import sys
+import os
+import grass.script as grass
+
+def construct_command(cmd):
+    line = cmd
+    for key, val in options.iteritems():
+        if val != "":
+            line += " %s=%s" % (key, val)
+    for key, val in flags.iteritems():
+        if val == True:
+            line += " -%s" % key
+    return line
+
+def main():
+    cmd_file = grass.gisenv()["GRASS_PYCMDFILE"]
+
+    if cmd_file == "" or os.path.exists(cmd_file) == False:
+        grass.message(_("GRASS_PYCMDFILE - File not found. Run p.mon."), "e")
+        return
+
+    cmd = construct_command("d"+os.path.basename(sys.argv[0])[1:-3])
+
+    fp = open(cmd_file, "a")
+    fp.write("%s\n" % cmd)
+    fp.close()
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.rast.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.vect.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.vect.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.vect.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+############################################################################
+#
+# MODULE:       p.vect
+# AUTHOR(S):    Jachym Cepicky, Hamish Bowman
+#               Converted to Python by Huidae Cho
+# PURPOSE:      Displays vector map layer in the active map display window.
+# COPYRIGHT:    (C) 2009 by The GRASS Development Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+############################################################################
+
+#%module
+#% description: Displays vector map layer in the active map display window.
+#% keywords: display, vector
+#%end
+#%flag
+#% key: a
+#% description: Get colors from map table column (of form RRR:GGG:BBB)
+#% guisection: Colors
+#%end
+#%flag
+#% key: c
+#% description: Random colors according to category number (or layer number if 'layer=-1' is given)
+#% guisection: Colors
+#%end
+#%flag
+#% key: i
+#% description: Use values from 'cats' option as feature id
+#% guisection: Selection
+#%end
+#%flag
+#% key: z
+#% description: Colorize polygons according to z height
+#%end
+#%option
+#% key: map
+#% type: string
+#% required: yes
+#% multiple: no
+#% key_desc: name
+#% description: Name of input vector map
+#% gisprompt: old,vector,vector
+#%end
+#%option
+#% key: display
+#% type: string
+#% required: no
+#% multiple: yes
+#% options: shape,cat,topo,dir,attr,zcoor
+#% description: Display
+#% answer: shape
+#%end
+#%option
+#% key: type
+#% type: string
+#% required: no
+#% multiple: yes
+#% options: point,line,boundary,centroid,area,face
+#% description: Feature type
+#% answer: point,line,boundary,centroid,area,face
+#% guisection: Selection
+#%end
+#%option
+#% key: layer
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Layer number (if -1, all layers are displayed)
+#% description: A single vector map can be connected to multiple database tables. This number determines which table to use.
+#% answer: 1
+#% gisprompt: old_layer,layer,layer_all
+#% guisection: Selection
+#%end
+#%option
+#% key: cats
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: range
+#% label: Category values
+#% description: Example: 1,3,7-9,13
+#% guisection: Selection
+#%end
+#%option
+#% key: where
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: sql_query
+#% label: WHERE conditions of SQL statement without 'where' keyword
+#% description: Example: income < 1000 and inhab >= 10000
+#% guisection: Selection
+#%end
+#%option
+#% key: color
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Line color
+#% description: Either a standard GRASS color, R:G:B triplet, or "none"
+#% answer: black
+#% gisprompt: old_color,color,color_none
+#% guisection: Colors
+#%end
+#%option
+#% key: fcolor
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Area fill color
+#% description: Either a standard GRASS color, R:G:B triplet, or "none"
+#% answer: 200:200:200
+#% gisprompt: old_color,color,color_none
+#% guisection: Colors
+#%end
+#%option
+#% key: rgb_column
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: name
+#% description: Name of color definition column (for use with -a flag)
+#% answer: GRASSRGB
+#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
+#% guisection: Colors
+#%end
+#%option
+#% key: zcolor
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: style
+#% description: Type of color table (for use with -z flag)
+#% answer: terrain
+#% guisection: Colors
+#%end
+#%option
+#% key: width
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Line width
+#% answer: 0
+#% guisection: Lines
+#%end
+#%option
+#% key: wcolumn
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: name
+#% description: Name of column for line widths (these values will be scaled by wscale)
+#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
+#% guisection: Lines
+#%end
+#%option
+#% key: wscale
+#% type: double
+#% required: no
+#% multiple: no
+#% description: Scale factor for wcolumn
+#% answer: 1
+#% guisection: Lines
+#%end
+#%option
+#% key: icon
+#% type: string
+#% required: no
+#% multiple: no
+#% options: basic/marker,basic/circle,basic/arrow1,basic/star,basic/point,basic/triangle,basic/box,basic/arrow2,basic/octagon,basic/cross2,basic/pushpin,basic/diamond,basic/cross1,basic/x,demo/smrk,demo/muchomurka,extra/dive_flag,extra/half-box,extra/bridge,extra/fiducial,extra/ping,extra/offbox_ne,extra/adcp,extra/alpha_flag,extra/4pt_star,extra/half-circle,extra/offbox_nw,extra/fancy_compass,extra/airport,extra/compass,extra/offbox_se,extra/fish,extra/target,extra/offbox_sw,extra/n_arrow1,extra/pentagon,extra/n_arrow2,geology/strike_circle,geology/strike_box,geology/strike_line,geology/strike_triangle
+#% description: Point and centroid symbol
+#% answer: basic/x
+#% guisection: Symbols
+#%end
+#%option
+#% key: size
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Symbol size
+#% answer: 5
+#% guisection: Symbols
+#%end
+#%option
+#% key: size_column
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: name
+#% description: Name of numeric column containing symbol size
+#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
+#% guisection: Symbols
+#%end
+#%option
+#% key: rot_column
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: name
+#% label: Name of numeric column containing symbol rotation angle
+#% description: Measured in degrees CCW from east
+#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
+#% guisection: Symbols
+#%end
+#%option
+#% key: llayer
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Layer number or name
+#% description: Layer number for labels (default: the given layer number)
+#% answer: 1
+#% gisprompt: old_layer,layer,layer
+#% guisection: Labels
+#%end
+#%option
+#% key: attrcol
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: name
+#% description: Name of column to be displayed
+#% gisprompt: old_dbcolumn,dbcolumn,dbcolumn
+#% guisection: Labels
+#%end
+#%option
+#% key: lcolor
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Label color
+#% description: Either a standard color name or R:G:B triplet
+#% answer: red
+#% gisprompt: old_color,color,color
+#% guisection: Labels
+#%end
+#%option
+#% key: bgcolor
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Label background color
+#% description: Either a standard GRASS color, R:G:B triplet, or "none"
+#% answer: none
+#% gisprompt: old_color,color,color_none
+#% guisection: Labels
+#%end
+#%option
+#% key: bcolor
+#% type: string
+#% required: no
+#% multiple: no
+#% label: Label border color
+#% description: Either a standard GRASS color, R:G:B triplet, or "none"
+#% answer: none
+#% gisprompt: old_color,color,color_none
+#% guisection: Labels
+#%end
+#%option
+#% key: lsize
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Label size (pixels)
+#% answer: 8
+#% guisection: Labels
+#%end
+#%option
+#% key: font
+#% type: string
+#% required: no
+#% multiple: no
+#% description: Font name
+#% guisection: Labels
+#%end
+#%option
+#% key: encoding
+#% type: string
+#% required: no
+#% multiple: no
+#% description: Text encoding
+#% guisection: Labels
+#%end
+#%option
+#% key: xref
+#% type: string
+#% required: no
+#% multiple: no
+#% options: left,center,right
+#% description: Label horizontal justification
+#% answer: left
+#% guisection: Labels
+#%end
+#%option
+#% key: yref
+#% type: string
+#% required: no
+#% multiple: no
+#% options: top,center,bottom
+#% description: Label vertical justification
+#% answer: center
+#% guisection: Labels
+#%end
+#%option
+#% key: minreg
+#% type: double
+#% required: no
+#% multiple: no
+#% description: Minimum region size (average from height and width) when map is displayed
+#%end
+#%option
+#% key: maxreg
+#% type: double
+#% required: no
+#% multiple: no
+#% description: Maximum region size (average from height and width) when map is displayed
+#%end
+#%option
+#% key: opacity
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: val
+#% answer: 100
+#% description: Set opacity between 0-100%
+#%end
+
+import sys
+import os
+import grass.script as grass
+
+def construct_command(cmd):
+    line = cmd
+    for key, val in options.iteritems():
+        if val != "":
+            line += " %s=%s" % (key, val)
+    for key, val in flags.iteritems():
+        if val == True:
+            line += " -%s" % key
+    return line
+
+def main():
+    cmd_file = grass.gisenv()["GRASS_PYCMDFILE"]
+
+    if cmd_file == "" or os.path.exists(cmd_file) == False:
+        grass.message(_("GRASS_PYCMDFILE - File not found. Run p.mon."), "e")
+        return
+
+    cmd = construct_command("d"+os.path.basename(sys.argv[0])[1:-3])
+
+    fp = open(cmd_file, "a")
+    fp.write("%s\n" % cmd)
+    fp.close()
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+    main()


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/p.vect.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/scripts/vkrige.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/scripts/vkrige.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/scripts/vkrige.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,500 @@
+"""
+MODULE:    v_krige_wxGUI
+
+AUTHOR(S): Anne Ghisla <a.ghisla AT gmail.com>
+
+PURPOSE:   Dedicated GUI for v.krige script.
+
+DEPENDS:   R 2.x, packages gstat, maptools and spgrass6, optional: automap
+
+COPYRIGHT: (C) 2009 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.
+"""
+
+#@TODO move here imports related to wxGUI
+
+### generic imports
+import os, sys
+from tempfile import gettempdir
+import time
+import thread
+## i18N
+import gettext
+gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+### dependencies to be checked once, as they are quite time-consuming. cfr. grass.parser.
+# GRASS binding
+try:
+    import grass.script as grass
+except ImportError:
+    sys.exit(_("No GRASS-python library found."))
+### wxGUI imports
+
+GUIModulesPath = os.path.join(os.getenv("GISBASE"), "etc", "wxpython", "gui_modules")
+sys.path.append(GUIModulesPath)
+
+import globalvar
+if not os.getenv("GRASS_WXBUNDLED"):
+    globalvar.CheckForWx()
+import gselect
+import goutput
+import menuform
+from preferences import globalSettings as UserSettings
+#import help
+
+import wx
+import wx.lib.flatnotebook as FN
+#import wx.lib.plot as plot # for plotting the variogram.
+
+# global variables
+maxint = 1e6 # instead of sys.maxint, not working with SpinCtrl on 64bit [reported by Bob Moskovitz]
+
+
+#@TODO move away functions not regarding the GUI
+
+class KrigingPanel(wx.Panel):
+    """ Main panel. Contains all widgets except Menus and Statusbar. """
+    def __init__(self, parent, Rinstance, controller, *args, **kwargs):
+        wx.Panel.__init__(self, parent, *args, **kwargs)
+        
+        self.parent = parent 
+        self.border = 4
+        
+        #    1. Input data 
+        InputBoxSizer = wx.StaticBoxSizer(wx.StaticBox(self, id = wx.ID_ANY, label = _("Input Data")), 
+                                          orient = wx.HORIZONTAL)
+        
+        flexSizer = wx.FlexGridSizer(cols = 3, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(1)
+
+        flexSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Point dataset:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        self.InputDataMap = gselect.VectorSelect(parent = self,
+                                                 ftype = 'points',
+                                                 updateOnPopup = False)
+        self.InputDataMap.SetFocus()
+        flexSizer.Add(item = self.InputDataMap, flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        RefreshButton = wx.Button(self, id = wx.ID_REFRESH)
+        RefreshButton.Bind(wx.EVT_BUTTON, self.OnButtonRefresh)
+        flexSizer.Add(item = RefreshButton, flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        flexSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Numeric column:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        self.InputDataColumn = gselect.ColumnSelect(self, id = wx.ID_ANY)
+        self.InputDataColumn.SetSelection(0)
+        flexSizer.Add(item = self.InputDataColumn)
+        
+        self.InputDataMap.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnInputDataChanged)
+        
+        InputBoxSizer.Add(item = flexSizer)
+        
+        #    2. Kriging. In book pages one for each R package. Includes variogram fit.
+        KrigingSizer = wx.StaticBoxSizer(wx.StaticBox(self, id = wx.ID_ANY, label = _("Kriging")), wx.HORIZONTAL)
+
+        self.RPackagesBook = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                        style = FN.FNB_BOTTOM |
+                                        FN.FNB_NO_NAV_BUTTONS |
+                                        FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)
+        
+        for Rpackage in ["gstat"]: # , "geoR"]: #@TODO: enable it if/when it'll be implemented.
+            self.CreatePage(package = Rpackage, Rinstance = Rinstance, controller = controller)
+        
+        ## Command output. From menuform module, cmdPanel class
+        self.goutput = goutput.GMConsole(parent = self, margin = False,
+                                         pageid = self.RPackagesBook.GetPageCount(),
+                                         notebook = self.RPackagesBook)
+        self.goutputId = self.RPackagesBook.GetPageCount()
+        self.outpage = self.RPackagesBook.AddPage(self.goutput, text = _("Command output"))
+        
+        self.RPackagesBook.SetSelection(0)
+        KrigingSizer.Add(self.RPackagesBook, proportion = 1, flag = wx.EXPAND)
+        
+        #    3. Output Parameters.
+        OutputSizer = wx.StaticBoxSizer(wx.StaticBox(self, id = wx.ID_ANY, label = _("Output")), wx.HORIZONTAL)
+        
+        OutputParameters = wx.GridBagSizer(hgap = 5, vgap = 5)
+        OutputParameters.AddGrowableCol(1)
+        OutputParameters.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Name for the output raster map:")),
+                             flag = wx.ALIGN_CENTER_VERTICAL,
+                             pos = (0, 0))
+        self.OutputMapName = gselect.Select(parent = self, id = wx.ID_ANY,
+                                            type = 'raster',
+                                            mapsets = [grass.gisenv()['MAPSET']])
+        OutputParameters.Add(item = self.OutputMapName, flag = wx.EXPAND | wx.ALL,
+                             pos = (0, 1))
+        self.VarianceRasterCheckbox = wx.CheckBox(self, id = wx.ID_ANY, label = _("Export variance map as well: "))
+        self.VarianceRasterCheckbox.SetValue(state = True)
+        OutputParameters.Add(item = self.VarianceRasterCheckbox,
+                             flag = wx.ALIGN_CENTER_VERTICAL,
+                             pos = (1, 0))
+        self.OutputVarianceMapName = gselect.Select(parent = self, id = wx.ID_ANY,
+                                            type = 'raster',
+                                            mapsets = [grass.gisenv()['MAPSET']])
+        self.VarianceRasterCheckbox.Bind(wx.EVT_CHECKBOX, self.OnVarianceCBChecked)
+        OutputParameters.Add(item = self.OutputVarianceMapName, flag = wx.EXPAND | wx.ALL,
+                             pos = (1, 1))
+        
+        self.OverwriteCheckBox = wx.CheckBox(self, id = wx.ID_ANY,
+                                             label = _("Allow output files to overwrite existing files"))
+        self.OverwriteCheckBox.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
+        OutputParameters.Add(item = self.OverwriteCheckBox,
+                             pos = (2, 0), span = (1, 2))
+        
+        OutputSizer.Add(OutputParameters, proportion = 0, flag = wx.EXPAND | wx.ALL, border = self.border)
+        
+        #    4. Run Button and Quit Button
+        ButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
+        HelpButton = wx.Button(self, id = wx.ID_HELP)
+        HelpButton.Bind(wx.EVT_BUTTON, self.OnHelpButton)
+        QuitButton = wx.Button(self, id = wx.ID_EXIT)
+        QuitButton.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+        self.RunButton = wx.Button(self, id = wx.ID_ANY, label = _("&Run")) # no stock ID for Run button.. 
+        self.RunButton.Bind(wx.EVT_BUTTON, self.OnRunButton)
+        self.RunButton.Enable(False) # disable it on loading the interface, as input map is not set
+        ButtonSizer.Add(HelpButton, proportion = 0, flag = wx.ALIGN_LEFT | wx.ALL, border = self.border)
+        ButtonSizer.Add(QuitButton, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = self.border)
+        ButtonSizer.Add(self.RunButton, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = self.border)
+        
+        #    Main Sizer. Add each child sizer as soon as it is ready.
+        Sizer = wx.BoxSizer(wx.VERTICAL)
+        Sizer.Add(InputBoxSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = self.border)
+        Sizer.Add(KrigingSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = self.border)
+        Sizer.Add(OutputSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = self.border)
+        Sizer.Add(ButtonSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = self.border)
+        self.SetSizerAndFit(Sizer)
+        
+        # last action of __init__: update imput data list.
+        # it's performed in the few seconds gap while user examines interface before clicking anything.
+        #@TODO: implement a splashcreen IF the maps cause a noticeable lag [markus' suggestion]
+        self.InputDataMap.GetElementList()
+        
+    def CreatePage(self, package, Rinstance, controller):
+        """ Creates the three notebook pages, one for each R package """
+        for package in ["gstat"]: 
+            classobj = eval("RBook"+package+"Panel")
+            setattr(self, "RBook"+package+"Panel", (classobj(self,
+                                                             id = wx.ID_ANY,
+                                                             Rinstance = Rinstance,
+                                                             controller = controller)))
+            self.RPackagesBook.AddPage(page = getattr(self, "RBook"+package+"Panel"), text = package)
+
+    def OnButtonRefresh(self, event):
+        """ Forces refresh of list of available layers. """
+        self.InputDataMap.GetElementList()
+
+    def OnCloseWindow(self, event):
+        """ Cancel button pressed"""
+        self.parent.Close()
+        event.Skip()
+
+    def OnHelpButton(self, event):
+        # file = os.path.join(os.getenv("GISBASE"), "docs", "html", "v.krige.html")
+        # file = os.path.join(os.path.curdir, "description.html")
+        # @TODO fix HelpWindow
+        # helpFrame = help.HelpWindow(parent=self, id=wx.ID_ANY,
+        #                            title=_("GRASS - Help page for v.krige"),
+        #                            size=(640, 480),
+        #                            file=file)
+        # helpFrame.Show(True)
+
+        grass.run_command('g.manual', entry = 'v.krige')
+        
+        event.Skip()
+
+    def OnInputDataChanged(self, event):
+        """ Refreshes list of columns and fills output map name TextCtrl """
+        MapName = event.GetString()
+        self.InputDataColumn.InsertColumns(vector = MapName,
+                                   layer = 1, excludeKey = True,
+                                   type = ['integer', 'double precision'])
+        self.InputDataColumn.SetSelection(0)
+        self.RunButton.Enable(self.InputDataColumn.GetSelection() is not -1)
+        self.RBookgstatPanel.PlotButton.Enable(self.InputDataColumn.GetSelection() is not -1)
+        
+        if self.InputDataColumn.GetSelection() is not -1:
+            self.OutputMapName.SetValue(MapName.split("@")[0]+"_kriging")
+            self.OutputVarianceMapName.SetValue(MapName.split("@")[0]+"_kriging_var")
+        else:
+            self.OutputMapName.SetValue('')
+            self.OutputVarianceMapName.SetValue('')
+        
+    def OnRunButton(self,event):
+        """ Execute R analysis. """
+        #@FIXME: send data to main method instead of running it here.
+        
+        #-1: get the selected notebook page. The user shall know that [s]he can modify settings in all
+        # pages, but only the selected one will be executed when Run is pressed.
+        SelectedPanel = self.RPackagesBook.GetCurrentPage()
+        
+        if self.RPackagesBook.GetPageText(self.RPackagesBook.GetSelection()) == 'Command output':
+            self.goutput.WriteError("No parameters for running. Please select \"gstat\" tab, check parameters and re-run.")
+            return False # no break invoked by above function
+        
+        # mount command string as it would have been written on CLI
+        command = ["v.krige", "input=" + self.InputDataMap.GetValue(),
+                   "column=" + self.InputDataColumn.GetValue(),
+                   "output=" + self.OutputMapName.GetValue(), 
+                   "package=" + '%s' % self.RPackagesBook.GetPageText(self.RPackagesBook.GetSelection())]
+        
+        if not hasattr(SelectedPanel, 'VariogramCheckBox') or not SelectedPanel.VariogramCheckBox.IsChecked():
+            command.append("model=" + '%s' % SelectedPanel.ModelChoicebox.GetStringSelection().split(" ")[0])
+            
+        for i in ['Sill', 'Nugget', 'Range']:
+            if getattr(SelectedPanel, i+"ChextBox").IsChecked():
+                command.append(i.lower() + "=" + '%s' % getattr(SelectedPanel, i+'Ctrl').GetValue())
+        
+        if SelectedPanel.KrigingRadioBox.GetStringSelection() == "Block kriging":
+            command.append("block=" + '%s' % SelectedPanel.BlockSpinBox.GetValue())
+        if self.OverwriteCheckBox.IsChecked():
+            command.append("--overwrite")
+        if self.VarianceRasterCheckbox.IsChecked():
+            command.append("output_var=" + self.OutputVarianceMapName.GetValue())
+        
+        # give it to the output console
+        #@FIXME: it runs the command as a NEW instance. Reimports data, recalculates variogram fit..
+        #otherwise I can use Controller() and mimic RunCmd behaviour.
+        self.goutput.RunCmd(command, switchPage = True)
+    
+    def OnVarianceCBChecked(self, event):
+        self.OutputVarianceMapName.Enable(event.IsChecked())
+
+class KrigingModule(wx.Frame):
+    """ Kriging module for GRASS GIS. Depends on R and its packages gstat and geoR. """
+    def __init__(self, parent, Rinstance, controller, *args, **kwargs):
+        wx.Frame.__init__(self, parent, *args, **kwargs)
+        # setting properties and all widgettery
+        self.SetTitle(_("Kriging Module"))
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_dialog.ico'), wx.BITMAP_TYPE_ICO))
+        self.log = Log(self) 
+        self.CreateStatusBar()
+        self.log.message(_("Ready."))
+        
+        self.Panel = KrigingPanel(self, Rinstance, controller)
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.GetBestSize())
+    
+class Log:
+    """ The log output is redirected to the status bar of the containing frame. """
+    def __init__(self, parent):
+        self.parent = parent
+
+    def message(self, text_string):
+        """ Updates status bar """
+        self.parent.SetStatusText(text_string.strip())
+
+class RBookPanel(wx.Panel):
+    """ Generic notebook page with shared widgets and empty kriging functions. """
+    def __init__(self, parent, *args, **kwargs):
+        wx.Panel.__init__(self, parent, *args, **kwargs)
+        
+        self.parent = parent
+        
+        self.VariogramSizer = wx.StaticBoxSizer(wx.StaticBox(self,
+                                                             id = wx.ID_ANY, 
+                                                             label = _("Variogram fitting")),
+                                                wx.HORIZONTAL)
+        self.LeftSizer = wx.BoxSizer(wx.VERTICAL)
+        self.RightSizer = wx.BoxSizer(wx.VERTICAL)
+        self.ParametersSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+
+        self.VariogramSizer.Add(self.LeftSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
+        self.VariogramSizer.Add(self.RightSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
+        
+        # left side of Variogram fitting. The checkboxes and spinctrls.
+        self.PlotButton = wx.Button(self, id = wx.ID_ANY, label = _("Plot/refresh variogram")) # no stock ID for Run button.. 
+        self.PlotButton.Bind(wx.EVT_BUTTON, self.OnPlotButton)
+        self.PlotButton.Enable(False) # grey it out until a suitable layer is available
+        self.LeftSizer.Add(self.PlotButton, proportion = 0, flag =  wx.ALL, border = parent.border)
+        self.LeftSizer.Add(self.ParametersSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
+        
+        self.ParametersList = ["Sill", "Nugget", "Range"]
+        MinValues = [0,0,1]
+        for n in self.ParametersList:
+            setattr(self, n+"ChextBox", wx.CheckBox(self,
+                                                    id = self.ParametersList.index(n),
+                                                    label = _(n + ":")))
+            setattr(self, n+"Ctrl", (wx.SpinCtrl(self,
+                                                 id = wx.ID_ANY,
+                                                 min = MinValues[self.ParametersList.index(n)],
+                                                 max = maxint)))
+            getattr(self, n+"ChextBox").Bind(wx.EVT_CHECKBOX,
+                                             self.UseValue,
+                                             id = self.ParametersList.index(n))
+            setattr(self, n+"Sizer", (wx.BoxSizer(wx.HORIZONTAL)))
+            self.ParametersSizer.Add(getattr(self, n+"ChextBox"),
+                                     flag = wx.ALIGN_CENTER_VERTICAL,
+                                     pos = (self.ParametersList.index(n),0))
+            self.ParametersSizer.Add(getattr(self, n+"Ctrl"),
+                                     flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
+                                     pos = (self.ParametersList.index(n),1))
+        
+        # right side of the Variogram fitting. The plot area.
+        #Plot = wx.StaticText(self, id= wx.ID_ANY, label = "Check Plot Variogram to interactively fit model.")
+        #PlotPanel = wx.Panel(self)
+        #self.PlotArea = plot.PlotCanvas(PlotPanel)
+        #self.PlotArea.SetInitialSize(size = (250,250))
+        #self.RightSizer.Add(PlotPanel, proportion=0, flag= wx.EXPAND|wx.ALL, border=parent.border)
+        
+        self.KrigingSizer = wx.StaticBoxSizer(wx.StaticBox(self,
+                                                             id = wx.ID_ANY,
+                                                             label = _("Kriging techniques")),
+                                                wx.VERTICAL)
+        
+        KrigingList = ["Ordinary kriging", "Block kriging"]#, "Universal kriging"] #@FIXME: i18n on the list?
+        self.KrigingRadioBox = wx.RadioBox(self,
+                                           id = wx.ID_ANY,
+                                           choices = KrigingList,
+                                           majorDimension = 1,
+                                           style = wx.RA_SPECIFY_COLS)
+        self.KrigingRadioBox.Bind(wx.EVT_RADIOBOX, self.HideBlockOptions)
+        self.KrigingSizer.Add(self.KrigingRadioBox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
+        
+        # block kriging parameters. Size.
+        BlockSizer = wx.BoxSizer(wx.HORIZONTAL)
+        BlockLabel = wx.StaticText(self, id = wx.ID_ANY, label = _("Block size:"))
+        self.BlockSpinBox = wx.SpinCtrl(self, id = wx.ID_ANY, min = 1, max = maxint)
+        self.BlockSpinBox.Enable(False) # default choice is Ordinary kriging so block param is disabled
+        BlockSizer.Add(BlockLabel, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = parent.border)
+        BlockSizer.Add(self.BlockSpinBox, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = parent.border)
+        self.KrigingSizer.Add(BlockSizer, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = parent.border)
+        
+        self.Sizer = wx.BoxSizer(wx.VERTICAL)
+        self.Sizer.Add(self.VariogramSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
+        self.Sizer.Add(self.KrigingSizer,  proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
+        
+    def HideBlockOptions(self, event):
+        self.BlockSpinBox.Enable(event.GetInt() == 1)
+    
+    def OnPlotButton(self,event):
+        """ Plots variogram with current options. """
+        pass
+    
+    def UseValue(self, event):
+        """ Enables/Disables the SpinCtrl in respect of the checkbox. """
+        n = self.ParametersList[event.GetId()]
+        getattr(self, n+"Ctrl").Enable(event.IsChecked())
+
+class RBookgstatPanel(RBookPanel):
+    """ Subclass of RBookPanel, with specific gstat options and kriging functions. """
+    def __init__(self, parent, Rinstance, controller, *args, **kwargs):
+        RBookPanel.__init__(self, parent, *args, **kwargs)
+        
+        # assigns Rinstance, that comes from the GUI call of v.krige.py.
+        robjects = Rinstance
+        self.controller = controller
+        
+        if robjects.r.require('automap')[0]:
+            self.VariogramCheckBox = wx.CheckBox(self, id = wx.ID_ANY, label = _("Auto-fit variogram"))
+            self.LeftSizer.Insert(0,
+                                  self.VariogramCheckBox,
+                                  proportion = 0,
+                                  flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
+                                  border = 4)
+            self.SetSizerAndFit(self.Sizer)
+            self.VariogramCheckBox.Bind(wx.EVT_CHECKBOX, self.HideOptions)
+            self.VariogramCheckBox.SetValue(state = True) # check it by default
+
+        ModelFactor = robjects.r.vgm().rx('long')
+        ModelList = robjects.r.levels(ModelFactor[0])
+        #@FIXME: no other way to let the Python pick it up..
+        # and this is te wrong place where to load this list. should be at the very beginning.
+        self.ModelChoicebox = wx.Choice(self, id = wx.ID_ANY, choices = ModelList)
+        
+        # disable model parameters' widgets by default
+        for n in ["Sill", "Nugget", "Range"]:
+            getattr(self, n+"Ctrl").Enable(False)
+        self.ModelChoicebox.Enable(False)
+        
+        VariogramSubSizer = wx.BoxSizer(wx.HORIZONTAL)
+        VariogramSubSizer.Add(item = wx.StaticText(self,
+                                                 id =  wx.ID_ANY,
+                                                 label = _("Model: ")),
+                              flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
+                              border = 4)
+        VariogramSubSizer.Add(item = self.ModelChoicebox,
+                              flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
+                              border = 4)
+        
+        self.LeftSizer.Insert(2, item = VariogramSubSizer)
+        
+        self.SetSizerAndFit(self.Sizer)
+    
+    def HideOptions(self, event):
+        self.ModelChoicebox.Enable(not event.IsChecked())
+        for n in ["Sill", "Nugget", "Range"]:
+            if not event.IsChecked():
+                getattr(self, n+"Ctrl").Enable(True)
+                getattr(self, n+ "ChextBox").SetValue(True)
+                getattr(self, n+ "ChextBox").Enable(False) # grey it out keeping it checked.. improvable
+            else:
+                getattr(self, n+"Ctrl").Enable(False)
+                getattr(self, n+ "ChextBox").SetValue(False)
+                getattr(self, n+ "ChextBox").Enable(True)
+        #@FIXME: was for n in self.ParametersSizer.GetChildren(): n.Enable(False) but doesn't work
+        
+    def OnPlotButton(self,event):
+        """ Plots variogram with current options. """
+        ## BIG WARNING: smell of code duplication. Fix this asap. emminchia!
+        #controller = Controller() # sed, if needed,
+        #controller = self.controller
+        map = self.parent.InputDataMap.GetValue()
+        column = self.parent.InputDataColumn.GetValue()
+        
+        # import data or pick them up
+        if globals()["InputData"] is None:
+            globals()["InputData"] = controller.ImportMap(map = map,
+                                                          column = column)
+        # fit the variogram or pick it up
+        Formula = controller.ComposeFormula(column = column,
+                                            isblock = self.KrigingRadioBox.GetStringSelection() == "Block kriging",
+                                            inputdata = globals()['InputData'])
+        #if globals()["Variogram"] is None:
+        if hasattr(self, 'VariogramCheckBox') and self.VariogramCheckBox.IsChecked():
+            self.model = ''
+            for each in ("Sill","Nugget","Range"):
+                if getattr(self, each+'ChextBox').IsChecked():
+                    setattr(self, each.lower(), getattr(self, each+"Ctrl").GetValue())
+                else:
+                    setattr(self, each.lower(), robjects.r('''NA'''))
+        else:
+            self.model = self.ModelChoicebox.GetStringSelection().split(" ")[0]
+            for each in ("Sill","Nugget","Range"):
+                if getattr(self, each+'ChextBox').IsChecked(): #@FIXME will be removed when chextboxes will be frozen
+                    setattr(self, each.lower(), getattr(self, each+"Ctrl").GetValue())
+            
+        globals()["Variogram"] = controller.FitVariogram(Formula,
+                                                         InputData,
+                                                         model = self.model,
+                                                         sill = self.sill,
+                                                         nugget = self.nugget,
+                                                         range = self.range)
+        
+        # use R plot function, in a separate window.
+        thread.start_new_thread(self.plot, ())
+        
+    def plot(self):
+        #robjects.r.X11()
+        #robjects.r.png("variogram.png")
+        textplot = robjects.r.plot(Variogram['datavariogram'], Variogram['variogrammodel'])
+        print textplot
+        self.refresh()
+        #robjects.r['dev.off']()
+
+    def refresh(self):
+        while True:
+            rinterface.process_revents()
+            time.sleep(0.1)
+        
+class RBookgeoRPanel(RBookPanel):
+    """ Subclass of RBookPanel, with specific geoR options and kriging functions. """
+    def __init__(self, parent, *args, **kwargs):
+        RBookPanel.__init__(self, parent, *args, **kwargs)
+        #@TODO: change these two lines as soon as geoR f(x)s are integrated.
+        for n in self.GetChildren():
+            n.Hide()
+        self.Sizer.Add(wx.StaticText(self, id = wx.ID_ANY, label = _("Work in progress! No functionality provided.")))
+        self.SetSizerAndFit(self.Sizer)


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/scripts/vkrige.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/support/update_menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/support/update_menudata.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/support/update_menudata.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,9 +1,9 @@
-"""
+"""!
 @brief Support script for wxGUI - only for developers needs. Updates
 menudata.xml file.
 
 Parse all GRASS modules in the search path ('bin' & 'script') and
-updates: - description (i.e. help)
+updates: - description (i.e. help) - keywords
 
 Prints warning for missing modules.
 
@@ -21,33 +21,17 @@
 
 import os
 import sys
-import locale
 import tempfile
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
 
-import xml.sax
-import xml.sax.handler
-HandlerBase=xml.sax.handler.ContentHandler
-from xml.sax import make_parser
-
 from grass.script import core as grass
 
-def ParseInterface(cmd):
-    """!Parse interface
-    
-    @param cmd command to be parsed (given as list)
-    """
-    grass_task = menuform.grassTask()
-    handler = menuform.processTask(grass_task)
-    enc = locale.getdefaultlocale()[1]
-    if enc and enc.lower() not in ("utf8", "utf-8"):
-        xml.sax.parseString(menuform.getInterfaceDescription(cmd[0]).decode(enc).split('\n',1)[1].replace('', '<?xml version="1.0" encoding="utf-8"?>\n', 1).encode("utf-8"),
-                            handler)
-    else:
-        xml.sax.parseString(menuform.getInterfaceDescription(cmd[0]),
-                            handler)
-        
-    return grass_task
-    
+sys.path.append('gui_modules')
+import menudata
+
 def parseModules():
     """!Parse modules' interface"""
     modules = dict()
@@ -68,22 +52,23 @@
         if module in ignore:
             continue
         try:
-            interface = ParseInterface(cmd = [module])
+            interface = menuform.GUI().ParseInterface(cmd = [module])
         except IOError, e:
             grass.error(e)
             continue
         modules[interface.name] = { 'label'   : interface.label,
-                                    'desc'    : interface.description }
+                                    'desc'    : interface.description,
+                                    'keywords': interface.keywords }
         
     return modules
 
 def updateData(data, modules):
     """!Update menu data tree"""
     # list of modules to be ignored
-    ignore =  [ 'v.type_wrapper.py',
-                'vcolors' ]
+    ignore = ['v.type_wrapper.py',
+              'vcolors']
     
-
+    menu_modules = list()    
     for node in data.tree.getiterator():
         if node.tag != 'menuitem':
             continue
@@ -94,7 +79,7 @@
         
         if not item.has_key('command'):
             continue
-
+        
         if item['command'] in ignore:
             continue
         
@@ -108,7 +93,21 @@
         else:
             desc = modules[module]['desc']
         node.find('help').text = desc
+        
+        if not modules[module].has_key('keywords'):
+            grass.warning('%s: keywords missing' % module)
+        else:
+            if node.find('keywords') is None:
+                node.insert(2, etree.Element('keywords'))
+                grass.warning("Adding tag 'keywords' to '%s'" % module)
+            node.find('keywords').text = ','.join(modules[module]['keywords'])
+        
+        menu_modules.append(item['command'])
     
+    for module in modules.keys():
+        if module not in menu_modules:
+            grass.warning("'%s' not available from the menu" % module)
+    
 def writeData(data, file = None):
     """!Write updated menudata.xml"""
     if file is None:
@@ -132,12 +131,12 @@
 def main(argv = None):
     if argv is None:
         argv = sys.argv
-    
+
     if len(argv) > 1 and argv[1] == '-d':
         printDiff = True
     else:
         printDiff = False
-    
+
     if len(argv) > 1 and argv[1] == '-h':
         print >> sys.stderr, __doc__
         return 1
@@ -149,7 +148,7 @@
     modules = dict()
     modules = parseModules()
     grass.info("Step 3: reading menu data...")
-    data = menudata.Data()
+    data = menudata.ManagerData()
     grass.info("Step 4: updating menu data...")
     updateData(data, modules)
     
@@ -164,13 +163,12 @@
     else:
         grass.info("Step 5: writing menu data (menudata.xml)...")
         writeData(data)
-    
+        
     return 0
 
 if __name__ == '__main__':
     if os.getenv("GISBASE") is None:
-        print >> sys.stderr, "You must be in GRASS GIS to run this program."
-        sys.exit(1)
+        sys.exit("You must be in GRASS GIS to run this program.")
     
     sys.path.append(os.path.join(os.getenv("GISBASE"), 'etc', 'wxpython', 'gui_modules'))
     import menudata

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/Makefile
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/Makefile	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/Makefile	2010-10-14 15:39:14 UTC (rev 43911)
@@ -15,6 +15,7 @@
 
 ETCDIR = $(ETC)/wxpython
 
+EXTRA_CLEAN_FILES = grass6_wxvdigit.py grass6_wxvdigit.i grass6_wxvdigit_wrap.cpp
 default:
 
 ifneq ($(USE_WXWIDGETS),)
@@ -32,8 +33,9 @@
 
 $(LIB_NAME).py: $(SHLIB)
 
-$(SHLIB): $(LIB_NAME).i
-	python setup.py build_ext --swig=$(SWIG) --swig-opts=-c++ --build-lib=$(OBJDIR) --build-temp=$(OBJDIR)
+$(SHLIB): $(LIB_NAME).i cats.cpp digit.cpp driver.cpp driver_draw.cpp \
+	driver_select.cpp line.cpp message.cpp select.cpp undo.cpp vertex.cpp
+	$(PYTHON) setup.py build_ext --swig=$(SWIG) --swig-opts=-c++ --build-lib=$(OBJDIR) --build-temp=$(OBJDIR)
 
 .NOTPARALLEL: $(LIB_NAME).py $(LIB_NAME)_wrap.cpp
 

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.cpp
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.cpp	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.cpp	2010-10-14 15:39:14 UTC (rev 43911)
@@ -34,8 +34,9 @@
 Digit::Digit(DisplayDriver *ddriver, wxWindow *window)
 {
     display = ddriver;
-    display->parentWin = window;
-
+    if (!display)
+	return;
+    
     if (display->mapInfo) {
 	InitCats();
     }
@@ -64,11 +65,16 @@
    \brief Update digit settings
 
    \param breakLines break lines on intersection
+   \param addCentroid add centroid to left/right area
+   \param catBoundary attach category to boundary
 */
-void Digit::UpdateSettings(bool breakLines)
+void Digit::UpdateSettings(bool breakLines,
+			   bool addCentroid, bool catBoundary)
 {
     settings.breakLines = breakLines;
-
+    settings.addCentroid = addCentroid;
+    settings.catBoundary = !catBoundary; /* do not attach */
+    
     return;
 }
 

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.h
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.h	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/digit.h	2010-10-14 15:39:14 UTC (rev 43911)
@@ -24,6 +24,8 @@
     /* settings */
     struct _settings {
 	bool breakLines;
+	bool addCentroid;
+	bool catBoundary;
     } settings;
 
     /* undo/redo */
@@ -80,6 +82,10 @@
 					double, double, double, bool,
 					int, int, double);
 
+    double GetLineLength(int);
+    double GetAreaSize(int);
+    double GetAreaPerimeter(int);
+
     int CopyCats(std::vector<int>, std::vector<int>, bool);
     int GetCategory(int);
     std::map<int, std::vector<int> > GetLineCats(int);
@@ -89,7 +95,8 @@
     int Undo(int);
     int GetUndoLevel();
 
-    void UpdateSettings(bool);
+    void UpdateSettings(bool, 
+			bool, bool);
 };
 
 #endif /* WXVDIGIT_DIGIT_H */

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.cpp
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.cpp	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.cpp	2010-10-14 15:39:14 UTC (rev 43911)
@@ -20,6 +20,16 @@
 
 #include "driver.h"
 
+#define MSG  0
+#define WARN 1
+#define ERR  2
+
+static int print_error(const char *, const int);
+static int print_percent(int);
+static void print_sentence (PyObject*, const int, const char *);
+static PyObject *logStream;
+static int message_id = 1;
+
 /**
    \brief Initialize driver
 
@@ -30,15 +40,17 @@
    
    \return
 */
-DisplayDriver::DisplayDriver(void *device, void *deviceTmp)
+DisplayDriver::DisplayDriver(gwxPseudoDC *device, gwxPseudoDC *deviceTmp,
+			     PyObject *log)
 {
     G_gisinit(""); /* GRASS functions */
 
     mapInfo = NULL;
 
-    dc = (gwxPseudoDC *) device;
-    dcTmp = (gwxPseudoDC *) deviceTmp;
-
+    dc = device;
+    dcTmp = deviceTmp;
+    logStream = log;
+    
     points = Vect_new_line_struct();
     pointsScreen = new wxList();
     cats = Vect_new_cats_struct();
@@ -50,11 +62,11 @@
 
     drawSegments = false;
 
-    G_set_verbose(0);
-    
+    G_set_error_routine(&print_error);
+    // G_set_percent_routine(&print_percent);
+
     // avoid GUI crash when G_fatal_error() is called (opening the vector map)
     // Vect_set_fatal_error(GV_FATAL_PRINT);
-    // G_set_error_routine(print_error);
 }
 
 /**
@@ -71,6 +83,9 @@
     if (mapInfo)
 	CloseMap();
 
+    G_unset_error_routine();
+    // G_unset_percent_routine();
+    
     Vect_destroy_line_struct(points);
     delete pointsScreen;
     Vect_destroy_cats_struct(cats);
@@ -503,17 +518,103 @@
     return region;
 }
 
-/**
-   \brief Error messages handling 
+/*
+  \brief Print one message, prefix inserted before each new line
 
-   \param msg message
-   \param type type message (MSG, WARN, ERR)
+  From lib/gis/error.c
+*/
+void print_sentence (PyObject *pyFd, const int type, const char *msg)
+{
+    char prefix[256];
+    const char *start;
+    char* sentence;
 
-   \return 0
+    switch (type) {
+    case MSG: 
+	sprintf (prefix, "GRASS_INFO_MESSAGE(%d,%d): Vdigit: ", getpid(), message_id);
+	break;
+    case WARN:
+	sprintf (prefix, "GRASS_INFO_WARNING(%d,%d): Vdigit: ", getpid(), message_id);
+	break;
+    case ERR:
+	sprintf (prefix, "GRASS_INFO_ERROR(%d,%d): Vdigit: ", getpid(), message_id);
+	break;
+    }
+
+    start = msg;
+    
+    PyFile_WriteString("\n", pyFd);
+
+    while (*start != '\0') {
+	const char *next = start;
+	
+	PyFile_WriteString(prefix, pyFd);
+	
+	while ( *next != '\0' ) {
+	    next++;
+	    
+	    if ( *next == '\n' ) {
+	        next++;
+		break;
+	    }
+	}
+	
+	sentence = (char *) G_malloc((next - start + 1) * sizeof (char));
+	strncpy(sentence, start, next - start + 1);
+	sentence[next-start] = '\0';
+	
+	PyFile_WriteString(sentence, pyFd);
+	G_free((void *)sentence);
+	
+	PyFile_WriteString("\n", pyFd);
+	start = next;
+    }
+    
+    PyFile_WriteString("\n", pyFd);
+    sprintf(prefix, "GRASS_INFO_END(%d,%d)\n", getpid(), message_id);
+    PyFile_WriteString(prefix, pyFd);
+    
+    message_id++;
+}
+
+/*!
+  \brief Print error/warning/message
+
+  \param msg message buffer
+  \param type message type
+
+  \return 0
 */
-int print_error(const char *msg, int type)
+int print_error(const char *msg, const int type)
 {
-    fprintf(stderr, "%s", msg);
+    if (logStream) {
+	print_sentence(logStream, type, msg);
+    }
+    else {
+	fprintf(stderr, "Vdigit: %s\n", msg);
+    }
+
+    return 0;
+}
+
+/*!
+  \brief Print percentage information
+
+  \param x value
+
+  \return 0
+*/
+int print_percent(int x)
+{
+    char msg[256];
+
+    if (logStream) {
+	sprintf(msg, "GRASS_INFO_PERCENT: %d\n", x);
+	PyFile_WriteString(msg, logStream);
+    }
+    else {
+	fprintf(stderr, "GRASS_INFO_PERCENT: %d\n", x);
+    }
     
     return 0;
 }

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.h
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.h	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/driver.h	2010-10-14 15:39:14 UTC (rev 43911)
@@ -176,7 +176,7 @@
     
 public:
     /* constructor */
-    DisplayDriver(void *, void *);
+    DisplayDriver(gwxPseudoDC *, gwxPseudoDC *, PyObject *);
     /* destructor */
     ~DisplayDriver();
 
@@ -232,6 +232,4 @@
 			int, int);
 };
 
-int print_error(const char *, int);
-
 #endif /* WXVDIGIT_DRIVER_H */

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/line.cpp
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/line.cpp	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/line.cpp	2010-10-14 15:39:14 UTC (rev 43911)
@@ -33,6 +33,7 @@
    \param thresh threshold value for snapping
 
    \return fid on success
+   \return 0 for boundary if no centroid is added
    \return -1 on failure
 */
 int Digit::AddLine(int type, std::vector<double> coords, int layer, int cat,
@@ -44,6 +45,8 @@
     int newline;
     int changeset;
 
+    int left, right;         /* left, right area */
+
     struct line_pnts *Points;
     struct line_cats *Cats;
 
@@ -55,6 +58,8 @@
 	return -1;
     }
 
+    left = right = -1;
+    
     npoints = coords.size() / (Vect_is_3d(display->mapInfo) ? 3 : 2);
     if (coords.size() != npoints * (Vect_is_3d(display->mapInfo) ? 3 : 2)) {
 	wxString msg;
@@ -90,7 +95,9 @@
     Points = Vect_new_line_struct();
     Cats = Vect_new_cats_struct();
 
-    if (layer > 0) {
+    if (layer > 0 &&
+	(type != GV_BOUNDARY ||
+	 (type == GV_BOUNDARY && settings.catBoundary))) {
 	Vect_cat_set(Cats, layer, cat);
 	
 	if (cat > GetCategory(layer)) {
@@ -110,7 +117,8 @@
 	}
     }
 
-    if (type & GV_BOUNDARY) { /* close boundary */
+    if (type & GV_BOUNDARY) {
+	/* close boundary */
 	int last = Points->n_points-1;
 	if (Vect_points_distance(Points->x[0], Points->x[0], Points->z[0],
 				 Points->x[last], Points->x[last], Points->z[last],
@@ -134,6 +142,54 @@
 	return -1;
     }
 
+    if (type & GV_BOUNDARY && settings.addCentroid) {
+	double x, y;
+	struct line_pnts *bpoints;
+	
+	bpoints = Vect_new_line_struct();
+	
+	/* add centroid for left/right area */
+	Vect_get_line_areas(display->mapInfo, newline,
+			    &left, &right);
+	/* check if area exists and has no centroid inside */
+	if (layer > 0 && (left > 0 || right > 0)) {
+	    Vect_cat_set(Cats, layer, cat);
+	
+	    if (cat > GetCategory(layer)) {
+		SetCategory(layer, cat); /* set up max category for layer */
+	    }
+	}
+	
+	if (left > 0 &&
+	    Vect_get_area_centroid(display->mapInfo, left) == 0) {
+	    if (Vect_get_area_points(display->mapInfo, left, bpoints) > 0 &&
+		Vect_find_poly_centroid(bpoints, &x, &y) == 0) {
+		Vect_reset_line(bpoints);
+		Vect_append_point(bpoints, x, y, 0.0);
+		if (Vect_write_line(display->mapInfo, GV_CENTROID,
+				    bpoints, Cats) < 0) {
+		    display->WriteLineMsg();
+		    return -1;
+		}
+	    }
+	}
+	if (right > 0 &&
+	    Vect_get_area_centroid(display->mapInfo, right) == 0) {
+	    if (Vect_get_area_points(display->mapInfo, right, bpoints) > 0 &&
+		Vect_find_poly_centroid(bpoints, &x, &y) == 0) {
+		Vect_reset_line(bpoints);
+		Vect_append_point(bpoints, x, y, 0.0);
+		if (Vect_write_line(display->mapInfo, GV_CENTROID,
+				    bpoints, Cats) < 0) {
+		    display->WriteLineMsg();
+		    return -1;
+		}
+	    }
+	}
+	
+	Vect_destroy_line_struct(bpoints);
+    }
+
     /* register changeset */
     changeset = changesets.size();
     AddActionToChangeset(changeset, ADD, newline);
@@ -150,6 +206,12 @@
 	Vect_close(BgMap[0]);
     }
 
+    if (type & GV_BOUNDARY &&
+	!settings.catBoundary &&
+	left < 1 && right < 1) {
+	newline = 0;
+    }
+    
     return newline;
 }
 
@@ -978,3 +1040,94 @@
 
     return ret;
 }
+
+/*!
+  \brief Get line length
+  
+  \todo LL locations
+
+  \param line feature id
+
+  \return line length
+  \return -1 for non-linear features
+*/
+double Digit::GetLineLength(int line)
+{
+    int type;
+    double length;
+    
+    struct line_pnts *points;
+    
+    if (!Vect_line_alive(display->mapInfo, line))
+	return -1;
+    
+    points = Vect_new_line_struct();
+    
+    type = Vect_read_line(display->mapInfo, points, NULL, line);
+    
+    length = -1;
+    if (type & GV_LINES)
+	length = Vect_line_length(points);
+
+    Vect_destroy_line_struct(points);
+
+    return length;
+}
+
+/*!
+  \brief Get area size
+
+  \param centroid centroid id
+
+  \return area in map units
+  \return -1 on error
+*/
+double Digit::GetAreaSize(int centroid)
+{
+    int area;
+    double size;
+    
+    area = Vect_get_centroid_area(display->mapInfo, centroid);
+    size = -1;
+    if (area > 0) {
+	if (!Vect_area_alive(display->mapInfo, area))
+	    return -1;
+	
+	size = Vect_get_area_area(display->mapInfo, area);
+    }
+    
+    return size;
+}
+
+/*!
+  \brief Get area perimeter
+
+  \param centroid centroid id
+
+  \return perimeter in map units
+  \return -1 on error
+*/
+double Digit::GetAreaPerimeter(int centroid)
+{
+    int area;
+    double perimeter;
+    struct line_pnts *points = NULL;
+    
+    area = Vect_get_centroid_area(display->mapInfo, centroid);
+    perimeter = -1;
+    if (area > 0) {
+
+	if (!Vect_area_alive(display->mapInfo, area))
+	return -1;
+    
+	points = Vect_new_line_struct();
+    
+	Vect_get_area_points(display->mapInfo, area, points);
+	perimeter = Vect_area_perimeter(points);
+    }
+    
+    if (points)
+	Vect_destroy_line_struct(points);
+    
+    return perimeter;
+}

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/pseudodc.i
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/pseudodc.i	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/pseudodc.i	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,7 +1,7 @@
-
 %{
 #include <wx/wxPython/wxPython.h>
 #include <wx/wxPython/pyclasses.h>
+#include <wx/dcbuffer.h>
 %}
 
 %{
@@ -10,18 +10,60 @@
 
 %rename(PseudoDC) gwxPseudoDC;
 
+%typemap(in) wxString& (bool temp=false) {
+    $1 = wxString_in_helper($input);
+    if ($1 == NULL) SWIG_fail;
+    temp = true;
+}
+%typemap(freearg) wxString& {
+    if (temp$argnum)
+        delete $1;
+}
+
+%apply wxString& { wxString* };
+
+
+%typemap(in) wxRect& (wxRect temp) {
+    $1 = &temp;
+    if ( ! wxRect_helper($input, &$1)) SWIG_fail;
+}
+%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) wxRect& {
+    $1 = wxPySimple_typecheck($input, wxT("wxRect"), 4);
+}
+
+%apply wxRect& { wxRect* };
+
 %typemap(out) wxRect {
-	$result = Py_BuildValue("iiii", $1.x, $1.y, $1.width, $1.height);
+    $result = Py_BuildValue("[iiii]", $1.x, $1.y, $1.width, $1.height);
 }
 
+%typemap(out) wxArrayInt& {
+    $result = wxArrayInt2PyList_helper(*$1);
+}
+
+%typemap(out) wxArrayInt {
+    $result = wxArrayInt2PyList_helper($1);
+}
+
 class gwxPseudoDC
 {
 public:
 	gwxPseudoDC();
 	~gwxPseudoDC();
 	void Clear();
+    	void ClearId(int);
 	void RemoveAll();
-	void RemoveId(int id);
+	void RemoveId(int);
+	void BeginDrawing();
+	void EndDrawing();
+        void SetBackground(const wxBrush&);
+	void SetId(int);
+        void SetBrush(const wxBrush&);
+        void SetPen(const wxPen&);
+	void SetIdBounds(int, wxRect&);
+	void DrawLine(const wxPoint&, const wxPoint&);
+	void SetFont(const wxFont&);
+	void SetTextForeground(const wxColour&);
 	%extend {
 		void DrawToDC(void *dc) {
 			self->DrawToDC((wxDC *) dc);
@@ -34,6 +76,27 @@
 			self->GetIdBounds(id, rect);
 			return rect;
 		}
+		void TranslateId(int id, float dx, float dy) {
+		        self->TranslateId(id, (wxCoord) dx, (wxCoord) dy);
+		}
+		PyObject *FindObjects(float x, float y, int radius) {
+		        return self->FindObjects((wxCoord) x, (wxCoord) y,
+			                         (wxCoord) radius, *wxWHITE);
+                }
+		void DrawRectangleRect(const wxRect& rect) {
+		        return self->DrawRectangle(rect);
+                }
+		void DrawText(const wxString& text, float x, float y) {
+		        return self->DrawText(text, (wxCoord) x, (wxCoord) y);
+		}
+		void DrawRotatedText(const wxString& text, float x, float y, double angle) {
+		        return self->DrawRotatedText(text, (wxCoord) x, (wxCoord) y, angle);
+		}
+                void DrawBitmap(const wxBitmap& bitmap, float x, float y, bool useMask) {
+                        return self->DrawBitmap(bitmap, (wxCoord) x, (wxCoord) y, useMask);
+                }
+		void DrawLinePoint(const wxPoint& p1, const wxPoint& p2) {
+		        return self->DrawLine(p1, p2);
+                }
 	}
 };
-

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/setup.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/setup.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/setup.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -32,6 +32,7 @@
 
 for flag in ('GDALCFLAGS',
              'GDALLIBS',
+             'GEOSCFLAGS',
              'WXWIDGETSLIB',
              'WXWIDGETSCXXFLAGS'):
     update_opts(flag, macros, inc_dirs, lib_dirs, libs, extras)

Modified: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/vertex.cpp
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/vertex.cpp	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/vertex.cpp	2010-10-14 15:39:14 UTC (rev 43911)
@@ -28,7 +28,7 @@
    \param thresh_coords threshold value to identify vertex position
    \param thresh_snap threshold value to snap moved vertex
 
-   \param 1 vertex moved
+   \param id id of the new feature
    \param 0 nothing changed
    \param -1 error
 */
@@ -95,7 +95,7 @@
 
     Vect_destroy_line_struct(point);
 
-    return ret;
+    return nlines + 1; // feature is write at the end of the file
 }
 
 /**
@@ -107,7 +107,7 @@
    \param x,y,z coordinates (z is used only if map is 3d
    \param thresh threshold value to identify vertex position
 
-   \param 1 vertex added/removed
+   \param id id of the new feature
    \param 0 nothing changed
    \param -1 error
 */
@@ -155,5 +155,5 @@
 
     Vect_destroy_line_struct(point);
 
-    return ret;
+    return nlines + 1; // feature is write at the end of the file
 }

Modified: grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py	2010-10-14 15:39:14 UTC (rev 43911)
@@ -9,7 +9,7 @@
  - GMFrame
  - GMApp
 
-(C) 2006-2009 by the GRASS Development Team
+(C) 2006-2010 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.
@@ -22,36 +22,25 @@
 import sys
 import os
 import time
-import traceback
-import types
-import re
 import string
 import getopt
 import platform
-import shlex
+import signal
+import tempfile
 
 ### XML 
-import xml.sax
-import xml.sax.handler
-HandlerBase=xml.sax.handler.ContentHandler
-from xml.sax import make_parser
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
 
 ### i18N
 import gettext
 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
 
 import gui_modules
-gmpath = gui_modules.__path__[0]
-sys.path.append(gmpath)
+sys.path.append(gui_modules.__path__[0])
 
-import images
-imagepath = images.__path__[0]
-sys.path.append(imagepath)
-
-import icons
-gmpath = icons.__path__[0]
-sys.path.append(gmpath)
-
 import gui_modules.globalvar as globalvar
 if not os.getenv("GRASS_WXBUNDLED"):
     globalvar.CheckForWx()
@@ -61,10 +50,6 @@
 import wx.combo
 import wx.html
 import wx.stc
-import wx.lib.customtreectrl as CT
-import wx.lib.flatnotebook as FN
-import  wx.lib.scrolledpanel as scrolled
-from wx.lib.wordwrap import wordwrap
 try:
     import wx.lib.agw.customtreectrl as CT
     import wx.lib.agw.flatnotebook   as FN
@@ -72,22 +57,15 @@
     import wx.lib.customtreectrl as CT
     import wx.lib.flatnotebook   as FN
 
-try:
-    import subprocess
-except:
-    import compat.subprocess as subprocess
-
-grassPath = os.path.join(globalvar.ETCDIR, "python")
-sys.path.append(grassPath)
+sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
 from grass.script import core as grass
 
 import gui_modules.utils as utils
 import gui_modules.preferences as preferences
-import gui_modules.wxgui_utils as wxgui_utils
+import gui_modules.layertree as layertree
 import gui_modules.mapdisp as mapdisp
 import gui_modules.menudata as menudata
 import gui_modules.menuform as menuform
-import gui_modules.grassenv as grassenv
 import gui_modules.histogram as histogram
 import gui_modules.profile as profile
 import gui_modules.rules as rules
@@ -99,25 +77,34 @@
 import gui_modules.goutput as goutput
 import gui_modules.gdialogs as gdialogs
 import gui_modules.colorrules as colorrules
-from   gui_modules.debug import Debug as Debug
-from   icons.icon import Icons as Icons
+import gui_modules.ogc_services as ogc_services
+import gui_modules.prompt as prompt
+import gui_modules.menu as menu
+import gui_modules.gmodeler as gmodeler
+import gui_modules.vclean as vclean
+import gui_modules.nviz_tools as nviz_tools
+from   gui_modules.debug import Debug
+from   gui_modules.ghelp import MenuTreeWindow
+from   gui_modules.ghelp import AboutWindow
+from   gui_modules.toolbars import LayerManagerToolbar
+from   icons.icon import Icons
 
 UserSettings = preferences.globalSettings
 
 class GMFrame(wx.Frame):
+    """!Layer Manager frame with notebook widget for controlling GRASS
+    GIS. Includes command console page for typing GRASS (and other)
+    commands, tree widget page for managing map layers.
     """
-    GIS Manager frame with notebook widget for controlling
-    GRASS GIS. Includes command console page for typing GRASS
-    (and other) commands, tree widget page for managing GIS map layers.
-    """
-    def __init__(self, parent, id=wx.ID_ANY, title=_("GRASS GIS Layer Manager"),
-                 workspace=None):
+    def __init__(self, parent, id = wx.ID_ANY, title = _("GRASS GIS Layer Manager"),
+                 workspace = None,
+                 size = (600, 450), style = wx.DEFAULT_FRAME_STYLE, **kwargs):
         self.parent    = parent
         self.baseTitle = title
         self.iconsize  = (16, 16)
-
-        wx.Frame.__init__(self, parent=parent, id=id, size=(550, 400),
-                          style=wx.DEFAULT_FRAME_STYLE)
+        
+        wx.Frame.__init__(self, parent = parent, id = id, size = size,
+                          style = style, **kwargs)
                           
         self.SetTitle(self.baseTitle)
         self.SetName("LayerManager")
@@ -130,44 +117,40 @@
         self.disp_idx      = 0            # index value for map displays and layer trees
         self.curr_page     = ''           # currently selected page for layer tree notebook
         self.curr_pagenum  = ''           # currently selected page number for layer tree notebook
-        self.encoding      = 'ISO-8859-1' # default encoding for display fonts
         self.workspaceFile = workspace    # workspace file
-        self.menucmd       = {}           # menuId / cmd
+        self.workspaceChanged = False     # track changes in workspace
         self.georectifying = None         # reference to GCP class or None
         self.gcpmanagement = None         # reference to GCP class or None
+        # list of open dialogs
+        self.dialogs        = dict()
+        self.dialogs['preferences'] = None
+        self.dialogs['atm'] = list()
         
         # creating widgets
-        # -> self.notebook, self.goutput, self.outpage
-        self.notebook  = self.__createNoteBook()
-        self.cmdprompt = self.__createCommandPrompt()
-        self.menubar   = self.__createMenuBar()
-        self.toolbar   = self.__createToolBar()
+        self.menubar = menu.Menu(parent = self, data = menudata.ManagerData())
+        self.SetMenuBar(self.menubar)
+        self.menucmd = self.menubar.GetCmd()
         self.statusbar = self.CreateStatusBar(number=1)
-
+        self.notebook  = self.__createNoteBook()
+        self.toolbar = LayerManagerToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        
         # bindings
-        self.Bind(wx.EVT_CLOSE,     self.OnCloseWindow)
-        self.Bind(wx.EVT_LEFT_DOWN, self.AddRaster, self.notebook)
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 
         # minimal frame size
         self.SetMinSize((500, 400))
 
         # AUI stuff
-        #       self._auimgr.AddPane(self.toolbar, wx.aui.AuiPaneInfo().ToolbarPane().
-        #                              Top().Dockable(False).CloseButton(False).
-        #                              DestroyOnClose(True).Row(0).Layer(0))
         self._auimgr.AddPane(self.notebook, wx.aui.AuiPaneInfo().
                              Left().CentrePane().BestSize((-1,-1)).Dockable(False).
                              CloseButton(False).DestroyOnClose(True).Row(1).Layer(0))
-        self._auimgr.AddPane(self.cmdprompt, wx.aui.AuiPaneInfo().
-                             Bottom().BestSize((-1,25)).Dockable(False).
-                             CloseButton(False).DestroyOnClose(True).
-                             PaneBorder(False).Row(1).Layer(0).Position(0).
-                             CaptionVisible(False))
 
         self._auimgr.Update()
 
         wx.CallAfter(self.notebook.SetSelection, 0)
-
+        
         # use default window layout ?
         if UserSettings.Get(group='general', key='defWindowPos', subkey='enabled') is True:
             dim = UserSettings.Get(group='general', key='defWindowPos', subkey='dim')
@@ -178,11 +161,14 @@
                self.SetSize((w, h))
             except:
                 pass
-
+        else:
+            self.Centre()
+        
+        self.Layout()
         self.Show()
-
+        
         # load workspace file if requested
-        if (self.workspaceFile):
+        if self.workspaceFile:
             # load given workspace file
             if self.LoadWorkspaceFile(self.workspaceFile):
                 self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
@@ -196,131 +182,26 @@
         # -> OnSize() -> UpdateMap()
         if self.curr_page and not self.curr_page.maptree.mapdisplay.IsShown():
             self.curr_page.maptree.mapdisplay.Show()
-
+        
         # redirect stderr to log area    
         self.goutput.Redirect()
+        # fix goutput's pane size
+        self.goutput.SetSashPosition(int(self.GetSize()[1] * .45))
+
+        self.workspaceChanged = False
         
         # start with layer manager on top
-        self.curr_page.maptree.mapdisplay.Raise()
+        if self.curr_page:
+            self.curr_page.maptree.mapdisplay.Raise()
         wx.CallAfter(self.Raise)
-
-    def __doLayout(self):
-        """Do Layout (unused bacause of aui manager...)"""
-        # self.panel = wx.Panel(self,-1, style= wx.EXPAND)
-        # sizer= wx.BoxSizer(wx.VERTICAL)
-        # self.cmdsizer = wx.BoxSizer(wx.HORIZONTAL)
-
-        # item, proportion, flag, border, userData
-        # self.sizer.Add(self.notebook, proportion=1, flag=wx.EXPAND, border=1)
-        # self.sizer.Add(self.cmdinput, proportion=0, flag=wx.EXPAND, border=1)
-        # self.SetSizer(self.sizer)
-
-        # self.sizer.Fit(self)
-        # self.Layout()
-
-    def __createCommandPrompt(self):
-        """Creates command-line input area"""
-        self.cmdprompt = wx.Panel(self)
-
-        label = wx.StaticText(parent=self.cmdprompt, id=wx.ID_ANY, label="Cmd >")
-	# label.SetFont(wx.Font(pointSize=11, family=wx.FONTFAMILY_DEFAULT,
-        #                      style=wx.NORMAL, weight=wx.BOLD))
-        input = wx.TextCtrl(parent=self.cmdprompt, id=wx.ID_ANY,
-                            value="",
-                            style=wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
-                            size=(-1, 25))
-
-        input.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, ''))
-
-        wx.CallAfter(input.SetInsertionPoint, 0)
-
-        self.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd,        input)
-        self.Bind(wx.EVT_TEXT,       self.OnUpdateStatusBar, input)
-
-        # layout
-        sizer = wx.BoxSizer(wx.HORIZONTAL)
-        sizer.Add(item=label, proportion=0,
-                  flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER,
-                  border=4)
-        sizer.Add(item=input, proportion=1,
-                  flag=wx.EXPAND | wx.ALL,
-                  border=1)
-
-        self.cmdprompt.SetSizer(sizer)
-        sizer.Fit(self.cmdprompt)
-        self.cmdprompt.Layout()
-
-        return self.cmdprompt
-
-    def __createMenuBar(self):
-        """Creates menubar"""
-
-        self.menubar = wx.MenuBar()
-        menud = menudata.Data()
-        for eachMenuData in menud.GetMenu():
-            for eachHeading in eachMenuData:
-                menuLabel = eachHeading[0]
-                menuItems = eachHeading[1]
-                self.menubar.Append(self.__createMenu(menuItems), menuLabel)
-        self.SetMenuBar(self.menubar)
-
-        return self.menubar
-
-    def __createMenu(self, menuData):
-        """Creates menu"""
-
-        menu = wx.Menu()
-        for eachItem in menuData:
-            if len(eachItem) == 2:
-                label = eachItem[0]
-                subMenu = self.__createMenu(eachItem[1])
-                menu.AppendMenu(wx.ID_ANY, label, subMenu)
-            else:
-                self.__createMenuItem(menu, *eachItem)
-        self.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
-        return menu
-
-    def __createMenuItem(self, menu, label, help, handler, gcmd, kind=wx.ITEM_NORMAL):
-        """Creates menu items"""
-        if not label:
-            menu.AppendSeparator()
-            return
         
-        if len(gcmd) > 0:
-            helpString = gcmd + ' -- ' + help
-        else:
-            helpString = help
-        
-        menuItem = menu.Append(wx.ID_ANY, label, helpString, kind)
-        
-        self.menucmd[menuItem.GetId()] = gcmd
-        
-        if gcmd:
-            try:
-                cmd = shlex.split(str(gcmd))
-            except UnicodeError:
-                cmd = shlex.split(utils.EncodeString((gcmd)))
-            if cmd and cmd[0] not in globalvar.grassCmd['all']:
-                menuItem.Enable(False)
-        
-        rhandler = eval(handler)
-
-        self.Bind(wx.EVT_MENU, rhandler, menuItem)
-
     def __createNoteBook(self):
-        """Creates notebook widgets"""
-
-        #create main notebook widget
-        nbStyle = FN.FNB_FANCY_TABS | \
-            FN.FNB_BOTTOM | \
-            FN.FNB_NO_NAV_BUTTONS | \
-            FN.FNB_NO_X_BUTTON
-        
+        """!Creates notebook widgets"""
         if globalvar.hasAgw:
-            self.notebook = FN.FlatNotebook(parent=self, id=wx.ID_ANY, agwStyle = nbStyle)
+            self.notebook = FN.FlatNotebook(parent=self, id=wx.ID_ANY, agwStyle = globalvar.FNPageDStyle)
         else:
-            self.notebook = FN.FlatNotebook(parent=self, id=wx.ID_ANY, style = nbStyle)
-        
+            self.notebook = FN.FlatNotebook(parent=self, id=wx.ID_ANY, style = globalvar.FNPageDStyle)
+
         # create displays notebook widget and add it to main notebook page
         cbStyle = globalvar.FNPageStyle
         if globalvar.hasAgw:
@@ -328,95 +209,120 @@
         else:
             self.gm_cb = FN.FlatNotebook(self, id=wx.ID_ANY, style = cbStyle)
         self.gm_cb.SetTabAreaColour(globalvar.FNPageColor)
-        self.notebook.AddPage(self.gm_cb, text=_("Map layers for each display"))
-
+        self.notebook.AddPage(self.gm_cb, text=_("Map layers"))
+        
         # create command output text area and add it to main notebook page
         self.goutput = goutput.GMConsole(self, pageid=1)
-        self.outpage = self.notebook.AddPage(self.goutput, text=_("Command output"))
-
+        self.notebook.AddPage(self.goutput, text=_("Command console"))
+        
+        # create 'search module' notebook page
+        self.search = MenuTreeWindow(parent = self)
+        self.notebook.AddPage(self.search, text = _("Search module"))
+        
         # bindings
         self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnCBPageChanged)
         self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
         self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.OnCBPageClosed)
-
-        sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(item=self.goutput, proportion=1,
-                  flag=wx.EXPAND | wx.ALL, border=1)
-        self.SetSizer(sizer)
-#        self.out_sizer.Fit(self.outpage)
-        self.Layout()
-
-        self.Centre()
+        
         return self.notebook
 
-    def __createToolBar(self):
-        """Creates toolbar"""
-
-        self.toolbar = self.CreateToolBar()
-        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
-
-        for each in self.ToolbarData():
-            self.AddToolbarButton(self.toolbar, *each)
-        self.toolbar.Realize()
-
-        return self.toolbar
-
-    def OnMenuHighlight(self, event):
+    def AddNviz(self):
+        """!Add nviz notebook page"""
+        self.nviz = nviz_tools.NvizToolWindow(parent = self,
+                                              display = self.curr_page.maptree.GetMapDisplay()) 
+        self.notebook.AddPage(self.nviz, text = _("3D view"))
+        self.notebook.SetSelection(self.notebook.GetPageCount() - 1)
+        
+    def RemoveNviz(self):
+        """!Remove nviz notebook page"""
+        # print self.notebook.GetPage(1)
+        self.notebook.RemovePage(3)
+        del self.nviz
+        self.notebook.SetSelection(0)
+        
+    def WorkspaceChanged(self):
+        """!Update window title"""
+        if not self.workspaceChanged:
+            self.workspaceChanged = True
+        
+        if self.workspaceFile:
+            self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile) + '*')
+        
+    def OnGeorectify(self, event):
+        """!Launch georectifier module
         """
-        Default menu help handler
-        """
-         # Show how to get menu item info from this event handler
-        id = event.GetMenuId()
-        item = self.GetMenuBar().FindItemById(id)
-        if item:
-            text = item.GetText()
-            help = item.GetHelp()
+        georect.GeorectWizard(self)
 
-        # but in this case just call Skip so the default is done
-        event.Skip()
-
     def OnGCPManager(self, event):
+        """!Launch georectifier module
         """
-        Launch GCP Manager module
-        """
         import gui_modules.gcpmanager as gcpmanager
-
         gcpmanager.GCPWizard(self)
 
-    def OnGeorectify(self, event):
-        """
-        Launch georectifier module
-        """
-        georect.GeorectWizard(self)
+    def OnGModeler(self, event):
+        """!Launch Graphical Modeler"""
+        win = gmodeler.ModelFrame(parent = self)
+        win.CentreOnScreen()
         
+        win.Show()
+        
+    def OnDone(self, returncode):
+        """Command execution finised"""
+        if hasattr(self, "model"):
+            self.model.DeleteIntermediateData(log = self.goutput)
+            del self.model
+        self.SetStatusText('')
+        
+    def OnRunModel(self, event):
+        """!Run model"""
+        filename = ''
+        dlg = wx.FileDialog(parent = self, message=_("Choose model to run"),
+                            defaultDir = os.getcwd(),
+                            wildcard=_("GRASS Model File (*.gxm)|*.gxm"))
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            return
+        
+        self.model = gmodeler.Model()
+        self.model.LoadModel(filename)
+        self.SetStatusText(_('Validating model...'), 0)
+        result =  self.model.Validate()
+        if result:
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _('Model is not valid. Do you want to '
+                                               'run the model anyway?\n\n%s') % '\n'.join(errList),
+                                   caption=_("Run model?"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT |
+                                   wx.ICON_QUESTION | wx.CENTRE)
+            ret = dlg.ShowModal()
+            if ret != wx.ID_YES:
+                return
+        
+        self.SetStatusText(_('Running model...'), 0)
+        self.model.Run(log = self.goutput,
+                       onDone = self.OnDone)
+        
     def OnMapsets(self, event):
+        """!Launch mapset access dialog
         """
-        Launch mapset access dialog
-        """
-        dlg = preferences.MapsetAccess(parent=self, id=wx.ID_ANY)
+        dlg = preferences.MapsetAccess(parent = self, id = wx.ID_ANY)
         dlg.CenterOnScreen()
-
-        # if OK is pressed...
+        
         if dlg.ShowModal() == wx.ID_OK:
             ms = dlg.GetMapsets()
-            # run g.mapsets with string of accessible mapsets
-            cmdlist = ['g.mapsets', 'mapset=%s' % ','.join(ms)]
-            gcmd.Command(cmdlist)
-            
-    def OnRDigit(self, event):
-        """
-        Launch raster digitizing module
-        """
-        pass
-
+            gcmd.RunCommand('g.mapsets',
+                            parent = self,
+                            mapset = '%s' % ','.join(ms))
+        
     def OnCBPageChanged(self, event):
-        """Page in notebook (display) changed"""
+        """!Page in notebook (display) changed"""
         old_pgnum = event.GetOldSelection()
         new_pgnum = event.GetSelection()
         
         self.curr_page   = self.gm_cb.GetCurrentPage()
         self.curr_pagenum = self.gm_cb.GetSelection()
-        
         try:
             self.curr_page.maptree.mapdisplay.SetFocus()
             self.curr_page.maptree.mapdisplay.Raise()
@@ -426,17 +332,18 @@
         event.Skip()
 
     def OnPageChanged(self, event):
-        """Page in notebook changed"""
+        """!Page in notebook changed"""
         page = event.GetSelection()
         if page == self.goutput.pageid:
             # remove '(...)'
-            self.notebook.SetPageText(page, _("Command output"))
+            self.notebook.SetPageText(page, _("Command console"))
+            wx.CallAfter(self.goutput.cmd_prompt.SetFocus)
+        self.SetStatusText('', 0)
         
         event.Skip()
 
     def OnCBPageClosed(self, event):
-        """
-        Page of notebook closed
+        """!Page of notebook closed
         Also close associated map display
         """
         if UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
@@ -466,62 +373,39 @@
                     dlg.Destroy()
                     return
                 dlg.Destroy()
-        
+
         self.gm_cb.GetPage(event.GetSelection()).maptree.Map.Clean()
         self.gm_cb.GetPage(event.GetSelection()).maptree.Close(True)
-        
+
         self.curr_page = None
-        
+
         event.Skip()
 
-    def OnRunCmd(self, event):
-        """!Run command
-
-        @todo: Handle unicode with shlex
-        """
-        cmdString = event.GetString()
-        
-        if cmdString[:2] == 'd.' and not self.curr_page:
-            self.NewDisplay(show=True)
-        
-        try:
-            cmd = shlex.split(str(cmdString))
-        except UnicodeError:
-            cmd = shlex.split(utils.EncodeString((cmdString)))
-            
-        if len(cmd) > 1:
-            self.goutput.RunCmd(cmd, switchPage=True)
-        else:
-            self.goutput.RunCmd(cmd, switchPage=False)
-        
-        self.OnUpdateStatusBar(None)
-
+    def GetLayerTree(self):
+        """!Get current layer tree"""
+        return self.curr_page.maptree
+    
     def GetLogWindow(self):
-        """Get widget for command output"""
+        """!Get widget for command output"""
         return self.goutput
-
-    def OnUpdateStatusBar(self, event):
-        if event is None:
-            self.statusbar.SetStatusText("")
-        else:
-            self.statusbar.SetStatusText(_("Type GRASS command and run by pressing ENTER"))
-
+    
     def GetMenuCmd(self, event):
-        """Get GRASS command from menu item
+        """!Get GRASS command from menu item
 
         Return command as a list"""
         layer = None
         
-        cmd = self.menucmd[event.GetId()]
+        if event:
+            cmd = self.menucmd[event.GetId()]
         
         try:
             cmdlist = cmd.split(' ')
         except: # already list?
             cmdlist = cmd
-            
+        
         # check list of dummy commands for GUI modules that do not have GRASS
         # bin modules or scripts. 
-        if cmd in ['vcolors']:
+        if cmd in ['vcolors', 'r.mapcalc', 'r3.mapcalc']:
             return cmdlist
 
         try:
@@ -530,30 +414,98 @@
             type = self.curr_page.maptree.GetPyData(layer)[0]['type']
         except:
             layer = None
+
         if layer and len(cmdlist) == 1: # only if no paramaters given
             if (type == 'raster' and cmdlist[0][0] == 'r' and cmdlist[0][1] != '3') or \
                     (type == 'vector' and cmdlist[0][0] == 'v'):
                 input = menuform.GUI().GetCommandInputMapParamKey(cmdlist[0])
                 if input:
                     cmdlist.append("%s=%s" % (input, name))
-
+        
         return cmdlist
 
-    def RunMenuCmd(self, event):
-        """Run command selected from menu"""
-        cmd = self.GetMenuCmd(event)
-        self.goutput.RunCmd(cmd, switchPage=True)
+    def RunMenuCmd(self, event, cmd = ''):
+        """!Run command selected from menu"""
+        if event:
+            cmd = self.GetMenuCmd(event)
+        self.goutput.RunCmd(cmd, switchPage=False)
 
-    def OnMenuCmd(self, event):
-        """Parse command selected from menu"""
-        cmd = self.GetMenuCmd(event)
+    def OnMenuCmd(self, event, cmd = ''):
+        """!Parse command selected from menu"""
+        if event:
+            cmd = self.GetMenuCmd(event)
         menuform.GUI().ParseCommand(cmd, parentframe=self)
 
-    def OnNewVector(self, event):
-        """Create new vector map layer"""
-        name, add = gdialogs.CreateNewVector(self, log=self.goutput,
-                                             cmdDef=(['v.edit', 'tool=create'], "map"))
+    def OnRunScript(self, event):
+        """!Run script"""
+        # open dialog and choose script file
+        dlg = wx.FileDialog(parent = self, message = _("Choose script file"),
+                            defaultDir = os.getcwd(), wildcard = _("Bash script (*.sh)|*.sh|Python script (*.py)|*.py"))
+        
+        filename = None
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            return False
 
+        if not os.path.exists(filename):
+            wx.MessageBox(parent = self,
+                          message = _("Script file '%s' doesn't exist. Operation cancelled.") % filename,
+                          caption = _("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            return
+        
+        self.goutput.WriteCmdLog(_("Launching script '%s'...") % filename)
+        self.goutput.RunCmd(filename, switchPage = True)
+        
+    def OnChangeLocation(self, event):
+        """Change current location"""
+        dlg = gdialogs.LocationDialog(parent = self)
+        if dlg.ShowModal() == wx.ID_OK:
+            location, mapset = dlg.GetValues()
+            if location and mapset:
+                ret = gcmd.RunCommand("g.gisenv",
+                                      set = "LOCATION_NAME=%s" % location)
+                ret += gcmd.RunCommand("g.gisenv",
+                                       set = "MAPSET=%s" % mapset)
+                if ret > 0:
+                    wx.MessageBox(parent = self,
+                                  message = _("Unable to switch to location <%(loc)s> mapset <%(mapset)s>.") % \
+                                      { 'loc' : location, 'mapset' : mapset },
+                                  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                else:
+                    # close workspace
+                    self.OnWorkspaceClose()
+                    self.OnWorkspaceNew()
+                    wx.MessageBox(parent = self,
+                                  message = _("Current location is <%(loc)s>.\n"
+                                              "Current mapset is <%(mapset)s>.") % \
+                                      { 'loc' : location, 'mapset' : mapset },
+                                  caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+                    
+    def OnChangeMapset(self, event):
+        """Change current mapset"""
+        dlg = gdialogs.MapsetDialog(parent = self)
+        if dlg.ShowModal() == wx.ID_OK:
+            mapset = dlg.GetMapset()
+            if mapset:
+                if gcmd.RunCommand("g.gisenv",
+                                   set = "MAPSET=%s" % mapset) != 0:
+                    wx.MessageBox(parent = self,
+                                  message = _("Unable to switch to mapset <%s>.") % mapset,
+                                  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                else:
+                    wx.MessageBox(parent = self,
+                                  message = _("Current mapset is <%s>.") % mapset,
+                                  caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+        
+    def OnNewVector(self, event):
+        """!Create new vector map layer"""
+        name, add = gdialogs.CreateNewVector(self, log = self.goutput,
+                                             cmd = (('v.edit',
+                                                     { 'tool' : 'create' },
+                                                     'map')))
+        
         if name and add:
             # add layer to map layer tree
             self.curr_page.maptree.AddLayer(ltype='vector',
@@ -561,15 +513,15 @@
                                             lchecked=True,
                                             lopacity=1.0,
                                             lcmd=['d.vect', 'map=%s' % name])
-            
+        
     def OnAboutGRASS(self, event):
-        """Display 'About GRASS' dialog"""
+        """!Display 'About GRASS' dialog"""
         win = AboutWindow(self)
         win.CentreOnScreen()
         win.Show(True)  
         
     def OnWorkspace(self, event):
-        """Workspace menu (new, load)"""
+        """!Workspace menu (new, load)"""
         point = wx.GetMousePosition()
         menu = wx.Menu()
 
@@ -588,11 +540,11 @@
         self.PopupMenu(menu)
         menu.Destroy()
 
-    def OnWorkspaceNew(self, event=None):
-        """Create new workspace file
+    def OnWorkspaceNew(self, event = None):
+        """!Create new workspace file
 
-        Erase current workspace settings first"""
-
+        Erase current workspace settings first
+        """
         Debug.msg(4, "GMFrame.OnWorkspaceNew():")
         
         # start new map display if no display is available
@@ -602,7 +554,9 @@
         maptree = self.curr_page.maptree
         
         # ask user to save current settings
-        if maptree.GetCount() > 0:
+        if self.workspaceFile and self.workspaceChanged:
+            self.OnWorkspaceSave()
+        elif self.workspaceFile is None and maptree.GetCount() > 0:
              dlg = wx.MessageDialog(self, message=_("Current workspace is not empty. "
                                                     "Do you want to store current settings "
                                                     "to workspace file?"),
@@ -627,12 +581,13 @@
         
         # no workspace file loaded
         self.workspaceFile = None
+        self.workspaceChanged = False
         self.SetTitle(self.baseTitle)
         
     def OnWorkspaceOpen(self, event=None):
-        """Open file with workspace definition"""
+        """!Open file with workspace definition"""
         dlg = wx.FileDialog(parent=self, message=_("Choose workspace file"),
-                            defaultDir=os.getcwd(), wildcard="*.gxw")
+                            defaultDir=os.getcwd(), wildcard=_("GRASS Workspace File (*.gxw)|*.gxw"))
 
         filename = ''
         if dlg.ShowModal() == wx.ID_OK:
@@ -652,156 +607,126 @@
         self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
 
     def LoadWorkspaceFile(self, filename):
-        """Load layer tree definition stored in GRASS Workspace XML file (gxw)
+        """!Load layer tree definition stored in GRASS Workspace XML file (gxw)
 
-        Return True on success
-        Return False on error"""
-
+        @todo Validate against DTD
+        
+        @return True on success
+        @return False on error
+        """
         # dtd
         dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxw.dtd")
-
-        # validate xml agains dtd
-        #         dtd = xmldtd.load_dtd(dtdFilename)
-        #         parser = xmlproc.XMLProcessor()
-        #         parser.set_application(xmlval.ValidatingApp(dtd, parser))
-        #         parser.dtd = dtd
-        #         parser.ent = dtd
-        #         try:
-        #             # TODO: set_error_handler(self,err)
-        #             parser.parse_resource(filename)
-        #         except:
-        #             dlg = wx.MessageDialog(self, _("Unable to open workspace file <%s>. "
-        #                                            "It is not valid GRASS Workspace File.") % filename,
-        #                                    _("Error"), wx.OK | wx.ICON_ERROR)
-        #             dlg.ShowModal()
-        #             dlg.Destroy()
-        #             return False
-
-        # read file and fix patch to dtd
+        
+        # parse workspace file
         try:
-            file = open(filename, "r")
+            gxwXml = workspace.ProcessWorkspaceFile(etree.parse(filename))
+        except Exception, e:
+            gcmd.GError(parent = self,
+                        message = _("Reading workspace file <%s> failed.\n"
+                                    "Invalid file, unable to parse XML document.") % filename)
+            return
+        
+        busy = wx.BusyInfo(message=_("Please wait, loading workspace..."),
+                           parent=self)
+        wx.Yield()
 
-            fileStream = ''.join(file.readlines())
-            p = re.compile('(grass-gxw.dtd)')
-            p.search(fileStream)
-            if subprocess.mswindows:
-                # FIXME mixing '\' and '/' causes error in p.sub
-                dtdFilename = dtdFilename.replace("\\", "/") 
-            fileStream = p.sub(dtdFilename, fileStream)
-
-            # sax
-            gxwXml = workspace.ProcessWorkspaceFile()
+        #
+        # load layer manager window properties
+        #
+        if UserSettings.Get(group='workspace', key='posManager', subkey='enabled') is False:
+            if gxwXml.layerManager['pos']:
+                self.SetPosition(gxwXml.layerManager['pos'])
+            if gxwXml.layerManager['size']:
+                self.SetSize(gxwXml.layerManager['size'])
+        
+        #
+        # start map displays first (list of layers can be empty)
+        #
+        displayId = 0
+        mapdisplay = list()
+        for display in gxwXml.displays:
+            mapdisp = self.NewDisplay(show=False)
+            mapdisplay.append(mapdisp)
+            maptree = self.gm_cb.GetPage(displayId).maptree
             
-            try:
-                xml.sax.parseString(fileStream, gxwXml)
-            except xml.sax.SAXParseException, err:
-                raise gcmd.GStdError(_("Reading workspace file <%s> failed. "
-                                       "Invalid file, unable to parse XML document.") % filename + \
-                                         "\n\n%s" % err,
-                                     parent=self)
-            except ValueError, err:
-                raise gcmd.GStdError(_("Reading workspace file <%s> failed. "
-                                       "Invalid file, unable to parse XML document.") % filename + \
-                                         "\n\n%s" % err,
-                                     parent=self)
+            # set windows properties
+            mapdisp.SetProperties(render=display['render'],
+                                  mode=display['mode'],
+                                  showCompExtent=display['showCompExtent'],
+                                  constrainRes=display['constrainRes'],
+                                  projection=display['projection']['enabled'])
 
-            busy = wx.BusyInfo(message=_("Please wait, loading workspace..."),
-                               parent=self)
-            wx.Yield()
-
-            #
-            # load layer manager window properties
-            #
-            if UserSettings.Get(group='workspace', key='posManager', subkey='enabled') is False:
-                if gxwXml.layerManager['pos']:
-                    self.SetPosition(gxwXml.layerManager['pos'])
-                if gxwXml.layerManager['size']:
-                    self.SetSize(gxwXml.layerManager['size'])
-
-            #
-            # start map displays first (list of layers can be empty)
-            #
-            displayId = 0
-            mapdisplay = []
-            for display in gxwXml.displays:
-                mapdisplay.append(self.NewDisplay(show=False))
-                maptree = self.gm_cb.GetPage(displayId).maptree
-
-                # set windows properties
-                mapdisplay[-1].SetProperties(render=display['render'],
-                                             mode=display['mode'],
-                                             showCompExtent=display['showCompExtent'],
-                                             constrainRes=display['constrainRes'])
-
-                # set position and size of map display
-                if UserSettings.Get(group='workspace', key='posDisplay', subkey='enabled') is False:
-                    if display['pos']:
-                        mapdisplay[-1].SetPosition(display['pos'])
-                    if display['size']:
-                        mapdisplay[-1].SetSize(display['size'])
-
-                # set extent if defined
-                if display['extent']:
-                    w, s, e, n = display['extent']
-                    maptree.Map.region = maptree.Map.GetRegion(w=w, s=s, e=e, n=n)
+            if display['projection']['enabled']:
+                if display['projection']['epsg']:
+                    UserSettings.Set(group = 'display', key = 'projection', subkey = 'epsg',
+                                     value = display['projection']['epsg'])
+                    if display['projection']['proj']:
+                        UserSettings.Set(group = 'display', key = 'projection', subkey = 'proj4',
+                                         value = display['projection']['proj'])
+            
+            # set position and size of map display
+            if UserSettings.Get(group='workspace', key='posDisplay', subkey='enabled') is False:
+                if display['pos']:
+                    mapdisp.SetPosition(display['pos'])
+                if display['size']:
+                    mapdisp.SetSize(display['size'])
                     
-                mapdisplay[-1].Show()
-
-                displayId += 1
+            # set extent if defined
+            if display['extent']:
+                w, s, e, n = display['extent']
+                region = maptree.Map.region = maptree.Map.GetRegion(w=w, s=s, e=e, n=n)
+                mapdisp.GetWindow().ResetZoomHistory()
+                mapdisp.GetWindow().ZoomHistory(region['n'],
+                                                region['s'],
+                                                region['e'],
+                                                region['w'])
+                
+            mapdisp.Show()
+            
+            displayId += 1
     
-            maptree = None 
-            selected = [] # list of selected layers
-            # 
-            # load list of map layers
-            #
-            for layer in gxwXml.layers:
-                display = layer['display']
-                maptree = self.gm_cb.GetPage(display).maptree
-
-                newItem = maptree.AddLayer(ltype=layer['type'],
-                                           lname=layer['name'],
-                                           lchecked=layer['checked'],
-                                           lopacity=layer['opacity'],
-                                           lcmd=layer['cmd'],
-                                           lgroup=layer['group'],
-                                           lnviz=layer['nviz'])
+        maptree = None 
+        selected = [] # list of selected layers
+        # 
+        # load list of map layers
+        #
+        for layer in gxwXml.layers:
+            display = layer['display']
+            maptree = self.gm_cb.GetPage(display).maptree
+            
+            newItem = maptree.AddLayer(ltype=layer['type'],
+                                       lname=layer['name'],
+                                       lchecked=layer['checked'],
+                                       lopacity=layer['opacity'],
+                                       lcmd=layer['cmd'],
+                                       lgroup=layer['group'],
+                                       lnviz=layer['nviz'],
+                                       lvdigit=layer['vdigit'])
+            
+            if layer.has_key('selected'):
+                if layer['selected']:
+                    selected.append((maptree, newItem))
+                else:
+                    maptree.SelectItem(newItem, select=False)
+            
+        for maptree, layer in selected:
+            if not maptree.IsSelected(layer):
+                maptree.SelectItem(layer, select=True)
+                maptree.layer_selected = layer
                 
-                # tag 'selected' added 2008/04
-                if layer.has_key('selected'):
-                    if layer['selected']:
-                        selected.append((maptree, newItem))
-                    else:
-                        maptree.SelectItem(newItem, select=False)
-                    
-            for maptree, layer in selected:
-                if not maptree.IsSelected(layer):
-                    maptree.SelectItem(layer, select=True)
-                    maptree.layer_selected = layer
-
-            busy.Destroy()
+        busy.Destroy()
+        
+        if maptree:
+            # reverse list of map layers
+            maptree.Map.ReverseListOfLayers()
             
-            if maptree:
-                # reverse list of map layers
-                maptree.Map.ReverseListOfLayers()
+        for mdisp in mapdisplay:
+            mdisp.MapWindow2D.UpdateMap()
 
-            for mdisp in mapdisplay:
-                mdisp.MapWindow2D.UpdateMap()
-
-            file.close()
-        except IOError, err:
-            wx.MessageBox(parent=self,
-                          message="%s <%s>. %s%s" % (_("Unable to read workspace file"),
-                                                     filename, os.linesep, err),
-                          caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
-            return False
-        except gcmd.GStdError, e:
-            print e
-            return False
-                               
         return True
 
     def OnWorkspaceLoad(self, event=None):
-        """Load given map layers into layer tree"""
+        """!Load given map layers into layer tree"""
         dialog = gdialogs.LoadMapLayersDialog(parent=self, title=_("Load map layers into layer tree"))
 
         if dialog.ShowModal() == wx.ID_OK:
@@ -829,9 +754,9 @@
             busy.Destroy()
 
     def OnWorkspaceLoadGrcFile(self, event):
-        """Load map layers from GRC file (Tcl/Tk GUI) into map layer tree"""
+        """!Load map layers from GRC file (Tcl/Tk GUI) into map layer tree"""
         dlg = wx.FileDialog(parent=self, message=_("Choose GRC file to load"),
-                            defaultDir=os.getcwd(), wildcard="*.grc")
+                            defaultDir=os.getcwd(), wildcard=_("Old GRASS Workspace File (*.grc)|*.grc"))
 
         filename = ''
         if dlg.ShowModal() == wx.ID_OK:
@@ -867,10 +792,9 @@
             maptree.Map.ReverseListOfLayers()
 
     def OnWorkspaceSaveAs(self, event=None):
-        """Save workspace definition to selected file"""
-
+        """!Save workspace definition to selected file"""
         dlg = wx.FileDialog(parent=self, message=_("Choose file to save current workspace"),
-                            defaultDir=os.getcwd(), wildcard="*.gxw", style=wx.FD_SAVE)
+                            defaultDir=os.getcwd(), wildcard=_("GRASS Workspace File (*.gxw)|*.gxw"), style=wx.FD_SAVE)
 
         filename = ''
         if dlg.ShowModal() == wx.ID_OK:
@@ -898,8 +822,7 @@
         self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
 
     def OnWorkspaceSave(self, event=None):
-        """Save file with workspace definition"""
-
+        """!Save file with workspace definition"""
         if self.workspaceFile:
             dlg = wx.MessageDialog(self, message=_("Workspace file <%s> already exists. "
                                                    "Do you want to overwrite this file?") % \
@@ -910,70 +833,72 @@
             else:
                 Debug.msg(4, "GMFrame.OnWorkspaceSave(): filename=%s" % self.workspaceFile)
                 self.SaveToWorkspaceFile(self.workspaceFile)
+                self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
+                self.workspaceChanged = False
         else:
             self.OnWorkspaceSaveAs()
 
     def SaveToWorkspaceFile(self, filename):
-        """Save layer tree layout to workspace file
-
+        """!Save layer tree layout to workspace file
+        
         Return True on success, False on error
         """
-
+        tmpfile = tempfile.TemporaryFile(mode='w+b')
         try:
-            file = open(filename, "w")
-        except IOError:
-            wx.MessageBox(parent=self,
-                          message=_("Unable to open workspace file <%s> for writing.") % filename,
-                          caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            workspace.WriteWorkspaceFile(lmgr = self, file = tmpfile)
+        except StandardError, e:
+            gcmd.GError(parent = self,
+                        message = _("Writing current settings to workspace file "
+                                    "failed."))
             return False
-
+        
         try:
-            workspace.WriteWorkspaceFile(lmgr=self, file=file)
-        except StandardError, e:
-            file.close()
-            wx.MessageBox(parent=self,
-                          message=_("Writing current settings to workspace file failed (%s)." % e),
-                          caption=_("Error"),
-                          style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            mfile = open(filename, "w")
+            tmpfile.seek(0)
+            for line in tmpfile.readlines():
+                mfile.write(line)
+        except IOError:
+            gcmd.GError(parent = self,
+                        message = _("Unable to open file <%s> for writing.") % filename)
             return False
-
-        file.close()
         
+        mfile.close()
+        
         return True
     
-    def OnWorkspaceClose(self, event=None):
-        """Close file with workspace definition
-
+    def OnWorkspaceClose(self, event = None):
+        """!Close file with workspace definition
+        
         If workspace has been modified ask user to save the changes.
         """
-
         Debug.msg(4, "GMFrame.OnWorkspaceClose(): file=%s" % self.workspaceFile)
-        self.workspaceFile = None
-        self.SetTitle(self.baseTitle)
-
-        displays = []
+        
+        displays = list()
         for page in range(0, self.gm_cb.GetPageCount()):
             displays.append(self.gm_cb.GetPage(page).maptree.mapdisplay)
         
         for display in displays:
             display.OnCloseWindow(event)
         
+        self.workspaceFile = None
+        self.workspaceChanged = False
+        self.SetTitle(self.baseTitle)
         self.disp_idx = 0
         self.curr_page = None
         
-
-    def RulesCmd(self, event):
+    def RulesCmd(self, event, cmd = ''):
         """
         Launches dialog for commands that need rules
         input and processes rules
         """
-        command = self.GetMenuCmd(event)
+        if event:
+            cmd = self.GetMenuCmd(event)
                 
-        if command[0] == 'r.colors' or command[0] == 'vcolors':
-            ctable = colorrules.ColorTable(self, cmd=command[0])
+        if cmd[0] == 'r.colors' or cmd[0] == 'vcolors':
+            ctable = colorrules.ColorTable(self, cmd=cmd[0])
             ctable.Show()
         else:
-            dlg = rules.RulesText(self, cmd=command)
+            dlg = rules.RulesText(self, cmd=cmd)
             dlg.CenterOnScreen()
             if dlg.ShowModal() == wx.ID_OK:
                 gtemp = utils.GetTempfile()
@@ -983,7 +908,7 @@
                 finally:
                     output.close()
     
-                cmdlist = [command[0],
+                cmdlist = [cmd[0],
                            'input=%s' % dlg.inmap,
                            'output=%s' % dlg.outmap,
                            'rules=%s' % gtemp]
@@ -1053,11 +978,16 @@
         
         # reset display mode
         os.environ['GRASS_RENDER_IMMEDIATE'] = 'TRUE'
-
+        
     def OnPreferences(self, event):
-        """General GUI preferences/settings"""
-        preferences.PreferencesDialog(parent=self).ShowModal()
+        """!General GUI preferences/settings"""
+        if not self.dialogs['preferences']:
+            dlg = preferences.PreferencesDialog(parent=self)
+            self.dialogs['preferences'] = dlg
+            self.dialogs['preferences'].CenterOnScreen()
 
+        self.dialogs['preferences'].ShowModal()
+        
     def DispHistogram(self, event):
         """
         Init histogram display canvas and tools
@@ -1082,96 +1012,77 @@
         self.profile.Refresh()
         self.profile.Update()
         
-    def OnMapCalculator(self, event):
+    def OnMapCalculator(self, event, cmd = ''):
         """!Init map calculator for interactive creation of mapcalc statements
         """
-        win = mapcalculator.MapCalcFrame(parent = self, title = _('GRASS GIS Raster Map Calculator'))
+
+        if event:
+            cmd = self.GetMenuCmd(event)
+
+        win = mapcalculator.MapCalcFrame(parent = self,
+                                         cmd=cmd[0])
         win.CentreOnScreen()
         win.Show()
         
-    def OnMapCalculator3D(self, event):
-        """!Init map calculator for interactive creation of mapcalc statements
+    def OnVectorCleaning(self, event, cmd = ''):
+        """!Init interactive vector cleaning
         """
-        win = mapcalculator.MapCalcFrame(parent = self, title = _('GRASS GIS Raster Map Calculator (3D raster)'),
-                                         rast3d = True)
+        
+        if event:
+            cmd = self.GetMenuCmd(event)
+
+        win = vclean.VectorCleaningFrame(parent = self, cmd = cmd[0])
         win.CentreOnScreen()
         win.Show()
         
-    def AddToolbarButton(self, toolbar, label, icon, help, handler):
-        """Adds button to the given toolbar"""
-
-        if not label:
-            toolbar.AddSeparator()
-            return
-        tool = toolbar.AddLabelTool(id=wx.ID_ANY, label=label, bitmap=icon, shortHelp=help)
-        self.Bind(wx.EVT_TOOL, handler, tool)
-
-    def ToolbarData(self):
-
-        return   (
-                 ('newdisplay', Icons["newdisplay"].GetBitmap(),
-                  Icons["newdisplay"].GetLabel(), self.OnNewDisplay),
-                 ('', '', '', ''),
-                 ('workspaceLoad', Icons["workspaceLoad"].GetBitmap(),
-                  Icons["workspaceLoad"].GetLabel(), self.OnWorkspace),
-                 ('workspaceOpen', Icons["workspaceOpen"].GetBitmap(),
-                  Icons["workspaceOpen"].GetLabel(), self.OnWorkspaceOpen),
-                 ('workspaceSave', Icons["workspaceSave"].GetBitmap(),
-                  Icons["workspaceSave"].GetLabel(), self.OnWorkspaceSave),
-                 ('', '', '', ''),
-                 ('addrast', Icons["addrast"].GetBitmap(),
-                  Icons["addrast"].GetLabel(), self.OnAddRaster),
-                 ('addshaded', Icons["addshaded"].GetBitmap(),
-                  _("Add various raster-based map layers"), self.OnAddRasterMisc),
-                 ('addvect', Icons["addvect"].GetBitmap(),
-                  Icons["addvect"].GetLabel(), self.OnAddVector),
-                 ('addthematic', Icons["addthematic"].GetBitmap(),
-                  _("Add various vector-based map layer"), self.OnAddVectorMisc),
-                 ('addcmd',  Icons["addcmd"].GetBitmap(),
-                  Icons["addcmd"].GetLabel(),  self.OnAddCommand),
-                 ('addgrp',  Icons["addgrp"].GetBitmap(),
-                  Icons["addgrp"].GetLabel(), self.OnAddGroup),
-                 ('addovl',  Icons["addovl"].GetBitmap(),
-                  Icons["addovl"].GetLabel(), self.OnAddOverlay),
-                 ('delcmd',  Icons["delcmd"].GetBitmap(),
-                  Icons["delcmd"].GetLabel(), self.OnDeleteLayer),
-                 ('', '', '', ''),
-                 ('attrtable', Icons["attrtable"].GetBitmap(),
-                  Icons["attrtable"].GetLabel(), self.OnShowAttributeTable)
-                  )
-
     def OnImportDxfFile(self, event):
-        """Convert multiple DXF layers to GRASS vector map layers"""
-        dlg = gdialogs.MultiImportDialog(parent=self, type='dxf',
-                                         title=_("Import DXF layers"))
+        """!Convert multiple DXF layers to GRASS vector map layers"""
+        dlg = gdialogs.DxfImportDialog(parent=self)
         dlg.ShowModal()
 
     def OnImportGdalLayers(self, event):
         """!Convert multiple GDAL layers to GRASS raster map layers"""
-        dlg = gdialogs.MultiImportDialog(parent=self, type='gdal',
-                                         title=_("Import raster data"))
+        dlg = gdialogs.GdalImportDialog(parent=self)
         dlg.ShowModal()
 
     def OnLinkGdalLayers(self, event):
         """!Link multiple GDAL layers to GRASS raster map layers"""
-        dlg = gdialogs.MultiImportDialog(parent=self, type='gdal',
-                                         title=_("Link raster data"),
-                                         link = True)
+        dlg = gdialogs.GdalImportDialog(parent=self, link = True)
         dlg.ShowModal()
         
     def OnImportOgrLayers(self, event):
-        """Convert multiple OGR layers to GRASS vector map layers"""
-        dlg = gdialogs.MultiImportDialog(parent=self, type='ogr',
-                                         title=_("Import vector data"))
+        """!Convert multiple OGR layers to GRASS vector map layers"""
+        dlg = gdialogs.GdalImportDialog(parent=self, ogr = True)
         dlg.ShowModal()
-    
+        
     def OnLinkOgrLayers(self, event):
         """!Links multiple OGR layers to GRASS vector map layers"""
-        dlg = gdialogs.MultiImportDialog(parent=self, type='ogr',
-                                         title=_("Link vector data"),
-                                         link = True)
+        dlg = gdialogs.GdalImportDialog(parent=self, ogr = True, link = True)
         dlg.ShowModal()
         
+    def OnImportWMS(self, event):
+        """!Import data from OGC WMS server"""
+        dlg = ogc_services.WMSDialog(parent = self, service = 'wms')
+        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]
+                    styles = ','.join(layers[layer])
+                    if styles:
+                        cmd.append('styles=%s' % styles)
+                    self.goutput.RunCmd(cmd, switchPage = True)
+            else:
+                self.goutput.WriteWarning(_("Nothing to import. No WMS layer selected."))
+        
+        dlg.Destroy()
+        
     def OnShowAttributeTable(self, event):
         """
         Show attribute table of the given vector map layer
@@ -1179,19 +1090,19 @@
         if not self.curr_page:
             self.MsgNoLayerSelected()
             return
-
+        
         layer = self.curr_page.maptree.layer_selected
         # no map layer selected
         if not layer:
             self.MsgNoLayerSelected()
             return
-
+        
         # available only for vector map layers
         try:
             maptype = self.curr_page.maptree.GetPyData(layer)[0]['maplayer'].type
         except:
             maptype = None
-
+        
         if not maptype or maptype != 'vector':
             wx.MessageBox(parent=self,
                           message=_("Attribute management is available only "
@@ -1199,42 +1110,35 @@
                           caption=_("Message"),
                           style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
             return
-
+        
         if not self.curr_page.maptree.GetPyData(layer)[0]:
             return
         dcmd = self.curr_page.maptree.GetPyData(layer)[0]['cmd']
         if not dcmd:
             return
         
-        mapname = utils.GetLayerNameFromCmd(dcmd)
-        
-        for option in dcmd:
-            if option.find('size') > -1:
-                size = option.split('=')[1]
-            elif option.find('icon') > -1:
-                icon = option.split('=')[1]
-        
         busy = wx.BusyInfo(message=_("Please wait, loading attribute data..."),
                            parent=self)
         wx.Yield()
-
-        self.dbmanager = dbm.AttributeManager(parent=self, id=wx.ID_ANY,
-                                              title="%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
-                                                                   mapname),
-                                              size=wx.Size(500,300), vectmap=mapname,
-                                              item=layer, log=self.goutput)
-
-
+        
+        dbmanager = dbm.AttributeManager(parent=self, id=wx.ID_ANY,
+                                         size=wx.Size(500, 300),
+                                         item=layer, log=self.goutput)
+        
         busy.Destroy()
-
-        self.dbmanager.Show()
-
+        
+        # register ATM dialog
+        self.dialogs['atm'].append(dbmanager)
+        
+        # show ATM window
+        dbmanager.Show()
+        
     def OnNewDisplay(self, event=None):
-        """Create new layer tree and map display instance"""
+        """!Create new layer tree and map display instance"""
         self.NewDisplay()
 
     def NewDisplay(self, show=True):
-        """Create new layer tree, which will
+        """!Create new layer tree, which will
         create an associated map display frame
 
         @param show show map display window if True
@@ -1242,20 +1146,20 @@
         @return reference to mapdisplay intance
         """
         Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.disp_idx)
-
+        
         # make a new page in the bookcontrol for the layer tree (on page 0 of the notebook)
         self.pg_panel = wx.Panel(self.gm_cb, id=wx.ID_ANY, style= wx.EXPAND)
         self.gm_cb.AddPage(self.pg_panel, text="Display "+ str(self.disp_idx + 1), select = True)
         self.curr_page = self.gm_cb.GetCurrentPage()
-
+        
         # create layer tree (tree control for managing GIS layers)  and put on new notebook page
-        self.curr_page.maptree = wxgui_utils.LayerTree(self.curr_page, id=wx.ID_ANY, pos=wx.DefaultPosition,
-                                                       size=wx.DefaultSize, style=wx.TR_HAS_BUTTONS
-                                                       |wx.TR_LINES_AT_ROOT|wx.TR_HIDE_ROOT
-                                                       |wx.TR_DEFAULT_STYLE|wx.NO_BORDER|wx.FULL_REPAINT_ON_RESIZE,
-                                                       idx=self.disp_idx, gismgr=self, notebook=self.gm_cb,
-                                                       auimgr=self._auimgr, showMapDisplay=show)
-
+        self.curr_page.maptree = layertree.LayerTree(self.curr_page, id=wx.ID_ANY, pos=wx.DefaultPosition,
+                                                     size=wx.DefaultSize, style=wx.TR_HAS_BUTTONS |
+                                                     wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
+                                                     wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
+                                                     idx=self.disp_idx, lmgr=self, notebook=self.gm_cb,
+                                                     auimgr=self._auimgr, showMapDisplay=show)
+        
         # layout for controls
         cb_boxsizer = wx.BoxSizer(wx.VERTICAL)
         cb_boxsizer.Add(self.curr_page.maptree, proportion=1, flag=wx.EXPAND, border=1)
@@ -1263,7 +1167,7 @@
         cb_boxsizer.Fit(self.curr_page.maptree)
         self.curr_page.Layout()
         self.curr_page.maptree.Layout()
-
+        
         # use default window layout
         if UserSettings.Get(group='general', key='defWindowPos', subkey='enabled') is True:
             dim = UserSettings.Get(group='general', key='defWindowPos', subkey='dim')
@@ -1275,61 +1179,61 @@
                 self.curr_page.maptree.mapdisplay.SetSize((w, h))
             except:
                 pass
- 
+        
         self.disp_idx += 1
-
+        
         return self.curr_page.maptree.mapdisplay
 
-    # toolBar button handlers
     def OnAddRaster(self, event):
-        """Add raster map layer"""
+        """!Add raster map layer"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
         
         self.AddRaster(event)
+
+    def OnAddRaster3D(self, event):
+        """!Add 3D raster map layer"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
         
+        self.AddRaster3D(event)
+        
     def OnAddRasterMisc(self, event):
-        """Add raster menu"""
+        """!Add raster menu"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
-
+            self.NewDisplay(show = True)
+        
         point = wx.GetMousePosition()
         rastmenu = wx.Menu()
-
-        # add items to the menu
-        if self.curr_page.maptree.mapdisplay.toolbars['nviz']:
-            addrast3d = wx.MenuItem(rastmenu, -1, Icons ["addrast3d"].GetLabel())
-            addrast3d.SetBitmap(Icons["addrast3d"].GetBitmap (self.iconsize))
-            rastmenu.AppendItem(addrast3d)
-            self.Bind(wx.EVT_MENU, self.AddRaster3d, addrast3d)
-
+        
         addshaded = wx.MenuItem(rastmenu, -1, Icons ["addshaded"].GetLabel())
         addshaded.SetBitmap(Icons["addshaded"].GetBitmap (self.iconsize))
         rastmenu.AppendItem(addshaded)
         self.Bind(wx.EVT_MENU, self.AddShaded, addshaded)
-
+        
         addrgb = wx.MenuItem(rastmenu, -1, Icons["addrgb"].GetLabel())
         addrgb.SetBitmap(Icons["addrgb"].GetBitmap(self.iconsize))
         rastmenu.AppendItem(addrgb)
         self.Bind(wx.EVT_MENU, self.AddRGB, addrgb)
-
+        
         addhis = wx.MenuItem(rastmenu, -1, Icons ["addhis"].GetLabel())
         addhis.SetBitmap(Icons["addhis"].GetBitmap (self.iconsize))
         rastmenu.AppendItem(addhis)
         self.Bind(wx.EVT_MENU, self.AddHIS, addhis)
-
+        
         addrastarrow = wx.MenuItem(rastmenu, -1, Icons ["addrarrow"].GetLabel())
         addrastarrow.SetBitmap(Icons["addrarrow"].GetBitmap (self.iconsize))
         rastmenu.AppendItem(addrastarrow)
         self.Bind(wx.EVT_MENU, self.AddRastarrow, addrastarrow)
-
+        
         addrastnums = wx.MenuItem(rastmenu, -1, Icons ["addrnum"].GetLabel())
         addrastnums.SetBitmap(Icons["addrnum"].GetBitmap (self.iconsize))
         rastmenu.AppendItem(addrastnums)
         self.Bind(wx.EVT_MENU, self.AddRastnum, addrastnums)
-
+        
         # Popup the menu.  If an item is selected then its handler
         # will be called before PopupMenu returns.
         self.PopupMenu(rastmenu)
@@ -1339,18 +1243,18 @@
         self.curr_page.maptree.mapdisplay.Show()
 
     def OnAddVector(self, event):
-        """Add vector map layer"""
+        """!Add vector map layer"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
         
         self.AddVector(event)
         
     def OnAddVectorMisc(self, event):
-        """Add vector menu"""
+        """!Add vector menu"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
 
         point = wx.GetMousePosition()
         vectmenu = wx.Menu()
@@ -1374,10 +1278,10 @@
         self.curr_page.maptree.mapdisplay.Show()
 
     def OnAddOverlay(self, event):
-        """Add overlay menu""" 
+        """!Add decoration overlay menu""" 
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
 
         point = wx.GetMousePosition()
         ovlmenu = wx.Menu()
@@ -1401,7 +1305,12 @@
         addrhumb.SetBitmap(Icons["addrhumb"].GetBitmap(self.iconsize))
         ovlmenu.AppendItem(addrhumb)
         self.Bind(wx.EVT_MENU, self.AddRhumb, addrhumb)
-
+        
+        addcmd = wx.MenuItem(ovlmenu, wx.ID_ANY, Icons["addcmd"].GetLabel())
+        addcmd.SetBitmap(Icons["addcmd"].GetBitmap(self.iconsize))
+        ovlmenu.AppendItem(addcmd)
+        self.Bind(wx.EVT_MENU, self.OnAddCommand, addcmd)
+        
         # Popup the menu.  If an item is selected then its handler
         # will be called before PopupMenu returns.
         self.PopupMenu(ovlmenu)
@@ -1411,58 +1320,59 @@
         self.curr_page.maptree.mapdisplay.Show()
 
     def AddRaster(self, event):
+        """!Add raster to the layer tree"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('raster')
 
-    def AddRaster3d(self, event):
+    def AddRaster3D(self, event):
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('3d-raster')
 
     def AddRGB(self, event):
-        """Add RGB layer"""
+        """!Add RGB layer"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('rgb')
 
     def AddHIS(self, event):
-        """Add HIS layer"""
+        """!Add HIS layer"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('his')
 
     def AddShaded(self, event):
-        """Add shaded relief map layer"""
+        """!Add shaded relief map layer"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('shaded')
 
     def AddRastarrow(self, event):
-        """Add raster flow arrows map"""
+        """!Add raster flow arrows map"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('rastarrow')
 
     def AddRastnum(self, event):
-        """Add raster map with cell numbers"""
+        """!Add raster map with cell numbers"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('rastnum')
 
     def AddVector(self, event):
-        """Add vector layer"""
+        """!Add vector layer"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('vector')
 
     def AddThemeMap(self, event):
-        """Add thematic map layer"""
+        """!Add thematic map layer"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('thememap')
 
     def AddThemeChart(self, event):
-        """Add thematic chart layer"""
+        """!Add thematic chart layer"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('themechart')
 
     def OnAddCommand(self, event):
-        """Add command line layer"""
+        """!Add command line layer"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
 
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('command')
@@ -1471,10 +1381,10 @@
         self.curr_page.maptree.mapdisplay.Show()
 
     def OnAddGroup(self, event):
-        """Add layer group"""
+        """!Add layer group"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
 
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('group')
@@ -1483,25 +1393,25 @@
         self.curr_page.maptree.mapdisplay.Show()
 
     def AddGrid(self, event):
-        """Add layer grid"""
+        """!Add layer grid"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('grid')
 
     def AddGeodesic(self, event):
-        """Add layer geodesic"""
+        """!Add layer geodesic"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('geodesic')
 
     def AddRhumb(self, event):
-        """Add layer rhumb"""
+        """!Add layer rhumb"""
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('rhumb')
 
     def OnAddLabels(self, event):
-        """Add layer vector labels"""
+        """!Add layer vector labels"""
         # start new map display if no display is available
         if not self.curr_page:
-            self.NewDisplay(show=False)
+            self.NewDisplay(show = True)
 
         self.notebook.SetSelection(0)
         self.curr_page.maptree.AddLayer('labels')
@@ -1520,7 +1430,7 @@
         if UserSettings.Get(group='manager', key='askOnRemoveLayer', subkey='enabled'):
             layerName = ''
             for item in self.curr_page.maptree.GetSelections():
-                name = self.curr_page.maptree.GetItemText(item)
+                name = str(self.curr_page.maptree.GetItemText(item))
                 idx = name.find('(opacity')
                 if idx > -1:
                     layerName += '<' + name[:idx].strip(' ') + '>,\n'
@@ -1550,15 +1460,42 @@
                 self.curr_page.maptree.DeleteChildren(layer)
             self.curr_page.maptree.Delete(layer)
         
+    def OnKeyDown(self, event):
+        """!Key pressed"""
+        kc = event.GetKeyCode()
+        
+        if event.ControlDown():
+            if kc == wx.WXK_TAB:
+                # switch layer list / command output
+                if self.notebook.GetSelection() == 0:
+                    self.notebook.SetSelection(1)
+                else:
+                    self.notebook.SetSelection(0)
+        
+        try:
+            ckc = chr(kc)
+        except ValueError:
+            event.Skip()
+            return
+        
+        if event.CtrlDown():
+            if kc == 'R':
+                self.OnAddRaster(None)
+            elif kc == 'V':
+                self.OnAddVector(None)
+        
+        event.Skip()
+
     def OnCloseWindow(self, event):
-        """Cleanup when wxGUI is quit"""
+        """!Cleanup when wxGUI is quitted"""
         if not self.curr_page:
             self._auimgr.UnInit()
             self.Destroy()
             return
         
         maptree = self.curr_page.maptree
-        if UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
+        if self.workspaceChanged and \
+                UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
             if self.workspaceFile:
                 message = _("Do you want to save changes in the workspace?")
             else:
@@ -1597,164 +1534,18 @@
         self.Destroy()
         
     def MsgNoLayerSelected(self):
-        """Show dialog message 'No layer selected'"""
+        """!Show dialog message 'No layer selected'"""
         wx.MessageBox(parent=self,
                       message=_("No map layer selected. Operation cancelled."),
                       caption=_("Message"),
                       style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-
-class AboutWindow(wx.Frame):
-    def __init__(self, parent):
-        """!Create custom About Window
-
-        @todo improve styling
-        """
-        wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400), 
-                          title=_('About GRASS GIS'))
-        
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
-        
-        panel = wx.Panel(parent = self, id = wx.ID_ANY)
-        
-        # version and web site
-        version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
-                                                                 flags = 'r',
-                                                                 read = True).splitlines()
-
-        infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
-        infoSizer = wx.BoxSizer(wx.VERTICAL)
-        logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
-        logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
-                                     bitmap = wx.Bitmap(name = logo,
-                                                        type = wx.BITMAP_TYPE_ICO))
-        infoSizer.Add(item = logoBitmap, proportion = 0,
-                      flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
-        
-        i = 0
-        for label in [version.replace('GRASS', 'GRASS GIS').strip() + '\n\n',
-                      _('Official GRASS site: http://grass.osgeo.org') + '\n\n',
-                      _('GIS Library') + ' ' + svn_gis_h_rev + '(' + svn_gis_h_date.split(' ')[1] + ')']:
-            info = wx.StaticText(parent = infoTxt,
-                                 id = wx.ID_ANY,
-                                 label = label)
-            if i == 0:
-                info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
-            infoSizer.Add(item = info, proportion = 0,
-                          flag = wx.TOP | wx.ALIGN_CENTER, border = 5)
-            i += 1
-            
-        # copyright information
-        copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
-        if os.path.exists(copyfile):
-            copyrightFile = open(copyfile, 'r')
-            copytext = copyrightFile.read()
-            copyrightFile.close()
-        else:
-            copytext = _('COPYING file missing')
-        # put text into a scrolling panel
-        copyrightwin = scrolled.ScrolledPanel(panel, id=wx.ID_ANY, 
-                                              size=wx.DefaultSize,
-                                              style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
-        copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext)
-        copyrightwin.SetAutoLayout(1)
-        copyrightwin.SetupScrolling()
-        copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
-        copyrightwin.sizer.Add(item=copyrighttxt, proportion=1,
-                               flag=wx.EXPAND | wx.ALL, border=1)
-        copyrightwin.SetSizer(copyrightwin.sizer)
-        copyrightwin.Layout()
-
-        # license
-        licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
-        if os.path.exists(licfile):
-            licenceFile = open(licfile, 'r')
-            license = ''.join(licenceFile.readlines())
-            licenceFile.close()
-        else:
-            license = _('GPL.TXT file missing')
-        # put text into a scrolling panel
-        licensewin = scrolled.ScrolledPanel(panel, id=wx.ID_ANY, 
-                                            size=wx.DefaultSize,
-                                            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
-        licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license)
-        licensewin.SetAutoLayout(1)
-        licensewin.SetupScrolling()
-        licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
-        licensewin.sizer.Add(item=licensetxt, proportion=1,
-                flag=wx.EXPAND | wx.ALL, border=1)
-        licensewin.SetSizer(licensewin.sizer)
-        licensewin.Layout()
-        
-        # credits
-        authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
-        if os.path.exists(authfile):
-            authorsFile = open(authfile, 'r')
-            authors = unicode(''.join(authorsFile.readlines()), "utf-8")
-            authorsFile.close()
-        else:
-            authors = _('AUTHORS file missing')
-        authorwin = scrolled.ScrolledPanel(panel, id=wx.ID_ANY, 
-                                           size=wx.DefaultSize,
-                                           style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
-        authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=authors)
-        authorwin.SetAutoLayout(1)
-        authorwin.SetupScrolling()
-        authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
-        authorwin.sizer.Add(item=authortxt, proportion=1,
-                flag=wx.EXPAND | wx.ALL, border=1)
-        authorwin.SetSizer(authorwin.sizer)
-        authorwin.Layout()      
-        
-        # create a flat notebook for displaying information about GRASS
-        nbstyle = FN.FNB_VC8 | \
-                FN.FNB_BACKGROUND_GRADIENT | \
-                FN.FNB_TABS_BORDER_SIMPLE | \
-                FN.FNB_NO_X_BUTTON
-        
-        if globalvar.hasAgw:
-            aboutNotebook = FN.FlatNotebook(panel, id=wx.ID_ANY, agwStyle=nbstyle)
-        else:
-            aboutNotebook = FN.FlatNotebook(panel, id=wx.ID_ANY, style=nbstyle)
-        aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
-        
-        # make pages for About GRASS notebook
-        pg1 = aboutNotebook.AddPage(infoTxt,    text=_("Info"))
-        pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright"))
-        pg3 = aboutNotebook.AddPage(licensewin,   text=_("License"))
-        pg4 = aboutNotebook.AddPage(authorwin,    text=_("Authors"))
-
-        wx.CallAfter(aboutNotebook.SetSelection, 0)
-        
-        # buttons
-        btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
-        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
-        btnSizer.Add(item = btnClose, proportion = 0,
-                     flag = wx.ALL | wx.ALIGN_RIGHT,
-                     border = 5)
-        # bindings
-        # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
-        btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
-
-        infoTxt.SetSizer(infoSizer)
-        infoSizer.Fit(infoTxt)
-        
-        sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(item=aboutNotebook, proportion=1,
-                  flag=wx.EXPAND | wx.ALL, border=1)
-        sizer.Add(item=btnSizer, proportion=0,
-                  flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
-        panel.SetSizer(sizer)
-        self.Layout()
     
-    def OnCloseWindow(self, event):
-        """!Close window"""
-        self.Close()
-        
 class GMApp(wx.App):
-    """
-    GMApp class
-    """
-    def __init__(self, workspace=None):
+    def __init__(self, workspace = None):
+        """!Main GUI class.
+
+        @param workspace path to the workspace file
+        """
         self.workspaceFile = workspace
         
         # call parent class initializer
@@ -1763,7 +1554,10 @@
         self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
         
     def OnInit(self):
-        # initialize all available image handlers
+        """!Initialize all available image handlers
+        
+        @return True
+        """
         wx.InitAllImageHandlers()
 
         # create splash screen
@@ -1773,7 +1567,7 @@
         wx.SplashScreen (bitmap=introBmp, splashStyle=wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
                          milliseconds=2000, parent=None, id=wx.ID_ANY)
         wx.Yield()
-
+        
         # create and show main frame
         mainframe = GMFrame(parent=None, id=wx.ID_ANY,
                             workspace = self.workspaceFile)
@@ -1788,7 +1582,7 @@
         self.msg = msg
 
 def printHelp():
-    """Print program help"""
+    """!Print program help"""
     print >> sys.stderr, "Usage:"
     print >> sys.stderr, " python wxgui.py [options]"
     print >> sys.stderr, "%sOptions:" % os.linesep
@@ -1796,7 +1590,7 @@
     sys.exit(0)
 
 def process_opt(opts, args):
-    """Process command-line arguments"""
+    """!Process command-line arguments"""
     workspaceFile = None
     for o, a in opts:
         if o in ("-h", "--help"):

Added: grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxm.dtd
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxm.dtd	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxm.dtd	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,101 @@
+<!--	grass-gxm.dtd
+
+	Copyright (C) 2010 by the GRASS Development Team
+	Author: Martin Landa <landa.martin gmail.com>
+	
+	This program is free software under the GPL (>=v2)
+	Read the file COPYING coming with GRASS for details.
+-->
+
+
+<!--
+        grass-gxm defines model file content
+-->
+
+<!ELEMENT grass-gxm (gxm)>
+
+<!ELEMENT gxm (action*, data*, loop*, properties?, variables?)>
+
+<!--    an action defines action properties (usually GRASS modules)
+-->
+<!ELEMENT action (task)>
+<!ATTLIST action id	CDATA #REQUIRED>
+<!ATTLIST action name	CDATA #REQUIRED>
+<!ATTLIST action pos	CDATA #REQUIRED>
+<!ATTLIST action size	CDATA #REQUIRED>
+
+<!--	a task describes the interface of a single
+	GRASS command
+-->
+<!ELEMENT task	        (flag*, parameter*, disabled?)>
+<!ATTLIST task	name	CDATA #REQUIRED>
+
+<!--	defines task to be enabled/disabled
+-->
+<!ELEMENT disabled    EMPTY>
+
+<!--	a parameter must have a name and a value
+-->
+<!ELEMENT parameter     (value, parameterized?)>
+<!ATTLIST parameter	name	CDATA #REQUIRED>
+
+<!--    value of parameter
+-->
+<!ELEMENT value         (#PCDATA)>
+
+<!--	parameterized
+-->
+<!ELEMENT parameterized EMPTY>
+
+<!--    enabled flag
+-->
+<!ELEMENT flag          EMPTY>
+<!ATTLIST flag          name    CDATA #REQUIRED>
+<!ATTLIST parameterized         (0|1) #IMPLIED>
+
+<!--    a data defines data properties (usually data layers)
+-->
+<!ELEMENT data (data-parameter, intermediate?, relation*)>
+<!ATTLIST data pos	CDATA #REQUIRED>
+<!ATTLIST data size	CDATA #REQUIRED>
+
+<!--	a data-parameter defines data items properties
+-->
+<!ELEMENT data-parameter  (value)>
+<!ATTLIST data-parameter  prompt	CDATA #REQUIRED>
+
+<!--	a data intermediate?
+-->
+<!ELEMENT intermediate    EMPTY>
+
+<!--	a relation defines relation between data and actions
+-->
+<!ELEMENT relation  (point*)>
+<!ATTLIST relation  id  	CDATA       #REQUIRED>
+<!ATTLIST relation  dir	        (from | to) #REQUIRED>
+<!ATTLIST relation  name  	CDATA       #REQUIRED>
+
+<!--	a point defines control point of linear shape
+-->
+<!ELEMENT point  (x, y)>
+<!ELEMENT x      (#PCDATA)>
+<!ELEMENT y      (#PCDATA)>
+
+<!--	a properties describes model properties
+-->
+<!ELEMENT properties    (name?, description?, author?, flag*)>
+<!ELEMENT name          (#PCDATA)>
+<!ELEMENT description   (#PCDATA)>
+<!ELEMENT author        (#PCDATA)>
+
+<!--	a variable describes model variable
+-->
+<!ELEMENT variables       (variable*)>
+<!ELEMENT variable        (description?, value?)>
+<!ATTLIST variable  name  CDATA                      #REQUIRED>
+<!ATTLIST variable  type  (integer | float | string) #REQUIRED>
+
+<!--	a loop describes model loops
+-->
+<!ELEMENT loop       (condition)>
+<!ELEMENT condition  (#PCDATA)>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxw.dtd
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxw.dtd	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/grass-gxw.dtd	2010-10-14 15:39:14 UTC (rev 43911)
@@ -30,13 +30,20 @@
 
         render, mode and showCompExtent (added 2004/08)
 -->
-<!ELEMENT display ((group|layer)*)>
+<!ELEMENT display (projection?, (group|layer)*)>
 <!ATTLIST display  render	   (0 | 1) #IMPLIED>
 <!ATTLIST display  mode            (0 | 1 | 2 | 3 | 4 | 5 | 6) #IMPLIED>
 <!ATTLIST display  showCompExtent  (0 | 1) #IMPLIED>
+<!ATTLIST display  constrainRes    (0 | 1) #IMPLIED>
 <!-- window dimenstion (x, y (ur), width, heigth -->
 <!ATTLIST display  dim             CDATA   #IMPLIED>
+<!ATTLIST display  extent          CDATA   #IMPLIED>
 
+<!-- projection (statusbar coordinates)
+-->
+<!ELEMENT projection   (value?)>
+<!ATTLIST projection   epsg	CDATA   #IMPLIED>
+
 <!--    group of map layers
 -->
 <!ELEMENT group (layer*)>
@@ -75,6 +82,19 @@
 <!ELEMENT flag          EMPTY>
 <!ATTLIST flag          name            CDATA #REQUIRED>
 
+<!-- *********************** Vector digitizer *********************** -->
+
+<!--    vdigit layer properties
+-->
+<!ELEMENT vdigit	    (geometryAttribute?)>
+
+<!--    geometry attribute parameter
+-->
+<!ELEMENT geometryAttribute EMPTY>
+<!ATTLIST parameter	type	(length | area | perimeter) #REQUIRED>
+<!ATTLIST parameter	column	CDATA                       #REQUIRED>
+<!ATTLIST parameter	units	CDATA                       #IMPLIED>
+
 <!-- *********************** Nviz *********************** -->
 
 <!--    nviz layer properties

Modified: grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml	2010-10-14 15:24:19 UTC (rev 43910)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml	2010-10-14 15:39:14 UTC (rev 43911)
@@ -1,7 +1,7 @@
 <menudata>
   <menubar>
     <menu>
-      <label>File</label>
+      <label>&amp;File</label>
       <items>
 	<menu>
 	  <label>Workspace</label>
@@ -9,39 +9,43 @@
 	    <menuitem>
 	      <label>New</label>
 	      <help>Create new workspace</help>
-	      <handler>self.OnWorkspaceNew</handler>
+	      <handler>OnWorkspaceNew</handler>
+	      <shortcut>Ctrl+N</shortcut>
 	    </menuitem>
 	    <menuitem>
 	      <label>Open</label>
 	      <help>Load workspace from file</help>
-	      <handler>self.OnWorkspaceOpen</handler>
+	      <handler>OnWorkspaceOpen</handler>
+	      <shortcut>Ctrl+O</shortcut>
 	    </menuitem>
 	    <menuitem>
 	      <label>Save</label>
-	      <help>Save workspace to open file</help>
-	      <handler>self.OnWorkspaceSave</handler>
+	      <help>Save workspace</help>
+	      <handler>OnWorkspaceSave</handler>
+	      <shortcut>Ctrl+S</shortcut>
 	    </menuitem>
 	    <menuitem>
 	      <label>Save as</label>
 	      <help>Save workspace to file</help>
-	      <handler>self.OnWorkspaceSaveAs</handler>
+	      <handler>OnWorkspaceSaveAs</handler>
 	    </menuitem>
 	    <menuitem>
 	      <label>Close</label>
-	      <help>Close loaded workspace</help>
-	      <handler>self.OnWorkspaceClose</handler>
+	      <help>Close workspace file</help>
+	      <handler>OnWorkspaceClose</handler>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Load map layers</label>
 	      <help>Load map layers into layer tree</help>
-	      <handler>self.OnWorkspaceLoad</handler>
+	      <handler>OnWorkspaceLoad</handler>
+	      <shortcut>Ctrl+L</shortcut>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Load GRC file (Tcl/Tk GUI)</label>
 	      <help>Load map layers from GRC file to layer tree</help>
-	      <handler>self.OnWorkspaceLoadGrcFile</handler>
+	      <handler>OnWorkspaceLoadGrcFile</handler>
 	    </menuitem>
 	  </items>
 	</menu>
@@ -52,95 +56,105 @@
 	    <menuitem>
 	      <label>Import raster data</label>
 	      <help>Import GDAL supported raster file into a binary raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.gdal</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Bulk import of raster data</label>
 	      <help>Converts multiple GDAL supported layers to GRASS raster maps.</help>
-	      <handler>self.OnImportGdalLayers</handler>
+	      <handler>OnImportGdalLayers</handler>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Link raster data</label>
-	      <help>Link GDAL supported raster data as a pseudo GRASS raster map layer.</help>
+	      <help>Link GDAL supported raster file to a binary raster map layer.</help>
 	      <keywords>raster,import</keywords>
-              <handler>self.OnMenuCmd</handler>
+              <handler>OnMenuCmd</handler>
 	      <command>r.external</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Bulk link of raster data</label>
 	      <help>Link multiple GDAL supported raster maps as pseudo GRASS raster map layers.</help>
-              <handler>self.OnLinkGdalLayers</handler>
+              <handler>OnLinkGdalLayers</handler>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Aggregate ASCII xyz import</label>
 	      <help>Create a raster map from an assemblage of many coordinates using univariate statistics.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,import,LIDAR</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.xyz</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>ASCII grid import</label>
 	      <help>Converts ASCII raster file to binary raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,import,conversion</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.ascii</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>ASCII polygons and lines import</label>
 	      <help>Creates raster maps from ASCII polygon/line/point data files.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.poly</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Binary file import</label>
 	      <help>Import a binary raster file into a GRASS raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.bin</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>ESRI ASCII grid import</label>
 	      <help>Converts an ESRI ARC/INFO ascii raster file (GRID) into a (binary) raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.arc</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>GRIDATB.FOR import</label>
 	      <help>Imports GRIDATB.FOR map file (TOPMODEL) into GRASS raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.gridatb</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>MAT-File (v.4) import</label>
+	      <label>Matlab 2D array import</label>
 	      <help>Imports a binary MAT-File(v4) to a GRASS raster.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.mat</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>SPOT NDVI import</label>
 	      <help>Import of SPOT VGT NDVI file into a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,imagery,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.in.spotvgt</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>SRTM HGT import</label>
 	      <help>Import SRTM HGT files into GRASS</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.srtm</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Terra ASTER HDF import</label>
 	      <help>Georeference, rectify and import Terra-ASTER imagery and relative DEM's using gdalwarp.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,imagery,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.in.aster</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>WMS import</label>
-	      <help>Downloads and imports data from WMS servers.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>r.in.wms</command>
+	      <help>Downloads and imports data from WMS servers</help>
+	      <handler>OnImportWMS</handler>
 	    </menuitem>
 	  </items>
 	</menu>
@@ -150,87 +164,104 @@
 	    <menuitem>
 	      <label>Import vector data</label>
 	      <help>Convert OGR vector layers to GRASS vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.ogr</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Bulk import of vector data</label>
 	      <help>Converts selected OGR layers to GRASS vector maps using v.in.ogr</help>
-	      <handler>self.OnImportOgrLayers</handler>
+	      <handler>OnImportOgrLayers</handler>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Link vector data</label>
-	      <help>Creates a new pseudo-vector map as a link to an OGR-supported layer.</help>
-	      <keywords>vector,external,ogr</keywords>
-              <handler>self.OnMenuCmd</handler>
+	      <help>Creates a new vector as a read-only link to OGR layer.</help>
+	      <keywords>vector</keywords>
+              <handler>OnMenuCmd</handler>
 	      <command>v.external</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Bulk link of vector data</label>
 	      <help>Creates multiple new pseduo-vector maps as a read-only links using OGR.</help>
-              <handler>self.OnLinkOgrLayers</handler>
+              <handler>OnLinkOgrLayers</handler>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>ASCII points/GRASS ASCII vector import</label>
 	      <help>Creates a vector map from ASCII points file or ASCII vector file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.ascii</command>
 	    </menuitem>
 	    <menuitem>
+	      <label>ASCII points as a vector lines</label>
+	      <help>Import ASCII x,y[,z] coordinates as a series of lines.</help>
+	      <keywords>vector,import</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.in.lines</command>
+	    </menuitem>
+	    <menuitem>
 	      <label>Old GRASS vector import</label>
 	      <help>Imports older versions of GRASS vector maps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,conversion</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.convert</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>DXF import</label>
 	      <help>Converts files in DXF format to GRASS vector map format.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.dxf</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Bulk import of DXF</label>
 	      <help>Converts multiple DXF layers to GRASS vector maps.</help>
-	      <handler>self.OnImportDxfFile</handler>
+	      <handler>OnImportDxfFile</handler>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>ESRI e00 import</label>
 	      <help>Import E00 file into a vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.e00</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Garmin GPS import</label>
 	      <help>Download waypoints, routes, and tracks from a Garmin GPS receiver into a vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,GPS</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.garmin</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>GPSBabel GPS import</label>
 	      <help>Import waypoints, routes, and tracks from a GPS receiver or GPS download file into a vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,GPS</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.gpsbabel</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Geonames import</label>
 	      <help>Imports geonames.org country files into a GRASS vector points map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,gazetteer</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.geonames</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>GEOnet import</label>
 	      <help>Imports US-NGA GEOnet Names Server (GNS) country files into a GRASS vector points map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,gazetteer</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.gns</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>Matlab-ASCII or Mapgen import</label>
-	      <help>Import Mapgen or Matlab-ASCII vector maps into GRASS.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <label>Matlab array and MapGen import</label>
+	      <help>Import Mapgen or Matlab vector maps into GRASS.</help>
+	      <keywords>vector,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.mapgen</command>
 	    </menuitem>
 	  </items>
@@ -241,13 +272,15 @@
 	    <menuitem>
 	      <label>ASCII 3D import</label>
 	      <help>Convert a 3D ASCII raster text file into a (binary) 3D raster map layer</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel,import</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.in.ascii</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Vis5D import</label>
 	      <help>import of 3-dimensional Vis5D files (i.e. the v5d file with 1 variable and 1 time step)</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.in.v5d</command>
 	    </menuitem>
 	  </items>
@@ -258,7 +291,8 @@
 	    <menuitem>
 	      <label>Multiple import formats using OGR</label>
 	      <help>Imports attribute tables in various formats.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>database,attribute table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.in.ogr</command>
 	    </menuitem>
 	  </items>
@@ -270,95 +304,110 @@
 	    <menuitem>
 	      <label>Common export formats</label>
 	      <help>Exports GRASS raster maps into GDAL supported formats.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.gdal</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>ASCII grid export</label>
 	      <help>Converts a raster map layer into an ASCII text file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.ascii</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>ASCII x,y,z export</label>
 	      <help>Export a raster map to a text file as x,y,z values based on cell centers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.xyz</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>ESRI ASCII grid export</label>
 	      <help>Converts a raster map layer into an ESRI ARCGRID file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.arc</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>GRIDATB.FOR export</label>
 	      <help>Exports GRASS raster map to GRIDATB.FOR map file (TOPMODEL)</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.gridatb</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>MAT-File (v.4) export</label>
+	      <label>Matlab 2D array export</label>
 	      <help>Exports a GRASS raster to a binary MAT-File.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.mat</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Binary export</label>
 	      <help>Exports a GRASS raster to a binary array.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.bin</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>MPEG-1 export</label>
 	      <help>Raster File Series to MPEG Conversion Program.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.mpeg</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>PNG export</label>
-	      <help>Export GRASS raster as non-georeferenced PNG image format.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Export GRASS raster as non-georeferenced PNG image.</help>
+	      <keywords>raster,png</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.png</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>PPM export</label>
 	      <help>Converts a GRASS raster map to a PPM image file at the pixel resolution of the currently defined region.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.ppm</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>PPM from RGB export</label>
 	      <help>Converts 3 GRASS raster layers (R,G,B) to a PPM image file at the pixel resolution of the CURRENTLY DEFINED REGION.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.ppm3</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>POV-Ray export</label>
 	      <help>Converts a raster map layer into a height-field file for POVRAY.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.pov</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>TIFF export</label>
 	      <help>Exports a GRASS raster map to a 8/24bit TIFF image file at the pixel resolution of the currently defined region.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.tiff</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>VRML export</label>
 	      <help>Export a raster map to the Virtual Reality Modeling Language (VRML)</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,export,VRML</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.vrml</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>VTK export</label>
 	      <help>Converts raster maps into the VTK-Ascii format</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.out.vtk</command>
 	    </menuitem>
 	  </items>
@@ -369,44 +418,51 @@
 	    <menuitem>
 	      <label>Common export formats</label>
 	      <help>Converts to one of the supported OGR vector formats.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.ogr</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>ASCII points/GRASS ASCII vector export</label>
 	      <help>Converts a GRASS binary vector map to a GRASS ASCII vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.ascii</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>DXF export</label>
 	      <help>Exports GRASS vector map layers to DXF file format.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.dxf</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Multiple GPS export formats using GPSBabel</label>
 	      <help>Exports a vector map to a GPS receiver or file format supported by GpsBabel.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,export,GPS</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.gpsbabel</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>POV-Ray export</label>
 	      <help>Converts to POV-Ray format, GRASS x,y,z -&gt; POV-Ray x,z,y</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.pov</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>SVG export</label>
 	      <help>Exports a GRASS vector map to SVG.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.svg</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>VTK export</label>
 	      <help>Converts a GRASS binary vector map to VTK ASCII output.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.out.vtk</command>
 	    </menuitem>
 	  </items>
@@ -417,19 +473,22 @@
 	    <menuitem>
 	      <label>ASCII 3D export</label>
 	      <help>Converts a 3D raster map layer into an ASCII text file</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel,export</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.out.ascii</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Vis5D export</label>
 	      <help>Export of GRASS 3D raster map to 3-dimensional Vis5D file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.out.v5d</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>VTK export</label>
 	      <help>Converts 3D raster maps (G3D) into the VTK-Ascii format</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.out.vtk</command>
 	    </menuitem>
 	  </items>
@@ -440,7 +499,8 @@
 	    <menuitem>
 	      <label>Multiple export formats using OGR</label>
 	      <help>Exports attribute tables into various formats.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>database,attribute table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.out.ogr</command>
 	    </menuitem>
 	  </items>
@@ -452,40 +512,46 @@
 	    <menuitem>
 	      <label>Copy</label>
 	      <help>Copies available data files in the user's current mapset search path and location to the appropriate element directories under the user's current mapset.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,map management</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.copy</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>List</label>
 	      <help>Lists available GRASS data base files of the user-specified data type to standard output.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,map management</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.list</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>List filtered</label>
 	      <help>Lists available GRASS data base files of the user-specified data type to standard output.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,map management</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.mlist</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Rename</label>
 	      <help>Renames data base element files in the user's current mapset.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,map management</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.rename</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Delete</label>
 	      <help>Removes data base element files from the user's current mapset.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,map management</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.remove</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Delete filtered</label>
 	      <help>Removes data base element files from the user's current mapset.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,map management</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.mremove</command>
 	    </menuitem>
 	  </items>
@@ -495,52 +561,60 @@
 	  <items>
 	    <menuitem>
 	      <label>Raster to vector</label>
-	      <help>Converts a raster map into a vector map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Converts a raster map into a vector map.</help>
+	      <keywords>raster,conversion,vectorization</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.to.vect</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Raster series to volume</label>
 	      <help>Converts 2D raster map slices to one 3D raster volume map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,volume,conversion</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.to.rast3</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Raster 2.5D to volume</label>
 	      <help>Creates a 3D volume map based on 2D elevation and value raster maps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,raster3d,voxel,conversion</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.to.rast3elev</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Vector to raster</label>
 	      <help>Converts a binary GRASS vector map into a GRASS raster map .</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,raster,conversion</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.to.rast</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Vector to volume</label>
 	      <help>Converts a binary GRASS vector map (only points) layer into a 3D GRASS raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,volume,conversion</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.to.rast3</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>2D vector to 3D vector</label>
 	      <help>Performs transformation of 2D vector features to 3D.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,transformation,3D</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.to.3d</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Sites to vector</label>
 	      <help>Converts a GRASS site_lists file into a vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,sites</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.sites</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Volume to raster series</label>
 	      <help>Converts 3D raster maps to 2D raster maps</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.to.rast</command>
 	    </menuitem>
 	  </items>
@@ -549,45 +623,72 @@
 	<menuitem>
 	  <label>Manage Ground Control Points</label>
 	  <help>Manage Ground Control Points for Georectification</help>
-	  <handler>self.OnGCPManager</handler>
+	  <handler>OnGCPManager</handler>
 	</menuitem>
 	<menuitem>
 	  <label>Georectify</label>
 	  <help>Georectify raster and vector maps</help>
-	  <handler>self.OnGeorectify</handler>
+	  <handler>OnGeorectify</handler>
 	</menuitem>
 	<separator />
 	<menuitem>
+	  <label>Graphical modeler</label>
+	  <help>Launch Graphical modeler</help>
+	  <handler>OnGModeler</handler>
+	</menuitem>
+	<menuitem>
+	  <label>Run model</label>
+	  <help>Run model prepared by Graphical modeler</help>
+	  <handler>OnRunModel</handler>
+	</menuitem>
+	<separator />
+	<menuitem>
 	  <label>NVIZ (requires Tcl/Tk)</label>
 	  <help>nviz - Visualization and animation tool for GRASS data.</help>
 	  <keywords>raster,vector,visualization</keywords>
-	  <handler>self.OnMenuCmd</handler>
+	  <handler>OnMenuCmd</handler>
 	  <command>nviz</command>
 	</menuitem>
+	<menuitem>
+	  <label>NVIZ (non-interactive)</label>
+	  <help>Experimental NVIZ CLI prototype.</help>
+	  <keywords>visualization,raster,vector,raster3d</keywords>
+	  <handler>OnMenuCmd</handler>
+	  <command>nviz_cmd</command>
+	</menuitem>
 	<separator />
 	<menuitem>
 	  <label>Bearing/distance to coordinates</label>
 	  <help>A simple utility for converting bearing and distance measurements to coordinates and vice versa.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>miscellaneous</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>m.cogo</command>
 	</menuitem>
 	<separator />
 	<menuitem>
 	  <label>Postscript plot</label>
 	  <help>Hardcopy PostScript map output utility.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>postscript,map,printing</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>ps.map</command>
 	</menuitem>
 	<separator />
 	<menuitem>
-	  <label>E&amp;xit</label>
-	  <help>Exit GUI</help>
-	  <handler>self.OnCloseWindow</handler>
+	  <label>Launch script</label>
+	  <help>Launches script file.</help>
+	  <handler>OnRunScript</handler>
 	</menuitem>
+	<separator />
+	<menuitem>
+	  <label>Exit GUI</label>
+	  <help>Quit wxGUI session</help>
+	  <handler>OnCloseWindow</handler>
+	  <shortcut>Ctrl+W</shortcut>
+	</menuitem>
       </items>
     </menu>
     <menu>
-      <label>Config</label>
+      <label>&amp;Settings</label>
       <items>
 	<menu>
 	  <label>Region</label>
@@ -595,13 +696,15 @@
 	    <menuitem>
 	      <label>Display region</label>
 	      <help>Manages the boundary definitions for the geographic region.</help>
-	      <handler>self.RunMenuCmd</handler>
+	      <keywords>general</keywords>
+	      <handler>RunMenuCmd</handler>
 	      <command>g.region -p</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Set region</label>
 	      <help>Manages the boundary definitions for the geographic region.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.region</command>
 	    </menuitem>
 	  </items>
@@ -612,42 +715,61 @@
 	    <menuitem>
 	      <label>Mapset access</label>
 	      <help>Set/unset access to other mapsets in current location</help>
-	      <handler>self.OnMapsets</handler>
+	      <handler>OnMapsets</handler>
 	    </menuitem>
 	    <menuitem>
+	      <label>User access</label>
+	      <help>Controls access to the current mapset for other users on the system.</help>
+	      <keywords>general</keywords>
+              <handler>OnMenuCmd</handler>
+	      <command>g.access</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Change working environment</label>
 	      <help>Change current mapset.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,settings</keywords>
+              <handler>OnMenuCmd</handler>
 	      <command>g.mapset</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>User access</label>
-	      <help>Controls access to the current mapset for other users on the system.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>g.access</command>
+	      <label>Change location and mapset</label>
+	      <help>Change current location and mapset.</help>
+              <handler>OnChangeLocation</handler>
 	    </menuitem>
 	    <menuitem>
+	      <label>Change mapset</label>
+	      <help>Change current mapset.</help>
+              <handler>OnChangeMapset</handler>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Show settings</label>
 	      <help>Outputs and modifies the user's current GRASS variable settings.</help>
-	      <handler>self.RunMenuCmd</handler>
-	      <command>g.gisenv --v</command>
+	      <keywords>general</keywords>
+              <handler>RunMenuCmd</handler>
+	      <command>g.gisenv -n</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Change settings</label>
 	      <help>Outputs and modifies the user's current GRASS variable settings.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general</keywords>
+              <handler>OnMenuCmd</handler>
 	      <command>g.gisenv</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Change default GUI</label>
 	      <help>Changes the default GRASS graphical user interface (GUI) setting.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,gui</keywords>
+              <handler>OnMenuCmd</handler>
 	      <command>g.change.gui.sh</command>
 	    </menuitem>
+	    <separator />
 	    <menuitem>
 	      <label>Version</label>
 	      <help>Displays version and copyright information.</help>
-	      <handler>self.RunMenuCmd</handler>
+	      <keywords>general</keywords>
+              <handler>RunMenuCmd</handler>
 	      <command>g.version -c</command>
 	    </menuitem>
 	  </items>
@@ -659,39 +781,51 @@
 	      <label>Display map projection</label>
 	      <help>Print projection information (in conventional GRASS format).</help>
 	      <keywords>general,projection</keywords>
-	      <handler>self.RunMenuCmd</handler>
+	      <handler>RunMenuCmd</handler>
 	      <command>g.proj -p</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Manage projections</label>
 	      <help>Converts co-ordinate system descriptions (i.e. projection information) between various formats (including GRASS format).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>general,projection</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>g.proj</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Change projection for current location</label>
 	      <help>Interactively reset the location's projection settings.</help>
-	      <handler>self.OnXTermNoXMon</handler>
+	      <keywords>general,projection</keywords>
+	      <handler>OnXTermNoXMon</handler>
 	      <command>g.setproj</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Convert coordinates</label>
 	      <help>Convert coordinates from one projection to another (cs2cs frontend).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>miscellaneous,projection</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>m.proj</command>
 	    </menuitem>
 	  </items>
 	</menu>
+	<separator />
+	    <menuitem>
+	      <label>Manage add-ons extensions</label>
+	      <help>Tool to maintain GRASS extensions in local GRASS installation.</help>
+	      <keywords>general,extensions</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>g.extension</command>
+	    </menuitem>
+	<separator />
 	<menuitem>
 	  <label>Preferences</label>
 	  <help>User GUI preferences (display font, commands, digitizer, etc.)</help>
-	  <handler>self.OnPreferences</handler>
+	  <handler>OnPreferences</handler>
 	</menuitem>
       </items>
     </menu>
     <menu>
-      <label>Raster</label>
+      <label>&amp;Raster</label>
       <items>
 	<menu>
 	  <label>Develop raster map</label>
@@ -699,91 +833,104 @@
 	    <menuitem>
 	      <label>Digitize raster (requires XTerm)</label>
 	      <help>Interactive tool used to draw and save vector features on a graphics monitor using a pointing device (mouse) and save to a raster map.</help>
-	      <handler>self.OnXTerm</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnXTerm</handler>
 	      <command>r.digit</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Compress/decompress</label>
 	      <help>Compresses and decompresses raster maps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.compress</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Region boundaries</label>
 	      <help>Sets the boundary definitions for a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.region</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Manage NULL values</label>
 	      <help>Manages NULL-values of given raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,null data</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.null</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Quantization</label>
 	      <help>Produces the quantization file for a floating-point map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.quant</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Timestamp</label>
 	      <help>Print/add/remove a timestamp for a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.timestamp</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Resample using aggregate statistics</label>
 	      <help>Resamples raster map layers to a coarser grid using aggregation.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,resample</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.resamp.stats</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Resample using multiple methods</label>
 	      <help>Resamples raster map layers to a finer grid using interpolation.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,resample</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.resamp.interp</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Resample using nearest neighbor</label>
 	      <help>GRASS raster map layer data resampling capability.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.resample</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Resample using spline tension</label>
 	      <help>Reinterpolates and optionally computes topographic analysis from input raster map to a new raster map (possibly with different resolution) using regularized spline with tension and smoothing.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.resamp.rst</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Support file maintenance</label>
 	      <help>Allows creation and/or modification of raster map layer support files.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,metadata</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.support</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Update map statistics</label>
 	      <help>Update raster map statistics</help>
 	      <keywords>raster,statistics</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.support.stats</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Reproject raster</label>
 	      <help>Re-projects a raster map from one location to the current location.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,projection</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.proj</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Tiling</label>
 	      <help>Produces tilings of the source projection for use in the destination region and projection.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,tiling</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.tileset</command>
 	    </menuitem>
 	  </items>
@@ -794,38 +941,44 @@
 	    <menuitem>
 	      <label>Color tables</label>
 	      <help>Creates/modifies the color table associated with a raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,color table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.colors</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Color tables (stddev)</label>
 	      <help>Set color rules based on stddev from a map's mean value.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,color table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.colors.stddev</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Color rules</label>
 	      <help>Creates/modifies the color table associated with a raster map layer.</help>
-	      <handler>self.RulesCmd</handler>
+	      <keywords>raster,color table</keywords>
+	      <handler>RulesCmd</handler>
 	      <command>r.colors</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Blend 2 color rasters</label>
 	      <help>Blends color components of two raster maps by a given ratio.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.blend</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Create RGB</label>
 	      <help>Combines red, green and blue raster maps into a single composite raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,composite</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.composite</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>RGB to HIS</label>
 	      <help>Generates red, green and blue raster map layers combining hue, intensity and saturation (HIS) values from user-specified input raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.his</command>
 	    </menuitem>
 	  </items>
@@ -836,13 +989,15 @@
         <menuitem>
           <label>Query values by coordinates</label>
           <help>Queries raster map layers on their category values and category labels.</help>
-          <handler>self.OnMenuCmd</handler>
+          <keywords>raster</keywords>
+          <handler>OnMenuCmd</handler>
           <command>r.what</command>
         </menuitem>
         <menuitem>
           <label>Query colors by value</label>
           <help>Queries colors for a raster map layer.</help>
-          <handler>self.OnMenuCmd</handler>
+          <keywords>raster</keywords>
+          <handler>OnMenuCmd</handler>
           <command>r.what.color</command>
         </menuitem>
 	  </items>
@@ -851,25 +1006,30 @@
 	<menuitem>
 	  <label>Buffer rasters</label>
 	  <help>Creates a raster map layer showing buffer zones surrounding cells that contain non-NULL category values.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster,buffer</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r.buffer</command>
 	</menuitem>
 	<menuitem>
 	  <label>Closest points</label>
 	  <help>Locates the closest points between objects in two raster maps.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r.distance</command>
 	</menuitem>
 	<menuitem>
 	  <label>Mask</label>
-	  <help>Create a MASK for limiting raster operation</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <help>Creates a mask for limiting raster operation.</help>
+	  <keywords>raster,mask</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r.mask</command>
 	</menuitem>
 	<menuitem>
 	  <label>Raster map calculator</label>
 	  <help>Map calculator for raster map algebra.</help>
-	  <handler>self.OnMapCalculator</handler>
+	  <keywords>raster,algebra</keywords>
+	  <handler>OnMapCalculator</handler>
+	  <command>r.mapcalc</command>
 	</menuitem>
 	<menu>
 	  <label>Neighborhood analysis</label>
@@ -877,13 +1037,15 @@
 	    <menuitem>
 	      <label>Moving window</label>
 	      <help>Makes each cell category value a function of the category values assigned to the cells around it, and stores new cell values in an output raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.neighbors</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Neighborhood points</label>
 	      <help>Makes each cell value a function of the attribute values assigned to the vector points or centroids around it, and stores new cell values in an output raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,raster,aggregation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.neighbors</command>
 	    </menuitem>
 	  </items>
@@ -894,26 +1056,30 @@
 	    <menuitem>
 	      <label>Cross product</label>
 	      <help>Creates a cross product of the category values from multiple raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.cross</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Raster series</label>
 	      <help>Makes each output cell value a function of the values assigned to the corresponding cells in the input raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,series</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.series</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch raster maps</label>
 	      <help>Creates a composite raster map layer by using known category values from one (or more) map layer(s) to fill in areas of "no data" in another map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.patch</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Statistical overlay</label>
 	      <help>Calculates category or object oriented statistics.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.statistics</command>
 	    </menuitem>
 	  </items>
@@ -924,13 +1090,15 @@
 	    <menuitem>
 	      <label>Solar irradiance and irradiation</label>
 	      <help>Solar irradiance and irradiation model.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.sun</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Shadows map</label>
-	      <help>Calculates cast shadow areas from sun position and DEM. Either A: exact sun position is specified, or B: date/time to calculate the sun position by r.sunmask itself.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Calculates cast shadow areas from sun position and elevation raster map.</help>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.sunmask</command>
 	    </menuitem>
 	  </items>
@@ -941,65 +1109,75 @@
 	    <menuitem>
 	      <label>Cumulative movement costs</label>
 	      <help>Outputs a raster map layer showing the anisotropic cumulative cost of moving between different geographic locations on an input elevation raster map layer whose cell category values represent elevation combined with an input raster map layer whose cell values represent friction cost.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.walk</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Cost surface</label>
 	      <help>Creates a raster map showing the cumulative cost of moving between different geographic locations on an input raster map whose cell category values represent cost.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,cost surface,cumulative costs</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.cost</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Least cost route or flow</label>
 	      <help>Traces a flow through an elevation model on a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.drain</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Shaded relief</label>
 	      <help>Creates shaded relief map from an elevation map (DEM).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,elevation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.shaded.relief</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Slope and aspect</label>
-	      <help>Generates raster map layers of slope, aspect, curvatures and partial derivatives from a raster map layer of true elevation values. Aspect is calculated counterclockwise from east.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Generates raster maps of slope, aspect, curvatures and partial derivatives from a elevation raster map.</help>
+	      <keywords>raster,terrain</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.slope.aspect</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Terrain parameters</label>
 	      <help>Extracts terrain parameters from a DEM.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,geomorphology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.param.scale</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Textural features</label>
 	      <help>Generate images with textural features from a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.texture</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Visibility</label>
 	      <help>Line-of-sight raster analysis program.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.los</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Distance to features</label>
 	      <help>Generates a raster map layer of distance to features in input layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.grow.distance</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Horizon angle</label>
 	      <help>Horizon angle computation from a digital elevation model.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.horizon</command>
 	    </menuitem>
 	  </items>
@@ -1010,19 +1188,22 @@
 	    <menuitem>
 	      <label>Clump</label>
 	      <help>Recategorizes data in a raster map by grouping cells that form physically discrete areas into unique categories.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics,reclass</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.clump</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Grow</label>
 	      <help>Generates a raster map layer with contiguous areas grown by one cell.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.grow</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Thin</label>
 	      <help>Thins non-zero cells that denote linear features in a raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.thin</command>
 	    </menuitem>
 	  </items>
@@ -1033,80 +1214,90 @@
 	  <items>
 	    <menuitem>
 	      <label>Carve stream channels</label>
-	      <help>Takes vector stream data, transforms it to raster and subtracts depth from the output DEM.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Generates stream channels.</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.carve</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Fill lake</label>
 	      <help>Fills lake from seed at given level.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.lake</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Depressionless map and flowlines</label>
-	      <help>Filters and generates a depressionless elevation map and a flow direction map from a given elevation layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Filters and generates a depressionless elevation map and a flow direction map from a given elevation raster map.</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.fill.dir</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Flow accumulation</label>
-	      <help>Flow computation for massive grids (Float version).</help>
-	      <keywords>raster</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Flow computation for massive grids (float version).</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.terraflow</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Flow lines</label>
-	      <help>Construction of slope curves (flowlines), flowpath lengths, and flowline densities (upslope areas) from a raster digital elevation model (DEM)</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Constructs flow lines.</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.flow</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>SIMWE Overland flow modeling</label>
-	      <help>Overland flow hydrologic simulation using path sampling method (SIMWE)</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Overland flow hydrologic simulation using path sampling method (SIMWE).</help>
+	      <keywords>raster,flow,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.sim.water</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>SIMWE Sediment flux modeling</label>
-	      <help>Sediment transport and erosion/deposition simulation using path sampling method (SIMWE)</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Sediment transport and erosion/deposition simulation using path sampling method (SIMWE).</help>
+	      <keywords>raster,sediment flow,erosion,deposition</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.sim.sediment</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Topographic index map</label>
-	      <help>Creates topographic index [ln(a/tan(beta))] map from elevation map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Creates topographic index map from elevation raster map.</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.topidx</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>TOPMODEL simulation</label>
 	      <help>Simulates TOPMODEL which is a physically based hydrologic model.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.topmodel</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Watershed subbasins</label>
-	      <help>Generates a raster map layer showing watershed subbasins.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Generates watershed subbasins raster map.</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.basins.fill</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Watershed analysis</label>
-	      <help>Watershed basin analysis program.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Watershed basin analysis program</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.watershed</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Watershed basin creation</label>
-	      <help>Watershed basin creation program.</help>
-	      <keywords>raster</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Creates watershed basins.</help>
+	      <keywords>raster,hydrology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.water.outlet</command>
 	    </menuitem>
 	  </items>
@@ -1114,7 +1305,8 @@
     <menuitem>
       <label>Groundwater modeling</label>
       <help>Numerical calculation program for transient, confined and unconfined groundwater flow in two dimensions.</help>
-      <handler>self.OnMenuCmd</handler>
+      <keywords>raster</keywords>
+      <handler>OnMenuCmd</handler>
       <command>r.gwflow</command>
     </menuitem>
 	<menu>
@@ -1123,26 +1315,30 @@
 	    <menuitem>
 	      <label>Set up (requires XTerm)</label>
 	      <help>Interactive tool used to setup the sampling and analysis framework that will be used by the other r.le programs.</help>
-	      <handler>self.OnXTerm</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnXTerm</handler>
 	      <command>r.le.setup</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Analyze landscape</label>
 	      <help>Contains a set of measures for attributes, diversity, texture, juxtaposition, and edge.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.le.pixel</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Analyze patches</label>
 	      <help>Calculates attribute, patch size, core (interior) size, shape, fractal dimension, and perimeter measures for sets of patches in a landscape.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.le.patch</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Output</label>
 	      <help>Displays the boundary of each r.le patch and shows how the boundary is traced, displays the attribute, size, perimeter and shape indices for each patch and saves the data in an output file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.le.trace</command>
 	    </menuitem>
 	  </items>
@@ -1153,89 +1349,103 @@
 	    <menuitem>
 	      <label>Set up sampling and analysis framework</label>
 	      <help>Configuration editor for r.li.'index'</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.setup</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Edge density</label>
 	      <help>Calculates edge density index on a raster map, using a 4 neighbour algorithm</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.edgedensity</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Contrast weighted edge density</label>
 	      <help>Calculates contrast weighted edge density index on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.cwed</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Patch area mean</label>
 	      <help>Calculates mean patch size index on a raster map, using a 4 neighbour algorithm</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.mps</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch area range</label>
 	      <help>Calculates range of patch area size on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.padrange</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch area Std Dev</label>
 	      <help>Calculates standard deviation of patch area a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.padsd</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch area Coeff Var</label>
 	      <help>Calculates coefficient of variation of patch area on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.padcv</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch density</label>
 	      <help>Calculates patch density index on a raster map, using a 4 neighbour algorithm</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.patchdensity</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch number</label>
 	      <help>Calculates patch number index on a raster map, using a 4 neighbour algorithm.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.patchnum</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Dominance's diversity</label>
 	      <help>Calculates dominance's diversity index on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,diversity index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.dominance</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Shannon's diversity</label>
 	      <help>Calculates Shannon's diversity index on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,diversity index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.shannon</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Simpson's diversity</label>
 	      <help>Calculates Simpson's diversity index on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,diversity index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.simpson</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Richness</label>
 	      <help>Calculates dominance's diversity index on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,dominance index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.richness</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Shape index</label>
 	      <help>Calculates shape index on a raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,landscape structure analysis,patch index</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.li.shape</command>
 	    </menuitem>
 	  </items>
@@ -1246,19 +1456,22 @@
 	    <menuitem>
 	      <label>Rate of spread</label>
 	      <help>Generates three, or four raster map layers showing 1) the base (perpendicular) rate of spread (ROS), 2) the maximum (forward) ROS, 3) the direction of the maximum ROS, and optionally 4) the maximum potential spotting distance.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.ros</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Least-cost spread paths</label>
 	      <help>Recursively traces the least cost path backwards to cells from which the cumulative cost was determined.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.spreadpath</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Anisotropic spread simulation</label>
 	      <help>Simulates elliptically anisotropic spread on a graphics window and generates a raster map of the cumulative time of spread, given raster maps containing the rates of spread (ROS), the ROS directions and the spread origins.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.spread</command>
 	    </menuitem>
 	  </items>
@@ -1270,52 +1483,60 @@
 	    <menuitem>
 	      <label>Interactively edit category values</label>
 	      <help>Interactively edit cell values in a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>display,raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>d.rast.edit</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Reclassify by size</label>
 	      <help>Reclasses a raster map greater or less than user specified area size (in hectares).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics,aggregation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.reclass.area</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Reclassify interactively</label>
 	      <help>Creates a new map layer whose category values are based upon a reclassification of the categories in an existing raster map layer.</help>
-	      <handler>self.RulesCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>RulesCmd</handler>
 	      <command>r.reclass</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Reclassify using rules file</label>
 	      <help>Creates a new map layer whose category values are based upon a reclassification of the categories in an existing raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.reclass</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Recode interactively</label>
 	      <help>Recodes categorical raster maps.</help>
-	      <handler>self.RulesCmd</handler>
+	      <keywords>raster,recode category</keywords>
+	      <handler>RulesCmd</handler>
 	      <command>r.recode</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Recode using rules file</label>
-	      <help>r.recode.rules - Use ascii rules file to recode categories in raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>r.recode.file - Use ascii rules file to recode categories in raster map</help>
+	      <keywords>raster,recode</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.recode.file</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Rescale</label>
 	      <help>Rescales the range of category values in a raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.rescale</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Rescale with histogram</label>
 	      <help>Rescales histogram equalized the range of category values in a raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.rescale.eq</command>
 	    </menuitem>
 	  </items>
@@ -1324,7 +1545,8 @@
 	<menuitem>
 	  <label>Concentric circles</label>
 	  <help>Creates a raster map containing concentric rings around a given point.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r.circle</command>
 	</menuitem>
 	<menu>
@@ -1333,13 +1555,15 @@
 	    <menuitem>
 	      <label>Random cells</label>
 	      <help>Generates random cell values with spatial dependence.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,random,cell</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.random.cells</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Random cells and vector points</label>
 	      <help>Creates a raster map layer and vector point map containing randomly located points.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.random</command>
 	    </menuitem>
 	  </items>
@@ -1350,40 +1574,46 @@
 	    <menuitem>
 	      <label>Fractal surface</label>
 	      <help>Creates a fractal surface of a given fractal dimension.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,DEM,fractal</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.fractal</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Gaussian kernel density surface</label>
 	      <help>Generates a raster density map from vector points data using a moving 2D isotropic Gaussian kernel or optionally generates a vector density map on vector network with a 1D kernel.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,kernel density</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.kernel</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Gaussian deviates surface</label>
 	      <help>GRASS module to produce a raster map layer of gaussian deviates whose mean and standard deviation can be expressed by the user. It uses a gaussian random number generator.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.gauss</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Plane</label>
 	      <help>Creates raster plane map given dip (inclination), aspect (azimuth) and one point.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,elevation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.plane</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Random deviates surface</label>
 	      <help>Produces a raster map layer of uniform random deviates whose range can be expressed by the user.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.random</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Random surface with spatial dependence</label>
 	      <help>Generates random surface(s) with spatial dependence.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,random,surface</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.random.surface</command>
 	    </menuitem>
 	  </items>
@@ -1391,7 +1621,8 @@
 	<menuitem>
 	  <label>Generate contour lines</label>
 	  <help>Produces a vector map of specified contours from a raster map.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster,DEM,contours,vector</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r.contour</command>
 	</menuitem>
 	<menu>
@@ -1400,52 +1631,68 @@
 	    <menuitem>
 	      <label>Bilinear from raster points</label>
 	      <help>Bilinear interpolation utility for raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.bilinear</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Bilinear and bicubic from vector points</label>
 	      <help>Bicubic or bilinear spline interpolation with Tykhonov regularization.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,interpolation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.surf.bspline</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>IDW from raster points</label>
 	      <help>Surface interpolation utility for raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,interpolation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.idw</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>IDW from raster points (alternate method for sparse points)</label>
 	      <help>Surface generation program.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.idw2</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>IDW from vector points</label>
 	      <help>Surface interpolation from vector point data by Inverse Distance Squared Weighting.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,interpolation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.surf.idw</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Raster contours</label>
 	      <help>Surface generation program from rasterized contours.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.contour</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Regularized spline tension</label>
 	      <help>Spatial approximation and topographic analysis from given point or isoline data in vector format to floating point raster format using regularized spline with tension.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.surf.rst</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
+	      <label>Ordinary or block kriging</label>
+	      <help>Performs ordinary or block kriging.</help>
+	      <keywords>vector,interpolation,kriging</keywords>
+              <handler>RunMenuCmd</handler>
+	      <command>v.krige.py</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Fill NULL cells</label>
 	      <help>Fills no-data areas in raster maps using v.surf.rst splines interpolation</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,elevation,interpolation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.fillnulls</command>
 	    </menuitem>
 	  </items>
@@ -1457,94 +1704,109 @@
 	    <menuitem>
 	      <label>Basic raster metadata</label>
 	      <help>Output basic information about a raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.info</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Manage category information</label>
 	      <help>Manages category values and labels associated with user-specified raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.category</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>General statistics</label>
 	      <help>Generates area statistics for raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.stats</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Quantiles for large data sets</label>
 	      <help>Compute quantiles using two passes.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.quantile</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Range of category values</label>
 	      <help>Prints terse list of category values found in a raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.describe</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Sum category values</label>
 	      <help>Sums up the raster cell values.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.sum</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Sum area by raster map and category</label>
 	      <help>Reports statistics for raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.report</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Statistics for clumped cells</label>
 	      <help>Calculates the volume of data "clumps", and (optionally) produces a GRASS vector points map containing the calculated centroids of these clumps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.volume</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Total corrected area</label>
 	      <help>Surface area estimation for rasters.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.surf.area</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Univariate raster statistics</label>
 	      <help>Calculates univariate statistics from the non-null cells of a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.univar</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Sample transects</label>
 	      <help>Outputs the raster map layer values lying on user-defined line(s).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.profile</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Sample transects (bearing/distance)</label>
 	      <help>Outputs raster map layer values lying along user defined transect line(s).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.transect</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Covariance/correlation</label>
 	      <help>Outputs a covariance/correlation matrix for user-specified raster map layer(s).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.covar</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Linear regression</label>
 	      <help>Calculates linear regression from two raster maps: y = a + b*x</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.regression.line</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Mutual category occurrences</label>
 	      <help>Tabulates the mutual occurrence (coincidence) of categories for two raster map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.coin</command>
 	    </menuitem>
 	  </items>
@@ -1552,7 +1814,7 @@
       </items>
     </menu>
     <menu>
-      <label>Vector</label>
+      <label>&amp;Vector</label>
       <items>
 	<menu>
 	  <label>Develop vector map</label>
@@ -1560,130 +1822,155 @@
 	    <menuitem>
 	      <label>Create new vector map</label>
 	      <help>Create new empty vector map</help>
-	      <handler>self.OnNewVector</handler>
+	      <handler>OnNewVector</handler>
 	    </menuitem>
 	    <menuitem>
 	      <label>Digitize vector map using Tcl/Tk digitizer</label>
 	      <help>Interactive editing and digitization of vector maps.</help>
-	      <keywords>vector, editing, digitization</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,editing,digitization</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.digit</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Edit vector map (non-interactively)</label>
 	      <help>Edits a vector map, allows adding, deleting and modifying selected vector features.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,editing,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.edit</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Create or rebuild topology</label>
 	      <help>Creates topology for GRASS vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,topology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.build</command>
 	    </menuitem>
 	    <menuitem>
+	      <label>Rebuild topology on all vector maps</label>
+	      <help>Rebuilds topology on all vector maps in the current mapset..</help>
+	      <keywords>vector,topology</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.build.all</command>
+	    </menuitem>
+	    <menuitem>
 	      <label>Clean vector map</label>
 	      <help>Toolset for cleaning topology of vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,topology</keywords>
+	      <handler>OnVectorCleaning</handler>
 	      <command>v.clean</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Smooth or simplify</label>
 	      <help>Vector based generalization.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,generalization,simplification,smoothing,displacement,network generalization</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.generalize</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Convert object types</label>
 	      <help>Change the type of geometry elements.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.type_wrapper.sh</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Add centroids</label>
 	      <help>Adds missing centroids to closed boundaries.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,centroid,area</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.centroids</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Build polylines</label>
 	      <help>Builds polylines from lines or boundaries.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry,topology</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.build.polylines</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Split lines</label>
 	      <help>Split lines to shorter segments.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.split</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Split polylines</label>
 	      <help>Creates points/segments from input vector lines and positions.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.segment</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Parallel lines</label>
 	      <help>Creates parallel line to input vector lines.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.parallel</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Dissolve boundaries</label>
 	      <help>Dissolves boundaries between adjacent areas sharing a common category number or attribute.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,area,dissolve</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.dissolve</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Create 3D vector over raster</label>
 	      <help>Converts vector map to 3D by sampling of elevation raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry,sampling</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.drape</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Extrude 3D vector map</label>
 	      <help>Extrudes flat vector object to 3D with defined height.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry,3D</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.extrude</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Create labels</label>
-	      <help>Creates paint labels for a vector map from attached attributes.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>v.label</command>
+	      <help>Create optimally placed labels for vector map(s)</help>
+	      <keywords>vector,paint labels</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.label.sa</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Create optimally placed labels</label>
-	      <help>Create optimally placed labels for vector map(s)</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>v.label.sa</command>
+	      <help>Creates paint labels for a vector map from attached attributes.</help>
+	      <keywords>vector,paint labels</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.label</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Reposition vector map</label>
 	      <help>Performs an affine transformation (shift, scale and rotate, or GPCs) on vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,transformation</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.transform</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Reproject vector map</label>
 	      <help>Allows projection conversion of vector maps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,projection</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.proj</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Support file maintenance</label>
 	      <help>Updates vector map metadata.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,metadata</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.support</command>
 	    </menuitem>
 	  </items>
@@ -1694,13 +1981,15 @@
 	    <menuitem>
 	      <label>Color tables</label>
 	      <help>Set color rules for features in a vector using a numeric attribute column.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,color table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.colors</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Color rules</label>
 	      <help>Set colors interactively by entering color rules</help>
-	      <handler>self.RulesCmd</handler>
+	      <handler>RulesCmd</handler>
+	      <keywords>vector, colors</keywords>
 	      <command>vcolors</command>
 	    </menuitem>
 	  </items>
@@ -1709,26 +1998,30 @@
 	<menuitem>
 	  <label>Query with attributes</label>
 	  <help>Selects vector objects from an existing vector map and creates a new map containing only the selected objects.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,extract</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.extract</command>
 	</menuitem>
 	<menuitem>
 	  <label>Query with coordinate(s)</label>
 	  <help>Queries a vector map layer at given locations.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,querying</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.what</command>
 	</menuitem>
 	<menuitem>
 	  <label>Query with another vector map</label>
 	  <help>Selects features from vector map (A) by features from other vector map (B).</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,spatial query</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.select</command>
 	</menuitem>
 	<separator />
 	<menuitem>
 	  <label>Buffer vectors</label>
 	  <help>Creates a buffer around features of given type (areas must contain centroid).</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,buffer</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.buffer</command>
 	</menuitem>
 	<menu>
@@ -1737,19 +2030,22 @@
 	    <menuitem>
 	      <label>Detect edges</label>
 	      <help>Detects the object's edges from a LIDAR data set.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LIDAR,edges</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lidar.edgedetection</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Detect interiors</label>
 	      <help>Building contour determination and Region Growing algorithm for determining the building inside</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LIDAR</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lidar.growing</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Correct and reclassify objects</label>
 	      <help>Correction of the v.lidar.growing output. It is the last of the three algorithms for LIDAR filtering.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LIDAR</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lidar.correction</command>
 	    </menuitem>
 	  </items>
@@ -1760,25 +2056,29 @@
 	    <menuitem>
 	      <label>Create LRS</label>
 	      <help>Create Linear Reference System</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LRS,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lrs.create</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Create stationing</label>
 	      <help>Create stationing from input lines, and linear reference system</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LRS,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lrs.label</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Create points/segments</label>
 	      <help>Creates points/segments from input lines, linear reference system and positions read from stdin or a file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LRS,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lrs.segment</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Find line id and offset</label>
 	      <help>Finds line id and real km+offset for given points in vector map using linear reference system.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,LRS,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.lrs.where</command>
 	    </menuitem>
 	  </items>
@@ -1786,58 +2086,127 @@
 	<menuitem>
 	  <label>Nearest features</label>
 	  <help>Finds the nearest element in vector map 'to' for elements in vector map 'from'.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,database,attribute table</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.distance</command>
 	</menuitem>
 	<menu>
 	  <label>Network analysis</label>
-	  <items>
+	  <items>`
 	    <menuitem>
+	      <label>Network maintenance</label>
+	      <help>Performs network maintenance.</help>
+	      <keywords>vector,networking</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Allocate subnets</label>
 	      <help>Allocate subnets for nearest centres (direction from centre).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.net.alloc</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>Network maintenance</label>
-	      <help>Performs network maintenance.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>v.net</command>
+	      <label>Split net</label>
+	      <help>Splits net by cost isolines.</help>
+	      <keywords>vector,networking</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.iso</command>
 	    </menuitem>
+	    <separator />
 	    <menuitem>
+	      <label>Shortest path</label>
+	      <help>Finds shortest path on vector network.</help>
+	      <keywords>vector,networking</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.path</command>
+	    </menuitem>
+	    <menuitem>
+	      <label>Shortest path for sets of features</label>
+	      <help>Computes shortest distance via the network between the given sets of features.</help>
+	      <keywords>vector,network,shortest path</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.distance</command>
+	    </menuitem>
+	    <menuitem>
+	      <label>Shortest path using timetables</label>
+	      <help>Finds shortest path using timetables.</help>
+	      <keywords>vector,network,shortest path</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.timetable</command>
+	    </menuitem>
+	    <menuitem>
+	      <label>Shortest path for all pairs</label>
+	      <help>Computes the shortest path between all pairs of nodes in the network.</help>
+	      <keywords>vector,network,shortest path</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.allpairs</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Visibility network</label>
 	      <help>Visibility graph construction.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,path,visibility</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.net.visibility</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>Shortest path</label>
-	      <help>Finds shortest path on vector network.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>v.net.path</command>
+	      <label>Bridges and articulation points</label>
+	      <help>Computes bridges and articulation points in the network.</help>
+	      <keywords>vector,network,articulation points</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.bridge</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>Display shortest route (requires XTerm)</label>
-	      <help>Find shortest path for selected starting and ending node</help>
-	      <handler>self.OnXTerm</handler>
-	      <command>d.path.sh</command>
+	      <label>Maximum flow</label>
+	      <help>Computes the maximum flow between two sets of nodes in the network.</help>
+	      <keywords>vector,network,flow</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.flow</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>Split net</label>
-	      <help>Splits net by cost isolines.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>v.net.iso</command>
+	      <label>Vertex connectivity</label>
+	      <help>Computes vertex connectivity between two sets of nodes in the network.</help>
+	      <keywords>vector,network,connectivity</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.connectivity</command>
 	    </menuitem>
 	    <menuitem>
+	      <label>Components</label>
+	      <help>Computes strongly and weakly connected components in the network.</help>
+	      <keywords>vector,network,components</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.components</command>
+	    </menuitem>
+	    <menuitem>
+	      <label>Centrality</label>
+	      <help>Computes degree, centrality, betweeness, closeness and eigenvector centrality measures in the network.</help>
+	      <keywords>vector,network,centrality measures</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.centrality</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Steiner tree</label>
 	      <help>Create Steiner tree for the network and given terminals</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.net.steiner</command>
 	    </menuitem>
 	    <menuitem>
+	      <label>Minimum spanning tree</label>
+	      <help>Computes minimum spanning tree for the network.</help>
+	      <keywords>vector,network,spanning tree</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>v.net.spanningtree</command>
+	    </menuitem>
+	    <menuitem>
 	      <label>Traveling salesman analysis</label>
 	      <help>Creates a cycle connecting given nodes (Traveling salesman problem).</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,networking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.net.salesman</command>
 	    </menuitem>
 	  </items>
@@ -1848,13 +2217,15 @@
 	    <menuitem>
 	      <label>Overlay vector maps</label>
 	      <help>Overlays two vector maps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.overlay</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Patch vector maps</label>
 	      <help>Create a new vector map layer by combining other vector map layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.patch</command>
 	    </menuitem>
 	  </items>
@@ -1866,19 +2237,22 @@
 	    <menuitem>
 	      <label>Manage or report categories</label>
 	      <help>Attach, delete or report vector categories to map geometry.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,category</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.category</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Reclassify objects interactively</label>
 	      <help>Changes vector category values for an existing vector map according to results of SQL queries or a value in attribute table column.</help>
-	      <handler>self.RulesCmd</handler>
+	      <keywords>vector,reclass,attributes</keywords>
+	      <handler>RulesCmd</handler>
 	      <command>v.reclass</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Reclassify objects using rules file</label>
 	      <help>Changes vector category values for an existing vector map according to results of SQL queries or a value in attribute table column.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,reclass,attributes</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.reclass</command>
 	    </menuitem>
 	  </items>
@@ -1887,7 +2261,8 @@
 	<menuitem>
 	  <label>Generate area for current region</label>
 	  <help>Create a new vector from the current region.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.in.region</command>
 	</menuitem>
 	<menu>
@@ -1895,20 +2270,23 @@
 	  <items>
 	    <menuitem>
 	      <label>Convex hull</label>
-	      <help>Uses a GRASS vector points map to produce a convex hull vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <help>Produces a convex hull for a given vector map.</help>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.hull</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Delaunay triangles</label>
 	      <help>Creates a Delaunay triangulation from an input vector map containing points or centroids.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.delaunay</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Voronoi diagram/Thiessen polygons</label>
 	      <help>Creates a Voronoi diagram from an input vector map containing points or centroids.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.voronoi</command>
 	    </menuitem>
 	  </items>
@@ -1916,7 +2294,8 @@
 	<menuitem>
 	  <label>Generate grid</label>
 	  <help>Creates a GRASS vector map of a user-defined grid.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.mkgrid</command>
 	</menuitem>
 	<menu>
@@ -1925,25 +2304,29 @@
 	    <menuitem>
 	      <label>Generate from database</label>
 	      <help>Creates new vector (points) map from database table containing coordinates.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,import,database,points</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.in.db</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Generate points along lines</label>
 	      <help>Create points along input lines in new vector with 2 layers.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,geometry</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.to.points</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Generate random points</label>
 	      <help>Randomly generate a 2D/3D vector points map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.random</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Perturb points</label>
 	      <help>Random location perturbations of GRASS vector points</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.perturb</command>
 	    </menuitem>
 	  </items>
@@ -1952,26 +2335,30 @@
 	<menuitem>
 	  <label>Remove outliers in point sets</label>
 	  <help>Removes outliers from vector point data.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,statistics</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.outlier</command>
 	</menuitem>
 	<menuitem>
 	  <label>Test/training point sets</label>
 	  <help>Randomly partition points into test/train sets.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,statistics</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.kcv</command>
 	</menuitem>
 	<separator />
 	<menuitem>
 	  <label>Update area attributes from raster</label>
-	  <help>Calculates univariate statistics from a GRASS raster map based on vector polygons and uploads statistics to new attribute columns.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <help>Calculates univariate statistics from a raster map based on vector polygons and uploads statistics to new attribute columns.</help>
+	  <keywords>vector,raster,statistics</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.rast.stats</command>
 	</menuitem>
 	<menuitem>
 	  <label>Update point attributes from areas</label>
 	  <help>Uploads vector values at positions of vector points to the table.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector,database,attribute table</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.what.vect</command>
 	</menuitem>
 	<menu>
@@ -1980,13 +2367,15 @@
 	    <menuitem>
 	      <label>Sample raster maps at point locations</label>
 	      <help>Uploads raster values at positions of vector points to the table.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,raster,attribute table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.what.rast</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Sample raster neighborhood around points</label>
 	      <help>Samples a raster map at vector point locations.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.sample</command>
 	    </menuitem>
 	  </items>
@@ -1998,52 +2387,60 @@
 	    <menuitem>
 	      <label>Basic vector metadata</label>
 	      <help>Outputs basic information about a user-specified vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,metadata,history</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.info</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Classify attribute data</label>
 	      <help>Classifies attribute data, e.g. for thematic mapping</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.class</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Report topology by category</label>
 	      <help>Reports geometry statistics for vectors.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,report,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.report</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Upload or report topology</label>
 	      <help>Populates database values from vector features.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,database,attribute table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.to.db</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Univariate attribute statistics for points</label>
 	      <help>Calculates univariate statistics for attribute. Variance and standard deviation is calculated only for points if specified.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.univar</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Univariate statistics for attribute columns</label>
 	      <help>Calculates univariate statistics on selected table column for a GRASS vector map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.univar</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Quadrat indices</label>
 	      <help>Indices for quadrat counts of sites lists.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.qcount</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Test normality</label>
 	      <help>Tests for normality for points.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>vector,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.normal</command>
 	    </menuitem>
 	  </items>
@@ -2051,7 +2448,7 @@
       </items>
     </menu>
     <menu>
-      <label>Imagery</label>
+      <label>&amp;Imagery</label>
       <items>
 	<menu>
 	  <label>Develop images and groups</label>
@@ -2059,20 +2456,23 @@
 	    <menuitem>
 	      <label>Create/edit group</label>
 	      <help>Creates, edits, and lists groups and subgroups of imagery files.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.group</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Target group</label>
 	      <help>Targets an imagery group to a GRASS location and mapset.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.target</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Mosaic images</label>
 	      <help>Mosaics up to 4 images and extends colormap; creates map *.mosaic</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,imagery,mosaicking</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.image.mosaic</command>
 	    </menuitem>
 	  </items>
@@ -2083,19 +2483,22 @@
 	    <menuitem>
 	      <label>Color balance for RGB</label>
 	      <help>Performs auto-balancing of colors for LANDSAT images.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,imagery,colors</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.landsat.rgb</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>HIS to RGB</label>
 	      <help>Transforms raster maps from HIS (Hue-Intensity-Saturation) color space to RGB (Red-Green-Blue) color space.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,color transformation,RGB,HIS</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.his.rgb</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>RGB to HIS</label>
 	      <help>Transforms raster maps from RGB (Red-Green-Blue) color space to HIS (Hue-Intensity-Saturation) color space.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,color transformation,RGB,HIS</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.rgb.his</command>
 	    </menuitem>
 	  </items>
@@ -2103,20 +2506,23 @@
 	<menuitem>
 	  <label>Rectify image or raster</label>
 	  <help>Rectifies an image by computing a coordinate transformation for each pixel in the image based on the control points.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>imagery,rectify</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>i.rectify</command>
 	</menuitem>
 	<menuitem>
 	  <label>Ortho photo rectification (requires Xterm)</label>
 	  <help>Menu driver for the photo imagery programs.</help>
-	  <handler>self.OnXTerm</handler>
+	  <keywords>imagery,orthorectify</keywords>
+	  <handler>OnXTerm</handler>
 	  <command>i.ortho.photo</command>
 	</menuitem>
 	<separator />
 	<menuitem>
 	  <label>Brovey sharpening</label>
 	  <help>Brovey transform to merge multispectral and high-res panchromatic channels</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>imagery,fusion,Brovey</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>i.fusion.brovey</command>
 	</menuitem>
 	<menu>
@@ -2125,39 +2531,45 @@
 	    <menuitem>
 	      <label>Clustering input for unsupervised classification</label>
 	      <help>Generates spectral signatures for land cover types in an image using a clustering algorithm.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,classification,signatures</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.cluster</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Maximum likelihood classification (MLC)</label>
 	      <help>Classifies the cell spectral reflectances in imagery data.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,classification,MLC</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.maxlik</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Sequential maximum a posteriori classification (SMAP)</label>
 	      <help>Performs contextual image classification using sequential maximum a posteriori (SMAP) estimation.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,classification,supervised,SMAP</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.smap</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Interactive input for supervised classification (requires Xterm)</label>
 	      <help>Generates spectral signatures for an image by allowing the user to outline regions of interest.</help>
-	      <handler>self.OnXTerm</handler>
+	      <keywords>imagery</keywords>
+	      <handler>OnXTerm</handler>
 	      <command>i.class</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Input for supervised MLC</label>
 	      <help>Generates statistics for i.maxlik from raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,classification,supervised,MLC</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.gensig</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Input for supervised SMAP</label>
 	      <help>Generates statistics for i.smap from raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,classification,supervised,SMAP</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.gensigset</command>
 	    </menuitem>
 	  </items>
@@ -2168,32 +2580,36 @@
 	    <menuitem>
 	      <label>Edge detection</label>
 	      <help>Zero-crossing "edge detection" raster function for image processing.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.zc</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Matrix/convolving filter</label>
 	      <help>Raster map matrix filter.</help>
-	      <handler>self.OnMenuCmd</handler>
-	      <command>r.mfilter.fp</command>
+	      <keywords>raster,map algebra</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>r.mfilter</command>
 	    </menuitem>
 	  </items>
 	</menu>
 	<menuitem>
 	  <label>Histogram</label>
 	  <help>Generate histogram of image</help>
-	  <handler>self.DispHistogram</handler>
+	  <handler>DispHistogram</handler>
 	</menuitem>
 	<menuitem>
 	  <label>Spectral response</label>
 	  <help>Displays spectral response at user specified locations in group or images.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>imagery,raster,multispectral</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>i.spectral</command>
 	</menuitem>
 	<menuitem>
 	  <label>Tasseled cap vegetation index</label>
 	  <help>Tasseled Cap (Kauth Thomas) transformation for LANDSAT-TM data</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster,imagery</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>i.tasscap</command>
 	</menuitem>
 	<menu>
@@ -2202,25 +2618,29 @@
 	    <menuitem>
 	      <label>Canonical correlation</label>
 	      <help>Canonical components analysis (cca) program for image processing.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.cca</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Principal components</label>
 	      <help>Principal components analysis (pca) program for image processing.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,image transformation,PCA</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.pca</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Fast Fourier</label>
 	      <help>Fast Fourier Transform (FFT) for image processing.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,FFT</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.fft</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Inverse Fast Fourier</label>
 	      <help>Inverse Fast Fourier Transform (IFFT) for image processing.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>imagery,FFT</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.ifft</command>
 	    </menuitem>
 	  </items>
@@ -2228,7 +2648,8 @@
 	<menuitem>
 	  <label>Atmospheric correction</label>
 	  <help>Performs atmospheric correction using the 6S algorithm.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>imagery,atmospheric correction</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>i.atcorr</command>
 	</menuitem>
 	<separator />
@@ -2238,19 +2659,22 @@
 	    <menuitem>
 	      <label>Bit pattern comparison </label>
 	      <help>Compares bit patterns with a raster map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.bitpattern</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Kappa analysis</label>
 	      <help>Calculate error matrix and kappa parameter for accuracy assessment of classification result.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r.kappa</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>OIF for LandSat TM</label>
 	      <help>Calculates Optimum-Index-Factor table for LANDSAT TM bands 1-5, &amp; 7</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster,imagery,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>i.oif</command>
 	    </menuitem>
 	  </items>
@@ -2258,7 +2682,7 @@
       </items>
     </menu>
     <menu>
-      <label>Volumes</label>
+      <label>V&amp;olumes</label>
       <items>
 	<menu>
 	  <label>Develop volumes</label>
@@ -2266,13 +2690,15 @@
 	    <menuitem>
 	      <label>Manage 3D NULL values</label>
 	      <help>Explicitly create the 3D NULL-value bitmap file.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.null</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Manage timestamp</label>
 	      <help>Print/add/remove a timestamp for a 3D raster map</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.timestamp</command>
 	    </menuitem>
 	  </items>
@@ -2281,30 +2707,36 @@
 	<menuitem>
 	  <label>3D Mask</label>
 	  <help>Establishes the current working 3D raster mask.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster3d,voxel</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r3.mask</command>
 	</menuitem>
 	<menuitem>
 	  <label>Volume calculator</label>
 	  <help>Map calculator for 3D raster map algebra.</help>
-	  <handler>self.OnMapCalculator3D</handler>
+	  <keywords>raster,algebra</keywords>
+	  <handler>OnMapCalculator</handler>
+	  <command>r3.mapcalc</command>
 	</menuitem>
 	<menuitem>
 	  <label>Cross section</label>
 	  <help>Creates cross section 2D raster map from 3d raster map based on 2D elevation map</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>raster3d,voxel</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>r3.cross.rast</command>
 	</menuitem>
     <menuitem>
       <label>Groundwater modeling</label>
       <help>Numerical calculation program for transient, confined groundwater flow in three dimensions</help>
-      <handler>self.OnMenuCmd</handler>
+      <keywords>raster3d,voxel</keywords>
+      <handler>OnMenuCmd</handler>
       <command>r3.gwflow</command>
     </menuitem>
    	<menuitem>
 	  <label>Interpolate volume from points</label>
 	  <help>Interpolates point data to a G3D grid volume using regularized spline with tension (RST) algorithm.</help>
-	  <handler>self.OnMenuCmd</handler>
+	  <keywords>vector</keywords>
+	  <handler>OnMenuCmd</handler>
 	  <command>v.vol.rst</command>
 	</menuitem>
 	<separator />
@@ -2314,19 +2746,22 @@
 	    <menuitem>
 	      <label>Basic volume metadata</label>
 	      <help>Outputs basic information about a user-specified 3D raster map layer.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,voxel</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.info</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Voxel statistics</label>
 	      <help>Generates volume statistics for raster3d maps.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.stats</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Univariate statistics for volumes</label>
 	      <help>Calculates univariate statistics from the non-null 3d cells of a raster3d map.</help>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>raster3d,statistics</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>r3.univar</command>
 	    </menuitem>
 	  </items>
@@ -2334,7 +2769,7 @@
       </items>
     </menu>
     <menu>
-      <label>Database</label>
+      <label>&amp;Database</label>
       <items>
 	<menu>
 	  <label>Database information</label>
@@ -2343,28 +2778,28 @@
 	      <label>List drivers</label>
 	      <help>List all database drivers.</help>
 	      <keywords>database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.drivers</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>List tables</label>
 	      <help>Lists all tables for a given database.</help>
 	      <keywords>database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.tables</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Describe table</label>
 	      <help>Describes a table in detail.</help>
 	      <keywords>database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.describe</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>List columns</label>
 	      <help>List all columns for a given table.</help>
 	      <keywords>database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.columns</command>
 	    </menuitem>
 	  </items>
@@ -2376,30 +2811,30 @@
 	    <menuitem>
 	      <label>Connect</label>
 	      <help>Prints/sets general DB connection for current mapset and exits.</help>
-	      <keywords>database,attribute table,connection settings</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>database,attribute table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.connect</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Login</label>
 	      <help>Sets user/password for driver/database.</help>
-	      <keywords>database,connection</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>database,SQL</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.login</command>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
 	      <label>Drop table</label>
 	      <help>Drops an attribute table.</help>
-	      <keywords>database,SQL</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <keywords>database,attribute table</keywords>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.droptable</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Copy table</label>
 	      <help>Copy a table.</help>
 	      <keywords>database,attribute table,SQL</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.copy</command>
 	    </menuitem>
 	    <separator />
@@ -2407,7 +2842,7 @@
 	      <label>Test</label>
 	      <help>Test database driver, database must exist and set by db.connect.</help>
 	      <keywords>database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.test</command>
 	    </menuitem>
 	  </items>
@@ -2417,23 +2852,23 @@
 	  <items>
 	    <menuitem>
 	      <label>Query any table</label>
-	      <help>Selects data from table.</help>
+	      <help>Selects data from attribute table (performs SQL query statement(s)).</help>
 	      <keywords>database,attribute table,SQL</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.select</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Query vector attribute data</label>
 	      <help>Prints vector map attributes.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.select</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>SQL statement</label>
 	      <help>Executes any SQL statement.</help>
 	      <keywords>database,attribute table,SQL</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>db.execute</command>
 	    </menuitem>
 	  </items>
@@ -2446,21 +2881,21 @@
 	      <label>New table</label>
 	      <help>Creates and connects a new attribute table to a given layer of an existing vector map.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.addtable</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Remove table</label>
 	      <help>Removes existing attribute table of a vector map.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.droptable</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Join table</label>
 	      <help>Allows to join a table to a vector map table.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.join</command>
 	    </menuitem>
 	    <separator />
@@ -2468,21 +2903,21 @@
 	      <label>Add columns</label>
 	      <help>Adds one or more columns to the attribute table connected to a given vector map.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.addcol</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Drop column</label>
 	      <help>Drops a column from the attribute table connected to a given vector map.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.dropcol</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Rename column</label>
 	      <help>Renames a column in the attribute table connected to a given vector map.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.renamecol</command>
 	    </menuitem>
 	    <separator />
@@ -2490,7 +2925,7 @@
 	      <label>Change values</label>
 	      <help>Allows to update a column in the attribute table connected to a vector map.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.update</command>
 	    </menuitem>
 	    <separator />
@@ -2498,14 +2933,14 @@
 	      <label>Reconnect vectors to database</label>
 	      <help>Reconnects vectors to a new database.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.reconnect.all</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Set vector map - database connection</label>
 	      <help>Prints/sets DB connection for a vector map to attribute table.</help>
 	      <keywords>vector,database,attribute table</keywords>
-	      <handler>self.OnMenuCmd</handler>
+	      <handler>OnMenuCmd</handler>
 	      <command>v.db.connect</command>
 	    </menuitem>
 	  </items>
@@ -2513,24 +2948,27 @@
       </items>
     </menu>
     <menu>
-      <label>Help</label>
+      <label>&amp;Help</label>
       <items>
 	<menuitem>
-	  <label>GRASS GIS help</label>
+	  <label>GRASS help</label>
 	  <help>Display the HTML man pages of GRASS</help>
-	  <handler>self.RunMenuCmd</handler>
+	  <keywords>general,manual,help</keywords>
+	  <handler>RunMenuCmd</handler>
 	  <command>g.manual -i</command>
 	</menuitem>
 	<menuitem>
-	  <label>GRASS GIS GUI help</label>
+	  <label>GUI help</label>
 	  <help>Display the HTML man pages of GRASS</help>
-	  <handler>self.RunMenuCmd</handler>
+	  <keywords>general,manual,help</keywords>
+	  <handler>RunMenuCmd</handler>
 	  <command>g.manual wxGUI</command>
 	</menuitem>
+	<separator />
 	<menuitem>
 	  <label>About GRASS GIS</label>
 	  <help>About GRASS GIS</help>
-	  <handler>self.OnAboutGRASS</handler>
+	  <handler>OnAboutGRASS</handler>
 	</menuitem>
       </items>
     </menu>

Added: grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml	2010-10-14 15:39:14 UTC (rev 43911)
@@ -0,0 +1,147 @@
+<menudata>
+  <menubar>
+    <menu>
+      <label>&amp;File</label>
+      <items>
+	<menuitem>
+	  <label>New</label>
+	  <help>Create new model</help>
+	  <handler>OnModelNew</handler>
+	  <shortcut>Ctrl+N</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Open</label>
+	  <help>Load model from file</help>
+	  <handler>OnModelOpen</handler>
+	  <shortcut>Ctrl+O</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Save</label>
+	  <help>Save model</help>
+	  <handler>OnModelSave</handler>
+	  <shortcut>Ctrl+S</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Save as</label>
+	  <help>Save model to file</help>
+	  <handler>OnModelSaveAs</handler>
+	</menuitem>
+	<menuitem>
+	  <label>Close</label>
+	  <help>Close model file</help>
+	  <handler>OnModelClose</handler>
+	</menuitem>
+	<separator />
+	<menuitem>
+	  <label>Export to image</label>
+	  <help>Export model to image</help>
+	  <handler>OnExportImage</handler>
+	</menuitem>
+	<menuitem>
+	  <label>Export to Python</label>
+	  <help>Export model to Python script</help>
+	  <handler>OnExportPython</handler>
+	  <shortcut>Ctrl+P</shortcut>
+	</menuitem>
+	<separator />
+	<menuitem>
+	  <label>Quit modeler</label>
+	  <help>Close modeler window</help>
+	  <handler>OnCloseWindow</handler>
+	  <shortcut>Ctrl+W</shortcut>
+	</menuitem>
+      </items>
+    </menu>
+    <menu>
+      <label>&amp;Settings</label>
+      <items>
+	<menuitem>
+	  <label>Preferences</label>
+	  <help>Modeler settings</help>
+	  <handler>OnPreferences</handler>
+	</menuitem>
+      </items>
+    </menu>
+    <menu>
+      <label>&amp;Model</label>
+      <items>
+	<menuitem>
+	  <label>Add action</label>
+	  <help>Add action (GRASS module) to model</help>
+	  <handler>OnAddAction</handler>
+	  <shortcut>Ctrl+A</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Add data</label>
+	  <help>Add data item to model</help>
+	  <handler>OnAddData</handler>
+	  <shortcut>Ctrl+D</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Define relation</label>
+	  <help>Define relation between data and action items</help>
+	  <handler>OnDefineRelation</handler>
+	</menuitem>
+	<menuitem>
+	  <label>Add loop</label>
+	  <help>Adds loop (for) to model</help>
+	  <handler>OnDefineLoop</handler>
+	  <shortcut>Ctrl+L</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Add condition</label>
+	  <help>Adds condition (if/else) to model</help>
+	  <handler>OnDefineCondition</handler>
+	  <shortcut>Ctrl+I</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Remove item</label>
+	  <help>Remove action/data from model</help>
+	  <handler>OnRemoveItem</handler>
+	</menuitem>
+	<separator />
+ 	<menuitem>
+	  <label>Model properties</label>
+	  <help>Model properties (name, purpose, etc.)</help>
+	  <handler>OnModelProperties</handler>
+	</menuitem>
+	<separator />
+	<menuitem>
+	  <label>Delete intermediate data</label>
+	  <help>Delete intermediate data defined in the model</help>
+	  <handler>OnDeleteData</handler>
+	</menuitem>
+	<separator />
+	<menuitem>
+	  <label>Run model</label>
+	  <help>Run entire model</help>
+	  <handler>OnRunModel</handler>
+	  <shortcut>Ctrl+R</shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>Validate model</label>
+	  <help>Validate entire model</help>
+	  <handler>OnValidateModel</handler>
+	  <shortcut>Ctrl+V</shortcut>
+	</menuitem>
+      </items>
+    </menu>
+    <menu>
+      <label>&amp;Help</label>
+      <items>
+	<menuitem>
+	  <label>Help</label>
+	  <help>Display the HTML man pages of Graphical modeler</help>
+	  <handler>OnHelp</handler>
+	</menuitem>
+	<separator />
+	<menuitem>
+	  <label>About Graphical Modeler</label>
+	  <help>Display information about Graphical Modeler</help>
+	  <handler>OnAbout</handler>
+	</menuitem>
+
+      </items>
+    </menu>
+  </menubar>
+</menudata>



More information about the grass-commit mailing list