[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, w